When to switch to hero_idle animation?

Started by
14 comments, last by Alberth 8 years, 1 month ago

Guys, sorry for the reaaally slow answers, I had 29308742 courseworks this month.... Java, Assembly,, ADT, Matlab, Arduino and maybe a few more I don't even remember. I haven't touched my game for two weeks. Now about the code - I read somewhere that it's better to divide the code to 4128239 header and source files, and I decided to give it a try, regretted that decision immediately. Now most of my mistakes are: forgot to add #include, forgot to declare function in header, forgot to add file to project.. and so on. Ok I attached the two files as .txt, see them and if you are too lazy, just give me pseudocode, nothing more.Basically I want to somehow handle all animations and make the code readable somehow ( including idle_anim ). Also see that frames_something variables, I have frame variable for every animation and I'm copy/pasting it cuz I cant find something better.
And any feedback concerning code modularity and simplicity is appreciated. Thanks for reading.

Advertisement

Should I attach the whole project? ( I can attach the SDL 1.2 libs if you want, too )

EDIT: here is the project and libs and exe.

You can achieve that with sometihg like this, i hope it helps:

in the input function:


void input()
{
  //KEY PRESSED
  if(KEYDOWN_LEFT)
  {
     player.setWalkingLeft(true);
  }
  if(KEYDOWN_RIGHT)
  {
     player.setWalkingRight(true); 
  }
  if(KEYDOWN_DOWN)
  { 
    player.setCrouch(true); // I dont know, use your imagination :D
  }
  //KEY RELEASED
  if(KEYUP_LEFT)
  {
     player.setWalkingLeft(false);
  }
  if(KEYUP_RIGHT)
  {
     player.setWalingRight(false);
  }
  if(KEYUP_DOWN)
  {
     player.setCrouch(false);
  }
}
   

The player could have some boolean variables indicating what is doing. And then in you can make this:


void Update()
{
 if(player.getWalkingRight() == true)
 {
    player.WalkingRightAnim();
 }
 else if(player.getWalkingLeft() == true)
 {
   player.WalkingLeftAnim();
 }
 else if(player.getCrouch() == true)
 { 
   player.CrouchedAnimation();
 }
 else
 {
   player.Idle_animation();
 }
}

I hope this pseudocode helps you get the idea. You need to specify which action is the player doing. If none of those is being made, you play de Idle animation

While reading the project code I noticed a few things that you could change. I failed to understand the logic of the various ryu animations, but let's discuss that further down this post.

Ok, the first thing I noticed was some global variable magic. A fragment:


void Ryu::handle_walking()
{
    //If a key was pressed
    if( event.type == SDL_KEYDOWN )

Where does this "event" come from? Ryu is just one of the game characters, and SDL is the game-wide library, but it has no "event" variable of itself. A small search revealed it's a global and you set it here:


        while( SDL_PollEvent( &event ))
        {
            currentState->handle_events();

I think if you change the "handle_events" to "handle_events(const SDL_Event &event)", and pass in the event returned by the pollEvent, you can remove the global event variable.

In the other direction, you seem to change state:


void Stage18::handle_events()
{
    //If the user has Xed out the window
    if( event.type == SDL_QUIT )
    {
        //Quit the program
        set_next_game_state( STATE_EXIT );

If you give this new state as return value, I think you can drop the nextState variable, since a few lines below the poll loop you do "change_game_state();" that needs the new state.

Similar considerations hold for other global variables, like "screen". "background" doesn't look used?

I have more to say about Stage18::handle_events below.

Before going there, I'd like to discuss switching game states.


void change_game_state()
{
    //If the state needs to be changed
    if( nextState != STATE_NULL )
    {
        //Delete the current state
        if( nextState != STATE_EXIT )
        {
            delete currentState;
        }
    }

    //Change the state
    if( nextState == STATE_INTRO )
    {
        currentState = new Intro();
    }

    if( nextState == STATE_MENU )
    {
        currentState = new Menu();
    }

    .....

The logic of trying "if( nextState == STATE_INTRO ) ..." when "nextState == STATE_NULL" is beyond me, but what I wanted to talk about is the bigger picture.

You have several game states that you switch between. I think that is a good idea. Maybe there are better solutions, but I don't know any currently.

The question I have here is why do you repeatedly throw the "old" state away (and with it all loaded images and so on), and "new" a new state? Chances are soon you will leave this state as well, and go back to one you just deleted. The C++ code that you have looks fine, you nicely free all resources every time, but is that really needed?

Why not do something like


Intro intro;  // Instantiate all game states
Menu menu;
...

// elsewhere
// Switch state by just setting the pointer to the right state.
switch (nextState) {
    case STATE_NULL:
        break; // Do nothing

    case STATE_INTRO:
        currentState = &intro;
        currentState->Reset(); // Let the new state know it was chosen so it can reset itself (if required).
        break;
    ...
}

In your current setup, a state that you enter is "new"-ed, so it automatically initializes it local variables. In my proposal, you need to explicitly tell the state that it is chosen again so it can reset itself (unless none of your states has local variables of its own of course).

This will of course load all game states and all art that they carry, so it will take more memory.

Back to the "handle_events". You seem to handle all keys in the game state. That in itself is a good idea, since you know what to do with a key like "up". On the other hand, the "exit" case appears everywhere, and you just pass a result back to the main program since you cannot do anything with it.

An alternative is to stack key handlers. Instead of one place handling all, have several places, one after the other. First the "main" code gets a turn, if it cannot handle the key, the game state tries. If the game state doesn't know, the character gets a turn, and so on (although I may have listed all levels already :) ).

This ties the key handling code of a key to the point where you can do something useful with the information, less need to pass information up or down.

With the character, I am quite lost in what you're doing.


void Ryu::handle_events()
{
    handle_walking();
    handle_l_punch();
    handle_l_kick();
}

This suggests Ryu can do walking, lpunch, and lkick all at the same time? (they all get a change to handle keys or change animations).

I would expect there is one place where you select what you do, and you only do one of the above rather than all.

Maybe you are doing that, the entire movement logic is difficult to follow, you have so many functions. Also, this post is already too long. I think this would be enough for now.

1st - the global variable magic biggrin.png
When I have only 1 variable of a given type, I make it global so I don't have to pass it as an argument everywhere. For example I have only 1 window and only 1 event, so this means that I make them global. Now I don't do this because basically everyone says that it's bad practice so now I almost unlearned it, but I wrote that code months ago.

2nd - about dropping the nextState variable.
Normally I need only 1 function and that is change_game_state(), but in 1 tutorial some guy said that in order my code to be more modular, I need to first set it and only change it only after all the logic() and before the render() cuz I shouldn't be "changing game objects all over the place"... so I have set() function and change() function...forget this, I got used to it and don't give it much thought now. biggrin.png

3rd - unused global variables:
I do this a lot, maybe because I know the game is only 2-4 MB and I subconsiously become sloppier ,who knows biggrin.png

4th - about the STATE_NULL.
Basically, the program loops through while() once every frame. So every frame it sets the nextState to STATE_NULL. And if I want to change the level, I pass a new state to set_next_game_state(newState) and the actual change happens in change_game_state(). And then immediately STATE_NULL is assigned. Because if I don't do this, It will load the level every frame. Forget this too, it's not important. Normally I change my code every month when I see something new biggrin.png

5th - about repeatedly throwing the old state again. I just saw this used in a tutorial, I haven't thought about that. It's good cuz its saving RAM, but it uses more processing power, because you need to load everything a lots of times. But the game is small so I don't care. But in large games I don't really know which is better, load everything once and forever, or kill it if you don't use it. I never even wondered about this...

6th - about the ryu handle_events() function. Normally, I need to do all the stuff in 1 function. 1 function for handle_events(), 1 for logic() and 1 for render(). Because if I have everything on a different place, I can't follow through. and in these functions, there are IF statements all over the place, so it actually says: if "u" is pressed, set_next_anim() to light punch, and if "j" is pressed, use handle_l_kick() function and set_next_anim() to low kick, and then change the animation after the logic in change_animation().
And if you run the exe file, and spam the light kick and light punch, you see that there is no delay, you can interupt the animations whenever you want, that I can't fix. So basically all my problems are in ryu.cpp and ryu.h files. I fixed some of the errors that you were getting and I attached the project again and I hope you can compile it. If you can't, tell me where the errors are.

And if you run the exe file, and spam the light kick and light punch, you see that there is no delay, you can interupt the animations whenever you want, that I can't fix.
Your pre-condition fails already, windows .exe file don't work at Linux systems :p

I deduced your findings from reading the source code though. You run the "handle_events" from all moves, and each of them sets a new animation without regard to what is currently being shown. Iirc you don't reset the animation frame counter (although maybe you do that somewhere hidden?), in which case the next animation would start at the frame where the previous animation was.

That is why I asked if ryu could do all 3 actions at the same time, but my reason was not clear enough to you, as you gave a different answer :)

With your new post it is now also more clear to me what your problem actually is. You want to have better control when you can switch an animation.

Unfortunately, the solution is not so simple. I wrote an article about it I realize now, but perhaps that's a bit too generic for you to just throw the link in :)

As a small introduction towards the solution, you're dealing not with 1 but with 2 different (but tightly connected) problems. The first problem is most obvious, and it is what you already said to be the problem. You don't want to switch animations at any point in time, you want to control when it may happen (and in particular, when it may NOT happen).

The solution to this problem is a state machine. Just like the player can see only one GameState at a time, your character can only show one animation at a time. After starting, the animation is in full control. It progresses through its frames, and decides when the player input should be honoured (and give control to another animation, or restart itself (eg the 2nd punch immediately after the 1st one finishes). In general, action sequences should probably played til the end, but eg a walk cycle could be aborted at some point, and "idle" would always accept player input to a different animation.

This should work, but leads to the same code duplication problem you have in the key handlers of the game states. Every game state handles all keys. To solve that, you can see the key-input as a separate problem from the animation display. The former processes keys pressed by the player, and "posts" suggestions like "player likes to punch". The animation plays its animation, and decides whether the suggestion is allowed by its rules. If it is not, the suggestion is ignored. If it is, the animation is switched to the suggested one.

The third problem that is ignored here so far, is the capabilities of the player. For example, to kick, you need special shoes, which you may not have won yet. This may not be relevant in your game though, but I hope you can see the general case can be quite complicated.

To make an already long story a bit shorter, http://www.gamedev.net/page/resources/_/technical/game-programming/from-user-input-to-animations-using-state-machines-r4155 is what I wrote earlier about this subject. I hope it makes sense. The general solution may be a bit too much, but hopefully it acts as a source of inspiration for you.

This topic is closed to new replies.

Advertisement