| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- #include "cglm/call/vec2.h"
- #include "game.h"
- #include "scufeng/engine.h"
- #include "scufeng/util.h"
- typedef enum {
- BCR_PADDLE_L_GOAL,
- BCR_PADDLE_R_GOAL,
- } BALL_COLLISION_RESULT;
- const GameStateParameters game_default_params = {
- .paddles_w = 0.01f,
- .paddles_h = 0.10f,
- .paddle_l_x = (1.0f / 16),
- .ball_w = 0.005f,
- .ball_pos = { 0.5f, 0.5f },
- .paddle_speed = 0.7f,
- .ball_speed = 0.7f,
- };
- const char *GameStateEvent_to_str(const GameStateEvent evt) {
- #define X(evt) \
- case evt: \
- return #evt;
- switch (evt) {
- XGameStateEvent(X)
- }
- #undef X
- }
- GameState GameState_new(
- const GameStateParameters params,
- const bool ai_enabled
- ) {
- #define BALL_START_DIR { -1.0f, 0.0f }
- // clang-format off
- GameState state = {
- .ai_enabled = ai_enabled,
- .paddle_l = {
- .pos = { params.paddle_l_x, 0.5f },
- .w = params.paddles_w,
- .h = params.paddles_h,
- .dirv = GLM_VEC2_ZERO_INIT,
- .speed = params.paddle_speed
- },
- .paddle_r = {
- .pos = { 1.0f - params.paddle_l_x, 0.5f },
- .w = params.paddles_w,
- .h = params.paddles_h,
- .dirv = GLM_VEC2_ZERO_INIT,
- .speed = params.paddle_speed
- },
- .ball = {
- // .pos = { 0.5f, 0.5f },
- .pos = { params.ball_pos[0], params.ball_pos[1] },
- .w = params.ball_w,
- .h = params.ball_w,
- .dirv = BALL_START_DIR,
- .speed = params.ball_speed
- },
- .ball_start_pos = { params.ball_pos[0], params.ball_pos[1] },
- .ball_start_dir = BALL_START_DIR,
- .score_l = 0,
- .score_r = 0
- };
- // clang-format on
- return state;
- }
- static void paddle_collision_check(MovingObject *const paddle) {
- if (paddle->pos[1] - paddle->h / 2.0f < 0.0f) {
- paddle->pos[1] = paddle->h / 2.0f;
- }
- if (paddle->pos[1] + paddle->h / 2.0f > 1.0f) {
- paddle->pos[1] = 1.0f - paddle->h / 2.0f;
- }
- }
- static void paddle_r_change_dir_ai(GameState *const state) {
- const float ball_y = state->ball.pos[1];
- const float target_zone_range = state->paddle_r.h / 2.0f;
- const float target_zone_top = state->paddle_r.pos[1] - target_zone_range;
- const float target_zone_middle = state->paddle_r.pos[1];
- const float target_zone_bottom = state->paddle_r.pos[1] + target_zone_range;
- const bool ball_is_below = ball_y > target_zone_bottom;
- const bool ball_is_above = ball_y < target_zone_top;
- const bool ball_is_centered = ball_y <= target_zone_middle + target_zone_range / 2.0f
- && ball_y >= target_zone_middle - target_zone_range / 2.0f;
- // printf("%d%d\n", ball_is_below, ball_is_above);
- if (ball_is_below) {
- state->paddle_r.dirv[1] = 1.0f;
- } else if (ball_is_above) {
- state->paddle_r.dirv[1] = -1.0f;
- } else if (ball_is_centered) {
- state->paddle_r.dirv[1] = 0.0f;
- }
- }
- static void game_next_round(GameState *const state) {
- glmc_vec2_copy(state->ball_start_pos, state->ball.pos);
- glmc_vec2_copy(state->ball_start_dir, state->ball.dirv);
- }
- static GameStateEvent ball_bounds_collision_check(GameState *const state, vec2 new_pos) {
- MovingObject *const ball = &state->ball;
- const float wd2 = ball->w / 2.0f;
- const float hd2 = ball->h / 2.0f;
- if (new_pos[0] + wd2 <= 0.0f) {
- return GSEVENT_R_SCORE;
- } else if (new_pos[0] - wd2 >= 1.0f) {
- return GSEVENT_L_SCORE;
- }
- if (new_pos[1] - hd2 < 0.0f) {
- new_pos[1] = hd2;
- glmc_vec2_reflect(ball->dirv, (vec2) { 0.0f, -1.0f }, ball->dirv);
- return GSEVENT_BALL_DIR_CHANGE;
- }
- if (new_pos[1] + hd2 > 1.0f) {
- new_pos[1] = 1.0f - hd2;
- glmc_vec2_reflect(ball->dirv, (vec2) { 0.0f, 1.0f }, ball->dirv);
- return GSEVENT_BALL_DIR_CHANGE;
- }
- return GSEVENT_NONE;
- }
- static GameStateEvent ball_paddles_collision_check(GameState *const state, vec2 new_pos) {
- static const float possible_y_directions[] = {
- 0.0f,
- 0.5f,
- -0.5f
- };
- MovingObject *const ball = &state->ball;
- const MovingObject *const paddle_l = &state->paddle_l;
- const MovingObject *const paddle_r = &state->paddle_r;
- const float ball_wd2 = ball->w / 2.0f;
- const float ball_hd2 = ball->h / 2.0f;
- const float paddle_wd2 = paddle_l->w / 2.0f;
- const float paddle_hd2 = paddle_l->h / 2.0f;
- vec2 paddle_side_top;
- vec2 paddle_side_bottom;
- vec2 ball_side_top;
- vec2 ball_side_bottom;
- float paddle_back_x;
- vec2 ball_pos;
- vec2 new_pos_adj;
- paddle_side_top[0] = paddle_l->pos[0] + paddle_wd2;
- paddle_side_top[1] = paddle_l->pos[1] - paddle_hd2;
- paddle_side_bottom[0] = paddle_side_top[0];
- paddle_side_bottom[1] = paddle_l->pos[1] + paddle_hd2;
- ball_side_top[0] = ball->pos[0] - ball_wd2;
- ball_side_top[1] = ball->pos[1] - ball_hd2;
- ball_side_bottom[0] = ball_side_top[0];
- ball_side_bottom[1] = ball->pos[1] + ball_hd2;
- paddle_back_x = paddle_l->pos[0] - paddle_wd2;
- glmc_vec2_copy(ball->pos, ball_pos);
- glmc_vec2_copy(new_pos, new_pos_adj);
- bool collision = false;
- bool mirror_ip = false;
- vec2 ip;
- if (state->ball.dirv[0] > 0.0f) {
- mirror_ip = true;
- paddle_side_top[0] = 1.0f - (paddle_r->pos[0] - paddle_r->w / 2.0f);
- paddle_side_top[1] = (paddle_r->pos[1] - paddle_r->h / 2.0f);
- paddle_side_bottom[0] = paddle_side_top[0];
- paddle_side_bottom[1] = paddle_side_top[1] + paddle_r->h;
- ball_pos[0] = 1.0f - ball_pos[0];
- ball_side_top[0] = ball_pos[0] - ball_wd2;
- ball_side_bottom[0] = ball_side_top[0];
- paddle_back_x = 1.0f - (paddle_r->pos[0] + paddle_r->w / 2.0f);
- new_pos_adj[0] = 1.0f - new_pos_adj[0];
- }
- // SEResult res = se_render_line_segment_norm_coord(
- // paddle_side_top,
- // paddle_side_bottom,
- // (vec4) { 0.0f, 1.0f, 0.0f, 1.0f }
- // );
- // if (res != SE_SUCCESS) {
- // SE_LOG_ERRORF("Failed to draw segment: %d", res);
- // }
- // res = se_render_line_segment_norm_coord(
- // ball_side_top,
- // ball_side_bottom,
- // (vec4) { 1.0f, 0.0f, 0.0f, 1.0f }
- // );
- // if (res != SE_SUCCESS) {
- // SE_LOG_ERRORF("Failed to draw segment: %d", res);
- // }
- // res = se_render_line_segment_norm_coord(
- // (vec2) { paddle_back_x, 0.0f },
- // (vec2) { paddle_back_x, 1.0f },
- // (vec4) { 0.0f, 1.0f, 0.0f, 1.0f }
- // );
- // if (res != SE_SUCCESS) {
- // SE_LOG_ERRORF("Failed to draw segment: %d", res);
- // }
- const bool inside_paddle = (ball_side_top[0] <= paddle_side_top[0] && ball_side_top[0] >= paddle_back_x)
- && ((ball_side_top[1] <= paddle_side_bottom[1] && ball_side_top[1] >= paddle_side_top[1])
- || (ball_side_bottom[1] <= paddle_side_bottom[1] && ball_side_bottom[1] >= paddle_side_top[1]));
- if (inside_paddle) {
- ball->dirv[0] *= -1.0f;
- ball->dirv[1] = possible_y_directions[se_rand_int() % 3];
- return GSEVENT_BALL_DIR_CHANGE;
- };
- collision = se_line_segments_intersect(
- ball_pos,
- new_pos_adj,
- paddle_side_top,
- paddle_side_bottom,
- ip
- );
- if (collision) {
- new_pos[0] = mirror_ip ? 1.0f - ip[0] : ip[0];
- new_pos[1] = ip[1];
- state->ball.dirv[0] *= -1.0f;
- state->ball.dirv[1] = possible_y_directions[se_rand_int() % 3];
- // state->ball.dirv[1] = 0.0f;
- return GSEVENT_BALL_DIR_CHANGE;
- }
- return GSEVENT_NONE;
- }
- static GameStateEvent ball_move(GameState *const state, const double delta) {
- vec2 ball_movement;
- const float ball_movement_scale = delta * state->ball.speed;
- // glmc_vec2_normalize(state->ball.dirv);
- glmc_vec2_scale(state->ball.dirv, ball_movement_scale, ball_movement);
- vec2 new_pos;
- glmc_vec2_add(state->ball.pos, ball_movement, new_pos);
- GameStateEvent event = ball_bounds_collision_check(state, new_pos);
- if (event != GSEVENT_NONE) {
- return event;
- }
- event = ball_paddles_collision_check(state, new_pos);
- glmc_vec2_copy(new_pos, state->ball.pos);
- return event;
- }
- void GameState_paddle_l_change_dir(GameState *const state, const SEDirection dir) {
- switch (dir) {
- case SE_UP:
- state->paddle_l.dirv[1] = -1.0f;
- break;
- case SE_DOWN:
- state->paddle_l.dirv[1] = 1.0f;
- break;
- case SE_NONE:
- state->paddle_l.dirv[1] = 0.0f;
- default:
- break;
- }
- }
- void GameState_paddle_r_change_dir(GameState *const state, const SEDirection dir) {
- switch (dir) {
- case SE_UP:
- state->paddle_r.dirv[1] = -1.0f;
- break;
- case SE_DOWN:
- state->paddle_r.dirv[1] = 1.0f;
- break;
- case SE_NONE:
- state->paddle_r.dirv[1] = 0.0f;
- default:
- break;
- }
- }
- GameStateEvent GameState_update(GameState *const state, const double delta) {
- // Paddles
- vec2 paddle_movement;
- const float paddle_movement_scale = delta * state->paddle_l.speed;
- glmc_vec2_scale(state->paddle_l.dirv, paddle_movement_scale, paddle_movement);
- glmc_vec2_add(state->paddle_l.pos, paddle_movement, state->paddle_l.pos);
- paddle_collision_check(&state->paddle_l);
- if (state->ai_enabled) {
- paddle_r_change_dir_ai(state);
- }
- glmc_vec2_scale(state->paddle_r.dirv, paddle_movement_scale, paddle_movement);
- glmc_vec2_add(state->paddle_r.pos, paddle_movement, state->paddle_r.pos);
- paddle_collision_check(&state->paddle_r);
- // Ball
- const GameStateEvent event = ball_move(state, delta);
- switch (event) {
- case GSEVENT_L_SCORE:
- state->score_l++;
- game_next_round(state);
- if (state->score_l > 5) {
- return GSEVENT_L_WIN;
- } else {
- return event;
- }
- case GSEVENT_R_SCORE:
- state->score_r++;
- game_next_round(state);
- if (state->score_r > 5) {
- return GSEVENT_R_WIN;
- } else {
- return event;
- }
- case GSEVENT_NONE:
- case GSEVENT_BALL_DIR_CHANGE:
- break;
- default:
- break;
- }
- return event;
- }
- SEDirection GameState_paddle_l_dir_get(const GameState *const state) {
- if (state->paddle_l.dirv[1] == -1.0f) {
- return SE_UP;
- } else if (state->paddle_l.dirv[1] == 1.0f) {
- return SE_DOWN;
- } else {
- return SE_NONE;
- }
- }
|