5.2 2D 物理与碰撞


5.2 2D 物理与碰撞

本节摘要:物理引擎以固定步长离散地推进世界,节点世界与物理世界之间存在一层变换同步。碰撞体有四大家族——运动学身体、动力学刚体、静态体、检测区域,各管一种运动范式。射线负责"感知",物理材质负责"手感"。本节讲透这套谱系与调校方法,为 5.3 的角色控制打底。

为什么角色会卡进墙里半格?为什么电梯平台上的箱子会抖?这些问题的答案都在物理引擎的离散本质里:它不是连续地计算世界,而是按固定节拍一小步一小步地推(第 2 章讲过的固定步长),步与步之间靠插值补视觉。理解"离散推进加插值呈现",一半的物理怪象就能自我解释。

四类碰撞体的谱系

选对碰撞体是物理设计的第一决策。四大家族的分工用"谁决定位置"一句话区分:

类型 谁决定位置 典型角色 关键特性
运动学身体 你的代码 玩家角色、电梯 与墙碰撞滑动,速度可查询,不被力推动
动力学刚体 物理引擎 掉落的箱子、滚的石球 受重力与力,全自动化
静态体 没人动 地面、墙壁 只当障碍,永不移动
检测区域 没有实体 触发器、拾取范围 只感知进入离开,不阻挡任何人

运动学身体是主角的标准载体:位置由代码驱动(移动与滑动方法),碰撞由引擎解析——撞墙就停、贴坡能滑。它的哲学是"手感归我,碰撞归引擎"。

动力学刚体是道具的标准载体:设置好质量与摩擦,物理引擎接管一切。想对它施力有一套完整的接口(冲量、力、力矩)。刚体与刚体之间会真实碰撞、堆叠、滚动。

静态体是世界的骨架:瓦片地图自动生成静态碰撞,独立的静态体补充零散障碍。

检测区域是无形的哨兵:玩家走进范围触发信号,矿石被拾取、敌人发现目标、存档点激活,全用它。区域可以完全不参与"阻挡",只做感知。

# 拾取矿石:检测区域的标准用法 extends Area2D signal collected(ore_type: StringName, value: int) @export var ore_type: StringName = &"copper" @export var value := 5 func _ready() -> void: body_entered.connect(_on_body_entered) func _on_body_entered(body: Node2D) -> void: if body.is_in_group(&"player"): collected.emit(ore_type, value) queue_free()

运动学身体的移动心法

移动与滑动是运动学身体的核心方法,理解它的输入输出就理解了平台游戏的手感工程。

extends CharacterBody2D const SPEED := 220.0 const JUMP_VELOCITY := -420.0 const GRAVITY := 980.0 func _physics_process(delta: float) -> void: if not is_on_floor(): velocity.y += GRAVITY * delta # 重力累加到速度 if Input.is_action_just_pressed(&"jump") and is_on_floor(): velocity.y = JUMP_VELOCITY # 起跳:直接设定垂直速度 velocity.x = Input.get_axis(&"move_left", &"move_right") * SPEED move_and_slide() # 引擎解析碰撞,返回后更新位置

这段代码浓缩了三条心法。其一,速度是唯一状态:代码只改速度,位置由移动方法按速度推进——重力加进速度、跳跃设定速度、输入调水平速度,各管一个分量。其二,落地查询在移动之后:着地、着墙、天花板这些状态由上一次移动调用更新,先移动后查询。其三,手感是参数组:重力、跳速、水平速度三个数字决定角色的"性格",跑得快跳得低是利落,跑得慢跳得高是绵软——调手感就是调这三个数的组合。

💡 跳跃手感进阶:松开跳跃键提前截断上升速度(短按小跳)、下落时重力加大(落地干脆)、离地宽限几帧(走下坡也能接跳)。三招齐上,"矿工"的跳跃立刻从能用变成舒服——5.3 复盘会逐个装上。

射线:物理世界的眼睛

射线从一点沿方向发射,报告撞到的第一个碰撞体与命中点。它是"感知"的精确工具:敌人视线检测、地面坡度采样、子弹命中判定。

# 视线检测:敌人能否看见玩家 func can_see_player() -> bool: var query := PhysicsRayQueryParameters2D.create( global_position, _player.global_position) query.exclude = [get_rid()] # 排除自己 var hit := get_world_2d().direct_space_state.intersect_ray(query) return hit and hit.collider == _player # 第一个挡路的是玩家才叫看见

用射线有两条纪律。一,射线数量乘以频率就是开销,每帧几十条无妨,成千条要节制(空间分区或降频采样)。二,射线的坐标系是世界坐标,从局部坐标出发记得先转换,否则"看不见斜坡下的玩家"这类怪象难以排查。

物理材质:摩擦与弹性的微调

物理材质决定表面性格:摩擦控制滑动难易,弹性控制反弹力度。冰面低摩擦、橡胶高弹性,数值都在材质资源上调。经验做法是建立材质库(普通地面、冰、泥、弹簧垫各一份),碰撞体引用材质而不是各调各的参数——调"冰"一份,全关卡冰面同步变。

图 1 物理系统的协作全景

图 1 物理系统的协作全景

碰撞层与掩码:谁理谁的一张表

碰撞体系的最后一块拼图是层与掩码。每个碰撞体同时具有两层身份:自己在哪几层(层)与自己关心哪几层(掩码)。两个碰撞体是否相互作用,由双方的层与掩码交叉决定。

这张机制解决"世界很挤但互不干扰"的组织问题:玩家的层是玩家层、掩码是地形加敌人;敌人的层是敌人层、掩码是地形加玩家;矿石区域的层是道具层、掩码只有玩家——矿石与敌人擦肩而过互不触发。层的命名在项目设置里做,语义化命名(地形、玩家、敌人、道具、子弹)后,整个碰撞拓扑就是一张可读可查的表。

⚠️ 常见坑:全部用默认的第一层,一旦需要"子弹不打队友"就只能写代码判断,越堆越乱。项目第一天就把层语义定好,后期的每一次新实体接入都是查表填空。

本节要点回顾

  • 离散本质:固定步长推进加插值呈现,物理怪象先从离散与同步找原因
  • 四大家族:运动学身体、动力学刚体、静态体、检测区域,按"谁决定位置"选择
  • 移动心法:代码改速度、引擎挪位置、落地查询在移动之后;手感是参数组
  • 射线是眼睛:视线、采样、命中判定;控量并注意世界坐标
  • 物理材质库:表面性格集中管理,一份材质全关卡同步
  • 层与掩码:碰撞拓扑的项目级设计,第一天就定语义

渲染与物理的地基打完,下一节正式开工:从空项目到手感正确的矿工角色。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U