diff --git a/README.md b/README.md index dc26f19..79b4342 100644 --- a/README.md +++ b/README.md @@ -1 +1,43 @@ # GameX + +## Game Controls: + +#### Movement + +- **W:** Move the ball forward +- **A:** Move the ball left +- **S:** Move the ball backward +- **D:** Move the ball right +- **R:** Rotate the ball with angular velocity in the z-direction +- **J:** Jump, perform a double jump while falling + +#### Difficulty Modes + +- **0:** Switch to original mode +- **1:** Switch to easy mode +- **2:** Switch to medium mode +- **3:** Switch to difficult mode + +## Gameplay: + +Welcome to the ball game! + +Your mission is to navigate your ball strategically to defeat the stationary enemy. Master the controls to adjust direction, rotation, and execute jumps to defeat your enemy in a more fantasy way. When starting the game, your adversary remains motionless, providing you with an opportunity to plan your moves. Choose the level of challenge that suits you by pressing the corresponding number key. + +## End of Game: + +The game concludes when the ball falls from the platform. At this point, the results of your game will be displayed in a new window. The results may include your score. A new game will restart automatically after falling down for a few seconds. + +### Known Issues: + +After the results window closes, the mouse focus may not automatically return to the game window. To resume playing, simply click the mouse once within the game window. In addition, if you have any suggestions to enhance the visual appeal of the window, I would greatly appreciate hearing them. + +## Tips: + +* Experiment with the controls to get comfortable with the ball's movement and rotation. +* Timing is crucial, especially when performing jumps and double jumps. +* Pay attention to the environment and adapt your strategy to overcome different challenges. + +Have a great time playing the Ball Game! If you encounter any issues or have suggestions for improvements, feel free to provide feedback. + +#### Enjoy the game and have fun rolling your way to victory! diff --git a/external/.DS_Store b/external/.DS_Store new file mode 100644 index 0000000..a930e2b Binary files /dev/null and b/external/.DS_Store differ diff --git a/src/GameBall/core/game_ball.cpp b/src/GameBall/core/game_ball.cpp index a47b79e..067113e 100644 --- a/src/GameBall/core/game_ball.cpp +++ b/src/GameBall/core/game_ball.cpp @@ -37,6 +37,8 @@ void GameBall::OnInit() { auto primary_player = world->CreatePlayer(); auto enemy_player = world->CreatePlayer(); + primary_player->is_enemy = false; + enemy_player->is_enemy = true; auto primary_unit = world->CreateUnit( primary_player->PlayerId(), glm::vec3{0.0f, 1.0f, 0.0f}, 1.0f, 1.0f); auto enemy_unit = world->CreateUnit( diff --git a/src/GameBall/logic/player.h b/src/GameBall/logic/player.h index ef33e2e..9fb5893 100644 --- a/src/GameBall/logic/player.h +++ b/src/GameBall/logic/player.h @@ -20,6 +20,8 @@ class Player { PlayerInput TakePlayerInput(); + bool is_enemy; + private: World *world_; uint64_t player_id_{}; diff --git a/src/GameBall/logic/player_input.cpp b/src/GameBall/logic/player_input.cpp index c002a91..c8dec53 100644 --- a/src/GameBall/logic/player_input.cpp +++ b/src/GameBall/logic/player_input.cpp @@ -12,6 +12,12 @@ PlayerInput PlayerInputController::GetInput() { input_.move_backward = (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS); input_.move_left = (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS); input_.move_right = (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS); + input_.rotate_z = (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS); + input_.move_up = (glfwGetKey(window, GLFW_KEY_J) == GLFW_PRESS); + input_.easy_mode = (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS); + input_.medium_mode = (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS); + input_.difficult_mode = (glfwGetKey(window, GLFW_KEY_3) == GLFW_PRESS); + input_.original_mode = (glfwGetKey(window, GLFW_KEY_0) == GLFW_PRESS); input_.brake = (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS); auto camera_controller = app_->CameraController(); auto pitch_yaw = camera_controller->GetPitchYaw(); diff --git a/src/GameBall/logic/player_input.h b/src/GameBall/logic/player_input.h index 98b60c0..a8e0cb3 100644 --- a/src/GameBall/logic/player_input.h +++ b/src/GameBall/logic/player_input.h @@ -11,6 +11,12 @@ struct PlayerInput { bool move_backward{false}; bool move_left{false}; bool move_right{false}; + bool rotate_z{false}; + bool move_up{false}; + bool easy_mode{false}; + bool medium_mode{false}; + bool difficult_mode{false}; + bool original_mode{false}; bool brake{false}; glm::vec3 orientation{0.0f, 0.0f, 1.0f}; }; diff --git a/src/GameBall/logic/units/regular_ball.cpp b/src/GameBall/logic/units/regular_ball.cpp index af1cc95..3281f58 100644 --- a/src/GameBall/logic/units/regular_ball.cpp +++ b/src/GameBall/logic/units/regular_ball.cpp @@ -1,6 +1,7 @@ #include "GameBall/logic/units/regular_ball.h" #include "GameBall/core/game_ball.h" +#include "GameBall/logic/units/settlement.h" #include "GameBall/logic/world.h" namespace GameBall::Logic::Units { @@ -45,47 +46,113 @@ void RegularBall::UpdateTick() { auto physics_world = world_->PhysicsWorld(); auto &sphere = physics_world->GetSphere(sphere_id_); - // auto owner = world_->GetPlayer(player_id_); - // if (owner) { - // if (UnitId() == owner->PrimaryUnitId()) { - // auto input = owner->TakePlayerInput(); - // - // glm::vec3 forward = glm::normalize(glm::vec3{input.orientation}); - // glm::vec3 right = - // glm::normalize(glm::cross(forward, glm::vec3{0.0f, 1.0f, 0.0f})); - // - // glm::vec3 moving_direction{}; - // - // float angular_acceleration = glm::radians(2880.0f); - // - // if (input.move_forward) { - // moving_direction -= right; - // } - // if (input.move_backward) { - // moving_direction += right; - // } - // if (input.move_left) { - // moving_direction -= forward; - // } - // if (input.move_right) { - // moving_direction += forward; - // } - // - // if (glm::length(moving_direction) > 0.0f) { - // moving_direction = glm::normalize(moving_direction); - // sphere.angular_velocity += - // moving_direction * angular_acceleration * delta_time; - // } - // - // if (input.brake) { - // sphere.angular_velocity = glm::vec3{0.0f}; - // } - // } - // } - + auto owner = world_->GetPlayer(player_id_); sphere.velocity *= std::pow(0.5f, delta_time); sphere.angular_velocity *= std::pow(0.2f, delta_time); + if (owner) { + auto input = owner->TakePlayerInput(); + if (input.original_mode) { + std::cout << "Switch to original mode\n"; + world_->difficulty = 0; + } + if (input.easy_mode) { + std::cout << "Switch to easy mode\n"; + world_->difficulty = 2; + } + if (input.medium_mode) { + std::cout << "Switch to medium mode\n"; + world_->difficulty = 4; + } + if (input.difficult_mode) { + std::cout << "Switch to difficult mode\n"; + world_->difficulty = 6; + } + if (UnitId() == owner->PrimaryUnitId()) { + glm::vec3 forward = glm::normalize(glm::vec3{input.orientation}); + glm::vec3 right = + glm::normalize(glm::cross(forward, glm::vec3{0.0f, 1.0f, 0.0f})); + glm::vec3 z_rotate_speed = + glm::normalize(glm::cross(forward, glm::vec3{0.0f, 0.0f, 1.0f})); + + glm::vec3 moving_direction{}; + + float angular_acceleration = glm::radians(2880.0f); + + if (input.move_forward) { + moving_direction -= right; + } + if (input.move_backward) { + moving_direction += right; + } + if (input.move_left) { + moving_direction -= forward; + } + if (input.move_right) { + moving_direction += forward; + } + if (input.rotate_z) { + moving_direction += z_rotate_speed; + } + if (input.move_up) { + if (jump == 1 && sphere.velocity.y < 0) { + sphere.velocity.y = 5; + jump++; + // std::cout << "double jump" << std::endl; + } else if (jump == 0) { + sphere.velocity.y += 3.5; + jump++; + // std::cout << "first jump" << std::endl; + } + // std::cout << sphere.velocity.y << " " << sphere.position.y << + // std::endl; + } + if (glm::length(moving_direction) > 0.0f) { + moving_direction = glm::normalize(moving_direction); + sphere.angular_velocity += + moving_direction * angular_acceleration * delta_time; + } + + if (input.brake) { + sphere.angular_velocity = glm::vec3{0.0f}; + } + world_->owner_place = sphere.position; + } + else { + glm::vec3 relative_pos = glm::normalize(sphere.position - world_->owner_place); + sphere.velocity -= 0.01f * world_->difficulty * relative_pos; + } + } + + position_ = sphere.position; + if (position_.y < -30) { + std::string result; + if (owner->is_enemy) { + result = "you win"; + sphere.position = glm::vec3{-5.0f, 1.0f, 0.0f}; + world_->restart = 1; + world_->score[0]++; + } else { + result = "you lose"; + sphere.position = glm::vec3{0.0f, 1.0f, 0.0f}; + world_->restart = 2; + world_->score[1]++; + } + result += "\t\tSCORE: " + std::to_string(world_->score[0]) + " : " + + std::to_string(world_->score[1]); + showWindow(result); + // std::cout << "SCORE: " << world_->score[0] << " " << world_->score[1] + // << std::endl; + } else if (position_.y > 0 && position_.y < 1) { + jump = 0; + } + if (world_->restart == 1 && !owner->is_enemy) { + sphere.position = glm::vec3{0.0f, 1.0f, 0.0f}; + world_->restart = 0; + } else if (world_->restart == 2 && owner->is_enemy) { + sphere.position = glm::vec3{-5.0f, 1.0f, 0.0f}; + world_->restart = 0; + } position_ = sphere.position; velocity_ = sphere.velocity; orientation_ = sphere.orientation; diff --git a/src/GameBall/logic/units/regular_ball.h b/src/GameBall/logic/units/regular_ball.h index 4aebf2b..107d164 100644 --- a/src/GameBall/logic/units/regular_ball.h +++ b/src/GameBall/logic/units/regular_ball.h @@ -34,6 +34,7 @@ class RegularBall : public Unit { private: float radius_{1.0f}; float mass_{1.0f}; + int jump; glm::vec3 position_{}; glm::vec3 velocity_{}; glm::mat3 orientation_{1.0f}; diff --git a/src/GameBall/logic/units/settlement.h b/src/GameBall/logic/units/settlement.h new file mode 100644 index 0000000..f084919 --- /dev/null +++ b/src/GameBall/logic/units/settlement.h @@ -0,0 +1,45 @@ +#include +#ifdef __APPLE__ +#include +#include + +void renderLoop(GLFWwindow* window) { + // Set the start time + double startTime = glfwGetTime(); + + while (glfwGetTime() - startTime <= 2.0) { + glfwSwapBuffers(window); + glfwPollEvents(); + } + glfwSetWindowShouldClose(window, true); + glfwDestroyWindow(window); +} + +void createWindowAndRunLoop(std::string str) { + GLFWwindow* window = glfwCreateWindow(600, 50, "window", NULL, NULL); + if (!window) { + glfwTerminate(); + // Handle error appropriately + return; + } + glfwSetWindowTitle(window, str.c_str()); + glfwMakeContextCurrent(window); + + // Run the rendering loop on the main thread + renderLoop(window); +} + +void showWindow(std::string str) { + if (!glfwInit()) { + return; + } + // Use dispatch_async to execute window creation on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + createWindowAndRunLoop(str); + }); +} +#else +void showWindow(std::string str) { + std::cout << str << std::endl; +} +#endif \ No newline at end of file diff --git a/src/GameBall/logic/world.h b/src/GameBall/logic/world.h index 59dadf6..5b1c84a 100644 --- a/src/GameBall/logic/world.h +++ b/src/GameBall/logic/world.h @@ -91,6 +91,14 @@ class World { void UpdateTick(); + int restart = 0; + + int score[2] = {0, 0}; + + glm::vec3 owner_place = {0.0f, 1.0f, 0.0f}; + + int difficulty = 0; + private: friend ::GameBall::GameBall; friend ::GameBall::Logic::Manager;