diff --git a/config.json b/config.json index dc6cc3f..13ffc66 100644 --- a/config.json +++ b/config.json @@ -14,6 +14,7 @@ }, "keys": { + "toggle-boom": ["CTRL", "b"] }, "input": { @@ -31,8 +32,17 @@ }, "sim": { - "wall-count": 48, + "cuckoo-time": 10.0, + "cuckoo-wall-count": 48, + "cuckoo-outer-radius": 0.75, + "cuckoo-inner-radius": 0.65, "ball-scale": 0.15, - "spawn-radius": 0.35 + "ball-count": 3, + "spawn-radius": 0.45, + "ball-bounce-speed": 0.0005, + "ball-shaking-friction": 0.00005, + "ball-not-shaking-friction": 0.0001, + "cuckoo-pull-factor": 1.2, + "cuckoo-return-speed": 0.0075 } } diff --git a/lib/sb b/lib/sb index 7e31b5a..a3fba9c 160000 --- a/lib/sb +++ b/lib/sb @@ -1 +1 @@ -Subproject commit 7e31b5a1c00f38dbd9d667f7e4174a5341ac550f +Subproject commit a3fba9c38aa344e8352abc101a4410bc544afcdf diff --git a/src/Pepy.cpp b/src/Pepy.cpp index 32061c2..31d059d 100644 --- a/src/Pepy.cpp +++ b/src/Pepy.cpp @@ -19,9 +19,9 @@ Pepy::Pepy() get_delegate().subscribe(&Pepy::respond, this, SDL_MOUSEBUTTONDOWN); get_delegate().subscribe(&Pepy::respond, this, SDL_MOUSEBUTTONUP); /* create a glowing ring */ - int point_count = configuration()["sim"]["wall-count"].get() + 1; - std::vector outer_points = sb::points_on_circle(point_count, 0.75f, {0.0f, 0.0f}); - std::vector inner_points = sb::points_on_circle(point_count, 0.65f, {0.0f, 0.0f}); + int point_count = configuration()["sim"]["cuckoo-wall-count"].get() + 1; + std::vector outer_points = sb::points_on_circle(point_count, configuration()["sim"]["cuckoo-outer-radius"], {0.0f, 0.0f}); + std::vector inner_points = sb::points_on_circle(point_count, configuration()["sim"]["cuckoo-inner-radius"], {0.0f, 0.0f}); float inner_saturation = 0.1f; float inner_value = 1.0f; float outer_saturation = 1.0f; @@ -43,15 +43,13 @@ Pepy::Pepy() cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast(point_count) * 255.0f, inner_saturation, inner_value))); cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast(point_count) * 255.0f, outer_saturation, outer_value))); } - /* create balls */ - float size = configuration()["sim"]["ball-scale"]; - auto spawn_points = sb::points_on_circle(5, configuration()["sim"]["spawn-radius"]); - for (auto spawn_point : spawn_points) + /* Create the playing balls */ + for (std::size_t ball_ii = 0; ball_ii < configuration()["sim"]["ball-count"]; ball_ii++) { Plane ball; - ball.transformation(glm::translate(glm::vec3{spawn_point.x, spawn_point.y, 1.0f}) * glm::scale(glm::vec3{size, size, 1.0f})); wad.push_back(std::pair{ball, glm::vec2{0.0f, 0.0f}}); } + situate_balls(); background.transformation(glm::scale(glm::vec3{5.0f, 5.0f, 1.0f})); /* load Open GL */ load_gl_context(); @@ -59,7 +57,7 @@ Pepy::Pepy() sb::Texture wad_texture {"resource/wad.png"}; wad_texture.load(); /* Apply the wad texture to each wad */ - for (size_t wad_ii = 0; wad_ii < wad.size(); wad_ii++) + for (std::size_t wad_ii = 0; wad_ii < wad.size(); wad_ii++) { wad[wad_ii].first.texture(wad_texture); } @@ -68,7 +66,17 @@ Pepy::Pepy() background.texture(texture); } -/* Create GL context via super class and load vertices, UV data, and shaders */ +void Pepy::situate_balls() +{ + float size = configuration()["sim"]["ball-scale"]; + auto spawn_points = sb::points_on_circle(wad.size(), configuration()["sim"]["spawn-radius"]); + int ii = 0; + for (auto& ball : wad) + { + ball.first.transformation(glm::translate(glm::vec3{spawn_points[ii].x, spawn_points[ii++].y, 1.0f}) * glm::scale(glm::vec3{size, size, 1.0f})); + } +} + void Pepy::load_gl_context() { super::load_gl_context(); @@ -115,17 +123,33 @@ void Pepy::respond(SDL_Event& event) { /* Will check if reset should be triggered */ bool reset = false; + if (Delegate::compare(event, "toggle-boom")) + { + boom = !boom; + } /* Check for any direction key when waiting to reset */ if (stopped && delegate.compare(event, {"up", "down", "left", "right"})) { reset = true; } - /* Check mouse buttons for grabbing */ + /* Check mouse buttons to initiate grabbing, thrusting, or reset */ if (event.type == SDL_MOUSEBUTTONDOWN) { if (shaking) { - grabbed = true; + /* In regular mode, a mouse click means grab the cuckoo */ + if (!boom) + { + grabbed = true; + } + /* In BOOM mode, a mouse click means to thrust the cuckoo */ + else + { + /* Point cuckoo in the direction of the mouse click relative to the center of the screen. Set the velocity to + * maximum so the cuckoo immediately moves in the calculated direction. */ + cuckoo_velocity.x = sb::angle_between({0.0f, 0.0f}, mouse_ndc()); + cuckoo_velocity.y = 0.5f; + } } /* Mouse button will trigger reset when game is waiting to reset */ else if (stopped) @@ -133,6 +157,7 @@ void Pepy::respond(SDL_Event& event) reset = true; } } + /* Ungrab is the only thing mouse up does */ else if (event.type == SDL_MOUSEBUTTONUP && shaking) { grabbed = false; @@ -140,23 +165,27 @@ void Pepy::respond(SDL_Event& event) /* Reset to cuckoo time */ if (reset) { - float size = configuration()["sim"]["ball-scale"]; - auto spawn_points = sb::points_on_circle(5, configuration()["sim"]["spawn-radius"]); - int ii = 0; - for (auto& ball : wad) - { - ball.first.transformation(glm::translate(glm::vec3{spawn_points[ii].x, spawn_points[ii++].y, 1.0f}) * glm::scale(glm::vec3{size, size, 1.0f})); - } + situate_balls(); stopped = false; shaking = true; } } -/* Update state and draw the screen */ +glm::vec2 Pepy::mouse_ndc() +{ + glm::ivec2 mouse_pixel; + SDL_GetMouseState(&mouse_pixel.x, &mouse_pixel.y); + return { + static_cast(mouse_pixel.x) / window_box().width() * 2.0f - 1.0f, + (1.0f - static_cast(mouse_pixel.y) / window_box().height()) * 2.0f - 1.0f + }; +} + void Pepy::update() { /* number of seconds running */ time_seconds = SDL_GetTicks() / 1000.0f; + /* Move countdown along during the shaking phase */ if (shaking) { countdown -= last_frame_length / 1000.0f; @@ -164,20 +193,23 @@ void Pepy::update() { shaking = false; flying = true; - countdown = 10.0f; + countdown = configuration()["sim"]["cuckoo-time"]; + /* During a BOOM shake, freeze motion at the release point. */ + if (boom) + { + for (auto& ball : wad) + { + ball.second.y = 0.0f; + } + } } } - /* move cuckoo with mouse */ + /* Move cuckoo with mouse */ if (grabbed && shaking) { - /* get mouse coordinates in NDC */ - SDL_GetMouseState(&mouse_pixel.x, &mouse_pixel.y); - glm::vec2 mouse_ndc { - static_cast(mouse_pixel.x) / window_box().width() * 2.0f - 1.0f, - (1.0f - static_cast(mouse_pixel.y) / window_box().height()) * 2.0f - 1.0f - }; - cuckoo_offset.x += weight(mouse_ndc.x / 20.0f) * std::max(std::min(std::abs(1.0f - cuckoo_offset.x), 1.0f), 0.1f); - cuckoo_offset.y += weight(mouse_ndc.y / 20.0f) * std::max(std::min(std::abs(1.0f - cuckoo_offset.y), 1.0f), 0.1f); + glm::vec2 ndc = mouse_ndc(); + cuckoo_offset.x += weight(ndc.x / 20.0f) * std::max(std::min(std::abs(1.0f - cuckoo_offset.x), 1.0f), 0.1f); + cuckoo_offset.y += weight(ndc.y / 20.0f) * std::max(std::min(std::abs(1.0f - cuckoo_offset.y), 1.0f), 0.1f); } else { @@ -230,9 +262,22 @@ void Pepy::update() cuckoo_offset.x -= motion; } } - cuckoo_offset.x -= weight(glm::sign(cuckoo_offset).x * return_speed); - cuckoo_offset.y -= weight(glm::sign(cuckoo_offset).y * return_speed); - cuckoo.transformation(glm::translate(cuckoo_offset)); + cuckoo_offset.x -= weight(glm::sign(cuckoo_offset).x * configuration()["sim"]["cuckoo-return-speed"].get()); + cuckoo_offset.y -= weight(glm::sign(cuckoo_offset).y * configuration()["sim"]["cuckoo-return-speed"].get()); + /* In regular mode, apply the cuckoo offset */ + if (!boom) + { + cuckoo.transformation(glm::translate(cuckoo_offset)); + } + /* In boom mode, apply the cuckoo velocity */ + else + { + /* Retract */ + cuckoo_velocity.y = std::max(0.0f, cuckoo_velocity.y - weight(0.01f)); + /* Change velocity to offset */ + glm::vec2 delta = sb::velocity_to_delta(cuckoo_velocity); + cuckoo.transformation(glm::translate(glm::vec3{delta.x, delta.y, 0.0f})); + } hue_offset += weight(0.002f); glm::vec2 x_range = {-1.0f, 1.0f}; glm::vec2 y_range = {-1.0f, 1.0f}; @@ -246,11 +291,18 @@ void Pepy::update() if (shaking) { /* The ball will bounce back toward the center of the cuckoo if it's a certain distance away from it. */ - if (distance > 0.65f) + if (distance > configuration()["sim"]["cuckoo-inner-radius"]) { /* Calculate the angle of the ball and add speed. */ ball.second.x = sb::angle_between(ball_center, cuckoo_center); - ball.second.y += 0.0005f; + if (!boom) + { + ball.second.y += configuration()["sim"]["ball-bounce-speed"].get(); + } + else + { + ball.second.y += cuckoo_velocity.y * std::abs(sb::angle_ratio(ball.second.x, cuckoo_velocity.x)) * 0.1f; + } } } glm::vec2 step = sb::velocity_to_delta(ball.second); @@ -260,11 +312,11 @@ void Pepy::update() float friction; if (shaking) { - friction = 0.00005f; + friction = configuration()["sim"]["ball-shaking-friction"]; } else { - friction = 0.0001f; + friction = configuration()["sim"]["ball-not-shaking-friction"]; } ball.second.y = std::max(0.0f, ball.second.y - friction); } diff --git a/src/Pepy.hpp b/src/Pepy.hpp index d7b46a0..fb32dcf 100644 --- a/src/Pepy.hpp +++ b/src/Pepy.hpp @@ -59,22 +59,40 @@ private: /* Convention for calling parent class in a consistent way across classes */ typedef Game super; - bool shaking = true, flying = false, stopped = false; + bool shaking = true, flying = false, stopped = false, boom = false, grabbed = false; sb::VAO cuckoo_vao, wad_vao; sb::VBO vbo; GLuint shader; Model cuckoo; std::vector> wad; std::map uniform; - float hue_offset = 0.0f, time_seconds = 0.0f, aspect_ratio = 1.0f, return_speed = 0.0075f, cuckoo_speed = 0.0f, countdown = 10.0f; + float hue_offset = 0.0f, time_seconds = 0.0f, aspect_ratio = 1.0f, cuckoo_speed = 0.0f, + countdown = configuration()["sim"]["cuckoo-time"]; glm::mat4 orthographic_projection {1}; glm::vec3 cuckoo_offset {0.0f, 0.0f, 0.0f}; glm::vec2 cuckoo_velocity {0.0f, 0.0f}; - bool grabbed = false; - glm::ivec2 mouse_pixel = {0, 0}; Plane background; + /*! + * Situate the balls in their starting position. Useful for reseting to initial state. + */ + void situate_balls(); + + /*! + * Call the Game class's GL load functions then load graphics (vertices, UVs, shaders, buffers, uniforms) into OpenGL. + */ void load_gl_context(); + + /*! + * Get current mouse coordinates as NDC. + * + * @return vector of X/Y coordinates of current mouse position + */ + glm::vec2 mouse_ndc(); + + /*! + * Update state and draw the screen. + */ void update(); public: