4
\$\begingroup\$

I'm writing a shmup and I'm working on implementing things like level design and achievements. I have a trigger system that I've implemented which allows me to do basic things and can assist the basic level workflow. However, as it is, this isn't really flexible enough to recognize some other things, such as:

  • Determine when an enemy is killed vs. removed from the game
  • Not taking damage during some period of time
  • Killing enemies using a particular weapon
  • Killing enemies in a particular order

Whereas the triggers I have available now basically poll for information, these need to look through a series of events to understand whether the condition is met. So I've conceived a StatisticManager class which would be able to take and store all these events, and provide queries so that new triggers could be written to test for these things. For example, a trigger might ask for an ordered list of enemies killed during some time period, and then determine whether a particular sequence of kills is met.

The design raises a lot of concerns, though, such as determining when to discard / archive data, and how to get statistical data from previous game sessions. Certainly efficiency is a concern.

So my question is: Does my StatisticManager idea seem appropriate for my needs? What other designs should I consider?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Are these triggers defined by level, and about how many of them will you plan to have? These questions can help you figure out what is the best approach to efficiency. If your triggers are always active and polling for data, it would only become a problem if you have to iterate through thousands of them each frame (for each possible event in the game). If you only need to iterate through several, it would be fine, though there are several cases where you can optimize a bit more.

Polling for "global" level statistics that apply everywhere (such as # of enemies killed/visited and time passed since last hit) is easier on the game loop than having all triggers on notice, because you only will have to poll for a few items.

However, for triggers to fire immediately based on specific conditions you can have a "sequence reader" that reads from certain lists. These lists will be like a log for several things happening in the level. The enemies themselves could have particular statistics attached to them, like a killedBy variable that points to the type of weapon that killed this enemy, and a timeKilled variable to let the game know when this particular enemy was killed. Then this data can be added to the particular list when they're killed or removed from the screen.

For particulars of reading sequences, Take advantage of string matching. If it's possible to encode some of these stats as 8-bit values, do this as much as possible, then your lists can simply be a group of character values and the sequence reader can read it as a string. For larger values you can split it into two or more characters with bit shifting and you just have to read longer strings. The triggers that need to match patterns will query the appropriate list(s) and see if all matches are met, and if they are, then that condition is met.

If you want to be efficient about this reserve a string buffer of a given size which you can be sure won't overflow during the play of that level. Your StatisticManager sounds like it's more in this direction. I would code it simply as an aggregation of data, and have the sequence reader read from these stats.

At the end of each level collect data from the lists and save it as temporary session data, either directly in memory or as a temporary binary file (another reason why ASCII encoding/decoding can be beneficial) which will be removed when the user closes the program. Archive only the times when the player has reached certain achievements. Detailed session data such as enemy kills or player movements is only useful if you are planning some sort of "replay/record" feature in your game.

\$\endgroup\$
1
\$\begingroup\$

In and of itself, your StatisticManager class, sounds like a good idea to encapsulate this sort of logic. However let's address the hurdles you mention.

Storage

Backlog size is not what I would call a problem. The reason is that every application that has ever kept a backlog has had to draw the line somewhere, to limit the amount of history it retains. You have to cut off somewhere. You need to decide on a backlog size (something, anything!) and work with that constraint in mind, in regards to your game logic. You can adapt it as you move forward in your development processes; other factors will either support or work against your existing choice.

I am not sure I would want to keep data outside of game sessions without there being a very good reason for that. I would keep just the results of earlier analyses. It's true that you wont have anything but the results, so you won't, at a later time, know how those results were derived. But primary and secondary storage space aren't infinite either (no matter how big it is). Again, you have to sacrifice somewhere. If you absolutely had to keep at least certain prior-session data, without losing anything from those sessions, well then that's what file compression is for. Some games do this, and keep a data set that continually grows, eg. save games in some RPGs. But it needs to be darned reasonable if you're going to do that. 1mb per hour is rough. 1kb per hour probably isn't. Even so, it's not an advisable practice.

Processing

As you already know, this all depends on the size of the data set. The less data you include in the calculation, the less concern you have. In many cases, the criterion for selection for processing will be time-proximity, i.e. how recent the data is. Which comes down to the same as the above -- just keep the most recent data, up to some predefined limit of storage space or elapsed time. You could store only as much as you'll ever be able to process at once, if processing cost were a bigger issue than storage cost.

You could parallelise this (CPU/GPU). But it sounds like overkill to me, a brute force approach that doesn't really deal with the core of the problem which is, "How do I restrict my data set and still get good results?" Some of the most expansive games ever made were created on a single-CPU, single-core machine with very limited memory. XCOM/UFO and Elite are two such examples. Dwarf fortress is another. That's because those developers knew where to draw the line.

Conclusion

Another way to look at this is that you are creating a game with a set of unique servers that host the game, much like an MMO. This being precisely because you have this unusual technical constraint (of storage requirements) that simply makes it unfeasible to do on client systems where you have no say over available storage. If this is the conclusion you come to, it means that you're probably not looking at your problem correctly, and even if you are, you'd need to be in the position to host servers, buy more storage when needed etc. I am guessing you're not in that position right now, because that usually requires solid backing for funding. Any engineering problem is about working to your existing practical constraints, so that solution immediately looks unreasonable.

First and foremost, you need to decide what the hard upper limit to your storage requirements are. From there you can prototype the processing, and see what is or isn't too demanding, set yourself another reasonable limit on processing cost, and move forward with your game from there.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .