Fix idle/run animation flicker and bullet cell bleed; add F1 hitbox overlay
ci / verify (push) Successful in 46s
ci / verify (push) Successful in 46s
The ship looked like it played both animations at once because it effectively did. is_moving() read the tail of `pending`, which _reconcile drains on every snapshot -- so it returned false about 20 times a second and the sprite alternated between the run and idle strips. Now reads the last sampled input, which survives the queue being emptied. Bullets were still wrong for a reason my earlier ASCII check could not see: I had dumped alpha only, and this pack animates as a colour shimmer over a fixed silhouette, so identical-looking frames told me nothing. The actual defect was that several sprites overflow their 16px cell and bleed into the neighbour below -- the first "heavy" I picked dragged in a solid slice of the sprite above it. Cells are re-picked to ones with empty borders in every frame, and build_local_assets.py now asserts that rather than trusting the choice, so a future pick that bleeds fails at build time instead of looking like a rendering bug. Added a hitbox overlay on F1 (src/view/debug_draw.gd). It draws what the simulation actually collides against over what is drawn: player hitbox against sprite radius, the muzzle point, enemy and boss radii, aggro rings, every live bullet's radius, and terrain outlined by which of the three flags each tile sets -- so a pit that stops feet but not bullets looks different from a wall. It deliberately ignores fog, since hiding half the evidence would defeat the point, and it reads every number from SimConfig/MapGrid/Content rather than keeping its own copies, or it would just confirm its own mistakes. Nearly every "that looked wrong" report in this project has been art and simulation disagreeing, and each took a round trip to diagnose. This makes that class of bug visible directly. 163 tests. check.sh, test.sh and smoke.sh pass.
This commit is contained in:
@@ -32,6 +32,9 @@ var input_tick: int = 0
|
||||
var predicted_pos := Vector2.ZERO
|
||||
var aim: float = 0.0
|
||||
var pending: Array[InputFrame] = []
|
||||
## Movement from the most recent sampled input. Survives `pending` being
|
||||
## drained, which is the whole point -- see is_moving().
|
||||
var _last_move := Vector2.ZERO
|
||||
|
||||
# Authoritative mirror of the local player.
|
||||
var my_hp: int = SimConfig.PLAYER_MAX_HP
|
||||
@@ -106,6 +109,7 @@ func _physics_process(delta: float) -> void:
|
||||
input_tick += 1
|
||||
|
||||
var frame := _sample_input()
|
||||
_last_move = frame.move
|
||||
pending.append(frame)
|
||||
# Only enough history to cover the worst reconciliation window.
|
||||
while pending.size() > SimConfig.INPUT_MAX_AGE:
|
||||
@@ -207,8 +211,13 @@ func _resync_input_tick(server_tick: int, why: String) -> void:
|
||||
|
||||
## Whether the local player is moving, for choosing a run vs idle animation.
|
||||
## View-only; nothing in the simulation asks.
|
||||
##
|
||||
## Reads the last sampled input rather than the tail of `pending`, which is
|
||||
## drained on every reconcile: derived from `pending` this flickered false ~20
|
||||
## times a second, so the ship alternated between its run and idle strips and
|
||||
## looked like both were playing at once.
|
||||
func is_moving() -> bool:
|
||||
return not pending.is_empty() and pending[pending.size() - 1].move.length_squared() > 0.04
|
||||
return _last_move.length_squared() > 0.04
|
||||
|
||||
|
||||
func on_map_chunks(from_instance: int, data: PackedByteArray) -> void:
|
||||
|
||||
Reference in New Issue
Block a user