Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Transform position is not moving in sync while using managed FixedUpdate

Discussion in 'Scripting' started by cdr9042, Sep 17, 2020.

  1. cdr9042

    cdr9042

    Joined:
    Apr 22, 2018
    Posts:
    169
    I'm trying to optimize moving hundreds of bullets by iterating through all the bullet and call the function to update their position based on their speed and direction stored in their values. But somehow, there are some bullets that lag behind. I don't know why they don't move in sync

    Here are videos, notice the left turret's bullets always lag behind other bullets
    https://imgur.com/a/xHYh4Np

    When I switch to use individual update by adding FixedUpdate() in the bullet's script, the bullets move in sync as expected. But if I do that, the performance becomes worse, I can only get to 200-300 bullets before the FPS is dropped on my phone.

    What am I doing wrong?

    Bullet system manager:
    Code (CSharp):
    1. public class BulletSystem : MonoBehaviour
    2. {
    3.     [SerializeField] BulletMove bulletPrefab;
    4.     public List<BulletMove> activeBullets = new List<BulletMove>();
    5.     public int activeBulletCount; //for debug
    6.     public delegate void OnBulletDespawn(BulletMove sender);
    7.     public static OnBulletDespawn onBulletDespawn;
    8.  
    9.     public static BulletSystem instance;
    10.     private void Awake()
    11.     {
    12.         instance = this;
    13.     }
    14.  
    15.     //turret simply call this function to shoot
    16.     public static void Shoot(BulletMove bulletPrefab, Vector3 origin, Vector3 shootDirection)
    17.     {
    18.         BulletMove newBullet = LeanPool.Spawn(bulletPrefab, origin, Quaternion.identity);
    19.         newBullet.Setup(shootDirection);
    20.         instance.activeBulletCount++;
    21.         instance.activeBullets.Add(newBullet);
    22.     }
    23.  
    24.     public static void HandleOnBulletDespawn(BulletMove sender)
    25.     {
    26.         instance.activeBullets.Remove(sender);
    27.         instance.activeBulletCount--;
    28.         LeanPool.Despawn(sender);
    29.     }
    30.  
    31.     private void FixedUpdate()
    32.     {
    33.         for (int i = 0; i < activeBullets.Count; i++)
    34.         {
    35.             activeBullets[i].UpdatePosition();
    36.         }
    37.     }
    38. }
    39.  
    Bullet movement:
    Code (CSharp):
    1. public class BulletMove : MonoBehaviour
    2. {
    3.     public float damage = 1f;
    4.     public float speed = 1f;
    5.     private Vector3 direction;
    6.     [SerializeField] Vector3 forwardDirection = Vector3.right;
    7.     [SerializeField] Collider2D m_Collider;
    8.     [SerializeField] DeathSystem deathSystem;
    9.     bool isDespawning;
    10.  
    11.     public Vector3 Direction
    12.     {
    13.         get => direction; set
    14.         {
    15.             direction = value;
    16.             //transform.rotation = Quaternion.LookRotation(Vector3.back, direction);
    17.             transform.rotation = Quaternion.FromToRotation(forwardDirection, direction);
    18.         }
    19.     }
    20.  
    21.     public void Setup(Vector3 direction)
    22.     {
    23.         Direction = direction;
    24.         isDespawning = false;
    25.         m_Collider.enabled = true;
    26.     }
    27.  
    28.     public void UpdatePosition()
    29.     {
    30.         transform.position = transform.position + Direction.normalized * speed * Time.fixedDeltaTime;
    31.         CheckDespawnPosition();
    32.     }
    33. [...]
    34. }
     
  2. cdr9042

    cdr9042

    Joined:
    Apr 22, 2018
    Posts:
    169
    I wonder if the order of calling the function is to blame

    The turrets' code:

    Code (csharp):
    1.  
    2. public class AutoShooter : MonoBehaviour, IShooter
    3. {
    4.     public BulletMove bullet;
    5.     public int bulletsPerShoot = 1;
    6.     public int magazineSize = 1; //how many time this weapon shoot before reloading
    7.     public float totalSpreadAngle = 10f;
    8.     public float fireRate = 0f; //shoot times per second
    9.     public float reloadRate = 1f; //speed of reloading magazine
    10.     public RotateStyle rotateStyle;
    11.     public float rotateSpeed = 1f; //positive for clockwise, negative for counter clockwise
    12.  
    13.     [SerializeField] AnimationCurve autoRotateAngleCurve;
    14.  
    15.     [SerializeField] Vector3 initialDirection = Vector3.down;
    16.     Vector3 currentShootDirection;
    17.  
    18.     float timerRotate;
    19.     float timerReload;
    20.     float timerShoot;
    21.     int shootTimesLeft;
    22.     WeaponState weaponState = WeaponState.Reloading;
    23.     WeaponCommand weaponCommand = WeaponCommand.Shoot;
    24.     public bool isShooting = true;
    25.  
    26.     [SerializeField] ShipBrain m_Ship;
    27.     [SerializeField] bool rotateTransform; //rotate transform with shoot direction
    28.     [SerializeField] float gunLength;
    29.  
    30.     float FireInterval { get { return fireRate > 0 ? 1f / fireRate : 0f; } }
    31.     float ReloadTime { get { return reloadRate > 0 ? 1f / reloadRate : 0f; } }
    32.  
    33.     Vector3 GunPosition { get => transform.position; }
    34.     Vector3 muzzleRelativePosition = Vector3.zero;
    35.  
    36.     private void Start()
    37.     {
    38.         shootTimesLeft = magazineSize;
    39.         if (m_Ship == null)
    40.             m_Ship = GetComponentInParent<ShipBrain>();
    41.         if (m_Ship == null) { Debug.LogWarning("No Shipbrain found"); }
    42.     }
    43.  
    44.     public void Shoot()
    45.     {
    46.         if (weaponState == WeaponState.Ready) { weaponState = WeaponState.Shooting; }
    47.     }
    48.  
    49.     void Shoot(int bulletID)
    50.     {
    51.         float angleBetweenBullet = bulletsPerShoot != 1 ? totalSpreadAngle / (bulletsPerShoot - 1) : 0f;
    52.         Vector3 bulletDirection = Quaternion.Euler(0f, 0f, -totalSpreadAngle / 2f + angleBetweenBullet * bulletID) * currentShootDirection;
    53.         Shoot(bulletDirection);
    54.     }
    55.  
    56.     public void Shoot(Vector3 direction)
    57.     {
    58.         BulletSystem.Shoot(bullet, GunPosition + muzzleRelativePosition, direction);
    59.     }
    60.  
    61.     public void SetState(WeaponCommand state)
    62.     {
    63.         weaponCommand = state;
    64.     }
    65.  
    66.     public void Reload()
    67.     {
    68.         timerShoot = 0f;
    69.         weaponState = WeaponState.Reloading;
    70.         timerReload = ReloadTime;
    71.     }
    72.  
    73.     private void FixedUpdate()
    74.     {
    75.         if (rotateStyle == RotateStyle.AimClosest)
    76.         {
    77.             if (m_Ship != null && m_Ship.team == GameTeam.Enemy && GameShmupController.instance.playerHealth != null)
    78.             {
    79.                 Vector3 aimDirection = GameShmupController.instance.playerHealth.transform.position - GunPosition;
    80.                 currentShootDirection = Vector3.RotateTowards(currentShootDirection, aimDirection, rotateSpeed * Time.fixedDeltaTime, 0.1f);
    81.             }
    82.             /*else
    83.             {
    84.                 Debug.LogError("Unable to aim");
    85.             }*/
    86.             RotateTransform();
    87.         }
    88.  
    89.         switch (weaponState)
    90.         {
    91.             case WeaponState.None:
    92.                 break;
    93.             case WeaponState.Reloading:
    94.                 if (timerReload > 0f)
    95.                 {
    96.                     timerReload -= Time.fixedDeltaTime;
    97.                 }
    98.                 else
    99.                 {
    100.                     shootTimesLeft = magazineSize;
    101.                     weaponState = WeaponState.Shooting;
    102.                     goto case WeaponState.Shooting;
    103.                     //StartCoroutine(CoShootMagazine());
    104.                 }
    105.                 break;
    106.             case WeaponState.Ready:
    107.                 if (weaponCommand == WeaponCommand.Shoot)
    108.                 {
    109.                     weaponState = WeaponState.Shooting;
    110.                     goto case WeaponState.Shooting;
    111.                 }
    112.                 break;
    113.             case WeaponState.Shooting:
    114.                 if (weaponCommand == WeaponCommand.NoShoot)
    115.                 {
    116.                     weaponState = WeaponState.Ready;
    117.                     return;
    118.                 }
    119.                 if (shootTimesLeft > 0)
    120.                 {
    121.                     UpdateShootDirection();
    122.                     if (timerShoot > 0f)
    123.                     {
    124.                         timerShoot -= Time.fixedDeltaTime;
    125.                     }
    126.                     else
    127.                     {
    128.                         //Shoot(bulletsPerShoot - shootTimesLeft);
    129.                         for (int i = 0; i < bulletsPerShoot; i++)
    130.                         {
    131.                             Shoot(i);
    132.                         }
    133.                         shootTimesLeft--;
    134.                         timerShoot = FireInterval;
    135.                     }
    136.                 }
    137.  
    138.                 if (shootTimesLeft <= 0)
    139.                 {
    140.                     Reload();
    141.                 }
    142.                 break;
    143.         }
    144.     }
    145. }