pixie_dust 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|