🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Modularizing my code / Not making everything depend on everything. HELP

Started by
7 comments, last by Kylotan 4 years, 6 months ago

Heyyy,

so I understand that you should program in a way that lets you reuse code and not make scripts depend on others. But my biggest problem/question is: how do I achieve that?

Example:

Currently in my game the player can attack an AI and kill it or die. Now my Health script knows when I'm dead but idk what's a good way to tell my combat or movement script that my player has died?


Thanks for any help!

Regards,

Erik

Advertisement

Or How do I tell my AI that the player is dead and it should stop following him and stop attacking him. If there are any good resources out there that I missed please let me know.

This is a question of how to design your game loop instead of making your game code modular. Making it modular means to separate contextual code from each other, for example adding all the AI code into one module, asset code into another and so on.

You have several possible solutions to make your game loop more flexible: ECS, Events, State Maschine to name some. I personally prefer some kind of Data Driven Design in an asynchronous scenario and balanced by events

What are these scripts you mention? What data structures and communication mechanisms do they share? Is there a game engine that manages the execution of your scripts in the proper sequence and the updates of game state data structures?

For example, a RPG-like game engine could execute attacks for the current turn of combat one by one, automatically check whether someone dies and automatically skip their actions because they are flagged as corpses.

Omae Wa Mou Shindeiru

Hi Erik,

Generally you want to structure your program so that general stuff owns specific stuff, e.g.:

  • Application has a Game
  • Game has a World
  • World has several Characters (AI or Player)
  • Characters have a Health

...and so on. When information travels in this direction, you can usually just push it in via function calls, e.g. the standard way.

To move information in the other direction, you have several options, including:

  • Return values - sometimes you can simply call a function to get a value. e.g. The player object can call Health.GetAmount to find out how much health he or she has left.
  • Back-references - occasionally it makes sense for the owner to pass a reference to itself into the object. e.g. The World owns all the Characters, but when it creates them, it might also store itself inside the Character. So the Character can call functions like World.NotifyOfMovement to tell the world it's moved. Circular references like these can be tricky to manage properly in some programming languages and can cause compile errors or memory leaks.
  • Events/signals/observers/etc - there are a lot of different names for this concept but usually it allows one object to 'observe' an event, and other objects can notify when the event has happened, and all the observers get that notification. For example, it's common for character animations to have events that trigger when the foot is on the floor, and other parts of the game can subscribe to this event - the audio manager plays a footstep sound when it receives the event, whereas the particle system manager shows a puff of dust.

So in your specific case, there are a few ways to consider:

  • Every frame, the Character asks the Health component "am I dead?" by calling a function on it which returns a boolean, or similar.
  • Pros: very easy to implement.
  • Cons: you don't get notified of the death until the next time you call that update. You might lose information (e.g. if 2 people deal damage, simply asking "Am I Dead" won't give you that information.
  • The Health component gets a reference to its owning Character when its created, and when that Health realises it should be dead, it can call a function on the Character to say "hey, we're dead".
  • Pros: easy to implement. Death is notified instantly. Function call can contain meta data as an argument (e.g. "who killed me").
  • Cons: circular reference could cause problems.
  • Health components get an OnDead event, which the owning Character subscribes to. The Health component fires the event when it realises it should be dead, and the Health component gets the notification and handles the event accordingly.
  • Pros: Health component doesn't need to know anything about Characters. Other components can subscribe to the event for more flexibility. Event notification can contain meta data as an argument (e.g. "who killed me").
  • Cons: More complex to implement. Flow of logic may not be as clear as the previous options.

Game scripts and game objects should be separate. All scripts should have access to all objects. For example, there should be a player-related script that has access to both player object and the NPCs' objects. There should also be NPC-related scripts that have access to both player and all NPCs' objects. This way any script can operate on anything. This greatly eases development.


VoxycDev said:

Game scripts and game objects should be separate. All scripts should have access to all objects. For example, there should be a player-related script that has access to both player object and the NPCs' objects. There should also be NPC-related scripts that have access to both player and all NPCs' objects. This way any script can operate on anything. This greatly eases development.


"Any script can operate on anything" might be a convenient way to set things up when prototyping, on when working on a smallish project. if the project grows beyond some threshold though, you can easily have a maintenance nightmare in your hands, because now any part of the code might change any part of the state, therefore every part of the code depends on every other part of the code.

alvaro said:
VoxycDev said:

Game scripts and game objects should be separate. All scripts should have access to all objects. For example, there should be a player-related script that has access to both player object and the NPCs' objects. There should also be NPC-related scripts that have access to both player and all NPCs' objects. This way any script can operate on anything. This greatly eases development.

"Any script can operate on anything" might be a convenient way to set things up when prototyping, on when working on a smallish project. if the project grows beyond some threshold though, you can easily have a maintenance nightmare in your hands, because now any part of the code might change any part of the state, therefore every part of the code depends on every other part of the code.

Agreed. I see this in Unity projects a lot. A bug might get logged with the title "Player moves to (-123423, 0, -23346634) randomly" and you have no easy way of tracking it down because pretty much any code can just write `GameObject.Find("Player").transform = newPosition` and you have no way of knowing which of those lines it was.

This topic is closed to new replies.

Advertisement