2
votes

I'm trying to create some spell than can be used by both the Player & the Enemies. So, I've created a script "BasicAttack" as you can see below.

public class BasicAttak : Spell {
    public int          dmg = 2;
    public float        speed = 10.0f;
    public Rigidbody    projectile; 
    private float       currentCooldown = 0.0f;

    void Awake()
    {
        currentCooldown = 0.0f;
    }

    public override bool    launch(Vector3 launcherPosition, Vector3 targetPosition)
    {
        Debug.Log("Launch Basic Attack, cooldown = " + this.currentCooldown + " - if = " + (this.currentCooldown <= 0.0f) );
        if (this.currentCooldown <= 0.0f) {
            if (projectile == null)
                return (false);
            Quaternion r = Quaternion.LookRotation (targetPosition - launcherPosition);
            Rigidbody shot = Instantiate (projectile, launcherPosition, r) as Rigidbody;
            ProjectileScript a = shot.GetComponent<ProjectileScript>();
            a.damage = this.dmg;
            a.speed = this.speed;
            this.currentCooldown = this.cooldown;
            return (true);
        }
        return (false);
    }

    void    Update()
    {
        if (this.currentCooldown > 0.0f)
            this.currentCooldown -= Time.deltaTime;
        Debug.Log("currentCooldown = "+currentCooldown);
    }
}

Because I want to use this script to create different type of spell (using the damage and speed parameter), I've create some GameObject, one for each spell.

I've assigned my script to each Gameobject and set different values to each variables. Then, once everything configured, I've created prefabs for those spells like this one: Unity Spell Prefab

Everything seems fine so far. So, I've assigned my prefab to the player spell slot, I've launched the game and I've tried to launch a spell. The very first spell is working but, for the second spell, the variable currentCooldown is never reset. Here is what I get in the console.

Launch Basic Attack, cooldown = 0.5 - if = False

UnityEngine.Debug:Internal_Log(Int32, String, Object) UnityEngine.Debug:Log(Object) BasicAttak:launch(Vector3, Vector3) (at Assets\MyAsset\Scripts\Spell\BasicAttak.cs:18) ACharacter:LaunchSpell(Int32) (at Assets\MyAsset\Scripts\ACharacter.cs:70) Player:FixedUpdate() (at Assets\MyAsset\Scripts\Player.cs:20) (Filename: Assets/MyAsset/Scripts/Spell/BasicAttak.cs Line: 18)

currentCooldown = 0

UnityEngine.Debug:Internal_Log(Int32, String, Object) UnityEngine.Debug:Log(Object) BasicAttak:Update() (at Assets\MyAsset\Scripts\Spell\BasicAttak.cs:42)

At exactly the same frame, the currentCooldown variable is displayed at 0.5 in the launch function and at 0 in the update function. When it's the same variable in the exact same script in one prefab.

If I want to launch another spell, I can't just stop and play the game again. I have to relaunch Unity just to have my first spell launched.

It seems quite mystical to me, and I'm not even sure which question to ask. How can I do to have the same value for my variable ?

Note: If, instead of assigning a prefab to my player spell slot, I assign a GameObject from the scene, this problem doesn't occur. But, I can't really use this "solution" because I have to create one GameObject for each enemy that will also use the spell. Which mean that if I have 786 enemy, I need to create 786 GameObject and assign the spell manually to each enemy.

1
What happens if you move the second Debug.Log to the start of the Update() method? Before Time.deltaTime is applied to currentCooldown?Eraph
You should evaluate a TimeSpan (DateTime.UTCNow - LastFireTime > Cooldown) upon fire rather than updating every frame to check the time that's passed.ThisHandleNotInUse
@ThisHandleNotInUse Unity3D's inspector has a debug mode which shows private variables. You can enable it in the dropdown menu shown hereGoibon
Where do you fire your launch? I mean, in witch Unity's main lifecycle loop (Update, LateUpdate, Some Event...)?Frohlich
How do you instantiate the prefabs? Are you sure, that there is a prefab for each "setup"?d4Rk

1 Answers

2
votes

This is happening because in Unity in most case Update method is called more frequently then the FixedUpdate method. As a fact Update is called once per frame but frequency for FixedUpdate method depends on your Fixed Timestep settings (look into Edit > Project Settings > Time). It may be called once after every 3 frames. Hence the problem Fixed update display something and Update displaying something else.

As a fix you should use Invoke instead of update. Here's how

public class BasicAttak : Spell {
    public int          dmg = 2;
    public float        speed = 10.0f;
    public Rigidbody    projectile; 
    private float       currentCooldown = 0.0f;

    void Awake()
    {
        currentCooldown = 0.0f;
    }

    public override bool    launch(Vector3 launcherPosition, Vector3 targetPosition)
    {
        Debug.Log("Launch Basic Attack, cooldown = " + this.currentCooldown + " - if = " + (this.currentCooldown <= 0.0f) );
        if (this.currentCooldown <= 0.0f) {
            if (projectile == null)
                return (false);
            Quaternion r = Quaternion.LookRotation (targetPosition - launcherPosition);
            Rigidbody shot = Instantiate (projectile, launcherPosition, r) as Rigidbody;
            ProjectileScript a = shot.GetComponent<ProjectileScript>();
            a.damage = this.dmg;
            a.speed = this.speed;
            this.currentCooldown = this.cooldown;
            Invoke("ResetCoolDown", this.cooldown);
            return (true);
        }
        return (false);
    }

    void ResetCoolDown()
    {
        this.currentCooldown = 0.0f;
    }
}

If you don't know about Invoke

public void Invoke(string methodName, float time);

will call the method (method name provided in string methodName parameter) after time provide in float time parameter so in Invoke("ResetCoolDown", this.cooldown); Invoke will call the method ResetCoolDown after this.cooldown time (which is set to 0.5f) in your case.