Search Unity

RTS unit's update loop order problem

Discussion in 'Scripting' started by litebox, Aug 6, 2018.

  1. litebox

    litebox

    Joined:
    Aug 29, 2011
    Posts:
    158
    Hello, Unity folks,
    I'm working on some kind of strategy game and have issue with units' update order.
    Let's say I have two units Unit Red and Unit Blue, both of them are instantiated one after another in space. Then both of them go towards each other (via FixedUpdate rigidbody position). And both of them have an Update method, which is:

    Code (CSharp):
    1. private void Update()
    2. {
    3.   RaycastHit hit;
    4.   if (Physics.Raycast(transform.position, transform.forward, out hit, _attackDistance, _attackLayerMask))
    5.   {
    6.   _target = hit.transform;
    7.   }
    8.   if (_target != null)
    9.   {
    10.     Unit unit = _target.GetComponent<Unit>();
    11.     unit.hit();
    12.   }
    13. }
    14.  
    The problem is, unit which was spawned first always hits second unit:

    Spawn unit = "Unit Blue"
    Spawn unit = "Unit Red"
    "Unit Blue" found a target "Unit Red"
    "Unit Blue" hit a target "Unit Red"



    No matter how many times I'm launching this simulation result is the same. I can understand why this happens, but I cannot figure out how to fix this.

    What I'm trying to achieve is when two units approach eath other (both was moved in FixedUpdate and have ability to hit each other at next Update), system adds random factor which will guarantee units have 50% / 50% chances to hit one another, while now it is 100% / 0% chances.

    Currently the only solution I can see is to add all units to List, and do own custom update flow, after which I reorder List and go on:
    Code (CSharp):
    1. private void Update()
    2. {
    3. foreach (var unit in _unitList)
    4. {
    5. unit.customUpdate();
    6. }
    7. _unitList.Sort(randomizeList);
    8. }
    9.  
    But I think there should be more elegant way to achieve this,
    Thanks!
     
  2. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Depending on the depth and style of AI, you could always implement a reaction speed for them. So they will only react to the change of data (i.e within range) based upon a reaction speed timer of how quickly they react. At the start of play each unit could have a random amount of reflex. Just one option of many :).
     
    litebox likes this.
  3. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Assuming both have same detection ranges then isn't your problem the fact that weapon usage time is instantaneous.

    Try something like:
    Target acquired -> shoot pending = true -> return (end the update there.)

    and at the start of the Update :
    if(shoot pending) -> don't check whether there is a target, just shoot.

    This way both should shoot and both should hit. then you can add some delay to shooting process.

    (2nd update cycle)

    and at the start of the Update :
    if(shoot pending && random reaction time (that may have basis in unit's rank *nudge nudge wink wink*) has passed) -> don't check whether there is a target, just shoot.
     
    litebox and Nigey like this.
  4. litebox

    litebox

    Joined:
    Aug 29, 2011
    Posts:
    158
    Yup, good idea.
    I divide Update method to Raycast and Action methods, if Raycast returns target, I create random delay interval (e.g. 0.25f + Random.value * 0.25 sec). So the Action is called only when this random delay is finished, and at this point any of units could hit first. Also this brings "agile" idea to unit's properties, with which I can tune how quick units hit or move.
     
  5. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Rather than directly resolving hits, I would use a secondary combat resolver that accepts all hits across the entire frame and then resolves them at the end of the frame. No misses or randomness and you can trade killing blows if they happen at the exact same time this way.
     
    litebox and Madgvox like this.
  6. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    I second @GroZZleR here. A bit of randomness would also be beneficial. If your unit has a swing/shoot timer, and that timer varies depending on its stats (accuracy, speed, whatever) then as the units clash there will be more even results.
     
    litebox likes this.
  7. litebox

    litebox

    Joined:
    Aug 29, 2011
    Posts:
    158
    Combat Resolver is an interesting idea. I replaced MonoBehavior's Update method per unit by my own UnitManager's update loop through all units. This gives me ability to split update on two separate parts:

    unit.updateActions(); - which is raycast and do calculations (accumulate overall damage) - odd frame
    unit.commitActions(); - which is called later and applies all changes to every unit - even frame


    Code (CSharp):
    1. private void Update()
    2. {
    3.     if (Time.frameCount % 2 == 0)
    4.     {
    5.         // collection could be modified at this point
    6.         for (int i = _unitList.Count - 1; i >= 0; i--)
    7.         {
    8.             Unit unit = _unitList[i];
    9.             unit.commitActions();
    10.         }
    11.     }
    12.     else
    13.     {
    14.         foreach (var unit in _unitList)
    15.         {
    16.             unit.updateActions();
    17.         }
    18.     }
    19. }
    This resolves issue when two units hit each other at one frame (situation is rare after adding random delay between raycast and actual hit, but still occurs from time to time), which is exactly what I need.