21. Finished Platformer

First thing I added was a title screen. To be honest, I made this as quickly as possible, just to show the proof of concept. I made it in NES Screen Tool, and exported the background as a compressed RLE .h file “title.h”. So the title mode waits for the user to press start, and cycles the color a bit to be a little less boring.

26_full_game1

temp1 = get_frame_count();
temp1 = (temp1 >> 3) & 3;
pal_col(3,title_color_rotate[temp1]);

title_color_rotate is an array of 4 possible colors.

Now, I didn’t like making 1 room at a time, so I made it so you could have 1 long Tiled file, and export it to csv, and convert it to arrays with CSV2C_BIG.py.

26_Tiled

(I had it auto generate an array of pointers at the bottom, but I didn’t end up using those, so I delete them, and instead made 1 larger array of pointers, with all levels in it).

const unsigned char * const Levels_list[]={
Level1_0,Level1_1,Level1_2,Level1_3,Level1_4,Level1_5,Level1_6,Level1_7,
Level2_0,Level2_1,Level2_2,Level2_3,Level2_4,Level2_5,Level2_6,Level2_7,
Level3_0,Level3_1,Level3_2,Level3_3,Level3_4,Level3_5,Level3_6,Level3_7
};

I am using 2 kinds of enemies and 2 kinds of coins.

And then, I made a picture with all the Sprite objects on it (with transparency for background), and imported that as a separate tileset in Tiled, and added another layer, where the Sprite objects are placed. I placed the enemies on that 2nd layer (as tiles), and exported another csv file. Then I wrote another .py file “CSV2C_SP.py” to convert those into arrays.

26_Tiled2

Well, I didn’t end up using it exactly like this. It mixes the coins and the enemies, and I want them in separate arrays. So, I cut and pasted the different kinds of objects into 2 different arrays. But the .py file is helpful, and definitely sped this up.

These arrays might need to be edited slightly, like if we need coins at a different X offset from the tile grid.

 

Again, I made 2 kinds of enemies. The chasers now collide with walls. I was going to use the same code that the hero used, but decided it was too slow to check many objects this way, so I wrote a much simpler one.

bg_collision_fast(). This only checks 2 points instead of 4.

The chaser code isn’t very smart, they only move X and never change Y. If you put them on a platform, they would float right off it like a ghost. Maybe in the future I will edit this with improved enemy move logic, so he won’t float off a platform, but rather change directions, or fall, or something.

The other enemy is the bouncer. He just bounces up and down. He checks the floor below when falling, to stop exactly at the floor point, reusing the same code from the hero checking the floor.

bg_check_low();

The second kind of coin is just an end-of-level marker. I suppose we could have added some cool sound fx for reaching the end of the level, or an animation. That would have been nice. Currently, it just fades to black in the switch mode.

Oh, yeah. I added more modes. More game states. title, game, pause, end, switch (transition from one level to another). These are fairly obvious as how they work.

Debugging wasn’t too bad. Mostly I was worried about running past the frame and getting slowdown. I was testing code by placing gray_line() around functions. This helped me speed up things. I combined some of the enemy steps to speed them up. And I would put a gray_line() at the end of the game logic to see how far down on the screen we were. Here’s one of the tests, back when I thought I was going to use sprite zero hit and a HUD above.

full_game_debug

We don’t want to make our enemy logic too much more complex, nor put too many objects on the same screen, or we might get slow down, so we need to test as we go, and see how many enemies we can fit on a screen before it crawls to a halt. I think we can handle 7 or 8. That’s more than I need, so we’re still ok.

And finally, I put the # of coins as sprites in the top left. I didn’t put it too high up, where it might get cut off on some old TVs. 16 pixels down is fine.

Oh, almost forgot. The sprite shuffing code. Remember from the Sprite page, I mentioned that you can only have 8 sprites per horizontal line? Well, since that is a possibility, we must add some kind of shuffling to the position of each object inside the OAM buffer, so that we don’t have an enemy disappear entirely.

The simplest way to do this would be to change the starting position each frame. Instead of sprid = 0, you would do sprid = rand8() * 4, or something like that. It wouldn’t be very good, though.

I decided to go through the list of enemies in a different order each frame (and keep sprid = 0 at the top of the sprite drawing code).

const unsigned char shuffle_array[]={
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,
0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15,
15,13,11,9,7,5,3,1,14,12,10,8,6,4,2,0
};

So, the first pass, it goes forward 0-15. The second pass it goes in reverse. The 3rd pass it does even then odds. The 4th pass, reverse that. This would break immediately if we changed the # of enemies, so it could use some improvement. Also, I’m not shuffling coins, so I have to make sure there aren’t too many coins on the same horizontal line.

And here’s our working game, with 3 levels, 8 rooms each. This could have been expanded a bit. It takes about 2000 bytes per level. We have about 16000 bytes left, so we could have added 7-8 more levels… so maybe 10 levels total. If we needed more than that, we would need to think about a different mapper, or maybe some kind of compression.

26_full_game2

https://github.com/nesdoug/26_Full_Game/blob/master/full_game.c

https://github.com/nesdoug/26_Full_Game

 

And, that’s all. Go make some games.

 

Advertisements

3 thoughts on “21. Finished Platformer

  1. Hey, Doug, in the reset code of the “hello world” program, why is there a colon sandwiched between stx $4010 and lda $2002? And what does the “bpl :-” sandwiched between lda $2002 and lda #$00 mean? This is all before the Blankram routine.

    Like

  2. Hello, it’s me again. This time, I’m using 6502 Assembly alone to program. So far, I have been majorly unsuccessful at trying to even draw a simple sprite on the screen. NESDEV forums couldn’t help me, nor were they probably aware I was using the cc65 compiler. This is the code I wrote based on another code that blinks the screen from gray to grayish blue:
    .segment “HEADER”
    .byte “NES”
    .byte $1a
    .byte $02
    .byte $01
    .byte %00000000
    .byte $00
    .byte $00
    .byte $00
    .byte $00
    .byte $00,$00,$00,$00,$00

    .segment “STARTUP”
    .segment “ZEROPAGE”
    flag: .res 1
    counter: .res 1
    .segment “CODE”

    WAITVBLANK:
    :
    BIT $2002
    BPL :-
    RTS

    RESET:
    SEI ; disable IRQs
    CLD ; disable decimal mode
    LDX #$40
    STX $4017 ; disable APU frame IRQ
    LDX #$FF
    TXS ; Set up stack
    INX ; now X = 0
    STX $2000 ; disable NMI
    STX $2001 ; disable rendering
    STX $4010 ; disable DMC IRQs

    JSR WAITVBLANK

    clrmem:
    LDA #$00
    STA $0000, x
    STA $0100, x
    STA $0200, x
    STA $0400, x
    STA $0500, x
    STA $0600, x
    STA $0700, x
    LDA #$FE
    STA $0300, x
    INX
    BNE clrmem

    LDA #%10000000
    STA flag

    JSR WAITVBLANK
    LDA #$3F
    STA $2006
    LDA #$01
    STA $2006
    LDA #$07
    STA $2007
    LDA #$17
    STA $2007
    LDA #$37
    STA $2007
    LDA #$80
    STA $0200
    LDA #$10
    STA $0201
    LDA #$00
    STA $0202
    LDA #$70
    STA $0203
    LDA #%00000000
    STA counter
    LDA #%00010000
    STA $2001
    LDA #%10001000
    STA $2000
    LDA #$3F
    STA $2006
    LDA #$00
    STA $2006
    STA $2007
    Forever:
    JMP Forever

    VBLANK:
    LDA #$00
    STA $2003
    LDA #$02
    STA $4014
    INC counter
    LDA counter
    CMP #$3C
    BNE SkipColorChange
    LDA flag
    EOR #%10000000
    STA flag
    STA $2001
    LDA #$00
    STA counter
    SkipColorChange:
    RTI

    .segment “VECTORS”
    .word VBLANK
    .word RESET
    .word 0

    .segment “CHARS”
    .incbin “ball.chr”

    And whenever I compile, I use the following commands;
    cc65 blinktest.asm -o blinktest.o -t nes
    ld65 blinktest.o -o blinktest.nes -t nes

    What am I doing wrong? I need some honest help because this is driving me insane being that what should be a simple task is legendarily difficult for me. If you’re unable to help me with this, can you refer me to someone you know who might know all this? Thanks!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s