Entity Picking


Setup

This example describes method of selecting a game object from the 3D scene on the click of the mouse using collision-based picking:

  • We use collision object components to define a pickable shape for each relevant game object. This example uses 3D physics, which is enabled in the game.project file.
  • When the user clicks the mouse button, we convert screen coordinates to world coordinates and fire a raycast into the 3D world using the physics.raycast() function.
  • If the ray intersects with a collision object, the corresponding game object is considered “picked”.

The models used in this example are from Kenney’s Prototype Kit, licensed under CC0.

Scripts

entity_picking.script

local camera_math = require("example.camera_math")

--- Performs a raycast from the camera through a screen position to find an entity.
-- @param screen_x number The x-coordinate on the screen
-- @param screen_y number The y-coordinate on the screen
-- @param collision_groups table The collision groups to check against as array of hash values
-- @return table|nil The first entity hit by the ray, or nil if nothing was hit
local function pick_entity(screen_x, screen_y, collision_groups)
    local from = camera_math.screen_to_world(screen_x, screen_y, 0, "/camera#camera")
    local to = camera_math.screen_to_world(screen_x, screen_y, 100, "/camera#camera")
    local results = physics.raycast(from, to, collision_groups, { all = false })
    if not results then
        return nil
    end

    return results[1]
end

function init(self)
    -- Use the projection provided by the camera
    msg.post("@render:", "use_camera_projection")

    -- Acquire input focus to receive input events
    msg.post(".", "acquire_input_focus")

    self.input_pressed = false -- Tracks if the input is currently pressed
    self.last_input = nil      -- Stores the last input action received
    self.previous = nil        -- Keeps track of the previously highlighted entity
end

function update(self, dt)
    if not self.last_input then
        -- No input received yet
        return
    end

    local result = pick_entity(self.last_input.screen_x, self.last_input.screen_y, { hash("target") })
    if result then
        -- Store in the result table the model URL of the entity just for convenience
        result.model_url = msg.url(nil, result.id, "model")

        -- Set the tint of the entity to highlight it
        go.set(result.model_url, "tint.w", 1.5)

        -- If the input is currently pressed, move the camera to the entity
        if self.input_pressed then
            -- We want to move the camera to only the X,Y of the entity, so we get its position
            local move_to = go.get("/camera", "position")
            move_to.x = result.position.x
            move_to.y = result.position.y

            go.cancel_animations("/camera", "position")
            go.animate("/camera", "position", go.PLAYBACK_ONCE_FORWARD, move_to, go.EASING_INOUTQUAD, 0.5)
        end

        -- If the previously highlighted entity is different from the current entity, reset its tint
        if self.previous and self.previous.id ~= result.id then
            go.set(self.previous.model_url, "tint.w", 1)
        end
        self.previous = result
    else
        -- No entity was hit, so reset the tint of the previously highlighted entity
        if self.previous then
            go.set(self.previous.model_url, "tint.w", 1)
            self.previous = nil
        end
    end
end

function on_input(self, action_id, action)
    if action_id == hash("touch") then
        -- "touch" is a screen touch or mouse click. We only want to react to the press event.
        self.input_pressed = action.pressed
    elseif not action_id then
        -- If action_id is nil, it means that the action is a mouse move event.
        -- "action" contains the mouse move event data. We want to store it for later use.
        self.last_input = action
    end
end

This example was created by Artsiom Trubchyk.

DOWNLOAD

 

Do you want to see more examples? Why not write a few yourself and submit a pull request? We love contributions.

GITHUB