In my game I have a shooting enemy that I want to attack the player only when he's in range, and to attack him once every 3 seconds. This is the code I need repeated
Whatever I try doing just results in the enemy shooting a metric ton of bullets at the player to the point when it sometimes crashes my project(tried for loops, while loops, played with booleans and timing with Time.deltaTime). Can someone smarter than me give me some pointers how I could do this?
CodePudding user response:
You'd probably want to do the check not every frame but whenever the state changes. So you cache the player.
Transform target;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
target = other.transform;
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("Player"))
target = null;
}
Whenever target
is not null, you may shoot to it.
private void Update()
{
if (target != null)
ShootAt(target.position);
}
private void ShootAt(Vector3 position)
{
// Spawn your stuff here to shoot at the given location
}
Now how about the timing? I am not a fan of coroutines in Unity. They tend to get messy, are overused and the "WaitForSeconds" is not very precise. It just waits until the time has passed, but not exactly. Maybe you want more precision, who knows?
So let's extend the Update
call to have a small timer.
private float shootCooldown;
private void Update()
{
if (target != null)
{
shootCooldown -= Time.deltaTime;
if (shootCooldown <= 0.0f)
{
shootCooldown = 3.0f;
ShootAt(target.position);
}
}
}
One last thing: If you want the timer to reset at some point, just set it to 0.0f
. A good location for that would be when the player leaves the trigger. Also, maybe you want the timer to continue to go down, even if the target is null, so move the shootCooldown -= Time.deltaTime
line out of the if
-scope but make sure it does not count down forever, but rests at 0
.
CodePudding user response:
I would avoid using OnTriggerStay2D() and use OnTriggerEnter2D and OnTriggerExit2D in this situation. You need to call the 'yield return' statement within a loop as shown below. The below code should do what you are asking.
private void OnTriggerEnter2D(Collider2D other) {
if (other.tag == "Player") {
StartCoroutine(ShootAtPlayer());
}
}
private void OnTriggerExit2D(Collider2D other) {
if (other.tag == "Player") {
StopAllCoroutines();
}
}
private IEnumerator ShootAtPlayer() {
while (true) {
Instantiate(enemyBullet, enemy.transform.localPosition, transform.LocalRotation);
yield return new WaitForSeconds(3.0f);
}
}
CodePudding user response:
Not directly linked to the question, since I think it might have already be answered, but I might have two suggestions.
Try to use object pooling, instead of instantiating a new GameObject every time, which should save you quite a loot of performance.
And new WaitForSeconds()
is allocating, so if you use Coroutines try using it like this:
private IEnumerator myCoroutine() {
WaitForSeconds wait = new WaitForSeconds(3.0f);
while (true) {
// do stuff
yield return wait;
}
}