collision object 碰撞物
area2d
area2d只提供检测,能够触发接触检测,区域进出检测,不可用于碰撞,可以想象成雷达。也可以检测区域是否重叠。区域也可以接收鼠标/触摸输入。
static body 2d
静态物,无法被移动,固定在场景中,可被碰撞。静态物体可以设置线性速度和线性角动量,用来影响碰撞的刚体。
rigid body 2d
刚体,最好不要直接移动物体,而是给物体施加力,否则会破坏其中的物理系统。当刚体不运动后,物体会保持静止,类似静态物一样,直到再次受力。
- rigid:刚体模式,默认
- static: 类似static body 2d 无法移动
- charactor: 和rigid一样,只是无法旋转
- kinematic:类似kinematic body 2d,只能通过代码来表现它的运动效果
kinematic body 2d
动态物体,不会被物理引擎约束,可以随意控制位置,能和刚体和静态物相互碰撞。
移动时不要使用move(),要使用move_and_collide()或者move_and_slide()方法。如果碰撞,则api会停止,碰撞触发后手动处理之后的事情。
碰撞形状
绝对不要对碰撞形状进行缩放
物理过程
在_process中刷新可能物理位置不稳定,使用_physics_process来处理
layer mask 层与遮罩
层
物体所在的层
遮罩
物体可以碰撞的层
可以在项目设置中对图层命名
使用力,避免使用线性速度和角速度
extends RigidBody2D
var thrust = Vector2(0, 250)
var torque = 20000
func _integrate_forces(state):
if Input.is_action_pressed("ui_up"):
applied_force = thrust.rotated(rotation)
else:
applied_force = Vector2()
var rotation_dir = 0
if Input.is_action_pressed("ui_right"):
rotation_dir += 1
if Input.is_action_pressed("ui_left"):
rotation_dir -= 1
applied_torque = rotation_dir * torque
[!!!]当进入睡眠时,_integrate_forces()不再有效,你必须一直确保它与其他物体间有碰撞,或者关闭can_sleep
运动碰撞响应
move_and_collision
返回 空 或者 kinematic collision 2d对象,其中包含了碰撞信息
查找碰撞点
extends KinematicBody2D
var velocity = Vector2(250, 250)
func _physics_process(delta):
var collision_info = move_and_collide(velocity * delta)
if collision_info:
var collision_point = collision_info.position
反弹效果:
extends KinematicBody2D
var velocity = Vector2(250, 250)
func _physics_process(delta):
var collision_info = move_and_collide(velocity * delta)
if collision_info:
velocity = velocity.bounce(collision_info.normal)
move_and_slide
对move_and_collision的高级处理,直接进行滑动运动
move_and_slide包含了时间步长,所以向量不要乘delta
行走和跳跃
extends KinematicBody2D
var run_speed = 350
var jump_speed = -1000
var gravity = 2500
var velocity = Vector2()
func get_input():
velocity.x = 0
var right = Input.is_action_pressed('ui_right')
var left = Input.is_action_pressed('ui_left')
var jump = Input.is_action_just_pressed('ui_select')
if is_on_floor() and jump:
velocity.y = jump_speed
if right:
velocity.x += run_speed
if left:
velocity.x -= run_speed
func _physics_process(delta):
velocity.y += gravity * delta
get_input()
velocity = move_and_slide(velocity, Vector2(0, -1))
move_and_collision 与 move_and_slide参数区别
move_and_collision参数需要乘delta
velocity.y += delta * GRAVITY
var motion = velocity * delta
move_and_collide(motion)
move_and_slide参数不需要乘delta
velocity.y += delta * GRAVITY
move_and_slide(velocity, Vector2(0, -1))
move_and_slide_with_snap 粘着
跳跃时会产生冲突,跳跃时可设置snap向量为0
用collision和slide来实现snap
# using move_and_collide
var collision = move_and_collide(velocity * delta)
if collision:
velocity = velocity.slide(collision.normal)
# using move_and_slide
velocity = move_and_slide(velocity)
使用kinematic 和 rigid的controller区别
kinematic 不能被物理定律约束,比如主角落地后弹跳,摩擦系数等
rigid的controller 不能被有效的控制,有时候可能会因为物理约束导致不必要的结果
两种方案都需要在游戏中进行针对性的优化
射线 / 发射
获取physics 2d direct space state
要对物理空间执行查询,必须使用Physics2DDirectSpaceState
func _physics_process(delta):
var space_rid = get_world_2d().space
var space_state = Physics2DServer.space_get_direct_state(space_rid)
或者
func _physics_process(delta):
var space_state = get_world_2d().direct_space_state
raycast查询
func _physics_process(delta):
var space_state = get_world_2d().direct_space_state
# use global coordinates, not local to node
var result = space_state.intersect_ray(Vector2(0, 0), Vector2(50, 100))
if result:
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
}
碰撞异常
排除掉对自身的检测
extends KinematicBody2D
func _physics_process(delta):
var space_state = get_world_2d().direct_space_state
var result = space_state.intersect_ray(global_position, enemy_position, [self])
碰撞遮罩
对大型系统来讲,使用碰撞遮罩更好
extends KinematicBody2D
func _physics_process(delta):
var space_state = get_world().direct_space_state
var result = space_state.intersect_ray(global_position, enemy_position,
[self], collision_mask)
摄像机投射射线
const ray_length = 1000
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == 1:
var camera = $Camera
var from = camera.project_ray_origin(event.position)
var to = from + camera.project_ray_normal(event.position) * ray_length
请记住,在 _input() 期间,空间可能被锁定,所以实际上这个查询应该在 _physics_process() 中运行。