Sign in to follow this  

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

This topic is 680 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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 );
    }
}
 

Edited by Heelp

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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. 

Edited by WoopsASword

Share this post


Link to post
Share on other sites

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'?

Edited by Heelp

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Edited by Heelp

Share this post


Link to post
Share on other sites
 

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.

Share this post


Link to post
Share on other sites

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);
}

Share this post


Link to post
Share on other sites

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.

Edited by Heelp

Share this post


Link to post
Share on other sites


But I prefer globals because the code seems clearer when the functions take no arguments.

This is something you should unlearn as quickly as possible.

 

While I can see that feeling cleaner, consider the fact that anything, anywhere, at any time, can access and modify the variables.

As the project you're working on starts to grow, this will become a problem. Suddenly, the variable isn't what you expect it to be, and finding out why is extremely difficulty and time-consuming.

 

On the other hand, if you pass the variables you need into a function, you can follow it a lot easier. You won't pass lots of parameters that aren't used (because it's tedious and boring to pass everything all the time), so you'll end up with functions of code, for the most part, only take the parameters they actually need.

 

Globals are bad.

There are some exceptions (e.g. globals that are read-only aren't as bad), but for the most part you should stay away from them unless you have very very good reasons not to.

Share this post


Link to post
Share on other sites
I usually avoid offering advice when discussing globals, because it's much more effective for the person to discover just how bad it can get in their own codebase. It makes a bigger impression to see the problem first-hand than to trust people's warnings that you don't fully understand yet.

Share this post


Link to post
Share on other sites

Nypyren, normally, you are right, but it depends on the person. Me, I don't like reinventing the wheel, you guys seem like you suffered a lot because of globals + my lecturer also told me that globals are bad, so I prefer to listen, and I removed them. Some things I prefer not to fully understand laugh.png  

Share this post


Link to post
Share on other sites

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

 

This is not a good solution. I'm sorry but you have to roll it back. (If you dont work with git yet, start to). 

 

I've never stated you need a global, you do need an instance but definetly not a global.

 

I'll explain how your code should look like (This will be long then read carefully when you are not tired):
- Main entry point begins:

Usually you initialize your first instances.

for example the game class,

load the first level or the menu class.

 

-Then the rest of the game, usualyl a game has a game loop which executes Draw and Update around 60 times per second.

The real magic happens when you change the state of the game, for example a user chose to play a map.

Only then you should create your instance for that map and pass it to the relevant class which will hold the map and draw/update it.

 

To explain better what you should do and how to create/manage instance I'll show you some work of mine:

I've created a class called "Window" which inherits from "IWindow".

The window has some render which does the actual render and I register a render by calling SetRender.

This how it looks: 

class IWindow
{
protected:
	/* ctors + dtor*/
public:
	// Sets the current render to the choosen render
	void SetRenderer(IRender* render);

	/* more fucntions here*/
};

then I create a new Window in the main class and use this method to set the actual render:

	window->SetRenderer(new ShaderRender(mesh, window));

* Mesh is another object of the type Mesh and window is my window... ShaderRender inherits from IRender. 

 

This way, I know when I initialized my instance + it is not a global. 

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

More C++ related topic, Use the std::shard_point and std::unique_pointer instead of raw pointers.

Raw pointers are good for managing pointers which are not the source of your memory but just a reference to it. 

Share this post


Link to post
Share on other sites

Yeah, I got it. Just make the functions take arguments instead of using globals, less worries. Thanks to all for the help, really. I'm really productive when I don't spend a whole week figuring out something stupid on my own. Thanks again :)) wub.png

Share this post


Link to post
Share on other sites

This topic is 680 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this