Back to Home

Dot Game V2

So I took a few days off. Thought about where I wanna go with this.

A note on reading this: scroll down to the main() function, remember its broad strokes, don't dig until you gotta.

AI: You're not going to talk about the teleporting target dot?

I'm sure they'll notice. Why rush things?

Enjoy:

  1. Dot Game (Inspired by "Dot Chase")
    • A single dot moves randomly on the screen, and the player controls another dot to "catch" it.
    • Learn: Basic rendering, input handling, and collision detection.
#include "raylib.h"
#include <cstdlib>
#include <ctime>
#include <memory>
#include <vector>

#ifdef PLATFORM_WEB
#include <emscripten/emscripten.h>
#endif

// Global constants
const int screenWidth = 800;
const int screenHeight = 600;

// Base Dot class
class Dot {
protected:
  Vector2 position;
  float radius;
  Color color;

public:
  Dot(Vector2 startPos, float radius, Color color)
      : position(startPos), radius(radius), color(color) {}

  virtual void Control(float deltaTime) {
    // Default behavior: no movement
  }

  void Draw() const { DrawCircleV(position, radius, color); }

  Vector2 GetPosition() const { return position; }

  float GetRadius() const { return radius; }

  void SetPosition(Vector2 newPos) { position = newPos; }
};

// Player class (inherits from Dot)
class Player : public Dot {
private:
  float speed;

public:
  Player(Vector2 startPos, float radius, Color color, float speed)
      : Dot(startPos, radius, color), speed(speed) {}

  virtual ~Player() {}

  void Control(float deltaTime) override {
    if (IsKeyDown(KEY_W))
      position.y -= speed * deltaTime;
    if (IsKeyDown(KEY_S))
      position.y += speed * deltaTime;
    if (IsKeyDown(KEY_A))
      position.x -= speed * deltaTime;
    if (IsKeyDown(KEY_D))
      position.x += speed * deltaTime;

    // Keep player within screen bounds
    if (position.x < radius)
      position.x = radius;
    if (position.x > screenWidth - radius)
      position.x = screenWidth - radius;
    if (position.y < radius)
      position.y = radius;
    if (position.y > screenHeight - radius)
      position.y = screenHeight - radius;
  }
};

// Target class (inherits from Dot)
class Target : public Dot {
public:
  Target(Vector2 startPos, float radius, Color color)
      : Dot(startPos, radius, color) {}

  virtual ~Target() {}

  void Respawn(Vector2 newPos) { position = newPos; }
};

// PositionManager class
class PositionManager {
private:
  std::vector<Dot *> dots;

public:
  void AddDot(Dot *dot) { dots.push_back(dot); }

  bool IsPositionValid(Vector2 newPos, float radius) const {
    for (const Dot *dot : dots) {
      if (CheckCollisionCircles(newPos, radius, dot->GetPosition(),
                                dot->GetRadius())) {
        return false;
      }
    }
    return true;
  }

  Vector2 GetValidPosition(float radius) {
    Vector2 newPos;
    do {
      newPos = {static_cast<float>(rand() % screenWidth),
                static_cast<float>(rand() % screenHeight)};
    } while (!IsPositionValid(newPos, radius));
    return newPos;
  }
};

// Game class
class Game {
private:
  std::unique_ptr<Player> player;
  std::unique_ptr<Target> target;
  PositionManager positionManager;
  int score;

public:
  Game() : score(0) {
    // Initialize player and target
    player = std::make_unique<Player>(
        Vector2{screenWidth / 2.0f, screenHeight / 2.0f}, 15.0f, BLUE, 200.0f);
    target = std::make_unique<Target>(positionManager.GetValidPosition(10.0f),
                                      10.0f, RED);

    // Register dots with the position manager
    positionManager.AddDot(player.get());
    positionManager.AddDot(target.get());
  }

  void Update(float deltaTime) {
    // Update player
    player->Control(deltaTime);

    // Check collision between player and target
    if (!positionManager.IsPositionValid(target->GetPosition(),
                                         target->GetRadius())) {
      score++;
      target->Respawn(positionManager.GetValidPosition(target->GetRadius()));
    }
  }

  void Render() const {
    // Draw game elements
    BeginDrawing();
    ClearBackground(RAYWHITE);

    DrawText("Catch the moving dot!", 10, 10, 20, DARKGRAY);
    DrawText(TextFormat("Score: %d", score), 10, 40, 20, DARKGRAY);

    player->Draw();
    target->Draw();

    EndDrawing();
  }

  bool IsRunning() const { return !WindowShouldClose(); }
};

Game *gameInstance = nullptr;

void MainLoop() {
  float deltaTime = GetFrameTime();
  gameInstance->Update(deltaTime);
  gameInstance->Render();
}

int main() {
  // Initialization
  InitWindow(screenWidth, screenHeight, "Dot Game - Catch the Dot");
  SetTargetFPS(60);

  // Seed random number generator
  std::srand(std::time(nullptr));

  // Create game object
  Game game;
  gameInstance = &game;

#ifdef PLATFORM_WEB
  emscripten_set_main_loop(MainLoop, 0, 1);
#else
  while (game.IsRunning()) {
    float deltaTime = GetFrameTime();
    game.Update(deltaTime);
    game.Render();
  }
#endif

  CloseWindow();

  return 0;
}
Next: Debugging, Part 1 Previous: Refactoring Code, Part 2