git.y1.nz

gbdk-2020

GameBoy Development Kit
download: https://git.y1.nz/archives/gbdk.tar.gz
README | Files | Log | Refs | LICENSE

gbdk-lib/examples/cross-platform/platformer_template/src/player.c

      1 #pragma bank 255
      2 
      3 #include <gbdk/platform.h>
      4 #include <gbdk/metasprites.h>
      5 #include <stdint.h>
      6 #include "common.h"
      7 #include "PlayerCharacterSprites.h"
      8 #include "camera.h"
      9 #include "level.h"
     10 
     11 BANKREF_EXTERN(PlayerCharacterSprites)
     12 
     13 
     14 #define GRAVTY 45
     15 #define GROUND_FRICTION 15
     16 #define PLAYER_CHARACTER_INCREASE_JUMP_TIMER_MAX 20
     17 #define PLAYER_CHARACTER_JUMP_VELOCITY 550
     18 #define PLAYER_CHARACTER_WALK_VELOCITY 325
     19 #define PLAYER_CHARACTER_RUN_VELOCITY 425
     20 #define PLAYER_CHARACTER_WALK_TWO_FRAME_COUNTER 3
     21 #define PLAYER_CHARACTER_RUN_TWO_FRAME_COUNTER 5
     22 
     23 #define PLAYER_CHARACTER_METASPRITE_PIVOT_X 12
     24 #define PLAYER_CHARACTER_METASPRITE_PIVOT_Y 6
     25 #define PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH 5
     26 #define PLAYER_CHARACTER_BOUNDING_BOX_HALF_HEIGHT 12
     27 #define PLAYER_CHARACTER_BOUNDING_BOX_HEIGHT 24
     28 
     29 uint8_t facingRight =TRUE;
     30 
     31 uint8_t playerJumpIncrease = 0;
     32 uint8_t threeFrameCounter=0;
     33 uint16_t playerX, playerY;
     34 int16_t playerXVelocity, playerYVelocity;
     35 
     36 const uint8_t baseProp=0;
     37 
     38 #if defined(SEGA)
     39     #define PLAYER_PALETTES_BANK CURRENT_BANK
     40     #define PLAYER_PALETTES PlayerPalettesGGSMS
     41     #define set_player_sprite_data set_sprite_native_data
     42 
     43 #else
     44     #define PLAYER_PALETTES_BANK BANK(PlayerCharacterSprites)
     45     #define PLAYER_PALETTES PlayerCharacterSprites_palettes
     46     #define set_player_sprite_data set_sprite_data
     47 #endif
     48 
     49 
     50 
     51 const palette_color_t PlayerPalettesGGSMS[16] = {
     52 	RGB8(255,128, 64), RGB8(248,248,248), RGB8(168,168,168), RGB8(  0,  0,  0)
     53 	,
     54 	RGB8(143,  0,  0), RGB8(  6,112,  0), RGB8(  0, 31,173), RGB8(122,134,  0)
     55 	,
     56 	RGB8(  0,138,111), RGB8( 75,  0, 82), RGB8(255,  0,  0), RGB8( 33,255,  0)
     57 	,
     58 	RGB8(  0, 46,255), RGB8(255,238,  0), RGB8(  0,225,255), RGB8(253,132,255)
     59 	
     60 };
     61 
     62 /**
     63  * @brief We'll Put the PlayerCharacter's tiles in VRAM.
     64  * Not every platform supports tile-flipping. We want To avoid wasting vram space with both left & right animations.
     65  * we'll keep tiles only for the direction we are facing.
     66  */
     67 void UpdatePlayerVRAMTiles(void) NONBANKED{
     68     uint8_t _previous_bank = CURRENT_BANK;
     69 
     70 
     71     SWITCH_ROM(BANK(PlayerCharacterSprites));
     72    
     73     set_player_sprite_data (0,PlayerCharacterSprites_TILE_COUNT,PlayerCharacterSprites_tiles);
     74 
     75     SWITCH_ROM(_previous_bank);
     76 }
     77 
     78 void SetPlayerPalettes(void) NONBANKED{
     79     uint8_t _previous_bank = CURRENT_BANK;
     80 
     81         SWITCH_ROM(PLAYER_PALETTES_BANK);
     82     
     83     // Set up color palettes
     84     #if defined(SEGA)
     85             set_sprite_palette(baseProp, 1, PLAYER_PALETTES);
     86     #elif defined(GAMEBOY)
     87         if (_cpu == CGB_TYPE) {
     88             set_sprite_palette(OAMF_CGB_PAL0, 1, PLAYER_PALETTES);
     89         }
     90     #elif defined(NINTENDO_NES)
     91         set_sprite_palette(baseProp, 4, PLAYER_PALETTES);
     92     #endif
     93 
     94 
     95     SWITCH_ROM(_previous_bank);
     96 }
     97 
     98 void SetupPlayer(void) BANKED{
     99 
    100     // Player will start at 40,40
    101     // the playerX and playerY variables are scaled, so we shift to the left by 4
    102     playerX=40<<4;
    103     playerY=40<<4;
    104 
    105     playerXVelocity=0;
    106     playerYVelocity=0;
    107     
    108     UpdatePlayerVRAMTiles();
    109 
    110 
    111     SetPlayerPalettes();
    112  
    113 
    114 }
    115 
    116 uint8_t DrawPlayer(uint16_t playerRealX, uint16_t playerRealY, uint8_t frame) NONBANKED{
    117 
    118     uint8_t spritesUsed=0;
    119     uint8_t _previous_bank = CURRENT_BANK;
    120 
    121 
    122     // Get the player's position relative to the camera's position
    123     uint16_t playerCameraX = (playerRealX-camera_x)+DEVICE_SPRITE_PX_OFFSET_X;
    124     uint16_t playerCameraY= (playerRealY)+DEVICE_SPRITE_PX_OFFSET_Y;
    125 
    126     SWITCH_ROM(BANK(PlayerCharacterSprites));
    127 
    128     spritesUsed = move_metasprite_ex(PlayerCharacterSprites_metasprites[frame],0,baseProp,0,playerCameraX,playerCameraY);
    129  
    130     SWITCH_ROM(_previous_bank);
    131 
    132     return spritesUsed;
    133 }
    134 
    135 uint8_t UpdatePlayer(void) BANKED{
    136     
    137     // Use the run velocity if the B button is held
    138     // Animate the threeFrameCounter faster when B is held
    139     int16_t moveSpeed = (joypadCurrent & J_B) ?PLAYER_CHARACTER_RUN_VELOCITY:PLAYER_CHARACTER_WALK_VELOCITY;
    140     uint8_t threeFrameCounterSpeed = (joypadCurrent & J_B) ? PLAYER_CHARACTER_RUN_TWO_FRAME_COUNTER : PLAYER_CHARACTER_WALK_TWO_FRAME_COUNTER;
    141 
    142     threeFrameCounter+=threeFrameCounterSpeed;
    143     uint8_t threeFrameCounterValue = threeFrameCounter>>4;
    144     if(threeFrameCounterValue>=3){
    145         threeFrameCounter=0;
    146         threeFrameCounterValue=0;
    147     }
    148 
    149     uint8_t turning = FALSE;
    150 
    151     if(joypadCurrent &J_RIGHT){
    152 
    153         // If we are facing the other direction?
    154         if(playerXVelocity<0){
    155 
    156             // Just decrease the velocity, and save that we are turning
    157             playerXVelocity+=GROUND_FRICTION;
    158 
    159             // We are turning, IF we didn't just finish changing direction.
    160             if(playerXVelocity<0)turning=TRUE;
    161             else{
    162                 
    163                 facingRight=TRUE;
    164             }
    165         }else{
    166             playerXVelocity=moveSpeed;
    167 
    168             // Switch our vram data for the player
    169             if(!facingRight){
    170                 facingRight=TRUE;
    171             }
    172             
    173         }
    174     }else if(joypadCurrent &J_LEFT){
    175 
    176         // If we are facing the other direction?
    177         if(playerXVelocity>0){
    178 
    179             // Just decrease the velocity, and save that we are turning
    180             playerXVelocity-=GROUND_FRICTION;
    181 
    182             // We are turning, IF we didn't just finish changing direction.
    183             if(playerXVelocity>0)turning=TRUE;
    184              else{
    185                 
    186                 facingRight=FALSE;
    187                 
    188             }
    189         }else{
    190             playerXVelocity=-moveSpeed;
    191 
    192             // Switch our vram data for the player
    193             if(facingRight){
    194                 facingRight=FALSE;
    195                 
    196             }
    197             
    198         }
    199         
    200     }else{
    201 
    202           // Move the x velocity towards 0
    203         if (playerXVelocity > 0) {
    204           if (playerXVelocity >= GROUND_FRICTION) playerXVelocity -= GROUND_FRICTION;
    205           else playerXVelocity=0;
    206         }
    207         else if (playerXVelocity < 0) {
    208             if (playerXVelocity <= GROUND_FRICTION) playerXVelocity += GROUND_FRICTION;
    209             else playerXVelocity=0;
    210         }
    211     }
    212 
    213     uint16_t playerRealX = playerX>>4;
    214     uint16_t playerRealY = playerY>>4;
    215 
    216     uint8_t grounded = FALSE;
    217 
    218     // The Player Y is the top of the bounding box.
    219     // If the player starts to go above the top of the screen
    220     // then the unsigned integer will wrap. This will cause this loop
    221     // to fire for a long time until 'playerRealY+PLAYER_CHARACTER_BOUNDING_BOX_HEIGHT-1' wraps around to the bottom of the level.
    222     // For that reason, because we only scroll horizontally, make sure the player's y position isn't higher than the screen is tall
    223     if(playerRealY<DEVICE_SCREEN_PX_HEIGHT){
    224 
    225         // Prevent getting stuck in the ground
    226         while(IsTileSolid(playerRealX,playerRealY+PLAYER_CHARACTER_BOUNDING_BOX_HEIGHT-1)){
    227             playerY-=16;
    228             playerRealY = playerY>>4;
    229         }
    230     }
    231 
    232     /**
    233      * @brief Important Note: We need can't use the same x for horizontal collision as we do vertical.
    234      * For the horizontal collisions, we'll use player x + or - 5. 
    235      * For the vertical collisions, we'll use player x + or - 3.
    236      * If we use the same offset value for both, you'll see player getting stuck in the ground. 
    237      * This would be because both are firing & succeeding, and as a result: player's
    238      * x and y velocities are constantly being set to 0.
    239      */
    240 
    241     // If the player is moving horizontally
    242     if(playerXVelocity!=0){
    243 
    244         // If the player is moving to the right
    245         if(playerXVelocity>0){
    246 
    247             // The player sprite is sort of tall, we need to check in multiple places along the right edge
    248             // Subtract a little from the top & bottom edges so player doesn't get caught in ceilings/floors
    249             if(IsTileSolid(playerRealX+PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH,playerRealY+2)||IsTileSolid(playerRealX+PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH,playerRealY+PLAYER_CHARACTER_BOUNDING_BOX_HALF_HEIGHT)||IsTileSolid(playerRealX+PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH,playerRealY+(PLAYER_CHARACTER_BOUNDING_BOX_HEIGHT-2)))playerXVelocity=0;
    250 
    251         // If the player is moving to the left
    252         }else if(playerXVelocity<0){
    253 
    254             // The player sprite is sort of tall, we need to check in multiple places along the left edge
    255             // Subtract a little from the top & bottom edges so player doesn't get caught in ceilings/floors
    256             if(IsTileSolid(playerRealX-PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH,playerRealY+2)||IsTileSolid(playerRealX-PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH,playerRealY+PLAYER_CHARACTER_BOUNDING_BOX_HALF_HEIGHT)||IsTileSolid(playerRealX-PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH,playerRealY+(PLAYER_CHARACTER_BOUNDING_BOX_HEIGHT-2)))playerXVelocity=0;
    257         }
    258     }
    259 
    260     // If the player is moving downwards or still
    261     if(playerYVelocity>=0){
    262 
    263         // Check both sides of player's feet (left and right)
    264         // Subtract a little from the edge so player doesn't get caught in walls
    265         if(IsTileSolid(playerRealX+(PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH-2),playerRealY+PLAYER_CHARACTER_BOUNDING_BOX_HEIGHT)||IsTileSolid(playerRealX-(PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH-2),playerRealY+PLAYER_CHARACTER_BOUNDING_BOX_HEIGHT)){
    266             playerYVelocity=0;
    267             grounded=TRUE;
    268         }
    269 
    270     // If the player is moving upwards
    271     }else if(playerYVelocity<0){
    272 
    273        
    274         // Check both sides of player's head (left and right)
    275         // Subtract a little from the edge so player doesn't get caught in walls
    276         // To prevent getting stuck, move the player downward also and loop until there's no overlap
    277         while(IsTileSolid(playerRealX+(PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH-2),playerRealY)||IsTileSolid(playerRealX-(PLAYER_CHARACTER_BOUNDING_BOX_HALF_WIDTH-2),playerRealY)){
    278             playerYVelocity=0;
    279             playerY+=16;
    280             playerRealY = playerY>>4;
    281         }
    282 
    283 
    284     }
    285 
    286     uint8_t pressedA = (joypadCurrent & J_A && !(joypadPrevious & J_A));
    287     uint8_t pressedUp = (joypadCurrent & J_UP && !(joypadPrevious & J_UP));
    288     uint8_t pressedAOrUp = pressedA||pressedUp;
    289 
    290     // If we are grounded, and the A/Up button was JUST pressed
    291     if(grounded && pressedAOrUp){
    292         playerYVelocity=-PLAYER_CHARACTER_JUMP_VELOCITY;
    293         playerJumpIncrease=PLAYER_CHARACTER_INCREASE_JUMP_TIMER_MAX;
    294     }
    295 
    296     // If the player is in the air
    297     if(!grounded){
    298 
    299         // If we are in the air, increase the amount of time the player can increase the jump height
    300         if(playerJumpIncrease>0)playerJumpIncrease--;
    301 
    302         // If we are not holding A/Up, or if the amount of time we can increase our jump has ended
    303         if(!((joypadCurrent & J_A||joypadCurrent & J_UP))||playerJumpIncrease==0){
    304 
    305             // Apply gravity
    306             playerYVelocity+=GRAVTY;
    307 
    308             // Reset to zero here, so the player has to hold initally to increase jump height
    309             playerJumpIncrease=0;
    310         }
    311 
    312     // If the player is grounded, and moving downward
    313     }else if(playerYVelocity>=0){
    314 
    315         //  Stop the velocity now
    316         playerYVelocity=0;
    317     }
    318 
    319     // Apply the players velocity
    320     playerX+=playerXVelocity>>4;
    321     playerY+=playerYVelocity>>4;
    322 
    323     // Get the non-scaled version  of the player's position
    324     playerRealX = playerX>>4;
    325     playerRealY = playerY>>4;
    326 
    327     // if we've gone past the half-screen mark
    328     if(playerRealX>=(DEVICE_SCREEN_PX_WIDTH>>1)){
    329         uint16_t max = currentLevelWidth-DEVICE_SCREEN_PX_WIDTH;
    330         camera_x=playerRealX-(DEVICE_SCREEN_PX_WIDTH>>1);
    331 
    332         // Limit the camera position to avoid drawing offscreen/looping data
    333         if(camera_x>max)camera_x=max;
    334     }
    335     else camera_x=0;
    336 
    337     // Which of the player's metasprite frames should be shown?
    338     // If we are grounded:
    339     //   Turning        = Frame 5
    340     //   Standing Still = Frame 0
    341     //   Running        = Frame 0 - 2 (via threeFrameCounterValue variable)
    342     // If we are in the air:
    343     //   Rising         = Frame 3
    344     //   Falling        = Frame 4
    345     uint8_t frame = grounded ? (turning ? 5 :((playerXVelocity>>4)==0 ? 0 : threeFrameCounterValue)) : (playerYVelocity<0 ? 3 : 4);
    346     uint8_t directionOffset = facingRight ? 0 : 6;
    347     
    348     uint8_t spritesUsed = DrawPlayer(playerRealX,playerRealY,frame+directionOffset);
    349 
    350     // Increase the level if the player is at the end
    351     if(playerRealX>currentLevelWidth-32){
    352         nextLevel++;
    353     }
    354 
    355     return spritesUsed;
    356 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.