-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcamera.coffee
More file actions
139 lines (102 loc) · 3.66 KB
/
camera.coffee
File metadata and controls
139 lines (102 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
oldCamera = Camera
Camera = (I={}) ->
Object.reverseMerge I,
cameraBounds: Rectangle # World Coordinates
x: 0
y: 0
width: App.width
height: App.height
screen: Rectangle # Screen Coordinates
x: 0
y: 0
width: App.width
height: App.height
deadzone: Point(0, 0) # Screen Coordinates
zoom: 1
transform: Matrix()
x: App.width/2 # World Coordinates
y: App.height/2 # World Coordinates
velocity: Point.ZERO
maxSpeed: 750
t90: 2 # Time in seconds for camera to move 90% of the way to the target
currentType = "centered"
currentObject = null
objectFilters = []
transformFilters = []
focusOn = (object, elapsedTime) ->
dampingFactor = 2
#TODO: Different t90 value inside deadzone?
c = elapsedTime * 3.75 / I.t90
if c >= 1
# Spring is configured to be too intense, just snap to target
self.position(target)
I.velocity = Point.ZERO
else
objectCenter = object.center()
target = objectCenter
delta = target.subtract(self.position())
force = delta.subtract(I.velocity.scale(dampingFactor))
self.changePosition(I.velocity.scale(c).clamp(I.maxSpeed))
I.velocity = I.velocity.add(force.scale(c))
followTypes =
centered: (object, elapsedTime) ->
I.deadzone = Point(0, 0)
focusOn(object, elapsedTime)
topdown: (object, elapsedTime) ->
helper = Math.max(I.screen.width, I.screen.height) / 4
I.deadzone = Point(helper, helper)
focusOn(object, elapsedTime)
platformer: (object, elapsedTime) ->
width = I.screen.width / 8
height = I.screen.height / 3
I.deadzone = Point(width, height)
focusOn(object, elapsedTime)
self = Core(I).extend
follow: (object, type="centered") ->
currentObject = object
currentType = type
objectFilterChain: (fn) ->
objectFilters.push fn
transformFilterChain: (fn) ->
transformFilters.push fn
screenToWorld: (point) ->
self.transform().inverse().transformPoint(point)
self.attrAccessor "transform"
self.bind "afterUpdate", (elapsedTime) ->
if currentObject
followTypes[currentType](currentObject, elapsedTime)
# Hard clamp camera to world bounds
I.x = I.x.clamp(I.cameraBounds.left + I.screen.width/2, I.cameraBounds.right - I.screen.width/2)
I.y = I.y.clamp(I.cameraBounds.top + I.screen.height/2, I.cameraBounds.bottom - I.screen.height/2)
I.transform = Matrix.translate(I.screen.width/2 - I.x.floor(), I.screen.height/2 - I.y.floor())
self.bind "draw", (canvas, objects) ->
# Move to correct screen coordinates
canvas.withTransform Matrix.translate(I.screen.x, I.screen.y), (canvas) ->
canvas.clip(0, 0, I.screen.width, I.screen.height)
objects = objectFilters.pipeline(objects)
transform = transformFilters.pipeline(self.transform().copy())
canvas.withTransform transform, (canvas) ->
self.trigger "beforeDraw", canvas
objects.invoke "draw", canvas
self.trigger 'flash', canvas
self.bind "overlay", (canvas, objects) ->
canvas.withTransform Matrix.translate(I.screen.x, I.screen.y), (canvas) ->
canvas.clip(0, 0, I.screen.width, I.screen.height)
objects = objectFilters.pipeline(objects)
objects.invoke "trigger", "overlay", canvas
self.include "Ageable", "Bounded"
# The order of theses includes is important for
# the way in wich they modify the camera view transform
for moduleName in Camera.defaultModules
self.include "Camera.#{moduleName}"
return self
Object.extend Camera, oldCamera
Camera.defaultModules = [
"ZSort"
"Zoom"
"Rotate"
"Shake"
"Flash"
"Fade"
"Transition"
]