From 3af14378779e05f3b3a63d949aed14cf1a569702 Mon Sep 17 00:00:00 2001 From: Akbar Rahman Date: Wed, 8 Jun 2022 21:44:43 +0000 Subject: [PATCH] sprite-better-collision (#1) Co-authored-by: Alvie Rahman Reviewed-on: https://git.alv.cx/alvierahman90/untitled2dshooter/pulls/1 --- src/constants.h | 3 + src/main.c | 201 ++++-------------------------------------------- src/sprite.c | 179 ++++++++++++++++++++++++++++++++++++++++++ src/sprite.h | 22 ++++++ src/vec.c | 9 --- src/vec.h | 14 ++++ 6 files changed, 232 insertions(+), 196 deletions(-) create mode 100644 src/sprite.c create mode 100644 src/sprite.h delete mode 100644 src/vec.c create mode 100644 src/vec.h diff --git a/src/constants.h b/src/constants.h index 08c62b3..87c73e4 100644 --- a/src/constants.h +++ b/src/constants.h @@ -1,6 +1,8 @@ #ifndef CONSTANTS_LOADED #define CONSTANTS_LOADED +#define MAX_PLAYER_COUNT 1 + #define GRAV_ACC 1 #define TERM_VELY 3 #define TERM_VELX 2 @@ -11,6 +13,7 @@ #define JUMP_TIMEOUT_FRAMES 18 #define DASH_VELL 7 +// mfw when pythagoras irl :scream_cat: #define DASH_VELD 5 #define DASH_TIMEOUT_FRAMES 30 diff --git a/src/main.c b/src/main.c index e5ca645..ac2969e 100644 --- a/src/main.c +++ b/src/main.c @@ -5,60 +5,8 @@ #include"../res/sprites.h" #include "./flags.h" #include "./constants.h" -#include "./vec.c" - -int getTileIndexByCoord(unsigned int x, unsigned int y) -{ - return ((y / PIXELS_PER_TILE) * 20) + (x / PIXELS_PER_TILE); -} - -// check for external collisions (if rectangle is at least touchign something) -unsigned int collision(unsigned char map[], struct UVec *sprite_pos, struct UVec *sprite_size) -{ - unsigned int rv = 0; - struct UVec itl, itr, ibl, ibr; - - itl.x = sprite_pos->x-8; itl.y = sprite_pos->y-16; - itr.x = sprite_pos->x-1; itr.y = sprite_pos->y-16; - ibl.x = sprite_pos->x-8; ibl.y = sprite_pos->y-9; - ibr.x = sprite_pos->x-1; ibr.y = sprite_pos->y-9; - - // check if corners are in a non 0 tile - if (map[getTileIndexByCoord(itl.x, itl.y-1)]) rv = rv | SC_TL; - if (map[getTileIndexByCoord(itr.x, itr.y-1)]) rv = rv | SC_TR; - - if (map[getTileIndexByCoord(ibl.x, ibl.y+1)]) rv = rv | SC_BL; - if (map[getTileIndexByCoord(ibr.x, ibr.y+1)]) rv = rv | SC_BR; - - if (map[getTileIndexByCoord(itl.x-1, itl.y)]) rv = rv | SC_LT; - if (map[getTileIndexByCoord(ibl.x-1, ibl.y)]) rv = rv | SC_LB; - - if (map[getTileIndexByCoord(itr.x+1, itr.y)]) rv = rv | SC_RT; - if (map[getTileIndexByCoord(ibr.x+1, ibr.y)]) rv = rv | SC_RB; - - return rv; -} - - -// calculate internal collisions from external collisions -unsigned int icollision(unsigned char map[], struct UVec *sprite_pos, struct UVec *sprite_size) -{ - unsigned int c, rv = 0; - struct UVec itl, itr, ibl, ibr; - - itl.x = sprite_pos->x-8; itl.y = sprite_pos->y-16; - itr.x = sprite_pos->x-1; itr.y = sprite_pos->y-16; - ibl.x = sprite_pos->x-8; ibl.y = sprite_pos->y-9; - ibr.x = sprite_pos->x-1; ibr.y = sprite_pos->y-9; - - if (map[getTileIndexByCoord(itl.x, itl.y)]) rv = rv | SC_ITL; - if (map[getTileIndexByCoord(itr.x, itr.y)]) rv = rv | SC_ITR; - if (map[getTileIndexByCoord(ibl.x, ibl.y)]) rv = rv | SC_IBL; - if (map[getTileIndexByCoord(ibr.x, ibr.y)]) rv = rv | SC_IBR; - - return rv; -} - +#include "./sprite.h" +#include "./vec.h" void init_gfx() { @@ -77,146 +25,25 @@ void init_gfx() void main(void) { - unsigned int c, search_found, frames_since_last_jump, frames_since_last_dash, fc = 0; - int xsearch, ysearch; - struct UVec sprite_pos, search_pos, sprite_size; - struct Vec velocity, acceleration; + unsigned int fc = 0; + struct Sprites sprite; init_gfx(); - sprite_pos.x = 72; - sprite_pos.y = 35; - sprite_size.x = 8; - sprite_size.y = 8; - frames_since_last_jump = JUMP_TIMEOUT_FRAMES; - frames_since_last_dash = DASH_TIMEOUT_FRAMES; - velocity.x = 0; - velocity.y = 0; - acceleration.x = 0; - acceleration.y = 0; + sprite.pos.x = 72; + sprite.pos.y = 36; + sprite.vel.x = 0; + sprite.vel.y = 0; + sprite.acc.x = 0; + sprite.acc.y = 0; + move_sprite(0, sprite.pos.x, sprite.pos.y); - move_sprite(0, sprite_pos.x, sprite_pos.y); // Loop forever while(1) { - // the gameboy simulates physics too quickly to have good control over the speeds of things - // so draw them less often and move them more - if (frames_since_last_jump <= JUMP_TIMEOUT_FRAMES) frames_since_last_jump++; - if (frames_since_last_dash <= DASH_TIMEOUT_FRAMES) frames_since_last_dash++; - - if (++fc % 2 == 0) { - velocity.y += GRAV_ACC; - fc = 0; - } - - // player move left/right - if ((joypad() & J_RIGHT) && (velocity.x < TERM_VELX)) acceleration.x = 1; - else if ((joypad() & J_LEFT) && (velocity.x > -TERM_VELX)) acceleration.x = -1; - else acceleration.x = -velocity.x/abs(velocity.x); - velocity.x += acceleration.x; - - // player dash - if ((joypad() & J_B) && (frames_since_last_dash > DASH_TIMEOUT_FRAMES)) { - int frames_since_last_dash_copy = frames_since_last_dash; - frames_since_last_dash = 0; - - if ((joypad() & J_LEFT) && !(joypad() & (J_UP | J_RIGHT | J_DOWN))) { - velocity.x = -DASH_VELL; - } else if ((joypad() & J_RIGHT) && !(joypad() & (J_UP | J_LEFT | J_DOWN))) { - velocity.x = DASH_VELL; - } else if ((joypad() & J_UP) && !(joypad() & (J_RIGHT | J_LEFT | J_DOWN))) { - velocity.y = -DASH_VELL; - } else if ((joypad() & J_DOWN) && !(joypad() & (J_UP | J_LEFT | J_RIGHT))) { - velocity.y = DASH_VELL; - } else if ((joypad() & J_RIGHT) && (joypad() & J_UP)) { - velocity.x = DASH_VELD; - velocity.y = -DASH_VELD; - } else if ((joypad() & J_LEFT) && (joypad() & J_UP)) { - velocity.x = -DASH_VELD; - velocity.y = -DASH_VELD; - } else if ((joypad() & J_LEFT) && (joypad() & J_DOWN)) { - velocity.x = -DASH_VELD; - velocity.y = DASH_VELD; - } else if ((joypad() & J_RIGHT) && (joypad() & J_DOWN)) { - velocity.x = DASH_VELD; - velocity.y = DASH_VELD; - } else { - // if player doesn't hold the right buttons to dash, don't dash and set the - // frames_since_last_dash counter back to what it was before - frames_since_last_dash = frames_since_last_dash_copy; - } - } - - // slow down sprite in kind of realistic way - // (in real life i think it's drag is proportional to velocity square but not - // sure if that is worth the compute yet) - if (velocity.y > TERM_VELY) velocity.y -= (abs(velocity.y)-TERM_VELY)/TERM_VELY_DIVISOR; - if (velocity.y < -TERM_VELY) velocity.y += (abs(velocity.y)-TERM_VELY)/TERM_VELY_DIVISOR; - if (velocity.x > TERM_VELX) velocity.x -= (abs(velocity.x)-TERM_VELX)/TERM_VELX_DIVISOR; - if (velocity.x < -TERM_VELX) velocity.x += (abs(velocity.x)-TERM_VELX)/TERM_VELX_DIVISOR; - - // don't let player move in a direction which they are already touching something - if (((c & SC_TL) | (c & SC_TR)) && (velocity.y < 0)) { velocity.y = 0; } - if (((c & SC_BL) | (c & SC_BR)) && (velocity.y > 0)) { - // player jump - if ((joypad() & J_A) && (frames_since_last_jump > JUMP_TIMEOUT_FRAMES)) { - velocity.y = -JUMP_VEL; - frames_since_last_jump = 0; - } - else velocity.y = 0; - } - if (((c & SC_RT) | (c & SC_RB)) && (velocity.x > 0)) { velocity.x = 0; } - if (((c & SC_LT) | (c & SC_LB)) && (velocity.x < 0)) { velocity.x = 0; } - - sprite_pos.x += velocity.x; - sprite_pos.y += velocity.y; - - c = collision(map, &sprite_pos, &sprite_size); - - // move player out of tiles by brute force searching :eyes: - // TODO this is really stupid and bad and i think it will have to be changed when there - // are more players moving around - search_found = 0; - for (ysearch = 0; ysearch <= SPRITE_HEIGHT; ysearch++) { - for (xsearch = 0; xsearch <= SPRITE_WIDTH; xsearch++) { - search_pos.x = sprite_pos.x + xsearch; - search_pos.y = sprite_pos.y + ysearch; - if(icollision(map, &search_pos, &sprite_size) == 0) { - sprite_pos.x = search_pos.x; - sprite_pos.y = search_pos.y; - search_found = 1; - break; - } - search_pos.x = sprite_pos.x - xsearch; - search_pos.y = sprite_pos.y + ysearch; - if(icollision(map, &search_pos, &sprite_size) == 0) { - sprite_pos.x = search_pos.x; - sprite_pos.y = search_pos.y; - search_found = 1; - break; - } - search_pos.x = sprite_pos.x - xsearch; - search_pos.y = sprite_pos.y - ysearch; - if(icollision(map, &search_pos, &sprite_size) == 0) { - sprite_pos.x = search_pos.x; - sprite_pos.y = search_pos.y; - search_found = 1; - break; - } - search_pos.x = sprite_pos.x + xsearch; - search_pos.y = sprite_pos.y - ysearch; - if(icollision(map, &search_pos, &sprite_size) == 0) { - sprite_pos.x = search_pos.x; - sprite_pos.y = search_pos.y; - search_found = 1; - break; - } - } - if (search_found) break; - } - - // finally move the sprite - move_sprite(0, sprite_pos.x, sprite_pos.y); + fc++; + sprite_iter_frame(&sprite, map, joypad(), &fc); + move_sprite(0, sprite.pos.x, sprite.pos.y); // Done processing, yield CPU and wait for start of next frame wait_vbl_done(); diff --git a/src/sprite.c b/src/sprite.c new file mode 100644 index 0000000..b40e682 --- /dev/null +++ b/src/sprite.c @@ -0,0 +1,179 @@ +#include +#include +#include "./vec.h" +#include "./constants.h" +#include "./flags.h" + +typedef struct Sprites { + int frames_since_last_dash; + int frames_since_last_jump; + int size; + UVec pos; + Vec vel; + Vec acc; +} Sprite; + + +typedef struct SpriteCorners { + UVec itl; + UVec itr; + UVec ibl; + UVec ibr; +} SpriteCorners; + + +void sprite_iter_frame(Sprite *sprite, unsigned char map[], int joypad, unsigned int *fc); +int getTileIndexByCoord(unsigned int x, unsigned int y); +unsigned int sprite_internal_collision(unsigned char map[], Sprite *sprite); +unsigned int sprite_collision(unsigned char map[], Sprite *sprite); +void sprite_decollide(unsigned char map[], Sprite *sprite); + +// update a sprite for stuff that changes every frame +void sprite_iter_frame(Sprite *sprite, unsigned char map[], int joypad, unsigned int *fc) +{ + int c, collision_check_steps; + + // target displacement is sprite->vel but may collide, giving actual displacment + Vec displacement = {.x = 0, .y = 0}; + UVec original_position; + + if (sprite->frames_since_last_jump <= JUMP_TIMEOUT_FRAMES) sprite->frames_since_last_jump++; + if (sprite->frames_since_last_dash <= DASH_TIMEOUT_FRAMES) sprite->frames_since_last_dash++; + + if (*fc % 2 == 0) { + sprite->vel.y += GRAV_ACC; + *fc = 0; + } + + // player move left/right + if ((joypad & J_RIGHT) && (sprite->vel.x < TERM_VELX)) sprite->acc.x = 1; + else if ((joypad & J_LEFT) && (sprite->vel.x > -TERM_VELX)) sprite->acc.x = -1; + else sprite->acc.x = -sprite->vel.x/abs(sprite->vel.x); + sprite->vel.x += sprite->acc.x; + + // player dash + if ((joypad & J_B) && (sprite->frames_since_last_dash > DASH_TIMEOUT_FRAMES)) { + int frames_since_last_dash_copy = sprite->frames_since_last_dash; + sprite->frames_since_last_dash = 0; + + if ((joypad & J_LEFT) && !(joypad & (J_UP | J_RIGHT | J_DOWN))) { + sprite->vel.x = -DASH_VELL; + } else if ((joypad & J_RIGHT) && !(joypad & (J_UP | J_LEFT | J_DOWN))) { + sprite->vel.x = DASH_VELL; + } else if ((joypad & J_UP) && !(joypad & (J_RIGHT | J_LEFT | J_DOWN))) { + sprite->vel.y = -DASH_VELL; + } else if ((joypad & J_DOWN) && !(joypad & (J_UP | J_LEFT | J_RIGHT))) { + sprite->vel.y = DASH_VELL; + } else if ((joypad & J_RIGHT) && (joypad & J_UP)) { + sprite->vel.x = DASH_VELD; + sprite->vel.y = -DASH_VELD; + } else if ((joypad & J_LEFT) && (joypad & J_UP)) { + sprite->vel.x = -DASH_VELD; + sprite->vel.y = -DASH_VELD; + } else if ((joypad & J_LEFT) && (joypad & J_DOWN)) { + sprite->vel.x = -DASH_VELD; + sprite->vel.y = DASH_VELD; + } else if ((joypad & J_RIGHT) && (joypad & J_DOWN)) { + sprite->vel.x = DASH_VELD; + sprite->vel.y = DASH_VELD; + } else { + // if player doesn't hold the right buttons to dash, don't dash and set the + // frames_since_last_dash counter back to what it was before + sprite->frames_since_last_dash = frames_since_last_dash_copy; + } + } + + // slow down sprite in kind of realistic way + // (in real life i think it's drag is proportional to sprite->vel square but not + // sure if that is worth the compute yet cause everythign else is shit) + if (sprite->vel.y > TERM_VELY) sprite->vel.y -= (abs(sprite->vel.y)-TERM_VELY)/TERM_VELY_DIVISOR; + if (sprite->vel.y < -TERM_VELY) sprite->vel.y += (abs(sprite->vel.y)-TERM_VELY)/TERM_VELY_DIVISOR; + if (sprite->vel.x > TERM_VELX) sprite->vel.x -= (abs(sprite->vel.x)-TERM_VELX)/TERM_VELX_DIVISOR; + if (sprite->vel.x < -TERM_VELX) sprite->vel.x += (abs(sprite->vel.x)-TERM_VELX)/TERM_VELX_DIVISOR; + + c = sprite_collision(map, sprite); + + // don't let player move in a direction which they are already touching something + if (((c & SC_TL) | (c & SC_TR)) && (sprite->vel.y < 0)) { sprite->vel.y = 0; } + if (((c & SC_BL) | (c & SC_BR)) && (sprite->vel.y > 0)) { + // player jump + if ((joypad & J_A) && (sprite->frames_since_last_jump > JUMP_TIMEOUT_FRAMES)) { + sprite->vel.y = -JUMP_VEL; + sprite->frames_since_last_jump = 0; + } + else sprite->vel.y = 0; + } + if (((c & SC_RT) | (c & SC_RB)) && (sprite->vel.x > 0)) { sprite->vel.x = 0; } + if (((c & SC_LT) | (c & SC_LB)) && (sprite->vel.x < 0)) { sprite->vel.x = 0; } + + int absvelx = abs(sprite->vel.x); + int absvely = abs(sprite->vel.y); + collision_check_steps = (absvelx > absvely) ? absvelx : absvely; + + original_position.x = sprite->pos.x; + original_position.y = sprite->pos.y; + // check if following the path by the velocity will lead to a collision + for (int step = collision_check_steps; step >= 0; step--) { + sprite->pos.x = original_position.x + ((step * sprite->vel.x)/collision_check_steps); + sprite->pos.y = original_position.y + ((step * sprite->vel.y)/collision_check_steps); + + if (!sprite_internal_collision(map, sprite)) break; + } + + // holy shit it's so crunchy + //sprite_decollide(map, sprite); +} + + +int getTileIndexByCoord(unsigned int x, unsigned int y) +{ + return ((y / PIXELS_PER_TILE) * 20) + (x / PIXELS_PER_TILE); +} + + +SpriteCorners *getSpriteCorners(Sprite *sprite) +{ + SpriteCorners r = { + .itl = { .x = sprite->pos.x-8, .y = sprite->pos.y-16 }, + .itr = { .x = sprite->pos.x-1, .y = sprite->pos.y-16 }, + .ibl = { .x = sprite->pos.x-8, .y = sprite->pos.y-9 }, + .ibr = { .x = sprite->pos.x-1, .y = sprite->pos.y-9 } + }; + return &r; +} + + + +// calculate internal collisions +unsigned int sprite_internal_collision(unsigned char map[], Sprite *sprite) +{ + unsigned int rv = 0; + SpriteCorners *sc = getSpriteCorners(sprite); + + if (map[getTileIndexByCoord(sc->itl.x, sc->itl.y)]) rv = rv | SC_ITL; + if (map[getTileIndexByCoord(sc->itr.x, sc->itr.y)]) rv = rv | SC_ITR; + if (map[getTileIndexByCoord(sc->ibl.x, sc->ibl.y)]) rv = rv | SC_IBL; + if (map[getTileIndexByCoord(sc->ibr.x, sc->ibr.y)]) rv = rv | SC_IBR; + + return rv; +} + + +// check for external collisions (if rectangle is at least touchign something) +unsigned int sprite_collision(unsigned char map[], Sprite *sprite) +{ + unsigned int rv = 0; + SpriteCorners *sc = getSpriteCorners(sprite); + + // check if corners are in a non 0 tile + if (map[getTileIndexByCoord(sc->itl.x, sc->itl.y-1)]) rv = rv | SC_TL; + if (map[getTileIndexByCoord(sc->itr.x, sc->itr.y-1)]) rv = rv | SC_TR; + if (map[getTileIndexByCoord(sc->ibl.x, sc->ibl.y+1)]) rv = rv | SC_BL; + if (map[getTileIndexByCoord(sc->ibr.x, sc->ibr.y+1)]) rv = rv | SC_BR; + if (map[getTileIndexByCoord(sc->itl.x-1, sc->itl.y)]) rv = rv | SC_LT; + if (map[getTileIndexByCoord(sc->ibl.x-1, sc->ibl.y)]) rv = rv | SC_LB; + if (map[getTileIndexByCoord(sc->itr.x+1, sc->itr.y)]) rv = rv | SC_RT; + if (map[getTileIndexByCoord(sc->ibr.x+1, sc->ibr.y)]) rv = rv | SC_RB; + + return rv; +} diff --git a/src/sprite.h b/src/sprite.h new file mode 100644 index 0000000..423dcc8 --- /dev/null +++ b/src/sprite.h @@ -0,0 +1,22 @@ +#ifndef SPRITE_H_ +#define SPRITE_H_ + +#include "./vec.h" + +typedef struct Sprites { + int frames_since_last_dash; + int frames_since_last_jump; + int size; + UVec pos; + Vec vel; + Vec acc; +} Sprite; + +void sprite_iter_frame(Sprite *sprite, unsigned char map[], int joypad, unsigned int *fc); +int getTileIndexByCoord(unsigned int x, unsigned int y); +unsigned int sprite_internal_collision(unsigned char map[], Sprite *sprite); +unsigned int sprite_collision(unsigned char map[], Sprite *sprite); +void sprite_decollide(unsigned char map[], Sprite *sprite); + + +#endif diff --git a/src/vec.c b/src/vec.c deleted file mode 100644 index ac662ce..0000000 --- a/src/vec.c +++ /dev/null @@ -1,9 +0,0 @@ -struct UVec { - unsigned int x; - unsigned int y; -}; - -struct Vec { - int x; - int y; -}; diff --git a/src/vec.h b/src/vec.h new file mode 100644 index 0000000..1591f38 --- /dev/null +++ b/src/vec.h @@ -0,0 +1,14 @@ +#ifndef VEC_H_ +#define VEC_H_ + +typedef struct UVecs { + unsigned int x; + unsigned int y; +} UVec; + +typedef struct Vecs { + int x; + int y; +} Vec; + +#endif