1

I have a group of methods that is going to be very large. I need to be able to call methods systematically from a large group, in two different ways. The methods create a new item object with variables specific to the type of item created, for instance the rubber(); method makes an item with

itemId = 12, weight = 1, name = "Rubber", ...

for 20+ more variables. I am currently able to call these by name

object.rubber();

I want to be able to call them by itemId

object.getById(12);

I need to be able to process them by itemId for situations like this:

/* itemId:
1: ruined copper
2: weak copper
3: refined copper
4: ruined iron
5: weak iron
6: refined iron
7: ruined brass
-(More metals in this pattern)-
36: sturdy aluminum
*/


//within furnace class

public void smelt(item input, inventory furnace){
//input is the item to smelt, furnace is the furnace's crucible.

    if(input.getId() < 36 && input.getId()%3 = 1){
    //if the imput's ID is less than 36, it's a metal, and if ID % 3 = 1, it's a ruined metal, meaning it is smeltable.

        furnace.addItem(handler.list.addById(input.getId() + 1))
        //add an item to the furnace with an ID one higher than that of the input, which will be a 'weak' metal of the same type

        furnace.removeItem(input);
        //delete the input item
    }
}

The metals are all regularly ordered, so if I can call them by ID it will be possible to manipulate them with very little code, whereas otherwise the furnace will need an if statement for each individual metal to determine what it smelts into. Many other processes will need to be able to work similarly.

I've found two possible solutions so far:

one would be to use reflect to call the methods, but I've been warned that it's notoriously unstable and difficult to figure out.

the other would be to overhaul my current system and store the item properties on a text file, something like

Ruined_Copper false 4 256 ...
Weak_Copper false 4 128 ...
etc.

and then have a filereader open the file and count lines until the line number equaled the target itemId. It would then split the line by whitespace and assign the values to variables, ie:

newItem.setName(lineOutput(1));
newItem.setIsContainer(lineOutput(2));
...

This would output an item with

name=Ruined_Copper, isContainer = false, etc.

I'd prefer to figure this out early in development so I don't have to change thousands of items to a new format in the future. Which one of these will go faster? Which one will be more stable? Is there a better way alltogether?

EDIT: To clarify, I realize that it would be a bad idea to have metals 0-36, woods 37-56, etc. My goal is to allow systematic or player-created items to be added by player actions in game, so the finished product will have space between catagories, ie 0-60 are metals, 61-999 are blank, 1000-1085 are wood types, 1086-1999 are blank, etc.

5

When developing games it is often a good idea to separate the game engine from the game content by moving such information to external files. It allows to tinker with values without having to recompile the game, it allows non-programmers to edit these files and it makes the game more modding-friendly.

However, keep in mind that file access is very slow compared to memory access. Therefor, you should parse such files once at startup and store the information in memory.

If you want to look up objects by number, you can use an ArrayList<ItemType>. If you want to look up objects by name, you could use a HashMap<String, ItemType>.

Using ID numbers with certain "magic" ranges will bite you back sooner or later. When the items 0-36 are metal, what will you do if you some day decide that you want to add even more kinds of metals to the game? You would have to extend that range into a range which might already be taken by non-metal items, which then need to be renumbered. This can turn quite ugly, for example with backward-compatibility of savegames. Or what if you decide that you would like to have metals with more or less than three quality levels?

A better design would be if the item ID number itself carries no inherent information and that information is also read from the config file:

name="Ruined Copper", weight=1, category="metal", qualityLevel=0, smeltsTo="Weak Copper"
name="Weak Copper", weight=2, category="metal", qualityLevel=1
  • Definitely a good idea: store this data in a configuration file for easy patching and modding. – user22815 Jan 24 '16 at 0:57
  • The arraylist seems to run a lot more smoothly than directly filereading. Adding categories like smeltsTo is a really good idea, I feel silly for trying to pack all the information into an int. – krill Jan 24 '16 at 20:08
  • And by HashSet I assume you mean HashMap, or just Map... – David Conrad Jan 26 '16 at 1:03
  • @DavidConrad Yes, it was a typo. I corrected it. Map is just an interface, so krill would need to pick a concrete container class which implements it anyway. Other Map implementations like TreeMap or LinkedHashMap might also be options, but I didn't want to go too far into detail in this regard because that's not what the question is about. – Philipp Jan 26 '16 at 9:24
  • Being an amateur game designer and ex-professional developer, I can tell you this idiom works for more than just games! Making your algorithms and business logic generic and adaptable will save you tons of maintenance in the long run and force your stakeholders to really think about their processes before cutting IT a check to automate them. If I had a dollar for every process they wanted me to automate before they had figured out the manual processes and the patterns between them, I could buy us a rather nice steak dinner. – corsiKa Mar 14 '16 at 20:48
4
if(input.getId() < 36 && input.getId()%3 = 1){
//if the imput's ID is less than 36, it's a metal, and if ID % 3 = 1, it's a ruined metal, meaning it is smeltable.

Giving magic meaning to the id numbers is asking for trouble. What if you want add a new type of metal? What about removing one? What about adding a new state? Multiply this across all the different types of items in your game, and you have disaster looming.

one would be to use reflect to call the methods, but I've been warned that it's notoriously unstable and difficult to figure out.

Unstable usually means either something that crashes a lot or something that changes a lot. Neither of these are true of reflection. Its also really not difficult. However, reflection is slow, prevents the compiler from checking your code as much, renders refactoring tools ineffective, and generally indicates a poor design.

the other would be to overhaul my current system and store the item properties on a text file

I'm a big fan of configuration. Whenever possible, I like to put details in configuration rather than code. This reduces duplication and boilerplate. It follows the rule of least power. There are a few different ways to do this:

One way is a text file as you mention. I wouldn't recommend a plain text file, I'd use YAML or JSON as a basic syntax. That you get the parsing for free, and various tools to help you work with it.

Another way is java enums. Java enums are objects and you can attach extra data and methods to them. I've soured on enums a bit because its tempting to switch on them when you should add a property, and can't create enums at runtime. (It's useful to be able to construct arbitrary combinations of properties in tests)

Another way is simple to create immutable objects in java. With the text file approach, you eventually create java objects from the data. You could also just create the java objects directly. This is somewhat nice because you don't have to switch out of java to modify your objects. On the other hand, its harder to develop/use tools that manipulate the data.

  • (Note: this is purely opinion) JSON is great as an interchange format because it is very efficient. However, XML, in its grand verbosity, is better for configurations because a human can read the tags and understand how to edit it. Stuff the XML in a compressed file (maybe put all the configs in a zip or jar file) and it is just as efficient in terms of storage space and download size. – user22815 Jan 24 '16 at 1:00
  • 1
    @Snowman, I have to disagree. What's different between seeing the keys in a json file and tag's in a xml file? Both seem equally understandable to me. But XML adds a lot of pointless additional verbosity and complexity that aren't helpful. – Winston Ewert Jan 24 '16 at 1:02
  • 2
    That "XML vs. JSON vs. YAML vs. INI vs. whatever" discussion never leads anywhere. – Philipp Jan 24 '16 at 1:15
  • Thank you, I'll look into JSON, I know one of the people I'm working with has experience with it. – krill Jan 24 '16 at 19:50
0

My solution is have a config file where you have a property associated with the method in config. This will be used in a builder class.

And also have a config where you give the properties in a xml/properties/json.

eg

Builder Config name=setName() category=setCategory qualityLevel=setQualityLevel

Value Config name=Jack category=metal qualityLevel=1

Pseduo Code Read BuilderCOnfig and store.

While ( Value Config.hasElements() ) { method= BuilderConfig.getMethod() call object.method(); }

This makes everything configurable for future additions to properties/methods. Also takes care when some properties are missing.

The builder element can be done with if you use Spring which auto wires the methods if they are named consistently. And all you need is a bunch of xml files.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.