package physics import "testing" // planeWorld 测试世界:y < 63 全固体,y >= 63 空气;x == 5 一堵墙。 type planeWorld struct{} func (planeWorld) Solid(x, y, z int32) bool { return y < 63 || x == 5 } func (planeWorld) Fluid(x, y, z int32) bool { return false } // TestFallOntoGround 下落 → 停在 63 层顶部(浮点余量内),OnGround 置位。 func TestFallOntoGround(t *testing.T) { w := planeWorld{} p := NewPlayer(0.5, 80, 0.5) for i := 0; i < 200; i++ { p.Tick(w, 1.0/20) } if p.Pos[1] < 63 || p.Pos[1] >= 63.5 { t.Fatalf("脚底期望 [63, 63.5),实际 %v", p.Pos[1]) } if !p.OnGround { t.Fatal("应判定落地") } } // TestWallBlock 水平移动撞墙不穿墙(物理与碰撞.md §8 踩坑)。 func TestWallBlock(t *testing.T) { w := planeWorld{} p := NewPlayer(4.0, 63, 0.5) // 墙在 x=5,玩家宽 0.6(右缘 4.3) p.Vel[0] = 10 p.Tick(w, 1.0/20) if p.Pos[0]+p.Width/2 > 5 { t.Fatalf("穿墙:右缘 %v 超过墙 x=5", p.Pos[0]+p.Width/2) } } // TestJump 起跳与回落。 func TestJump(t *testing.T) { w := planeWorld{} p := NewPlayer(0.5, 63, 0.5) p.Tick(w, 1.0/20) // 先落地判定 if !p.OnGround { t.Fatal("初始应在地面") } p.Jump() peak := 0.0 for i := 0; i < 100; i++ { p.Tick(w, 1.0/20) if p.Pos[1] > peak { peak = p.Pos[1] } if p.OnGround && i > 5 { break } } if peak < 64.0 { t.Fatalf("起跳高度过低: %v", peak) } } type waterWorld struct{} func (waterWorld) Solid(x, y, z int32) bool { return y < 50 } func (waterWorld) Fluid(x, y, z int32) bool { return y >= 50 && y <= 56 } func TestOxygenDrainAndRefill(t *testing.T) { w := waterWorld{} p := NewPlayer(0.5, 52, 0.5) // 眼睛约 53.6,在水中 p.updateFluid(w) if !p.EyeInWater { t.Fatal("眼睛应在水中") } p.TickAir(1) if p.Air >= AirMax { t.Fatalf("水下氧气应递减,实际 %v", p.Air) } for i := 0; i < 20; i++ { p.TickAir(1) } if p.Air != 0 { t.Fatalf("长时间潜水氧气应为 0,实际 %v", p.Air) } p.Pos[1] = 60 // 出水 p.updateFluid(w) p.TickAir(0.05) if p.Air != AirMax { t.Fatalf("出水应回满氧气,实际 %v", p.Air) } } func TestSwimUpWithoutGround(t *testing.T) { w := waterWorld{} p := NewPlayer(0.5, 53, 0.5) p.updateFluid(w) if !p.InWater { t.Fatal("应在水中") } p.OnGround = false p.SwimUp() if p.Vel[1] <= 0 { t.Fatal("水中上浮速度应 > 0") } }