Unity 2D Enemies don't take damage - c#

I'm a newbie in unity and game development. I'm trying to build a combat 2d game, but I can't damage enemies and I'm banging my head against a wall for 1 week, because I can't see where is the error.
This is my Player script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAttack : MonoBehaviour
{
//[SerializeField] private float attackCooldown;
[SerializeField] private float range;
[SerializeField] private int damage;
[SerializeField] private LayerMask enemyLayer;
public Transform AttackPoint;
//private float cooldownTimer = Mathf.Infinity;
private Animator anim;
private Enemy enemyHealth;
private void Awake()
{
anim = GetComponent<Animator>();
enemyHealth = GetComponent<Enemy>();
}
private void Update()
{
if (Input.GetButtonDown("Fire1"))
{
anim.SetTrigger("Attack");
Attack();
Debug.Log("attacking");
}
}
private void OnDrawGizmos()
{
if (AttackPoint == null)
return;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(AttackPoint.position, range);
}
public void Attack()
{
Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(AttackPoint.position, range, enemyLayer);
foreach (Collider2D Enemy in hitEnemies)
{
if (Enemy.CompareTag("Enemy"))
Enemy.transform.GetComponent<Enemy>().TakeDamage(damage);
Debug.Log("Enemy Hit!");
}
}
}
And this is the Enemy script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
[SerializeField] private float startingHealth;
public float currentHealth;
public Animator anim;
private bool dead;
void Start()
{
currentHealth = startingHealth;
}
public void TakeDamage(float _damage)
{
//currentHealth = Mathf.Clamp(currentHealth - _damage, 0, startingHealth);
if (currentHealth > 0)
{
//hurt anim
Debug.Log("damage taken");
}
if (!dead)
{
//dead anim
GetComponent<EnemyPatrol>().enabled = false; //enemy cannot move while he is dead
GetComponent<EnemyMelee>().enabled = false; //enemy cannot attack
dead = true;
}
}
private void Update() //for testing
{
if (Input.GetKeyDown(KeyCode.P))
TakeDamage(1);
}
}
I tried testing it with the debug, and unity tells me that I'm hitting the enemy but without damaging him.
I tried also to damage the enemy pressing P for testing, and this works.
Thank you guys in advance for your help,
Federico.
Built Player and Enemy scripts. The player can be damaged by enemies but the player cannot damage the enemies.

In your attack function, here:
if (Enemy.CompareTag("Enemy"))
Enemy.transform.GetComponent<Enemy>().TakeDamage(damage);
Debug.Log("Enemy Hit!");
Only the TakeDamage line is inside the if statement, the log is not so it always executes. YOu need to change it look like this:
if (Enemy.CompareTag("Enemy"))
{
Enemy.transform.GetComponent<Enemy>().TakeDamage(damage);
Debug.Log("Enemy Hit!");
}
So the issue is therefore that CompareTag is returning false, if you're seeing the enemy hit log and not the take damage log. The enemy does not have the correct tag.

Related

How to use events in Unity?

I've watched bunch of videos about events in unity, but still cant figure out how to use them.
I have 2 scripts, in first i detect collision, second script should teleport an object with the first script attached.
First script
using UnityEngine;
public class PlayerShip : MonoBehaviour
{
private Rigidbody2D rb;
private float angle;
public delegate void TeleportHandler(GameObject Border);
public event TeleportHandler OnShipCollidedEvent;
[SerializeField] private float speedMoving;
[SerializeField] private float speedRotating;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
if (Input.GetAxis("Horizontal") != 0)
{
angle = -Input.GetAxis("Horizontal") * Time.deltaTime * speedRotating;
transform.Rotate(transform.rotation.x, transform.rotation.y, angle);
}
if (Input.GetKey(KeyCode.W))
rb.AddRelativeForce(Vector2.up * speedMoving);
}
private void OnTriggerEnter2D(Collider2D other)
{
this.OnShipCollidedEvent?.Invoke(other.gameObject);
}
}
Second script - OnShipCollided doesn't output Test
using UnityEngine;
public class BordersCommands : MonoBehaviour
{
private PlayerShip _playerShip;
[SerializeField] private GameObject LeftBorder;
[SerializeField] private GameObject RightBorder;
[SerializeField] private GameObject BotBorder;
[SerializeField] private GameObject TopBorder;
public BordersCommands(PlayerShip _playerShip)
{
this._playerShip = _playerShip;
this._playerShip.OnShipCollidedEvent += OnShipCollided;
}
private void OnShipCollided(GameObject border)
{
Debug.Log("Test");//Here will be teleportation
}
}
Solved by adding Borders = new BordersCommands(this); in first script Start

Unity2D Melee Combat

I'm new to programming and C#. I'm trying to build a melee system for my platform game following tutorials on yt.
This is my PlayerAttack script:
using System.Collections; using System.Collections.Generic; using UnityEngine;
public class PlayerAttack : MonoBehaviour {
//[SerializeField] private float attackCooldown;
[SerializeField] private float range;
[SerializeField] private int damage;
[SerializeField] private LayerMask enemyLayer;
public Transform AttackPoint;
//private float cooldownTimer = Mathf.Infinity;
private Animator anim;
private Enemy enemyHealth;
private void Awake()
{
anim = GetComponent<Animator>();
enemyHealth = GetComponent<Enemy>();
}
private void Update()
{
if (Input.GetButtonDown("Fire1"))
{
anim.SetTrigger("Attack");
Attack();
Debug.Log("attacking");
}
}
private void OnDrawGizmos()
{
if (AttackPoint == null)
return;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(AttackPoint.position, range);
}
void Attack()
{
Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(AttackPoint.position, range, enemyLayer);
foreach (Collider2D Enemy in hitEnemies)
{
Enemy.transform.GetComponent<Enemy>().TakeDamage(damage);
}
}
}
and this one is the EnemyHealth script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
[SerializeField] private int startingHealth;
public int currentHealth;
public Animator anim;
void Start()
{
currentHealth = startingHealth;
}
public void TakeDamage(int _damage)
{
currentHealth = Mathf.Clamp(currentHealth - _damage, 0, startingHealth);
if (currentHealth > 0)
{
//hurt animation
//invulnerability
}
else
{
//die animation
GetComponentInParent<EnemyPatrol>().enabled = false;
GetComponent<EnemyMelee>().enabled = false;
}
}
void Die()
{
if (currentHealth <= 0)
{
Debug.Log("Enemy Dead!");
Destroy(gameObject);
//die animation
}
}
}
I'm getting an error from the Unity editor "object reference not set to an instance of an object" on line 61 of the PlayerAttack:
enter code here
Enemy.transform.GetComponent().TakeDamage(damage);
I checked the scripts names and they are fine, and I checked also if I missed something in the editor , I don't know what's wrong.
When I hit the enemy the game crashes and i get this error.
Thanks
You are trying to call Enemy.transform.GetComponent<Enemy>().TakeDamage(damage); on everything that has collider but some of those objects don't have the Enemy component so you can't call TakeDamage on them. That is where the error comes from. To fix it you have to find only the enemy game objects. To do this you can assign tags to your enemy and check the ones with Enemy tag through the script like this:
foreach (Collider2D Enemy in hitEnemies)
{
if (Enemy.tag == "Enemy")
Enemy.transform.GetComponent<Enemy>().TakeDamage(damage);
}
There is another way to do it without tags which I don't recommend:
foreach (Collider2D Enemy in hitEnemies)
{
if (Enemy.transform.GetComponent<Enemy>() != null)
Enemy.transform.GetComponent<Enemy>().TakeDamage(damage);
}
Check if the player has hit the enemy or not:
foreach (Collider2D hitEnemy in hitEnemies) {
if(hitEnemy.TryGetComponent(out Enemy enemy)) {
enemy.TakeDamage(damage);
}
}
Other than the answer:
In your PlayerAttack.cs script, you have a field enemyHealth which you are trying to get a reference to it using enemyHealth = GetComponent<Enemy>(); in your Awake() method. This doesn't get a reference to it because of Enemy.cs and PlayerAttack.cs scripts are attached to different gameobjects.

GameObject not being removed from List?

I've tried 2/3 different ways of removing my gameObject from my List but none are working. When I debug the method the debug log is showing up as it should yet the gameobject still isn't removed from the list.
When my teammates kill an enemy I want the enemy to be removed from the list and then destroyed so I can continue to iterate through the List to find the closest enemy to begin attacking. Because the gameObject's are not being removed I get a null reference and i can loop through my for loop to check.
1st Script: List is created and used in a for loop, removing and destroying the enemy also occurs in here.
public class FriendlyManager : MonoBehaviour
{
public NavMeshAgent navMeshAgent;
public Transform player;
public static FriendlyManager singleton;
public float health;
public float minimumDistance;
public int damage;
public List<GameObject> enemies;
private GameObject enemy;
private GameObject enemyObj;
// animations
[SerializeField] Animator animator;
bool isAttacking;
bool isPatroling;
// attacking
[SerializeField] Transform attackPoint;
[SerializeField] public GameObject projectile;
public float timeBetweenAttacks;
bool alreadyAttacked;
private void Awake()
{
navMeshAgent = GetComponent<NavMeshAgent>();
animator = GetComponent<Animator>();
enemyObj = new GameObject();
}
private void Start()
{
singleton = this;
isAttacking = false;
isPatroling = true;
animator.SetBool("isPatroling", true);
}
private void Update()
{
for(int i = 0; i < enemies.Count; i++)
{
if(Vector3.Distance(player.transform.position, enemies[i].transform.position) <= minimumDistance)
{
enemy = enemies[i];
Attacking(enemy);
}
}
}
private void Attacking(GameObject enemy)
{
// stop enemy movement.
navMeshAgent.SetDestination(transform.position);
enemyObj.transform.position = enemy.transform.position;
transform.LookAt(enemyObj.transform);
if (!alreadyAttacked)
{
isAttacking = true;
animator.SetBool("isAttacking", true);
animator.SetBool("isPatroling", false);
Rigidbody rb = Instantiate(projectile, attackPoint.position, Quaternion.identity).GetComponent<Rigidbody>();
rb.AddForce(transform.forward * 32f, ForceMode.Impulse);
alreadyAttacked = true;
Invoke(nameof(ResetAttack), timeBetweenAttacks);
}
}
private void ResetAttack()
{
alreadyAttacked = false;
animator.SetBool("isAttacking", false);
}
public void DestroyEnemy(GameObject enemy)
{
enemies.Remove(enemy);
Debug.Log("AHHHHHHH M GOING CRAZY");
Destroy(gameObject);
}
}
}
2nd Script: Deals with the damage and checks enemy's currentHealth. (I have to post it as an image because for Stack Overflow is being annoying.) ._.
Shouldn't it just be
Destroy(enemy);
and not
Destroy(gameObject);

All the guns in the game shoot at the same time when I only want the gun that I'm holding to shoot

My problem is that when I'm holding a gun and shoot it, all the other guns on the floor start shooting as well. How do I make it so that only the gun that I'm holding with the mouse can shoot?
You pick up the gun with the mouse left click, and you shoot with right click
My pickup code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PickUp : MonoBehaviour
{
bool Pressed = false;
void OnMouseDown()
{
Pressed = true;
GetComponent<Rigidbody2D>().isKinematic = true;
}
void OnMouseUp()
{
Pressed = false;
GetComponent<Rigidbody2D>().isKinematic = false;
}
void Update()
{
if(Pressed)
{
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position = mousePos;
}
}
}
My gun code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour{
public Transform firePoint;
public float fireRate = 15f;
public GameObject bulletPrefab;
public Transform MuzzleFlashPrefab;
private float nextTimeToFire = 0f;
void Update() {
if (Input.GetButton("Fire1") && Time.time >= nextTimeToFire)
{
nextTimeToFire = Time.time + 1f/fireRate;
Shoot();
}
}
void Shoot ()
{
Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
Transform clone = Instantiate (MuzzleFlashPrefab, firePoint.position, firePoint.rotation) as Transform;
clone.parent = firePoint;
float size = Random.Range (0.02f, 0.025f);
clone.localScale = new Vector3 (size, size, size);
Destroy (clone.gameObject, 0.056f);
}
}
and my bullet code
using System.Collections.Generic;
using UnityEngine;
public class bulet : MonoBehaviour
{
public float speed = 40f;
public Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb.velocity = transform.right * speed;
}
}
Set a flag in your Weapon, something called liked pickedUp, and toggle it when the weapon is picked up/dropped. Then in your Weapon's Update function, check if you should process Inputs for that weapon based on its picked up state. If its not picked up, there is no need to check if it should fire a bullet or not.

Player does not take damage when near enemy

I want the player to take damage whenever the enemy is near, the enemy will take damage but the player doesn't(i tried taking away the function that kills the enemy to check if it is too quick). I am quite new to unity and this is one of my first "independent" scripts, so it may be a quite obvious error. There are no errors or warnings however.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AttackableScript : MonoBehaviour {
public float health;
public float attackSpeed;
public float damage;
public float radious = 3f;
public Transform enemyArea;
bool dead = false;
public GameObject player;
public float KillCount;
public void Die()
{
Debug.Log(health);
health = health - 0.04f;
if (health == 0f)
{
dead = true;
}
}
IEnumerator Attack()
{
GameObject thePlayer = GameObject.Find("player");
PlayerMovement playerScript = thePlayer.GetComponent<PlayerMovement>();
playerScript.playerHealth = playerScript.playerHealth - damage;
yield return new WaitForSeconds(attackSpeed);
Debug.Log("player health = " + playerScript.playerHealth);
}
void Update() {
float distance = Vector3.Distance(player.transform.position, enemyArea.position);
if (distance <= radious && dead != true)
{
Attack();
}
if (health <= 0)
{
health = 0;
Destroy(gameObject);
Destroy(this);
}
}
}
the game is 2d.
I asked a discord and they pointed out my misuse of coroutines, i forgot to start one when i called the attack function. So instead of Attack() , it was StartCoroutine(Attack());

Categories

Resources