messing up the functions from inherited classes from two different base classes

Started by
13 comments, last by Heelp 8 years, 2 months ago

Ok, I'm making Street Fighter. Some foos from gamedev have written a tutorial about Game States.I used it to make my menu. I made 1 base class GameState and 3-4 inherited classes( class Intro, class Menu, class Stage1 ) and I made another base class Characters and only one inherited class, for now, which is class Ryu. Now I somehow need to say to the game: show Ryu and all his stuff (like movement, handle_keys) only when the Stage is active, I don't want my Ryu to walk in the Intro stepping onto the Street Fighter logo.... I tried to make it work in the main function, ugly stuff.. If someone knows other ways, please tell, and here is my code. My code is split between multiple source files, so I posted only the two classes and I need you to give me some pseudocode so I can know what to do.

Basically, I think I want to call the function logic() from the Ryu class into the logic() function in the Stage1 class and the render() from Ruy into the render() in Stage1.

But I need to initialize an object so I can call the Ryu functions, but I don't want any more globals.....


 
//The headers
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include <string>
#include "constants.h"
#include "game_state.h"
#include "stage13.h"
#include "timer.h"
#include "functions.h"
#include "globals.h"
 
Stage13::Stage13()
{
    //Load the backgrounds
    background[ 0 ] = load_image( "sprites/Stages/Stage13_part1.png" );
    background[ 1 ] = load_image( "sprites/Stages/Stage13_part2.png" );
 
 
}
 
Stage13::~Stage13()
{
    //Free the background surfaces
    for( int i = 0; i < 2; i ++ )
    {
        SDL_FreeSurface( background[ i ] );
    }
}
 
void Stage13::handle_events()
{
 
    //If the user has Xed out the window
    if( event.type == SDL_QUIT )
    {
        //Quit the program
        set_next_state( STATE_EXIT );
    }
    if( event.type == SDL_KEYDOWN )
    {
        //If the user pressed enter
        if( event.key.keysym.sym == SDLK_ESCAPE )
        {
            //Move to the title screen
            set_next_state( STATE_MENU );
        }
    }
}
 
void Stage13::logic()
{
    ;
}
 
void Stage13::render()
{
    //Show the background
    for( int i = 0; i < 2; i ++ )
    {
        apply_surface( 0, 0, background[ i ], screen );
    }
}
 
 

 
//The headers
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include <string>
#include "constants.h"
#include "timer.h"
#include "character.h"
#include "ryu.h"
#include "functions.h"
#include "globals.h"
 
Ryu::Ryu()
{
    //Load the sprites
    ryu[ 0 ] = load_image( "sprites/Characters/Ryu/Walking/ryu_walking1.png" );
    ryu[ 1 ] = load_image( "sprites/Characters/Ryu/Walking/ryu_walking2.png" );
    ryu[ 2 ] = load_image( "sprites/Characters/Ryu/Walking/ryu_walking3.png" );
    ryu[ 3 ] = load_image( "sprites/Characters/Ryu/Walking/ryu_walking4.png" );
    ryu[ 4 ] = load_image( "sprites/Characters/Ryu/Walking/ryu_walking5.png" );
 
    //Initialize the velocity
    Xvelocity = 0;
    Yvelocity = 0;
 
    walkstatus = FOO_RIGHT;
    frame = 0;
}
 
Ryu::~Ryu()
{
    //Free the Ryu surfaces
    for( int i = 0; i <=4; i ++ )
    {
        SDL_FreeSurface( ryu[ i ] );
    }
 
}
 
void Ryu::handle_events()
{
 
    //If a key was pressed
    if( event.type == SDL_KEYDOWN )
    {
        //Adjust the velocity
        if( event.key.keysym.sym == SDLK_UP )
        {
            Yvelocity -= RYU_SPEED;
        }
        if( event.key.keysym.sym == SDLK_DOWN )
        {
            Yvelocity += RYU_SPEED;
        }
        if( event.key.keysym.sym == SDLK_LEFT )
        {
            Xvelocity -= RYU_SPEED;
        }
        if( event.key.keysym.sym == SDLK_RIGHT )
        {
            Xvelocity += RYU_SPEED;
        }
    }
    //If a key was released
    else if( event.type == SDL_KEYUP )
    {
        //Adjust the velocity
        if( event.key.keysym.sym == SDLK_UP )
        {
            Yvelocity += RYU_SPEED;
        }
        if( event.key.keysym.sym == SDLK_DOWN )
        {
            Yvelocity -= RYU_SPEED;
        }
        if( event.key.keysym.sym == SDLK_LEFT )
        {
            Xvelocity += RYU_SPEED;
        }
        if( event.key.keysym.sym == SDLK_RIGHT )
        {
            Xvelocity -= RYU_SPEED;
        }
    }
}
 
void Ryu::movement()
{
    //Move the Ryu left or right
    ryubox.x += Xvelocity;
    ryubox.y += Yvelocity;
 
    //If the Ryu went too far to the left or right
    if( ( ryubox.x < 0 ) || ( ryubox.x + RYU_WIDTH > SCREEN_WIDTH ) )
    {
        //move back
        ryubox.x -= Xvelocity;
    }
 
    //Move the Ryu up or down
    //y += Yvelocity;
 
    //If the Ryu went too far up or down
    if( ( ryubox.y < 0 ) || ( ryubox.y + RYU_HEIGHT > SCREEN_HEIGHT ) )
    {
        //move back
        ryubox.y -= Yvelocity;
    }
}
 
void Ryu::walk_anim()
{
    if( Xvelocity < 0 )
    {
        //Set the animation to left
        walkstatus = FOO_LEFT;
 
        //Move to the next frame in the animation
        frame++;
    }
 
    //If Ryu is moving forward
    else if( Xvelocity > 0 )
    {
        //Set the animation to forward
        walkstatus = FOO_RIGHT;
 
        //Move to the next frame
        frame++;
    }
 
    //If Ryu is standing
    else
    {
        //Set the animation to standing
        walkstatus = FOO_STILL;
    }
 
    //Loop the animation
    if( frame >= 20 )
    {
        frame = 0;
    }
}
 
void Ryu::logic()
{
    movement();
    walk_anim();
}
 
void Ryu::render()
{
    //Show Ryu
    if( walkstatus == FOO_STILL )
    {
        apply_surface( ryubox.x, ryubox.y, ryu[ frame/5 ], screen );
    }
 
    else if( walkstatus == FOO_RIGHT )
    {
        apply_surface( ryubox.x, ryubox.y, ryu[ frame/5 ], screen );
    }
 
    else if( walkstatus == FOO_LEFT )
    {
        apply_surface( ryubox.x, ryubox.y, ryu[ frame/5 ], screen );
    }
}
 

Advertisement
Instead of having a game state for each stage you could have a game/play state that contains Ryu and the current stage (which is no longer a game state). Then you can easily switch stage without loosing your Ryu object, and he will not appear in the other game states.

Seems to me that you are not dealing with your instances correctly.

"But I need to initialize an object so I can call the Ryu functions, but I don't want any more globals.."

OF COURSE you need to initialize an object, this is how OOP works.

Firstly, don't move any logic concerning Ryu to your Stage class.

Ryu class should deal with your character logic, If you think movement should not be in ryu, create a new class that concerns with the movement logic.

Secondly, Create a "Level" class (I guess this is "stage"). This level class should contain your "Ryu" class.

However, you should introduce another abstract level called "character" to maintain extensibility with other characters that might be.

Then you can register an instance to the level class (For example using a vector of chartacters, you iterate other them and call the Draw method).

This way you have an extensible class with a nicer design.

----------------------

You really should put more focus on learning how to design code properly.

Beginning with a basic pattern, the strategy pattern.

1st question goes to Wooh: Are you suggesting that I should make a third base class and inherit all the levels (without the menu and intro ) from it? Because I already have a base class for the different screens( like intro, menu, character select, and the stages where you fight ). I really don't think it's a good idea to make another base class. I think i should somehow find a way to call the ryu logic() function into the Stage logic() function. I already have GameStates that should deal with everything, 3rd base class is ugly, in my opinion.
2nd question goes to WoopsASword: Ryu's logic is in the ryu class in the function logic(). And I've already made a base class Character. And what do you mean by 'register and instance to the level class'?

1st question goes to Wooh: Are you suggesting that I should make a third base class and inherit all the levels (without the menu and intro ) from it? Because I already have a base class for the different screens( like intro, menu, character select, and the stages where you fight ). I really don't think it's a good idea to make another base class. I think i should somehow find a way to call the ryu logic() function into the Stage logic() function. I already have GameStates that should deal with everything, 3rd base class is ugly, in my opinion.
2nd question goes to WoopsASword: Ryu's logic is in the ryu class in the function logic(). And I've already made a base class Character. And what do you mean by 'register and instance to the level class'?

Sounds like you need to understand the difference between a class (a description of data and the rules for operating on those data) and an object (a realized instance of a class). An object of your Stage class needs to be able to ask an object of your Character class to do its logic(). Inheritance is not the right tool for this job.

Stephen M. Webb
Professional Free Software Developer

I just wanted from you to tell me how to somehow load the Chars logic into the stage logic, nobody wants to give me a straight answer, I guess I need to figure it out on my own dry.png

I DID IT. I just made 2 global variables: GameState *currentState and Character *currentChar. I went to the main() function and wrote this: currentState = new Intro(); and currentChar = new Ryu(); and I made a change_state function that changes screens between classes Intro, Menu and Stage1. So when I am in Intro, when I press enter I go to Menu and if I press enter again, I go to Stage1, and the opposite works with Esc.
and I went to the Stage1::logic() and there I put currentChar->logic(). and then I went to the Stage1::handle_events and I put the currentChar->handle_events() and I did the same with the render() function. I was just confused because my professor told me not to use global variables, and I thought that I can do without them, but maybe he was telling this, because I was just starting with programming last year, so everything is fine now. Apparently WoopsASword is right - you can't do OOP without some globals haha. Lost 2 days for this simple thing

nobody wants to give me a straight answer

Don't ask open ended questions if you want narrow answers. Actually, come to think of it.. you didn't ask a question. Not until post #4 at least.

You are having trouble with a growing number of globals because of how you are trying to design this game. If you take a look at your global variables and think of your global space which defines them as "just another object" you might be able to have some ideas click. A symptom of bad design, or a design process gone horribly wrong is when global variables feel bloated or forced, as if there was no other way to accomplish the task. My suggestion is that you start from scratch and grab some paper when you decide to move onto version 2.0.

Apparently WoopsASword is right - you can't do OOP without some globals

I must have missed that part.

I have made games that have zero global variables, so this is entirely invalid on the basis that you can in fact OOP without ANY globals.

Apparently WoopsASword is right - you can't do OOP without some globals

I do not see where he said that.

Try passing your currentChar to your Stage1::logic() as a parameter instead. Something like the following.


 
int main()
{
    Ryu currentChar;
    Intro introState;
    GameState* currentState = &introState;
 
    currentState->logic(&currentChar);
    currentState->handle_events(&currentChar);
    currentState->render(&currentChar);
}

Stephen M. Webb
Professional Free Software Developer

Yes, Bregma, I tried your way and it works. But I prefer globals because the code seems clearer when the functions take no arguments. And, yeah, WoopsASword didn't say that, but maybe I understood it that way, I don't really remember writing this. laugh.png It's people like me who start World Wars.

This topic is closed to new replies.

Advertisement