I am working on a script to make my top-down 2d enemy, melee attack the player. My script works but not correctly. I want the enemy to attack the player when it gets in range but I want a delay between the attacks. I thought I had it setup right to attack once every 5 seconds using Time.time but it waits for the delay and then attacks continuously, like 800 times in 5 seconds. I assume I am using Time.time incorrectly. I thought it was a measure of seconds in actual game time. This script is separate from the enemy wander code. Here is the meleeattack script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMeleeAttack : MonoBehaviour
{
public float attackRange = 1.5f;
public int damage;
public GameObject playerHitEffect;
public float timeBetweenAttacks;
public Transform target;
private float timeSinceLastAttack;
void Start()
{
//find the player
target = GameObject.FindGameObjectWithTag("Player").transform;
}
void Update()
{
//check distance to the Player
float distanceToPlayer = Vector3.Distance(transform.position, target.position);
if(distanceToPlayer < attackRange)
{
//check if enough time has passed between attacks
if (Time.time > timeSinceLastAttack + timeBetweenAttacks)
{
//Attack the player
MeleeAttack();
//record the attack so we can start the countdown to the next attack
timeSinceLastAttack = Time.time;
}
}
}
public void MeleeAttack()
{
Debug.Log("Enemy Attack");
PlayerHealth attackPlayer = target.GetComponent<PlayerHealth>();
attackPlayer.TakeDamage(damage);
Instantiate(playerHitEffect, transform.position, transform.rotation);
}
}
The first suggestion is to Debug.Log what Time.time and what timeSinceLastAttack + timeBetweenAttacks values are before the if check to make sure you have the values you expect. Also make sure your timeBetweenAttacks is a value that you expect.
Not exactly sure what’ going wrong, but I suspect it has to do with using Time.time. I’d recommend trying the following.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMeleeAttack : MonoBehaviour
{
public float attackRange = 1.5f;
public int damage;
public GameObject playerHitEffect;
public float timeBetweenAttacks;
public Transform target;
private float timer, timerDelay; //changed
void Start()
{
//set delay in seconds
timerDelay = 5;
//find the player
target = GameObject.FindGameObjectWithTag("Player").transform;
}
void Update()
{
//set timer to advance with time
timer += Time.deltaTime;
//check distance to the Player
float distanceToPlayer = Vector3.Distance(transform.position, target.position);
if (distanceToPlayer < attackRange)
{
//check if enough time has passed between attacks
if (timer > timerDelay)
{
//Attack the player
MeleeAttack();
//reset the timer
timer = 0; //changed
}
}
}
public void MeleeAttack()
{
Debug.Log("Enemy Attack");
PlayerHealth attackPlayer = target.GetComponent<PlayerHealth>();
attackPlayer.TakeDamage(damage);
Instantiate(playerHitEffect, transform.position, transform.rotation);
}
}
Thanks for the reply. I tried that and even initializing timer to zero in Start but it seems like the delay is only used one time (on initial contact) and after that the enemy attacks every frame.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMeleeAttack : MonoBehaviour
{
public float attackRange = 1.5f;
public int damage;
public GameObject playerHitEffect;
public Transform target;
private float timer, timerDelay;
void Start()
{
//find the player
target = GameObject.FindGameObjectWithTag("Player").transform;
timerDelay = 3;
timer = 0;
}
void Update()
{
//set timer to advance time
timer += Time.deltaTime;
//check distance to the Player
float distanceToPlayer = Vector3.Distance(transform.position, target.position);
if(distanceToPlayer < attackRange)
{
//check if enough time has passed between attacks
if (timer > timerDelay)
{
//Attack the player
MeleeAttack();
//record the attack so we can start the countdown to the next attack
timer = 0;
}
}
}
public void MeleeAttack()
{
Debug.Log("Enemy Attack");
PlayerHealth attackPlayer = target.GetComponent<PlayerHealth>();
attackPlayer.TakeDamage(damage);
Instantiate(playerHitEffect, transform.position, transform.rotation);
}
}
When you say that the enemy keeps attacking, are you referring to an animation, or that your player’s health keeps dropping? Also, what are you doing with the playerHitEffect on line 50 after it’s instantiated? It would help narrow down the potential problems if you posted the PlayerHealth script as well as the script attached to playerHitEffect, if there is one.
I even added a “timer = 0” line to the meleeAttack function but for some reason the timer is never reset. I added a debug.log showing the timer and as you can see it does not seem to reset after an attack
I dont have any animations setup yet, it is just a static enemy at the moment. I normally get the code to work and then add the animation/animation event for the attacks. I have not added a playerhiteffect yet either. I normally save that till after the code works as well. I plan to do a simple blood splatter effect. (I might be a bit backwards in how I do things but i like the code to work first and then add the animations/effects since the coding seems to be my weakest area)
I can not get the image to show in a post but I did link the console display below showing the attackes and what the timer value is at each moment of attack
You have an error. Errors throw things off. Fix the error first and see if that helps. Even if all you do is check for null and then don’t execute the code throwing the error.
LOL, I dont know how but by commenting out the playerhiteffect it now seems to work. Thanks for all your help. I dont understand how having the null reference to the playerhiteffect stopped the timer from resetting but what the heck!
Thanks again for the help
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMeleeAttack : MonoBehaviour
{
public float attackRange = 1.5f;
public int damage;
//public GameObject playerHitEffect;
public Transform target;
private float timer, timerDelay;
void Start()
{
//find the player
target = GameObject.FindGameObjectWithTag("Player").transform;
//initialize timer values
timerDelay = 3;
timer = 0;
}
void Update()
{
//set timer to advance time
timer += Time.deltaTime;
//check distance to the Player
float distanceToPlayer = Vector3.Distance(transform.position, target.position);
if(distanceToPlayer < attackRange)
{
//check if enough time has passed between attacks
if (timer > timerDelay)
{
//Attack the player
MeleeAttack();
//record the attack so we can start the countdown to the next attack
timer = 0;
}
}
}
public void MeleeAttack()
{
Debug.Log("Enemy Attack");
Debug.Log("Current Timer Value = " +timer);
PlayerHealth attackPlayer = target.GetComponent<PlayerHealth>();
attackPlayer.TakeDamage(damage);
//Instantiate(playerHitEffect, transform.position, transform.rotation);
}
}
Because it’s an error. When errors get thrown, almost always your code will stop executing. So MeleeAttack hits the end of it’s method and throws an error. timer = 0 never executes. But Update will run again. And the cycle continues, always hitting that error and not running the timer = 0 code.
I suspect that maybe you just haven’t placed a gameobject in the playerHitEffect in your inspector window. If this is empty, it will cause a null reference exception. Happens to me all the time. And Brathnann is right, this error will stop your code dead.