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
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
|
+
|