Entités¶
Types d'entités dans R-Type.
Hiérarchie¶
classDiagram
class Entity {
+uint32_t id
+float x, y
+float vx, vy
+update(dt)
+getAABB() AABB
}
class Player {
+int health
+float shootCooldown
+uint32_t lastAckedInput
+applyInput(Input)
+shoot()
}
class Enemy {
+EnemyType type
+int health
+updateAI(world)
}
class Missile {
+uint32_t ownerId
+bool isEnemy
+int damage
}
Entity <|-- Player
Entity <|-- Enemy
Entity <|-- Missile
Player¶
class Player : public Entity {
public:
static constexpr int MAX_HEALTH = 100;
static constexpr float SPEED = 300.0f;
static constexpr float SHOOT_COOLDOWN = 0.2f; // 5 tirs/sec
static constexpr int WIDTH = 64;
static constexpr int HEIGHT = 32;
int health_ = MAX_HEALTH;
float shootCooldown_ = 0;
uint32_t lastAckedInput_ = 0;
uint8_t shipSkin_ = 1;
void update(float dt) override {
if (shootCooldown_ > 0)
shootCooldown_ -= dt;
}
bool canShoot() const {
return shootCooldown_ <= 0;
}
void shoot() {
shootCooldown_ = SHOOT_COOLDOWN;
}
void takeDamage(int damage) {
health_ -= damage;
if (health_ <= 0) {
health_ = 0;
alive_ = false;
}
}
AABB getAABB() const override {
return {x_, y_, WIDTH, HEIGHT};
}
};
Types d'Ennemis (5 types)¶
1. Basic¶
Mouvement linéaire simple.
class BasicEnemy : public Enemy {
public:
static constexpr float SPEED = 150.0f;
static constexpr int HEALTH = 1;
void updateAI(GameWorld& world) override {
vx_ = -SPEED; // Vers la gauche
}
};
2. Zigzag¶
Mouvement en zigzag.
class ZigzagEnemy : public Enemy {
float phase_ = 0;
public:
static constexpr float SPEED_X = 100.0f;
static constexpr float SPEED_Y = 200.0f;
static constexpr float FREQ = 3.0f;
void updateAI(GameWorld& world) override {
vx_ = -SPEED_X;
vy_ = std::sin(phase_) * SPEED_Y;
phase_ += FREQ * TICK_DURATION;
}
};
3. Follower¶
Suit le joueur le plus proche.
class FollowerEnemy : public Enemy {
public:
static constexpr float SPEED = 80.0f;
void updateAI(GameWorld& world) override {
auto* target = world.findNearestPlayer(x_, y_);
if (!target) return;
float dx = target->x - x_;
float dy = target->y - y_;
float dist = std::sqrt(dx*dx + dy*dy);
if (dist > 0) {
vx_ = (dx / dist) * SPEED;
vy_ = (dy / dist) * SPEED;
}
}
};
4. Shooter¶
Tire des projectiles.
class ShooterEnemy : public Enemy {
float shootTimer_ = 0;
public:
static constexpr float SHOOT_INTERVAL = 2.0f;
void updateAI(GameWorld& world) override {
vx_ = -50.0f; // Lent
shootTimer_ -= TICK_DURATION;
if (shootTimer_ <= 0) {
world.spawnEnemyMissile(x_, y_);
shootTimer_ = SHOOT_INTERVAL;
}
}
};
5. Boss¶
Ennemi de fin de niveau.
class BossEnemy : public Enemy {
enum class Phase { Enter, Attack, Rage };
Phase phase_ = Phase::Enter;
float phaseTimer_ = 0;
public:
static constexpr int HEALTH = 50;
static constexpr int WIDTH = 256;
static constexpr int HEIGHT = 128;
void updateAI(GameWorld& world) override {
switch (phase_) {
case Phase::Enter:
// Entrer lentement
if (x_ > WORLD_WIDTH - 300) {
vx_ = -50.0f;
} else {
phase_ = Phase::Attack;
}
break;
case Phase::Attack:
// Pattern d'attaque
attackPattern(world);
if (health_ < HEALTH / 2) {
phase_ = Phase::Rage;
}
break;
case Phase::Rage:
// Plus agressif
ragePattern(world);
break;
}
}
};
Missiles¶
class Missile : public Entity {
public:
uint32_t ownerId_;
bool isEnemy_;
int damage_;
static constexpr float PLAYER_MISSILE_SPEED = 600.0f;
static constexpr float ENEMY_MISSILE_SPEED = 300.0f;
static constexpr int WIDTH = 16;
static constexpr int HEIGHT = 8;
Missile(uint32_t owner, bool enemy)
: ownerId_(owner)
, isEnemy_(enemy)
, damage_(enemy ? 20 : 10)
{
vx_ = enemy ? -ENEMY_MISSILE_SPEED : PLAYER_MISSILE_SPEED;
}
bool isOutOfBounds() const {
return x_ < -WIDTH || x_ > WORLD_WIDTH + WIDTH;
}
};
Résumé¶
| Type | Health | Vitesse | Comportement |
|---|---|---|---|
| Basic | 1 | 150 | Ligne droite |
| Zigzag | 2 | 100 | Sinusoïdal |
| Follower | 2 | 80 | Suit joueur |
| Shooter | 3 | 50 | Tire projectiles |
| Boss | 50 | Variable | Patterns |