Blupi
Player character — physics, collision, animation, and billboard rendering.
Overview
Blupi is the player-controlled character. It handles:
- Physics: gravity, jump, horizontal movement and strafing
- AABB voxel collision against the world
- Fall-death detection (Y < -10)
- Billboard sprite rendering (UV-mapped from
blupi.png) - Animation state machine: Stop, March, Turn, Jump, Air
- Invincibility flash
- External delta (platform carry from
Decor)
Sources: src/GalaxyEggbert/Game/Blupi.hpp and .cpp
Physics Constants
static constexpr float kGravity = -22.0f; // units/s²
static constexpr float kJumpSpeed = 10.0f; // units/s (initial Y velocity)
static constexpr float kMoveSpeed = 5.5f; // units/s horizontal
static constexpr float kTurnSpeed = 180.0f; // degrees/s rotation
static constexpr float kHalfW = 0.35f; // half-width for AABB
static constexpr float kHalfH = 0.70f; // half-height for AABB
Public Interface
class Blupi {
public:
Blupi(Context* context, Scene* scene, const World* world, int wcx, int wcz);
~Blupi();
void Update(float dt);
void Respawn();
void SetSpawnPoint(Vector3 pos);
void ApplyExternalDelta(Vector3 delta);
void StartFlash(float duration);
Node* GetNode() const;
Vector3 GetPosition() const;
float GetFacingYaw() const;
bool WasJumpedThisFrame() const;
bool WasLandedThisFrame() const;
bool IsOnGround() const;
bool WasFallDeath() const;
void ClearFallDeath();
};
Update() Flow
Blupi::Update(dt):
1. Read rotation input (LEFT/RIGHT/Q/E) → update facingYaw_
2. Compute forward/right vectors from facingYaw_
3. Read movement input (UP/W, DOWN/S) → forward
4. Read strafe input (A, D) → strafe
5. vel_.x/z = (fwd * fwd + right * strafe) * kMoveSpeed
6. vel_.y += kGravity * dt; vel_.y = max(vel_.y, -30)
7. Jump: if onGround_ and Space pressed → vel_.y = kJumpSpeed
8. pos.y += vel_.y * dt
9. ResolveY(pos) ← vertical AABB
10. pos.x += vel_.x * dt; pos.z += vel_.z * dt
11. ResolveXZ(pos) ← horizontal AABB
12. Fall death: if pos.y < -10 → fallDeath_=true, SpawnAt(spawn_)
13. node_->SetPosition(pos)
14. Update animation state and animTick_
15. Invincibility flash update
16. UpdateSprite() ← update billboard UV
Collision
ResolveY(pos): checks solid blocks below/above Blupi using IsSolid(wx, wy, wz).
When falling and foot hits a solid block top, Blupi is snapped to the block top + kHalfH.
When rising and head hits a block, Y velocity is zeroed.
ResolveXZ(pos): checks 4 side edges (±kHalfW in X and Z) at body Y height.
Floor blocks at wy=0 are excluded from wall collision via bodyWY = round(pos.y).
Block solidity: IsSolid(wx, wy, wz) returns !world->getBlock(...).isAir().
Out-of-bounds negative coordinates → solid. Out-of-bounds positive → air.
Sprite Animation
Animation frames are looked up via Tables::GetBlupiIcon(action_, animTick_ / 3).
Dividing by 3 matches the original 20 fps animation at a 60 fps update rate.
UV coordinates are computed from the icon index into blupi.png
(600×2040 px, 10 cols, 60×60 tiles).
Invincibility Flash
StartFlash(duration) sets flashTimer_.
Every 0.1 s while active, the billboard's enabled_ flag is toggled.
On expiry, the sprite is forced visible.
Dependencies
- Urho3D: Scene, Context, Input, BillboardSet, Texture2D, Material
World(read-only reference for collision)Tables(static animation frame lookup)