From 5f522abcb5a9182c198ef13f7a3e6cee02d46a98 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Tue, 27 Feb 2024 21:07:15 -0800 Subject: [PATCH] Player controller --- game/art/game_player.aseprite | Bin 815 -> 743 bytes game/art/game_player.png | Bin 634 -> 601 bytes game/game.c | 35 ++------ game/game.h | 1 + game/game_player.c | 159 ++++++++++++++++++++++++++++++++++ game/game_player.h | 8 ++ sdl_host/main.c | 10 +-- sys/sys.c | 4 + sys/sys.h | 5 ++ sys/sys_input.c | 36 ++++++++ sys/sys_input.h | 33 +++++++ 11 files changed, 256 insertions(+), 35 deletions(-) create mode 100644 game/game_player.c create mode 100644 game/game_player.h create mode 100644 sys/sys_input.c create mode 100644 sys/sys_input.h diff --git a/game/art/game_player.aseprite b/game/art/game_player.aseprite index 3c72b90070a7d40b17302217218ab3993fb722e1..8eb75bfec15b3aa020df8095988e8bc5b6bf5858 100644 GIT binary patch delta 361 zcmV-v0ha!+2ImC==K_%eegS8Zf%pM#lX?Lhf7R=j{69x};JEds}_Bf5#PY+jJB!BUxAUpgPy))31S+VWM!}MA- zmF<|&ZQG7>b{flennTSjTGpFxr?#bKpJj0~?yqw;(%HMJnI>au)&)J(%L4E9OT!cw zsj>Qv{_nHSq|(oWzQ$uOTtJVg$z3|x2!DCHGKZgX#XGw5dP0yB& zsK##1;J*!T4c|7a;nw!Ty`dE*6bg$d>L%sCt-j3(-%wxUwf4GU$c$fYErv3i` Hc=8^g(w@Ra delta 434 zcmV;j0Zsnr1+NAIF9VSRegUtMf%pNolX?Lhf0f;mgD?n%6EdSD@BhFBqWL+olHJ+M zWqQ$1@bqX2gdjo89ER{1f|JEIKiqyCy})DiLy|t@+43HvAHg$f{<^rq=UbT zFBiFxn!d-YpInMT$zh8pKl(UXDoW1n?YuGgAxlf7_O@SH^+rEjSqtnx_4v}e{YWR+ zfAa|@Z3>T3Pg;puo}QUGH(vBeo6(++D12jXT=lwsZDuyz-rOv=ope;TuPqOHcOL5l z1m+)${^OUy_SxqL0saZEdW;qCJdw@AVWN5rdR)h0?dAx}~h=I4?>+5h`I zhIT^Tr_pLWUi?J8UiYcxJzo51f$h^+*Yi8Cesm!nL%qYRpJAWwF#59s*{9(Q*MIuX z=+6prJ6%4bKix;v``hKkALboa5{2OeK)uJPAF^`z%Jzd}e$k^mo=D@~q1LU_9EqLp cr)&RDJgnz?|9`EweGc^j0`oV208_;tpvo8KG5`Po diff --git a/game/art/game_player.png b/game/art/game_player.png index 2e9eabfe377f7a3bd6be7f879a1e7e2208cc89b0..f1f3b4821046fa05d4ef4a61544fa436c5ae9c5a 100644 GIT binary patch delta 474 zcmV<00VV$W1la_TiGRCEL_t(oh3%M8j>8}fM1zqM!~GAu*sy_;Y+K`5sUNkXN-sz= zhExW#Z4$)H+Ynav21ku$M)>c0SG2M_sOTNxI0dcjjxwVoh+tb+D<&t~ ztZ6)FWMy}7OgXDsPH%8ofX8vae^D%wIi+`)6o+wdG{KbOdw^;$7|nt&xYR)X zB;`$nl*3u@6_+Ypp2h@7Km<<%x@YFp4M{cvmIWb%e-lhy@RmnIB06Z-A4meawXrVs z$lahCGI#?V6PRX1x03_T_#5B&$Y6ce8Ib|I7?8mvX=R_e94>}JXJvrDoU+fo&B}lT z^pu_RBD;g?A%8cACTV52Kg*Qru`e$$B7w5mr}KDmW#TKt{s2ud@x*kTEc^2?Uia%h z4#;2s7( z(EI%*hGbCs{q5L}HAZBR9?JT2F5RLqt{6+#MZMO)UkxHXayL-JVZ3(Jz-D=#k@8^hdq05kyCyKr=>C4-pRNaf0Fx{ppsg+G Qv;Y7A07*qoM6N<$f)=6SPXGV_ delta 507 zcmV>((bg^RVK+mzw@6bQbOJT+46@i>TxX=VvY+jL^FE|pYSvOczL~r1%0?u#n zR1s}JOgW=(Pq<=P5p|%CDRH(G_js&`Uf=teB~pxAJXJ&)z<>F3_&p{=V?~sKB_S!` z4p&26MYQ_Jzj4mzsk^LrGNg^%)SwB#JtM}(lOeqW>Z|-413WS|E`~0H77j>&pR8wq z0B3bbU)#DcWMO~>q~N@3T)*A)jc@$pK>qCWI~?F&_zi|oO1x%iP$^DFYuDIUd}mOp zPj}4L*;jmPNPlO5@0clVvoNF$C|A7Xs>F(=A+AWZ_^CSTIqxV0jt{!4*q``iPY9nfa0Xb!!7G;9=ye*>d9@4q#&g xP~Q&>+)dxV?+2dt>kaU{F9fGeV%_TXzz;k=9-tI(0mA?Q002ovPDHLkV1muY^>_dP diff --git a/game/game.c b/game/game.c index be9c206..0ba7f72 100644 --- a/game/game.c +++ b/game/game.c @@ -8,16 +8,6 @@ uint32_t game_frame; -game_bbox game_player_bbox = { - .x=0x9000, - // .y=0x5800, - .y=0x0000, - .w=0x1000, - .h=0x1000, -}; -sys_i32 game_player_dx = 0; -sys_i32 game_player_dy = 0; - const char* game_title() { return "Croc Party!"; } @@ -26,6 +16,7 @@ void game_init() { sys_init(); game_palette_init(); + game_player_init(); } void game_destroy() { @@ -33,32 +24,16 @@ void game_destroy() { } void game_update() { + sys_update(); + game_frame += 1; + game_player_update(); - game_player_dy += 1; - - if (device_buttons[0]) { game_player_dx -= 16; } - if (device_buttons[1]) { game_player_dx += 16; } - - game_collision collision = game_collision_move_to_contact( - &game_player_bbox, - game_player_dx, - game_player_dy - ); - if (collision.collided_x) { game_player_dx = 0; } - if (collision.collided_y) { game_player_dy = 0; } } void game_draw() { sys_cls(9); sys_map_draw(map_game_map, spr_game_tiles, 0, 0, 0, 0, 32, 18); - sys_sprite_draw_ext( - spr_game_player, - (game_frame / 8) % 4 * 2, - game_player_bbox.x / 0x100, - game_player_bbox.y / 0x100, - 2, 2, - false, false - ); + game_player_draw(); } diff --git a/game/game.h b/game/game.h index 02e3a79..0240977 100644 --- a/game/game.h +++ b/game/game.h @@ -3,6 +3,7 @@ #include "game_collision.h" #include "game_palette.h" +#include "game_player.h" const char* game_title(); void game_init(); diff --git a/game/game_player.c b/game/game_player.c new file mode 100644 index 0000000..b732839 --- /dev/null +++ b/game/game_player.c @@ -0,0 +1,159 @@ +#include "art/game_player.h" +#include "art/game_tiles.h" +#include "device/device.h" +#include "map/game_map.h" +#include "game.h" +#include "sys/sys.h" + +game_bbox game_player_bbox = { + .x=0x9000, + // .y=0x5800, + .y=0x0000, + .w=0x1000, + .h=0x1000, +}; +bool game_player_grounded = false; +sys_i32 game_player_jump_frames = 0; +sys_i32 game_player_coyote_frames = 0; +sys_i32 game_player_dx = 0; +sys_i32 game_player_dy = 0; +bool game_player_faceleft = false; + +typedef enum { + GAME_PLAYER_ANIM_STANDING, + GAME_PLAYER_ANIM_WALKING, + GAME_PLAYER_ANIM_FLOATING, +} game_player_anim; + +game_player_anim game_player_anim_state = GAME_PLAYER_ANIM_STANDING; +uint8_t game_player_anim_progress = 0; + +void game_player_anim_transition(game_player_anim anim); +void game_player_anim_add_progress(uint8_t amt); + +void game_player_init() { +} + +void game_player_update() { + game_collision collision = game_collision_move_to_contact( + &game_player_bbox, + game_player_dx, + game_player_dy + ); + + if (collision.collided_x) { game_player_dx = 0; } + if (collision.collided_y) { game_player_dy = 0; } + + game_player_grounded = !game_collision_can_move(game_player_bbox, 0, 1); + + { + sys_i32 fric_numerator = game_player_grounded ? 0x70 : 0x7f; + game_player_dx = ( + game_player_dx * fric_numerator + + // round up + 0x7f * sys_sgn_i32(game_player_dx) + ) / 0x80; + if (sys_abs_i32(game_player_dx) > 0x200) { + game_player_dx = sys_sgn_i32(game_player_dx) * 0x200; + } + + int wanted_dx = game_player_dx; + if (sys_btn(DEVICE_BUTTON_L)) { wanted_dx -= 0x50; } + if (sys_btn(DEVICE_BUTTON_R)) { wanted_dx += 0x50; } + + // allow this if grounded or if it's not a change of direction + if ( + game_player_grounded || + (sys_sgn_i32(wanted_dx) == sys_sgn_i32(game_player_dx)) + ) { + game_player_dx = wanted_dx; + } else { + // the smallest possible amount of movement without changing sign + game_player_dx = sys_sgn_i32(game_player_dx); + } + } + + if (game_player_grounded || game_player_coyote_frames > 0) { + if (sys_btn(DEVICE_BUTTON_U)) { + game_player_dy = -0x300; + game_player_jump_frames = 15; + game_player_grounded = false; + } + } + + bool wants_to_go_higher = false; + if (game_player_grounded) { + game_player_coyote_frames = 5; + if (game_player_dy > 0) { game_player_dy = 0; } + if (game_player_dx < 0) { game_player_faceleft = true; } + if (game_player_dx > 0) { game_player_faceleft = false; } + if (sys_abs_i32(game_player_dx) < 0x20) { game_player_dx = 0; } + } + else { + game_player_coyote_frames = + sys_max_i32(0, game_player_coyote_frames - 1); + wants_to_go_higher = sys_btn(DEVICE_BUTTON_U); + bool is_going_higher = wants_to_go_higher && game_player_jump_frames > 0; + if (game_player_dy >= 0 || !is_going_higher) { + game_player_dy += 48; + } else { + game_player_jump_frames -= 1; + } + } + + // deduce animation state + if (game_player_grounded) { + if (game_player_dx == 0) { + game_player_anim_transition(GAME_PLAYER_ANIM_STANDING); + } else { + game_player_anim_transition(GAME_PLAYER_ANIM_WALKING); + game_player_anim_add_progress( + sys_min_i32(sys_abs_i32(game_player_dx) / 0x10, 0x8) + ); + } + } else { + if (wants_to_go_higher) { + game_player_anim_transition(GAME_PLAYER_ANIM_FLOATING); + } else { + game_player_anim_transition(GAME_PLAYER_ANIM_STANDING); + } + } +} + +void game_player_draw() { + int game_player_image; + switch (game_player_anim_state) { + case GAME_PLAYER_ANIM_WALKING: + game_player_image = 2 + (game_player_anim_progress / 0x80) * 2; + break; + case GAME_PLAYER_ANIM_FLOATING: + game_player_image = 6; + break; + default: + case GAME_PLAYER_ANIM_STANDING: + game_player_image = 0; + break; + } + + sys_sprite_draw_ext( + spr_game_player, + game_player_image, + game_player_bbox.x / 0x100, + game_player_bbox.y / 0x100, + 2, 2, + game_player_faceleft, false + ); +} + +void game_player_anim_transition(game_player_anim anim) { + if (game_player_anim_state == anim) { return ;} + game_player_anim_state = anim; + game_player_anim_progress = 0; +} + +void game_player_anim_add_progress(uint8_t amt) { + game_player_anim_progress = ( + (uint32_t) game_player_anim_progress + + (uint32_t) amt + ) & 0xff; +} diff --git a/game/game_player.h b/game/game_player.h new file mode 100644 index 0000000..61b7d16 --- /dev/null +++ b/game/game_player.h @@ -0,0 +1,8 @@ +#ifndef GAME_PLAYER_H +#define GAME_PLAYER_H + +void game_player_init(); +void game_player_update(); +void game_player_draw(); + +#endif // GAME_PLAYER_H \ No newline at end of file diff --git a/sdl_host/main.c b/sdl_host/main.c index fc6bceb..3e99280 100644 --- a/sdl_host/main.c +++ b/sdl_host/main.c @@ -65,16 +65,16 @@ int main(int argc, char** argv) { sdl_host_loop(); - // renderer_cleanup: - SDL_DestroyRenderer(sdl_host_renderer); - sdl_host_renderer = NULL; - renderer_done: ; - // target_cleanup: SDL_DestroyTexture(sdl_host_target); sdl_host_target = NULL; target_done: ; + // renderer_cleanup: + SDL_DestroyRenderer(sdl_host_renderer); + sdl_host_renderer = NULL; + renderer_done: ; + // window_cleanup: SDL_DestroyWindow(sdl_host_window); sdl_host_window = NULL; diff --git a/sys/sys.c b/sys/sys.c index 3d806af..70558d6 100644 --- a/sys/sys.c +++ b/sys/sys.c @@ -15,6 +15,10 @@ void sys_init() { sys_dpal_reset(); } +void sys_update() { + sys_input_update(); +} + bool sys_get_initialized() { return sys_initialized; } diff --git a/sys/sys.h b/sys/sys.h index d4fba77..449f5e7 100644 --- a/sys/sys.h +++ b/sys/sys.h @@ -10,6 +10,11 @@ */ void sys_init(); +/** + * Update sys -- usually for input state. + */ +void sys_update(); + /** * Return whether sys was initialized. */ diff --git a/sys/sys_input.c b/sys/sys_input.c new file mode 100644 index 0000000..3ea4934 --- /dev/null +++ b/sys/sys_input.c @@ -0,0 +1,36 @@ +#include "device/device.h" +#include "sys/sys.h" +#include "sys_input.h" + +sys_i32 sys_input_down_frames[DEVICE_BUTTON_N]; + +void sys_input_update() { + for (DeviceButton db = 0; db < DEVICE_BUTTON_N; db++) { + if (device_buttons[db]) { + sys_input_down_frames[db] += 1; + } else { + sys_input_down_frames[db] = 0; + } + } +} + +void sys_input_reset() { + for (DeviceButton db = 0; db < DEVICE_BUTTON_N; db++) { + sys_input_down_frames[db] = 0; + } +} + +bool sys_btn(DeviceButton button) { + if (button >= DEVICE_BUTTON_N) { return false; } + return sys_input_down_frames[button] >= 1; +} + +bool sys_btnp(DeviceButton button) { + if (button >= DEVICE_BUTTON_N) { return false; } + if (sys_input_down_frames[button] == 1) { return true; } + // 31 and every 8 frames after that + if (sys_input_down_frames[button] >= 31 && (sys_input_down_frames[button] - 31) % 8 == 0) { + return true; + } + return false; +} \ No newline at end of file diff --git a/sys/sys_input.h b/sys/sys_input.h new file mode 100644 index 0000000..f7a27ca --- /dev/null +++ b/sys/sys_input.h @@ -0,0 +1,33 @@ +#ifndef SYS_INPUT_H +#define SYS_INPUT_H + +/** + * Update the state of every button. Each button's btnp state depends + * on how long it's been down, and calling this function adds one frame. + * + * Usually this is called by sys_update(), at the start of frame. + */ +void sys_input_update(); + +/** + * Resets the input state -- all buttons have been held for 0 frames. + * + * If the buttons are held next frame, this will lead to a btnp(). + * + * There's rarely any reason for user code to call this. + */ +void sys_input_reset(); + +/** + * Return whether a button is down. + */ +bool sys_btn(DeviceButton button); + +/** + * Return whether a button was just pressed. + * + * Repeats after 30 frames, returning true every 8 frames after that. + */ +bool sys_btnp(DeviceButton button); + +#endif // SYS_INPUT_H \ No newline at end of file