#include "MyPongScene.h" #include // rand #include #include #include #include #include #define LEFT_PADDLE_UP_KEY GLFW_KEY_A #define LEFT_PADDLE_DOWN_KEY GLFW_KEY_Z #define RIGHT_PADDLE_UP_KEY GLFW_KEY_APOSTROPHE #define RIGHT_PADDLE_DOWN_KEY GLFW_KEY_SLASH #define OUTLINE_THICKNESS 25.0f #define OUTLINE_OFFSET 25.0f #define OUTLINE_INWARDS (OUTLINE_OFFSET + OUTLINE_THICKNESS) #define PADDLE_THICKNESS 25.0f #define PADDLE_HEIGHT 150.0f #define LIFE_THICKNESS 15.0f #define LIFE_OFFSET 10.0f #define LIFE_INWARDS (OUTLINE_INWARDS + LIFE_THICKNESS) #define BALL_THICKNESS 25.0f #define HALF_BALL_THICKNESS 12.5f #define INITIAL_BALL_SPEED 250.0f #define BALL_SPEED_INCREMENT 50.0f #define PADDLE_SPEED 500.0f #define STARTING_LIVES 5 MyPongScene::MyPongScene(Application& application) : Scene(application), m_outline_column(meshgenerator::gen_rect_p(DRAW_TRIANGLES, m_screen_size.x - 2 * OUTLINE_THICKNESS, OUTLINE_THICKNESS), DRAW_TRIANGLES), m_outline_row(meshgenerator::gen_rect_p(DRAW_TRIANGLES, OUTLINE_THICKNESS, m_screen_size.y - 2 * OUTLINE_THICKNESS), DRAW_TRIANGLES), m_ball(meshgenerator::gen_rect_p(DRAW_TRIANGLES, BALL_THICKNESS, BALL_THICKNESS), DRAW_TRIANGLES), m_life(meshgenerator::gen_rect_p(DRAW_TRIANGLES, LIFE_THICKNESS, LIFE_THICKNESS), DRAW_TRIANGLES), m_paddle(meshgenerator::gen_rect_p(DRAW_TRIANGLES, PADDLE_THICKNESS, PADDLE_HEIGHT), DRAW_TRIANGLES), m_outline_column_batch(&m_outline_column, 2), m_outline_row_batch(&m_outline_row, 2), m_ball_batch(&m_ball, 1), m_life_batch(&m_life, STARTING_LIVES * 2), m_paddle_batch(&m_paddle, 2), m_outline_top_pose(glm::vec3(0.0f, (m_screen_size.y - OUTLINE_THICKNESS) / 2.0f - OUTLINE_OFFSET, 0.0f)), m_outline_bottom_pose(glm::vec3(0.0f, -((m_screen_size.y - OUTLINE_THICKNESS) / 2.0f - OUTLINE_OFFSET), 0.0f)), m_outline_left_pose(glm::vec3(-((m_screen_size.x - OUTLINE_THICKNESS) / 2.0f - OUTLINE_OFFSET), 0.0f, 0.0f)), m_outline_right_pose(glm::vec3((m_screen_size.x - OUTLINE_THICKNESS) / 2.0f - OUTLINE_OFFSET, 0.0f, 0.0f)), m_ball_pose(glm::vec3(0.0f, 0.0f, 0.0f)), m_paddle_left_pose(glm::vec3(-(m_screen_size.x / 2.0f - OUTLINE_INWARDS - 2.5f * PADDLE_THICKNESS), 0.0f, 0.0f)), m_paddle_right_pose(glm::vec3(m_screen_size.x / 2.0f - OUTLINE_INWARDS - 2.5f * PADDLE_THICKNESS, 0.0f, 0.0f)), m_camera(m_screen_size), m_left_lives(STARTING_LIVES), m_right_lives(STARTING_LIVES) { m_pipeline.add_batch(&m_outline_column_batch); m_pipeline.add_batch(&m_outline_row_batch); m_pipeline.add_batch(&m_ball_batch); m_pipeline.add_batch(&m_life_batch); m_pipeline.add_batch(&m_paddle_batch); m_pipeline.set_camera(&m_camera); } void MyPongScene::init() { // Batches m_outline_column_batch.init(); m_outline_row_batch.init(); m_ball_batch.init(); m_life_batch.init(); m_paddle_batch.init(); // Set these once here since they will never change m_outline_column_batch.reset_rendered(); m_outline_column_batch.add_rendered(m_outline_top_pose); m_outline_column_batch.add_rendered(m_outline_bottom_pose); m_outline_row_batch.reset_rendered(); m_outline_row_batch.add_rendered(m_outline_left_pose); m_outline_row_batch.add_rendered(m_outline_right_pose); reset_ball(); } void MyPongScene::update(float delta_time, clock_t clock) { // Move Paddles const glm::vec3 up(0.0f, 1.0f, 0.0f); const glm::vec3 down(0.0f, -1.0f, 0.0f); if (m_left_lives > 0) { if (m_input_manager.is_key_down(LEFT_PADDLE_UP_KEY)) { m_paddle_left_pose.translate(up * PADDLE_SPEED * delta_time); } if (m_input_manager.is_key_down(LEFT_PADDLE_DOWN_KEY)) { m_paddle_left_pose.translate(down * PADDLE_SPEED * delta_time); } } if (m_right_lives > 0) { if (m_input_manager.is_key_down(RIGHT_PADDLE_UP_KEY)) { m_paddle_right_pose.translate(up * PADDLE_SPEED * delta_time); } if (m_input_manager.is_key_down(RIGHT_PADDLE_DOWN_KEY)) { m_paddle_right_pose.translate(down * PADDLE_SPEED * delta_time); } } // Move ball if (m_right_lives > 0 && m_left_lives > 0) { m_ball_pose.translate(glm::vec3(m_ball_direction * m_ball_speed * delta_time, 0.0f)); } vec3 ball_position = m_ball_pose.get_position(); // Bounce Top and Bottom if (ball_position.y + HALF_BALL_THICKNESS + OUTLINE_INWARDS > m_screen_size.y / 2.0f) { m_ball_direction.y = -glm::abs(m_ball_direction.y); } if (ball_position.y - HALF_BALL_THICKNESS - OUTLINE_INWARDS < -m_screen_size.y / 2.0f) { m_ball_direction.y = glm::abs(m_ball_direction.y); } // Scoring if (ball_position.x + HALF_BALL_THICKNESS + OUTLINE_INWARDS > m_screen_size.x / 2.0f) { m_right_lives -= 1; reset_ball(); } if (ball_position.x - HALF_BALL_THICKNESS - OUTLINE_INWARDS < -m_screen_size.x / 2.0f) { m_left_lives -= 1; reset_ball(); } // Bounce off Paddles glm::vec3 left_paddle_position = m_paddle_left_pose.get_position(); glm::vec3 right_paddle_position = m_paddle_right_pose.get_position(); physics::Rect ball_hitbox(glm::vec2(ball_position.x, ball_position.y), BALL_THICKNESS, BALL_THICKNESS); physics::Rect left_paddle_hitbox(glm::vec2(left_paddle_position.x, left_paddle_position.y), PADDLE_THICKNESS, PADDLE_HEIGHT); physics::Rect right_paddle_hitbox(glm::vec2(right_paddle_position.x, right_paddle_position.y), PADDLE_THICKNESS, PADDLE_HEIGHT); if (physics::collision::rect_in_rect(ball_hitbox, left_paddle_hitbox)) { float ball_y = m_ball_direction.y * m_ball_speed; if (m_input_manager.is_key_down(LEFT_PADDLE_UP_KEY)) { ball_y += PADDLE_SPEED; } else if (m_input_manager.is_key_down(LEFT_PADDLE_DOWN_KEY)) { ball_y -= PADDLE_SPEED; } m_ball_direction = glm::normalize(glm::vec2(m_ball_speed, ball_y)); m_ball_speed += BALL_SPEED_INCREMENT; } if (physics::collision::rect_in_rect(ball_hitbox, right_paddle_hitbox)) { float ball_y = m_ball_direction.y * m_ball_speed; if (m_input_manager.is_key_down(RIGHT_PADDLE_UP_KEY)) { ball_y += PADDLE_SPEED; } else if (m_input_manager.is_key_down(RIGHT_PADDLE_DOWN_KEY)) { ball_y -= PADDLE_SPEED; } m_ball_direction = glm::normalize(glm::vec2(-m_ball_speed, ball_y)); m_ball_speed += BALL_SPEED_INCREMENT; } // Update Render Batches m_ball_batch.reset_rendered(); m_ball_batch.add_rendered(m_ball_pose); m_paddle_batch.reset_rendered(); m_paddle_batch.add_rendered(m_paddle_left_pose); m_paddle_batch.add_rendered(m_paddle_right_pose); // Update Life Render Batch m_life_batch.reset_rendered(); Poseable left_life_pose(glm::vec3(-m_screen_size.x / 2.0f + LIFE_INWARDS, -m_screen_size.y / 2.0f + LIFE_INWARDS, 0.0f)); Poseable right_life_pose(glm::vec3(m_screen_size.x / 2.0f - LIFE_INWARDS, -m_screen_size.y / 2.0f + LIFE_INWARDS, 0.0f)); for (int i = 0; i < m_left_lives; ++i) { m_life_batch.add_rendered(left_life_pose); left_life_pose.translate(glm::vec3(LIFE_THICKNESS + LIFE_OFFSET, 0.0f, 0.0f)); } for (int i = 0; i < m_right_lives; ++i) { m_life_batch.add_rendered(right_life_pose); right_life_pose.translate(glm::vec3(-(LIFE_THICKNESS + LIFE_OFFSET), 0.0f, 0.0f)); } } void MyPongScene::prerender() { m_camera.prerender(); m_outline_column_batch.prerender(); m_outline_row_batch.prerender(); m_ball_batch.prerender(); m_life_batch.prerender(); m_paddle_batch.prerender(); } void MyPongScene::render() { glutil::clear_screen(); m_pipeline.render(); } float MyPongScene::random() { return rand() / (float)RAND_MAX; } vec2 MyPongScene::rand_dir(float radians_low, float radians_high) { float radians = radians_low + random() * (radians_high - radians_low); return glm::vec2(glm::cos(radians), glm::sin(radians)); } void MyPongScene::reset_ball() { m_ball_speed = INITIAL_BALL_SPEED; m_ball_pose.update_position(vec3(0.0f, 0.0f, 0.0f)); if (random() < 0.5) { // Toward right m_ball_direction = rand_dir((float)charcoal::TAU_7_8, (float)charcoal::TAU_7_8 + (float)charcoal::TAU_1_4); } else { // Toward left m_ball_direction = rand_dir((float)charcoal::TAU_3_8, (float)charcoal::TAU_5_8); } }