I’m not sure if this is where I should be putting this post because it’s not really a debugging question as such, but here goes anyway
What’s the best practise for something like this situation:
We’re looking at a tile-based game working around collecting resources to use to build things.
I have a ResourceManager class that maintains tiles on the map that are either Trees or Stone. It keeps the level of resource left on a particular tile (a tree is a finite resource, after all) and will de-spawn said resource if it runs out.
I have an entity class that now needs to find these resources.
The Level class doesn’t give a fig what the map looks like, it just keep a huge list of numbers in an array. Similarly the main Game class that everyone is descended from doesn’t care what the map looks like either, he just holds a GUI and requests that each entity draws itself every now and then.
Should the entity that is looking have the information built-in to know what a Tree is or what Stones look like? Or should it have some interaction with the ResourceManager in order to ask whether a given tile has a resource on it?
If the latter is the answer should the entity store a link to the Game’s instance of the ResourceManager or should the Game provide a public method to allow the entity to call it to request the information?
So far the way everything is set up is that every class has a Game field that they can use to get back to the main Game class but that Game is getting rather busy with methods like canWeMoveOverThisTile() or isThereAnEntityOnThisTile() and soon isThisTileAResource() !!
note, these aren’t actually the method names, I just made them up as examples.
Or maybe I should be moving these tile checking routines to a new class somewhere else?
Thanks for reading my load of rubbish, I hope you understand it and are able to offer me any pointers
IMHO composition is the answer. I assume you may have some sort of update / game tick callback from your “Game” class which each entity receives. For a more traditional OOP / explicit composition approach I’d make a ResourceHandler that can be added to the entity which gets triggered with that update tick / callback. The Entity class itself doesn’t know anything about Game or ResourceManager, but ResourceHandler does have a reference to ResourceManager (passed into it via constructor or other method). All you have to do at minimum is figure out how you can add a ResourceHandler to an Entity perhaps under an “IClockTick” interface or whatever defines your update method as an interface. This way internally your Entity class also knows nothing about ResourceHander and simply finds all the IClockTick objects and calls the associated update method when the entity gets the update callback.
As far as the ResourceManager goes I’m a big fan of generics and generic methods. Let’s say your tiles are integer X / Y based. Potentially you could have a method in ResourceManager like:
public <T> int getCount(int x, int y, Class<T> resourceType)
{
// find tile x / y however you have it stored
Tile tile = findTile(x, y);
return tile != null ? tile.getCount(resourceType) : 0;
}
resourceManager.getCount(20, 20, Tree.class);
... Your Tile class can have the same getCount method as above without the X/Y variables.
I’d start to try and figure out how to remove the Game reference from every single class if you can spare the time. The less each aspect of your game knows about the game and other sections of your code the better. At minimum you can use constructor injection (manually). I’d also start figuring out common interfaces for major sections of your game code such that if necessary you can swap out implementations for testing or developing new game mechanics / features, etc.
I think I understood some of what you were saying Catharsis…
I’m trying to split everything up into different packages so hopefully this will solve itself in time. When I first started out I was just hacking together bits to make them work first then moving them to other places when the needed
For example of common interfaces, I have an AI package that contains a static PathFinder class that anything can then call like PathFinder.getPath().
I’ll keep plugging away and try to remove the Game references, I got rid of a few last night and just pass in the Game object when running update methods
The rabbit hole goes quite deep; sounds like you are relatively new to Java dev! Welcome! As things go it does take a bit of diligence to write clean code. It’s a journey for sure.
The generics / generic method aspects are the doorway to advanced Java coding. Very powerful, but it can take a while to get a handle on what is really possible. You’ll mostly see generics referenced as parametrized types for say collections: ArrayList, etc. The cool thing is that in the method above which is a generic method you can use “resourceType” as a key in a Map to retrieve the count of that resource type as a value of the map and pass it back. It’s nice because with just a little bit of code you can then support an ever expanding amount of resource types.
Splitting things up in packages won’t immediately solve interdependencies. It’s a good idea to create separate packages for different functional areas of your code base.
That’s not actually an interface, but a static method on a particular class. It’s actually quite fragile because there is only one implementation of getPath(). If at some point you wanted to support two or more path finding implementations say for different units it would be hard to accomplish this with a static method unless you passed in more variables to getPath(). It’s even worse because the getPath method would get increasingly complicated.
A general tip: usually you want to limit variables passed through methods to no more than 5 for style and practical purposes.
There is a classic design pattern called the Singleton. I won’t go into it as you can Google for it. It is definitely no longer in vogue for many reasons and is generally considered an “anti-pattern”.
A Java interface provides a contract, but not the implementation. The nice thing about that is that if you can narrow things down to passing around interfaces between functional areas of your code and then at any time provide a different concrete implementation.
That is a good idea / direction to take! Consider making an interface for the Game class and passing that around in your update method which should also be defined as an interface.
public interface IUpdateTick
{
void update(IGame game, float deltaTime); // Lets say deltaTime is in seconds...
}
public interface IGame
{
IResourceManager getResourceManager();
}
public interface IResourceManager
{
<T> int getCount(int x, int y, Class<T> resourceType);
}
public interface IEntity extends IUpdateTick
{
boolean addSystem(Object system);
int getX();
int getY();
}
public class Entity implements IEntity
{
private ArrayList<IUpdateEntityTick> updatableObjects
public boolean addSystem(Object system)
{
if (system != null && system instanceof IUpdateEntityTick && !updatableObjects.contains(system))
{
updatableObjects.add((IUpdateEntityTick)system)
return true;
}
return false;
}
public void update(IGame game, float deltaTime)
{
for (int cntr = 0, size = updatableObjects.size(); cntr < size; cntr++)
{
updatableObjects.get(cntr).update(game, this, deltaTime);
}
}
}
public interface IUpdateEntityTick
{
void update(IGame game, IEntity entity, float deltaTime);
}
public class TreeChopResourceHandler implements IUpdateEntityTick
{
float timeSinceLastTreeCut = 0;
public void update(IGame game, IEntity entity, float deltaTime)
{
// Potentially cut a tree every 5 seconds...
if (timeSinceLastTreeCut > 5)
{
IResourceManager resourceManager = game.getResourceManager();
int count = resourceManager.getCount(entity.getX(), entity.getY(), Tree.class);
// Only cut trees if there are a lot of them! ;P
if (count > 50)
{
timeSinceLastTreeCut = 0;
resourceManager.decrease(entity.getX(), entity.getY(), Tree.class);
}
}
timeSinceLastTreeCut += deltaTime;
}
}
Don’t take the above as the way to do it… Just some psuedo-code showing interfaces and a more concrete example… There are problems with the code above like how do you remove TreeChopResourceHandler from an Entity. The neat thing though is that you can now add the tree chop behavior to any Entity regardless of unit type.
Wow. And these I was thinking I was starting to get the hang of this programming lark!
Yeah, you could say I’m quite new to this… I can get my head round the syntax as I come from JavaScript but I’ve never done anything as involved as a game like this.
I started with TicTacToe a month ago: )
So this is mostly way over my head, I’ve read that sample code about 20 times and while I understand it I can’t quite work out how to apply it to my code so far (probably because my code is so whacky it needs a massive overhaul to fit anything else in in lol).
Thank you very much for your comments in all of this though Catharsis, really interesting stuff to read and think about even if it’s over my head right now!