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.