The awkward stage between beginner and intermediate
Moderator: Moderators
The awkward stage between beginner and intermediate
Alt. Title: How the hell do I even start making a text box?
So I'm sort of new to NES development, but I just can't figure out how to structure my programs. I am not a complete newbie; I've coded something a bit more elaborate than a simple Hello, World! program. But after that, I'm a bit lost. Sure, I've read a lot of material on NES development so far (including borrowing some code from various nesdev places, like tokumaro's ASM6 template), but yeah...
What are the patterns used to structure code for games, programs and demos? How do I switch from controlling a character, making a text box, scripting cutscenes and all that fun stuff? Could someone point me to the intermediate tutorials on this stuff?
Sorry for the numerous questions. I've just kind of reached a roadblock in my epic quest of NES development.
So I'm sort of new to NES development, but I just can't figure out how to structure my programs. I am not a complete newbie; I've coded something a bit more elaborate than a simple Hello, World! program. But after that, I'm a bit lost. Sure, I've read a lot of material on NES development so far (including borrowing some code from various nesdev places, like tokumaro's ASM6 template), but yeah...
What are the patterns used to structure code for games, programs and demos? How do I switch from controlling a character, making a text box, scripting cutscenes and all that fun stuff? Could someone point me to the intermediate tutorials on this stuff?
Sorry for the numerous questions. I've just kind of reached a roadblock in my epic quest of NES development.
- Hamtaro126
- Posts: 786
- Joined: Thu Jan 19, 2006 5:08 pm
Re: The awkward stage between beginner and intermediate
Try looking at the NESDEV Wiki, under Jump Table or RTS trick. Then look at SMBDIS from Romhacking.Netbooker wrote:Alt. Title: How the hell do I even start making a text box?
So I'm sort of new to NES development, but I just can't figure out how to structure my programs. I am not a complete newbie; I've coded something a bit more elaborate than a simple Hello, World! program. But after that, I'm a bit lost. Sure, I've read a lot of material on NES development so far (including borrowing some code from various nesdev places, like tokumaro's ASM6 template), but yeah...
What are the patterns used to structure code for games, programs and demos? How do I switch from controlling a character, making a text box, scripting cutscenes and all that fun stuff? Could someone point me to the intermediate tutorials on this stuff?
Sorry for the numerous questions. I've just kind of reached a roadblock in my epic quest of NES development.
AKA SmilyMZX/AtariHacker.
Thanks. I've actually got the RTS/Jump trick implemented in that ROM.
I suppose the way to do it is to have multiple gameloops and then have a pointer to each one. Then, at the end have a and just switch the pointer between modes. Am I on the right track?
I suppose the way to do it is to have multiple gameloops and then have a pointer to each one. Then, at the end have a
Code: Select all
jmp (gameloopPointer)- never-obsolete
- Posts: 403
- Joined: Wed Sep 07, 2005 9:55 am
- Location: Phoenix, AZ
- Contact:
There are many ways to do it, but here's a quick breakdown of how I structure my code (this is from a LoZ type game I'm working on):
I tend to keep my main states (states that are not really related to each other) separate and use absolute JMP/JSR to get there. "RESET" actually falls into "Sub_TitleScreen" and then into "Sub_MainMenu," but they could be jumped to from anywhere in the code. The name entry screen is contained within "Sub_NewGame."
This is my main(), and also where I start breaking things down into gamestates and use jmp ($n).
frameFinished is cleared in the NMI handler after the vram buffer has been written and sprite dma is finished.
Moving actors (players, enemies, etc) is done in "run actor ai." Text boxes are a substate of the player. The reason for choosing to structure it that way was because the player should not be able to do anything but acknowledge the text box when it's on screen. This may not work for your design, I know there are some RPGs that let you walk around while being talked to. This is how I handle the state of actors:
Scripting of any kind is handled in "run map script." A pointer is set when an area is loaded and is jumped to every frame.
I tend to keep my main states (states that are not really related to each other) separate and use absolute JMP/JSR to get there. "RESET" actually falls into "Sub_TitleScreen" and then into "Sub_MainMenu," but they could be jumped to from anywhere in the code. The name entry screen is contained within "Sub_NewGame."
Code: Select all
RESET:
; init hardware, variables
Sub_TitleScreen:
; reset variables
; do title screen stuff
; loop until player presses start
Sub_MainMenu:
; do menu stuff
; loop untill player makes a choice
if ( choice == 0 )
jsr Sub_NewGame
else
jsr Sub_LoadGame
jsr Sub_GameplayMain
jmp Sub_TitleScreen
This is my main(), and also where I start breaking things down into gamestates and use jmp ($n).
Code: Select all
Sub_GameplayMain:
; init gamestate
_loop_gameplayMain:
; check player->actor collsion
; run actor ai
; scroll camera if needed
; check area transition
; run map script
; fill oam
lda #TRUE
sta frameFinished
_loop_wait:
lda frameFinished
bne _loop_wait
jmp _loop_gameplayMain
_loop_exit:
; death scene or game ending
rts
Moving actors (players, enemies, etc) is done in "run actor ai." Text boxes are a substate of the player. The reason for choosing to structure it that way was because the player should not be able to do anything but acknowledge the text box when it's on screen. This may not work for your design, I know there are some RPGs that let you walk around while being talked to. This is how I handle the state of actors:
Code: Select all
ldy Object_Type, X ;
lda objAI_ptrlo, Y ; get pointer to object
sta t0 ; state table
lda objAI_ptrhi, Y ;
sta t0 + 1 ;
lda Object_State, X ; get object state pointer
asl ;
tay ;
lda (t0), Y ;
sta IndJump ;
iny ;
lda (t0), Y ;
sta IndJump + 1 ;
jsr Func_IndirectJump ; goto ai handler
Code: Select all
Func_IndirectJump: jmp (IndJump)
Re: The awkward stage between beginner and intermediate
Each section of your game should have an initialization block followed by a loop (sections that are similar enough could share the code though). Each of these sections should have this basic structure:booker wrote:How do I switch from controlling a character, making a text box, scripting cutscenes and all that fun stuff?
Code: Select all
1. initialize the game section;
2. read the controllers;
3. update the game world;
4. wait for the vertical blank;
5. update the video;
6. update the audio;
7. go back to step 2;Common tasks such as reading the controllers, clearing sprites, etc. can be put into subroutines, so that they are easily accessible from any part of your game.
The last part (audio and video updates) can be shared among all the game sections if you want to. If you consistently use a standardized buffer system that satisfies all parts of the game you can utilize it for the whole program.
To move from one game section to another, just jump between them. You can JSR to a new section (which will actually be a sub-section) if you plan to return later (something you would do with an in-game menu, for example) or just JMP to it (like when you die and go back to the title screen). You can use jump tables (and the RTS trick) to navigate between game sections too.
Re: The awkward stage between beginner and intermediate
I implemented a really weird system that's really inflexible for this. I was wondering if you have any designs for such a system. The new one I'm working on is like a queue that takes up the entire $0300 RAM page. In it, you load the PPU address hibyte and lobyte, amount of tiles, and then the actual tile data. At VBlank, the address is copied, the tiles are read and copied, until there are no more chunks like these to copy. I was wondering if there is a better method than this.tokumaru wrote:If you consistently use a standardized buffer system that satisfies all parts of the game you can utilize it for the whole program.
- Hamtaro126
- Posts: 786
- Joined: Thu Jan 19, 2006 5:08 pm
Re: The awkward stage between beginner and intermediate
What about using a new mapper with MMC1 or MMC3, put the ram address from the routine as RAM $6x00-6xFF or such, and keep $300-$3FF free.booker wrote:I implemented a really weird system that's really inflexible for this. I was wondering if you have any designs for such a system. The new one I'm working on is like a queue that takes up the entire $0300 RAM page. In it, you load the PPU address hibyte and lobyte, amount of tiles, and then the actual tile data. At VBlank, the address is copied, the tiles are read and copied, until there are no more chunks like these to copy. I was wondering if there is a better method than this.tokumaru wrote:If you consistently use a standardized buffer system that satisfies all parts of the game you can utilize it for the whole program.
AKA SmilyMZX/AtariHacker.
Re: The awkward stage between beginner and intermediate
That's the way to go I think, as long as it never takes longer than vblank. And make sure your buffer stuffer code is safe to be interrupted by NMI, in case that can happen. If you use this for nametables also, you will want to have the 'VRAM address inc-by-32' feature available for drawing columns. There's a lot of ways to optimize the buffer-unloading code, but it probably only matters if you're wanting to rewrite tiles during vblank (16 bytes per tile adds up fast).booker wrote:I implemented a really weird system that's really inflexible for this. I was wondering if you have any designs for such a system. The new one I'm working on is like a queue that takes up the entire $0300 RAM page. In it, you load the PPU address hibyte and lobyte, amount of tiles, and then the actual tile data. At VBlank, the address is copied, the tiles are read and copied, until there are no more chunks like these to copy. I was wondering if there is a better method than this.tokumaru wrote:If you consistently use a standardized buffer system that satisfies all parts of the game you can utilize it for the whole program.
In my NMI routine, I normally rewrite the palette every vblank, copying from RAM. I've found that it makes palette cycling easy to do.
- Hamtaro126
- Posts: 786
- Joined: Thu Jan 19, 2006 5:08 pm
MMC5 seems to be a great mapper for your text routine, Just re-implent it in ExRAM and enable the ExAttribute mode for it! Saves you some space in both RAM areas too,
Or you can use the option that I said before, No big deal.
Memblers: I have to really agree with you, His implentation is really good. I may even use that system somehow. Credit goes to him, though, if I use this ASM file or modify it.
Or you can use the option that I said before, No big deal.
Memblers: I have to really agree with you, His implentation is really good. I may even use that system somehow. Credit goes to him, though, if I use this ASM file or modify it.
AKA SmilyMZX/AtariHacker.
Re: The awkward stage between beginner and intermediate
Yeah this is pretty much the standard thing to do.booker wrote:I was wondering if you have any designs for such a system. The new one I'm working on is like a queue that takes up the entire $0300 RAM page. In it, you load the PPU address hibyte and lobyte, amount of tiles, and then the actual tile data. At VBlank, the address is copied, the tiles are read and copied, until there are no more chunks like these to copy. I was wondering if there is a better method than this.
One trick is to place the buffer on the lower part of the stack memory area ($100-$1EF or something, depending on how big a buffer/how much stack space you need), since most of the time the stack doesn't grow very big. Then you can also use PLA/PHA to pull/push stuff to/from the buffer.
Also remember that since the PPU address is only 14 bits, you can use the top two bits for stuff like the inc1/inc32 flag. 12 bits is enough if you only need nametable addresses.
Re: The awkward stage between beginner and intermediate
Like others have said, your way is the most straightforward. I often have a bunch of custom code for this, because I'm a little more ambitious with the amount of bytes I transfer to VRAM than most people, so I really need a few unrolled loops.booker wrote:I was wondering if you have any designs for such a system.
One advice I have though is that you allow the program to pick different VBlank handlers. In my game, the main gameplay needs the specialized code I mentioned earlier, but the rest of the program can do with a standard handler, so I just switch as needed.
Great advice in this thread. Thanks, everybody!
But isn't MMC5 a rare mapper? If ever I wanted to convert my ROM into an actual cart, that would be difficult. Not that I really think I'll do many ROM to cart conversions, but it is a consideration. Apparently it doesn't even work on the PowerPak!Hamtaro126 wrote:MMC5 seems to be a great mapper for your text routine, Just re-implent it in ExRAM and enable the ExAttribute mode for it! Saves you some space in both RAM areas too,
Sane homebrewers will avoid the MMC5. The PowerPak doesn't have an MMC5 implementation, few games used it, it hasn't been cloned by the community, it doesn't work on clone systems... The list of disadvantages is endless.
I don't know why Hamtaro126 brought mappers to this discussion, that just needlessly complicates things and doesn't have anything to do with the original question.
I don't know why Hamtaro126 brought mappers to this discussion, that just needlessly complicates things and doesn't have anything to do with the original question.
- Hamtaro126
- Posts: 786
- Joined: Thu Jan 19, 2006 5:08 pm
Sorry, Someone else thought of the term ''Takes a lot of RAM'' before me, so I'd thought it would help, but it did not.tokumaru wrote:Sane homebrewers will avoid the MMC5. The PowerPak doesn't have an MMC5 implementation, few games used it, it hasn't been cloned by the community, it doesn't work on clone systems... The list of disadvantages is endless.
I don't know why Hamtaro126 brought mappers to this discussion, that just needlessly complicates things and doesn't have anything to do with the original question.
AKA SmilyMZX/AtariHacker.