For one of my major projects, I have been trying to create a mechanic where one of my characters has a deadly bullet. When the bullet hits an enemy, they will take steady poison damage for e.g. 5 seconds. Below are the scripts that I have so far: Enemy(Works well, no concerns): For Reference - This is where the ‘health’ is taken away once it receives poison damage.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMech : MonoBehaviour
{
public static int health = 100;
public GameObject deathEffect;
public void TakeDamage (int damage)
{
health -= damage;
if (health <= 0)
{
Die();
}
}
void Die ()
{
Instantiate(deathEffect, transform.position, Quaternion.identity);
Destroy(gameObject);
}
}
BulletMechA (Works well, copied from reference video):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletMechA : MonoBehaviour
{
public float speed = 20f;
public int damage = 40;
public Rigidbody2D rb;
void Start()
{
GetComponent<Rigidbody>().AddForce(Vector3.forward * speed);
}
private void OnTriggerEnter2D(Collider other)
{
if (other.GetComponent<AcidDamage>() != null)
{
other.GetComponent<AcidDamage>().ApplyPoison(6);
}
Destroy(gameObject);
}
}
AcidDamage(copied from reference video but keeps receiving ‘(37,13): error CS0103: The name ‘enemy’ does not exist in the current context’): To test - I would put 25 damage per tick for 5 ticks (if the enemy doesn’t die in 5 seconds, the script doesn’t work)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AcidDamage : MonoBehaviour
{
public int damage = 20;
public List<int> poisonTickTimer = new List<int>();
public void ApplyPoison(int ticks)
{
if (poisonTickTimer.Count <= 0)
{
poisonTickTimer.Add(ticks);
StartCoroutine(Poisoned());
}
else
{
poisonTickTimer.Add(ticks);
}
}
IEnumerator Poisoned()
{
while (poisonTickTimer.Count > 0)
{
for (int i = 0; 1 < poisonTickTimer.Count; i++)
{
poisonTickTimer[i]--;
}
[B] enemy.TakeDamage(damage); //CS0103[/B]
poisonTickTimer.RemoveAll(i => i == 0);
yield return new WaitForSeconds(0.75f);
}
}
}
Enemy Mech was made by me but the other 2 scripts were made from this video: (
) This is a tutorial made by Dapper Dino and I copied the scripts to a good standard. Was there something I missed? Or is there an easier script I could use for Poison Damage/Damage over time?
I understand the issue but I used this instead as I didn’t have a separate health script for the enemy (as EnemyMech has the health in it).
The below script shows what I was trying to resemble (although I didn’t have the poison mechanic created yet).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletMech : MonoBehaviour
{
public float bulletspeed = 20f;
public int damage = 40;
public Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb.velocity = transform.right * bulletspeed;
}
private void OnTriggerEnter2D(Collider2D hitInfo)
{
EnemyMech enemy = hitInfo.GetComponent();
if (enemy != null)
{
enemy.TakeDamage(damage);
}
Destroy(gameObject);
}
}
In other words, this is how an enemy would take damage normally if the poison mechanic wasn’t made. With your comment, I wonder how to include ‘enemy’ to ensure that the error disappears.
The easiest way would be to just put the poison damage code inside of your EnemyMech class, just like you did with TakeDamage. Why have it in its own MonoBehavior class? If it’s always going to be on the same object that another class is on, I don’t really see the point in making another script to attach to every object.
Thank you for the idea, I think this is a good idea. However, Now I am receiving a different error:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletMechA : MonoBehaviour
{
public float bulletspeed = 20f;
public int damage = 40;
public Rigidbody2D rb;
public List<int> poisonTickTimer = new List<int>();
// Start is called before the first frame update
void Start()
{
rb.velocity = transform.right * bulletspeed;
}
private void OnTriggerEnter2D(Collider2D hitInfo) //CS1624
{
EnemyMech enemy = hitInfo.GetComponent<EnemyMech>();
while (poisonTickTimer.Count > 0)
{
for (int i = 0; 1 < poisonTickTimer.Count; i++)
{
poisonTickTimer[i]--;
}
enemy.TakeDamage(damage);
poisonTickTimer.RemoveAll(i => i == 0);
yield return new WaitForSeconds(0.75f);
}
if (enemy == null)
{
Destroy(gameObject);
}
}
}
I attempted to mix the Enemy Mech with the Poison script a bit however, I don’t think Unity understands the way I written the private void as it came up with ‘error CS1624: The body of ‘BulletMechA.OnTriggerEnter2D(Collider2D)’ cannot be an iterator block because ‘void’ is not an iterator interface type’.
Sticking with your idea, how can we re-format the above to get the poison mechanic working?
Your error is telling you that you can’t have a “yield return” inside of your method because it has the incorrect return type. You’re only supposed to “yield return” in Coroutines.
It doesn’t really seem like you know exactly what you’re doing, but what I was trying to get you to do is to basically copy/paste your AcidDamage code into the EnemyMech class, and then call the ApplyPoison method from your bullet on hit.
Thank you so much! The code basically works. Just one more thing, I set the poison time limit (poisonTickTimer) to 5 seconds but it seams to kill the enemy at only 3 seconds (or less. Enemy health is 100 and damage is set to 25). Here is what I have done:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletMechA : MonoBehaviour
{
public float bulletspeed = 20f;
public int damage = 10;
public Rigidbody2D rb;
public List<int> poisonTickTimer = new List<int>();
// Start is called before the first frame update
void Start()
{
rb.velocity = transform.right * bulletspeed;
if (poisonTickTimer.Count <= 0)
{
poisonTickTimer.Add(10);
}
}
private void OnTriggerEnter2D(Collider2D hitInfo)
{
EnemyMech enemy = hitInfo.GetComponent<EnemyMech>();
if (enemy != null)
{
StartCoroutine(Poisoned());
}
IEnumerator Poisoned()
{
while (poisonTickTimer.Count > 0)
{
for (int i = 0; 1 < poisonTickTimer.Count; i++)
{
poisonTickTimer[i]--;
}
EnemyMech enemy = hitInfo.GetComponent<EnemyMech>();
enemy.TakeDamage(damage);
poisonTickTimer.RemoveAll(i => i == 0);
yield return new WaitForSeconds(1f);
if (enemy == null)
{
Destroy(gameObject);
}
}
}
}
}
It would be nice to set the poison time limit as this could vary between many games that I could make in the future. Otherwise, I think this is not a bad code despite the fact that it kills enemies (regardless of damage value) in around 3 seconds. Thanks for your help but if you can solve this small problem, please let me know!
The point of the poisonTickTimer is that is how long the poison lasts on the target, not how long it takes to kill. If an enemy has 100 health and takes 25 damage a second, it’s only going to take 4 seconds to kill them, that’s just simple math.