Get enemy attack to the fps controller? - c#

using UnityEngine;
using System.Collections;
public class EnemyAttack : MonoBehaviour
{
public float timeBetweenAttacks = 0.5f; // The time in seconds between each attack.
public int attackDamage = 10; // The amount of health taken away per attack.
Animator anim; // Reference to the animator component.
GameObject player; // Reference to the player GameObject.
PlayerHealth playerHealth; // Reference to the player's health.
//EnemyHealth enemyHealth; // Reference to this enemy's health.
bool playerInRange; // Whether player is within the trigger collider and can be attacked.
float timer; // Timer for counting up to the next attack.
void Awake ()
{
// Setting up the references.
player = GameObject.FindGameObjectWithTag ("Player");
playerHealth = player.GetComponent <PlayerHealth> ();
//enemyHealth = GetComponent<EnemyHealth>();
anim = GetComponent <Animator> ();
}
public void OnTriggerEnter (Collider other)
{
// If the entering collider is the player...
if(other.gameObject == player)
{
// ... the player is in range.
playerInRange = true;
anim = ("idle0ToAttack1");
}
}
void OnTriggerExit (Collider other)
{
// If the exiting collider is the player...
if(other.gameObject == player)
{
// ... the player is no longer in range.
playerInRange = false;
}
}
void Update ()
{
// Add the time since Update was last called to the timer.
timer += Time.deltaTime;
// If the timer exceeds the time between attacks, the player is in range and this enemy is alive...
if(timer >= timeBetweenAttacks && playerInRange /*&& enemyHealth.currentHealth > 0*/)
{
// ... attack.
Attack ();
}
// If the player has zero or less health...
if(playerHealth.currentHealth <= 0)
{
// ... tell the animator the player is dead.
Destroy(this.gameObject);
}
}
void Attack ()
{
// Reset the timer.
timer = 0f;
// If the player has health to lose...
if(playerHealth.currentHealth > 0)
{
// ... damage the player.
playerHealth.TakeDamage (attackDamage);
}
}
}
I want to get the enemy attack to the fps player. How do I do that? At the same time I want my fps controller to die. For your help I've also written the comments so that you all can understand.

I am assuming that you want your enemy to Path Find the player.
Read this if you need more help: Unity Topics: Navigation
First step, all objects that you want for your enemy to walk on must be selected as "static". (Click on the GameObject and tick 'static' in the top right corner)
Now you need to go to Windows>Navigation and click "Bake" down the bottom once you like your settings.
Make sure your "Enemy" has a rigid body on them and then add "Nav Mesh Agent" component.
Now add (or edit) a script on the enemy and add the code (make sure to add this up the top using UnityEngine.AI;):
//NavMeshAgent
private NavMeshAgent agent;
//Target Transform
public Transform trans;
void Start()
{
//Get the component
agent = GetComponent<NavMeshAgent>();
}
private void Update()
{
SetDestination(trans.position);
}
void SetDestination (Vector3 des)
{
//Set the destination
agent.SetDestination(des);
}
And there you go, there are other methods of doing this but this is by far the simplest.
EDIT:
This is what your script should look like:
using UnityEngine;
using System.Collections;
using UnityEngine.AI;
public class EnemyAttack : MonoBehaviour
{
public float timeBetweenAttacks = 0.5f; // The time in seconds between each attack.
public int attackDamage = 10; // The amount of health taken away per attack.
Animator anim; // Reference to the animator component.
GameObject player; // Reference to the player GameObject.
PlayerHealth playerHealth; // Reference to the player's health.
//EnemyHealth enemyHealth; // Reference to this enemy's health.
bool playerInRange; // Whether player is within the trigger collider and can be attacked.
float timer; // Timer for counting up to the next attack.
//NavMeshAgent
private NavMeshAgent agent;
//Target Transform
public Transform trans;
void Start()
{
//Get the component
agent = GetComponent<NavMeshAgent>();
}
void SetDestination(Vector3 des)
{
//Set the destination
agent.SetDestination(des);
}
void Awake()
{
// Setting up the references.
player = GameObject.FindGameObjectWithTag("Player");
playerHealth = player.GetComponent<PlayerHealth>();
//enemyHealth = GetComponent<EnemyHealth>();
anim = GetComponent<Animator>();
}
public void OnTriggerEnter(Collider other)
{
// If the entering collider is the player...
if (other.gameObject == player)
{
// ... the player is in range.
playerInRange = true;
anim = ("idle0ToAttack1");
}
}
void OnTriggerExit(Collider other)
{
// If the exiting collider is the player...
if (other.gameObject == player)
{
// ... the player is no longer in range.
playerInRange = false;
}
}
void Update()
{
SetDestination(trans.position);
// Add the time since Update was last called to the timer.
timer += Time.deltaTime;
// If the timer exceeds the time between attacks, the player is in range and this enemy is alive...
if (timer >= timeBetweenAttacks && playerInRange /*&& enemyHealth.currentHealth > 0*/)
{
// ... attack.
Attack();
}
// If the player has zero or less health...
if (playerHealth.currentHealth <= 0)
{
// ... tell the animator the player is dead.
Destroy(this.gameObject);
}
}
void Attack()
{
// Reset the timer.
timer = 0f;
// If the player has health to lose...
if (playerHealth.currentHealth > 0)
{
// ... damage the player.
playerHealth.TakeDamage(attackDamage);
}
}
}

Related

Recoil animation boolean instantly goes to false

So I am trying to make it so that when the user shoots the recoil animation plays, but I have an issue where the bool (which goes true the moment the Fire() is called), instantly goes back to false. I have tried to remove the line of code which is supposed to only make the bool false once the user stopped firing the gun, but the result of that was just the recoil animation playing in a loop.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeaponFire : MonoBehaviour
{
public GameObject playerCam; // The player camera
public float range = 150f;
public float damage = 25f;
public float rounds = 30f;
public float mags = 3f;
public GameObject FPS_char; // The gameobject that has the animator
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (FPS_char.GetComponent<Animator>().GetBool("WeaponFire")) // Check to see if the player has fired
{
FPS_char.GetComponent<Animator>().SetBool("WeaponFire", false); // Set to false if true
}
}
public void Fire()
{
// Shoot a ray
RaycastHit hit;
// Set the WeaponFire bool to true when Fire() is called, which is called when the player presses the left mouse button
FPS_char.GetComponent<Animator>().SetBool("WeaponFire", true);
// Create a ray
if (Physics.Raycast(playerCam.transform.position, playerCam.transform.forward, out hit, range))
{
if (rounds <= 0)
{
// Player needs to reload
return;
}
if(rounds >= 0)
{
rounds -= 1f;
gameObject.GetComponent<AudioSource>().Play(); // Play a weapon fire sound
EnemyManager enemyManager = hit.transform.GetComponent<EnemyManager>(); // A script that gives the enemies health and attack damage etc
if (enemyManager != null) // check to see if what the player hit contains that script (if it does then it is an enemy)
{
enemyManager.Hit(damage); // Call the Hit() function of the script on the enemy which removes health from the enemy depending on how much damage the weapon does
}
}
}
}
public void Reload()
{
mags -= 1f;
rounds = 30f;
// Add a reload animation
}
}
For things that need to play once use animator.SetTrigger which plays the animation when it is called once automatically. You need to change the Animator parameter from Bool to Trigger in the Animator window.
public void Fire()
{
FPS_char.GetComponent<Animator>().SetTrigger("Fire");
}

Shooting cooldown for enemy in unity2d

I'm doing a top-down shooter in unity, I'm trying to make the enemy shoot when it sees the player. So far, this is my code:
public class EnemyShooting : MonoBehaviour
{
public Transform firePoint;
public GameObject bulletPrefab;
public float bulletForce = 20f;
public FieldOfView _fieldofview;
void Start()
{
_fieldofview = FindObjectOfType<FieldOfView>();
}
// Update is called once per frame
void Update()
{
if(_fieldofview.canSeePlayer)
{
Shoot();
}
}
void Shoot()
{
GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
Rigidbody2D rb2d = bullet.GetComponent<Rigidbody2D>();
rb2d.AddForce(firePoint.up * bulletForce, ForceMode2D.Impulse);
}
}
However, when the player is detected it just spams the bullets due it doesn't has a cool down timer. I've tried with a Coroutine and the Invoke method but it doesn't works. Any ideas?
The following code defines two times. The current cooldown and shoot cooldown. Add the bottom and your problem will be solved.
public float shootCooldown = 5; // 5sec for e.g.
private float currentCooldown;
void Update()
{
if (currentCooldown > 0) // If shoot not in cooldown
{
currentCooldown = shootCooldown; // Set current cooldown time to shootCooldown
if(_fieldofview.canSeePlayer) Shoot();
}
else currentCooldown -= Time.deltaTime; // Reduce cooldown over time
}

Cause player to take damage in Unity 2D

I made two scripts. One that'll keep track of the player's health, health bar and cause the screen to flash when the player is damaged. The other script is meant to be placed on any object I wish to do damage to the player, on contact. My problem is, Nothing seems to be doing any damage to the player.
PlayerHealth.cs:
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : MonoBehaviour
{
public int currentHealth;
public float flashSpeed = 5;
public Slider healthSlider;
public Color flashColour = new Color(1, 0, 0, 0.1f);
bool isDead;
bool damaged;
private void Awake()
{
currentHealth = 100;
}
private void Update()
{
damaged = false;
}
public void TakeDamage(int amount)
{
damaged = true;
currentHealth -= amount;
healthSlider.value = currentHealth;
}
}
AttackPlayer.cs:
using UnityEngine;
public class AttackPlayer : MonoBehaviour
{
public float timeBetweenAttacks = 0.5f;
public int attackDamage = 10;
GameObject player;
PlayerHealth playerHealth;
float timer;
private void Awake()
{
player = GameObject.FindGameObjectWithTag("Player");
playerHealth = player.GetComponent<PlayerHealth>();
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject == player)
{
Attack();
}
}
private void Update()
{
timer += Time.deltaTime;
if(playerHealth.currentHealth <=0)
{
// TODO: add death script here.
}
}
void Attack()
{
timer = 0f;
if(playerHealth.currentHealth > 0)
{
playerHealth.TakeDamage(attackDamage);
}
}
}
The player has a rigidbody2D. The player and damaging objects have Box Collider 2D's on them.
Make sure that the players Collider has isTrigger enabled.
attackDamage is public -> set in the inspector. Make sure it is not 0.
You could use
[Range(1,100)] public int attackDamage = 10;
to automatically clamp the value in the inspector.
A guess but I'ld say your Collider might not be on the GameObject player but probably on one of its children => the condition col.gameObject == player is not true.
Instead of GameObject references rather compare the PlayerHealth (since there is only one) reference like
private void OnTriggerEnter2D(Collider2D col)
{
// gets PlayerHealth component on this or any parent object
var health = col.GetComponentInParent<PlayerHealth>();
if (health == playerHealth)
{
Attack();
}
}
You have
private void Update()
{
damaged = false;
}
public void TakeDamage(int amount)
{
damaged = true;
currentHealth -= amount;
healthSlider.value = currentHealth;
}
I don't know what else should happen on TakeDamage but the value of damaged is resetted in Update so right after it was set by the Trigger because Physics events like OnTriggerEnter are executed before Update (see execution Order).
Hint: Instead of
player = GameObject.FindGameObjectWithTag("Player");
playerHealth = player.GetComponent<PlayerHealth>();
you could also use
playerHealth = FindObjectOfType<PlayerHealth>();
if that component exists only once in your scene.
Or to be more flexible (having multiple Players) all you have to do is change your OnTriggerEnter2D and Attack method to
private void OnTriggerEnter2D(Collider2D col)
{
// gets PlayerHealth component on this or any parent object
var health = col.GetComponentInParent<PlayerHealth>();
if (health != null)
{
Attack(health);
}
}
void Attack(PlayerHealth health)
{
timer = 0f;
if(health.currentHealth > 0)
{
health.TakeDamage(attackDamage);
}
}
So you wouldn't need to get the reference before.

Why isn't my program recognizing my target object

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AIController : MonoBehaviour {
[Header("Player interaction")]
[SerializeField] private GameObject target;
[SerializeField] private int damage;
[SerializeField] private float attackDelay;
[SerializeField] private float stunTime;
[SerializeField] private int health;
public int Health{
get{return health; }
set{ health = value;
agent.speed = runningSpeed;
agent.SetDestination(target.transform.position);
onPatrol = false;
if (health <= 0){
Destroy(gameObject);
}
}
}
// Update is called once per frame
void Update () {
CanAttack(target);
}
here i want to make sure that the AI will attack the player only when he is in front of the AI and within .5 meters. the reason i use this instead of OnTriggerEnter is that the AI will only end up attacking once and then just stand on top of the player without really doing anything further
void CanAttack (GameObject other)
{
Vector3 vectorToTarget = other.transform.position - transform.position;
float angleToTarget = Vector3.Angle(transform.forward, vectorToTarget);
if (angleToTarget <= FOV)
{
RaycastHit raycastData;
if (Physics.Raycast(transform.position, vectorToTarget, out raycastData, .5f))
{
GameObject newTarget = raycastData.collider.gameObject;
Attack(target);
}
}
}
I have also used Attack(other) and this still does not work
void Attack(GameObject other)
{
if (!hasAttacked)//if the AI has not recently attacked
{
if (!Player.isShieldOn)//if the player shield is not up
{
if (!Player.hasShieldBraclet)//if the player has the shield braclet
{
other.gameObject.GetComponent<Player>().Health -= damage - ShieldBraclet.dmgReduction;//reduce the amount of damage taken
}
else
{
other.gameObject.GetComponent<Player>().Health -= damage;//otherwise take normal damage
}
StartCoroutine("DelayAttack");//delay time until AI can attack again
}
else
{
StartCoroutine(Stun(stunTime));//if the shield was up stun this enemy
}
}
}
this is used to make sure that the AI waits a moment before attacking again that way the player doesn't die instantly upon touching the AI.
IEnumerator DelayAttack()
{
hasAttacked = true;//the AI has attacked
yield return new WaitForSeconds(attackDelay);//wait to attack again
hasAttacked = false;//the AI can now attack again
}
}
For the most part everything runs fine but when it reaches the code to deal damage to the player it tells me that the object reference is not set to an instance of an object. How can I fix this?
The Specific error code is: NullReferenceException: Object reference not set to an instance of an object Enemy.Attack(UnityEngine.GameObject other).
This is referring to the line
other.gameObject.GetComponent<Player>().Health -= damage - ShieldBraclet.dmgReduction;//reduce the amount of damage taken

Trying to launch a projectile towards a gameobject, doesn't move!=

I'm making a 2D Tower Defense game and want my towers to launch a prefab at minions. However it currently only spawns my desired prefab, but doesn't move it.
My two scripts:
public class Attacker : MonoBehaviour {
// Public variables
public GameObject ammoPrefab;
public float reloadTime;
public float projectileSpeed;
// Private variables
private Transform target;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter(Collider co){
if (co.gameObject.tag == "Enemy" || co.gameObject.tag == "BlockTower") {
Debug.Log("Enemy tag detected");
if(this.gameObject.tag == "Enemy" && co.gameObject.tag != "Enemy"){
Debug.Log("This is an Enemy");
// Insert for Enemey to attack Block Towers.
}
if(this.gameObject.tag == "Tower" && co.gameObject.tag != "BlockTower"){
Debug.Log("This is a Tower");
Tower Tower = GetComponent<Tower>();
Tower.CalculateCombatTime(reloadTime, projectileSpeed);
Transform SendThis = co.transform;
Tower.SetTarget(SendThis);
}
}
}
}
and
public class Tower : MonoBehaviour {
private Transform target;
private float fireSpeed;
private double nextFireTime;
private GameObject bullet;
private Attacker source;
// Use this for initialization
public virtual void Start () {
source = this.GetComponent<Attacker> ();
}
// Update is called once per frame
public virtual void Update () {
if (target) {
Debug.Log("I have a target");
//if(nextFireTime <= Time.deltaTime)
FireProjectile ();
}
}
public void CalculateCombatTime(float time, float speed){
Debug.Log("Calculate Combat Speed");
nextFireTime = Time.time + (time * .5);
fireSpeed = speed;
}
public void SetTarget(Transform position){
Debug.Log("Set Target");
target = position;
}
public void FireProjectile(){
Debug.Log("Shoot Projectile");
bullet = (GameObject)Instantiate (source.ammoPrefab, transform.position, source.ammoPrefab.transform.rotation);
float speed = fireSpeed * Time.deltaTime;
bullet.transform.position = Vector3.MoveTowards (bullet.transform.position, target.position, speed);
}
}
Basicly Attacker detects the object that collides with it, then if its tag is Tower it will send the information to Tower. My debug shows that every function works, even "Debug.Log("Shoot Projectile");" shows up.
However it doesn't move towards my target so I guess "bullet.transform.position = Vector3.MoveTowards (bullet.transform.position, target.position, step);" is never being executed?
Vector3.MoveTowards only moves the object once, it's just a instant displacement when the FireProjectile is called.
You need to create some kind of projectile script with an Update() function to make it move over time.
Here is an example:
public class Projectile : MonoBehaviour
{
public Vector3 TargetPosition;
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, TargetPosition, speed * Time.DeltaTime);
}
}
Then right after your bullet instantiation, set the target:
bullet.GetComponent<Projectile>().TargetPosition = target.position;
Hope it helps.
You have to update the position of the bullet. You are only moving when you create the bullet.
Try to make a list of bullets and use the update function to change the position.

Categories