A problem with a property getting accessed before Start() can run

The title line captures the essentials of my problem.

Will post code.

These are the details; when I run my project and the missile (which has Bullet.Cs component attached to it) passes in to the Radar trigger, the turret should turn and aim at its calculated intercept point. My calculation is however wrong (I get NaN’s and Infinities) because for some reason the code that accesses a Property in my Bullet.cs class accesses it before the Start() function gets called for Bullet.cs! This results in my TopVelocity property being zero, which causes an illegal operation when I try to divide by it.

I’m not sure how to fix this.

My code:

using UnityEngine;

public class Turret : Spawnable
{
    public float turnSpeed = 1f, refireRate = 0.2f;
    public bool isTracking = false;
    Quaternion elevation, rotation, defaultElevation, defaultRotation;
    public Transform turretBase, turretGun, looker, bulletSpawn;
    Vector3 intercept;
    Bullet bScript, tScript; //Bullet and Target Bullet.cs scripts
    public GameObject bGO; //Bullet prefab
    Rigidbody tRB; //Target RigidBody
    public GameObject radarField;
    Timer refireTimer;

    new public void Start()
    {
        base.Start();
        defaultElevation = turretGun.rotation;
        defaultRotation = turretBase.rotation;
        refireTimer = gameObject.AddComponent<Timer>();
        refireTimer.Limit = refireRate;
        intercept = new Vector3();
        bScript = bGO.GetComponent<Bullet>();
    }

    void Calculate()
    {
        float t1, magnitude;
        magnitude = tScript.FixedAcceleration * Mathf.Pow(Time.realtimeSinceStartup, 2f); //Find magnitude
        t1 = magnitude / bScript.TopVelocity;
        intercept = t1 * tRB.velocity.normalized + tRB.position;

        turretBase.LookAt(intercept);

    }

    public void Traverse()
    {
        if (isTracking)
        {
/*NOTE: This code is commented out because I am having a beefin time trying to get the turret to point at the target atm. So I'm just using the LookAt() function above in Calculate()*/
            //turretGun.localRotation = elevation;
            // Quaternion.RotateTowards(turretGun.localRotation, elevation, turnSpeed);
        /*    turretBase.localRotation = rotation; //These Have to stay local or otherwise I must adjust the x axis of the base by 90 degrees.
            turretGun.localRotation = elevation;
         */   //Quaternion.RotateTowards(turretBase.rotation, rotation, turnSpeed);
        }
        else
        {
           // turretGun.localRotation = Quaternion.Slerp(turretGun.localRotation, defaultElevation, turnSpeed);
        //    turretBase.rotation = Quaternion.Slerp(turretBase.rotation, defaultRotation, turnSpeed);
        }
    }

    public void SetTarget(GameObject t)
    {
        if (t != null)
        {
            tRB = t.GetComponent<Rigidbody>();
            tScript = t.GetComponent<Bullet>();
            isTracking = true;
            print("Got target!");
            refireTimer.Begin();
        }
        else
        {
            isTracking = false;
            intercept = Vector3.forward;
            tRB = null;
        }
    }

    void Fire()
    {
        if (!refireTimer.IsRunning)
        {
            GameObject b = Instantiate(bGO, bulletSpawn.position, bulletSpawn.rotation);
            b.transform.position = bulletSpawn.position;
            b.transform.rotation = Quaternion.Euler(Vector3.forward) * bulletSpawn.rotation;

            refireTimer.Begin();
        }

    }

     public void Update()
    {
        if (bScript.BulletIsInitialized)
        {
            if (isTracking)
            {
                Calculate();

                Traverse();

                Fire();
            }
        }
    }


}
using UnityEngine;

public class Bullet : Spawnable
{
    public float force;
    Rigidbody rb;
 
    float maxV, physAcc, acc;

    // Use this for initialization
    new void Start()
    {
        base.Start();
        rb = GetComponent<Rigidbody>();

        maxV = force / rb.mass / rb.drag;
        print("Initial maxV= " + maxV + "   of object: " + gameObject.name);

    }


    void Update()
    {
        acc = maxV - rb.velocity.magnitude;
    }

    void FixedUpdate()
    {
        rb.AddRelativeForce(Vector3.forward * force, ForceMode.Acceleration);
        physAcc = maxV - rb.velocity.magnitude;
        //   print("Current velocity=" + rb.velocity + "for object:" + gameObject.name + "acceleration=" + Acceleration + "t=" + Time.realtimeSinceStartup);
    }

    public float Force
    {
        get { return force; }
    }

    public float TopVelocity
    {
        get { print(name + "    has topVelocity=" + maxV); return maxV; }
    }

    public float FixedAcceleration
    {
        get
        {
            return physAcc;
        }
    }

    public float Acceleration
    {
        get
        {
            return acc;
        }
    }
}
using UnityEngine;
 /*Most of this class is unused functionality, at least until I get the bullets to collide with proper */
public class Spawnable : MonoBehaviour, IKillable, IDamageable {

    Timer life;

    public void Spawn(Vector3 position, Vector3 eulerOrientation, float lifespan) //To be called when a Spawnable object needs to exist (DO NOT INSTANTIATE) [Also used by objectpooler much later!!]
    {
        gameObject.transform.rotation = Quaternion.Euler(eulerOrientation);
        gameObject.transform.position = position;
        gameObject.SetActive(true);
        life.Limit = lifespan;
        life.Begin();
    }

    public float healthpoints, defaultHealth = 1000f;

    public virtual void Start()
    {
        healthpoints = defaultHealth;
        life = gameObject.AddComponent<Timer>();
    }

    public void Kill()
    {
        gameObject.SetActive(false);
    }

    public void Damage(float damage)
    {
        healthpoints -= Mathf.Abs(damage);
        if (healthpoints <= 0f)
        {
            Kill();
        }

    }

}

I think there’s a conceptual misunderstanding about what bScript is and represents. Since it is a component on a prefab, you can’t think of it as actually existing in the game world. It’s simply a template that describes what will be created/copied when you call Instantiate() on the prefab. As such, I’m pretty sure that events such as Start() will never be called on this particular prefab instance, because it’s not an active participant in the game, it’s just a passive descriptor.

Looking at what you’re attempting to achieve, I think you might get there by moving your bullet’s Start() code to Awake() (this guarantees that it is executed the moment the bullet is instantiated), and then instantiating an initial bullet in the turret’s Start() event, but with the Bullet component disabled in the prefab. This will be more like having a bullet loaded into the turret, ready to fire. bScript would then get set from the actual bullet instance, and not from the prefab. You’d then “reload” the turret with a new bullet immediately after firing the previous bullet.

What the previous poster said is right on. Also, as a note, you would be better off to assign the bScript in your Fire() method. After instantiating, set: bScript = b.GetComponent<Bullet>();
That’s the script on the actual bullet you just created, as opposed to the prefab. :slight_smile:

Thank you!! It works!!!

Right on :slight_smile: (no matter who helped/both), glad it’s working for ya.