It is currently Sat Nov 18, 2017 8:55 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: CC65 pause pattern
PostPosted: Sat Sep 10, 2016 2:55 am 
Offline
User avatar

Joined: Sun Sep 26, 2010 10:29 pm
Posts: 35
Hello,
I am looking for a pattern to implement a pause functionality, using CC65. My current pattern works some kind of random. Some times it pauses the game, sometimes not. In a simplified model, I implemented my pause functionality as follows:

Code:
static unsigned char pause = 0;   /* 0 = no pause, 1 = pause */

void input_routine(void){
  ...
  if(input&PAD_START){
    pause = (pause == 1) ? 0 : 1;
  }
  ...
}

void main(void){
  ...
  while(1){
    input_routine();
   
     if(!pause){
      update_routine();
     }
   
    render_routine();
    ppu_wait_nmi();
  }
}


I tried out some combinations with the delay()-function, but nothing worked out too well. Can somebody explain, how to implement a pause functionality properly?

Regards
Sebastian


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Sat Sep 10, 2016 3:31 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2981
Location: Tampere, Finland
You have to check for "not pressed" => "pressed" transition, otherwise your pause state will keep toggling (0,1,0,1,0,...) while you hold down the button, ending on a more or less random value.

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Sat Sep 10, 2016 5:42 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1454
This is something that you should keep in mind for every button: Your controller update function should always save the input of the previous frame in a separate variable before setting the regular input variable. Because there are usually actions that require one button press only and that are not supposed to get repeated when the button is still pressed.

For example, when the character walks, then you just check for left or right.

But when the character jumps, then you check for "button A is pressed in the current frame, but wasn't pressed in the previous frame". Otherwise the character would keep jumping whenever he touches the ground again if the player keeps holding the button.
This might be desired in a fighting game, but games like "Super Mario Bros." require the player to release the button first and pressing it again before doing the next jump.

By the way, don't use the function "delay" in an NES game. Ever. Unless you want your game to lag. But even if a lag is desired (like in a slow motion scene) then I would simply count the frames and skip certain parts of the logic accordingly instead of using a function whose parameter is in the unit of milliseconds.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Sat Sep 10, 2016 6:49 am 
Offline

Joined: Fri Jul 22, 2016 9:58 am
Posts: 11
You may want to consider making a simple state machine, instead. Especially if you're going to have multiple screens (inventory, main menu, etc.), a state machine seems like it would make things simpler.

Consider a case where you have multiple pause screens (a la later zelda games, one screen for story progress, one screen for inventory). Having a state machine can drastically simplify your game logic.


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Sat Sep 10, 2016 7:52 am 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 356
I detect the player pushing START in my main loop. If such things happens, I enter the PAUSE subroutine, which:

1.- Waits until START is depressed.
2.- Plays the "PAUSE" sound effect.
3.- Dims the pal a bit.
4.- Pauses the music.
5.- Waits until START is pressed.
6.- Waits until START is depressed again.
7.- Restores the palette bright.
8.- Unpauses the music.

In some games I keep calling some update subroutines during step #5. For example, in Super Uwol I keep calling the routine which makes the coins flip.

_________________
http://www.mojontwins.com


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Sat Sep 10, 2016 3:06 pm 
Offline

Joined: Sun Mar 27, 2016 7:56 pm
Posts: 138
How I handle this is not by just storing the current and previous input, which would require comparing them each time you need to know what was pressed that frame, but by storing the current input and the input pressed this frame.

You could update them each frame with something like this:
Code:
pressed = current;
current = get_input();
pressed = (pressed ^ current) & current;
or how I actually have it in ASM:
Code:
; with new input in a
    tay
    eor current
    sty current
    and current
    sta pressed

Then, checking if Start was pressed this frame is as simple as if (pressed & PAD_START).


na_th_an wrote:
5.- Waits until START is pressed.
6.- Waits until START is depressed again.

I'm not sure I'd recommend that approach. It might be okay for pausing, but it's going to feel a little off for actual gameplay, since it introduces input lag for as long as they press the button.


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Mon Sep 12, 2016 5:40 am 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 356
It's for pausing the game, I haven't found any issues. For actual gameplay I use flags to control the buttons, which work great. I could use them in the Pause routine as well, of course, but I didn't find it necessary to over complicate things. I don't mind the lag when stopping or resuming the game.

Code:
if (pad & PAD_A) {
    if (!fire_button_flag) {
        fire ();
    }
    fire_button_flag = 1;
} else fire_button_flag = 0;


For pausing it could be...

Code:
if (pad_poll (0) & PAD_START) {
    if (!pause_flag) {
        music_pause ();
        palette_dim ();
        pause_flag = 1;
        while (1) {
            ppu_wait_nmi ();
            if (pad_poll (0) & PAD_START) {
                if (!pause_flag) break;
            } else pause_flag = 0;
        }
        palette_restore ();
        music_resume ();
    }
} else pause_flag = 0;

_________________
http://www.mojontwins.com


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Mon Sep 12, 2016 7:47 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1454
Correct me if I'm wrong, but if (pad & PAD_A) could make problems if you ever check for two buttons at once:

if (pad & (PAD_A | PAD_B))
--> Returns true, if only one of the buttons was pressed, even though you'd only want it to return true if both buttons are pressed.

The correct check should be if ((pad & (buttons)) == (buttons)) with buttons being the value that you want to check for (in your case PAD_A or in my case PAD_A | PAD_B).

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Mon Sep 12, 2016 12:04 pm 
Offline

Joined: Sun Mar 27, 2016 7:56 pm
Posts: 138
@na_th_an
Sure, but here's what that code would look like using my approach:
Code:
if (pressed & PAD_A) fire ();
Code:
if (pressed & PAD_START) {
    music_pause ();
    palette_dim ();
    while (1) {
        ppu_wait_nmi ();
        if (pressed & PAD_START) break;
    }
    palette_restore ();
    music_resume ();
}
That's why I feel like the flag approach is just overcomplicating things.

@DRW
That's correct, yes.


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Tue Sep 13, 2016 2:35 am 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 356
DRW wrote:
Correct me if I'm wrong, but if (pad & PAD_A) could make problems if you ever check for two buttons at once:

if (pad & (PAD_A | PAD_B))
--> Returns true, if only one of the buttons was pressed, even though you'd only want it to return true if both buttons are pressed.

The correct check should be if ((pad & (buttons)) == (buttons)) with buttons being the value that you want to check for (in your case PAD_A or in my case PAD_A | PAD_B).


Yeah, that's what I use when I need two or more inputs pressed at once. There's no need for the comparison if you are just checking one button. I don't know if cc65 notices and optimizes it.

Code:
if (pad & (PAD_A | PAD_B))


This is useful when you don't care which button has been pressed. For example, for one button games (like Sonic).

Code:
if (pad & PAD_A)

and
Code:
if ((pad & PAD_A) == PAD_A)


Are 100% equivalent (this is, when PAD_A is power of two, which it is), but I guess the latter generates worse code, unless cc65 is clever and optimizes it.

@Nicole, yes, you are right. Your approach is very clever. It will come handy when detecting the presses for jump or fire.

_________________
http://www.mojontwins.com


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Tue Sep 13, 2016 6:13 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1454
Sure, for one button, there's no difference. It always works. I just wanted to point out a little detail that might be problematic in a specific situation.

And no, CC65 doesn't optimize it.
if (variable & value)
creates smaller code than
if ((variable & value) == value)

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Mon Sep 19, 2016 2:46 am 
Offline
User avatar

Joined: Sun Sep 26, 2010 10:29 pm
Posts: 35
Sorry for the delay in responding to my post. I actually found some time to try out some of your hints, but nothing worked out well.
My most promising attempt looks like this:

Code:
static unsigned char pause = 0;   /* 0 = no pause, 1 = pause */
static unsigned char input = pad_poll(0);
static unsigned char lf_input = pad_state(0);

void input_routine(void){
  ...
  if(input&PAD_START){
    if(input != lf_input) pause = !pause;
  }
  ...
}


This doesn't pause my game in any case. The documentation of pad_state says "get previous pad state without polling ports",
so as far as I understand, it can be used to check, whether START was pressed, the frame before.


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Mon Sep 19, 2016 3:05 am 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 356
I could implement Nicole's solution using shiru's Neslib. It's as easy as this:

Code:
pad_this_frame = pad0;
pad0 = pad_poll (0);
pad_this_frame = (pad_this_frame ^ pad0) & pad0;


You can use the pad0 variable (i.e. "pad0 & PAD_START") to get info about what buttons are pressed. You can use the pad_this_frame variable (i.e. "pad_this_frame & PAD_START") to get info about what buttons have been just pressed _this frame_, and not before.

_________________
http://www.mojontwins.com


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Mon Sep 19, 2016 4:32 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2981
Location: Tampere, Finland
-Basti- wrote:
Code:
static unsigned char input = pad_poll(0);
static unsigned char lf_input = pad_state(0);

This calls pad_poll/pad_state once during the initialization of the program, not what you want. (I can't even remember if this is valid in C, or only C++.)

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
 Post subject: Re: CC65 pause pattern
PostPosted: Mon Sep 19, 2016 5:48 am 
Offline
Formerly ~J-@D!~
User avatar

Joined: Sun Mar 12, 2006 12:36 am
Posts: 445
Location: Rive nord de Montréal
thefox wrote:
-Basti- wrote:
Code:
static unsigned char input = pad_poll(0);
static unsigned char lf_input = pad_state(0);

This calls pad_poll/pad_state once during the initialization of the program, not what you want. (I can't even remember if this is valid in C, or only C++.)

Yeah, it's valid C++ but not C. The static initializer should be a constant expression because C doesn't support any kind of dynamic initialization for "globals".


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group