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

Started by
10 comments, last by Oberon_Command 7 years, 6 months ago

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 :)

Logging in from unsecured wireless, I hope no-one steals my password (October 22, 2016)
Advertisement

Would this help?

https://en.wikipedia.org/wiki/Pragma_once

https://en.wikipedia.org/wiki/Include_guard

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

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.

Logging in from unsecured wireless, I hope no-one steals my password (October 22, 2016)
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.

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.

Logging in from unsecured wireless, I hope no-one steals my password (October 22, 2016)

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.

Hello to all my stalkers.

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

Logging in from unsecured wireless, I hope no-one steals my password (October 22, 2016)

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.

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').

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.

Logging in from unsecured wireless, I hope no-one steals my password (October 22, 2016)

This topic is closed to new replies.

Advertisement