Godot实现物理体动态形状
简介
在《蜡笔物理学》和《一起剪吧!剪纸狙击手!》等游戏中,都有一种功能,即:可以在游戏中动态的修改物理形状,实现动态物理玩法。
本教程就展示如何在Godot中实现这一功能。
实现方式
实现思路
- 首先我们需要一个物理类,如果是场景等可以使用RigidBody2D,而如果是可以操控的玩家等角色,则可以使用CharacterBody2D。本教程使用的是RigidBody2D。
- 然后需要对物理形状节点和另一个形状进行布尔运算,这里可以使用Geometry2D实现。由于Geometry2D中形状数据均为PackedVector2Array,所以我们使用更加合适的多边形碰撞形状(CollisionPolygon2D)来实现功能
- 然后由于裁剪了物理体后,需要让对应的视觉效果也能展示出被裁剪的效果,而Node2D节点的clip child属性可以达到这一点。为了使用这个属性,我们需要用一个Polygon2D节点作为纹理节点的父级。并设置裁剪属性。
具体实现
创建可以裁切的物理体类,添加碰撞形状、纹理容器,纹理节点。 设置纹理容器的clip child属性为clip only,并为根节点添加脚本。
gdscript
# clipable_body.gd
# 可裁剪的物理体类,继承自 RigidBody2D
class_name ClipableBody
extends RigidBody2D
# 碰撞多边形节点,用于物理碰撞检测
@onready var collision_polygon_2d: CollisionPolygon2D = $CollisionPolygon2D
# 纹理容器节点,用于显示裁剪后的视觉效果
@onready var texture_container: Polygon2D = $TextureContainer
# 裁剪物理体形状
# 参数 shape: 用于裁剪的多边形形状(世界坐标系)
func clip_shape(shape: PackedVector2Array):
# 将世界坐标系的裁剪形状转换为本地坐标系
var local_shape = PackedVector2Array(Array(shape).map(func(p) : return to_local(p)))
# 获取当前碰撞多边形的形状
var current_polygon = collision_polygon_2d.polygon
# 使用 Geometry2D 进行多边形裁剪(布尔运算)
# 返回裁剪后的多边形数组,通常第一个元素是裁剪后的结果
var new_polygon = Geometry2D.clip_polygons(current_polygon, local_shape)
# 更新碰撞多边形的形状
collision_polygon_2d.polygon = new_polygon[0]
# 同步更新纹理容器的多边形形状,以匹配裁剪后的视觉效果
texture_container.polygon = new_polygon[0]创建游戏场景,添加场景中的StaticBody2D节点,然后添加一个可以裁剪的物理体,再添加一个Line2D作为绘制多边形的时候的线,以及一个Plygon2D节点作为裁剪的多边形指示。修改多边形指示节点的颜色为半透明。并为根节点添加脚本。
gdscript
# game.gd
# 游戏场景脚本,处理鼠标交互和裁剪操作
extends Node2D
# 裁剪区域显示节点,用于显示最终确定的裁剪多边形
@onready var clip_area: Polygon2D = $ClipArea
# 可裁剪的物理体节点
@onready var clipable_body: ClipableBody = $ClipableBody
# 绘制线条节点,用于实时显示正在绘制的多边形
@onready var line_2d: Line2D = $Line2D
# 存储当前正在绘制的多边形顶点数组
var polygon: Array[Vector2] = []
# 记录多边形绘制的起始点,用于判断是否闭合多边形
var start_point: Vector2
# 标记是否正在选择/绘制多边形顶点
var selecting_point = false
# 处理输入事件
func _input(event: InputEvent):
# 检测鼠标点击动作
if event.is_action_pressed("click"):
# 获取鼠标在世界坐标系中的位置
var mouse_position = get_global_mouse_position()
# 如果还没有开始选点,则开始新的多边形绘制
if not selecting_point:
# 开始选点
# 清空之前的多边形顶点
polygon = []
# 记录起始点位置
start_point = mouse_position
# 标记开始选点状态
selecting_point = true
# 隐藏裁剪区域显示
clip_area.hide()
# 显示绘制线条
line_2d.show()
# 判断是否完成多边形绘制
# 条件:至少3个点(构成多边形的最小点数)且鼠标位置接近起始点(距离小于5像素)
if polygon.size() >= 3 and (start_point - mouse_position).length() < 5:
# 结束选取
# 将绘制的多边形顶点数组转换为 PackedVector2Array 并设置到裁剪区域
clip_area.polygon = PackedVector2Array(polygon)
# 显示裁剪区域
clip_area.show()
# 隐藏绘制线条
line_2d.hide()
# 重置选点状态
selecting_point = false
# 执行切割操作,将多边形传递给可裁剪物理体
clipable_body.clip_shape(PackedVector2Array(polygon))
else:
# 添加一个点
# 将当前鼠标位置添加到多边形顶点数组
polygon.push_back(mouse_position)
# 更新绘制线条的顶点,实时显示当前绘制的多边形
line_2d.points = PackedVector2Array(polygon)具体实现效果如下
目前存在的问题
- 因为目前的架构中,物理体只有一个形状,在被裁剪后,则有可能出现多个形状,此处应该根据数量形成多个物理体。
- 目前只有一个物理体,可以设置多个,然后循环一下即可。

京公网安备11010502055496号