Sign in to follow this  

Attempting a proper 2D game engine, always find myself out-of-scope

This topic is 413 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

Hi again,
 
Over the past few months I've tried re-writing my program, several times in fact. Each time I run into a similar problem with scopes and modules that aren't able to interact. In my latest attempt, I made player into a class. I'm also using a graphics module to keep the ugly parts out of my main file. The problem is accessing the graphics class located in graphics.h/graphics.cpp - When I render my objects I'm in a for loop which seems to only access Tile but not Graphics
 
Example:
 
 

for (Tile& it : tileList) {


it.square.x = it.x;


it.square.y = it.y;


g.draw(g.wallgfx, g.Backbuffer, it.square); // square is an SDL Rect object for drawing on


};



g.draw(g.wallgfx, g.Backbuffer, &singleObjectName.square);


 
Of course it's not going to work. When I'm in the for loop I can't call the "g for graphics" object. The stand-alone line at the bottom works fine but I'm not going to make a specific object with its own name, for every detail in the game world! I could move the draw() function into tile.h or thing.h which Tile is derived from, but then (staying strictly with the engine concept) I have to call the backbuffer from graphics.h which is included in main.cpp but not available in tile.h
 
I know that I cannot #include graphics.h into both main.cpp and tile.h - I tried that a month or two ago!
 
This is disappointing because I spent a lot of time reading text books and tutorials, plus I asked many questions here. I'm 26 and I still don't understand how to 'modularise' a program in c++. I read Jazon Yamamoto's book but I haven't been able to replicate any of his complex code for my own use - his example code is a properly-written engine but it's too abstract for me to grasp. I could go back to including everything into my main file but that's exactly what I want to avoid. If PY-SDL2 is any good, then I might give that a try.

 

edit: added tags to post. Also note my use of QUOTE not CODE which allows for traffic light indication of good vs. bad code :)

Share this post


Link to post
Share on other sites

 

I'm happy to keep tapping the F9 key, and editing code, and pressing F9 again to run-and-compile. Including things once is what I'm already doing, the problem is getting the modules structured so that they work without causing me problems.

Share this post


Link to post
Share on other sites
I know that I cannot #include graphics.h into both main.cpp and tile.h - I tried that a month or two ago!

 

 

You are absolutely allowed to include a header in both your main.cpp and another header file, though be wary of including in header files due to circular dependencies (two header files cannot include each other, either directly or indirectly). You're not allowed to because of said circular dependencies. You can avoid them by forward declaring classes instead of including them when you don't need to access any members and only need the definition of the type, and put your #includes in the .cpp file as often as possible rather than in the header.

Edited by cmac

Share this post


Link to post
Share on other sites

I'm not sure I'm understanding your issue correctly, but it sounds like the Singleton pattern would be a simple solution to your problem.
 
You are absolutely be allowed to include a header in both your main.cpp and another header file, though be wary of including in header files due to circular dependencies (two header files cannot include each other). You're not allowed to because of said circular dependencies. You can avoid them by forward declaring classes instead of including them when you don't need to access any members and only need the definition of the type, and by limiting your #includes to .cpp files as much as possible.


I think that explains it: have a master class which allows any derived class to be accessed from anywhere in the program.

 

In my programs I have only ever included .h files, never .cpp ... so this sounds like an esoteric concept to me. I'll look into it.

Share this post


Link to post
Share on other sites

In my programs I have only ever included .h files, never .cpp

Include the .h files, but include them in .cpp files when possible.

 

Forward declare when possible, include when you must. Look up forward declaring if you're not familiar with it.

 

Your master class sounds like a bad idea. It sounds effectively like a global, which is generally considered a code smell. If everything can access and talk to everything else from anywhere, tracking code flow becomes a huge issue as the project grows.

Share this post


Link to post
Share on other sites

I found some resources which appear to show exactly what I was after:

 

http://gameprogrammingpatterns.com/design-patterns-revisited.html

 

Above: singleton pattern explained in the context of games; and finally ...

 

... http://www.gamefromscratch.com/page/Game-From-Scratch-CPP-Edition-Part-5.aspx

 

Above, using GameObjectManager to manage objects by avoiding a for ranged loop, instead using while ... from within the GameObjectManager member function.

 

 

void GameObjectManager::DrawAll(sf::RenderWindow& renderWindow)

{
 
std::map<std::string,VisibleGameObject*>::const_iterator itr = _gameObjects.begin();
 
while(itr != _gameObjects.end())
 
{
 
itr->second->Draw(renderWindow);
 
itr++;
 
}
}

 

Share this post


Link to post
Share on other sites

Above, using GameObjectManager to manage objects by avoiding a for ranged loop, instead using while ... from within the GameObjectManager member function.

 

A ranged-for loop is essentially doing the same thing as the while loop. There's no particular reason to avoid it here; it is, in fact, less prone to error (due to fewer points of failure on your part) than the manual while version of the same loop.

Share this post


Link to post
Share on other sites
I know that I cannot #include graphics.h into both main.cpp and tile.h - I tried that a month or two ago!

There are two ways around this, which one works depends on the situation.

 

First way is to avoid defining the entire content of graphics.h more than once, as indicated by Khatharr. I'll just give it below too for completeness.

// graphics.h

#ifndef GRAPHICS_H
#define GRAPHICS_H

// definitions of graphics.h

#endif

The '#define' line defines the symbol 'GRAPHICS' the first time this file is included. Any next time it is included, the '#ifndef' line jumps to the end of the file, skipping all definitions.

 

The second way is only define existence of the Graphics class, but not its contents, by

class Graphics;

This says 'there is a class called Graphics'. The above is sufficient for declaring pointers or references to a Graphics object, like "Graphics *ptr" or "Graphics &g".

Typically you see this in a header file, where a function parameter is a pointer or reference to the Graphics object, like  "void f(Graphics &g);"

 

In the .cpp where you define the implementation of the function f, you then #include "graphics.h", so the compiler knows about the members of the Graphics class (and g.draw() works, for example).

 

 

 

This is disappointing because I spent a lot of time reading text books and tutorials, plus I asked many questions here. I'm 26 and I still don't understand how to 'modularise' a program in c++. I read Jazon Yamamoto's book but I haven't been able to replicate any of his complex code for my own use

This is a TV phenomenon, Yamamoto of course shows the solution that works, not the zillion solutions that don't work.

 

The reason that you fail however is not so much because you are so bad at it, but because the problem is complicated. The number of wrong solutions vastly outnumbers the number of good solutions, so statistically, you'll normally end in a bad solution without further knowledge. As you find reasons for failing, you iterate to the next attempts, avoiding the known fails you found so far (and then typically walk straight into the next one).

What it means is that you're still puzzling on the correct structure of the solution. If you get it right, all pieces will fit precisely.

 

 

 

Above: singleton pattern explained in the context of games; and finally

Singleton, as to avoid having a Graphics parameter? First intuition says it's a bad trade, but I haven't seen the code, and the devil is in the details, as always.

 

 

 

using GameObjectManager to manage objects by avoiding a for ranged loop, instead using while

I agree with Josh Petrie that it shouldn't make a difference, but it does for you apparently, so something happens in some way. Could you give more details what problem gets solved by the 'while' ? (or even why the problem with the 'ranged loop' doesn't happen with 'while').

Edited by Alberth

Share this post


Link to post
Share on other sites

Could you give more details what problem gets solved by the 'while' ? (or even why the problem with the 'ranged loop' doesn't happen with 'while').


I've likely got the wrong idea in my head - I thought that scopes were handled differently (while versus for). A for-loop has its own scope/namespace, like a virtual container within the program, the same thing with functions. I thought while loops were different, because I can declare something immediately prior to the while loop, and access that data from within the while loop without needing to pass a value or reference.

Share this post


Link to post
Share on other sites

 

 


I thought while loops were different, because I can declare something immediately prior to the while loop, and access that data from within the while loop without needing to pass a value or reference.

 

Variable declaration outside also work with for and do loops. It is not constrain to while.


 

 


I thought while loops were different, because I can declare something immediately prior to the while loop, and access that data from within the while loop without needing to pass a value or reference.

 

Variable declaration outside also work with for and do loops. It is not constrain to while.

Share this post


Link to post
Share on other sites

Could you give more details what problem gets solved by the 'while' ? (or even why the problem with the 'ranged loop' doesn't happen with 'while').


I've likely got the wrong idea in my head - I thought that scopes were handled differently (while versus for). A for-loop has its own scope/namespace, like a virtual container within the program, the same thing with functions. I thought while loops were different, because I can declare something immediately prior to the while loop, and access that data from within the while loop without needing to pass a value or reference.


If something is declared outside a particular scope, it will be visible to scopes declared within that scope.

int x = 0;

void function1()
{
   // works because x is a global - declared outside function1
   x = 2;
   int y = x;
   for (int i = 0; i < 5; i++) 
     y *= i; // works, y is a local outside the scope of the for statement and i is within it

   // does NOT work, i is local to the for
   x = x * i;
}

Share this post


Link to post
Share on other sites

This topic is 413 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