跳至正文
版本:bleeding-edge 🩸

重力枪

如何制作一个简单的重力枪来四处移动道具。

最终结果

提示

你现在可以在沙盒游戏模式中找到功能完备的重力枪!

代码片段

Server/Index.lua
-- 生成一些 Props 和静态网格体
--(注:静态网格体没有物理效果,因此在释放时会自动停留在原处)
local p_sphere = Prop(Vector(200, 0, 200), Rotator(), "nanos-world::SM_Sphere")
local p_cone = Prop(Vector(200, 0, 200), Rotator(), "nanos-world::SM_Cone")
local sm_cube = StaticMesh(Vector(100, 0, 200), Rotator(), "nanos-world::SM_Cube")
local sm_cylinder = StaticMesh(Vector(300, 0, 200), Rotator(), "nanos-world::SM_Cylinder")

-- 订阅客户端自定义事件,当物体被抓取/放下时触发
Events.SubscribeRemote("PickUp", function(player, object, is_grabbing)
object:SetGravityEnabled(not is_grabbing)
object:TranslateTo(object:GetLocation(), 0)
end)

-- 订阅客户端自定义事件,更新玩家正在抓取的物体的位置
Events.SubscribeRemote("UpdateObjectPosition", function(player, object, location)
object:TranslateTo(location, 0.1)
end)
Client/Index.lua
-- 全局变量
picking_object = nil
distance_trace_object = nil
distance = 200

-- 设置索引 1 处的高亮颜色
Client.SetHighlightColor(Color(0, 20, 20, 1.5), 1, HighlightMode.OnlyVisible)

-- 当玩家点击时
Input.Subscribe("MouseUp", function(key_name)

-- 如果按下的鼠标键是左键
if (key_name == "LeftMouseButton") then

-- 如果当前正抓取物体,将其放下
if (picking_object) then
-- 调用服务器重新启用重力(如果可能)并更新其最终位置
Events.CallRemote("PickUp", Reliability.Reliable, picking_object, false)

-- 禁用高亮显示
picking_object:SetHighlightEnabled(false)

picking_object = nil
return
end

-- 获取 3D 世界空间中的相机位置
local viewport_2D_center = Viewport.GetViewportSize() / 2
local viewport_3D = Viewport.DeprojectScreenToWorld(viewport_2D_center)
local start_location = viewport_3D.Position

-- 获取射线检测的终点位置(前方 5000 单位)
local trace_max_distance = 5000
local end_location = start_location + viewport_3D.Direction * trace_max_distance

-- 确定射线检测的目标碰撞通道(WorldStatic - StaticMeshes - 以及 PhysicsBody - Props)
local collision_trace = CollisionChannel.WorldStatic | CollisionChannel.PhysicsBody

-- 设置射线检测模式(要求返回实体并绘制调试线)
local trace_mode = TraceMode.ReturnEntity | TraceMode.DrawDebug

-- 执行射线检测
local trace_result = Trace.LineSingle(start_location, end_location, collision_trace, trace_mode)

-- 如果击中了物体且击中的是一个实体
if (trace_result.Success and trace_result.Entity) then

-- 设置新抓取的物体
picking_object = trace_result.Entity

-- 计算击中点与物体中心点之间的偏移量
distance_trace_object = picking_object:GetLocation() - trace_result.Location

-- 计算物体与相机之间的距离
distance = trace_result.Location:Distance(viewport_3D.Position)

-- 远程调用以禁用该物体的重力(如果有)
Events.CallRemote("PickUp", Reliability.Reliable, picking_object, true)

-- 在索引 1 上启用高亮显示
picking_object:SetHighlightEnabled(true, 1)
end
elseif (key_name == "MouseScrollUp") then
-- 如果向上滚动鼠标滑轮,增加物体与相机之间的距离
distance = distance + 25
elseif (key_name == "MouseScrollDown") then
-- 如果向下滚动鼠标滑轮,减少物体与相机之间的距离
distance = distance - 25
if (distance < 100) then distance = 100 end
end
end)

Client.Subscribe("Tick", function(delta_time)
-- 在每个刻时,根据物体距离和相机旋转角度更新物体位置
if (picking_object == nil) then return end

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

-- 获取 3D 世界空间中的相机位置
local viewport_2D_center = Viewport.GetViewportSize() / 2
local viewport_3D = Viewport.DeprojectScreenToWorld(viewport_2D_center)

-- 计算物体的新位置
-- (相机方向 * 前方“距离”单位 + 首次击中时的物体偏移量,以保持相对位置)
local end_location = (viewport_3D.Position + viewport_3D.Direction * distance) + distance_trace_object

-- 远程调用以更新其位置
Events.CallRemote("UpdateObjectPosition", Reliability.Reliable, picking_object, end_location)
end)