Get selection box working and stuff

This commit is contained in:
Joseph Ferano 2025-10-30 09:54:40 +07:00
parent ca7e43a249
commit f1208d5d88
9 changed files with 349 additions and 155 deletions

View File

@ -14,12 +14,12 @@ components {
embedded_components { embedded_components {
id: "collisionobject" id: "collisionobject"
type: "collisionobject" type: "collisionobject"
data: "type: COLLISION_OBJECT_TYPE_KINEMATIC\n" data: "type: COLLISION_OBJECT_TYPE_TRIGGER\n"
"mass: 0.0\n" "mass: 0.0\n"
"friction: 0.1\n" "friction: 0.1\n"
"restitution: 0.5\n" "restitution: 0.5\n"
"group: \"default\"\n" "group: \"unit\"\n"
"mask: \"default\"\n" "mask: \"selection\"\n"
"embedded_collision_shape {\n" "embedded_collision_shape {\n"
" shapes {\n" " shapes {\n"
" shape_type: TYPE_BOX\n" " shape_type: TYPE_BOX\n"

View File

@ -2,100 +2,280 @@ tile_set: "/main/terrain.tilesource"
layers { layers {
id: "layer1" id: "layer1"
z: 0.0 z: 0.0
cell {
x: 1
y: 0
tile: 136
}
cell {
x: 2
y: 0
tile: 137
}
cell {
x: 3
y: 0
tile: 138
}
cell {
x: 4
y: 0
tile: 139
}
cell {
x: 5
y: 0
tile: 140
}
cell {
x: 6
y: 0
tile: 137
}
cell {
x: 7
y: 0
tile: 138
}
cell {
x: 8
y: 0
tile: 139
}
cell {
x: 9
y: 0
tile: 140
}
cell {
x: 10
y: 0
tile: 137
}
cell {
x: 11
y: 0
tile: 138
}
cell {
x: 12
y: 0
tile: 139
}
cell {
x: 13
y: 0
tile: 140
}
cell {
x: 14
y: 0
tile: 137
}
cell {
x: 15
y: 0
tile: 138
}
cell {
x: 16
y: 0
tile: 139
}
cell {
x: 17
y: 0
tile: 140
}
cell {
x: 18
y: 0
tile: 141
}
cell {
x: 1
y: 1
tile: 190
}
cell {
x: 2
y: 1
tile: 119
}
cell {
x: 3
y: 1
tile: 120
}
cell {
x: 4
y: 1
tile: 121
}
cell {
x: 5
y: 1
tile: 122
}
cell {
x: 6
y: 1
tile: 119
}
cell {
x: 7
y: 1
tile: 120
}
cell {
x: 8
y: 1
tile: 121
}
cell {
x: 9
y: 1
tile: 122
}
cell {
x: 10
y: 1
tile: 119
}
cell {
x: 11
y: 1
tile: 120
}
cell {
x: 12
y: 1
tile: 121
}
cell {
x: 13
y: 1
tile: 122
}
cell {
x: 14
y: 1
tile: 119
}
cell {
x: 15
y: 1
tile: 120
}
cell {
x: 16
y: 1
tile: 121
}
cell {
x: 17
y: 1
tile: 122
}
cell {
x: 18
y: 1
tile: 123
}
cell { cell {
x: 1 x: 1
y: 2 y: 2
tile: 90 tile: 100
} }
cell { cell {
x: 2 x: 2
y: 2 y: 2
tile: 91 tile: 101
} }
cell { cell {
x: 3 x: 3
y: 2 y: 2
tile: 92 tile: 102
} }
cell { cell {
x: 4 x: 4
y: 2 y: 2
tile: 93 tile: 103
} }
cell { cell {
x: 5 x: 5
y: 2 y: 2
tile: 94 tile: 104
} }
cell { cell {
x: 6 x: 6
y: 2 y: 2
tile: 91 tile: 101
} }
cell { cell {
x: 7 x: 7
y: 2 y: 2
tile: 92 tile: 102
} }
cell { cell {
x: 8 x: 8
y: 2 y: 2
tile: 93 tile: 103
} }
cell { cell {
x: 9 x: 9
y: 2 y: 2
tile: 94 tile: 104
} }
cell { cell {
x: 10 x: 10
y: 2 y: 2
tile: 91 tile: 101
} }
cell { cell {
x: 11 x: 11
y: 2 y: 2
tile: 92 tile: 102
} }
cell { cell {
x: 12 x: 12
y: 2 y: 2
tile: 93 tile: 103
} }
cell { cell {
x: 13 x: 13
y: 2 y: 2
tile: 94 tile: 104
} }
cell { cell {
x: 14 x: 14
y: 2 y: 2
tile: 91 tile: 101
} }
cell { cell {
x: 15 x: 15
y: 2 y: 2
tile: 92 tile: 102
} }
cell { cell {
x: 16 x: 16
y: 2 y: 2
tile: 93 tile: 103
} }
cell { cell {
x: 17 x: 17
y: 2 y: 2
tile: 94 tile: 104
} }
cell { cell {
x: 18 x: 18
y: 2 y: 2
tile: 95 tile: 105
} }
cell { cell {
x: 1 x: 1
y: 3 y: 3
tile: 72 tile: 82
} }
cell { cell {
x: 2 x: 2
@ -180,12 +360,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 3 y: 3
tile: 77 tile: 87
} }
cell { cell {
x: 1 x: 1
y: 4 y: 4
tile: 54 tile: 64
} }
cell { cell {
x: 2 x: 2
@ -270,12 +450,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 4 y: 4
tile: 59 tile: 69
} }
cell { cell {
x: 1 x: 1
y: 5 y: 5
tile: 36 tile: 46
} }
cell { cell {
x: 2 x: 2
@ -360,12 +540,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 5 y: 5
tile: 41 tile: 51
} }
cell { cell {
x: 1 x: 1
y: 6 y: 6
tile: 18 tile: 28
} }
cell { cell {
x: 2 x: 2
@ -450,12 +630,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 6 y: 6
tile: 23 tile: 33
} }
cell { cell {
x: 1 x: 1
y: 7 y: 7
tile: 72 tile: 82
} }
cell { cell {
x: 2 x: 2
@ -540,12 +720,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 7 y: 7
tile: 77 tile: 87
} }
cell { cell {
x: 1 x: 1
y: 8 y: 8
tile: 54 tile: 64
} }
cell { cell {
x: 2 x: 2
@ -630,12 +810,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 8 y: 8
tile: 59 tile: 69
} }
cell { cell {
x: 1 x: 1
y: 9 y: 9
tile: 36 tile: 46
} }
cell { cell {
x: 2 x: 2
@ -720,12 +900,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 9 y: 9
tile: 41 tile: 51
} }
cell { cell {
x: 1 x: 1
y: 10 y: 10
tile: 18 tile: 28
} }
cell { cell {
x: 2 x: 2
@ -810,12 +990,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 10 y: 10
tile: 23 tile: 33
} }
cell { cell {
x: 1 x: 1
y: 11 y: 11
tile: 72 tile: 82
} }
cell { cell {
x: 2 x: 2
@ -900,12 +1080,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 11 y: 11
tile: 77 tile: 87
} }
cell { cell {
x: 1 x: 1
y: 12 y: 12
tile: 54 tile: 64
} }
cell { cell {
x: 2 x: 2
@ -990,12 +1170,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 12 y: 12
tile: 59 tile: 69
} }
cell { cell {
x: 1 x: 1
y: 13 y: 13
tile: 36 tile: 46
} }
cell { cell {
x: 2 x: 2
@ -1080,12 +1260,12 @@ layers {
cell { cell {
x: 18 x: 18
y: 13 y: 13
tile: 41 tile: 51
} }
cell { cell {
x: 1 x: 1
y: 14 y: 14
tile: 18 tile: 28
} }
cell { cell {
x: 2 x: 2
@ -1170,97 +1350,97 @@ layers {
cell { cell {
x: 18 x: 18
y: 14 y: 14
tile: 23 tile: 33
} }
cell { cell {
x: 1 x: 1
y: 15 y: 15
tile: 0 tile: 10
} }
cell { cell {
x: 2 x: 2
y: 15 y: 15
tile: 1 tile: 11
} }
cell { cell {
x: 3 x: 3
y: 15 y: 15
tile: 2 tile: 12
} }
cell { cell {
x: 4 x: 4
y: 15 y: 15
tile: 3 tile: 13
} }
cell { cell {
x: 5 x: 5
y: 15 y: 15
tile: 4 tile: 14
} }
cell { cell {
x: 6 x: 6
y: 15 y: 15
tile: 1 tile: 11
} }
cell { cell {
x: 7 x: 7
y: 15 y: 15
tile: 2 tile: 12
} }
cell { cell {
x: 8 x: 8
y: 15 y: 15
tile: 3 tile: 13
} }
cell { cell {
x: 9 x: 9
y: 15 y: 15
tile: 4 tile: 14
} }
cell { cell {
x: 10 x: 10
y: 15 y: 15
tile: 1 tile: 11
} }
cell { cell {
x: 11 x: 11
y: 15 y: 15
tile: 2 tile: 12
} }
cell { cell {
x: 12 x: 12
y: 15 y: 15
tile: 3 tile: 13
} }
cell { cell {
x: 13 x: 13
y: 15 y: 15
tile: 4 tile: 14
} }
cell { cell {
x: 14 x: 14
y: 15 y: 15
tile: 1 tile: 11
} }
cell { cell {
x: 15 x: 15
y: 15 y: 15
tile: 2 tile: 12
} }
cell { cell {
x: 16 x: 16
y: 15 y: 15
tile: 3 tile: 13
} }
cell { cell {
x: 17 x: 17
y: 15 y: 15
tile: 4 tile: 14
} }
cell { cell {
x: 18 x: 18
y: 15 y: 15
tile: 7 tile: 15
} }
} }
material: "/builtins/materials/tile_map.material" material: "/builtins/materials/tile_map.material"

View File

@ -16,11 +16,11 @@ instances {
} }
} }
instances { instances {
id: "knight-blue1" id: "knight-blue-hello"
prototype: "/main/knight-blue.go" prototype: "/main/knight-blue.go"
position { position {
x: 488.0 x: 511.0
y: 162.0 y: 256.0
} }
component_properties { component_properties {
id: "knight" id: "knight"
@ -67,8 +67,8 @@ instances {
id: "knight-blue4" id: "knight-blue4"
prototype: "/main/knight-blue.go" prototype: "/main/knight-blue.go"
position { position {
x: 232.0 x: 144.0
y: 414.0 y: 424.0
} }
component_properties { component_properties {
id: "knight" id: "knight"
@ -97,10 +97,11 @@ instances {
} }
instances { instances {
id: "selection_box" id: "selection_box"
prototype: "/scripts/selection_box.go" prototype: "/main/selection_box.go"
position { }
z: 1.0 instances {
} id: "-Game"
prototype: "/main/Game.go"
} }
scale_along_z: 0 scale_along_z: 0
embedded_instances { embedded_instances {

35
main/selection_box.go Normal file
View File

@ -0,0 +1,35 @@
components {
id: "selection_box"
component: "/main/selection_box.sprite"
}
components {
id: "ControllerManager"
component: "/scripts/ControllerManager.script"
}
embedded_components {
id: "collisionobject"
type: "collisionobject"
data: "type: COLLISION_OBJECT_TYPE_TRIGGER\n"
"mass: 0.0\n"
"friction: 0.1\n"
"restitution: 0.5\n"
"group: \"selection\"\n"
"mask: \"unit\"\n"
"embedded_collision_shape {\n"
" shapes {\n"
" shape_type: TYPE_BOX\n"
" position {\n"
" }\n"
" rotation {\n"
" }\n"
" index: 0\n"
" count: 3\n"
" id: \"box\"\n"
" }\n"
" data: 3.0\n"
" data: 3.0\n"
" data: 0.5\n"
"}\n"
"locked_rotation: true\n"
""
}

View File

@ -1,2 +0,0 @@
default_animation: ""
material: "/builtins/materials/sprite.material"

View File

@ -1,62 +1,60 @@
-- Control_Manager.script local utils = require "scripts.utils"
-- Input Variables -- Input Variables
local dragging = false local dragging = false
local selection_box
-- Camera Variables -- Camera Variables
local rts_camera
local rts_camera_size
local inv_matrix -- inverse matrix cached on drag start local inv_matrix -- inverse matrix cached on drag start
-- Position Variables -- Position Variables
local start_pos = vmath.vector3() local start_pos = vmath.vector3()
local prev_pos = vmath.vector3() local prev_pos = vmath.vector3()
function on_reload(self)
-- TODO: Investigate why using `local` wasn't working on reload
-- msg.post(".", "acquire_input_focus")
-- -- NOTE: For some reason I need to also call this on_reload, or else it gets niled out
-- self.rts_camera = msg.url("CameraParent#camera")
-- local DISPLAY_WIDTH = sys.get_config_int("display.width")
-- local DISPLAY_HEIGHT = sys.get_config_int("display.height")
-- self.rts_camera_size = vmath.vector3(DISPLAY_WIDTH, DISPLAY_HEIGHT, 0)
self.selection_collider = msg.url("/selection_box#collisionobject")
end
function init(self) function init(self)
msg.post(".", "acquire_input_focus") -- Activates to get inputs msg.post(".", "acquire_input_focus") -- Activates to get inputs
selection_box = msg.url("#selection_box") -- Selection Box - Sprite Component self.selection_box = msg.url("#selection_box") -- Selection Box - Sprite Component
msg.post(selection_box, "disable") -- Sprite Hidden msg.post(self.selection_box, "disable") -- Sprite Hidden
rts_camera = msg.url("CameraParent#camera") -- Camera Component2 self.rts_camera = msg.url("CameraParent#camera") -- Camera Component2
-- Gets Display Properties from System self.selection_collider = msg.url("#selection_box/collisionobject")
-- Gets Display Properties from System
local DISPLAY_WIDTH = sys.get_config_int("display.width") local DISPLAY_WIDTH = sys.get_config_int("display.width")
local DISPLAY_HEIGHT = sys.get_config_int("display.height") local DISPLAY_HEIGHT = sys.get_config_int("display.height")
-- Get Screen To World Properties -- Get Screen To World Properties
rts_camera_size = vmath.vector3(DISPLAY_WIDTH, DISPLAY_HEIGHT, 0) self.rts_camera_size = vmath.vector3(DISPLAY_WIDTH, DISPLAY_HEIGHT, 0)
end end
local function screen_to_world(x, y) local function selection_update_start(self, action) -- Runs once when pressed
-- Convert screen coordinates to the [-1, 1] range (Normalized Device Coordinates)
local ndc_x = (x / rts_camera_size.x) * 2 - 1 -- X Ranges From [-1 (left) to 1 (right)]
local ndc_y = (y / rts_camera_size.y) * 2 - 1 -- Y Ranges From [-1 (bottom) to 1 (top)]
-- Applies the inverse camera transformation to convert NDC to world coordinates.
local world = inv_matrix * vmath.vector4(ndc_x, ndc_y, 0, 1) -- NDC position as a 4D vector
-- Converts homogeneous coordinates (x, y, z, w) to 3D world coordinates
-- No effect for orthographic(2D). Depth Scaling For perspective cameras.
return world.x / world.w, world.y / world.w -- Return X, Y
end
local function selection_update_start(action) -- Runs once when pressed
-- Updates Screen_to_World Variables -- Updates Screen_to_World Variables
local projection = camera.get_projection(rts_camera) -- Gets the Projection Size local projection = camera.get_projection(self.rts_camera) -- Gets the Projection Size
local view = camera.get_view(rts_camera) -- Gets the View Size local view = camera.get_view(self.rts_camera) -- Gets the View Size
inv_matrix = vmath.inv(projection * view) -- Gets the Inversion inv_matrix = vmath.inv(projection * view) -- Gets the Inversion
-- Initialize positions -- Initialize positions
local x, y = screen_to_world(action.x, action.y) -- Gets Global Position local x, y, _ = utils.screen_to_world(action.x, action.y, 0, "/CameraParent#camera") -- Gets Global Position
start_pos.x, start_pos.y = x, y -- Sets start_pos start_pos.x, start_pos.y = x, y -- Sets start_pos
prev_pos.x, prev_pos.y = x, y -- Updates prev_pos prev_pos.x, prev_pos.y = x, y -- Updates prev_pos
-- Update Sprite Properties -- Update Sprite Properties
go.set_position(start_pos, selection_box) -- Set Position go.set_position(start_pos, self.selection_box) -- Set Position
go.set(selection_box, "size", vmath.vector3(0.1, 0.1, 0)) -- Set Size(0.1) in X, Y go.set(self.selection_box, "size", vmath.vector3(0.1, 0.1, -1)) -- Set Size(0.1) in X, Y
msg.post(selection_box, "enable") -- Sprite Visable msg.post(self.selection_box, "enable") -- Sprite Visable
msg.post("/selection_box#collisionobject", "enable")
dragging = true -- Dragging True dragging = true -- Dragging True
end end
local function selection_update_dragging(x, y) -- Runs when Dragging local function selection_update_dragging(self, x, y) -- Runs when Dragging
-- Calculate bounding box -- Calculate bounding box
local min_x = math.min(start_pos.x, x) -- Min X local min_x = math.min(start_pos.x, x) -- Min X
local min_y = math.min(start_pos.y, y) -- Min Y local min_y = math.min(start_pos.y, y) -- Min Y
@ -64,41 +62,38 @@ local function selection_update_dragging(x, y) -- Runs when Dragging
local max_y = math.max(start_pos.y, y) -- Max Y local max_y = math.max(start_pos.y, y) -- Max Y
-- Calculates the center of the selection box -- Calculates the center of the selection box
local center = vmath.vector3( local center = vmath.vector3((min_x + max_x) * 0.5, (min_y + max_y) * 0.5, 0)
(min_x + max_x) * 0.5, -- X Direction
(min_y + max_y) * 0.5, -- Y Direction
0 -- Z Direction
)
-- Calculate width/height of the selection box from drag distance -- Calculate width/height of the selection box from drag distance
-- Ensures a minimum size of 0.1 to prevent the sprite from collapsing -- Ensures a minimum size of 0.1 to prevent the sprite from collapsing
local size = vmath.vector3( local size = vmath.vector3(math.max(max_x - min_x, 0.1),math.max(max_y - min_y, 0.1),0)
math.max(max_x - min_x, 0.1), -- Width
math.max(max_y - min_y, 0.1), -- Height
0 -- Z Direction
)
-- Set sprite properties -- Set sprite properties
go.set_position(center, selection_box) -- Sets Position go.set_position(center, self.selection_box) -- Sets Position
go.set(selection_box, "size", size) -- Sets Size go.set(self.selection_box, "size", size) -- Sets Size
-- go.set(self.selection_collider, "size", size)
local box = physics.get_shape("/selection_box#collisionobject", "box")
box.dimensions = size
physics.set_shape("/selection_box#collisionobject", "box", box)
end end
function on_input(self, action_id, action) function on_input(self, action_id, action)
if action_id ~= hash("touch") then return end if action_id ~= hash("touch") then return end
if action.pressed and not dragging then if action.pressed and not dragging then
selection_update_start(action) -- Applies Sprite Properties at Start selection_update_start(self, action) -- Applies Sprite Properties at Start
elseif action.released and dragging then elseif action.released and dragging then
dragging = false -- Dragging False dragging = false -- Dragging False
msg.post(selection_box, "disable") -- Sprite Hidden msg.post(self.selection_box, "disable") -- Sprite Hidden
msg.post("/selection_box#collisionobject", "disable")
elseif dragging then elseif dragging then
local x, y = screen_to_world(action.x, action.y) -- Gets Global Position local x, y = utils.screen_to_world(action.x, action.y, 0, "/CameraParent#camera") -- Gets Global Position
-- Only update if position changed -- Only update if position changed
if math.abs(x - prev_pos.x) > 0.001 or math.abs(y - prev_pos.y) > 0.001 then if math.abs(x - prev_pos.x) > 0.001 or math.abs(y - prev_pos.y) > 0.001 then
prev_pos.x, prev_pos.y = x, y -- Updates prev_pos prev_pos.x, prev_pos.y = x, y -- Updates prev_pos
selection_update_dragging(x, y) -- Applies Sprite Properties during dragging selection_update_dragging(self, x, y) -- Applies Sprite Properties during dragging
end end
end end
end end

View File

@ -1,7 +1,7 @@
local utils = require "scripts.utils"
function init(self) function init(self)
-- Add initialization code here msg.post(".", "acquire_input_focus")
-- Learn more: https://defold.com/manuals/script/
-- Remove this function if not needed
end end
function final(self) function final(self)
@ -31,33 +31,18 @@ function on_message(self, message_id, message, sender)
-- Remove this function if not needed -- Remove this function if not needed
end end
local function screen_to_world(x, y, z, camera_id)
local projection = camera.get_projection(camera_id)
local view = camera.get_view(camera_id)
local w, h = window.get_size()
-- https://defold.com/manuals/camera/#converting-mouse-to-world-coordinates
local inv = vmath.inv(projection * view)
x = (2 * x / w) - 1
y = (2 * y / h) - 1
z = (2 * z) - 1
local x1 = x * inv.m00 + y * inv.m01 + z * inv.m02 + inv.m03
local y1 = x * inv.m10 + y * inv.m11 + z * inv.m12 + inv.m13
local z1 = x * inv.m20 + y * inv.m21 + z * inv.m22 + inv.m23
return x1, y1, z1
end
function on_input(self, action_id, action) function on_input(self, action_id, action)
if action_id == hash("left-click") and action.pressed then if action_id == hash("touch") and action.pressed then
self.next_state = "moving" self.next_state = "moving"
local tx, ty, _ = screen_to_world(action.screen_x, action.screen_y, 0, "/CameraParent#camera") local tx, ty, _ = utils.screen_to_world(action.screen_x, action.screen_y, 0, "/CameraParent#camera")
self.target_pos = vmath.vector3(tx, ty, 0) self.target_pos = vmath.vector3(tx, ty, 0)
elseif action_id == hash("right-click") then elseif action_id == hash("right-click") then
-- msg.post(receiver, message_id, message)
end end
end end
function on_reload(self) function on_reload(self)
msg.post(".", "acquire_input_focus")
-- Add reload-handling code here -- Add reload-handling code here
-- Learn more: https://defold.com/manuals/hot-reload/ -- Learn more: https://defold.com/manuals/hot-reload/
-- Remove this function if not needed -- Remove this function if not needed

View File

@ -5,6 +5,7 @@ function init(self)
self.target_pos = vmath.vector3() self.target_pos = vmath.vector3()
self.state = "idling" self.state = "idling"
self.next_state = "idling" self.next_state = "idling"
self.selected = false
end end
local function move_to_target(self, target, dt) local function move_to_target(self, target, dt)
@ -31,9 +32,16 @@ function update(self, dt)
end end
function on_message(self, message_id, message, sender) function on_message(self, message_id, message, sender)
-- Add message-handling code here if message_id == hash("collision_response") then
-- Learn more: https://defold.com/manuals/message-passing/ print(go.get_id())
-- Remove this function if not needed end
if message_id == hash("trigger_response") then
if message.enter then
self.selected = true
else
self.selected = false
end
end
end end
function final(self) function final(self)

View File

@ -1,8 +0,0 @@
components {
id: "selection_box"
component: "/main/selection_box.sprite"
}
components {
id: "ControllerManager"
component: "/scripts/ControllerManager.script"
}