Jump to content

Create a simple class with all my variables


MexSource
 Share

Recommended Posts

Hey,

 

I'm trying to create a simple class where i wan't to store much variables (int string float vectors entitys, anything.). But i noticed when i try getting some variables i need to first create a new instance of the class

(no idea if i can say it like this, but here's an example for a getter function)

 

i do this:

Vec3 getRunnerSpawn(){
return RunnerSpawn;
}

(the above class is called "GameManager")

 

and to get the vector i need to do this:

GameManager gm;
gm.getRunnerSpawn();

 

but i don't won't to create a new instance, because then i would need to create a new instance for every class where i need to use this function.

 

Anyone an idea?..

C++ :3

Link to comment
Share on other sites

What you are looking for is called a Singleton. Google "C++ Singleton Pattern" and you'll see how you implement that. I would tell you that they are bad and not needed and should be avoided, but I think you might need to experience it for yourself by using them given this post. I can tell you until my face is blue why it's bad and not needed, but it's hard to understand why until you start running into the problems globals bring with them.

Link to comment
Share on other sites

i will have a look at them thanks wink.png but what are other possibilities, when this is so bad :/?

 

PS: and how is something like Leadwerks::Time::GetCurrent(); made? Is this a singleton too?

 

 

//After googling a bit i remembered how i did it in java, with static variables and functions.

This seems to work here. Would this be a good idea or are the negative aspects?

C++ :3

Link to comment
Share on other sites

Why Global Variables Should Be Avoided When Unnecessary

  • Non-locality -- Source code is easiest to understand when the scope of its individual elements are limited. Global variables can be read or modified by any part of the program, making it difficult to remember or reason about every possible use.
  • No Access Control or Constraint Checking -- A global variable can be get or set by any part of the program, and any rules regarding its use can be easily broken or forgotten. (In other words, get/set accessors are generally preferable over direct data access, and this is even more so for global data.) By extension, the lack of access control greatly hinders achieving security in situations where you may wish to run untrusted code (such as working with 3rd party plugins).
  • Implicit coupling -- A program with many global variables often has tight couplings between some of those variables, and couplings between variables and functions. Grouping coupled items into cohesive units usually leads to better programs.
  • Concurrency issues -- if globals can be accessed by multiple threads of execution, synchronization is necessary (and too-often neglected). When dynamically linking modules with globals, the composed system might not be thread-safe even if the two independent modules tested in dozens of different contexts were safe.
  • Namespace pollution -- Global names are available everywhere. You may unknowingly end up using a global when you think you are using a local (by misspelling or forgetting to declare the local) or vice versa. Also, if you ever have to link together modules that have the same global variable names, if you are lucky, you will get linking errors. If you are unlucky, the linker will simply treat all uses of the same name as the same object.
  • Memory allocation issues -- Some environments have memory allocation schemes that make allocation of globals tricky. This is especially true in languages where "constructors" have side-effects other than allocation (because, in that case, you can express unsafe situations where two globals mutually depend on one another). Also, when dynamically linking modules, it can be unclear whether different libraries have their own instances of globals or whether the globals are shared.
  • Testing and Confinement - source that utilizes globals is somewhat more difficult to test because one cannot readily set up a 'clean' environment between runs. More generally, source that utilizes global services of any sort (e.g. reading and writing files or databases) that aren't explicitly provided to that source is difficult to test for the same reason. For communicating systems, the ability to test system invariants may require running more than one 'copy' of a system simultaneously, which is greatly hindered by any use of shared services - including global memory - that are not provided for sharing as part of the test.

 

 

The real issue will come when you try to track down bugs and it becomes a nightmare to figure out what's going on with those globals. Their values can be changed at any point by anything! That will cause bugs that will be hard to track. 100% promise that. I don't mean to be a snob about that but many people on many projects have experienced this which is why we get the globals are evil idea. Google "globals are evil" and see all the posts about it.

 

Programming around globals will generally lead to easier code to read, maintain, & test. If you are used to coding without globals it can be hard to see how to do it so I'm willing to help if you like.

  • Upvote 1
Link to comment
Share on other sites

I will tell you tomorrow when I'm back at pc

BTW: what's bad with globals? (Never used them before in c++ and on the first look they seem OK)

Usually, nothing at all. A lot of spaghetti code is created trying to enforce the idea that this is the worst thing in the world.

 

This is the exception, and it is very bad. Global variables that depend on one another should be avoided:

http://www.parashift.com/c++-faq/static-init-order.html

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

A lot of spaghetti code is created trying to enforce the idea that this is the worst thing in the world.

 

State machines and the like are not considered spaghetti code. Using globals is what leads to spaghetti code. State machines are very standard in almost all games created. Also being able to unit test code is the standard in all programming and globals make that very hard if not impossible to do. Working inside a function where all state is inside that function and not outside of that function is clean coding that makes things easier to maintain and change later on.

Link to comment
Share on other sites

What is this position variable and why do you need it in every class? Can you give some game context on it?

 

The hardest part about globals is not knowing the future. There is no way to make a read only variable that you read in at run-time (ie it's no constant) so nothing stops you, or anyone that may work with your code later in the future, from changing that value. You may not think you want to change it today, but a situation may come up where you need to and now things might get complicated. This is just the general idea as it's hard to say without knowing the game related details of why you think you need this global.

 

Also, when your game is implemented as a state machine you can pass this variable to every state, or make it part of the state class so that it's not global but can still be used in every state.

Link to comment
Share on other sites

"It's a poor carpenter that blames his tools"

 

Sure using a hammer to fasten a screw gives undesirable results, but is that the fault of the hammer?

System:

Linux Mint 17 ( = Ubuntu 14.04 with cinnamon desktop ) Ubuntu 14.04, AMD HD 6850, i5 2500k

Link to comment
Share on other sites

I must be missing the idea with that statement in relation to this topic Guppy. What side are you on? smile.png

 

I thought it was fairly obvious that it was in reference to the whole "singletons are bad" rant.

System:

Linux Mint 17 ( = Ubuntu 14.04 with cinnamon desktop ) Ubuntu 14.04, AMD HD 6850, i5 2500k

Link to comment
Share on other sites

I have a GameManager.h file like this now:

 

#pragma once
#include "Leadwerks.h"
using namespace Leadwerks;

class GameManager
{
public:
static void initialize(World* world);
static Vec3 getRunnerSpawn();
static Vec3 getDeathSpawn();
static Entity* getEntityByName(string name, World* world);
};

 

And these to functions i need to be global

(GameManager.cpp)

Vec3 GameManager::getRunnerSpawn(){
return RunnerSpawn;
}
Vec3 GameManager::getDeathSpawn(){
return DeathSpawn;
}

 

(Btw: In my game there are two different types of players, the runner's and the death's)

 

On game startup i do this:

(GameManager.cpp too)

//Load objects
list<Entity*>::iterator iter;
for (iter = world->entities.begin(); iter != world->entities.end(); ++iter){
Entity* currEnt = (*iter);
System::Print("[GameManager] Entity loaded: " + currEnt->GetKeyValue("name"));
if (currEnt->GetKeyValue("name") == "runner_spawn"){
RunnerSpawn = currEnt->GetPosition();
}
if (currEnt->GetKeyValue("name") == "death_spawn"){
DeathSpawn = currEnt->GetPosition();
}
}

 

At the moment i only need this in one function at this position:

 

(Player.cpp)

player->SetPosition(GameManager::getRunnerSpawn());

 

But i will need it definitely more in future.

 

 

I think i'm going to add more such variables, like the current player amount, the time left etc. to simply use that in all classes

C++ :3

Link to comment
Share on other sites

Global variables are usefull for general game purposes and stuff, can be used but in a clever way.

 

For your variables, of general purposes, after reading objects position in your search function, place all variables that you have read in some Singleton Classes with all variables, specially for Player management that is the center data of your game.

 

 

Pseudo code :


list<Entity*>::iterator iter;
for (iter = world->entities.begin(); iter != world->entities.end(); ++iter){

...
if name = "PivotPlayerPosition" {
StaticData.positionPlayerStart = entity:GetPosition()
}

if name = "EnnemySpawner" {
StaticData.EnnemySpawnerPoint1Position = entity:GetPosition()
}

...
etc ..

}

  • Upvote 1

Stop toying and make games

Link to comment
Share on other sites

I thought it was fairly obvious that it was in reference to the whole "singletons are bad" rant.

 

I guess I'm to dense. Global state is bad (for many reasons) and singletons creates global state, hence they are bad. It's not as if people just made this stuff up. They've ran into the issues and share their experiences to help others avoid it. Moving on.

 

 

@Mex How do you manage the different states in your game (main menu, gameplay, etc)?

 

In your example, generally you'll load your map close to where you need it and you would want to pass the position (or spawner object) to the player class constructor while you create the player object during map loading. You probably won't like the reasons as to why this is better as I guess you don't do unit tests, but if you were to test things out, what you have makes it harder because you have this global requirement. The question is do you really need that global requirement? The answer is no. So knowing the limited I know about your game, I would say pass it to the Player constructor and store it in there for later use inside the player object.

 

What I see in your example is that your player variable is just an LE entity instead of being it's own class. There are other benefits to wrapping up your game objects into your own classes and using variables to hold the LE types. Think of LE types are just like ints, floats, etc. A player class is a perfect example of this.

 

 

class Player
{
private:
Entity playerEntity;
Vec3 spawnPosition;
public:
Player(Entity player, Vec3 pos) : playerEntity(player), spawnPosition(pos)
{
void Respawn()
{
playerEntity->SetPosition(spawnPosition);
}

Vec3 GetPosition()
{
// or if you are lazy just make a function to return the Entity so you can use the already created Entity functions
return playerEntity->GetPosition();
}
};

 

 

Generally your player objects will have things like health, speed, etc and you can nicely contain them all in this class instead of having all those variables floating around where they don't belong.

 

You probably aren't thinking about testing but think about unit testing this vs what you have with global state. This class doesn't have any special requirements of global state so when testing you can do something as easy as:

 

void Player_Respawn()
{
Entity* e = LoadEntity();

Player player(e, Vec3(0, 0, 0));

player.Respawn();

Assert::AreEqual(Vec3(0), player.GetPosition());
}

 

Note there is nothing messy or surprising about this. You don't have to know that you have to setup some global state before something else can be used correctly. Believe me, I've been on a project before where almost everything was global and it was a nightmare to learn. If you ever stop and come back it'll be hard to remember all the global states that need to be setup before something works. In my example the objects explicitly say what they need to work. If I was to hide a global state inside the Player object much like in your example then it's much harder to figure that out. You may think the small things you have in mind won't matter but they will and they will start adding up and things will be hard to understand how the flow goes pretty quickly.

 

Global variables are usefull for general game purposes and stuff, can be used but in a clever way.

 

They way you have it setup I would argue isn't clever and isn't needed. Looping over the loaded entities is generally happening at some point where you can create and store the objects inside a class (even if it's the App class) so store them there as normal variables and pass them along to other objects when needed.

  • Upvote 1
Link to comment
Share on other sites

Passing data around is exactly what you should be doing. You should be passing data to constructors like this. That is considered clean, flexible, & testable (again this is real world findings by more than just me). If you find that you are passing 10 variables to a function then that is a hint that something should probably be in a class to help organize things. Take a step back and think about things as real objects. Don't be afraid of classes. A class that has a bunch of unrelated variables (in terms of what a real world object is) raises a red flag.

 

Another hint for easy testing is that you should never use 'new' inside a constructor and instead pass objects to constructors that are already created outside of it for use. This again makes testing much easier.

 

Another tip would be to program to an interface. C++ doesn't have a strict interface but a class that just has just virtual functions and a name that starts with I like (IActor) can act as an interface and help make your design more flexible and easier to test. Although speed is sometimes an issue with games depending on what you are making and this one can sometimes (not likely for a 1 man game) slow down execution.

 

I'm not saying you need to do all this now, but if you incorporate some of these ideas now you may see some of the benefits and you will be moving towards a cleaner, testable, & flexible style of coding. Overdoing it at first if it's new to you will only complicate things. These are often concepts that you need to slow introduce and see their benefits over time.

  • Upvote 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...