#I try and keep my monobehaviors as

1 messages · Page 1 of 1 (latest)

bleak geode
#

Can I ask you if you are a professional developer and if yes then do your colleagues do the same?
What do you think of Simferoce's comment that such an architecture would lead to bad design choices and it isn't like you are gonna change engine midway?

queen onyx
#

Ive been a C# dev for over a decade now, professionally, yeh. However I mostly have been an application/web dev, unity I am newer to

#

I strongly dislike the practice of using the MonoBehavior scripts attached to individual game objects as your "place" to keep your game state and handle inter-object interactions, it produces an absolute mess of spaghetti code

bleak geode
#

What do you mean by as your "place" to keep your game state and handle inter-object interactions?

queen onyx
#

like if you have say, PlayerEntity which is a MonoBehavior on your player gameobj, and you decide to store all your Player's stats on that thing.

Now everything that wants to mutate any player stats needs a reference to the entire monobehavior

#

everything that can damage the player, everything that can change player stats, everything that can heal them, you have to keep drag and dropping a PlayerEntityref onto everything

#

you pretty quickly end up with a big messy spiderweb of all sorts of monobehaviors referencing other monobehaviors

#

@bleak geode

#

So effectively speaking:

MonoBehaviors only have 2 types of things on em, exposed "mutator" methods to mutate them, ie changing their transforms, their animator state(s), deleting/killing them, etc etc... and exposed interaction events like PointerEnter, ColliderEnter, etc

#

Then your EntityService keeps track of all these MonoBehaviors and handles all CRUD operations.

All your other services utilize EntityService to get refs to your entities, IE "GetEnemies" or "GetPlayer", "SpawnEnemy" etc

And then ALL of your game state is stored up in a set of INotifyPropertyChanged implementing classes, which I call the GameState

#

If you have used WPF before, this might be familiar to you as being quite similiar (not identical, but sort of same family as) "MVVM" pattern

proud briar
#

Pixxel...do you create a gamemanager/statemanager and just have everything public static in there to access it?

red bear
#

@bleak geode @queen onyx
I've been doing Unity for a while now (5 years), professionally for 2 years. I have studied video game development specifically and done a master degree. So, I'm not necessary the one with the most practical knowledge, however I am able to maneuver around a lot of architecture and academic term.

At the end of the day, you should implement something, then figure what are the issue, irritant point of it. Those would generally differ from one person to an other and definitely will be affected by the person background.

The architecture that @queen onyx is promoting will most likely work, however, I feel that it is overcomplicated for what the needs are in Video Game and that the strong points of such architecture are more or less lost in video game development. (I have implemented such architecture and it works really well for Desktop Application)

The issue I have with such architecture in Video Game, it is that most of the interaction come from the game itself, not from the User. (Enemy interact with DeadZone) They are also really granular. (Player interact with a Door) Finally, they are really frequent. (Each Frame, Player interact with Ground). All those characteristic means that there is a need of a high level of indirection to achieve correctly the intended architecture. (It is way more complicated than a standard application)

bleak geode
#

It's just that I am doing a test for an interview now, so I don't exactly have the time and place to build everything and then see what is better. The test is supposed to take only 3 days, with this one being the last

red bear
#

For an interview, that would be a really bad idea to do things that you do not completely know

queen onyx
queen onyx
# red bear <@332529807839723520> <@175774092287279105> I've been doing Unity for a while n...

The reason I like such an architecture is very straightforward:

When you decouple the things that modify game state apart from the things that listen for game state changes, you reduce duplicate code, and keep everything nice and neatly compartmentalized.

When you have something that modifies GameState.PlayerHealth, you dont need to do any further logic beyond "the health is modified"

Anything else that cares about the player health subscribes to listening for changes to player health, if it is relevant to them, and they will handle their end of the logic on what to do when it changes.

A "Single Source of Truth" in your code vastly helps you avoid the Byzantine Generals Problem

red bear
# queen onyx The reason I like such an architecture is very straightforward: When you decoup...

Due to the nature of the interaction, it is impossible to define what is being listen than what is modifying the game. The player health is a prime example of something that can be modify and listen at the same time. Also note that what you are doing might most likely turn into an event hell with multiple indirection causing a real hard time to the reader to know what is the actual implication of a given functionality.

That being said, event is a really powerful tool, however you should limit the depth of the callback and try to use it in situation where you might have multiple possible candidate for listener. Health being an example.

What is more, is that usually, game are made of a ton of serialize data that varies a lot in nature. This is not the case of a normal web application/desktop application. (There is what we call Design and Level Artist that their job is to populate/script such data) It implies, that your GameState, is not something that you can define in code due to the unpredictable nature of it, it is highly volatile and it varies in nature a lot.

I've never really experimented the Byzantine General Problem out of the initialization sequence of a game or level. Most of the time, you design things to be resilient to every situation you encounter and you limit the number of pre-supposition or be sure to always keep them true.

#

You are not wrong to want to centralize things and compartmentalize. It is just the way you are approaching is not optimal and biases by probably your extensive experience in other domain of application.

The best way to confront yourself, is to try what you want to do. Note the things that are not working well, and then search for alternative.

#

At the end of the day, you might find that your architecture work for you. I'm just not convinced that it will suit well the video game space.

queen onyx
#

What is more, is that usually, game are made of a ton of serialize data that varies a lot in nature. This is not the case of a normal web application/desktop application.
Not sure what you mean by this part tbh

red bear
#

What is the primal means to create a level ?

#

Who creates a level, design it ?

#

Take by example a labyrinth.

#

In a labyrinth, you would place walls, enemies, goal, obstacles, traps, chests.

#

A designer would define rooms, scripted event, action base on event.

#

Those would all be define as serialized data.

#

They would be part of the GameState, however, they would it would be hard and exhaustive to include them all in the particular state.

#

A type of game that work really well with what you want, would be a tabletop game.

queen onyx
#

I mean some of it would absolutely

#

but not all of that

red bear
#

Did you state it was the only truth ?

#

That it was trying to centralized everything.

#

To reduce the amount of component

queen onyx
#

Only the stuff that you actually need to keep track of

red bear
#

Divide by what is being modifed and what is listening

#

How would you define what needs to be tracked ?

#

A player always need to know where the walls are.

#

Those would be part of the state.

queen onyx
#

Well if your wall never changes and is immutable, you dont need to keep track of that

red bear
#

Some are, others are not.

queen onyx
#

Its only the mutable stuff you need to bother tracking and you can safely also ignore the things already baked into your MonoBehaviors (Transform primarily)

red bear
#

So, you would ignore the chest the designer placed ?

queen onyx
#

I would only track if its opened or not probably

red bear
#

And you would track it from the game state ?

#

Not the chest.

queen onyx
#

yeah prolly just a bool is all you need, thats the part that mutates, IsOpened or whatever

#

though for chests, assuming its not randomly generated, Id just store all the chests in a bitmask

red bear
#

I have implemented a chest in a production game.

#

It starts with just that

#

Then it finish with a lot of state

queen onyx
#

well is it a moveable chest?

red bear
#

A chest can be unlocked, or locked

#

A chest can be visible or invisible

#

A chest can be trackable or not trackable

queen onyx
#

yeah thats all fine, those are things I would store in Gamestate too

red bear
#

The list goes on

queen onyx
#

Would just have a ChestState object for each chest, which holds that data

red bear
#

No, the Chest MonoBehavior contains it states

#

It is serialized by the designer

#

Which define what the inital state

#

Then the chest save its own state whenever we close the game. (Or not)

#

Depending on what is wanted.

queen onyx
#

It largely boils down to whether I wanna be saving this data or not

red bear
#

So, the game state is only the save of the game ?

queen onyx
#

Well the two go hand in hand

red bear
#

What is persistent ?

#

What more it is then ?

queen onyx
#

Ideally the two go hand in hand

#

If done right it should be as simple as serializing your gamestate to the file, and you should be able to deserialize it "as is" and have the exact same game to the degree you care to

red bear
#

This is what you would do eitherway for a save game.

queen onyx
#

like if you dont care that reloading the game resets positions of stuff or perhaps items dropped on the ground vaporize, then those are things Id be fine with leaving on the MBs and not storing in the game state

red bear
#

Ideally, a bit more flexible then 1 files though.

#

What else does it do ?

queen onyx
#

The sort of litmus test is simply whether you need to save it or not

#

Like what animation is currently playing for a given object, Id say that doesnt need to be serialized and is a "glass box" property of the MonoBehavior

#

so Id put that on the MB, not the GameState

red bear
#

I understand that, but at the same time I do not see what can it give you if you follow that rules.

#

It is only a save file

#

You cant use it as a "GameState" if you do not consider the non-persistent data.

#

They still have an impact on the state of the game. Defining it as well.

queen onyx
#

So just as an example...

Lets say you have multiple conceptual distinct things that can mutate the Player's Health (poison debuff, taking an enemy hit, stepping on a trap, casting cure spell, and resting)...

And you have multiple conceptual distinct things that care about what your health currently is (health bar, text that shows health as a number, inventory screen also shows your health, when health is low it plays a sound, and when your health hits zero you wanna trigger death scene)

Without a pub/sub model, what would be your plan to wire all that up?

red bear
#

By using an Observer (pub/sub) pattern.

#

I would not do it otherwise

queen onyx
#

okay so, you would use a pub sub model

red bear
#

Yes

#

Not sure how it is relevant to the GameState

#

I'm sure you gonna link that to make it obvious

queen onyx
#

my GameState is just the name for where my PubSub model lives, thats all there is to it

red bear
#

So, your gamestate is not a state

#

Because Pub/Sub are not state

queen onyx
#

it is, thats what INotifyPropertyChanged does

red bear
#

Also, it would be way more convient to define multiple pub/sub.

queen onyx
#

you have properties, and your "pub" is the setters, and your "sub" is the PropertyChanged event

queen onyx
red bear
#

And, you should always prioritze observer over pub/sub.

#

The difference being that only the observable can publish

red bear
#

You may have multiple channel or whatever we call that.

queen onyx
#

There is for INotifyPropertyChanged, its just the interface for doing it

#

instead of calling methods the setters of the props are the publishers

#

You trigger a publish literally by just doing GameState.SomeValue = SomeNewValue and it will trigger the publish event (called PropertyChanged) out to all subscribed

red bear
#

Also, the channel needs to be as general at it can be.

#

By example, you better have channel for ALL the health modification.

#

Which is only use for those that actually wants to see most of the health modification.

#

Otherwise, you simply use an event.

queen onyx
red bear
#

Didnt you say that you had PlayerHealth as a properties (channel) in GameState ?

queen onyx
#

Sure, you could have something like GameState.Player.CurrentHealth

red bear
#

Don't you find it to much precise

queen onyx
#

What do you mean?

red bear
#

How many variable do you think you gonna have there.

#

In the player (which terribly design), we have maybe 500 variables.

#

Only on the player

queen onyx
#

You can further subdivide as you need

red bear
#

Yeah, needing to replicate most of what you have done if you subdivide the player.

queen onyx
#

GameState.Player.Stats.CurrentHealth and then you could have GameState.Player.Inventory[0] or whatever, as you need

#

(Though arrays can be tricky, its possible but takes a bit of know-how to do it)

red bear
#

You gonna need to have a MonoBehavior eitherway. So, you gonna have a replication of the player in the GameState ?

#

Also, where the data resides if your GameState is only a pub/sub

queen onyx
#

Nah, I would just have say a SetText on the PlayerHealthText object

queen onyx
red bear
#

The truth would be on the PlayerHealthText.

#

Not in the game state

queen onyx
#

I would only expose a simple SetText method or whatever on the game object, it should be extremely small and simple

red bear
#

Alright, I propose you try it out.

queen onyx
#

Already implemented, its quite smooth feeling

red bear
#

It won't scale well

queen onyx
#

what makes you say that?

red bear
#

Maybe you will not scale as much as have experience.

#

First, the data should always be closer to the owner. OOP basics

queen onyx
#

Mate have you never used a database before? XD

red bear
queen onyx
#

Your data can be in an entire different system running on an entire different server in an entire different country

#

Let alone even in the same codebase XD

#

And the data is close to the owner, its inside of it

#

The GameState is the owner

#

If you have like 5 things that all care about your health in the UI, how do you pick one of them as the "owner", thats silly

red bear
#

No, that is what we call an Anemic Domain Model.

#

Terrible idea.

#

You should read on it.

red bear
queen onyx
#

You have to go hunting around to find which one actually has it? Sounds like a huge pain

red bear
#

What do you mean ?

#

The player has health.

#

This is literally a business rule.

#

No ambiguity.

queen onyx
#

So you what, have to drop an entire reference to the whole player into your health bar just for the 1 value it actually needs?

red bear
#

No, you use interface or component

#

Instead or event at the worst

queen onyx
#

So you need to have like 30 interfaces on the player for all its pieces?

red bear
#

Not pub/sub.

#

An event.

queen onyx
#

Also interface doesnt magically change the fact you have to ref the whole player, you've just put a mask on it

red bear
#

If you have system that wants to know every health of all enemies, that you consider a pub sub as well.

queen onyx
#

The issue here is simple, the players "health" doesnt have an x/y/z position, or a rotation

#

So I see no reason for it to be on the game object

red bear
#

The player does though.

queen onyx
#

The player does yes, but not the players health

#

The only stuff that belongs on an MB as far as I can tell, is stuff that "exists" in the scene that actually has a position/rotation/etc

red bear
#

In Unity, GameObject always have a transform. Not something that is necessary, but it is like that.

#

The amount of GameObject that are not truly in the world is astonishing.

#

The amount of performance and memory lost is none.

queen onyx
#

Since player health is an ambevilant conceptual thing and doesnt "exist" in a "position" in the scene, I see no reason to bind it to a game object

red bear
#

Heck, we even use GameObject as seperator.

queen onyx
#

Nah I honestly find it to be weird to put stuff on game objects that have no need to "exist" in space in the scene, it makes no sense

red bear
#

You might find it that way now, however you gonna change you perspective.

#

All asset are made this way.

#

All Unity is made this way.

queen onyx
#

I see no reason for my character's stats to have an X/Y/Z position

red bear
#

They do not truly have a X/Y/Z positions, they reside on a GameObject that has a X/Y/Z position by default

queen onyx
#

which makes no sense, thats still giving em an X/Y/Z position

#

They have no reason to exist in "space"

red bear
#

Anyway, this is how it works. This is how everythings has been made in Unity.

queen onyx
#

No... you can just use a POCO

red bear
#

You gonna work against the engine.

queen onyx
#

I dont see how its "against" it

#

Unity has no issue with POCOs

red bear
#

No, if you do that you not gonna benefit from the serialization and compartmentalization that come with MonoBehavior.

#

They is no object that takes a POCO as reference in Unity.

#

All reference are UnityEngine.Object.

#

You cannot drag and drop a POCO.

queen onyx
#

Why would you?

red bear
#

Because, you need to setup your reference

queen onyx
#

Nope, you dont

red bear
#

How do you know which door to open ?

#

By referencing it.

queen onyx
#

once you decouple away from putting everything on MBs, you dont need to do that anymore

queen onyx
red bear
#

Yes, from an MB.

queen onyx
#

You invert the entire thing, your POCOs own the MBs, not the other way around

#

when done this way, not a single one of your MBs need to know anything about the surrounding world

#

they all just operate as glass boxes

red bear
#

What do you mean.

#

How would you setup which VFX to play when your chest opens ?

#

As a non-programmer

#

Without using custom editor

queen onyx
#
public class PlayerHealthBar : MonoBehavior {
   ...

   private int _health;
   private int _maxHealth;
   public void SetHealth(int health) {
       health = health;
       Redraw();
   }

   public void SetMaxHealth(int maxHealth) {
       _maxHealth = maxHealth;
       Redraw();
   }

   private void Redraw() {
       this.TMP.Text = $"{_health}/{_maxHealth}";
   }
}
#

Note how designed this way, this monobehavior doesnt "get" the health, it just exposes logic for setting the values and redrawing

red bear
#

Do not store a copy of _health though.

queen onyx
#

it has absolutely no concept of the outside world, it exists in a glass box

queen onyx
red bear
#

It would be exactly the same, except that it would have reference to something that has health.

queen onyx
#

but I just would have all my MBs exist in "glass boxes", they dont look outside, all they do is stay in their lanes and handle their own domain of what is drawn on the screen

red bear
#

And what better way to setup the whole thing that with a good old serialize field.

queen onyx
#

they dont "get" stuff, they have stuff passed into them

red bear
#

They have stuff serialized into them.

#

You gotta use the serialization.

queen onyx
red bear
#

Its there for that.

queen onyx
#

Nope, hard pass

red bear
#

The health bar only care for 1 health.

queen onyx
#

I see no reason to use that

red bear
#

For now.

#

And I'm serious

queen onyx
#

I only use it for serializing "glass box" values within the given object

red bear
#

You gotta hate your life.

queen onyx
#

nah its very slick

red bear
#

Always fight agaisnt Unity.

queen onyx
#

Its not "against" it at all, it works quite smoothly

red bear
#

No other library will work this way.

queen onyx
#

what? INotifyPropertyChanged?

#

Thats the backbone of a bunch of Microsofts stuff lol

red bear
#

Do you know Netcode for GameObject works ?

#

Or Photon

#

Or many other directly online library made for Unity.

#

They all have a NetworkObject/Identity

#

Which is component

#

That resides on a GameObject

#

And have no need of a position

#

This is how things are done.

queen onyx
#

Yeah I know a lot of people like to weirdly slap an X/Y/Z position on stuff that doesnt need it

#

thats not gonna convince me its a good idea lol

red bear
#

It is not a lot of people, it is the people that made the engine.

queen onyx
#

So? The person who invented the rubiks cube isnt the person I would talk to about how to speedcube, I would talk to the world champion of speedcubing

red bear
#

Not at all the samethings. The people that made the engine made choice in function of what is intended to be used at.

queen onyx
#

Just because they intended something to be some way doesnt mean its the right call

red bear
#

Doing the opposite is only working agaisnt what they suppose that you should do.

queen onyx
#

How many world first speedrunners were also the game developers who made the same game?

red bear
#

Meaning, that the tool the made will not be available for you.

queen onyx
#

Like are you gonna ask the game developer how to beat the game better... or someone whos actually good at beating the game?

#

theres tonnes of game devs that struggle to beat their own games well, so they arent the person I will ask about how to play their game "right"

red bear
#

Yeah, and there is also a lot of people that utilize "bad build" that were not made for what they should do

#

You do not heal with a DPS

#

Nor tank with a healer

queen onyx
#

Well... the point here is "who" is saying that

red bear
#

The developper say that.

queen onyx
#

If the game devs go "tanks shouldnt be healing" but the seasoned players are going "Cool story but thats the meta right now and optimal way to do it", who do you think knows better?

red bear
#

Yeah, if it is not.

#

Like in the situation.

queen onyx
#

Then both are in agreement lol

red bear
#

Where you forfeit all the advantage

#

For no gain.

#

Anyway, you can do whatever you want.

#

It is starting to be late.

#

Was fun to have an other view point.

#

I greatly encourage you to continue.