# hit_collision_test —— §3.5:__NormalAttackProcess 的碰撞几何 headless 自检。 # IntersectLineSegments(EterLib/lineintersect_utils.cpp) # DetectCollisionDynamicZCylinderVSDynamicZCylinder(GameLib/GameUtil.cpp) # .msm AttachingData CollisionType 3 = m_DefendingPointInstanceList # godot --headless --path project --script hit_collision_test.gd extends SceneTree const HitCollision = preload("res://hit_collision.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: hit_collision_test (segments / z-cylinder / msm defending)") quit(0) else: printerr("%d check(s) failed" % _fail) quit(1) func _near(a: Vector3, b: Vector3) -> bool: return a.distance_to(b) < 0.01 func _run() -> void: # --- IntersectLineSegments --- # 静止受击方(L22 < ε²):OutB = B1,OutA = A 上离 B1 最近点。 var r: Array = HitCollision.intersect_line_segments( Vector3(-100, 0, 0), Vector3(100, 0, 0), Vector3(0, 50, 0), Vector3(0, 50, 0)) _ck(_near(r[0], Vector3.ZERO) and _near(r[1], Vector3(0, 50, 0)), "stationary B -> nearest on A = (0,0,0)") # 退化 A(L11 < ε²) r = HitCollision.intersect_line_segments( Vector3(10, 10, 0), Vector3(10, 10, 0), Vector3(0, 0, 0), Vector3(100, 0, 0)) _ck(_near(r[0], Vector3(10, 10, 0)) and _near(r[1], Vector3(10, 0, 0)), "degenerate A -> OutB (10,0,0)") # 平行(|DetL| < ε):两段 clamp 参数取中点 r = HitCollision.intersect_line_segments( Vector3(0, 0, 0), Vector3(100, 0, 0), Vector3(0, 50, 0), Vector3(100, 50, 0)) _ck(_near(r[0], Vector3(50, 0, 0)) and _near(r[1], Vector3(50, 50, 0)), "parallel -> midpoints (50,0)/(50,50)") # 一般情形,s/t 双越界 -> AdjustNearestPoints r = HitCollision.intersect_line_segments( Vector3(0, 0, 0), Vector3(100, 0, 0), Vector3(200, -50, 0), Vector3(200, 50, 0)) _ck(_near(r[0], Vector3(100, 0, 0)) and _near(r[1], Vector3(200, 0, 0)), "both out of range -> (100,0)/(200,0)") # 参考端 rb = +dot(Lb, AB)(注释里是负号):两条运动段十字相交时 t 被算成 -0.5, # 经 AdjustNearestPoints 夹到 B1。1:1 保留这个出货行为。 r = HitCollision.intersect_line_segments( Vector3(-100, 0, 0), Vector3(100, 0, 0), Vector3(0, -100, 0), Vector3(0, 100, 0)) _ck(_near(r[0], Vector3.ZERO) and _near(r[1], Vector3(0, -100, 0)), "shipped rb sign: crossing moving segments -> OutB clamps to B1, got %s / %s" % [r[0], r[1]]) # --- DetectCollisionDynamicZCylinderVSDynamicZCylinder --- # 攻击球扫过静止受击球:只看扫掠段,不看端点;z(高度)清零。 _ck(HitCollision.detect_z_cylinder(Vector3(-200, 0, 500), Vector3(200, 0, 500), 20.0, Vector3(0, 60, 0), Vector3(0, 60, 0), 70.0), "sweep passes 60cm from victim, r 20+70 -> hit (height ignored)") _ck(not HitCollision.detect_z_cylinder(Vector3(-200, 0, 0), Vector3(200, 0, 0), 20.0, Vector3(0, 100, 0), Vector3(0, 100, 0), 70.0), "sweep passes 100cm away -> no hit") _ck(not HitCollision.detect_z_cylinder(Vector3(-200, 0, 0), Vector3(200, 0, 0), 20.0, Vector3(400, 0, 0), Vector3(400, 0, 0), 70.0), "AABB reject -> no hit") _ck(HitCollision.detect_z_cylinder(Vector3(0, 0, 0), Vector3(0, 0, 0), 20.0, Vector3(80, 0, 900), Vector3(80, 0, 900), 70.0), "static spheres 80cm apart, r sum 90 -> hit") # --- .msm 防御球(CollisionType 3),CollisionType 1 = body 不算 --- var assets := AssetRoot.path() var wolf := assets.path_join("Monster/ymir work/monster/wolf/wolf_blue.msm") var ds: Array = HitCollision.parse_msm_defending(wolf) _ck(ds.size() == 1, "wolf_blue.msm -> 1 defending sphere, got %d" % ds.size()) if ds.size() == 1: _ck(is_equal_approx(float(ds[0].radius), 90.0) and _near(ds[0].pos, Vector3(0, -15, 80)), "wolf defending sphere r90 (0,-15,80), got %s" % [ds[0]]) var war := assets.path_join("root/msm/warrior_m.msm") ds = HitCollision.parse_msm_defending(war) _ck(ds.size() >= 1 and is_equal_approx(float(ds[0].radius), 70.0) and _near(ds[0].pos, Vector3(0, 0, 100)), "warrior_m.msm defending sphere r70 (0,0,100), got %s" % [ds]) _ck(HitCollision.parse_msm_defending(assets.path_join("no/such.msm")).is_empty(), "missing msm -> []") # IS_HUGE_RACE(InstanceBase.cpp) _ck(HitCollision.is_huge_race(2493) and not HitCollision.is_huge_race(101), "IS_HUGE_RACE: only 2493")