Pregunta

I understand the basics of pointers and references, but the biggest issue I have is in deciding when they should be used (and which). The main example I will give is one of a basic game. Assume the setup is something like:

  • new World
    • Camera
    • Map

World is a pointer because every time the game starts a new save or loads an existing one, it deletes world and loads a new one. But inside of World, only one existence of Camera and Map should ever exist and only for the duration of World. If World is destroyed, then obviously they should be as well. However.. lets say Map needs to access camera (but so do other objects), should Camera be passed by reference into Map or as a pointer, or? For instance, if its supposed to be by reference, should it be:

map = Map(&camera);
(inside map class) Map(Camera camera) {...}

Or more like:

map = Map(camera);
(inside map class) Map(Camera &camera) {...}

Also, lets say Map holds a 2D vector of tiles called grid. Something like:

std::vector< std::vector< Tile > > > grid;

Now lets say I have a PathFinder class that needs that grid passed in. It needs to edit the tiles directly to change f, g, etc values (for pathfinding). Should that 2D vector be just a normal 2D vector of Tile's and the entire thing passed by reference to the PathFinder? Or should it be a 2D vector of Tile pointers?

Also, NPC's and Player will have a currentTile which is the tile they currently are on. They will need to have a reference or pointer to that tile so they can also set themselves as an occupant on that tile via something like this inside the NPC/Player classes:

currentTile = tile;
currentTile->SetOccupant(this);

The other concern comes in when I then destroy that grid to load a new map, how do I easily handle making sure nothing is pointing to Tiles that no longer exist. Do I simply have to loop over those classes and set the currentTile to NULL?

This is where I started to really get confused by this stuff. Any help is appreciated as I'm obviously pretty nooby. @_@; And sorry if this isn't really game topic related. If it needs moved to a different stackexchange, just let me know or move it (if you can). >_<

¿Fue útil?

Solución

Consider the Map class. You can pass in a Camera by reference or as a pointer. It really wont matter. What matters is what happens with the Camera passed in. Are you going to assign it to a member that is a Camera*? Otherwise, setting the Camera in the constructor will not accomplish much. You will have to supply a Camera pointer/reference with every call to Map that requires a Camera.

For storing Tile objects, a single dimension std::vector<> will do just fine. By using some simple math, this tile grid can be traversed quite easily. Nesting another vector inside will only cause unnecessary overhead. Here is a sample algorithm for doing so:

std::vector<Tile> tiles = makeTiles();
for (int y=0; y<mapHeight; y++)
{
   for (int x=0; x<mapWidth; x++)
   {
      Tile tile = tiles.at(x + y*mapWidth);
      tile.doSomethingWithTile();
   }
}

Deciding on how to get tile data from your Map to a PathFinder is really a matter of how well you want to protect your Map's data. By giving out a reference to to all the tile data, you are essentially making it public access. Since the PathFinder needs to edit individual tiles but not the entire tile array itself, a better approach would be to have a method like this:

Tile* Map::AccessTile(int tx, int ty);

This way, the entire vector of tile data is not exposed, but PathFinder will be able to get at what it needs. Additionally, Map::AccessTile() could be made private, and PathFinder declared as a friend to Map. The other approach is to provide methods like Map::SetTileF(int tx, int ty, float f). This can be tedious though.

For NPC and Player, a similar solution is available. They don't actually need to have direct write access to the tile they are on. Add a method like Map::SetTileOccupant(Entity *entity) and a corresponding Map::GetTileOccupant().

Now your concern about deleting objects. You should take a look at some of the emulated pointers provided by C++ (specifically std::shared_ptr<> and std::weak_ptr<>). A quick explanation of these is shared_ptr is a pointer that "owns" an object and a weak_ptr knows where to access the object, but doesn't actually "own" the object.

With these emulated pointers, you can do something like the following:

//prototype for setWorld
//note that shared_ptr<> casts to weak_ptr<> nicely
Camera::setWorld(std::weak_ptr<World> world);

//setup the camera and world
std::shared_ptr<World> world(new World);
Camera camera;
camera.setWorld(world);

With the above code, camera has a pointer to the world. If world is deleted for some reason, camera can figure this out by the following method:

bool Camera::worldIsValid()
{
   return (this->mWorld.expired() == false);
}

Also, you are encapsulating the Camera inside of a World and that is probably something you don't need to do. Instead, Camera could be standalone and just refer to a World whenever it needs information about it or the Map contained in World.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top