Collision System
AABB voxel collision — ResolveY, ResolveXZ, and block solidity rules.
AABB Shape
Blupi's hitbox is an axis-aligned bounding box (AABB):
Width = 2 * kHalfW = 0.70 units (X and Z)
Height = 2 * kHalfH = 1.40 units (Y)
The pivot point (pos) is at the bottom centre of the AABB
(i.e., feet position).
Block Solidity
bool IsSolid(int wx, int wy, int wz):
if wx < 0 || wy < 0 || wz < 0: return true // treat out-of-bounds-low as solid floor
if wx >= 100 || wy >= 100 || wz >= 100: return false // out-of-bounds-high is air
block = world_->getBlock(wx + kWCX, wy, wz + kWCZ)
return !block.isAir()
Note: kWCX = kWCZ = 50 translates gameplay coordinates to world storage coordinates.
The world Y axis is not offset.
ResolveY — Vertical Collision
Called after applying gravity and Y velocity displacement each frame:
ResolveY(pos):
footY = floor(pos.y) // block row that feet are in
headY = floor(pos.y + 2*kHalfH)
// Falling — check floor
if vel_.y <= 0:
if IsSolid(round(pos.x), footY, round(pos.z)):
pos.y = footY + 1 // snap feet to top of solid block
vel_.y = 0
onGround_ = true
else:
onGround_ = false
// Rising — check ceiling
if vel_.y > 0:
if IsSolid(round(pos.x), headY, round(pos.z)):
pos.y = headY - 2*kHalfH // push head below block
vel_.y = 0
ResolveXZ — Horizontal Collision
Called after applying X and Z displacement each frame:
ResolveXZ(pos):
bodyWY = round(pos.y) // which Y block row the body occupies
// +X face
if IsSolid(round(pos.x + kHalfW), bodyWY, round(pos.z)):
pos.x = round(pos.x + kHalfW) - kHalfW
// -X face
if IsSolid(round(pos.x - kHalfW), bodyWY, round(pos.z)):
pos.x = round(pos.x - kHalfW) + kHalfW
// +Z face
if IsSolid(round(pos.x), bodyWY, round(pos.z + kHalfW)):
pos.z = round(pos.z + kHalfW) - kHalfW
// -Z face
if IsSolid(round(pos.x), bodyWY, round(pos.z - kHalfW)):
pos.z = round(pos.z - kHalfW) + kHalfW
bodyWY),
not a full swept AABB. This is consistent with the original mobile-eggbert approach
and sufficient for the game's block-scale geometry.
Hazard Tiles
Lava (typeId=68) and Spike (typeId=373) blocks are solid for movement
but also trigger blupiHit_ when stood on.
This check happens in Blupi::Update() after ResolveY():
if onGround_ and the block below is a hazard type, the hit flag is set.
(Needs verification — exact implementation.)
Fall Death
// After position update and collision resolution:
if pos.y < -10.0f:
fallDeath_ = true
pos = spawnPoint_
vel_ = Vector3::ZERO
GalaxyEggbertGame::UpdatePlay() checks blupi_->WasFallDeath()
each frame and deducts a life.
Object Collision
Blupi↔object collision is handled in Decor::CheckContact() using a simple
sphere test (radius 0.85 units), not the full AABB. See Object Lifecycle.