-- Control_Manager.script -- Input Variables local dragging = false local selection_box -- Camera Variables local rts_camera local rts_camera_size local inv_matrix -- inverse matrix cached on drag start -- Position Variables local start_pos = vmath.vector3() local prev_pos = vmath.vector3() function init(self) msg.post(".", "acquire_input_focus") -- Activates to get inputs selection_box = msg.url("#selection_box") -- Selection Box - Sprite Component msg.post(selection_box, "disable") -- Sprite Hidden rts_camera = msg.url("CameraParent#camera") -- Camera Component2 -- Gets Display Properties from System local DISPLAY_WIDTH = sys.get_config_int("display.width") local DISPLAY_HEIGHT = sys.get_config_int("display.height") -- Get Screen To World Properties rts_camera_size = vmath.vector3(DISPLAY_WIDTH, DISPLAY_HEIGHT, 0) end local function screen_to_world(x, y) -- 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 local projection = camera.get_projection(rts_camera) -- Gets the Projection Size local view = camera.get_view(rts_camera) -- Gets the View Size inv_matrix = vmath.inv(projection * view) -- Gets the Inversion -- Initialize positions local x, y = screen_to_world(action.x, action.y) -- Gets Global Position start_pos.x, start_pos.y = x, y -- Sets start_pos prev_pos.x, prev_pos.y = x, y -- Updates prev_pos -- Update Sprite Properties go.set_position(start_pos, selection_box) -- Set Position go.set(selection_box, "size", vmath.vector3(0.1, 0.1, 0)) -- Set Size(0.1) in X, Y msg.post(selection_box, "enable") -- Sprite Visable dragging = true -- Dragging True end local function selection_update_dragging(x, y) -- Runs when Dragging -- Calculate bounding box local min_x = math.min(start_pos.x, x) -- Min X local min_y = math.min(start_pos.y, y) -- Min Y local max_x = math.max(start_pos.x, x) -- Max X local max_y = math.max(start_pos.y, y) -- Max Y -- Calculates the center of the selection box local center = vmath.vector3( (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 -- Ensures a minimum size of 0.1 to prevent the sprite from collapsing local size = vmath.vector3( math.max(max_x - min_x, 0.1), -- Width math.max(max_y - min_y, 0.1), -- Height 0 -- Z Direction ) -- Set sprite properties go.set_position(center, selection_box) -- Sets Position go.set(selection_box, "size", size) -- Sets Size end function on_input(self, action_id, action) if action_id ~= hash("touch") then return end if action.pressed and not dragging then selection_update_start(action) -- Applies Sprite Properties at Start elseif action.released and dragging then dragging = false -- Dragging False msg.post(selection_box, "disable") -- Sprite Hidden elseif dragging then local x, y = screen_to_world(action.x, action.y) -- Gets Global Position -- Only update if position changed 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 selection_update_dragging(x, y) -- Applies Sprite Properties during dragging end end end