pixie_dust 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README +16 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/game.js +9200 -0
- data/lib/corelib.js +3331 -0
- data/lib/pixie_dust/version.rb +3 -0
- data/lib/pixie_dust.rb +14 -0
- data/pixie.json +15 -0
- data/pixie_dust.gemspec +29 -0
- data/source/active_bounds.coffee +48 -0
- data/source/ageable.coffee +23 -0
- data/source/bounded.coffee +282 -0
- data/source/camera.coffee +138 -0
- data/source/camera.fade.coffee +69 -0
- data/source/camera.flash.coffee +69 -0
- data/source/camera.rotate.coffee +11 -0
- data/source/camera.shake.coffee +27 -0
- data/source/camera.zoom.coffee +25 -0
- data/source/camera.zsort.coffee +13 -0
- data/source/clampable.coffee +61 -0
- data/source/collidable.coffee +126 -0
- data/source/collision.coffee +272 -0
- data/source/collision_response.coffee +28 -0
- data/source/color.coffee +1113 -0
- data/source/color_table.coffee +2534 -0
- data/source/controllable.coffee +66 -0
- data/source/cooldown.coffee +82 -0
- data/source/debuggable.coffee +253 -0
- data/source/drawable.coffee +167 -0
- data/source/dust_emitter.coffee +36 -0
- data/source/easing.coffee +38 -0
- data/source/emitter.coffee +7 -0
- data/source/emitterable.coffee +68 -0
- data/source/engine.coffee +274 -0
- data/source/engine.collision.coffee +77 -0
- data/source/engine.data.coffee +23 -0
- data/source/engine.delay.coffee +41 -0
- data/source/engine.fps_counter.coffee +32 -0
- data/source/engine.game_state.coffee +86 -0
- data/source/engine.joysticks.coffee +47 -0
- data/source/engine.keyboard.coffee +17 -0
- data/source/engine.levels.coffee +69 -0
- data/source/engine.mouse.coffee +16 -0
- data/source/engine.selector.coffee +166 -0
- data/source/engine.stats.coffee +16 -0
- data/source/engine.tilemap.coffee +41 -0
- data/source/engine_background.coffee +32 -0
- data/source/expirable.coffee +47 -0
- data/source/flickerable.coffee +78 -0
- data/source/follow.coffee +65 -0
- data/source/framerate.coffee +42 -0
- data/source/game_object.coffee +181 -0
- data/source/game_object.effect.coffee +33 -0
- data/source/game_object.meter.coffee +191 -0
- data/source/game_over.coffee +40 -0
- data/source/game_state.coffee +67 -0
- data/source/game_state.save_state.coffee +76 -0
- data/source/game_state.single_camera.coffee +40 -0
- data/source/game_state_cameras.coffee +33 -0
- data/source/level_state.coffee +32 -0
- data/source/movable.coffee +57 -0
- data/source/oscillator.coffee +18 -0
- data/source/pixie_dust.coffee +2 -0
- data/source/resource_loader.coffee +35 -0
- data/source/rotatable.coffee +38 -0
- data/source/sprite.coffee +181 -0
- data/source/text_effect.coffee +74 -0
- data/source/text_effect.floating.coffee +22 -0
- data/source/text_screen.coffee +38 -0
- data/source/tilemap.coffee +56 -0
- data/source/timed_events.coffee +78 -0
- data/source/title_screen.coffee +38 -0
- data/source/tween.coffee +70 -0
- data/test/active_bounds.coffee +67 -0
- data/test/bounded.coffee +98 -0
- data/test/camera.coffee +29 -0
- data/test/clampable.coffee +18 -0
- data/test/collidable.coffee +51 -0
- data/test/collision.coffee +70 -0
- data/test/color.coffee +533 -0
- data/test/controllable.coffee +108 -0
- data/test/cooldown.coffee +116 -0
- data/test/debuggable.coffee +71 -0
- data/test/drawable.coffee +31 -0
- data/test/emitter.coffee +0 -0
- data/test/emitterable.coffee +15 -0
- data/test/engine.coffee +228 -0
- data/test/engine_data.coffee +12 -0
- data/test/engine_delay.coffee +14 -0
- data/test/engine_selector.coffee +100 -0
- data/test/expirable.coffee +35 -0
- data/test/flickerable.coffee +51 -0
- data/test/follow.coffee +34 -0
- data/test/game_object.coffee +78 -0
- data/test/game_object_effect.coffee +17 -0
- data/test/metered.coffee +33 -0
- data/test/movable.coffee +46 -0
- data/test/oscillator.coffee +28 -0
- data/test/resource_loader.coffee +7 -0
- data/test/rotatable.coffee +20 -0
- data/test/sprite.coffee +21 -0
- data/test/text.coffee +25 -0
- data/test/timed_events.coffee +23 -0
- data/test/tweening.coffee +18 -0
- 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,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
|
+
|