# physics_push_test —— §3.7:受击击退物理(GameLib/PhysicsObject.cpp CPhysicsObject + # MapUtil.cpp CEaseOutInterpolation)headless 自检。 # godot --headless --path project --script physics_push_test.gd extends SceneTree const PhysicsPush = preload("res://physics_push.gd") var _fail := 0 func _ck(c: bool, m: String) -> void: if not c: _fail += 1 printerr("FAIL: " + m) func _init() -> void: _run() if _fail == 0: print("PASS: physics_push_test (external force / friction / ease-out blend)") quit(0) else: printerr("%d check(s) failed" % _fail) quit(1) func _run() -> void: # 初始:不在 blending,每帧位移 0 var p := PhysicsPush.new() _ck(not p.is_blending(), "fresh object -> not blending") p.update(0.02) _ck(p.get_movement() == Vector2.ZERO, "fresh object -> zero movement") # IncreaseExternalForce(F=3, dir +X):vel 3 -> 每步摩擦 0.3,累计 2.7+2.4+…+0.3 = 13.5 cm p.set_direction(Vector2(1, 0)) p.increase_external_force(3.0) _ck(absf(p.get_last_position().x - 13.5) < 0.001 and absf(p.get_last_position().y) < 0.001, "F=3 along +X -> last position (13.5, 0), got %s" % p.get_last_position()) _ck(p.is_blending(), "after push -> blending (ease-out playing)") # 逐帧 AddMovement(GetXMovement, GetYMovement):2 秒 ease-out 累计 ≈ 13.5(离散 Euler 少 1/N) var sum := Vector2.ZERO var frames := 0 while p.is_blending() and frames < 1000: p.update(0.02) sum += p.get_movement() frames += 1 _ck(frames >= 99 and frames <= 101, "blend lasts LoopValue*c_fFrameTime = 2s, got %d frames" % frames) _ck(absf(sum.x - 13.5) < 0.3 and absf(sum.y) < 0.001, "ease-out total movement ≈ 13.5, got %s" % sum) p.update(0.02) _ck(p.get_movement() == Vector2.ZERO, "blend done -> movement 0") # 斜向:分量各自摩擦,13.5 * cos45 var q := PhysicsPush.new() q.set_direction(Vector2(1, 1).normalized()) q.increase_external_force(3.0) _ck(absf(q.get_last_position().x - 13.5 * 0.70710678) < 0.01 and absf(q.get_last_position().y - 13.5 * 0.70710678) < 0.01, "diagonal push -> per-axis 9.546, got %s" % q.get_last_position()) # 分量 vel*dir < EPSILON(0.001) 当步清零(3*0.01*0.01 = 3e-4) var w := PhysicsPush.new() w.set_direction(Vector2(0.99995, 0.01)) w.increase_external_force(3.0) _ck(w.get_last_position().y == 0.0, "tiny axis component zeroed on first step, got %s" % w.get_last_position()) # 力为 0:没有位移,也不 blending(Setup(0,0,2) 仍在播放 -> isBlending 为真,与参考端一致) var z := PhysicsPush.new() z.set_direction(Vector2(1, 0)) z.increase_external_force(0.0) _ck(z.get_last_position() == Vector2.ZERO, "zero force -> no movement") # 撞墙(IPhysicsWorld::isPhysicalCollision 为真)-> Initialize(),不推 var c := PhysicsPush.new() c.set_direction(Vector2(1, 0)) c.increase_external_force(3.0, func(_moved: Vector2) -> bool: return true) _ck(c.get_last_position() == Vector2.ZERO and not c.is_blending(), "world collision -> Initialize, no push")