Sports game - how to separate game logic and visuals and multi-threading.

Hello kind Unity folk,

I have made a small little sport simulator. Nothing special, but it has players kicking a ball around, scoring goals etc.

There are three main components: a ballController, gameController and a playerController for each player. Each component has a script controlling the logic (AI, actions, ball trajectories etc) and also a visual object (the ball, and for now just little circles for the players).

I would like to do two things:

  1. Separate the game logic scripts from the visual representation, such that the game can be simulated in the background. When people play games like Football Manager, Out of the Park Baseball, etc they often simulate a lot or all of the game.

  2. Once that is settled, I want to be able to launch multiple instances of the game engine to simulate multiple games at once, preferably in different CPU threads. This is so the user could manage and play with one team in the league and then all of the other games are simulated in the background.

Can you please give me a few tips for implementing one or both of the above? I managed to do both many years ago when I was programming with Visual Basic but I don’t know my way around Unity as well.

Thanks in advance :slight_smile:

You might take look at the Model-View-Controller design specification. Basically you separate out the data (Model) from the visual representation of it (View), and mediate between the two with a Controller. This way you can act on the data with a controller and only render it to view based on the state of the controller.

To make this a bit more concrete:

Suppose you have a racing game. You have a list of cars, with their stats and records. You have a list of tracks, conditions on those tracks, and their length. This is all data held in the Model.

Now you also have models, textures and animations for cars, tracks, and drivers along with UI for a player to drive an actual car. This is all view stuff.

There would be a number of controllers, to control different kinds of gameplay. To start you would have a RaceController which picks cars and driver from the roster, selects a track, and starts the race. It would have assigned CarControllers to every single car, which control how the car moves around the track and handle physics between cars (I’d abstract out the AI code. If you do, you can just plug the PlayerController in later to take control of a car). They would have the ability to pass their position, state and other necessary variables to a CarView which would update the model position and visual data on the track. When the race is over, the RaceController could update a file containing season data, which could in turn be used to update car stats and future races (ie if there is a bracket).

Finally the RaceController could be called up by a GameView script that would instantiate all the Cars, the Track and pull data from the RaceController to update the view. At this point you have the ability to run games invisibly, or watch one. Its here that I’d build in the ability for the player to take control of a specific car, or for the player’s car to be inserted into a race. You’d have build all the functionality for moving a car, and just be providing inputs to it from a different source than the AI.

The big problem I see here is the physics. I don’t know how I’d go about abstracting out Unity’s physics engine for cars (or a football). As for putting them on separate threads, basically if you create a static function in RaceController called PlayRace(data), you can tell that to run on its own thread.

A much simpler way of doing all this could just be to run games the player isn’t watching purely statistically, and only play out games the player actually plays in/watches.

These are ways I’d go about it. Might be someone has a better answer. Hope this helps though!

Hey mate,

Thank you very much for your help. I’ll have a look at the Model-View Controller you mention. The scenario you describe seems like what I would like.

R.e. physics there basically are no physics in the game. All player and ball movements and trajectories are directly controlled in the code, not calling on any physics processes. Hopefully this means I avoid the problems you mention.

R.e. running other games statistically I would really like to avoid this. You would essentially have two match engines and consequently the way certain teams/players behave in one is unlikely going to be perfectly replicated in the other.

It’s possible I would have to resort to that if I manage to do everything I want but ultimately find that the simulation is too slow to have dozens run in the background in a reasonable timeframe, but hopefully not…

So I’ve managed to set things up such that you can instantiate gameControllers, which within contain players, the ball etc. And you can do this multiple times and have multiple games all running simultaneously, with the players only interacting within each gameController, not across games.

My question now is r.e. multi-threading. The two games are being run simultaneously, but is it optimised? Is this like an automatic multi-threading or is there something else I should be doing? Given most people have multi-core CPUs these days it’d be nice to make the most of it. This is particularly since things get fairly laggy if many games are run at the same time.

If you intend to do multi-threading, note that Unity API is not thread safe. Therefore, it is best to use nothing from Unity to implement the model of your game. Aside multi-threading, this would also have the advantage that you could run a simulation of your game independently from Unity. Your game would be an independent module, a game engine by itself, and Unity would be a rendering backend.

Hey mate,

What are the issues with it not being thread safe, as long as I make sure the code does not have any calls between threads?

I’m also curious about the speed. When I have multiple things launched simultaneously in Unity does Unity automatically try to distribute them amongst multiple CPUs or is it not that clever and I’d need to do something manually?