diff --git a/.vscode/.BROWSE.VC.DB b/.vscode/.BROWSE.VC.DB index c87292e..47d7f1b 100644 Binary files a/.vscode/.BROWSE.VC.DB and b/.vscode/.BROWSE.VC.DB differ diff --git a/.vscode/.BROWSE.VC.DB-shm b/.vscode/.BROWSE.VC.DB-shm index a0bfb94..5ddeaa9 100644 Binary files a/.vscode/.BROWSE.VC.DB-shm and b/.vscode/.BROWSE.VC.DB-shm differ diff --git a/Observer.h b/Observer.h index fe38c29..eae5e65 100644 --- a/Observer.h +++ b/Observer.h @@ -1,12 +1,14 @@ // observer.h #ifndef OBSERVER_H #define OBSERVER_H +#include "obstacle.h" + +class Obstacle; class Observer { public: virtual ~Observer() {} virtual void CollisionUpdate(bool is_collision)=0; - virtual void deceaseSpeed(int newSpeed)=0; }; #endif // OBSERVER_H \ No newline at end of file diff --git a/Observer_pattern_tutorial/README.md b/Observer_pattern_tutorial/README.md deleted file mode 100644 index 23d2642..0000000 --- a/Observer_pattern_tutorial/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# Tutorial: Implementing the Observer Pattern in a Splashkit Project - -## 1. Overview of the Observer Pattern - -The Observer Pattern is a behavioral design pattern used to manage and synchronize the state between objects. In this tutorial, we will explore how this pattern is implemented in our game project to handle interactions between the `Player` and `Obstacle` classes. - -## 2. Code Structure - -### a. Header Files Overview - -#### `Subject.h` - -- **Purpose:** Defines the `Subject` class, which manages a list of observers and provides methods to attach, detach, and notify them. -- **Key Methods:** - - `attach(Observer* observer)` - - `detach(Observer* observer)` - - `notify(Observer* observer, bool is_collision)` - -#### `Observer.h` - -- **Purpose:** Defines the `Observer` interface with methods that the `Subject` can call to update observers. -- **Key Methods:** - - `CollisionUpdate(bool is_collision)` - - `deceaseSpeed(int newSpeed)` - -### b. Class Implementations - -#### `Player.h` and `Player.cpp` - -- **Purpose:** Implements the `Player` class, which is a `Subject` and notifies its observers about state changes. -- **Key Methods:** - - `notify(Observer* observer, bool is_collision)` - - `notify_all_observers()` - - **Explanation:** How these methods manage the observers list and call their methods. - -#### `Obstacle.h` and `Obstacle.cpp` - -- **Purpose:** Implements the `Obstacle` class, which is an `Observer` and reacts to notifications from the `Player`. -- **Key Methods:** - - `CollisionUpdate(bool is_collision)` - - `deceaseSpeed(int newSpeed)` - - **Explanation:** How the `Obstacle` updates its state based on notifications. - -## 3. Detailed Code Flow Explanation - -### a. Attaching Observers - -- **In `Player.cpp`:** - - The `attach` method in `Player` adds an `Observer` to its list. - - Example: - ```cpp - player.attach(&newObstacle); - ``` - -### b. Notifying Observers - -- **In `Player.cpp`:** - - **`notify` Method:** Calls `CollisionUpdate` on a specific observer. - - **`notify_all_observers` Method:** Iterates over all observers and calls `deceaseSpeed`. - - Example: - ```cpp - void Player::notify_all_observers() { - for (auto observer : observers) { - observer->deceaseSpeed(newSpeed); - } - } - ``` - -- **In `Obstacle.cpp`:** - - **`CollisionUpdate` Method:** Updates the obstacle’s state based on a collision notification. - - **`deceaseSpeed` Method:** Adjusts the obstacle’s speed based on the player's notification. - - Example: - ```cpp - void Obstacle::CollisionUpdate(bool is_collision) { - if (is_collision) { - // Handle collision - } - } - ``` - -## 4. Example Use Case in Your Game - -### Scenario - -When an obstacle collides with the player, the `Player` will notify the `Obstacle`: - -1. **Collision Detected:** `Player::notify` is called. -2. **Notify All Observers:** `Player::notify_all_observers` updates all attached `Obstacle` instances. -3. **Obstacle Processes Notification:** Each `Obstacle` processes the notification and updates its state. - -## 5. Code Demonstration - -### Attaching an Observer - -```cpp -// Example: Attaching an obstacle to the player -player.attach(&obstacle); -``` - -### Notifying a Specific Observer -```cpp -// Example: Notifying a specific observer about a collision -player.notify(&obstacle, true); -``` - -### Notifying All Observers -```cpp -// Example: Notifying all observers about a state change -player.notify_all_observers(); -``` -## 6.Conclusion -In this tutorial, we explored how the Observer Pattern can be applied to manage interactions between game entities in a Splashkit project. This pattern helps in efficiently managing state changes and interactions, making the codebase more modular and easier to maintain. diff --git a/bullet_factory.cpp b/bullet_factory.cpp new file mode 100644 index 0000000..9b3b4e1 --- /dev/null +++ b/bullet_factory.cpp @@ -0,0 +1,63 @@ +#include "bullet_factory.h" +#include "globals.h" +#include +#include + +std::vector BulletFactory::SprayProjectiles(ProjectileType type, point_2d origin, vector_2d direction, int count, float spread) { + std::vector bullets; + + float angleStep = spread / (count - 1); + float startAngle = -spread / 2; + + for (int i = 0; i < count; i++) { + float angle = startAngle + i * angleStep; + vector_2d rotatedDirection = RotateVector(direction, angle); + + sprite bullet = CreateBullet(type, origin, rotatedDirection); + if (bullet) { + bullets.push_back(bullet); + std::cout << "Created bullet at (" << sprite_x(bullet) << ", " << sprite_y(bullet) << ")" << std::endl; + } + } + + return bullets; +} + +sprite BulletFactory::CreateBullet(ProjectileType type, point_2d origin, vector_2d direction) { + if (!bitmap_valid(bullet)) { + std::cout << "Error: Could not load bullet bitmap in CreateBullet" << std::endl; + return nullptr; + } + + sprite bullet_sprite = create_sprite(bullet); + + // Scale the sprite + sprite_set_scale(bullet_sprite,0.2); // Scale by 50% + + sprite_set_position(bullet_sprite, origin); + + vector_2d velocity; + switch (type) { + case ProjectileType::NORMAL: + velocity = vector_multiply(direction, 5); + break; + case ProjectileType::FAST: + velocity = vector_multiply(direction, 10); + break; + case ProjectileType::EXPLOSIVE: + velocity = vector_multiply(direction, 3); + break; + } + sprite_set_velocity(bullet_sprite, velocity); + + return bullet_sprite; +} + +vector_2d BulletFactory::RotateVector(vector_2d vec, float angle) { + float cos_a = std::cos(angle); + float sin_a = std::sin(angle); + return vector_2d{ + vec.x * cos_a - vec.y * sin_a, + vec.x * sin_a + vec.y * cos_a + }; +} \ No newline at end of file diff --git a/bullet_factory.h b/bullet_factory.h new file mode 100644 index 0000000..9d3d55a --- /dev/null +++ b/bullet_factory.h @@ -0,0 +1,22 @@ +#ifndef BULLET_FACTORY_H +#define BULLET_FACTORY_H + +#include "splashkit.h" +#include + +enum class ProjectileType { + NORMAL, + FAST, + EXPLOSIVE +}; + +class BulletFactory { +public: + static std::vector SprayProjectiles(ProjectileType type, point_2d origin, vector_2d direction, int count, float spread); + +private: + static sprite CreateBullet(ProjectileType type, point_2d origin, vector_2d direction); + static vector_2d RotateVector(vector_2d vec, float angle); +}; + +#endif // BULLET_FACTORY_H \ No newline at end of file diff --git a/game.exe b/game.exe index 0dee3e1..19099e7 100644 Binary files a/game.exe and b/game.exe differ diff --git a/globals.h b/globals.h index fbdcb68..1737ed4 100644 --- a/globals.h +++ b/globals.h @@ -4,12 +4,10 @@ extern bitmap background; extern bitmap bee; extern bitmap box; +extern bitmap bullet; extern float player_posx; extern float player_posy; extern int RIGHT_BOUNDARY ; extern int LEFT_BOUNDARY ; extern int GRAVITY; -extern int WINDOW_WIDTH ; -extern int WINDOW_HEIGHT ; -extern int spawn_interval; -extern float BEE_SCALE; \ No newline at end of file +extern int spawn_interval; \ No newline at end of file diff --git a/images/bullet.png b/images/bullet.png new file mode 100644 index 0000000..e2bc886 Binary files /dev/null and b/images/bullet.png differ diff --git a/obstacle.cpp b/obstacle.cpp index 15b38a5..0081c4b 100644 --- a/obstacle.cpp +++ b/obstacle.cpp @@ -31,10 +31,5 @@ void Obstacle::CollisionUpdate(bool is_collision) { } -void Obstacle::deceaseSpeed(int newSpeed){ - this->speed = newSpeed; - std::cout << "The Speed equal to 2 now" << std::endl; -} - diff --git a/obstacle.h b/obstacle.h index 6d851fc..9987834 100644 --- a/obstacle.h +++ b/obstacle.h @@ -15,7 +15,7 @@ class Obstacle : public Observer { void update(); void draw(); void CollisionUpdate(bool is_collision); - void deceaseSpeed(int newSpeed); + private: float x, y, width, height, speed; diff --git a/player.cpp b/player.cpp index 0838c8b..aa7deff 100644 --- a/player.cpp +++ b/player.cpp @@ -3,19 +3,14 @@ #include "globals.h" #include #include -#include -#include - Player::Player(float x, float y, float speed) { this->x = x; this->y = y; this->speed = speed; - this->width = bitmap_width(bee)*BEE_SCALE; // Assuming 'bee' is the bitmap for the player - this->height = bitmap_height(bee)*BEE_SCALE; + this->width = bitmap_width(bee); // Assuming 'bee' is the bitmap for the player + this->height = bitmap_height(bee); } -int Player::HP = 3; // Initialize the static HP variable - void Player::move_right() { x += speed; @@ -32,25 +27,9 @@ void Player::attach(Observer* observer) { } void Player::detach(Observer* observer) { - auto it = std::remove(observers.begin(), observers.end(), observer); - if (it != observers.end()) { - std::cout << "Detaching observer" << std::endl; - observers.erase(it, observers.end()); - } + observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end()); } - void Player::notify(Observer* observer, bool is_collision) { observer->CollisionUpdate(is_collision); // Call onCollision on the observer, passing this obstacle -} - -void Player::notify_all_observers() { - std::cout << "Notifying all observers..." << std::endl; - for (Observer* observer : observers) { - if (observer == nullptr) { - std::cout << "Observer is null!" << std::endl; - continue; // Skip null observers - } - observer->deceaseSpeed(1); - } -} +} \ No newline at end of file diff --git a/player.h b/player.h index bd65d44..2b8603c 100644 --- a/player.h +++ b/player.h @@ -6,9 +6,7 @@ #include "obstacle.h" #include #include "Observer.h" -#include class Player : public Subject { - public: Player(float x, float y, float speed); void move_right(); @@ -18,15 +16,12 @@ class Player : public Subject { float get_width() { return width; } float get_height() { return height; } float get_speed() { return speed; } - static int get_HP(){return HP;} - static void set_HP(int hp){HP = hp;} - void attach(Observer* observer) override; + void attach(class Observer* observer) ; void detach(class Observer* observer) ; void notify(class Observer* observer, bool is_collision); - void notify_all_observers(); + private: float x, y, speed, width, height; - static int HP; std::vector observers; }; diff --git a/program.cpp b/program.cpp index 9c36c87..960777d 100644 --- a/program.cpp +++ b/program.cpp @@ -1,84 +1,39 @@ // import #include "splashkit.h" -#include "globals.h" // <- added this import -//#include +#include "globals.h" // Keep this from HEAD #include #include "player.h" #include "obstacle.h" #include "Observer.h" #include "Subject.h" #include -#include -//skm g++ program.cpp player.cpp obstacle.cpp -o game.exe +#include "bullet_factory.h" // Keep this from HEAD +#include // Keep this from origin/main +#include +#include + +//skm g++ program.cpp player.cpp obstacle.cpp bullet_factory.cpp -o game.exe bitmap background = bitmap_named("images/Background.jpg"); bitmap bee = bitmap_named("images/Bee.png"); bitmap box = bitmap_named("images/box.png"); -float player_posx = 550.0f; -float player_posy = 650.0f; -int RIGHT_BOUNDARY = 1200; +bitmap bullet = bitmap_named("images/bullet.png"); // Keep this from HEAD + +float player_posx = 550.0f; // Keep the updated player position from origin/main +float player_posy = 650.0f; // Keep this from origin/main +int RIGHT_BOUNDARY = 1200; // Keep the updated RIGHT_BOUNDARY from origin/main int LEFT_BOUNDARY = 0; int GRAVITY = 3; -int spawn_interval = 60;// Spawn obstacles at a rate of 1 per second -int WINDOW_WIDTH = 1280; +int spawn_interval = 60; // This is the same in both branches +int WINDOW_WIDTH = 1280; // Keep additional variables from origin/main int WINDOW_HEIGHT = 960; float BEE_SCALE = 0.6; -bool game_started = false; // Flag to track the game status -bool game_over = false; // Flag for game over status -int game_time = 0; // Time in seconds +bool game_started = false; // Keep these from origin/main +bool game_over = false; +int game_time = 0; timer my_timer; -// Function declarations -void start_game(); -void update_timer(); -void display_timer(); -void display_start_screen(); -void player_move(Player* player); -void Spawn_obstacle(std::vector>& obstacles, Player* player, int& spawn_timer); -void render(std::vector>& obstacles, Player& player); -void check_game_over(std::vector>& obstacles,Player& player); -void display_game_over_screen(); - -void start_game() { - game_started = true; - game_over = false; - Player::set_HP(3); - - // Reset obstacles, timer, etc. - reset_timer(my_timer); // Reset timer when game starts - start_timer(my_timer); -} - -void update_timer() { - game_time = timer_ticks(my_timer) / 1000; // Convert milliseconds to seconds -} - -void display_timer() { - draw_text("Time: " + std::to_string(game_time), COLOR_BLACK, "Arial", 60, 50, 50); - draw_text("Health: " + std::to_string(Player::get_HP()), COLOR_BLACK, "Arial", 60, 150, 50); -} - -void display_start_screen() { - - draw_text("Press SPACE to Start", COLOR_BLACK, "Arial", 200, 550, 200); -} - -void check_game_over(std::vector>& obstacles,Player& player) { - if (Player::get_HP() == 1) { - player.notify_all_observers(); - } - else if (Player::get_HP() <= 0) { - game_over = true; - game_started = false; // Stop the game - obstacles.clear(); - } -} - -void display_game_over_screen() { - draw_text("Game Over!", COLOR_RED, "Arial", 48, 510, 450); - draw_text("Press SPACE to Restart", COLOR_WHITE, "Arial", 32, 510, 500); -} template bool is_colliding(T& obj1, U& obj2) { @@ -93,7 +48,10 @@ bool is_colliding(T& obj1, U& obj2) { float h2 = obj2.get_height(); // Check for collision - return (x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2); + return (x1 < x2 + w2 && + x1 + w1 > x2 && + y1 < y2 + h2 && + y1 + h1 > y2); } // Function to handle collision between two objects @@ -101,13 +59,12 @@ template void handle_collision(T& subject, U& observer) { if (is_colliding(subject, observer)) { if (!observer.get_collision()) { // Collision started - subject.notify(&observer, true); - Player::set_HP(Player::get_HP()-1); // Decrease player health on collision + subject.notify(&observer,true); } - draw_text("Collision detected!", COLOR_BLACK, "Arial", 24, subject.get_x() + 10, subject.get_y() - 50); + draw_text("Collision detected!", COLOR_BLACK, "Arial", 24, subject.get_x()+10, subject.get_y() - 50); } else { if (observer.get_collision()) { // Collision ended - subject.notify(&observer, false); + subject.notify(&observer,false); } } } @@ -121,6 +78,21 @@ void player_move(Player* player) { } } +// Keep the bullet shooting logic from HEAD +void shoot_bullets(Player& player, std::vector& bullets) { + if (key_typed(SPACE_KEY)) { + point_2d origin = point_at(player.get_x(), player.get_y()); // Adjust based on player sprite size + vector_2d direction = vector_to(0, -1); // Shoot upwards + int bulletCount = 5; + float spreadAngle = 45 * (std::atan(1) * 4 / 180); // 45 degrees in radians + + std::vector newBullets = BulletFactory::SprayProjectiles(ProjectileType::NORMAL, origin, direction, bulletCount, spreadAngle); + bullets.insert(bullets.end(), newBullets.begin(), newBullets.end()); + + std::cout << "Created " << newBullets.size() << " bullets" << std::endl; + } +} + void Spawn_obstacle(std::vector>& obstacles, Player* player, int& spawn_timer) { spawn_timer++; if (spawn_timer >= spawn_interval) { @@ -132,31 +104,33 @@ void Spawn_obstacle(std::vector>& obstacles, Player* p } } - -void render(std::vector>& obstacles, Player& player) { - // Redrawing the bitmap after every clear background and bee - double center_x = player.get_x()+(player.get_width()/2); - double center_y = player.get_y()+(player.get_height()/2); +void render(std::vector>& obstacles, Player& player, std::vector& bullets) { + // Redraw background and bee + double center_x = player.get_x() + (player.get_width() / 2); + double center_y = player.get_y() + (player.get_height() / 2); draw_bitmap(background, 0, 0, option_to_screen()); - drawing_options scale_options = option_scale_bmp(BEE_SCALE+0.1, BEE_SCALE+0.1); // Scale to 70% of original size - draw_bitmap(bee, player.get_x()-50, player.get_y()-50,scale_options); - - // Get the circle that encompasses the scaled bitmap - - point_2d bee_position = point_at(center_x,center_y); - circle scaled_bee_circle = bitmap_cell_circle(bee, bee_position,BEE_SCALE); - - // Draw the circle for debugging - draw_circle(COLOR_RED,scaled_bee_circle); + drawing_options scale_options = option_scale_bmp(BEE_SCALE + 0.1, BEE_SCALE + 0.1); + draw_bitmap(bee, player.get_x() - 50, player.get_y() - 50, scale_options); - // Update and draw obstacles + // Draw obstacles for (const auto& obstacle_ptr : obstacles) { - // Dereference the unique_ptr to access the Obstacle object Obstacle& obstacle = *obstacle_ptr; obstacle.update(); obstacle.draw(); handle_collision(player, obstacle); } + + // Draw bullets + for (auto it = bullets.begin(); it != bullets.end();) { + update_sprite(*it); + if (sprite_y(*it) < -50 || sprite_y(*it) > WINDOW_HEIGHT || sprite_x(*it) < -50 || sprite_x(*it) > WINDOW_WIDTH) { + free_sprite(*it); + it = bullets.erase(it); + } else { + draw_sprite(*it); + ++it; + } + } } @@ -165,11 +139,9 @@ int main() { hide_mouse(); // Hide mouse while cursor is over the game window Player player(player_posx, player_posy, 10.0f); // Initialize player std::vector> obstacles; // List of obstacles - - // Timer for obstacle spawning - int spawn_timer = 0; + std::vector bullets; // List of bullets - // Initialize timer + int spawn_timer = 0; my_timer = create_timer("GameTimer"); while (!quit_requested()) { @@ -178,40 +150,31 @@ int main() { if (!game_started) { draw_bitmap(background, 0, 0, option_to_screen()); - drawing_options scale_options = option_scale_bmp(BEE_SCALE+0.1, BEE_SCALE+0.1); // Scale to 40% of original size - draw_bitmap(bee, player.get_x()-50, player.get_y()-50,scale_options); display_start_screen(); if (key_down(SPACE_KEY)) { - start_game(); } refresh_screen(60); - continue; // Skip the rest of the loop until the game starts + continue; } if (game_over) { - display_game_over_screen(); - if (key_down(SPACE_KEY)) { - start_game(); // Restart game + start_game(); } refresh_screen(60); - continue; // Skip the rest of the loop until restart + continue; } player_move(&player); - - // Spawn obstacles Spawn_obstacle(obstacles, &player, spawn_timer); + shoot_bullets(player, bullets); + render(obstacles, player, bullets); - // Render game objects - render(obstacles, player); - - // Update game elements update_timer(); display_timer(); - check_game_over(obstacles,player); + check_game_over(obstacles, player); refresh_screen(60); }