Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Implementing RTS/4X Game Time System

Discussion in 'Scripting' started by Talthilas, Nov 26, 2017.

  1. Talthilas

    Talthilas

    Joined:
    Apr 1, 2014
    Posts:
    44
    Hi guys,


    I have a question about implementing 'time' in my game. The game is a single-player 4X/RTS similar to Stellaris. In terms of time, after a certain amount of real-time has passed, a new 'day' (or 'turn' if you prefer) starts in the game. At the moment, I have implemented a separate fixed update loop that will update certain state in the game. This loop counts in 'ticks', with there being 'X' ticks to a day/turn. So say I make the fixed timestep of my loop to be 100ms, and the 'ticks per day' to be 10, then a new day starts after 1second of real-time. The state that gets updated on each tick in this loop are things like unit build progress, checking if certain conditions has been met, AI updates etc. I have created this separate fixed update loop rather than using unity's fixed update, as I intend to keep unity's fixed update for moving objects with colliders etc. in the game (i.e. physics stuff). I also think that fixed update is too tight a loop to use for updating the state I mentioned above (I know you can change the timestep in settings but as I mentioned I want to keep fixed update for physics stuff). I have been referencing 'witters' and 'gameloop' for the implementation of this fixed update loop.

    So anyway, all of the above is not too much trouble in itself, but the real trouble I am having is implementing game speed (i.e. making game time progress slower or faster). What I'm trying to do sounds like a common problem a lot of these style of games must face, so I imagine there are some tried and tested ways of approaching this which I am hoping you guys can share with me.

    I have tried two methods of implementing game speed, and they both 'work', however the game is in its early stages and is not taxing the CPU too much at this stage. I will explain each method I have implemented, and if you guys could give some feedback or suggest a better way to do it, it would be much appreciated.

    1. Change the 'ticks per day' value when the player changes the speed of the game. This seems like an easy solution, but it comes with some difficulties. At the moment, the 'ticks per day' variable is an integer value (let's say its equal to 10 for explanatory purposes). On each game tick, the tick count gets incremented by one until the 'ticks per day' value has been reached. At this point the 'day ended' event is sent and the tick count is reset to zero. Since each tick occurs at a fixed timestep (let's say 100ms for example), then by changing 'ticks per day', you are effectively changing the real-time that passes between consecutive game days. On each tick, state is updated in the game according to the 'game time i.e. days' that has passed each tick. Since there are a certain amount of ticks to a turn, each tick essentially progress the game by (1 / ticksPerDay) days (e.g. 1/10 =0.1days/tick). To make the game progress twice as fast that its current setting, we can change 'ticks per day' from 10 to 5. This now means that each turn takes 0.5secs and each tick is now worth 1/5=0.2days/tick. Seems easy right? Well there is a problem. Let's say the current ticks per turn value is 10, and the current tickCount is 9. Let's say the player wants to speed up the game at this point by x2. Ticks per turn becomes 5, and the tickCount becomes 4.5. This is where the problem lies. Ticks count in integer values, so you can't have a decimal value for tickCount. The way I thought of handling this situation was to wait until the tickCount reaches an even number before implementing the speed change. This means tickCount will always remain an integer value, however it only holds in the very specific case that when you implement speed changes by halving or doubling the current speed of the game (as that way you will always end up with a integer value for tickCount). This is okay for my game, but it seems like a limiting solution and doesn't sound like it would get used in practice.
    2. My preferred solution. Change the fixed time step between ticks when the player changes speed. This is pretty self-explanatory. All we are doing is having the ticks occur as smaller intervals to speed up the game, or longer intervals to slow down the game, with each tick progressing the game by (1 / ticksPerDay) days as outlined in item 1 above. This is easy to implement, however the concern would then be CPU processing power. Obviously the more frequent updates you have, the more work your CPU has to do. My thinking is that so long as you keep the time step above a minimum reasonable value, and that value is enough to update the game at an acceptable rate, then it seems the way to go. You would of course have to ensure that the timestep is greater than the time it takes to process a game state update, but for the things I want to update in my fixed timestep (see first paragraph), this is probably achievable.

    Anyway let me know what you guys think. Appreciate any advice.
     
    Last edited: Nov 27, 2017
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Set the time scale appropriately and continue to tick as normal. Your tick time accumulator will naturally scale with the time scale. Make sure you set the fixed timestep when you set the time scale as well or your physics will be running at a different rate.

    Code (csharp):
    1.  
    2. class TickManager : MonoBehaviour
    3. {
    4.    private const float tickRate = 0.1f; // tenth of a second
    5.  
    6.    private float accumulator = 0f;
    7.  
    8.    private void Update()
    9.    {
    10.        accumulator += Time.deltaTime; // will respond to changes in time scale
    11.  
    12.        if(accumulator >= tickRate)
    13.        {
    14.           accumulator -= tickRate; // note: not = 0, very important so we don't clip remainder
    15.  
    16.           // tick each object that runs on the tick timer
    17.        }
    18.     }
    19. }
    20.  
    I'm guessing updating your colonies and what not isn't very taxing, so your tick updates really shouldn't matter if they're running 4x as often. If the AI is sufficiently complex, I would run it on a secondary tick that runs on opposite frames or run just a little bit of it every frame. The AI shouldn't really care about the player's preferred game speed.

    Will physics performance suffer at 4x game speed? Impossible for us to tell you. It depends on how many objects you're simulating and the complexity.
     
    Last edited: Nov 27, 2017
    Talthilas likes this.
  3. Talthilas

    Talthilas

    Joined:
    Apr 1, 2014
    Posts:
    44
    Thanks @GroZZleR

    I updated my custom fixed update loop to scale deltatime by my timescale and its all working well now. Quite a simple solution really so thanks for that.

    For those interested, a great custom fixed update loop script can be found here. I use this to implement something similar to what GroZZleR was doing above.
     
    GroZZleR likes this.