- Home /
Physical shots
Hello! In my game, I need to create the mechanics of the shot as accurately as possible. I think that the best way is to release the bullet as a rigid body with the impulse applied to it (ForceMode.Impulse). But I also sometimes need to release bullets very often - every 0.1 sec. This leads to a decrease in the FPS. I already tried to do this with a raycast, but this does not work in case you need to shoot ahead.
What other ways are there to implement this mechanics? Or have I misconfigured physics? Any ideas.
GameObject bullet = _objectPoolManager.GetObject(PooledObject.PoolType.PlayerBullet);
bullet.SetActive(true);
Rigidbody bulletRB = bullet.GetComponent<Rigidbody>();
bullet.transform.position = firePoint.transform.position;
bullet.transform.rotation = firePoint.transform.rotation;
bulletRB.velocity = new Vector3(0.0f, 0.0f, 0.0f);
bulletRB.angularVelocity = new Vector3(0.0f, 0.0f, 0.0f);
bulletRB.AddForce (firePoint.transform.forward * bulletImpulse, ForceMode.Impulse);
This is how a profiler looks
After reading the recommendations in the comments, I made some changes to the code. I brought the variables out of the body of the function and measured the time of the Fire () method using StopWatch. The method is executed on average 0.2 ms. I also measured the execution time of the OnCollisionEnter(). It executes in an average of 0.2 ms, but very often it happens in 4-6 ms.
Here's what the profiler looks like when nothing happens in the game Spines all the time arise on different methods: PxsMBP.updateWork, PxsContext.mergeCMDescreteResults, ScSceneAfterInegrations, updateCCDMultiPass, Pxs.MBP.postUpdateWork.
Have you tried setting the bullet velocity directly? Should not make much of a difference to your current code except for bullets with different weight. I'd also suggest this because unity advises not to do physics stuff out of the FixedUpdate function.
Yes, I tried to set the velocity directly. This had no significant effect. Bullet weight is always the same. Here is part of the Fire () method code. It is executed directly by the user when the button is pressed.
Did you check that your funtion really only gets called once per button-press? (Get$$anonymous$$eyDown not Get$$anonymous$$ey) Or is there anything else you do related to the same button or on bullet impact?
Only one thing to say here... it probably wont help though.
It appears you are trying to utilise an object pool but in doing so you are Creating a new GameObject to contain it. This TOTALLY defeats the purpose of Object Pooling which is the Recycling of existing GameObjects to avoid the impact of Garbage Collection.
I'm not creating a new object. I declare a variable and then set it using the GetObject () function from the pool manager. GetObject () searches for free objects in the scene hierarchy and returns them here. Then I just activate the object, and after use deactivate it. The standard scheme of pool management. Correct me if I'm doing something wrong, maybe the problem is here.
Well, your GameObjects should be all pre-declared. The contents of the existing Variables are then changed as you need them. You totally avoid destroying and creating new GameObject variables on the fly for pooling to work. Ins$$anonymous$$d of destroying, disable Renderers and Rigidbodies etc perhaps load in default values if you need before it re-enters the pool to be handed fresh info.
Answer by Eugene-B · Apr 02, 2018 at 08:43 PM
The problem is gone after the upgrade to version 2018.1.0b13. Thank you all for your comments.
Answer by mani3307 · Mar 27, 2018 at 07:26 AM
@EvgenFree The only improvements according to my knowledge are 1.)predefine new Vector3(0,0,0)in both the lines and use the variable instead i.e., do this before start() Vector _emptyVector=new Vector3(0,0,0) and use _emptyVector variable where ever you need. 2.)Try to increase physics time step in settings according to your needs without losing quality of game mechanics
you told raycasting is not working to shoot ahead,I don't understand what you are saying exactly but If you want to detect collisions behind walls like that setup a Layer for your hittable objects and use it
shoot ahead - Shooting by moving target, ahead of. Sorry for my English. Shooting with the use of raycasting suggests that the bullet moves instantly from point A to point B.
The change in the rate of renewal of physics also had no effect. In addition, when I start the game, the value in the fixed time step is set to 0.01, whatever I write there. I'm beginning to think that this could be the problem of the engine. I'm using 2018.012b
@EvgenFree If the performance spikes are not solved you might want to check the unity learn site for performance optimisation tutorials
Probably, I read and applied everything that is written on that site. The problem is still there.
Answer by melsy · Mar 29, 2018 at 04:40 AM
you have a huuuuge garbage collection. Use a pool for the bullets. Instantiate them at the beginning of the scene then just call them when the gun shoots. then when they hit the target(or fly to far) set them back false and send them to the pool again.
Here is a pooler i builkt and use for all my games.
Create an empty object in the scene and call it Pools (or something)
create a new C# script called LevelPools(or something) create a new c# script called projectiles(or something) create a new c# script called ObjectPooler(or something) inside the projectiles class you will instatiate a new objectpooler for each type of bullet you want. build the pools then have a get method to retrieve it.
[Serializable]
public class Projectiles
{
public ObjectPooler bullet50Cal = new ObjectPooler();
public void BuildAllPools()
{
bullet50Cal.BuildPool();
}
public GameObject GetProjectile(ObjectPooler projectile) => projectile.GetClone();
}
Inside the object pooler you will actually build the pool from a list of gameobjects it gets from inspector fields. create a folder to put the object, a prefab for the object, and tell it how many to spawn using System.Collections.Generic; using UnityEngine; using System; [Serializable] public class ObjectPooler { [Tooltip("The folder that the clones will be placed into.")] public Transform cloneFolder = null; [Tooltip("The object that will be spawned.")] public GameObject objectToClone = null; [Tooltip("The number of clones that will be created.")] public int numberOfEachToSpawn = 100; [HideInInspector] public List<GameObject> objectPool = new List<GameObject>(); // Have the pools been built bool poolsBuilt = false; // Create a pool of an object. // This will clone the objectToClone by numberOfEachToSpawn times. // The clones will be placed inside the cloneFolder and set inactive. public void BuildPool() { for (int i = 0; i < numberOfEachToSpawn; i++) { GameObject projectileClone; if (cloneFolder != null) projectileClone = MonoBehaviour.Instantiate(objectToClone, cloneFolder); else projectileClone = MonoBehaviour.Instantiate(objectToClone); projectileClone.SetActive(false); objectPool.Add(projectileClone); } poolsBuilt = true; } // Grabs a clone from the pool and returns it to the method that calls this. public GameObject GetClone() { if (poolsBuilt) { for (int i = 0; i < numberOfEachToSpawn; i++) { if (!objectPool[i].gameObject.activeInHierarchy) { return objectPool[i]; } } } else Debug.Log("Need To Build The Object Pool On Awake"); return null; } }
Finnaly in the level pools script this will all come together. you will instiate a projectile class that houses the bullets build all the pools for the projectiles. then have a Get method that grabs the inactive projectile that you call from the weapon script. So instead of calling Instantiate(prefab); you call GameObject clone = levelPools.Get50CalBullet(); clone.transform.SetPositionAndRotation(barrelEnd.position,barrelEnd.rotation); clone.SetActive(true); then add the force to it on the projectile add a OnCollisionEnter or raycast that will set it inactive on colllision or detection. Here is the LevelPool
public class LevelPools : MonoBehaviour
{
public Projectiles projectiles = new Projectiles();
private void Awake()
{
projectiles.BuildAllPools();
}
#region Bullet projectiles
// Bullets
public GameObject Get50CalBullet() => projectiles.GetProjectile(projectiles.bullet50Cal);
// Create all the rest of the bullets calls here
#endregion
}
using this same setup in the same level pools you can build classes for muzzle flashes, impacts,... everything you want. just create the class just like the Projectiles class.
Just reread and saw you were using pools already DUH on me lol. Ill leave this here anyway.
Yes, I already use pools and yesterday a lot of work was done to optimize garbage collection. I should note that at the moment everything works just like a clock, except for these spikes in physics. I'm losing hope. There are interesting places in your pooling method, I will use your suggestions. Thank you!