Getting started again!! Romhacker to NES Game Programmer
Moderator: Moderators
Re: Getting started again!! Romhacker to NES Game Programmer
Controller reading in the NMI and game logic in the main thread could cause your game logic to miss presses, especially if the NMI handler also calculates the "pressed or released since last frame" flag.
Re: Getting started again!! Romhacker to NES Game Programmer
Ah yeah. On that topic too, I think it's crazy that so many games have the sprite 0 status bar on the bottom of the screen (looking at you, Rare!), that trick is a lot fancier than most (non-dev) people would think. You can't even have a frame-time overflow in the case. I mean, you can without a complete disaster, but it'll look like Konami's first TMNT game - maybe the glitchiest status bar ever?tokumaru wrote:Don't forget about raster effects. If you have a sprite 0 status bar, effects based on IRQs, and stuff like that, you also need to reset them for the new frame, otherwise you'll get glitched frames every time there's slowdown.
Oh man, kill it with fire.. I'll always love NT2 but I don't make new stuff in it. I spent so much effort working around that to make it sound better without knowing why exactly, didn't realize at the time that because the NES volume control is linear, a linear fade sounds terrible. That's why the NES's hardware decay sounds so distinctive and weird, it's just.. wrong. A built-in logarithmic decay could be useful perhaps, I suppose everyone will just build their own envelopes to do it themselves, though. But, being able to scale that (like the FT2's volume control channel) is something I really like, I'm glad you're considering that for the future. I suppose it can be quickly done with a 4-bit x 4-bit look-up table?tepples wrote:As far as I know, of the current game-oriented homebrew NES sound drivers, only Pently supports NT2-style decaying envelopes.Memblers wrote:I've been doing a soundtrack for a game I'm making and I haven't even started considering sound effects yet, but I've had my old Nerdtracker 2 habits in mind.
I had it in my head that Pently only accepted MML-type input, but looking into it again I see that NovaSquirrel had made a Famitracker converter for it. With that, I am immediately intrigued. The little testing I did previously seemed to have Pently appearing to be CPU efficient to an impressive degree, that's definitely something I will be exploring!
Re: Getting started again!! Romhacker to NES Game Programmer
In some earlier discussions we figured that this is probably because it's much more difficult to set the scroll arbitrarily mid-screen than in VBlank. Few developers seemed to manage it back in the day (like Rare in Battletoads). But it is indeed a PITA to have the status bar at the bottom without mapper IRQs.Memblers wrote:Ah yeah. On that topic too, I think it's crazy that so many games have the sprite 0 status bar on the bottom of the screen (looking at you, Rare!), that trick is a lot fancier than most (non-dev) people would think. You can't even have a frame-time overflow in the case. I mean, you can without a complete disaster, but it'll look like Konami's first TMNT game - maybe the glitchiest status bar ever?
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Re: Getting started again!! Romhacker to NES Game Programmer
What's so difficult about it? My game is mapper 0 without any fancy stuff and yet I easily managed clean parallax scrolling.*thefox wrote:In some earlier discussions we figured that this is probably because it's much more difficult to set the scroll arbitrarily mid-screen than in VBlank.
Just make sure that there's one pixel line that consists of all the same color. Put the sprite 0 there. And then set the scrolling. The scrolling change will be done before rendering the line is over and since the line consists of pixels of the same color, the player won't notice any artifacts.
* In fact, I do three scrolling changes per frame:
First one in NMI that goes to scrolling position 0.
Then the status bar is drawn. While this is done, I do some game logic whose duration is always pretty constant.
Then I wait for the nine sprites per scanline flag because I positioned nine empty sprites below the status bar. There, I set the scrolling for the background.
Then the background is drawn. In the meantime, I do the majority of the game logic.
Then sprite 0 split.
Then the foreground is drawn and I use the remaining time for some other constant-time logic.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Getting started again!! Romhacker to NES Game Programmer
Setting the X scroll is trivial (they managed that even in SMB1), but setting the Y scroll with pixel precision is rather involved. See http://wiki.nesdev.com/w/index.php/PPU_ ... g#ExamplesDRW wrote:What's so difficult about it? My game is mapper 0 without any fancy stuff and yet I easily managed clean parallax scrolling.thefox wrote:In some earlier discussions we figured that this is probably because it's much more difficult to set the scroll arbitrarily mid-screen than in VBlank.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Re: Getting started again!! Romhacker to NES Game Programmer
Oh, you're talking about vertical scrolling. Sorry, I thought it was just about status bar on top vs. status bar at the botton.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Getting started again!! Romhacker to NES Game Programmer
It sounds like I need to move the controller code out of the NMI then if it's going to be buggy.tepples wrote:Controller reading in the NMI and game logic in the main thread could cause your game logic to miss presses, especially if the NMI handler also calculates the "pressed or released since last frame" flag.
Now I'm back to the beginning. Should I make a loop in the reset to run the controller and logic code?
For example.
Code: Select all
LDA #%10010000 ; enable NMI, background $1000 VRAM, sprites $0000 VRAM
STA $2000 ; sprite size 8x8, PPU increment 1, name table $2000 VRAM
LDA #%0011000 ; background visible, sprites visible, background & sprites clipped
STA $2001 ; color display
startloop:
JMP startloop
Where I have the JMP startloop is where I should put logic code instead? One time I tried putting code there and it was running way faster than it should be. I also ran the sound driver init here once and the NTSC music driver I was using sped up big time, sounded like PAL. So then I tried keeping code out of there because at the time I didn't have the skill to work out some type of timing or I just had the wrong idea. This has always been a problem for me.
Re: Getting started again!! Romhacker to NES Game Programmer
Wait, no, tepples is right..IF your game logic goes longer than 1 frame, the logic will miss a change in button presses event. (Events like, Title Screen, exit if Start wasn't pressed last frame && Start is pressed this frame)
EDIT...
Have a flag for when the game logic is done, and only check buttons if it is set, or something like that.
nesdoug.com -- blog/tutorial on programming for the NES
Re: Getting started again!! Romhacker to NES Game Programmer
I'm writing you a shortened version of my own code. This works without problems and the NMI only does stuff that needs to be done during vblank.Gil-Galad wrote:Now I'm back to the beginning. Should I make a loop in the reset to run the controller and logic code?
Code: Select all
Reset:
; All the initialization stuff.
; ...
@gameLogic:
LDA WaitForNmi
BNE @end
JSR ControllerInput
JSR GameLogic
LDA #true
STA WaitForNmi
@end:
JMP @gameLogic
Nmi:
PHA
TXA
PHA
TYA
PHA
LDA WaitForNmi
BEQ @end
@nmiStart:
LDA #false
STA WaitForNmi
LDA PpuMaskValue
STA PpuMask
BEQ @end
LDA #<Sprites
STA OamAddr
LDA #>Sprites
STA OamDma
JSR UpdatePpu
JSR SetScrolling
@end:
JSR SoundUpdate
PLA
TAY
PLA
TAX
PLA
RTI
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Getting started again!! Romhacker to NES Game Programmer
Why is this a bad thing? Of course, if your game lags, the controller won't be updated until the logic is done. But that's what it's supposed to do.dougeff wrote:IF your game logic goes longer than 1 frame, the logic will miss a change in button presses event.
If I read the controller in every NMI interrupt call, even if my game logic isn't finished yet, then this means that half of the logic will do status updates based on another input than the other half:
- Read Controller: Player presses right.
- Start move function: Update vertical movement based on player pressing right.
- Continue move function: Update weapon attack based on player pressing right.
- Oops! Lag. NMI interrupts the program and reads the controller again: Player presses left.
- Continue move function: Update horizontal movement based on player pressing left.
--> Inconsistency! The value changed in the middle of a function that was never meant to handle a change of the value.
You want that?
This one is better in my opinion:
- Read Controller: Player presses right.
- Start move function: Update vertical movement based on player pressing right.
- Continue move function: Update weapon attack based on player pressing right.
- Oops! Lag. NMI interrupts the program, but doesn't read the controller again: Player presses left, but the value is still set to right.
- Continue move function: Update horizontal movement based on player pressing right.
--> No inconsistency at all. The game logic will recognize the new button press at the start of the next frame when the game logic starts again.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Getting started again!! Romhacker to NES Game Programmer
You're misunderstanding. It's not about the a change in button state during lag not being read. That is indeed expected and normal. It's about a change in button state during lag being read, and then having an undesired effect.Why is this a bad thing? Of course, if your game lags, the controller won't be updated until the logic is done. But that's what it's supposed to do.
NMI happens at ~60 FPS while it's enabled regardless of anything. I am reading my controller in the NMI, and so by extension it's happening at ~60FPS regardless of anything.
My game loop takes too long.
Code: Select all
GAMELOOP STARTS
NMI OCCURS: I have pressed A.
GAMELOOP CONTINUES
NMI OCCURS: I am still holding A. My "press" of A has become a hold.
GAMELOOP CHECKS FOR A PRESS OF A TO JUMP. But I am now holding A because the NMI hit twice during the game loop.
The jump fails.
Yes, you can read the joypad but not update whatever controls presses vs holds in your game in the NMI, and yes, you can conditionally not read/update the joypad in the NMI if the game loop has taken too long. But you wouldn't need those extra conditions at all if you just read the joypad not in your NMI.
Re: Getting started again!! Romhacker to NES Game Programmer
That's why I think you should never read the controller during NMI, but at the start of the game logic:Kasumi wrote:This is the case people are saying he should look out for when reading from the NMI.Code: Select all
GAMELOOP STARTS NMI OCCURS: I have pressed A. GAMELOOP CONTINUES NMI OCCURS: I am still holding A. My "press" of A has become a hold. GAMELOOP CHECKS FOR A PRESS OF A TO JUMP. But I am now holding A because the NMI hit twice during the game loop. The jump fails.
Code: Select all
CONTROLLER READING: I have pressed A.
GAMELOOP STARTS
NMI OCCURS: I am still holding A. But it isn't read. So, it doesn't become a hold.
GAMELOOP CONTINUES
NMI OCCURS: I am still holding A. But it isn't read. So, it doesn't become a hold.
GAMELOOP CHECKS FOR A PRESS OF A TO JUMP. Everything is fine. It's not a hold.
Exactly. So, why is reading the controller in the NMI an issue at all?Kasumi wrote:But you wouldn't need those extra conditions at all if you just read the joypad not in your NMI.
Even if I absolutely have to read the controller in NMI for some reason, it would still be trivial to avoid this problem: You don't need to implement any special conditions.
Since you need to have a condition to leave the NMI early anyway:
Code: Select all
Nmi:
PHA
TXA
PHA
TYA
PHA
LDA WaitForNmi
BEQ @end
@nmiStart:
LDA #false
STA WaitForNmi
LDA PpuMaskValue
STA PpuMask
BEQ @end
LDA #<Sprites
STA OamAddr
LDA #>Sprites
STA OamDma
JSR UpdatePpu
JSR SetScrolling
@end:
JSR SoundUpdate
PLA
TAY
PLA
TAX
PLA
RTI
Code: Select all
Nmi:
PHA
TXA
PHA
TYA
PHA
LDA WaitForNmi
BEQ @end
@nmiStart:
LDA #false
STA WaitForNmi
LDA PpuMaskValue
STA PpuMask
BEQ @end
JSR ReadController ; --> New code.
LDA #<Sprites
STA OamAddr
LDA #>Sprites
STA OamDma
JSR UpdatePpu
JSR SetScrolling
@end:
JSR SoundUpdate
PLA
TAY
PLA
TAX
PLA
RTI
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Getting started again!! Romhacker to NES Game Programmer
I'm not sure we're reading the same things. That's what everyone is saying!That's why I think you should never read the controller during NMI, but at the start of the game logic:
For that reason I said? Also note that I said this too:Exactly. So, why is reading the controller in the NMI an issue at all?
It's like... so we're warning him about a thing that can happen. And you're posting ways to fix that thing that can happen to say it's not a problem. And yeah, it's not a problem if you know about it. But... we warned him because he may not know about it, but also because there are better ways to do it. See the first thing I quoted from you in this post. Does that make sense?Yes, you can read the joypad but not update whatever controls presses vs holds in your game in the NMI, and yes, you can conditionally not read/update the joypad in the NMI if the game loop has taken too long. But you wouldn't need those extra conditions at all if you just read the joypad not in your NMI.
Maybe you don't need an extra condition, but it depends on your setup. And again: It's more about, "Hey, know about this thing." It's not, "Never do this thing, because there's no way around it." It's, "Here's a better way so you don't have to even think about it."
Last edited by Kasumi on Sat Aug 27, 2016 9:21 am, edited 1 time in total.
Re: Getting started again!! Romhacker to NES Game Programmer
DRW's example would work fine. That's basically what I do....
The first example that starts off like
The first example that starts off like
I'm writing you a shortened...
nesdoug.com -- blog/tutorial on programming for the NES
Re: Getting started again!! Romhacker to NES Game Programmer
Has anybody done measurements on what is a typical minimum "pulse width" on button presses (or releases) that one can expect? That is, if a player quickly taps the button. Or, holds it down, quickly releases it and then holds it down again.
I guess I might throw together a small test ROM for it.
I guess I might throw together a small test ROM for it.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi