Skip to content

Gravity Gun

Older/Work In Progress Page

  • This page was initially written for an older version of Helix, has not been updated, and may be out of date!
  • Contributions are more than welcome (see buttons at the top right of the page).

How to create a simple Gravity Gun to move Props around.

Final result#

Code Snippet#

Add the following code to Server/Index.lua file inside the package.

Server/Index.lua
-- Spawns some Props and Static Meshes
-- (note: Static Meshes don't have physics so they will freeze where released automatically)
local p_sphere = Prop(Vector(200, 0, 200), Rotator(), "helix::SM_Sphere")
local p_cone = Prop(Vector(200, 0, 200), Rotator(), "helix::SM_Cone")
local sm_cube = StaticMesh(Vector(100, 0, 200), Rotator(), "helix::SM_Cube")
local sm_cylinder = StaticMesh(Vector(300, 0, 200), Rotator(), "helix::SM_Cylinder")

-- Subscribe for Client's custom event, for when the object is grabbed/dropped
Events.SubscribeRemote("PickUp", function(player, object, is_grabbing)
    object:SetGravityEnabled(not is_grabbing)
    object:TranslateTo(object:GetLocation(), 0)
end)

-- Subscribe for Client's custom event, to update the position of the object he is grabbing
Events.SubscribeRemote("UpdateObjectPosition", function(player, object, location)
    object:TranslateTo(location, 0.1)
end)
Client/Index.lua
-- Global Variables
picking_object = nil
distance_trace_object = nil
distance = 200

-- Sets the color of Highlighing at index 1
Client.SetHighlightColor(Color(0, 20, 20, 1.5), 1, HighlightMode.OnlyVisible)

-- When Player clicks
Input.Subscribe("MouseUp", function(key_name)

    -- If mouse was left button
    if (key_name == "LeftMouseButton") then

        -- If is grabbing something, drop it
        if (picking_object) then
            -- Calls server to re-enable gravity (if possible) and update it's last position
            Events.CallRemote("PickUp", picking_object, false)

            -- Disables the highlight
            picking_object:SetHighlightEnabled(false)

            picking_object = nil
            return
        end

        -- Get the camera location in 3D World Space
        local viewport_2D_center = Viewport.GetViewportSize() / 2
        local viewport_3D = Viewport.DeprojectScreenToWorld(viewport_2D_center)
        local start_location = viewport_3D.Position

        -- Gets the end location of the trace (5000 units ahead)
        local trace_max_distance = 5000
        local end_location = viewport_3D.Position + viewport_3D.Direction * trace_max_distance

        -- Determine at which object we will be tracing for (WorldStatic - StaticMeshes - and PhysicsBody - Props)
        local collision_trace = CollisionChannel.WorldStatic | CollisionChannel.PhysicsBody

        -- Sets the trace modes (we want it to return Entity and Draws a Debug line)
        local trace_mode = TraceMode.ReturnEntity | TraceMode.DrawDebug

        -- Do the Trace
        local trace_result = Trace.LineSingle(start_location, end_location, collision_trace, trace_mode)

        -- If hit something and hit an Entity
        if (trace_result.Success and trace_result.Entity) then

            -- Sets the new picked up object
            picking_object = trace_result.Entity

            -- Calculates the offset of the hit and the center of the object
            distance_trace_object = picking_object:GetLocation() - trace_result.Location

            -- Calculates the distance of the object and the camera
            distance = trace_result.Location:Distance(viewport_3D.Position)

            -- Calls remote to disable gravity of this object (if has)
            Events.CallRemote("PickUp", picking_object, true)

            -- Enable Highlighting on index 1
            picking_object:SetHighlightEnabled(true, 1)
        end

    end
end)

Input.Subscribe("MouseScroll", function(mouse_x, mouse_y, delta)
    -- If mouse scroll, updates the Distance of the object from the camera
    localdistance = distance + delta * 55
    if (distance < 100) then distance = 100 end
end)


Client.Subscribe("Tick", function(delta_time)
    -- On Tick, updates the Position of the object, based on it's distance and camera rotation
    if (picking_object == nil) then return end

    local player = Client.GetLocalPlayer()
    if (player == nil) then return end

    -- Get the camera location in 3D World Space
    local viewport_2D_center = Viewport.GetViewportSize() / 2
    local viewport_3D = Viewport.DeprojectScreenToWorld(viewport_2D_center)
    local start_location = viewport_3D.Position

    -- Gets the new object location
    -- (camera direction * 'distance' units ahead + object offset from first Hit to keep it relative)
    local end_location = (viewport_3D.Position + viewport_3D.Direction * distance) + distance_trace_object

    -- Calls remote to update it's location
    Events.CallRemote("UpdateObjectPosition", picking_object, end_location)
end)