0
\$\begingroup\$

Question: What is the best data structure for GameObjects that have different states and thus different visuals?

The situation is:

I'm creating a game for mobiles (written in Kotlin, no Engine used).

My simplified object structure looks like this:

I have 2 GameObjects (e.g. Person and Obstacle)

Each GameObject might have two different states (IDLE and MOVE)

But each state might also have different graphics. For instance depending on the movement direction, the MOVE state might show Bitmap-Left or Bitmap-Right.

Person -> WALK-State -> Visualisation (WALK, PERSON) -> draw(bitmap_walk_left_person)

My problem is, that I'm really not sure about that structure. Should I make subclasses for each State or a composition? One Visual object for each State (e.g. MOVE, IDLE, TALK) containing all Bitmaps for Person and Obstacle and always showing the right one depending on the parent?

Currently it looks like this:

I have the GameObject. The object has a state that is called in the update() method (e.g. moving a bit to the left). Every State has a "Visual" object that knows its parent State (State = WALK). It draws itself related to its states type (e.g. WALK) and considers (if needed) the orientation (left or right).

This felt like it keeps structures as abstract as possible but I'm not sure sure thats a good solution because all objects have to keep references to their "parents". So the visuals need a reference to the state (WALK) and to the GameObject (Person) in order to draw the visual for a walking person. Would it be better to create subclases for each State? So I have a class "VISUAL_MOVE_PERSON", "VISUAL_IDLE_PERSON", "VISUAL_IDLE_TREE" ... That seems a lot of redundant code and classes.

Is there a common structure for Actions / States and their Visual Representation? Especially since I will have many more objects with at least a few different states (IDLE, MOVE, TALK, ...)

Should the visuals be part of each state? Or are they independent to each other (e.g. GameObjects have a State and a Visuals object) instead of the "chain of references" shown above?

\$\endgroup\$
  • \$\begingroup\$ Do I understand you're using something like a big if/switch statement or map in your Visualisation class to say "If I belong to a WALK state of a PERSON then..."? So your drawing class also needs to hold knowledge of every entity type and state in your game? That sounds pretty unscalable, so I'm betting I've misunderstood you somewhere... \$\endgroup\$ – DMGregory Aug 15 '17 at 12:13
  • \$\begingroup\$ Yea, somehow this is correct. Currently I'm trying the approach of a sub class for every state and there I pick the parent's type (e.g. person) and set the sprite acording to both. But sure this is not the best solution. \$\endgroup\$ – Tobias Reich Aug 15 '17 at 12:47
2
\$\begingroup\$

This is usually handled in a data-driven fashion. By treating your visuals as data objects, you end up with less code micro-managing which visual to use, and you open the possibility of being able to change your art, add more character types, or even allow your game to be modded by players, just by changing data files, without editing & recompiling your source code.

There are a lot of forms this could take:

  • you could have a Spritesheet data object that maps a key representing a visual state to its corresponding graphic/region of an atlas. Each character type would have its own instance of this data structure, indexing only its own visuals.

  • you could have an Animation data structure that knows the particular sequence of sprites to display for one action (which can help decouple your game logic from rendering logic when showing change over time). Your visual data for a single character would then be a collection of Animations for each state.

  • for maximum decoupling, you could build an Animation State Machine that keeps track of a collection of Animations and the triggers to switch between them. Now your gameplay & AI logic doesn't need to know anything about sprites or animations at all — it just sends events to its state machine and it handles the necessary visual transitions.

In all of these cases, the visual structure doesn't know anything about the kind of entity it's representing, it just exposes something like a SetState method where we can inject a signal that it can use to choose the right representation from its collection of data.

So

frog.animation.SetAnimationState(JUMP)

and

player.animation.SetAnimationState(SWING_SWORD)

Are the same code running in the same animation class but just different instances with different data.

In general, any time you're tempted to make a subclass for each variety of thing in your game, it's a good time to check if any of it can be handled by different instances of data objects instead.

\$\endgroup\$
  • \$\begingroup\$ So it looks like JUMP and SWING_SWORD are Enums or something like that. This would mean the Animation class gets the parents type (frog, player, wall) on creation, loads all graphics needed and shows the one according to the given enum? But this structure leaves no connection between the state and the visuals. This means when changing the state the same "action" has to trigger changing the animations? So the GameObject has a method like setState(JUMP) and sets the State as well as the Animation as seen? \$\endgroup\$ – Tobias Reich Aug 15 '17 at 14:33
  • \$\begingroup\$ Besides, how would be the approach for the even direction separated visuals (walk left vs. walk right)? As written, I solved this by telling the visuals the GameObject parent. So the position, direction etc. are known. But this gives a bidirectional connection that appears bad to me. So would you put everything in the method like "onDraw(x, y, directionInDegrees)" or something? \$\endgroup\$ – Tobias Reich Aug 15 '17 at 14:34
  • \$\begingroup\$ It's certainly possible. A common pattern with animation state machines is a SetDirection method / flag. Remember that the visual doesn't need to know the parent's type. By the time it gets the state key, it's just a plain old integer or similar as far as it's concerned. "Draw your picture for ID 5" doesn't require knowledge of whether ID 5 is a frog jumping or a sword swinging. As long as you've indexed your data correctly it will simply pick the right sprite your data says matches that ID. \$\endgroup\$ – DMGregory Aug 15 '17 at 14:58
  • \$\begingroup\$ Indexing in this case means something like an Enum or so? Like 1=player, 2=walls...? Or do you thinkg a real List/Map etc. for all objects would be a good solution? After all my world somehow knows them but putting all objects in a list sounds strange to me. \$\endgroup\$ – Tobias Reich Aug 16 '17 at 10:36

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.