In this lecture we added damage to our projectiles. I’m still pretty excited that we have finally implemented the functionality that eluded us first time round in this course - with both the the player and enemies we can can easily switch between melee and projectile weapons.
I’ve got something odd going on with my enemy that’s got a bow equipped. For some reason he doesn’t lay down when he dies. He does the animation and then just stands there. There’s also an issue where the enemies seem to die after one arrow is shot, rather than 2. each enemy has 20 HP and the bow does 10 damage. Took a look through the GitHub repository and couldn’t find anything in the scripts that’s different. I’m rather thorough when it comes to following along, but I can’t see what I missed. I can post a video, screenshots, or the whole game if needed. Just let me know.
Solved it. forgot to delete this line: target.TakeDamage(currentWeapon.GetDamage());
and had the bow attack overriding the death instead of the sword attack… guess that’s what happens when you do things at 3 AM.
Add the following line before the return statement in GetIsInRange()
if(!target) return false;
I replaced the target with currentWeapon and now it works. But thank You for your help otherwise I would not find it.
if (!currentWeapon) return false;
Ah, glad it was sorted. It’s actually generally a good idea to protect all reference calls.
Just in case this helps anyone: although I’ve been following the entire course carefully, at some stage I missed the addition of a RigidBody to the Enemy Prefab Variant, which prevented my arrows from hitting them as no collision was detected.
For some reason my arrows, when the target field in Projectile class is private and NOT serialized, they get the target set but then on update they do not have target and the method exits, which leaves the arrows suspended in midair.
If I add a [SerializedField] before the target variable, all works perfectly. I have no idea why this happens but it bothers me not understanding it!
Ok the same happnes with the damage… if I don’t serialize it it won’t get set…
Post your Projectile.cs class and we’ll take a look at what might be going on.
At work now but will do later on today.
However the code seems to be right, when I serialize the variables they do work. I introduced print statements and I realized that when I set the variables to private for the class, the public method SetTarget doesn’t seem to work on its own class private variables…
It’s the only bit that I change and creates differences…
Is there anything new in Unity/C# that prevents modifying private variables of a class with a public method on the same class? Should I try implementing a getter/setter?
Forgot I have the code on GitHub, here it is:
using RPG.Core;
using System.Collections;
using UnityEngine;
namespace RPG.Combat
{
public class Projectile : MonoBehaviour
{
[SerializeField] float speed = 1f;
[SerializeField] Health target;
[SerializeField] float damage = 0;
[SerializeField] bool isArrow;
float delayArrowDestroy = 0.2f;
void Update()
{
if (target == null)
{
print("No target");
return;
}
transform.LookAt(GetAimLocation());
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
public void SetTarget(Health target, float damage)
{
this.damage = damage;
this.target = target;
}
private Vector3 GetAimLocation()
{
CapsuleCollider targetCapsule = target.GetComponent<CapsuleCollider>();
if (!targetCapsule)
{
return target.transform.position;
}
return target.transform.position + Vector3.up * targetCapsule.height / 2;
}
private void OnTriggerEnter(Collider other)
{
Health otherHealth = other.GetComponent<Health>();
if (target != otherHealth) return;
otherHealth.TakeDamage(damage);
if (isArrow)
{
StartCoroutine(DelayDestroy());
}
else
{
Destroy(gameObject);
}
}
IEnumerator DelayDestroy()
{
yield return new WaitForSeconds(delayArrowDestroy);
Destroy(gameObject);
}
}
}
Not that I’m aware of . I have absolutely no idea why it’s not working. What I was looking for in the code was anything that might be overwriting target and damage. Serializing should only really be affecting things when the object is in the scene heirarchy, and not when the item is instantiated in the scene.
Let’s add a debug to SetTarget()
public void SetTarget(Health target, float damage)
{
Debug.Log($"Setting target to {target} and damage to {damage}");
this.damage=damage;
this.target=target;
if(this.target==null)
{
Debug.LogWarning($"Target == null");
}
else
{
Debug.Log($"Target has been set to {this.target}, damage has been set to {this.damage}");
}
}
I did what you say. Added a check in Update. The target is set but then lost by the time Update is called.
This is if I strip [SerializeField] from the variables.
Found the error:
Weapon script, LaunchProjectile method. I instantiated the projectile in a variable and then called another one for SetTarget (I’m so dumb).
Sorry and thnaks mate!
These sorts of things happen. Unfortunately, it’s not always easy to just look at a problem and say “Oh, that’s this” (well… I’d say for about 75% of student issues I had a ready fix, but usually those are things I’ve seen many many times.).
I’m not certain what’s happened, but now when an enemy dies, sometimes he floats up and away after completing the death animation.
The damage is decreasing as expected, and the arrow destroys itself properly upon impact when the bow is equipped.
I also tested with the Sword and Unarmed attacks, and it happens inconsistently then, too.
Any suggestions on what I should be looking at to address this issue? It’s weird that it doesn’t happen all the time.
It sounds like his RigidBody is misbehaving.
It’s best to make sure that the RigidBody is set to isKinematic=true.
I like to delete the colliders when an enemy dies as well. RigidBodies with no colliders are just dead scripts.
Thanks! I had forgotten to set the Rigidbody to isKinematic. All my enemies are behaving as expected now that I’ve set it to true.