游戏开发中的物理介绍

举报
海拥 发表于 2021/12/04 15:27:59 2021/12/04
【摘要】 🌊 作者主页:海拥🌊 简介:🏆CSDN全栈领域优质创作者、🥇HDZ核心组成员、🥈蝉联C站周榜前十🌊 粉丝福利:粉丝群 每周送六本书,不定期送各种小礼品,往期获奖公布在游戏开发中,您通常需要知道游戏中的两个对象何时相交或接触。这就是所谓的碰撞检测。当检测到碰撞时,您通常希望发生某些事情。这就是所谓的碰撞响应。Godot在2D和3D中提供了许多碰撞对象,以提供碰撞检测和响应。试图确定...

🌊 作者主页:海拥
🌊 简介:🏆CSDN全栈领域优质创作者、🥇HDZ核心组成员、🥈蝉联C站周榜前十
🌊 粉丝福利:粉丝群 每周送六本书,不定期送各种小礼品,往期获奖公布

在游戏开发中,您通常需要知道游戏中的两个对象何时相交或接触。这就是所谓的碰撞检测。当检测到碰撞时,您通常希望发生某些事情。这就是所谓的碰撞响应

Godot在2D和3D中提供了许多碰撞对象,以提供碰撞检测和响应。试图确定要为您的项目使用哪个选项可能会造成混淆。如果您了解每个工作原理以及各自的优缺点,则可以避免问题并简化开发。

在本指南中,您将学习:

  • 戈多的四种碰撞对象类型
  • 每个碰撞对象如何工作
  • 什么时候以及为什么要选择一种而不是另一种

==注意==

本文档的示例将使用2D对象。每个2D物理对象和碰撞形状在3D中具有直接等效的功能,并且在大多数情况下,它们的工作方式几乎相同。

碰撞对象

Godot提供了四种物理体,扩展了CollisionObject2D:

  • Area2D

Area2D节点提供检测和影响。它们可以检测物体何时重叠,并可以在物体进入或离开时发出信号。Area2D 还可以使用an来覆盖定义区域中的物理特性,例如重力或阻尼。
其他三个主体扩展了PhysicsBody2D:

  • StaticBody2D

静态物体是物理引擎不会移动的物体。它参与碰撞检测,但不会响应碰撞而移动。它们最常用于环境中的对象或不需要任何动态行为的对象。

  • RigidBody2D

这是实现模拟2D物理的节点。您无需RigidBody2D直接控制a ,而是要对其施加力(重力,脉冲等),然后物理引擎将计算最终的运动。阅读更多有关使用刚体的信息。

  • KinematicBody2D

提供碰撞检测但没有物理学的物体。所有运动和碰撞响应都必须用代码实现。
碰撞形状
物理物体可以将任意数量的Shape2D对象作为子对象。这些形状用于定义对象的碰撞范围并检测与其他对象的接触。

==注意==

为了检测碰撞,Shape2D必须至少分配一个对象。

分配形状的最常见方法是添加CollisionShape2D 或CollisionPolygon2D作为对象的子级。这些节点允许您直接在编辑器工作区中绘制形状。

重要

注意不要在编辑器中缩放碰撞形状。 检查器中的“比例”属性应保留为(1,1)。
更改碰撞形状的大小时,应始终使用大小控制柄,而不是Node2D比例控制柄。 缩放形状会导致意外的碰撞行为。

../../_images/player_coll_shape1.png

物理过程回调

物理引擎可以产生多个线程以提高性能,因此它最多可以使用一个完整的帧来处理物理。 因此,对于当前帧,身体的状态变量(例如位置或线速度)的值可能不准确。

为了避免这种不准确性,任何需要访问人体属性的代码都应在Node._physics_process() 回调中运行,该回调在每个物理步骤之前以恒定帧速率(默认为每秒60次)被调用。该方法将被传递一个delta 参数,该参数是一个浮点数,它等于自上一步以来经过的时间(以 秒为单位)。当使用默认的60 Hz物理更新速率时,通常等于0.01666…(但不总是如此,请参见下文)。

==注意==

建议始终delta在物理计算中使用相关参数,以便在您更改物理更新率或玩家的设备无法跟上时,游戏能够正确运行。

碰撞层和蒙版

碰撞层系统是最强大但经常被误解的碰撞特征之一。该系统使您可以在各种对象之间建立复杂的交互。关键概念是图层 和蒙版。每个CollisionObject2D都有可与之交互的20个不同的物理层。

让我们依次查看每个属性:

  • 碰撞层

这描述了对象出现在的层。默认情况下,所有实体都在layer上1。

  • 碰撞面罩

这描述了身体将扫描碰撞的层。如果对象不在遮罩层之一中,则主体将忽略它。默认情况下,所有实体都扫描layer 1。
这些属性可以通过代码或在检查器中编辑来配置。

跟踪每个图层的用途可能很困难,因此您可能会发现为使用的图层分配名称很有用。可以在项目设置->图层名称中分配名称。

../../_images/physics_layer_names.png

GUI示例

游戏中有四种节点类型:墙,玩家,敌人和硬币。玩家和敌人都应与墙碰撞。播放器节点应同时检测到与敌人和硬币的碰撞,但敌人和硬币应互相忽略。

首先命名第1-4层“墙”,“玩家”,“敌人”和“硬币”,然后使用“层”属性将每个节点类型放置在其相应的层中。然后通过选择每个节点应与之交互的层来设置每个节点的“蒙版”属性。例如,播放器的设置如下所示:

../../_images/player_collision_layers.png
../../_images/player_collision_mask.png

代码示例

在函数调用中,将图层指定为位掩码。如果功能默认启用所有图层,则图层蒙版将指定为0x7fffffff。您的代码可以对图层蒙版使用二进制,十六进制或十进制表示法,具体取决于您的偏好。

上面启用了第1、3和4层的示例的等效代码如下:

# Example: Setting mask value for enabling layers 1, 3 and 4

# Binary - set the bit corresponding to the layers you want to enable (1, 3, and 4) to 1, set all other bits to 0.
# Note: Layer 20 is the first bit, layer 1 is the last. The mask for layers 4,3 and 1 is therefore
0b00000000000000001101
# (This can be shortened to 0b1101)

# Hexadecimal equivalent (1101 binary converted to hexadecimal)
0x000d
# (This value can be shortened to 0xd)

# Decimal - Add the results of 2 to the power of (layer be enabled-1).
# (2^(1-1)) + (2^(3-1)) + (2^(4-1)) = 1 + 4 + 8 = 13
pow(2, 1) + pow(2, 3) + pow(2, 4)

Area2D

区域节点提供检测和影响。它们可以检测物体何时重叠并在物体进入或离开时发出信号。区域还可以用于覆盖定义区域中的物理属性,例如重力或阻尼。

Area2D有三个主要用途:

  • 给定区域中的替代物理参数(例如重力)。
  • 检测其他物体何时进入或离开区域或当前区域中有哪些物体。
  • 检查其他区域是否重叠。

默认情况下,区域还接收鼠标和触摸屏输入。

StaticBody2D

静态物体是物理引擎不会移动的物体。它参与碰撞检测,但不会响应碰撞而移动。但是,它可以利用其和属性为碰撞的物体提供运动或旋转,就好像它在运动一样。constant_linear_velocityconstant_angular_velocity

StaticBody2D 节点最常用于环境中的对象或不需要任何动态行为的对象。

示例用于StaticBody2D:

  • 平台(包括移动平台)
  • 输送带
  • 墙壁和其他障碍

RigidBody2D

这是实现模拟2D物理的节点。您不能直接控制 RigidBody2D。取而代之的是,您对其施加力,然后物理引擎会计算出最终的运动,包括与其他物体的碰撞以及碰撞响应(如弹跳,旋转等)。

您可以通过“质量”,“摩擦”或“弹跳”之类的属性来修改刚体的行为,这些属性可以在检查器中设置。

人体的行为也会受到世界属性(如在“ 项目设置”->“物理”中设置的)的影响,或者受输入 覆盖全球物理属性的Area2D的影响。

当刚体处于静止状态并且一段时间未移动时,它将进入睡眠状态。睡眠物体的作用类似于静态物体,其力不是由物理引擎计算的。当通过碰撞或通过代码施加力时,身体将醒来。

刚体模式
刚体可以设置为以下四种模式之一:

  • 刚性-身体表现为物理对象。它会与其他物体碰撞,并对其施加的力作出反应。这是默认模式。
  • 静态-主体的行为类似于StaticBody2D,并且不会移动。
  • 角色-与“刚性”模式相似,但身体无法旋转。
  • 运动-身体的行为类似于KinematicBody2D,必须通过代码移动。

使用RigidBody2D
使用刚体的好处之一是无需编写任何代码即可“免费”获得许多行为。例如,如果您要制作带有下降块的“愤怒的小鸟”式游戏,则只需创建RigidBody2Ds并调整其属性。堆积,下落和弹跳将由物理引擎自动计算。

但是,如果你想有过身体有一定的控制,你应该照顾-改变position,linear_velocity,刚体的或其他物理性质可能会导致意外的行为。如果您需要更改任何与物理学相关的属性,则应使用_integrate_forces() 回调而不是_physics_process()。在此回调中,您可以访问人体的Physics2DDirectBodyState,它可以安全地更改属性并将其与物理引擎同步。

例如,以下是“小行星”式太空飞船的代码:

class Spaceship : RigidBody2D
{
    private Vector2 _thrust = new Vector2(0, 250);
    private float _torque = 20000;

    public override void _IntegrateForces(Physics2DDirectBodyState state)
    {
        if (Input.IsActionPressed("ui_up"))
            SetAppliedForce(_thrust.Rotated(Rotation));
        else
            SetAppliedForce(new Vector2());

        var rotationDir = 0;
        if (Input.IsActionPressed("ui_right"))
            rotationDir += 1;
        if (Input.IsActionPressed("ui_left"))
            rotationDir -= 1;
        SetAppliedTorque(rotationDir * _torque);
    }
}

请注意,我们不是 直接设置linear_velocity或angular_velocity属性,而是将力(thrust和torque)施加到物体上,然后让物理引擎计算出所产生的运动。

注意

当刚体进入睡眠状态时,_integrate_forces()
将不会调用该功能。要覆盖此行为,您将需要通过创建碰撞,向其施加力或禁用can_sleep
属性来使身体保持清醒状态。请注意,这可能会对性能产生负面影响。

联系人报告
默认情况下,刚体不跟踪接触,因为如果场景中有很多刚体,这可能需要大量的内存。若要启用联系人报告,请将contacts_reported 属性设置为非零值。然后可以通过Physics2DDirectBodyState.get_contact_count()和相关函数来获取联系人 。

可以通过contact_monitor 属性启用通过信号的接触监视。有关可用信号的列表,请参见RigidBody2D。

KinematicBody2D

KinematicBody2D实体可以检测与其他实体的碰撞,但不受重力或摩擦等物理属性的影响。相反,它们必须由用户通过代码控制。物理引擎不会移动运动体。

移动运动机构时,请勿position直接设置它。而是使用move_and_collide()ormove_and_slide()方法。这些方法沿着给定的矢量移动物体,如果检测到与另一个物体的碰撞,它将立即停止。身体碰撞后,任何碰撞响应都必须手动编码。

运动碰撞响应
发生碰撞后,您可能希望身体反弹,沿墙滑动或改变其撞击的对象的属性。处理碰撞响应的方式取决于您用来移动KinematicBody2D的方法。

move_and_collide
使用时move_and_collide(),该函数返回 KinematicCollision2D对象,该对象包含有关碰撞和碰撞体的信息。您可以使用此信息来确定响应。

例如,如果要查找空间中发生碰撞的点:

class Body : KinematicBody2D
{
    private Vector2 _velocity = new Vector2(250, 250);

    public override void _PhysicsProcess(float delta)
    {
        var collisionInfo = MoveAndCollide(_velocity * delta);
        if (collisionInfo != null)
        {
            var collisionPoint = collisionInfo.GetPosition();
        }
    }
}

或从碰撞对象反弹:

class Body : KinematicBody2D
{
    private Vector2 _velocity = new Vector2(250, 250);

    public override void _PhysicsProcess(float delta)
    {
        var collisionInfo = MoveAndCollide(_velocity * delta);
        if (collisionInfo != null)
            _velocity = _velocity.Bounce(collisionInfo.Normal);
    }
}
move_and_slide

滑动是一种常见的碰撞反应。想象一个玩家在自上而下的游戏中沿着墙壁移动,或者在平台游戏中在斜坡上上下移动。虽然可以使用后,自己编写这种反应move_and_collide(), move_and_slide()提供了实现滑动,而无需编写大量代码的便捷方式。

警告

move_and_slide()自动包括在计算中时间步长,所以应该没有乘法的速度矢量通过delta。

例如,使用以下代码制作一个可以在地面上行走(包括斜坡)并在站立在地面上时可以跳跃的角色:

class Body : KinematicBody2D
{
    private float _runSpeed = 350;
    private float _jumpSpeed = -1000;
    private float _gravity = 2500;

    private Vector2 _velocity = new Vector2();

    private void GetInput()
    {
        _velocity.x = 0;

        var right = Input.IsActionPressed("ui_right");
        var left = Input.IsActionPressed("ui_left");
        var jump = Input.IsActionPressed("ui_select");

        if (IsOnFloor() && jump)
            _velocity.y = _jumpSpeed;
        if (right)
            _velocity.x += _runSpeed;
        if (left)
            _velocity.x -= _runSpeed;
    }

    public override void _PhysicsProcess(float delta)
    {
        _velocity.y += _gravity * delta;
        GetInput();
        _velocity = MoveAndSlide(velocity, new Vector2(0,-1));
    }
}

有关使用的更多详细信息,请参见运动字符(2D)move_and_slide(),包括带有详细代码的演示项目。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。