Game Architecture -- what is your approach?

I doing some basic “make a game in unity” type tutorials to learn coding, so bear with me.

I see some people set up their projects in a modular way. At the center, you have GameManager scripts, which handle the calling of events. Within events, you can subscribe whatever scripts you want. The big idea here is you are building many little pieces that you can essentially drag and drop to fit whatever configuration you need.

Is that a correct understanding of the event system? And is this like, a standard way most programmers are working? How wide a swath of genres can this cover? Any drawbacks?

When youre planning how you are going to design the architecture of a game, what are the biggest concerns you are trying to plan for? Is it different every single time, or is their some general templates you usually fall back on?

bonus: anybody keep trello for some game project showing the high level planning and mind sharing and expounding on that?

example from GTGD:

Yes, what you describe is a good starting point for just about any kind of game.

I made this tutorial on Unity Events years ago, and since then I’ve built up a library of little components designed to be snapped together via UnityEvents like LEGOs. They’re great for prototyping, but even in finished, polished games, I still use them (plus custom components that work in similar fashion) all over the place.

3 Likes

thanks @JoeStrout , i’m gonna download and check that out once I get through the event section of this current tutorial I working on.

That’s one way to look at it, and they’re certainly useful for that. Note that it doesn’t have to be about drag-and-drop, I most often use events in code to allow communication between objects which aren’t specifically aware of one another.

Some bits of advice here:
1 - Don’t go too nuts on being able to reuse individual bits. It’s useful to have that as a general aim, but being dogmatic about achieving it can make things more complicated than the otherwise need to be, eat up a bunch of time, etc. Also, as a beginner it can be hard to figure out what will actually be reused.
2 - Raise events on objects for any internal state changes which other objects might care about. Other objects are then responsible for listening to (and unlistening from) any events on other objects on an as-needed basis. Eg: a Door might raise a DoorStateChangedEvent to announce that it is now in the open state, and a Navigator may listen to that event and then decide whether the door being open should cause it to update a path.
3 - UnityEvents are great because you can hook them up in the Inspector. However, keep in mind that having layers or chains of these hooked up in the Inspector makes things really messy to debug. I use events in the Inspector to hook up presentation stuff, but all of my core logic is hooked up via code. Of course that suits me because I’m a programmer, so your mileage may vary if you’re specifically looking at events to do things without code.

4 Likes

You need to write your code in a way that’s execution order independent as much as possible, because you can’t always predict what order events will fire in. As a result this might sometimes involve having code do some redundant work (eg: recalculating values just before they’re about to get changed again anyway), but usually it’s relatively lightweight, and you’ll usually save more overall than it might cost in individual cases.

You also need to watch out for event loops - Event A results in raising Event B, which then raises Event A again… and crash.

3 Likes

Lot of this is over my head but I think will make more sense once I get a bit further.

In case of quoted, I believe that is the reasoning behind how in this tutorial I doing now, many methods begin with

if( thing !=null)

This is just failsafe, right? Make sure we aren’t trying to run the thing when it isn’t supposed to be there?

2 Likes

More or less. It’s really: the thing is supposed to be there, but let’s not crash if, in fact, it is not actually there.

2 Likes

Yes, but it’s a failsafe for a different issue.

I personally, think game management / state management is only useful after you’ve rapidly prototyped all the aspects of your game (rather ugly coding). Then begins the stripping down and organisation. Good luck!

EDIT*
Also, sometimes you spend so much time trying to modularise something and making it generic to handle all cases, you end up only using it once. Use this method carefully and sparingly IMO.

4 Likes

How different?

As I am learning, I try to translate everything into plain English.

So I read this:

  if (inventoryUI != null)
            {
                inventoryUI.SetActive(!inventoryUI.activeSelf);
                gameManagerMaster.isInventoryUIOn = !gameManagerMaster.isInventoryUIOn;
                gameManagerMaster.CallInventoryUIToggle();
            }

as: if inventory is enabled, toggle inventory UI state; etc etc

If we had not said “if inventory is enabled”, then there is danger of this running when the inventory was supposed to be hidden. For instance, is the escape menu is enabled, we don’t want inventory able to be called.

This make sense?

What? Do disabled objects return null? I didn’t think that was the case.

No. This says, if the inventoryUI reference is not null. It is probably not supposed to be null; it would be null if either (a) the inventoryUI reference were not properly assigned in the first place, or (b) the inventoryUI has been destroyed.

Has nothing to do with whether or not it’s enabled.

Any more than this thread has anything to do with game design. :stuck_out_tongue:

2 Likes

null is not antonym of enabled?

(thread was originally about big picture how you think about designing game architecture. I thought that goes under design, since its big picture stuff you plan ahead of time)

Actually, if inventoryUI is null it means that there is no UI object at all. For example, if I write:

inventoryUI = getComponent<Canvas>()

On an object that has no canvas, then inventoryUI would be null.
or if I write

Destroy(inventoryUI);

Then it would be null.

If you want to check if inventoryUI is enabled youd have to check if (inventoryUI.enabled).

3 Likes

gotcha. so null = it does not exist in any context, whereas enabled/disabled is just a toggle state of a thing that has been declared to exist?

While that clears up these words, I am not sure I understand then why I would be saying if (thing != null) at all.

Well whatever that is dragging away from main point of thread and its probably something I will come ot understand just by writing more code.

Maybe think of inventoryUI like a field in the Inspector:

5192237--516029--upload_2019-11-19_14-52-57.png

The field can point to an Inventory UI, or it can point to nothing as in the screenshot above. When it points to nothing, its value is null.

So if (inventoryUI != null) checks if inventory UI points to something.

You only want to activate what it points to if it actually points to something.

1 Like

Something bad is happening if we are looking for something that’s not there? Like an infinite search that waste resources?

Depends what you mean by “looking for” but if you attempt to access a member or function from a null reference, you just get an error message.

Yeah, just search the scripting forum for “null reference exception”. You’ll find thousands of them. :smile:

Exactly. It isn’t necessarily bad. It might just be that no one assigned anything to that “Inspector field” (variable), which might be perfectly allowed in the right context.

3 Likes
            if (inventoryUI != null)
            {
                inventoryUI.SetActive(!inventoryUI.activeSelf);
                gameManagerMaster.isInventoryUIOn = !gameManagerMaster.isInventoryUIOn;
                gameManagerMaster.CallInventoryUIToggle();
            }

Code like this is something that most programmers do a lot when they start coding, then over time they realize that it’s almost always a very bad thing to do.

What happens is, they realize that NullReferenceException can happen very easily in many situations, so to “fix” the situation, they start putting these null checks everywhere.

So when a method is called with bad data, it just silently does nothing, instead of allowing an exception to be thrown.

The short term benefit of doing this is that you might turn bugs that used to be game-stopping bugs into lesser bugs.
Maybe before the != null fix, your UI manager was getting completely stuck when OpenUI threw an exception, and now the inventory just never opens. So it’s clearly better and makes for a more stable game, right?!

The problem with this is that you are basically ignoring the real source of the bug by hiding one symptom of it. It’s like shooting the messenger.

Why was inventoryUI null in the first place? Should we assign the reference in Awake instead of Start? Did we forget to disable the player inputs between level loading? Was it destroyed as a side-effect of something?

The more error hiding you do, the more likely it is that the real issues never get fixed. Also, it gets harder and harder to find the source of a problem, when you don’t immediately see an error message when something is unexpectedly null.

Being aware of possible exceptions and preparing for them is a good thing, but just adding if(x != null) is not a good method of preparation.

If it should be possible to call a method and for it to fail to do it’s intended task, you should always notify the caller about it, so that it can react accordingly. For example this would make a lot more sense:

/// <summary> Open the inventory if possible at this time. </summary>
/// <returns> True if inventory was opened, false if not. </returns>
public bool TryOpenUI()
{
    if(inventoryUI == null)
    {
        return false;
    }
    ...

Now the caller clearly knows that the method can fail to open the UI from just the name of the method, and it has a way to detect when this happens, so that it can react accordingly. It is then for example less likely to get stuck waiting for the onInventoryClosed callback that never takes place, because the inventory was never opened in the first place.

5 Likes