/
ParallaxView.lua
220 lines (176 loc) · 5.56 KB
/
ParallaxView.lua
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
local stime = system.getTimer
local max = math.max
local cipr = require 'cipr'
local class = cipr.import 'cipr.class'
local log = cipr.import('cipr.logging').getLogger(...)
--[[
ParallaxLayer
-------------
A single layer within a ParallaxView
]]--
local ParallaxLayer = class.class('ParallaxLayer')
function ParallaxLayer:initialize(speed)
self.view = display.newGroup()
self.cols = {}
self.numCols = 0
self.totalWidth = 0
self._halfTotalWidth = 0
self.speed = speed
end
--[[
Add a Corona Display Object to this layer
]]--
function ParallaxLayer:addCol(displayObj)
self.view:insert(displayObj)
displayObj:setReferencePoint(display.BottomLeftReferencePoint)
displayObj.x = 0
displayObj.y = 0 + (displayObj.offsetY or 0)
local offset = self.totalWidth
self.totalWidth = self.totalWidth + displayObj.contentWidth
self._halfTotalWidth = max(displayObj.contentWidth, self._halfTotalWidth)
local col = {
obj = displayObj,
offset = offset,
}
self.numCols = self.numCols + 1
-- Set initial position
self:_scrollCol(self.numCols, col, 0, 0, 0, 0)
-- Start invisible, we'll make it visible when needed
col.obj.isVisible = false
self.cols[self.numCols] = col
end
function ParallaxLayer:_scrollCol(i, col, x, y, scale)
local w = self.totalWidth
x = (((x + col.offset) % w) % w ) - self._halfTotalWidth
col.obj:translate(x - col.obj.x, 0)
local right = x + col.obj.contentWidth
local left = x
-- Hide objects that are offscreen for performance
-- TODO Use the parallax view width to determine this
col.obj.isVisible = right >= -self._halfTotalWidth and left <= (display.contentWidth / scale)
end
function ParallaxLayer:scrollTo(x, y, scale)
x = -x * self.speed / scale
y = -y * self.speed / scale
self.view.xScale = scale
self.view.yScale = scale
for i=1,self.numCols do
local col = self.cols[i]
self:_scrollCol(i, col, x, y, scale)
end
end
--[[
ParallaxView
------------
A ParallaxView has many layers that scroll at different rates.
Example::
local view = ParallaxView(display.contentWidth, display.contentHeight)
-- This layer moves at half the speed
local layer1 = view:newLayer(0.5)
-- A layer must have at least 2 columns. The width of all the cols combined must be
-- at least the width of the view in order to loop properly
layer1:addCol(bg1)
layer1:addCol(bg2)
layer1:addCol(bg3)
-- This layer moves at 1/4 the speed
local layer2 = view:newLayer(0.25)
layer2:addCol(mySprite)
layer2:addCol(bgA)
layer2:addCol(bgB)
local function enterFrame()
view:scrollTo(world.x, world.y, world.xScale)
end
]]--
local ParallaxView = class.class('cipr.ui.widgets.ParallaxView')
function ParallaxView:initialize(width, height)
self.view = display.newGroup()
self.xPos = 0
self.yPos = 0
self.scale = 1
self.layers = {}
self.numLayers = 0
self.viewWidth = width
self.viewHeight = height
self.lastTime = stime()
self.odd = false
end
--[[
Create a new scrolling layer
:param speed: float - rate of movment compared to the value put in `scrollTo`. Ex: 0.5
:returns: ParallaxLayer
]]--
function ParallaxView:newLayer(speed)
local layer = ParallaxLayer:new(speed)
self.numLayers = self.numLayers + 1
self.layers[self.numLayers] = layer
self.view:insert(layer.view)
return layer
end
--[[
Add a column to an existing layer
]]--
function ParallaxView:addCol(layerNum, displayObj)
return self.layers[layerNum]:addCol(displayObj)
end
--[[
Scroll the ParallaxView
:param x: float - x coordinate of "camera"
:param y: float - y coordinate of "camera" (default 0)
:param scale: float - scale of ParallaxView, used for camera zoooms (default 1)
]]--
function ParallaxView:scrollTo(x, y, scale)
local y = y or 0
local scale = scale or 1
self.odd = not self.odd
local t = stime()
local delta
delta, self.lastTime = t - self.lastTime, t
-- At 60 fps we can afford to render half of the layers on the even frames
-- and the other half on the odd frames
-- local start = self.odd and 1 or 2
-- for i=start,self.numLayers,2 do
-- local layer = self.layers[i]
-- layer:scrollTo(x, y, scale)
-- end
self.xPos = x
self.yPos = y
self.scale = scale
for i=1,self.numLayers do
local layer = self.layers[i]
layer:scrollTo(x, y, scale)
end
-- BUG (Corona Version 2011.703) dispatchEvent before there are listeners causes this error:
-- Runtime error
-- ?:0: attempt to index a nil value
-- stack traceback:
-- [C]: ?
-- ?: in function '?'
-- ?: in function <?:215>
-- if not self.odd then
self.view:dispatchEvent{
name = 'parallaxScroll',
target = self.view,
parallaxView = self,
x = x,
y = y,
scale = scale,
time = t,
deltaTime = delta
}
-- end
end
--[[
Scroll the ParallaxView relative to current position
:param x: float - x coordinate of "camera"
:param y: float - y coordinate of "camera" (default 0)
:param scale: float - scale of ParallaxView, used for camera zoooms (default 1)
]]--
function ParallaxView:update(x, y, scale)
local y = y or 0
x, y, scale = self.xPos + x, self.yPos + y, scale or self.scale
self:scrollTo(x, y, scale)
end
function ParallaxView:addEventListener(...)
return self.view:addEventListener(...)
end
return ParallaxView