Would like to translate bullet but at what cost?

Hi there,
I am doing a side scroller like Mega Man combined with Astroids. What I am wondering is this.

I would like to have my bullets as colliders, that are moved along a trajectory. I would like to do this over ray, since I would like the player to actually see each projectile ( a common effect in this style of game). Problem is, with my method, the fps drops 5 frames, when my machine gun is firing.

Here is my code…

///this is part of the bullet script. It is put into motion via the machine gun's script
	void fireBullet (Vector3 trajectoryDel) {
		bulletTrajectory = trajectoryDel;
		thisBulletActive = true;
		StartCoroutine("propellBullet");
	}
	IEnumerator propellBullet(){
		while(thisBulletActive){
			crntPos = thisTransform.position;
			thisTransform.Translate(bulletTrajectory * Time.deltaTime * speed, Space.World);
			yield return null;		
		}
	}

The machine gun script that instructs the firing of a bullet

////on tap, init gun fire via InvokeRepeating()	   		   	
	void determineTrajectory(){		  		
		////gather the trajectory from two empty transforms positions (rise, run) 			 			
		aPt = Nozzle2.position; // touch position
		bPt = Nozzle1.position; //playerObject position in screen space
		nozzleRise = aPt.y - bPt.y; /// rise
		nozzleRun = aPt.x - bPt.x; /// run	
		bulletTrajectory = new Vector3(nozzleRun, nozzleRise, 0);
		gatherBullet();
		return;
	}
		
	void gatherBullet(){
		if(thisClipEmptied)
		return;
		
		for(int i = 0; i < bulletPoolsLength; i++){
			bullet = bullets[i];
			if(!bullet.active){
				bullet.active = true;
				bullet.transform.position = Nozzle2.position;
				bullet.transform.rotation = Nozzle2.rotation;
				bullet.SendMessage("fireBullet", bulletTrajectory);///Send message, questionable?
				wpnHudMngData.handleWpnHud();
				return;
			}
		}	
	}

What I am wondering is, the best way to have some visual representation of bullets flying with minimal cost to memory.

Get rid of the SendMessage call first and foremost.

Secondly - you don’t have to have a collider on your bullets. All you need to do is raycast ahead of the bullet (before moving it) using its current velocity as the distance. If the raycast returns a hit then the bullet will collide with something that frame and you can do whatever you need to do. If no hit is returned then move the bullet forward its velocity, yield, and repeat next frame.

Right,
So, moving it via translate, is not an issue. The engine should be able to handle copius amounts of projectiles in this fashion?

And you would use getComponent() over sendMessage? Just checking.

I would make bullets an array of your bullet scripts and not an array of GameObjects in order to not do either.

I am not sure what you mean.

I’m assuming bullets is defined as a GameObject[ ] or a List. I would instead define it as a BulletScript[ ] or List (or whatever the name of your class is that has the fireBullet method).

Then the send message call would just be this

bullet.fireBullet(bulletTrajectory);

I am sorry,
I dont fully get you. You are not retrieving the component of the bullet script.

I tried this…

brainBulletScript[] bulletsData;
bulletsData[i].fireBullet(bulletTrajectory);

but get an error at the fireBullet function.

Thats some un-quality code right there.

Do it this way…

ProjectileController.cs

Transform _transform;
Rigidbody _rigidbody;
void Start()
{
  _transform = transform;

  if(_transform.rigidbody != null)
    _rigidbody = _transform.rigidbody;
  else _rigidbody = gameObject.AddComponent<Rigidbody>();

  _rigidbody.drag = 0;
  _rigidbody.angularDrag = 0;
  _rigidbody.useGravity = false;

}

void Fire(Vector3 firePoint, Vector3 direction)
{
  _transform.position = firePoint;
  _transform.eulerAngles = direction;

  _rigidbody.AddRelativeForce(Vector3.forward * 10);
}

void OnCollisionEnter(Collision hit)
{
   _rigidbody.velocity = Vector3.zero;
   
   //do whatever here... hit player, return bullet to pool, explosions...
}

So you think rigidbody force over translate? Hm.

I can’t wait till my code is fully respected. For me, it is a matter of knowing what costs more and less to an engine. Practice I guess. I will try.

I dunno how you are maintaining and populating that array so I can’t really help you. My only point was that keeping a list of GameObjects when what you really need is some component on that GameObject is silly - just keep a list of the components instead.

Assume this class

public class SomeComp : MonoBehaviour
{
    public void FireBullet()
    {
        // do stuff
    }
}

This is dumb-

public GameObject[] bullets; // assign via Inspector

public void Foo()
{
    for (var i = 0; i < bullets.Length; i++)
    {
        bullets[i].SendMessage("FireBullet");
        // or
        bullets[i].GetComponent<SomeComp>().FireBullet();
    }
}

Instead, skip the SendMessage/GetComponent completely-

public SomeComp[] bullets; // still assigned via Inspector

public void Foo()
{
    for (var i =0; i < bullets.Length; i++)
    {
        bullets[i].FireBullet();
    }
}

So you are making bullets a subClass or member of the subclass?

I am trying to work out what you suggested Kelso, but am in a bit of a conundrum.

My issue is that, unless the bullet has the gunFireMng script attached to it, it will not fill a slot dedicated to the class, gunFireMng[ ]. I do not want to put the gunFireMng script onto each bullet since it holds so many various vars etc. How do I create a variable[ ] and script for variable that will only access one (or as many) function(s) from the gunFireMng as possible?

edit.
I have also tried this…
to make the bullets script derive from the mng, gunFireMng,

public class gunFireBullets : gunFireMng {

	void fireBullet(){
		print("qaqqq ");
	}
}

But I get an error, that

I did add the function to the main mng script, also, I call the function like this…

bullet = bullets[i];//from the pool
bullet.fireBullet();
}

void fireBullet(){///so the function fireBullet() has exists in this mng script and the subclass. or either with out. Both cause errors. 
}

Put the method that moves your bullet in it’s own script and create an array of that script instead. You’re getting that error because bullets is still defined as GameObject[ ].

How are you using SendMessage/GetComponent when you say that not every bullet has that script attached to it? My assumption was that each bullet had a script attached to it that had a fireBullet method.

In all honesty - it sounds like your game’s architecture is a mess…

Great thank you.
This is my final (for now) script, if you could, please critique. I have set up two additional arrays, for the bullet’s transform and gameObject, just wondering if A you think it is necessary and B efficient.

Thanks.

	void gatherBullet(){
		if(thisClipEmptied)
		return;
		
		for(int i = 0; i < bulletPoolsLength; i++){
			if(!bulletGmObjs[i].active){
				bulletGmObjs[i].active = true;
				bulletTrnsfrms[i].position = Nozzle2.position;
				bulletTrnsfrms[i].rotation = Nozzle2.rotation;
				bulletScripts[i].fireBullet(bulletTrajectory);
				wpnHudMngData.handleWpnHud();
				return;
			}
		}	
	}

No and no because you can access the GameObject and Transform from the script so there’s no need to maintain 3 arrays. Also - I wouldn’t maintain a variable for the length of the array because the length could change and that variable wouldn’t update accordingly.

for (var i = 0; i < bulletScripts.Length; i++)
{
    if (!bulletScripts[i].gameObject.active)
    {
        bulletScripts[i].gameObject.active = true; // or use SetActive(true)
        bulletScripts[i].transform.position = Nozzle2.position;
        bulletScripts[i].transform.rotation = Nozzle2.rotation;
        bulletScripts[i].fireBullet(bulletTrajectory);
        wpnHudMngData.handleWpnHud();
        return;
    }
}

I am under the impression that storing any component is basically a good thing. In other words thisTrnsfrm is better than transform. Tho I will take that into account, I am doing tests now, so I can see if there is an effect.

If you have one more minute sir, can you look at the whole process I use for the firing of the bullet and critique? I am still losing about 3-4 fps on a 4thGen iPod.

Thanks
Ren

///gun script
	void determineTrajectory(){		  		
		///means gun fire is not straight on. therefore cancel 
		if(trackingDummyTarget)
		return;		
		 
////this is a 2D game, this is the best way I can see to get the trajectory.??		 			
		aPt = Nozzle2.position; // end of nozzle
		bPt = Nozzle1.position; //base of nozzle
		nozzleRise = aPt.y - bPt.y; /// rise
		nozzleRun = aPt.x - bPt.x; /// run	
		bulletTrajectory = new Vector3(nozzleRun, nozzleRise, 0);
		gatherBullet();
		return;
	}
		
	void gatherBullet(){
		if(thisClipEmptied)
		return;
		///NOTED YOUR ADVICE IN LAST POST!
		for(int i = 0; i < bulletPoolsLength; i++){
			if(!bulletGmObjs[i].active){
				bulletGmObjs[i].active = true;
				bulletTrnsfrms[i].position = Nozzle2.position;
				bulletTrnsfrms[i].rotation = Nozzle2.rotation;
				bulletScripts[i].fireBullet(bulletTrajectory);
				wpnHudMngData.handleWpnHud();
				return;
			}
		}	
	}
///bullet script
	public void fireBullet (Vector3 trajectoryDel) {
		bulletTrajectory = trajectoryDel;
		thisBulletActive = true;
		StartCoroutine("propellBullet");
	}
	IEnumerator propellBullet(){
		while(thisBulletActive){
			crntPos = thisTransform.position;
			thisTransform.Translate(bulletTrajectory * Time.deltaTime * speed, Space.World);
			yield return null;		
		}
	}

You don’t need to cache everything and the chance of introducing a bug with your method is astronomical.

I’m assuming bulletScripts is like a pool of bullets? Instead of enumerating the entire collection each time to find an inactive one - create a List or something similar and remove items after you activate them and put them back in the list when you deactivate them. This way you’re only maintaining a list of available bullets. (Stacks and Queues would work as well or better than a List so look into those)

You also store a ton of redundant information. What is thisBulletActive? Can it not be replaced with gameObject.active? Same goes for crntPos. Why can’t you just reference thisTransform.position?

Lastly - assuming your bullets only move forward and assuming they must face the direction they are moving then you can simply do this instead of Translate

thisTransform.position += thisTransform.forward * speed * Time.deltaTime;

And since you align them to your gun nozzle (again, I’m assuming you do based on the code) then you shouldn’t need to calculate a trajectory at all. They align to the gun - which aligns them to the direction the player is aiming - and then they simply move forward at a set speed until they hit something - so you could theoretically throw out your entire determineTrajectory method.

As far as the lists goes, to replace the sorting thru inactive bullets, would you be able to give me a small example? Tho I am googling now.

Yes, thisBulletActive is somewhat redundant.

I use crntPos as I was taught this method. You think it is a bit of a waste eh?

Cheers

Super basic example of using a List as a pool.

public class BulletPool : MonoBehaviour
{
    [SerializeField()]
	private List<BulletScript> availableBullets;
	
	public BulletScript GetNextBullet()
	{
		if (this.availableBullets.Count == 0)
			return null;
			
		BulletScript b = this.availableBullets[0];
		this.availableBullets.RemoveAt(0);
		b.SetActive(true);
		return b;
	}
	
	public void AddUsedBullet(BulletScript bullet)
	{
		bullet.SetActive(false);
		bullet.transform.position = Vector3.zero;
		this.availableBullets.Add(bullet);
	}
}

// get reference somewhere in code or make BulletPool a singleton or whatever
BulletPool pool = GameObject.Find("Pool").GetComponent<BulletPool>();

// then replace your for loop with
BulletScript bullet = pool.GetNextBullet();
if (bullet == null) return;
bullet.FireBullet();

Cheers,
will utilize. If I have any more questions I will post.

Many thank you’s.