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

Issue with object pooling and bullet physics

Discussion in 'Scripting' started by Emmsii, Nov 17, 2015.

  1. Emmsii

    Emmsii

    Joined:
    Feb 27, 2014
    Posts:
    9
    I've started to implement some object pooling for my game, I've got pooling working for multiple object types. I'm having some issues with my pooled bullets.

    First issue:
    After the first 'batch' of pooled objects are fired and deactivated, any new bullets found in the pool have a weird spin to them.



    Here's the code attached to each bullet object:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class BulletController : MonoBehaviour {
    5.  
    6.     public float speed = 25f;
    7.     public float life = 5f;
    8.    
    9.     public ParticleSystem bulletHit;
    10.    
    11.     private Rigidbody rb;
    12.  
    13.     void Awake(){
    14.         rb = GetComponent<Rigidbody> ();
    15.     }
    16.  
    17.     private void Remove(){
    18.         gameObject.SetActive (false);
    19.     }
    20.  
    21.     private void OnEnable(){
    22.         rb.velocity = speed * transform.forward;
    23.         Invoke ("Remove", life);
    24.     }
    25.  
    26.     private void OnDisable(){
    27.         CancelInvoke ();
    28.     }
    29.  
    30.     void OnCollisionEnter(Collision other){
    31.         Remove ();
    32.     }
    33. }
    Each bullet is created with this line in my turret shoot script.

    Code (CSharp):
    1. GameObject bullet = ObjectPool.instance.GetPooledObject("Bullet");
    2. //Position where bullets will spawn, at the end of the barrel
    3. bullet.transform.position = t.position;
    4. //Direction of the target from the barrel
    5. bullet.transform.rotation = Quaternion.LookRotation(dir);
    6. bullet.SetActive(true);
    Second problem:

    I have a trail renderer attached to my bullets (hidden in previous image). It looks good, only there are weird stretched trails that appear.

    It looks like the bullet is traveling back down to the turret barrel after deactivating when hitting the target. I would assume the trail renderer is disabled when the bullet hits the collider.

    Here's my object pool script:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class ObjectPool : MonoBehaviour {
    6.  
    7.     public static ObjectPool instance;
    8.  
    9.     public GameObject[] objectPrefabs;
    10.     public int defaultPoolSize = 40;
    11.  
    12.     private Dictionary<string, List<GameObject>> objectPool;
    13.     protected GameObject containerObject;
    14.  
    15.     void Awake(){
    16.         instance = this;
    17.     }
    18.  
    19.     void Start(){
    20.         objectPool = new Dictionary<string, List<GameObject>> ();
    21.         containerObject = new GameObject ("ObjectPool");
    22.  
    23.         foreach (GameObject obj in objectPrefabs) {
    24.  
    25.             List<GameObject> objects = new List<GameObject>();
    26.  
    27.             for(int i = 0; i < defaultPoolSize; i++){
    28.                 GameObject newObj = Instantiate(obj);
    29.                 newObj.transform.parent = containerObject.transform;
    30.                 newObj.SetActive(false);
    31.                 objects.Add(newObj);
    32.             }
    33.  
    34.             objectPool.Add(obj.name, objects);
    35.         }
    36.     }
    37.  
    38.  
    39.     public GameObject GetPooledObject(string name){
    40.         List<GameObject> objs =  objectPool[name];
    41.  
    42.         foreach (GameObject obj in objs) {
    43.             if(!obj.activeInHierarchy){
    44.                 return obj;
    45.             }
    46.         }
    47.  
    48.         foreach (GameObject currentObj in objectPrefabs) {
    49.             if(currentObj.name == name){
    50.                 GameObject newObj = Instantiate(currentObj);
    51.                 newObj.SetActive(false);
    52.                 objectPool[name].Add(newObj);
    53.                 return newObj;
    54.             }
    55.         }
    56.  
    57.         return null;
    58.     }
    59. }
    60.  
    I've been looking through this and I can't seem to find the problem.
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,848
    I don't see anywhere that you're nulling out the object's physics state (in particular, its angular velocity). So it doesn't seem surprising to me that your recycled objects are still spinning.
     
  3. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    set angularvelocity of the rigidbody to zero.
     
  4. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    The strange thing about that is in my first projectile pooler, I never had to reset the angular velocity. I suspect the reason is that U4 reset these values when it was set inactive.
     
    JoeStrout likes this.
  5. Emmsii

    Emmsii

    Joined:
    Feb 27, 2014
    Posts:
    9
    That did the trick, bullets are no longer spinning! Now I need to look into the line renderer issue, I'll look in the morning.
     
  6. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    The way to stop the stretch problem is to disable the line renderer before you move.

    Of course if you just disable it, it will disappear. Best approach is to disable the projectile mesh, then return the projectile a second or two later so that the effects can finish
     
  7. Emmsii

    Emmsii

    Joined:
    Feb 27, 2014
    Posts:
    9
    Okay, waited for the trail renderer to disappear before adding it back into the pool.

    I changed my bullet script to this:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class BulletController : MonoBehaviour {
    5.  
    6.     public float speed = 25f;
    7.     public float life = 5f;
    8.    
    9.     public ParticleSystem bulletHit;
    10.    
    11.     private Rigidbody rb;
    12.     private TrailRenderer trailRenderer;
    13.     private MeshRenderer mesh;
    14.  
    15.     void Awake(){
    16.         rb = GetComponent<Rigidbody> ();
    17.         trailRenderer = GetComponent<TrailRenderer> ();
    18.         mesh = GetComponent<MeshRenderer> ();
    19.     }
    20.  
    21.     private void OnEnable(){
    22.         mesh.enabled = true;
    23.         trailRenderer.enabled = true;
    24.         rb.velocity = speed * transform.forward;
    25.         Invoke ("Remove", life);
    26.     }
    27.    
    28.     private void OnDisable(){
    29.         CancelInvoke ();
    30.     }
    31.  
    32.     private void Remove(){
    33.         transform.rotation = Quaternion.identity;
    34.         trailRenderer.enabled = false;
    35.         mesh.enabled = false;
    36.         rb.velocity = Vector3.zero;
    37.         rb.angularVelocity = Vector3.zero;
    38.         transform.position = transform.parent.position;
    39.         Invoke ("WaitTime", 0.5f);
    40.     }
    41.  
    42.     private void OnCollisionEnter(Collision other){
    43.         Remove ();
    44.     }
    45.    
    46.     private void WaitTime(){
    47.         gameObject.SetActive (false);
    48.     }
    49. }
    50.  
    This works fine, only now I get performance issues after running for 10-15 minutes. The fps plummets, I've looked in the profiler and saw that either the Overhead or Behaviour.WaitTime() was taking the longest time.
     
  8. MESSI007

    MESSI007

    Joined:
    Feb 18, 2017
    Posts:
    4
    thank you

    1. Code (CSharp):
      1.  rb.velocity = Vector3.zero;
      2. rb.angularVelocity = Vector3.zero;
    2. is the solution
     
    jordanb976 likes this.
  9. AlexDjenkov

    AlexDjenkov

    Joined:
    Nov 8, 2016
    Posts:
    2
    I've been struggling with this spinning issue too.
    Resetting velocity and angularVelocity didn't do the trick, but setting
    Code (CSharp):
    1. isKinematic = true
    when disabling gameObject (and back to false when getting from the pool) worked.
    Thank you all for the tips!
     
    bigChris likes this.
  10. bigChris

    bigChris

    Joined:
    Jul 5, 2017
    Posts:
    8
    @AlexDjenkov , et. al. Thanks! Question is why cycling isKinematic works, but it does!
     
  11. Temseii

    Temseii

    Joined:
    Aug 23, 2016
    Posts:
    14
    Old question, but instead of spinning around rigidbody values all over the place you can simply do the following;

    Create a separate script for your pooled object and attach it to it. Create an instance of your rigidbody so that you don't have to continously call GetComponent, and assign it in Start/Awake. Add OnEnable & OnDisable methods. In OnEnable, call the WakeUp method from your rigidbody. In OnDisable, put it to Sleep. This will get rid of the jittering caused by rigidbody physics not being reset when objects are enqueued.

    Code (CSharp):
    1. private Rigidbody _rigidbody;
    2.  
    3.     private void Awake()
    4.     {
    5.         _rigidbody = GetComponent<Rigidbody>();
    6.     }
    7.  
    8.     private void OnEnable()
    9.     {
    10.         _rigidbody.WakeUp();
    11.     }
    12.  
    13.     private void OnDisable()
    14.     {
    15.         _rigidbody.Sleep();
    16.     }
     
    bigChris likes this.