游戏开发中的物理之射线投射

简介: 游戏开发中的物理之射线投射

介绍


游戏开发中最常见的任务之一是投射光线(或自定义形状的物体)并检查其撞击。这样就可以进行复杂的行为,AI等。本教程将说明如何在2D和3D中执行此操作。


Godot将所有低级游戏信息存储在服务器中,而场景只是前端。因此,射线投射通常是较低级别的任务。对于简单的射线广播,诸如RayCast和RayCast2D之类的节点 将起作用,因为它们将在每一帧中返回射线广播的结果。


但是,很多时候,光线投射必须是一个更具交互性的过程,因此必须存在一种通过代码进行光线投射的方法。


空间


在物理世界中,戈多特将所有低级碰撞和物理信息存储在一个空间中。可以通过访问CanvasItem.get_world_2d()。space获取当前的2d空间(用于2D物理) 。对于3D,它是Spatial.get_world()。space。


生成的空间RID可以分别在 PhysicsServer和 Physics2DServer中用于3D和2D。


进入空间


Godot物理默认情况下与游戏逻辑在同一线程中运行,但可以设置为在单独的线程上运行以更有效地工作。因此,唯一安全的访问空间时间是在 Node._physics_process() 回调期间。由于空间被锁定,从此功能外部访问它可能会导致错误。


要对物理空间执行查询, 必须使用Physics2DDirectSpaceState 和PhysicsDirectSpaceState。


在2D中使用以下代码:


public override void _PhysicsProcess(float delta)
{
    var spaceRid = GetWorld2d().Space;
    var spaceState = Physics2DServer.SpaceGetDirectState(spaceRid);
}

或更直接地:


public override void _PhysicsProcess(float delta)
{
    var spaceState = GetWorld2d().DirectSpaceState;
}

在3D中:


public override void _PhysicsProcess(float delta)
{
    var spaceState = GetWorld().DirectSpaceState;
}

Raycast查询


为了执行2D射线广播查询, 可以使用Physics2DDirectSpaceState.intersect_ray()方法 。例如:


public override void _PhysicsProcess(float delta)
{
    var spaceState = GetWorld2d().DirectSpaceState;
    // use global coordinates, not local to node
    var result = spaceState.IntersectRay(new Vector2(), new Vector2(50, 100));
}


结果是一个字典。如果射线没有击中任何东西,则字典将为空。如果确实撞到了东西,它将包含碰撞信息:


if (result.Count > 0)
    GD.Print("Hit at point: ", result["position"]);

result发生碰撞时的词典包含以下数据:


{
   position: Vector2 # point in world space for collision
   normal: Vector2 # normal in world space for collision
   collider: Object # Object collided or null (if unassociated)
   collider_id: ObjectID # Object it collided against
   rid: RID # RID it collided against
   shape: int # shape index of collider
   metadata: Variant() # metadata of collider
}


使用Vector3坐标,数据在3D空间中相似。


碰撞异常


射线投射的一个常见用例是使角色能够收集有关其周围世界的数据。这样做的一个问题是,同一个角色具有对撞机,因此,光线将仅检测其父级的对撞机,如下图所示:

image.png



为了避免自相交,该intersect_ray()函数可以采用可选的第三个参数,该参数是一组异常。这是如何从KinematicBody2D或任何其他碰撞对象节点使用它的示例:


class Body : KinematicBody2D
{
    public override void _PhysicsProcess(float delta)
    {
        var spaceState = GetWorld2d().DirectSpaceState;
        var result = spaceState.IntersectRay(globalPosition, enemyPosition, new object[] { this });
    }
}


异常数组可以包含对象或RID。


防撞面罩


尽管exception方法可以很好地排除父正文,但是如果您需要大量和/或动态的exception列表,它将变得非常不便。在这种情况下,使用碰撞层/遮罩系统效率更高。


可选的第四个参数intersect_ray()是碰撞蒙版。例如,要使用与父实体相同的蒙版,请使用collision_mask 成员变量:


class Body : KinematicBody2D
{
    public override void _PhysicsProcess(float delta)
    {
        var spaceState = GetWorld2d().DirectSpaceState;
        var result = spaceState.IntersectRay(globalPosition, enemyPosition,
                        new object[] { this }, CollisionMask);
    }
}


有关如何设置碰撞遮罩的详细信息,请参见代码示例。


屏幕上的3D射线投射


将光线从屏幕投射到3D物理空间对于拾取对象很有用。不需要这样做,因为 CollisionObject 有一个“ input_event”信号,可以让您知道何时单击它,但是如果有手动操作的愿望,请按以下步骤操作。


要从屏幕投射光线,您需要一个Camera 节点。ACamera可以采用两种投影模式:透视和正交。因此,必须同时获得射线的起点和方向。这是因为origin在正交模式下会normal发生变化,而在透视模式下会 发生变化:

image.png



要使用相机获取它,可以使用以下代码:


private const float rayLength = 1000;
public override void _Input(InputEvent @event)
{
    if (@event is InputEventMouseButton eventMouseButton && eventMouseButton.Pressed && eventMouseButton.ButtonIndex == 1)
    {
        var camera = (Camera)GetNode("Camera");
        var from = camera.ProjectRayOrigin(eventMouseButton.Position);
        var to = from + camera.ProjectRayNormal(eventMouseButton.Position) * rayLength;
    }
}


请记住,在期间_input(),该空间可能被锁定,因此实际上该查询应在中运行_physics_process()。


目录
相关文章
|
4月前
|
算法
互动游戏解决遇到问题之基于射线投射寻路算法的问题如何解决
互动游戏解决遇到问题之基于射线投射寻路算法的问题如何解决
|
4月前
|
机器学习/深度学习
第5章-着色基础-5.2-光源
第5章-着色基础-5.2-光源
31 0
|
算法框架/工具
【物理】模拟粒子在电场和磁场中的轨迹研究(Matlab代码实现)
【物理】模拟粒子在电场和磁场中的轨迹研究(Matlab代码实现)
112 0
|
开发工具
如何做一个俄罗斯方块4:形状碰撞检测(上)
在游戏开发中,我们所说的“碰撞”经常指的是物理碰撞,什么是物理碰撞呢?一般的在游戏开发工具中都会包含一个叫做“物理引擎”的东西,它的作用就是在游戏中模拟出现实中的物理效果。例如,我们扔一个东西,这个东西会因为重力而下落,最终落到地上,与地面发生碰撞。在游戏中,我们可以借助物理引擎,来模拟出东西下落掉到地面上的效果。当东西掉到地面上时,我们就说这个东西与地面发生了碰撞。
319 0
如何做一个俄罗斯方块5:形状碰撞检测(下)
其实,两侧的碰撞判断跟我们上一节讲过的向下移动的碰撞判断原理是一样的,向下碰撞检测的是每一个方块下方的位置是否有其它方块,那么向左/右碰撞检测的就是每个方块左/右侧的位置是否有其他的方块。
325 0
|
算法 数据可视化
【视觉高级篇】25 # 如何用法线贴图模拟真实物体表面
【视觉高级篇】25 # 如何用法线贴图模拟真实物体表面
178 0
【视觉高级篇】25 # 如何用法线贴图模拟真实物体表面
碰撞检测——碰撞器和物理材质
碰撞检测——碰撞器和物理材质
223 0
碰撞检测——碰撞器和物理材质
|
图形学
Unity射线检测3d,2D,正交透视。
使用射线功能制作点击物体获取物体名字。可以做简单点击相应。 3D射线检测,Camera 在正交模式与透视模式皆可使用 void Update() { Ray ray = Camera.main.ScreenPointToRay(Input.
1943 0
游戏开发中的物理之刚体
游戏开发中的物理之刚体
139 0