Equip A Weapon

Well, technically, I should have named this lecture “wield a weapon” but for now its kinda the same thing. If you have any early questions or comments please leave them here.

I’m interested, does anyone have any thoughts on my silly oversized sword that I gave to the characters?

1 Like

@Rick_Davidson Looks more like a big stone club than a sword if I’m honest :rofl: Maybe make it a little thinner?

The Pen…cil is mightier than the sword.

6 Likes

I’m having some trouble. I made a different system, where the weapon has a Collider (not triggered) and the player and enemies have a collider (triggered).

Sometimes, I change the layer and deactivate the collider (because of reasons). I was doing this as a prototype and the weapon was part of the player prefab, instead of a GO that must be instantiated.

The issue is that that game freezes for a few frames when I perform these actions (runtime) (if the item is instantiated) and runs normally if the item is part of the Player prefab (like his fists or head).

It sounds like a for or a foreach loop is taking far too much time to complete…

What specific thing is causing a “freeze”? Equipping an item? Hitting a character with a weapon?

I’ll add some context:

Enemies and the Player Character have collider (triggered) for their hitboxes and weapons with their own collider (not triggered).

With this, I’m creating the effect of actually hitting the targets instead of using range (the movement is by using a gamepad, not point and click). I have 2 methods called by an Animator Event (Enable and Disable Weapons) to match the “hitting” part of the animation (I don’t want a sword to be able to hit the target when it is barely starting to swing)

If the attack gets interrupted before disabling the weapon, a state machine calls the method to Disable the weapon, because if an enemy hits the player while the player is attacking another target, the player’s attack is canceled.

So, when the player (or enemy) swings his weapon, during the attack animation (calling 2 animator events, calling Enable and Disable Weapon) if the weapon is instantiated instead of being part of the player’s prefab, it freezes for some frames.

The Methods are very simple, they change the layer of the weapon (to avoid the player and enemy colliding with themselves) and enable/disable the collider.

This happens at one specific frame when the player/enemy attacks.

    public void EnableWeaponDamage()
    {
        m_collider.enabled = true;
        if (thisIsThePlayer) m_Weapon.layer = playerWeaponLayer;
        else m_Weapon.layer = enemyWeaponLayer;
    }

    public void DisableWeaponDamage()
    {
        m_collider.enabled = false;
        m_Weapon.layer = propsLayer; //Weapon goes to the Props layers
    }```

It may well have to do with changing the layers within the game. I haven’t experimented with this, and truth be told, this is a case where a simple boolean would suffice…

public void EnableWeaponDamage()
{
    attackIsActive=true;
}
public void DisableWeaponDamage()
{
    attackIsActive=false;
}
void OnTriggerEnter()
{
    if(!attackIsActive) return;
}

I was thinking that since enemy and player weapons are in different layers, I could just ignore those layers from colliding in the settings, that way I would just stop the necessity to change layers.

Yes, but set those layers preferably in the prefabs, but if you must set a layer on a weapon that could be either one, do it when it’s equipped, once and once only. Even then, you still may get a slowdown while the physics system adjusts itself for the changed layers. The bool can quickly discard hits that aren’t “legal”

1 Like

I fixed the layers and changed the collision in the Project Setting - Physics. Worked perfectly.
And changed the logic to check a bool, and it works amazing. Now the entire system feels more light.

I cannot thank you enough, Brian!

1 Like

Something weird is happening, this should be simple right, i follow the step one by one and when i run the game, it spawn the sword in the player prefabs hand and then throws the following error:

UnassignedReferenceException: The variable weaponPrefab of Fighter has not been assigned.
You probably need to assign the weaponPrefab variable of the Fighter script in the inspector.
UnityEngine.Object.Instantiate (UnityEngine.Object original) (at <53aac14d88ba4477acc998b039cfd73a>:0)
UnityEngine.Object.Instantiate (UnityEngine.Object original, UnityEngine.Transform parent, System.Boolean instantiateInWorldSpace) (at <53aac14d88ba4477acc998b039cfd73a>:0)
UnityEngine.Object.Instantiate[T] (T original, UnityEngine.Transform parent, System.Boolean worldPositionStays) (at <53aac14d88ba4477acc998b039cfd73a>:0)
UnityEngine.Object.Instantiate[T] (T original, UnityEngine.Transform parent) (at <53aac14d88ba4477acc998b039cfd73a>:0)
RPG.Combat.Fighter.SpawnWeapon () (at Assets/Scripts/Combat/Fighter.cs:57)
RPG.Combat.Fighter.Start () (at Assets/Scripts/Combat/Fighter.cs:18)

My player prefab is like this:

And my code is the following:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RPG.Movement;
using RPG.Core;

namespace RPG.Combat{
    public class Fighter : MonoBehaviour, IAction{
        [SerializeField] float weaponRange = 2f;
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] float weaponDamage = 5f;
        [SerializeField] GameObject weaponPrefab = null;
        [SerializeField] Transform handTransform = null;
        Health target;
        float timeSinceLastAttack = Mathf.Infinity;

        private void Start() {
            SpawnWeapon();
        }

        private void Update(){
            timeSinceLastAttack += Time.deltaTime;

            if(target == null) return;
            if(target.GetIsDead()) return;

            if (!GetIsInRange()){
                GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            } else{
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        //Public
        public bool CanAttack(GameObject target){
            if(target == null) return false;

            Health targetToTest = target.GetComponent<Health>();
            
            return targetToTest != null && !targetToTest.GetIsDead();
        }

        public void Attack(GameObject combatTarget){
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel(){
            TriggerStopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        //Private
        private void SpawnWeapon(){
            Instantiate(weaponPrefab, handTransform);
        }

        private bool GetIsInRange(){
            return Vector3.Distance(transform.position, target.transform.position) < weaponRange;
        }

        private void AttackBehaviour(){
            transform.LookAt(target.transform);

            if(timeSinceLastAttack > timeBetweenAttacks){
                //This will trigger the Hit() event.
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        
        //Animations
        private void TriggerAttack(){
            GetComponent<Animator>().ResetTrigger("Stop Attack");
            GetComponent<Animator>().SetTrigger("Attack");
        }

        private void TriggerStopAttack(){
            GetComponent<Animator>().ResetTrigger("Attack");
            GetComponent<Animator>().SetTrigger("Stop Attack");
        }
        
        //Animation Event
        void Hit(){
            if(target == null) return;
            
            target.TakeDamage(weaponDamage);
        }
    }
}

Im using Unity 2021.3.33f1, so if there is any mistake or glitch that someone can see, that i can’t i would be thankful. I will keep trying, but i just don’t get it.

So i have been checking and the problem is for the enemies for some reason, they don’t like not having a weapon… They don’t like to be unhanded. Any ideas of what i can do to fix it, again the set up im using, it’s on the post i made above.
Btw, i just realize why probably i have not gotten an answer, the problem is im not getting warnings, it’s errors. The game it’s not running if the enemies don’t have weapons, and i don’t know if this could be a big problem.

Maybe check if this hint applies to your Enemy prefab? You have only shown us the Player prefab.

I have done that, sorry i didn’t mention, it still throws the problem

I just added a check similar to what we do with weapon later on, on the scriptable objects and it works now… So yeah, you have through of that. Thanks for the attention daberny, and if i sounded… Less then plesent,i apologies, this has been going around my head all weekend and just today i decided to keep going and now im just facepalming hard.

1 Like

Don’t worry! I am glad you solved it. Good luck and have fun going forward!

Thanks and same to you too :smiley:

1 Like

Sorry for the delay on my end. I tend to see new Ask topics much easier than Talk topics, so I didn’t notice this one.

Any character with a Fighter component (that’s the Player and the Enemies) must have their Hand Transforms set, and must have a default weapon assigned.
One unfortunate feature of the Missing Reference Exception is that it doesn’t tell you which character is missing the reference, which means sometimes we have to go hunting (or Debug.Log with the character’s name, something like

if(weaponPrefab==null)
{
    Debug.LogError($"{gameObject.name}'s Fighter component has no WeaponPrefab assigned!");
    return;
}

What I did was checking if the Fighter was properly configured and only equip the weapon if there is actually one and the hand to put it into is set as well:

        private void SpawnWeapon()
        {
            if (null != weaponPrefab && null != handTransform)
            {
                Instantiate(weaponPrefab, handTransform);
            }
        }

Privacy & Terms