Forum begins after the advertisement:


[Part 15] ProjectileWeapon.cs

Viewing 13 posts - 1 through 13 (of 13 total)
  • Author
    Posts
  • #15754
    정춤고 (ChoomGo)
    Level 13
    Former Patron
    Helpful?
    Up
    0
    ::
    <code>
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ProjectileWeapon : Weapon
    {
        protected float currentAttackInterval;
        protected int currentAttackCount;
        protected Projectile currentProjectile;
    
        protected override void Update()
        {
            base.Update();
    
            if (currentAttackInterval > 0)
            {
                currentAttackInterval -= Time.deltaTime;
                if (currentAttackInterval <= 0)
                {
                    Attack(currentAttackCount);
                }
            }        
        }
    
        public override bool CanAttack()
        {
            if (currentAttackCount > 0)
            {
                return true;
            }
    
            return base.CanAttack();
        }
    
        protected override bool Attack(int attackCount = 1)
        {
            if (!currentStats.projectilePrefab)
            {
                Debug.LogWarning(string.Format("Projectile prefab has not been set for {0}", name));
                currentCooldown = weaponData.baseStats.cooldown;
                return false;
            }
    
            if (!CanAttack())
            {
                return false;
            }
    
            /*
            float spawnAngle = GetSpawnAngle();
    
            Projectile prefab = Instantiate(currentStats.projectilePrefab, 
                ownerStats.transform.position + (Vector3)GetSpawnOffset(spawnAngle),
                Quaternion.Euler(0, 0, spawnAngle));
            */
    
            float spawnAngle = Mathf.Atan2(playerController.lastMovedVector.y, playerController.lastMovedVector.x) * Mathf.Rad2Deg;
    
            Vector2 spawnOffSet = Quaternion.Euler(0, 0, spawnAngle) * new Vector2(
                Random.Range(currentStats.spawnVariance.xMin, currentStats.spawnVariance.xMax),
                Random.Range(currentStats.spawnVariance.yMin, currentStats.spawnVariance.yMax));
    
            Projectile prefab = Instantiate(currentStats.projectilePrefab,
                ownerStats.transform.position + (Vector3)spawnOffSet,
                Quaternion.Euler(0, 0, spawnAngle));
    
            prefab.weapon = this;
            prefab.ownerStats = ownerStats;
    
            if (currentCooldown <= 0)
            {
                currentCooldown += currentStats.cooldown;
            }
    
            attackCount--;
    
            if (attackCount > 0)
            {
                currentAttackCount = attackCount;
                currentAttackInterval = weaponData.baseStats.projectileInterval;
            }
    
            return true;
        }
    
        protected virtual float GetSpawnAngle()
        {
            return Mathf.Atan2(playerController.lastMovedVector.y, playerController.lastMovedVector.x) * Mathf.Rad2Deg;
        }
    
        protected virtual Vector2 GetSpawnOffset(float spawnAngle = 0)
        {
            return Quaternion.Euler(0, 0, spawnAngle) * new Vector2(
                Random.Range(currentStats.spawnVariance.xMin, currentStats.spawnVariance.xMax),
                Random.Range(currentStats.spawnVariance.yMin, currentStats.spawnVariance.yMax));
        }
    }
    </code>

    Its my “ProjectileWeapon.cs” I’m not sure what the problem is, but I’m having a problem with the projectile not firing. Please let me know if you need anything else.

    #15757
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Add the following to your Attack() and CanAttack() functions, then see if the messages print on the Console. This will tell us whether the Attack() function is firing, and which part of it the attack is getting “stuck” on.

        public override bool CanAttack()
        {
            print("CanAttack() attack count: " + currentAttackCount);
            if (currentAttackCount > 0)
            {
                return true;
            }
    
            return base.CanAttack();
        }
    
        protected override bool Attack(int attackCount = 1)
        {
            print("Attack() fired.");
            if (!currentStats.projectilePrefab)
            {
                Debug.LogWarning(string.Format("Projectile prefab has not been set for {0}", name));
                currentCooldown = weaponData.baseStats.cooldown;
                return false;
            }
    
            if (!CanAttack())
            {
                print("Attack() failed.");
                return false;
            }
    
            print("Attack() succeeded!.");
            /*
            float spawnAngle = GetSpawnAngle();
    
            Projectile prefab = Instantiate(currentStats.projectilePrefab, 
                ownerStats.transform.position + (Vector3)GetSpawnOffset(spawnAngle),
                Quaternion.Euler(0, 0, spawnAngle));
            */
    
            float spawnAngle = Mathf.Atan2(playerController.lastMovedVector.y, playerController.lastMovedVector.x) * Mathf.Rad2Deg;
    
            Vector2 spawnOffSet = Quaternion.Euler(0, 0, spawnAngle) * new Vector2(
                Random.Range(currentStats.spawnVariance.xMin, currentStats.spawnVariance.xMax),
                Random.Range(currentStats.spawnVariance.yMin, currentStats.spawnVariance.yMax));
    
            Projectile prefab = Instantiate(currentStats.projectilePrefab,
                ownerStats.transform.position + (Vector3)spawnOffSet,
                Quaternion.Euler(0, 0, spawnAngle));
    
            prefab.weapon = this;
            prefab.ownerStats = ownerStats;
    
            if (currentCooldown <= 0)
            {
                currentCooldown += currentStats.cooldown;
            }
    
            attackCount--;
    
            if (attackCount > 0)
            {
                currentAttackCount = attackCount;
                currentAttackInterval = weaponData.baseStats.projectileInterval;
            }
    
            return true;
        }

    Show me the messages generated on the Console window after running the game again.

    #15759
    정춤고 (ChoomGo)
    Level 13
    Former Patron
    Helpful?
    Up
    0
    ::
    Attack() fired.
    UnityEngine.MonoBehaviour:print (object)
    ProjectileWeapon:Attack (int) (at Assets/02. Scripts/Weapon/WeaponEffects/ProjectileWeapon.cs:39)
    Weapon:Update () (at Assets/02. Scripts/Weapon/Weapon.cs:91)
    ProjectileWeapon:Update () (at Assets/02. Scripts/Weapon/WeaponEffects/ProjectileWeapon.cs:14)
    
    CanAttack() attack count: 0
    UnityEngine.MonoBehaviour:print (object)
    ProjectileWeapon:CanAttack () (at Assets/02. Scripts/Weapon/WeaponEffects/ProjectileWeapon.cs:28)
    ProjectileWeapon:Attack (int) (at Assets/02. Scripts/Weapon/WeaponEffects/ProjectileWeapon.cs:47)
    Weapon:Update () (at Assets/02. Scripts/Weapon/Weapon.cs:91)
    ProjectileWeapon:Update () (at Assets/02. Scripts/Weapon/WeaponEffects/ProjectileWeapon.cs:14)
    
    Attack() succeeded!.
    UnityEngine.MonoBehaviour:print (object)
    ProjectileWeapon:Attack (int) (at Assets/02. Scripts/Weapon/WeaponEffects/ProjectileWeapon.cs:52)
    Weapon:Update () (at Assets/02. Scripts/Weapon/Weapon.cs:91)
    ProjectileWeapon:Update () (at Assets/02. Scripts/Weapon/WeaponEffects/ProjectileWeapon.cs:14)
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    My Weapon.cs
    
    public abstract class Weapon : Item
    {
        [System.Serializable]
        public struct Stats
        {
            public string name, description;
    
            [Header("Visuals")]
            public Projectile projectilePrefab;
            public Aura auraPrefab;
            public ParticleSystem hitEffect;
            public Rect spawnVariance;
    
            [Header("Values")]
            public float lifespan;
            public float damage, damageVariance, area, speed, cooldown, projectileInterval, knockback;
            public int number, piercing, maxInstances;
    
            public static Stats operator +(Stats stats1, Stats stats2)
            {
                Stats result = new Stats();
                result.name = stats2.name ?? stats1.name;
                result.description = stats2.description ?? stats1.description;
                result.projectilePrefab = stats2.projectilePrefab ?? stats1.projectilePrefab;
                result.auraPrefab = stats2.auraPrefab ?? stats1.auraPrefab;
                result.hitEffect = stats2.hitEffect == null ? stats1.hitEffect : stats2.hitEffect;
                result.spawnVariance = stats2.spawnVariance;
                result.lifespan = stats1.lifespan + stats2.lifespan;
                result.damage = stats1.damage + stats2.damage;
                result.damageVariance = stats1.damageVariance + stats2.damageVariance;
                result.area = stats1.area + stats2.area;
                result.speed = stats1.speed + stats2.speed;
                result.cooldown = stats1.cooldown + stats2.cooldown;
                result.number = stats1.number + stats2.number;
                result.piercing = stats1.piercing + stats2.piercing;
                result.projectileInterval = stats1.projectileInterval + stats2.projectileInterval;
                result.knockback = stats1.knockback + stats2.knockback;
    
                return result;
            }
    
            public float GetDamage()
            {
                return damage + Random.Range(0, damageVariance);
            }
        }
    
        protected Stats currentStats;    
    
        public WeaponData weaponData;
    
        protected float currentCooldown;
    
        protected PlayerController playerController;
    
        public virtual void Initialise(WeaponData weaponData)
        {
            base.Initialise(weaponData);
            this.weaponData = weaponData;
            currentStats = weaponData.baseStats;
            playerController = GetComponent<PlayerController>();
            currentCooldown = currentStats.cooldown;
        }
    
        protected virtual void Awake()
        {
            if (weaponData)
            {
                currentStats = weaponData.baseStats;
            }
        }
    
        protected virtual void Start()
        {
            if (weaponData)
            {
                Initialise(weaponData);
            }
        }
    
    <strong>    protected virtual void Update()
        {
            currentCooldown -= Time.deltaTime;
    
            if (currentCooldown <= 0f)
            {
                Attack(currentStats.number); //Line91
            }
        }</strong>
    
        public override bool DoLevelUp()
        {
            base.DoLevelUp();
    
            if (!CanLevelUp())
            {
                Debug.LogWarning(string.Format("cannot level up {0} to Level {1}, max level of {2} already reached", 
                    name, currentLevel, weaponData.maxLevel));
    
                return false;
            }
    
            currentStats += weaponData.GetLevelData(++currentLevel);
            return true;
        }
    
        public virtual bool CanAttack()
        {
            return currentCooldown <= 0;
        }
    
        protected virtual bool Attack(int attackCount = 1)
        {
            if (CanAttack())
            {
                currentCooldown += currentStats.cooldown;
                return true;    
            }
    
            return false;
        }
    
        public virtual float GetDamage()
        {
            return currentStats.GetDamage() * ownerStats.CurrentMight;
        }
    
        public virtual Stats GetStats()
        {
            return currentStats;
        }
    }
    
    <strong>    protected virtual void Update()
        {
            currentCooldown -= Time.deltaTime;
    
            if (currentCooldown <= 0f)
            {
                Attack(currentStats.number); //Line91
            }
        }</strong>

    This part of the update appears in the console window.

    #15760
    정춤고 (ChoomGo)
    Level 13
    Former Patron
    Helpful?
    Up
    0
    ::
    NullReferenceException: Object reference not set to an instance of an object.
    ProjectileWeapon.Attack (System.Int32 attackCount) (at Assets/02. Scripts/Weapon/WeaponEffects/ProjectileWeapon.cs:62)
    Weapon.Update () (at Assets/02. Scripts/Weapon/Weapon.cs:91)
    ProjectileWeapon.Update () (at Assets/02. Scripts/Weapon/WeaponEffects/ProjectileWeapon.cs:14)
    
    <strong>float spawnAngle = Mathf.Atan2(playerController.lastMovedVector.y, playerController.lastMovedVector.x) * Mathf.Rad2Deg; //Line 62 </strong>
    #15761
    정춤고 (ChoomGo)
    Level 13
    Former Patron
    Helpful?
    Up
    0
    ::
    lReferenceException: Object reference not set to an instance of an object
    Projectile.Start () (at Assets/02. Scripts/Weapon/WeaponEffects/Projectile.cs:20)
    using System.Collections;
    using System.Collections.Generic;
    using Unity.VisualScripting.Antlr3.Runtime.Misc;
    using UnityEngine;
    
    [RequireComponent(typeof(Rigidbody2D))]
    public class Projectile : WeaponEffect
    {
        public enum DamageSource { projectile, owner}
        public DamageSource damageSource = DamageSource.projectile;
        public bool hasAutoAim = false;
        public Vector3 rotationSpeed = new Vector3(0, 0, 0);
    
        protected Rigidbody2D rigid;
        protected int piercing;
    
        protected virtual void Start()
        {
            rigid = GetComponent<Rigidbody2D>();
    <strong>        Weapon.Stats stats = weapon.GetStats(); //Line20</strong>
    
            if (rigid.bodyType == RigidbodyType2D.Dynamic)
            {
                rigid.angularVelocity = rotationSpeed.z;
                rigid.velocity = transform.right * stats.speed;
            }
    
            float area = stats.area == 0 ? 1 : stats.area;
    
            transform.localScale = new Vector3(
                area * Mathf.Sign(transform.localScale.x),
                area * Mathf.Sign(transform.localScale.y), 1);
    
            piercing = stats.piercing;
    
            if (stats.lifespan > 0)
            {
                Destroy(gameObject, stats.lifespan);
            }
    
            if (hasAutoAim)
            {
                AcquireAutoAimFacing();
            }
        }
    
        public virtual void AcquireAutoAimFacing()
        {
            float aimAngle;
    
            EnemyStats[] targets = FindObjectsOfType<EnemyStats>();
    
            if (targets.Length > 0)
            {
                EnemyStats selectedTarget = targets[Random.Range(0, targets.Length)];
                Vector2 difference = selectedTarget.transform.position - transform.position;
                aimAngle = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
            }
            else
            {
                aimAngle = Random.Range(0f, 360f);
            }
    
            transform.rotation = Quaternion.Euler(0, 0, aimAngle);
        }
    
        protected virtual void FixedUpdate()
        {
            if (rigid.bodyType == RigidbodyType2D.Kinematic)
            {
                Weapon.Stats stats = weapon.GetStats();
                transform.position += transform.right * stats.speed * Time.fixedDeltaTime;
                rigid.MovePosition(transform.position);
                transform.Rotate(rotationSpeed * Time.fixedDeltaTime);
            }
        }
    
        protected virtual void OnTriggerEnter2D(Collider2D coll)
        {
            EnemyStats enemyStats = coll.GetComponent<EnemyStats>();
            BreakableProps props = coll.GetComponent<BreakableProps>();
    
            if (enemyStats)
            {
                Vector3 source = damageSource == DamageSource.owner && ownerStats ?
                    ownerStats.transform.position : transform.position;
    
                enemyStats.TakeDamage(GetDamage(), source);
    
                Weapon.Stats weaponStats = weapon.GetStats();
    
                piercing--;
    
                if (weaponStats.hitEffect)
                {
                    Destroy(Instantiate(weaponStats.hitEffect, transform.position, Quaternion.identity), 5f);
                }
            }
            else if (props)
            {
                props.TakeDamage(GetDamage());
    
                piercing--;
    
                Weapon.Stats weaponStats = weapon.GetStats();
    
                if (weaponStats.hitEffect)
                {
                    Destroy(Instantiate(weaponStats.hitEffect, transform.position, Quaternion.identity), 5f);
                }
            }
    
            if (piercing <= 0)
            {
                Destroy(gameObject);
            }
        }
    }
    #15762
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    The issue seems to be with this line:

    float spawnAngle = Mathf.Atan2(playerController.lastMovedVector.y, playerController.lastMovedVector.x) * Mathf.Rad2Deg; //Line 62

    Because you don’t have a PlayerController component attached to your GameObject. We never created a PlayerController script in our tutorial, so I don’t know what that’s referring to.

    #15763
    정춤고 (ChoomGo)
    Level 13
    Former Patron
    Helpful?
    Up
    0
    ::

    PlayerController.cs is PlayerMovement.cs I only changed the name of the CS file.

    #15765
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    The issue is that your Initialise() function is not calling. Try making the following change to your PlayerInventory script’s Add() function:

        // Finds an empty slot and adds a weapon of a certain type, returns
        // the slot number that the item was put in.
        public int Add(WeaponData data)
        {
            int slotNum = -1;
    
            // Try to find an empty slot.
            for(int i = 0; i < weaponSlots.Capacity; i++)
            {
                if (weaponSlots[i].IsEmpty())
                {
                    slotNum = i;
                    break;
                }
            }
    
            // If there is no empty slot, exit.
            if (slotNum < 0) return slotNum;
    
            // Otherwise create the weapon in the slot.
            // Get the type of the weapon we want to spawn.
            Type weaponType = Type.GetType(data.behaviour);
    
            if (weaponType != null)
            {
                // Spawn the weapon GameObject.
                GameObject go = new GameObject(data.baseStats.name + " Controller");
                Weapon spawnedWeapon = (Weapon)go.AddComponent(weaponType);
                spawnedWeapon.Initialise(data);
                spawnedWeapon.transform.SetParent(transform); //Set the weapon to be a child of the player
                spawnedWeapon.transform.localPosition = Vector2.zero;
                spawnedWeapon.Initialise(data);
                spawnedWeapon.OnEquip();
    
                // Assign the weapon to the slot.
                weaponSlots[slotNum].Assign(spawnedWeapon);
    
                // Close the level up UI if it is on.
                if (GameManager.instance != null && GameManager.instance.choosingUpgrade)
                    GameManager.instance.EndLevelUp();
    
                return slotNum;
            }
            else
            {
                Debug.LogWarning(string.Format(
                    "Invalid weapon type specified for {0}.",
                    data.name
                ));
            }
    
            return -1;
        }

    And remove the Start() and Awake() functions on Weapon. This should force the Initialise() function to activate.

    #15777
    정춤고 (ChoomGo)
    Level 13
    Former Patron
    Helpful?
    Up
    0
    ::

    Thank you for your efforts. The problem is still not solved, but I’m sorry to take away your time anymore.

    I’ll try to work on my own.

    #15778
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Ok, hopefully you manage to find the issue. Let me elaborate on your issue a bit more. NullReferenceException errors occur when you try to use a variable that is unassigned or missing. For example:

    Rigidbody2D rb; // No value assigned.
    
    // Gives an error because rb.velocity evaluates to null.velocity
    rb.velocity = new Vector(3, 2);

    In your case, if we take one of your errors as an example:

        protected virtual void Update()
        {
            currentCooldown -= Time.deltaTime;
    
            if (currentCooldown <= 0f)
            {
                Attack(currentStats.number); //Line91
            }
        }

    The error is occurring because currentStats is null, hence causing it to evaluate to null.number when you use currentStats.number.

    currentStats is assigned in the Initialise() function in Weapon.

        public virtual void Initialise(WeaponData weaponData)
        {
            base.Initialise(weaponData);
            this.weaponData = weaponData;
            currentStats = weaponData.baseStats;
            playerController = GetComponent<PlayerController>();
            currentCooldown = currentStats.cooldown;
        }

    That is why I suspected the issue is with Initialise(). I hope this helps!

    #15786
    정춤고 (ChoomGo)
    Level 13
    Former Patron
    Helpful?
    Up
    0
    ::

    Invalid AABB inAABB UnityEngine.Canvas:SendWillRenderCanvases ()

    Is this error related to the prefab I created?

    #15787
    정춤고 (ChoomGo)
    Level 13
    Former Patron
    Helpful?
    Up
    0
    ::

    playerController = GetComponent<PlayerController>(); -> playerController = GetComponentInParent<PlayerController>();

    It works when I change it!

    #15802
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    Fantastic. Glad you managed to fix it!

Viewing 13 posts - 1 through 13 (of 13 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: