pixie_dust 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README +16 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/game.js +9200 -0
  8. data/lib/corelib.js +3331 -0
  9. data/lib/pixie_dust/version.rb +3 -0
  10. data/lib/pixie_dust.rb +14 -0
  11. data/pixie.json +15 -0
  12. data/pixie_dust.gemspec +29 -0
  13. data/source/active_bounds.coffee +48 -0
  14. data/source/ageable.coffee +23 -0
  15. data/source/bounded.coffee +282 -0
  16. data/source/camera.coffee +138 -0
  17. data/source/camera.fade.coffee +69 -0
  18. data/source/camera.flash.coffee +69 -0
  19. data/source/camera.rotate.coffee +11 -0
  20. data/source/camera.shake.coffee +27 -0
  21. data/source/camera.zoom.coffee +25 -0
  22. data/source/camera.zsort.coffee +13 -0
  23. data/source/clampable.coffee +61 -0
  24. data/source/collidable.coffee +126 -0
  25. data/source/collision.coffee +272 -0
  26. data/source/collision_response.coffee +28 -0
  27. data/source/color.coffee +1113 -0
  28. data/source/color_table.coffee +2534 -0
  29. data/source/controllable.coffee +66 -0
  30. data/source/cooldown.coffee +82 -0
  31. data/source/debuggable.coffee +253 -0
  32. data/source/drawable.coffee +167 -0
  33. data/source/dust_emitter.coffee +36 -0
  34. data/source/easing.coffee +38 -0
  35. data/source/emitter.coffee +7 -0
  36. data/source/emitterable.coffee +68 -0
  37. data/source/engine.coffee +274 -0
  38. data/source/engine.collision.coffee +77 -0
  39. data/source/engine.data.coffee +23 -0
  40. data/source/engine.delay.coffee +41 -0
  41. data/source/engine.fps_counter.coffee +32 -0
  42. data/source/engine.game_state.coffee +86 -0
  43. data/source/engine.joysticks.coffee +47 -0
  44. data/source/engine.keyboard.coffee +17 -0
  45. data/source/engine.levels.coffee +69 -0
  46. data/source/engine.mouse.coffee +16 -0
  47. data/source/engine.selector.coffee +166 -0
  48. data/source/engine.stats.coffee +16 -0
  49. data/source/engine.tilemap.coffee +41 -0
  50. data/source/engine_background.coffee +32 -0
  51. data/source/expirable.coffee +47 -0
  52. data/source/flickerable.coffee +78 -0
  53. data/source/follow.coffee +65 -0
  54. data/source/framerate.coffee +42 -0
  55. data/source/game_object.coffee +181 -0
  56. data/source/game_object.effect.coffee +33 -0
  57. data/source/game_object.meter.coffee +191 -0
  58. data/source/game_over.coffee +40 -0
  59. data/source/game_state.coffee +67 -0
  60. data/source/game_state.save_state.coffee +76 -0
  61. data/source/game_state.single_camera.coffee +40 -0
  62. data/source/game_state_cameras.coffee +33 -0
  63. data/source/level_state.coffee +32 -0
  64. data/source/movable.coffee +57 -0
  65. data/source/oscillator.coffee +18 -0
  66. data/source/pixie_dust.coffee +2 -0
  67. data/source/resource_loader.coffee +35 -0
  68. data/source/rotatable.coffee +38 -0
  69. data/source/sprite.coffee +181 -0
  70. data/source/text_effect.coffee +74 -0
  71. data/source/text_effect.floating.coffee +22 -0
  72. data/source/text_screen.coffee +38 -0
  73. data/source/tilemap.coffee +56 -0
  74. data/source/timed_events.coffee +78 -0
  75. data/source/title_screen.coffee +38 -0
  76. data/source/tween.coffee +70 -0
  77. data/test/active_bounds.coffee +67 -0
  78. data/test/bounded.coffee +98 -0
  79. data/test/camera.coffee +29 -0
  80. data/test/clampable.coffee +18 -0
  81. data/test/collidable.coffee +51 -0
  82. data/test/collision.coffee +70 -0
  83. data/test/color.coffee +533 -0
  84. data/test/controllable.coffee +108 -0
  85. data/test/cooldown.coffee +116 -0
  86. data/test/debuggable.coffee +71 -0
  87. data/test/drawable.coffee +31 -0
  88. data/test/emitter.coffee +0 -0
  89. data/test/emitterable.coffee +15 -0
  90. data/test/engine.coffee +228 -0
  91. data/test/engine_data.coffee +12 -0
  92. data/test/engine_delay.coffee +14 -0
  93. data/test/engine_selector.coffee +100 -0
  94. data/test/expirable.coffee +35 -0
  95. data/test/flickerable.coffee +51 -0
  96. data/test/follow.coffee +34 -0
  97. data/test/game_object.coffee +78 -0
  98. data/test/game_object_effect.coffee +17 -0
  99. data/test/metered.coffee +33 -0
  100. data/test/movable.coffee +46 -0
  101. data/test/oscillator.coffee +28 -0
  102. data/test/resource_loader.coffee +7 -0
  103. data/test/rotatable.coffee +20 -0
  104. data/test/sprite.coffee +21 -0
  105. data/test/text.coffee +25 -0
  106. data/test/timed_events.coffee +23 -0
  107. data/test/tweening.coffee +18 -0
  108. metadata +233 -0
@@ -0,0 +1,191 @@
1
+ ###*
2
+ The Metered module provides a simple drop-in
3
+ meter ui to track arbitrary numeric attributes.
4
+
5
+ player = GameObject
6
+ health: 100
7
+ heathMax: 100
8
+
9
+ enemy = GameObject
10
+ health: 500
11
+
12
+ someOtherObject = GameObject
13
+
14
+ player.meter 'health'
15
+ # => Sets up a health meter that will be drawn during the player overlay event
16
+
17
+ enemy.meter 'health'
18
+ # => Sets up a health meter that will be drawn during the enemy overlay event.
19
+ # Since healthMax wasn't provided, it is set to the value of I.health (500)
20
+
21
+ someOtherObject.meter 'turbo'
22
+ # => Sets up a turbo meter that will be drawn during the someOtherObject overlay event.
23
+ # Since neither turbo or turboMax were provided, they are both set to 100.
24
+
25
+ Metered module
26
+ @name Metered
27
+ @module
28
+ @constructor
29
+ @param {Object} I Instance variables
30
+ @param {GameObject} self Reference to including object
31
+ ###
32
+
33
+ GameObject.Meter = (I={}, self) ->
34
+ Object.reverseMerge I,
35
+ meters: {}
36
+
37
+ self.bind 'overlay', (canvas) ->
38
+ for name, meterData of I.meters
39
+ {
40
+ backgroundColor,
41
+ border: {color:borderColor, radius:borderRadius, width:borderWidth}
42
+ color,
43
+ height,
44
+ show,
45
+ width,
46
+ x,
47
+ y
48
+ } = meterData
49
+
50
+ {x, y} = meterData.position if meterData.position?
51
+
52
+ return unless show
53
+
54
+ ratio = (I[name] / I["#{name}Max"]).clamp(0, 1)
55
+
56
+ canvas.drawRoundRect
57
+ color: backgroundColor
58
+ radius: borderRadius
59
+ x: x
60
+ y: y
61
+ width: width
62
+ height: height
63
+
64
+ canvas.drawRoundRect
65
+ color: color
66
+ x: x
67
+ y: y
68
+ radius: borderRadius
69
+ width: width * ratio
70
+ height: height
71
+
72
+ canvas.drawRoundRect
73
+ x: x
74
+ y: y
75
+ width: width
76
+ height: height
77
+ radius: borderRadius
78
+ stroke:
79
+ color: borderColor
80
+ width: borderWidth
81
+ ###*
82
+ Configures a meter to be drawn each overlay event.
83
+
84
+ player = GameObject
85
+
86
+ player.meter 'health',
87
+ border
88
+ color: 'brown'
89
+ radius: 3
90
+ color: 'pink'
91
+ height: 20
92
+ x: 5
93
+ y: 5
94
+ show: true
95
+ width: 150
96
+
97
+ # => Sets up a health meter, using all the configuration options
98
+
99
+ @name meter
100
+ @methodOf Metered#
101
+ @param {String} name The name of the property to meter
102
+ @param {Object} options The meter configuration options
103
+ @param {String} border: color Color of the meter's border
104
+ @param {Number} border: width Width of the meter's border
105
+ @param {String} color Color of the meter's inner rectangle
106
+ @param {Number} height Height of the meter
107
+ @param {Object} position An x, y object representing the position of the meter
108
+ @param {Number} x x position of the meter
109
+ @param {Number} y y position of the meter
110
+ @param {Number} border: radius Border radius of the meter
111
+ @param {Boolean} show Boolean to toggle whether of not to display the meter
112
+ @param {Number} width How wide the meter is
113
+ ###
114
+ meter: (name, options={}) ->
115
+ Object.reverseMerge options,
116
+ backgroundColor: 'black'
117
+ border:
118
+ color: 'white'
119
+ radius: 2
120
+ width: 1.5
121
+ color: 'green'
122
+ height: 10
123
+ x: 0
124
+ y: 0
125
+ show: true
126
+ width: 100
127
+
128
+ I[name] ?= 100
129
+
130
+ if not I["#{name}Max"]
131
+ if I[name]
132
+ I["#{name}Max"] = I[name]
133
+ else
134
+ I["#{name}Max"] = 100
135
+
136
+ I.meters[name] = options
137
+
138
+ ###*
139
+ Shows the named meter
140
+
141
+ player = GameObject
142
+
143
+ # creates a health meter but disables visibility
144
+ player.meter 'health'
145
+ show: false
146
+
147
+ # enables visibility for the meter named 'health'
148
+ player.showMeter 'health'
149
+
150
+ @name showMeter
151
+ @methodOf Metered#
152
+ @param {String} name The name of the meter to show
153
+ ###
154
+ showMeter: (name) ->
155
+ I.meters[name].show = true
156
+
157
+ ###*
158
+ Hides the named meter
159
+
160
+ player = GameObject
161
+
162
+ # creates a health meter
163
+ player.meter 'health'
164
+
165
+ # disables visibility for the meter named 'health'
166
+ player.hideMeter 'health'
167
+
168
+ @name hideMeter
169
+ @methodOf Metered#
170
+ @param {String} name The name of the meter to hide
171
+ ###
172
+ hideMeter: (name) ->
173
+ I.meters[name].show = false
174
+
175
+ ###*
176
+ Toggles visibility of the named meter
177
+
178
+ player = GameObject
179
+
180
+ # creates a health meter
181
+ player.meter 'health'
182
+
183
+ # toggles visibility for the meter named 'health'
184
+ player.toggleMeter 'health'
185
+
186
+ @name toggleMeter
187
+ @methodOf Metered#
188
+ @param {String} name The name of the meter to toggle
189
+ ###
190
+ toggleMeter: (name) ->
191
+ I.meters[name].show = not I.meters[name].show
@@ -0,0 +1,40 @@
1
+ ###*
2
+ The Game Over class sets up a simple game state with restart instructions.
3
+
4
+ @see TextScreen
5
+ @name GameOver
6
+ @constructor
7
+ ###
8
+
9
+ ###*
10
+ Transitions to the title state on user input.
11
+
12
+ @name update
13
+ @methodOf GameOver#
14
+ @event
15
+ ###
16
+
17
+ ###*
18
+ Draws Game Over screen and reset instructions.
19
+
20
+ @name overlay
21
+ @methodOf GameOver#
22
+ @param {PixieCanvas} canvas
23
+ @event
24
+ ###
25
+ GameOver = (I={}) ->
26
+ self = TextScreen(I)
27
+
28
+ self.bind 'update', ->
29
+ if justPressed.any
30
+ engine.delay 15, ->
31
+ engine.setState TitleScreen()
32
+
33
+ self.bind "overlay", (canvas) ->
34
+ self.centerText canvas, "Game Over"
35
+
36
+ self.centerText canvas, "Press any key to restart",
37
+ size: 12
38
+ y: App.height / 2 + 30
39
+
40
+ return self
@@ -0,0 +1,67 @@
1
+ GameState = (I={}) ->
2
+ Object.reverseMerge I,
3
+ objects: []
4
+
5
+ queuedObjects = []
6
+
7
+ self = Core(I).extend {
8
+ ###*
9
+ The add method creates and adds an object to the game world. Two
10
+ other events are triggered around this one: beforeAdd and afterAdd.
11
+
12
+ # you can add arbitrary entityData and
13
+ # the engine will make it into a GameObject
14
+ engine.add
15
+ x: 50
16
+ y: 30
17
+ color: "red"
18
+
19
+ player = engine.add
20
+ class: "Player"
21
+
22
+ @name add
23
+ @methodOf Engine#
24
+ @param {Object} entityData The data used to create the game object.
25
+ @returns {GameObject}
26
+ ###
27
+ add: (entityData) ->
28
+ self.trigger "beforeAdd", entityData
29
+
30
+ object = GameObject.construct entityData
31
+ object.create()
32
+
33
+ self.trigger "afterAdd", object
34
+
35
+ if I.updating
36
+ queuedObjects.push object
37
+ else
38
+ I.objects.push object
39
+
40
+ return object
41
+
42
+ objects: ->
43
+ I.objects.copy()
44
+ }
45
+
46
+ # Add events and methods here
47
+ self.bind "update", (elapsedTime) ->
48
+ I.updating = true
49
+
50
+ I.objects.invoke "trigger", "beforeUpdate", elapsedTime
51
+
52
+ [toKeep, toRemove] = I.objects.partition (object) ->
53
+ object.update(elapsedTime)
54
+
55
+ I.objects.invoke "trigger", "afterUpdate", elapsedTime
56
+
57
+ toRemove.invoke "trigger", "remove"
58
+
59
+ I.objects = toKeep.concat(queuedObjects)
60
+ queuedObjects = []
61
+
62
+ I.updating = false
63
+
64
+ self.include "GameState.Cameras"
65
+ self.include "GameState.SaveState"
66
+
67
+ return self
@@ -0,0 +1,76 @@
1
+ ###*
2
+ The <code>SaveState</code> module provides methods to save and restore the current game state.
3
+
4
+ @name SaveState
5
+ @fieldOf GameState
6
+ @module
7
+ @param {Object} I Instance variables
8
+ @param {Object} self Reference to the game state
9
+ ###
10
+ GameState.SaveState = (I, self) ->
11
+ savedState = null
12
+
13
+ ###*
14
+ Save the current game state and returns a JSON object representing that state.
15
+
16
+ engine.bind 'update', ->
17
+ if justPressed.s
18
+ engine.saveState()
19
+
20
+ @name saveState
21
+ @methodOf GameState#
22
+ @returns {Array} An array of the instance data of all objects in the game state
23
+ ###
24
+ saveState: ->
25
+ savedState = I.objects.map (object) ->
26
+ Object.extend({}, object.I)
27
+
28
+ ###*
29
+ Loads the game state passed in, or the last saved state, if any.
30
+
31
+ engine.bind 'update', ->
32
+ if justPressed.l
33
+ # loads the last saved state
34
+ engine.loadState()
35
+
36
+ if justPressed.o
37
+ # removes all game objects, then reinstantiates
38
+ # them with the entityData passed in
39
+ engine.loadState([{x: 40, y: 50, class: "Player"}, {x: 0, y: 0, class: "Enemy"}, {x: 500, y: 400, class: "Boss"}])
40
+
41
+ @name loadState
42
+ @methodOf GameState#
43
+ @param [newState] An arraf of object instance data to load.
44
+ ###
45
+ loadState: (newState) ->
46
+ if newState ||= savedState
47
+ I.objects.invoke "trigger", "remove"
48
+ I.objects = []
49
+
50
+ newState.each (objectData) ->
51
+ self.add Object.extend({}, objectData)
52
+
53
+ ###*
54
+ Reloads the current game state, useful for hotswapping code.
55
+
56
+ engine.I.objects.each (object) ->
57
+ # bring all objects to (0, 0) for some reason
58
+ object.I.x = 0
59
+ object.I.y = 0
60
+
61
+ # reload all objects to make sure
62
+ # they are at (0, 0)
63
+ engine.reload()
64
+
65
+ @name reload
66
+ @methodOf GameState#
67
+ ###
68
+ reload: ->
69
+ oldObjects = I.objects
70
+ I.objects = []
71
+
72
+ oldObjects.each (object) ->
73
+ object.trigger "remove"
74
+
75
+ self.add object.I
76
+
@@ -0,0 +1,40 @@
1
+ ###*
2
+ The <code>SingleCamera</code> module provides provides a single camera view of the game.
3
+ Its transform can be adjusted to view different areas and provide various camera effects.
4
+
5
+ @name SingleCamera
6
+ @fieldOf GameState
7
+ @module
8
+ @param {Object} I Instance variables
9
+ @param {Object} self Reference to the game state
10
+ ###
11
+ GameState.SingleCamera = (I, self) ->
12
+ # Set some default properties
13
+ Object.reverseMerge I,
14
+ cameraTransform: Matrix.IDENTITY
15
+ zSort: true
16
+
17
+ self.attrAccessor "cameraTransform"
18
+
19
+ # Add events and methods here
20
+ self.bind "draw", (canvas) ->
21
+ canvas.withTransform I.cameraTransform, (canvas) ->
22
+ drawObjects = self.objects()
23
+
24
+ # TODO Turn this zSort into a per camera object stream filter
25
+ # This will also enable filters like clipping region tests
26
+ if I.zSort
27
+ drawObjects.sort (a, b) ->
28
+ a.I.zIndex - b.I.zIndex
29
+
30
+ drawObjects.invoke("draw", canvas)
31
+
32
+ # TODO This should be triggering a camera draw
33
+ # camera.trigger "draw", I.canvas
34
+ # possibly even with before and after.
35
+ # This is where per camera effects like shake,
36
+ # flash and fade will come in.
37
+
38
+ # No public methods
39
+ return {}
40
+
@@ -0,0 +1,33 @@
1
+ GameState.Cameras = (I, self) ->
2
+ cameras = [Camera()]
3
+
4
+ self.bind 'update', (elapsedTime) ->
5
+ self.cameras().invoke 'trigger', 'update', elapsedTime
6
+
7
+ self.bind 'afterUpdate', (elapsedTime) ->
8
+ self.cameras().invoke 'trigger', 'afterUpdate', elapsedTime
9
+
10
+ self.bind 'draw', (canvas) ->
11
+ self.cameras().invoke 'trigger', 'draw', canvas, self.objects()
12
+
13
+ self.bind 'overlay', (canvas) ->
14
+ self.cameras().invoke 'trigger', 'overlay', canvas, self.objects()
15
+
16
+ return {
17
+ addCamera: (data) ->
18
+ cameras.push(Camera(data))
19
+ ###*
20
+ Returns the array of camera objects.
21
+
22
+ @name cameras
23
+ @methodOf Engine#
24
+ @returns {Array}
25
+ ###
26
+ cameras: (newCameras) ->
27
+ if newCameras
28
+ cameras = newCameras
29
+
30
+ return self
31
+ else
32
+ return cameras
33
+ }
@@ -0,0 +1,32 @@
1
+ ###*
2
+ A Game State that loads the map for a given level and transitions into the level.
3
+
4
+ @see GameState
5
+ @name LevelState
6
+ @constructor
7
+ @param {Number} duration Amount of time in frames it takes to fade into the level
8
+ @param {String} level name of the map to load
9
+ ###
10
+
11
+ ###*
12
+ Fades in the current level and loads the map.
13
+
14
+ @name enter
15
+ @methodOf LevelState#
16
+ @event
17
+ ###
18
+ LevelState = (I={}) ->
19
+ Object.reverseMerge I,
20
+ duration: 10
21
+ level: 'level1'
22
+
23
+ self = GameState(I)
24
+
25
+ self.bind "enter", ->
26
+ engine.fadeIn
27
+ duration: I.duration
28
+
29
+ engine.loadMap I.level, ->
30
+ engine.I.transitioning = false
31
+
32
+ return self
@@ -0,0 +1,57 @@
1
+ ###*
2
+ The Movable module automatically updates the position and velocity of
3
+ GameObjects based on the velocity and acceleration. It does not check
4
+ collisions so is probably best suited to particle effect like things.
5
+
6
+ player = GameObject
7
+ x: 0
8
+ y: 0
9
+ velocity: Point(0, 0)
10
+ acceleration: Point(1, 0)
11
+ maxSpeed: 2
12
+
13
+ player.include(Movable)
14
+
15
+ # => `velocity is {x: 0, y: 0} and position is {x: 0, y: 0}`
16
+
17
+ player.update(1)
18
+ # => `velocity is {x: 1, y: 0} and position is {x: 1, y: 0}`
19
+
20
+ player.update(1)
21
+ # => `velocity is {x: 2, y: 0} and position is {x: 3, y: 0}`
22
+
23
+ # we've hit our maxSpeed so our velocity won't increase
24
+ player.update(1)
25
+ # => `velocity is {x: 2, y: 0} and position is {x: 5, y: 0}`
26
+
27
+ @name Movable
28
+ @module
29
+ @constructor
30
+ @param {Object} I Instance variables
31
+ @param {Core} self Reference to including object
32
+ ###
33
+ Movable = (I={}, self) ->
34
+ Object.reverseMerge I,
35
+ acceleration: Point(0, 0)
36
+ velocity: Point(0, 0)
37
+
38
+ # Force acceleration and velocity to be Points
39
+ # Useful when reloading data from JSON
40
+ I.acceleration = Point(I.acceleration.x, I.acceleration.y)
41
+ I.velocity = Point(I.velocity.x, I.velocity.y)
42
+
43
+ self.attrReader "velocity", "acceleration"
44
+
45
+ # Handle multi-include
46
+ self.unbind ".Movable"
47
+
48
+ self.bind 'update.Movable', (dt) ->
49
+ I.velocity = I.velocity.add(I.acceleration.scale(dt))
50
+
51
+ if I.maxSpeed?
52
+ currentSpeed = I.velocity.magnitude()
53
+ if currentSpeed > I.maxSpeed
54
+ I.velocity = I.velocity.scale(I.maxSpeed / currentSpeed)
55
+
56
+ I.x += I.velocity.x * dt
57
+ I.y += I.velocity.y * dt
@@ -0,0 +1,18 @@
1
+ ###*
2
+ Creates an oscillator function with the given parameters.
3
+
4
+ @name Oscillator
5
+ @constructor
6
+ @param {Number} amplitude How much to scale the oscillator function value
7
+ @param {Number} period How fast the osciallator function repeats
8
+ @param {Number} offset How much to offset the created oscillator function. Useful for translating between sin and cosine functions.
9
+ ###
10
+ Oscillator = (options={}) ->
11
+ {amplitude, period, offset} = options
12
+
13
+ amplitude = 1 unless amplitude?
14
+ period = 1 unless period?
15
+ offset = 0 unless offset?
16
+
17
+ return (t) ->
18
+ amplitude * Math.cos(Math.TAU * t / period + offset)
@@ -0,0 +1,2 @@
1
+ #= require cornerstone
2
+ #= require_tree .
@@ -0,0 +1,35 @@
1
+ ###*
2
+ @name ResourceLoader
3
+ @namespace
4
+
5
+ Helps access the assets in your game.
6
+ ###
7
+ (->
8
+ typeTable =
9
+ images: "png"
10
+ data: "json"
11
+ tilemaps: "tilemap"
12
+
13
+ ResourceLoader =
14
+ ###*
15
+ Return the url for a particular asset.
16
+
17
+ ResourceLoader.urlFor("images", "player")
18
+ # => This returns the url for the file "player.png" in your images directory.
19
+
20
+ @name urlFor
21
+ @methodOf ResourceLoader#
22
+ @param {String} directory The directory your file is in.
23
+ @param {String} name The name of the file.
24
+ @returns {String} The full url of your asset
25
+
26
+ ###
27
+ urlFor: (directory, name) ->
28
+ directory = App?.directories?[directory] || directory
29
+
30
+ type = typeTable[directory]
31
+
32
+ "#{BASE_URL}/#{directory}/#{name}.#{type}?#{MTIME}"
33
+
34
+ (exports ? this)["ResourceLoader"] = ResourceLoader
35
+ )()
@@ -0,0 +1,38 @@
1
+ ###*
2
+ The Rotatable module rotates the object
3
+ based on its rotational velocity.
4
+
5
+ player = GameObject
6
+ x: 0
7
+ y: 0
8
+ rotationalVelocity: Math.PI / 64
9
+
10
+ player.I.rotation
11
+ # => 0
12
+
13
+ player.update(1)
14
+
15
+ player.I.rotation
16
+ # => 0.04908738521234052 # Math.PI / 64
17
+
18
+ player.update(1)
19
+
20
+ player.I.rotation
21
+ # => 0.09817477042468103 # 2 * (Math.PI / 64)
22
+
23
+ @name Rotatable
24
+ @module
25
+ @constructor
26
+ @param {Object} I Instance variables
27
+ @param {Core} self Reference to including object
28
+ ###
29
+ Rotatable = (I={}, self) ->
30
+ Object.reverseMerge I,
31
+ rotation: 0
32
+ rotationalVelocity: 0
33
+
34
+ self.bind 'update', (dt) ->
35
+ I.rotation += I.rotationalVelocity * dt
36
+
37
+ return {}
38
+