Skip to content

Commit e4bb217

Browse files
committed
Updated how script properties are handled
1 parent 58f8421 commit e4bb217

File tree

4 files changed

+99
-71
lines changed

4 files changed

+99
-71
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Select the script component attached to the ```camera.go``` to modify the proper
2222
This is the near and far z-values used in the projection matrix, ie the near and far clipping plane. Anything with a z-value inside this range will be drawn by the render script.
2323

2424
#### zoom (number)
25-
This is the zoom level of the camera. Modify it by calling ```camera.zoom_to()```. Read it using ```camera.get_zoom()``` or using ```go.get(camera_id, "zoom")```.
25+
This is the zoom level of the camera. Modify it by calling ```camera.zoom_to()```, ```go.set(camera, "zoom")``` or ```go.animate(camera, "zoom", ...)```. Read it using ```camera.get_zoom()``` or ```go.get(camera_id, "zoom")```.
2626

2727
#### projection (hash)
2828
The camera can be configured to support different kinds of orthographic projections. The default projection (aptly named ```DEFAULT```) uses the same orthographic projection matrix as in the default render script (ie aspect ratio isn't maintained and content is stretched). Other projections are available out-of-the box:
@@ -37,6 +37,22 @@ Additional custom projections can be added, see ```camera.add_projector()``` bel
3737
#### enabled (boolean)
3838
This controls if the camera is enabled by default or not. Send ```enable``` and ```disable``` messages to the script or use ```go.set(id, "enable", true|false)``` to toggle this value.
3939

40+
#### follow (boolean)
41+
This controls if the camera should follow a target or not. See ```camera.follow()``` for details.
42+
43+
#### follow_target (hash)
44+
Id of the game object to follow. See ```camera.follow()``` for details.
45+
46+
#### follow_lerp (number)
47+
Amount of lerp when following a target. See ```camera.follow()``` for details.
48+
49+
#### bounds_left (number), bounds_right (number), bounds_top (number), bounds_bottom (number)
50+
The camera bounds. See ```camera.bounds()``` for details.
51+
52+
#### deadzone_left (number), deadzone_right (number), deadzone_top (number), deadzone_bottom (number)
53+
The camera deadzone. See ```camera.deadzone()``` for details.
54+
55+
4056
## Render script integration
4157
While the camera is enabled it will send ```set_view_projection``` messages once per frame to the render script. The message is the same as that of the camera component, meaning that it contains ```id```, ```view``` and ```projection``` values. Make sure that these values are handled and used properly in the render script:
4258

example/camera_controls.gui_script

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ function on_input(self, action_id, action)
3434
msg.post("camera", "unfollow")
3535
return true
3636
elseif gui.pick_node(gui.get_node("follow_basic/button"), action.x, action.y) then
37-
msg.post("camera", "follow", { target = "player" })
37+
msg.post("camera", "follow", { target = "/player" })
3838
return true
3939
elseif gui.pick_node(gui.get_node("follow_lerp/button"), action.x, action.y) then
40-
msg.post("camera", "follow", { target = "player", lerp = 0.1 })
40+
msg.post("camera", "follow", { target = "/player", lerp = 0.1 })
4141
return true
4242
elseif gui.pick_node(gui.get_node("shake_both/button"), action.x, action.y) then
4343
msg.post("camera", "shake", { intensity = 0.05, duration = 0.5, direction = hash("both") })

orthographic/camera.lua

Lines changed: 48 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,7 @@ function M.use_projector(camera_id, projector_id)
7979
assert(camera_id, "You must provide a camera id")
8080
assert(projector_id, "You must provide a projector id")
8181
local camera = cameras[camera_id]
82-
if camera then
83-
camera.projector_id = projector_id
84-
end
82+
msg.post(camera.url, "use_projection", { projection = projector_id })
8583
end
8684

8785

@@ -114,8 +112,13 @@ end
114112

115113
local function calculate_projection(camera_id)
116114
local camera = cameras[camera_id]
117-
local projector_fn = projectors[camera.projector_id] or projectors[hash("DEFAULT")]
118-
return projector_fn(camera_id, camera.near_z, camera.far_z, camera.zoom)
115+
local projector_id = go.get(camera.url, "projection")
116+
local near_z = go.get(camera.url, "near_z")
117+
local far_z = go.get(camera.url, "far_z")
118+
local zoom = go.get(camera.url, "zoom")
119+
camera.zoom = zoom
120+
local projector_fn = projectors[projector_id] or projectors[hash("DEFAULT")]
121+
return projector_fn(camera_id, near_z, far_z, zoom)
119122
end
120123

121124
local function calculate_view(camera_id, camera_world_pos, offset)
@@ -135,17 +138,12 @@ end
135138
--- Initialize a camera
136139
-- Note: This is called automatically from the init() function of the camera.script
137140
-- @param camera_id
138-
-- @param settings Camera settings. Accepted values:
139-
-- * near_z (number)
140-
-- * far_z (number)
141-
-- * projector_id (hash)
142-
function M.init(camera_id, settings)
141+
-- @param camera_script_url
142+
function M.init(camera_id, camera_script_url, settings)
143143
assert(camera_id, "You must provide a camera id")
144-
assert(settings.near_z, "You must provide a near z-value")
145-
assert(settings.far_z, "You must provide a far z-value")
146-
assert(settings.projector_id, "You must provide a projector id")
147-
settings.zoom = settings.zoom or 1
144+
assert(camera_script_url, "You must provide a camera script url")
148145
cameras[camera_id] = settings
146+
cameras[camera_id].url = camera_script_url
149147
cameras[camera_id].view = calculate_view(camera_id, go.get_world_position(camera_id))
150148
cameras[camera_id].projection = calculate_projection(camera_id)
151149
end
@@ -179,16 +177,22 @@ function M.update(camera_id, dt)
179177

180178
local camera_world_pos = go.get_world_position(camera_id)
181179
local camera_world_to_local_diff = camera_world_pos - go.get_position(camera_id)
182-
if camera.follow then
183-
local target_pos = go.get_position(camera.follow.target)
184-
local target_world_pos = go.get_world_position(camera.follow.target)
180+
local follow_enabled = go.get(camera.url, "follow")
181+
if follow_enabled then
182+
local follow = go.get(camera.url, "follow_target")
183+
local target_pos = go.get_position(follow)
184+
local target_world_pos = go.get_world_position(follow)
185185
local new_pos
186-
if camera.deadzone then
186+
local deadzone_top = go.get(camera.url, "deadzone_top")
187+
local deadzone_left = go.get(camera.url, "deadzone_left")
188+
local deadzone_right = go.get(camera.url, "deadzone_right")
189+
local deadzone_bottom = go.get(camera.url, "deadzone_bottom")
190+
if deadzone_top ~= 0 or deadzone_left ~= 0 or deadzone_right ~= 0 or deadzone_bottom ~= 0 then
187191
new_pos = vmath.vector3(camera_world_pos)
188-
local left_edge = camera_world_pos.x - camera.deadzone.left
189-
local right_edge = camera_world_pos.x + camera.deadzone.right
190-
local top_edge = camera_world_pos.y + camera.deadzone.top
191-
local bottom_edge = camera_world_pos.y - camera.deadzone.bottom
192+
local left_edge = camera_world_pos.x - deadzone_left
193+
local right_edge = camera_world_pos.x + deadzone_right
194+
local top_edge = camera_world_pos.y + deadzone_top
195+
local bottom_edge = camera_world_pos.y - deadzone_bottom
192196
if target_world_pos.x < left_edge then
193197
new_pos.x = new_pos.x - (left_edge - target_world_pos.x)
194198
elseif target_world_pos.x > right_edge then
@@ -203,19 +207,19 @@ function M.update(camera_id, dt)
203207
new_pos = target_world_pos
204208
end
205209
new_pos.z = camera_world_pos.z
206-
if camera.follow.lerp then
207-
camera_world_pos = vmath.lerp(camera.follow.lerp or 0.1, camera_world_pos, new_pos)
208-
camera_world_pos.z = new_pos.z
209-
else
210-
camera_world_pos = new_pos
211-
end
210+
local follow_lerp = go.get(camera.url, "follow_lerp")
211+
camera_world_pos = vmath.lerp(follow_lerp, camera_world_pos, new_pos)
212+
camera_world_pos.z = new_pos.z
212213
end
213214

214-
if camera.bounds then
215-
local bounds = camera.bounds
215+
local bounds_top = go.get(camera.url, "bounds_top")
216+
local bounds_left = go.get(camera.url, "bounds_left")
217+
local bounds_bottom = go.get(camera.url, "bounds_bottom")
218+
local bounds_right = go.get(camera.url, "bounds_right")
219+
if bounds_top ~= 0 or bounds_left ~= 0 or bounds_bottom ~= 0 or bounds_right ~= 0 then
216220
local cp = M.world_to_screen(camera_id, vmath.vector3(camera_world_pos))
217-
local tr = M.world_to_screen(camera_id, bounds.top_right) - OFFSET
218-
local bl = M.world_to_screen(camera_id, bounds.bottom_left) + OFFSET
221+
local tr = M.world_to_screen(camera_id, vmath.vector3(bounds_right, bounds_top, 0)) - OFFSET
222+
local bl = M.world_to_screen(camera_id, vmath.vector3(bounds_left, bounds_bottom, 0)) + OFFSET
219223

220224
cp.x = math.max(cp.x, bl.x)
221225
cp.x = math.min(cp.x, tr.x)
@@ -226,6 +230,7 @@ function M.update(camera_id, dt)
226230
end
227231

228232
go.set_position(camera_world_pos + camera_world_to_local_diff, camera_id)
233+
229234

230235
if camera.shake then
231236
camera.shake.duration = camera.shake.duration - dt
@@ -275,14 +280,15 @@ end
275280
function M.follow(camera_id, target, lerp)
276281
assert(camera_id, "You must provide a camera id")
277282
assert(target, "You must provide a target")
278-
cameras[camera_id].follow = { target = target, lerp = lerp }
283+
msg.post(cameras[camera_id].url, "follow", { target = target, lerp = lerp })
279284
end
280285

281286

282287
--- Unfollow a game object
283288
-- @param camera_id
284289
function M.unfollow(camera_id)
285-
cameras[camera_id].follow = nil
290+
assert(camera_id, "You must provide a camera id")
291+
msg.post(cameras[camera_id].url, "unfollow")
286292
end
287293

288294
--- Set the camera deadzone
@@ -293,15 +299,11 @@ end
293299
-- @param bottom
294300
function M.deadzone(camera_id, left, top, right, bottom)
295301
assert(camera_id, "You must provide a camera id")
302+
local camera = cameras[camera_id]
296303
if left and right and top and bottom then
297-
cameras[camera_id].deadzone = {
298-
left = left,
299-
right = right,
300-
bottom = bottom,
301-
top = top,
302-
}
304+
msg.post(camera.url, "deadzone", { left = left, top = top, right = right, bottom = bottom })
303305
else
304-
cameras[camera_id].deadzone = nil
306+
msg.post(camera.url, "deadzone")
305307
end
306308
end
307309

@@ -314,17 +316,11 @@ end
314316
-- @param bottom
315317
function M.bounds(camera_id, left, top, right, bottom)
316318
assert(camera_id, "You must provide a camera id")
319+
local camera = cameras[camera_id]
317320
if left and top and right and bottom then
318-
cameras[camera_id].bounds = {
319-
left = left,
320-
right = right,
321-
bottom = bottom,
322-
top = top,
323-
bottom_left = vmath.vector3(left, bottom, 0),
324-
top_right = vmath.vector3(right, top, 0),
325-
}
321+
msg.post(camera.url, "bounds", { left = left, top = top, right = right, bottom = bottom })
326322
else
327-
cameras[camera_id].bounds = nil
323+
msg.post(camera.url, "bounds")
328324
end
329325
end
330326

@@ -347,6 +343,7 @@ function M.shake(camera_id, intensity, duration, direction, cb)
347343
}
348344
end
349345

346+
350347
--- Stop shaking a camera
351348
-- @param camera_id
352349
function M.stop_shaking(camera_id)
@@ -355,14 +352,12 @@ function M.stop_shaking(camera_id)
355352
end
356353

357354

358-
359355
--- Simulate a recoil effect
360356
-- @param camera_id
361357
-- @param offset Amount to offset the camera with
362358
-- @param duration Duration of the recoil. Optional, default: 0.5s.
363359
function M.recoil(camera_id, offset, duration)
364360
assert(camera_id, "You must provide a strength id")
365-
print("recoil", offset, duration)
366361
cameras[camera_id].recoil = {
367362
offset = offset,
368363
duration = duration or 0.5,
@@ -377,9 +372,7 @@ end
377372
function M.set_zoom(camera_id, zoom)
378373
assert(camera_id, "You must provide a camera id")
379374
assert(zoom, "You must provide a zoom level")
380-
cameras[camera_id].zoom = zoom
381-
cameras[camera_id].view = calculate_view(camera_id, go.get_world_position(camera_id))
382-
cameras[camera_id].projection = calculate_projection(camera_id)
375+
msg.post(cameras[camera_id], "zoom_to", { zoom = zoom })
383376
end
384377

385378

@@ -392,7 +385,6 @@ function M.get_zoom(camera_id)
392385
end
393386

394387

395-
396388
--- Get the projection matrix for a camera
397389
-- @param camera_id
398390
-- @return Projection matrix

orthographic/camera.script

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ go.property("zoom", 1)
44
go.property("projection", hash("DEFAULT"))
55
go.property("enabled", true)
66

7+
go.property("follow", false)
8+
go.property("follow_target", hash(""))
9+
go.property("follow_lerp", 0.5)
10+
11+
go.property("bounds_left", 0)
12+
go.property("bounds_bottom", 0)
13+
go.property("bounds_right", 0)
14+
go.property("bounds_top", 0)
15+
go.property("deadzone_left", 0)
16+
go.property("deadzone_bottom", 0)
17+
go.property("deadzone_right", 0)
18+
go.property("deadzone_top", 0)
19+
20+
721
local camera = require "orthographic.camera"
822

923

@@ -20,12 +34,7 @@ local UPDATE_CAMERA = hash("update_camera")
2034
local ZOOM_TO = hash("zoom_to")
2135

2236
function init(self)
23-
camera.init(go.get_id(), {
24-
near_z = self.near_z,
25-
far_z = self.far_z,
26-
projector_id = self.projection,
27-
zoom = self.zoom,
28-
})
37+
camera.init(go.get_id(), msg.url(), { zoom = self.zoom })
2938
end
3039

3140

@@ -36,7 +45,6 @@ end
3645

3746
function update(self, dt)
3847
if self.enabled then
39-
4048
-- update camera and view projection after all game objects have been updated
4149
-- will jitter otherwise
4250
msg.post("#", UPDATE_CAMERA, { dt = dt })
@@ -53,13 +61,25 @@ function on_message(self, message_id, message, sender)
5361
elseif message_id == DISABLE then
5462
self.enabled = false
5563
elseif message_id == UNFOLLOW then
56-
camera.unfollow(go.get_id())
64+
self.follow = false
65+
elseif message_id == USE_PROJECTION then
66+
assert(message.projection, "You must provide a projection")
67+
self.projection = message.projection
5768
elseif message_id == FOLLOW then
58-
camera.follow(go.get_id(), message.target, message.lerp)
69+
assert(message.target, "You must provide a target")
70+
self.follow = true
71+
self.follow_target = type(message.target) == "string" and hash(message.target) or message.target
72+
self.follow_lerp = message.lerp or 1
5973
elseif message_id == DEADZONE then
60-
camera.deadzone(go.get_id(), message.left, message.top, message.right, message.bottom)
74+
self.deadzone_right = message.right or 0
75+
self.deadzone_top = message.top or 0
76+
self.deadzone_left = message.left or 0
77+
self.deadzone_bottom = message.bottom or 0
6178
elseif message_id == BOUNDS then
62-
camera.bounds(go.get_id(), message.left, message.top, message.right, message.bottom)
79+
self.bounds_right = message.right or 0
80+
self.bounds_top = message.top or 0
81+
self.bounds_left = message.left or 0
82+
self.bounds_bottom = message.bottom or 0
6383
elseif message_id == SHAKE then
6484
camera.shake(go.get_id(), message.intensity, message.duration, message.direction, function()
6585
msg.post(sender, "shake_completed")
@@ -69,7 +89,7 @@ function on_message(self, message_id, message, sender)
6989
elseif message_id == STOP_SHAKING then
7090
camera.stop_shaking(go.get_id())
7191
elseif message_id == ZOOM_TO then
92+
assert(message.zoom, "You must provide a zoom level")
7293
self.zoom = message.zoom
73-
camera.set_zoom(go.get_id(), message.zoom)
7494
end
7595
end

0 commit comments

Comments
 (0)