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
data/lib/pixie_dust.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "pixie_dust/version"
|
2
|
+
|
3
|
+
require "cornerstone-source"
|
4
|
+
|
5
|
+
if defined? ::Rails
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
config.paths['app/assets'] = "source"
|
8
|
+
end
|
9
|
+
elsif defined? ::Sprockets
|
10
|
+
root_dir = File.expand_path(File.dirname(File.dirname(__FILE__)))
|
11
|
+
asset_dir = File.join(root_dir, "source")
|
12
|
+
|
13
|
+
::Sprockets.append_path asset_dir
|
14
|
+
end
|
data/pixie.json
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"author": "STRd6",
|
3
|
+
"main": null,
|
4
|
+
"name": "gamelib",
|
5
|
+
"library": true,
|
6
|
+
"libs": {
|
7
|
+
"corelib.js": "https://raw.github.com/PixieEngine/Cornerstone/pixie/game.js"
|
8
|
+
},
|
9
|
+
"directories": {
|
10
|
+
"lib": "lib",
|
11
|
+
"source": "source",
|
12
|
+
"test": "test"
|
13
|
+
},
|
14
|
+
"autosave": true
|
15
|
+
}
|
data/pixie_dust.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'pixie_dust/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "pixie_dust"
|
8
|
+
spec.version = PixieDust::VERSION
|
9
|
+
spec.authors = ["Daniel Moore"]
|
10
|
+
spec.email = ["yahivin@gmail.com"]
|
11
|
+
spec.description = %q{
|
12
|
+
Amazing libraries for developing games.
|
13
|
+
Defines the main PixieEngine GameObject and includes mixins to easily augment their behavior.
|
14
|
+
}
|
15
|
+
|
16
|
+
spec.summary = %q{Amazing libraries for developing games}
|
17
|
+
spec.homepage = ""
|
18
|
+
spec.license = "MIT"
|
19
|
+
|
20
|
+
spec.files = `git ls-files`.split($/)
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
|
28
|
+
spec.add_dependency "cornerstone-source"
|
29
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
###*
|
2
|
+
The ActiveBounds module automatically destroys objects that
|
3
|
+
are outside of the specified bounds. The default bounds are
|
4
|
+
the dimensions of your game. Useful for bullet type objects.
|
5
|
+
|
6
|
+
bullet = GameObject
|
7
|
+
x: 10
|
8
|
+
y: 50
|
9
|
+
width: 20
|
10
|
+
height: 20
|
11
|
+
velocity: Point(120, 0)
|
12
|
+
|
13
|
+
bullet.include ActiveBounds
|
14
|
+
|
15
|
+
# => bullet will be removed when it
|
16
|
+
goes outside of the game bounds.
|
17
|
+
|
18
|
+
bullet2 = GameObject
|
19
|
+
x: 50
|
20
|
+
y: 50
|
21
|
+
width: 30
|
22
|
+
height: 20
|
23
|
+
activeBounds: Rectangle
|
24
|
+
x: 30
|
25
|
+
y: 20
|
26
|
+
width: 100
|
27
|
+
height 100
|
28
|
+
|
29
|
+
# => bullet2 will be removed unless 30 <= I.x <= 130
|
30
|
+
and 20 <= I.y <= 120
|
31
|
+
|
32
|
+
@name ActiveBounds
|
33
|
+
@module
|
34
|
+
@constructor
|
35
|
+
@param {Object} I Instance variables
|
36
|
+
@param {Core} self Reference to including object
|
37
|
+
###
|
38
|
+
ActiveBounds = (I={}, self) ->
|
39
|
+
Object.reverseMerge I,
|
40
|
+
activeBounds: Rectangle
|
41
|
+
x: 0
|
42
|
+
y: 0
|
43
|
+
width: App.width
|
44
|
+
height: App.height
|
45
|
+
|
46
|
+
self.bind 'update', ->
|
47
|
+
self.destroy() unless I.activeBounds.left <= I.x <= I.activeBounds.right
|
48
|
+
self.destroy() unless I.activeBounds.top <= I.y <= I.activeBounds.bottom
|
@@ -0,0 +1,23 @@
|
|
1
|
+
###*
|
2
|
+
The Ageable module handles keeping track of an object's age.
|
3
|
+
|
4
|
+
player = GameObject()
|
5
|
+
|
6
|
+
player.update(1)
|
7
|
+
|
8
|
+
#=> player.I.age == 1
|
9
|
+
|
10
|
+
@name Ageable
|
11
|
+
@module
|
12
|
+
@constructor
|
13
|
+
@param {Object} I Instance variables
|
14
|
+
@param {Core} self Reference to including object
|
15
|
+
###
|
16
|
+
Ageable = (I={}, self) ->
|
17
|
+
Object.reverseMerge I,
|
18
|
+
age: 0
|
19
|
+
|
20
|
+
self.bind 'afterUpdate', (dt) ->
|
21
|
+
I.age += dt
|
22
|
+
|
23
|
+
return {}
|
@@ -0,0 +1,282 @@
|
|
1
|
+
###*
|
2
|
+
The Bounded module is used to provide basic data about the
|
3
|
+
location and dimensions of the including object. This module is included
|
4
|
+
by default in `GameObject`.
|
5
|
+
|
6
|
+
player = Core
|
7
|
+
x: 10
|
8
|
+
y: 50
|
9
|
+
width: 20
|
10
|
+
height: 20
|
11
|
+
other: "stuff"
|
12
|
+
more: "properties"
|
13
|
+
|
14
|
+
player.position()
|
15
|
+
# => Uncaught TypeError: Object has no method 'position'
|
16
|
+
|
17
|
+
player.include(Bounded)
|
18
|
+
|
19
|
+
# now player has all the methods provided by this module
|
20
|
+
player.position()
|
21
|
+
# => {x: 10, y: 50}
|
22
|
+
|
23
|
+
@see GameObject
|
24
|
+
|
25
|
+
Bounded module
|
26
|
+
@name Bounded
|
27
|
+
@module
|
28
|
+
@constructor
|
29
|
+
@param {Object} I Instance variables
|
30
|
+
@param {Core} self Reference to including object
|
31
|
+
###
|
32
|
+
|
33
|
+
Bounded = (I={}, self) ->
|
34
|
+
Object.reverseMerge I,
|
35
|
+
x: 0
|
36
|
+
y: 0
|
37
|
+
width: 8
|
38
|
+
height: 8
|
39
|
+
collisionMargin: Point(0, 0)
|
40
|
+
|
41
|
+
###*
|
42
|
+
Get the object closest to this one.
|
43
|
+
|
44
|
+
@name closest
|
45
|
+
@methodOf Bounded#
|
46
|
+
@param {Object|Array|String} selector An object or set of objects to find the closest from.
|
47
|
+
###
|
48
|
+
closest: (selector) ->
|
49
|
+
if Object.isString(selector)
|
50
|
+
selector = engine.find(selector)
|
51
|
+
else
|
52
|
+
selector = [].concat(selector)
|
53
|
+
|
54
|
+
position = self.position()
|
55
|
+
|
56
|
+
selector.sort (a, b) ->
|
57
|
+
Point.distanceSquared(position, a.position()) - Point.distanceSquared(position, b.position())
|
58
|
+
.first()
|
59
|
+
|
60
|
+
###*
|
61
|
+
Distance between two objects. Proxies to Point.distance.
|
62
|
+
In order for this to work, `otherObj` must have a
|
63
|
+
position method.
|
64
|
+
|
65
|
+
player = GameObject
|
66
|
+
x: 50
|
67
|
+
y: 50
|
68
|
+
width: 10
|
69
|
+
height: 10
|
70
|
+
|
71
|
+
player.include Bounded
|
72
|
+
|
73
|
+
enemy = GameObject
|
74
|
+
x: 110
|
75
|
+
y: 120
|
76
|
+
width: 7
|
77
|
+
height: 20
|
78
|
+
|
79
|
+
player.distance(enemy)
|
80
|
+
# => 92.19544457292888
|
81
|
+
|
82
|
+
@name distance
|
83
|
+
@methodOf Bounded#
|
84
|
+
@see Point.distance
|
85
|
+
@returns {Number} Distance between the two objects
|
86
|
+
###
|
87
|
+
distance: (otherObj) ->
|
88
|
+
Point.distance(self.position(), otherObj.position())
|
89
|
+
|
90
|
+
###*
|
91
|
+
The position of this game object. By default it is the top left point.
|
92
|
+
Redefining the center method will change the relative position.
|
93
|
+
|
94
|
+
player = Core
|
95
|
+
x: 50
|
96
|
+
y: 40
|
97
|
+
|
98
|
+
player.include(Bounded)
|
99
|
+
|
100
|
+
player.position()
|
101
|
+
# => {x: 50, y: 40}
|
102
|
+
|
103
|
+
@name position
|
104
|
+
@methodOf Bounded#
|
105
|
+
@returns {Point} The position of this object
|
106
|
+
###
|
107
|
+
position: (newPosition) ->
|
108
|
+
if newPosition?
|
109
|
+
I.x = newPosition.x
|
110
|
+
I.y = newPosition.y
|
111
|
+
else
|
112
|
+
Point(I.x, I.y)
|
113
|
+
|
114
|
+
changePosition: (delta) ->
|
115
|
+
I.x += delta.x
|
116
|
+
I.y += delta.y
|
117
|
+
|
118
|
+
self
|
119
|
+
|
120
|
+
###*
|
121
|
+
Does a check to see if this object is overlapping
|
122
|
+
with the bounds passed in.
|
123
|
+
|
124
|
+
player = Core
|
125
|
+
x: 4
|
126
|
+
y: 6
|
127
|
+
width: 20
|
128
|
+
height: 20
|
129
|
+
|
130
|
+
player.include(Bounded)
|
131
|
+
|
132
|
+
player.collides({x: 5, y: 7, width: 20, height: 20})
|
133
|
+
# => true
|
134
|
+
|
135
|
+
@name collides
|
136
|
+
@methodOf Bounded#
|
137
|
+
@returns {Point} The position of this object
|
138
|
+
###
|
139
|
+
collides: (bounds) ->
|
140
|
+
Collision.rectangular(self.bounds(), bounds)
|
141
|
+
|
142
|
+
###*
|
143
|
+
This returns a modified bounds based on the collision margin.
|
144
|
+
The area of the bounds is reduced if collision margin is positive
|
145
|
+
and increased if collision margin is negative.
|
146
|
+
|
147
|
+
player = Core
|
148
|
+
collisionMargin:
|
149
|
+
x: -2
|
150
|
+
y: -4
|
151
|
+
x: 50
|
152
|
+
y: 50
|
153
|
+
width: 20
|
154
|
+
height: 20
|
155
|
+
|
156
|
+
player.include(Bounded)
|
157
|
+
|
158
|
+
player.collisionBounds()
|
159
|
+
# => {x: 38, y: 36, height: 28, width: 24}
|
160
|
+
|
161
|
+
player.collisionBounds(10, 10)
|
162
|
+
# => {x: 48, y: 46, height: 28, width: 24}
|
163
|
+
|
164
|
+
@name collisionBounds
|
165
|
+
@methodOf Bounded#
|
166
|
+
@param {Number} xOffset the amount to shift the x position
|
167
|
+
@param {Number} yOffset the amount to shift the y position
|
168
|
+
@returns {Object} The collision bounds
|
169
|
+
###
|
170
|
+
collisionBounds: (xOffset, yOffset) ->
|
171
|
+
bounds = self.bounds(xOffset, yOffset)
|
172
|
+
|
173
|
+
bounds.x += I.collisionMargin.x
|
174
|
+
bounds.y += I.collisionMargin.y
|
175
|
+
bounds.width -= 2 * I.collisionMargin.x
|
176
|
+
bounds.height -= 2 * I.collisionMargin.y
|
177
|
+
|
178
|
+
return bounds
|
179
|
+
|
180
|
+
###*
|
181
|
+
Returns infomation about the location of the object and its dimensions with optional offsets.
|
182
|
+
|
183
|
+
player = Core
|
184
|
+
x: 3
|
185
|
+
y: 6
|
186
|
+
width: 2
|
187
|
+
height: 2
|
188
|
+
|
189
|
+
player.include(Bounded)
|
190
|
+
|
191
|
+
player.bounds()
|
192
|
+
# => {x: 3, y: 6, width: 2, height: 2}
|
193
|
+
|
194
|
+
player.bounds(7, 4)
|
195
|
+
# => {x: 10, y: 10, width: 2, height: 2}
|
196
|
+
|
197
|
+
@name bounds
|
198
|
+
@methodOf Bounded#
|
199
|
+
@param {Number} xOffset the amount to shift the x position
|
200
|
+
@param {Number} yOffset the amount to shift the y position
|
201
|
+
###
|
202
|
+
bounds: (xOffset, yOffset) ->
|
203
|
+
center = self.center()
|
204
|
+
|
205
|
+
x: center.x - I.width/2 + (xOffset || 0)
|
206
|
+
y: center.y - I.height/2 + (yOffset || 0)
|
207
|
+
width: I.width
|
208
|
+
height: I.height
|
209
|
+
|
210
|
+
###*
|
211
|
+
The centeredBounds method returns infomation about the center
|
212
|
+
of the object along with the midpoint of the width and height.
|
213
|
+
|
214
|
+
player = Core
|
215
|
+
x: 3
|
216
|
+
y: 6
|
217
|
+
width: 2
|
218
|
+
height: 2
|
219
|
+
|
220
|
+
player.include(Bounded)
|
221
|
+
|
222
|
+
player.centeredBounds()
|
223
|
+
# => {x: 4, y: 7, xw: 1, yw: 1}
|
224
|
+
|
225
|
+
@name centeredBounds
|
226
|
+
@methodOf Bounded#
|
227
|
+
###
|
228
|
+
centeredBounds: () ->
|
229
|
+
center = self.center()
|
230
|
+
|
231
|
+
x: center.x
|
232
|
+
y: center.y
|
233
|
+
xw: I.width/2
|
234
|
+
yw: I.height/2
|
235
|
+
|
236
|
+
###*
|
237
|
+
The center method returns the {@link Point} that is
|
238
|
+
the center of the object.
|
239
|
+
|
240
|
+
player = Core
|
241
|
+
x: 50
|
242
|
+
y: 40
|
243
|
+
width: 10
|
244
|
+
height: 30
|
245
|
+
|
246
|
+
player.include(Bounded)
|
247
|
+
|
248
|
+
player.center()
|
249
|
+
# => {x: 30, y: 35}
|
250
|
+
|
251
|
+
@name center
|
252
|
+
@methodOf Bounded#
|
253
|
+
@returns {Point} The middle of the calling object
|
254
|
+
###
|
255
|
+
center: (newCenter) ->
|
256
|
+
self.position(newCenter)
|
257
|
+
|
258
|
+
###*
|
259
|
+
Return the circular bounds of the object. The circle is
|
260
|
+
centered at the midpoint of the object.
|
261
|
+
|
262
|
+
player = Core
|
263
|
+
radius: 5
|
264
|
+
x: 50
|
265
|
+
y: 50
|
266
|
+
other: "stuff"
|
267
|
+
|
268
|
+
player.include(Bounded)
|
269
|
+
|
270
|
+
player.circle()
|
271
|
+
# => {radius: 5, x: 50, y: 50}
|
272
|
+
|
273
|
+
@name circle
|
274
|
+
@methodOf Bounded#
|
275
|
+
@returns {Object} An object with a position and a radius
|
276
|
+
###
|
277
|
+
circle: () ->
|
278
|
+
circle = self.center()
|
279
|
+
circle.radius = I.radius || I.width/2 || I.height/2
|
280
|
+
|
281
|
+
return circle
|
282
|
+
|
@@ -0,0 +1,138 @@
|
|
1
|
+
oldCamera = Camera
|
2
|
+
|
3
|
+
Camera = (I={}) ->
|
4
|
+
Object.reverseMerge I,
|
5
|
+
cameraBounds: Rectangle # World Coordinates
|
6
|
+
x: 0
|
7
|
+
y: 0
|
8
|
+
width: App.width
|
9
|
+
height: App.height
|
10
|
+
screen: Rectangle # Screen Coordinates
|
11
|
+
x: 0
|
12
|
+
y: 0
|
13
|
+
width: App.width
|
14
|
+
height: App.height
|
15
|
+
deadzone: Point(0, 0) # Screen Coordinates
|
16
|
+
zoom: 1
|
17
|
+
transform: Matrix()
|
18
|
+
x: App.width/2 # World Coordinates
|
19
|
+
y: App.height/2 # World Coordinates
|
20
|
+
velocity: Point.ZERO
|
21
|
+
maxSpeed: 25
|
22
|
+
t90: 2 # Time in seconds for camera to move 90% of the way to the target
|
23
|
+
|
24
|
+
currentType = "centered"
|
25
|
+
currentObject = null
|
26
|
+
|
27
|
+
objectFilters = []
|
28
|
+
transformFilters = []
|
29
|
+
|
30
|
+
focusOn = (object, elapsedTime) ->
|
31
|
+
dampingFactor = 2
|
32
|
+
|
33
|
+
#TODO: Different t90 value inside deadzone?
|
34
|
+
|
35
|
+
c = elapsedTime * 3.75 / I.t90
|
36
|
+
if c >= 1
|
37
|
+
# Spring is configured to be too intense, just snap to target
|
38
|
+
self.position(target)
|
39
|
+
I.velocity = Point.ZERO
|
40
|
+
else
|
41
|
+
objectCenter = object.center()
|
42
|
+
|
43
|
+
target = objectCenter
|
44
|
+
|
45
|
+
delta = target.subtract(self.position())
|
46
|
+
|
47
|
+
force = delta.subtract(I.velocity.scale(dampingFactor))
|
48
|
+
self.changePosition(I.velocity.scale(c).clamp(I.maxSpeed))
|
49
|
+
I.velocity = I.velocity.add(force.scale(c))
|
50
|
+
|
51
|
+
followTypes =
|
52
|
+
centered: (object, elapsedTime) ->
|
53
|
+
I.deadzone = Point(0, 0)
|
54
|
+
|
55
|
+
focusOn(object, elapsedTime)
|
56
|
+
|
57
|
+
topdown: (object, elapsedTime) ->
|
58
|
+
helper = Math.max(I.screen.width, I.screen.height) / 4
|
59
|
+
|
60
|
+
I.deadzone = Point(helper, helper)
|
61
|
+
|
62
|
+
focusOn(object, elapsedTime)
|
63
|
+
|
64
|
+
platformer: (object, elapsedTime) ->
|
65
|
+
width = I.screen.width / 8
|
66
|
+
height = I.screen.height / 3
|
67
|
+
|
68
|
+
I.deadzone = Point(width, height)
|
69
|
+
|
70
|
+
focusOn(object, elapsedTime)
|
71
|
+
|
72
|
+
self = Core(I).extend
|
73
|
+
follow: (object, type="centered") ->
|
74
|
+
currentObject = object
|
75
|
+
currentType = type
|
76
|
+
|
77
|
+
objectFilterChain: (fn) ->
|
78
|
+
objectFilters.push fn
|
79
|
+
|
80
|
+
transformFilterChain: (fn) ->
|
81
|
+
transformFilters.push fn
|
82
|
+
|
83
|
+
screenToWorld: (point) ->
|
84
|
+
self.transform().inverse().transformPoint(point)
|
85
|
+
|
86
|
+
self.attrAccessor "transform"
|
87
|
+
|
88
|
+
self.bind "afterUpdate", (elapsedTime) ->
|
89
|
+
if currentObject
|
90
|
+
followTypes[currentType](currentObject, elapsedTime)
|
91
|
+
|
92
|
+
# Hard clamp camera to world bounds
|
93
|
+
I.x = I.x.clamp(I.cameraBounds.left + I.screen.width/2, I.cameraBounds.right - I.screen.width/2)
|
94
|
+
I.y = I.y.clamp(I.cameraBounds.top + I.screen.height/2, I.cameraBounds.bottom - I.screen.height/2)
|
95
|
+
|
96
|
+
I.transform = Matrix.translate(I.screen.width/2 - I.x.floor(), I.screen.height/2 - I.y.floor())
|
97
|
+
|
98
|
+
self.bind "draw", (canvas, objects) ->
|
99
|
+
# Move to correct screen coordinates
|
100
|
+
canvas.withTransform Matrix.translate(I.screen.x, I.screen.y), (canvas) ->
|
101
|
+
canvas.clip(0, 0, I.screen.width, I.screen.height)
|
102
|
+
|
103
|
+
objects = objectFilters.pipeline(objects)
|
104
|
+
transform = transformFilters.pipeline(self.transform().copy())
|
105
|
+
|
106
|
+
canvas.withTransform transform, (canvas) ->
|
107
|
+
self.trigger "beforeDraw", canvas
|
108
|
+
objects.invoke "draw", canvas
|
109
|
+
|
110
|
+
self.trigger 'flash', canvas
|
111
|
+
|
112
|
+
self.bind "overlay", (canvas, objects) ->
|
113
|
+
canvas.withTransform Matrix.translate(I.screen.x, I.screen.y), (canvas) ->
|
114
|
+
canvas.clip(0, 0, I.screen.width, I.screen.height)
|
115
|
+
objects = objectFilters.pipeline(objects)
|
116
|
+
|
117
|
+
objects.invoke "trigger", "overlay", canvas
|
118
|
+
|
119
|
+
self.include "Bounded"
|
120
|
+
|
121
|
+
# The order of theses includes is important for
|
122
|
+
# the way in wich they modify the camera view transform
|
123
|
+
|
124
|
+
for moduleName in Camera.defaultModules
|
125
|
+
self.include "Camera.#{moduleName}"
|
126
|
+
|
127
|
+
return self
|
128
|
+
|
129
|
+
Camera.defaultModules = [
|
130
|
+
"ZSort"
|
131
|
+
"Zoom"
|
132
|
+
"Rotate"
|
133
|
+
"Shake"
|
134
|
+
"Flash"
|
135
|
+
"Fade"
|
136
|
+
]
|
137
|
+
|
138
|
+
Object.extend Camera, oldCamera
|
@@ -0,0 +1,69 @@
|
|
1
|
+
###*
|
2
|
+
The <code>Fade</code> module provides convenience methods for accessing common Engine.Flash presets.
|
3
|
+
|
4
|
+
@name Fade
|
5
|
+
@fieldOf Camera
|
6
|
+
@module
|
7
|
+
@param {Object} I Instance variables
|
8
|
+
@param {Object} self Reference to the engine
|
9
|
+
@see Camera.Flash
|
10
|
+
###
|
11
|
+
Camera.Fade = (I, self) ->
|
12
|
+
fadeInDefaults =
|
13
|
+
alpha: 0
|
14
|
+
color: 'black'
|
15
|
+
duration: 30
|
16
|
+
|
17
|
+
fadeOutDefaults =
|
18
|
+
alpha: 1
|
19
|
+
color: 'transparent'
|
20
|
+
duration: 30
|
21
|
+
|
22
|
+
configureFade = (duration, color, alpha) ->
|
23
|
+
I.flashDuration = duration
|
24
|
+
I.flashCooldown = duration
|
25
|
+
I.flashColor = Color(color)
|
26
|
+
I.flashTargetAlpha = alpha
|
27
|
+
|
28
|
+
###*
|
29
|
+
A convenient way to set the flash effect instance variables. This provides a shorthand for fading the screen in
|
30
|
+
from a given color over a specified duration.
|
31
|
+
|
32
|
+
engine.fadeIn()
|
33
|
+
# => Sets the effect variables to their default state. This will the screen to go from black to transparent over the next 30 frames.
|
34
|
+
|
35
|
+
engine.fadeIn('blue', 50)
|
36
|
+
# => This effect will start off blue and fade to transparent over 50 frames.
|
37
|
+
|
38
|
+
@name fadeIn
|
39
|
+
@methodOf Camera#
|
40
|
+
@param {Number} [duration=30] How long the effect lasts
|
41
|
+
@param {Color} [color="black"] The color to fade from
|
42
|
+
###
|
43
|
+
fadeIn: (options={}) ->
|
44
|
+
{alpha, color, duration} = Object.reverseMerge(options, fadeInDefaults)
|
45
|
+
|
46
|
+
configureFade(duration, color, alpha)
|
47
|
+
|
48
|
+
###*
|
49
|
+
A convenient way to set the flash effect instance variables. This provides a shorthand for fading
|
50
|
+
the screen to a given color over a specified duration.
|
51
|
+
|
52
|
+
camera.fadeOut()
|
53
|
+
# => Sets the effect variables to their default state. This will the screen to fade from ransparent to black over the next 30 frames.
|
54
|
+
|
55
|
+
camera.fadeOut
|
56
|
+
color: blue
|
57
|
+
duration: 30
|
58
|
+
# => This effect will start off transparent and change to blue over 50 frames.
|
59
|
+
|
60
|
+
@name fadeOut
|
61
|
+
@methodOf Camera#
|
62
|
+
@param {Number} [duration=30] How long the effect lasts
|
63
|
+
@param {Color} [color="transparent"] The color to fade to
|
64
|
+
###
|
65
|
+
fadeOut: (options={}) ->
|
66
|
+
{alpha, color, duration} = Object.reverseMerge(options, fadeOutDefaults)
|
67
|
+
|
68
|
+
configureFade(duration, color, alpha)
|
69
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
###*
|
2
|
+
The <code>Flash</code> module allows you to flash a color onscreen and then fade to transparent over a time period.
|
3
|
+
This is nice for lightning type effects or to accentuate major game events.
|
4
|
+
|
5
|
+
@name Flash
|
6
|
+
@fieldOf Camera
|
7
|
+
@module
|
8
|
+
@param {Object} I Instance variables
|
9
|
+
@param {Object} self Reference to the camera
|
10
|
+
###
|
11
|
+
Camera.Flash = (I, self) ->
|
12
|
+
Object.reverseMerge I,
|
13
|
+
flashColor: Color(0, 0, 0, 0)
|
14
|
+
flashDuration: 12
|
15
|
+
flashCooldown: 0
|
16
|
+
flashTargetAlpha: 0
|
17
|
+
|
18
|
+
defaultParams =
|
19
|
+
color: 'white'
|
20
|
+
duration: 12
|
21
|
+
targetAlpha: 0
|
22
|
+
|
23
|
+
self.bind 'afterUpdate', ->
|
24
|
+
if I.flashCooldown > 0
|
25
|
+
I.flashColor.a = I.flashColor.a.approach(I.flashTargetAlpha, 1 / I.flashDuration).clamp(0, 1)
|
26
|
+
I.flashColor.a = 0 if I.flashColor.a < 0.00001
|
27
|
+
I.flashColor.a = 1 if I.flashColor.a > 0.9999
|
28
|
+
|
29
|
+
I.flashCooldown = I.flashCooldown.approach(0, 1)
|
30
|
+
|
31
|
+
self.bind 'flash', (canvas) ->
|
32
|
+
canvas.fill I.flashColor
|
33
|
+
|
34
|
+
###*
|
35
|
+
A convenient way to set the flash effect instance variables. Alternatively, you can modify them by hand, but
|
36
|
+
using Camera#flash is the suggested approach.
|
37
|
+
|
38
|
+
camera.flash()
|
39
|
+
# => Sets the flash effect variables to their default state. This will cause a white flash that will turn transparent in the next 12 frames.
|
40
|
+
|
41
|
+
camera.flash
|
42
|
+
color: 'green'
|
43
|
+
duration: 30
|
44
|
+
# => This flash effect will start off green and fade to transparent over 30 frames.
|
45
|
+
|
46
|
+
camera.flash
|
47
|
+
color: Color(255, 0, 0, 0)
|
48
|
+
duration: 20
|
49
|
+
targetAlpha: 1
|
50
|
+
# => This flash effect will start off transparent and move toward red over 20 frames
|
51
|
+
|
52
|
+
@name flash
|
53
|
+
@methodOf Camera#
|
54
|
+
@param {Color} [color="white"] The flash color
|
55
|
+
@param {Number} [duration=12] How long the effect lasts
|
56
|
+
@param {Number} [targetAlpha=0] The alpha value to fade to. By default, this is set to 0, which fades the color to transparent.
|
57
|
+
###
|
58
|
+
flash: (options={}) ->
|
59
|
+
Object.reverseMerge(options, defaultParams)
|
60
|
+
|
61
|
+
{color, duration, targetAlpha} = options
|
62
|
+
|
63
|
+
I.flashColor = Color(color)
|
64
|
+
I.flashTargetAlpha = targetAlpha
|
65
|
+
I.flashCooldown = duration
|
66
|
+
I.flashDuration = duration
|
67
|
+
|
68
|
+
self
|
69
|
+
|