简体   繁体   English

如果不使用静态变量来获得健康,敌人就不会受到伤害

[英]Enemy cannot be damaged if not using static variable for health

So I have a problem where my enemy cannot be damaged if I'm not using a static variable for their health.所以我有一个问题,如果我没有为他们的健康使用静态变量,我的敌人就不会受到伤害。 But when using static variable if one of the enemy dies, the others die as well.但是当使用静态变量时,如果一个敌人死了,其他人也会死。 The enemy doing damage to the player works ok, so I don't know what causing the problem.对玩家造成伤害的敌人工作正常,所以我不知道是什么导致了问题。 Debug log says I'm hitting the enemy just fine, but the enemy health is not decreasing.调试日志说我击中了敌人就好了,但是敌人的生命值并没有减少。

here's my code:这是我的代码:

Player播放器

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;

public class PlayerController : MonoBehaviour
{
    //Input System
    private PlayerControlInput playerInput;
    private InputAction movement;
    public KnightController knight;
    public EnemyArcher archer;

    //Movement and Physics
    private Rigidbody rb;
    [SerializeField]
    private float moveForce = 1f;
    [SerializeField]
    private float maxSpeed = 5f;
    private Vector3 forceDirection = Vector3.zero;
    public Vector3 destination;

    //Animation and Camera
    [SerializeField]
    private Camera playerCam;
    private Animator animator;

    //Audio
    public GameObject playerWeapon;
    public AudioClip lightAttackSound;
    public AudioClip heavyAttackSound;
    public AudioClip spellSound;
    public AudioClip walkingSound;

    //Player stats
    public int playerHealth;
    public int lightAttackDamage = 5;
    public bool isLightAttacking = false;
    public int heavyAttackDamage = 7;
    public bool isHeavyAttacking = false;
    public GameObject spell;
    public Transform casterPostion;
    public int spellDamage = 20;
    public bool isSpellCasting = false;
    public bool isAttacking = false;
    public bool isPlayerBlocking = false;
    public bool canCastSpell;
    public bool canAttack;
    public bool canBlock;
    private float lightAttackCooldown = 1f;
    private float heavyAttackCooldown = 2f;
    private float parryCooldown = 2f;
    private float spellCooldown = 5f;
    public int totalEnemySlain;
    public bool isDead;

    private void Start()
    {
        knight.GetComponent<KnightController>();
        archer.GetComponent<EnemyArcher>();
        knight.enabled = true;
        archer.enabled = true;
        isDead = false;
        playerHealth = 250;
        totalEnemySlain = 0;
    }
    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
        playerInput = new PlayerControlInput(); 
        animator = GetComponent<Animator>();
    }

    private void OnEnable()
    {
        movement = playerInput.Player.Movement;
        movement.Enable();

        playerInput.Player.Light_Attack.started += LightAttack;
        playerInput.Player.Light_Attack.Enable();
        playerInput.Player.Heavy_Attack.started += HeavyAttack;
        playerInput.Player.Heavy_Attack.Enable();
        playerInput.Player.Spell_Casting.performed += spellCast;
        playerInput.Player.Spell_Casting.Enable();
        playerInput.Player.Block.started += Blocking;
        playerInput.Player.Block.Enable();
    }

    private void OnDisable()
    {
        movement.Disable();
        playerInput.Player.Spell_Casting.Disable();
        playerInput.Player.Light_Attack.started -= LightAttack;
        playerInput.Player.Light_Attack.Disable();
        playerInput.Player.Heavy_Attack.started += HeavyAttack;
        playerInput.Player.Heavy_Attack.Disable();
        playerInput.Player.Block.started -= Blocking;
        playerInput.Player.Block.Disable();
    }

    #region Action Control
    public void delayMove()
    {
        movement.Enable();
    }

    #region Blocking
    public void Blocking(InputAction.CallbackContext obj)
    {
        isBlocking();
    }

    public void isBlocking()
    {
        canBlock = false;
        isPlayerBlocking = true;
        movement.Disable();
        animator.SetTrigger("Blocking");
        Invoke("delayMove", 1f);
        StartCoroutine(resetParryCoolDown());
    }
    
    IEnumerator resetParryCoolDown()
    {
        StartCoroutine(resetIsParrying());
        yield return new WaitForSeconds(parryCooldown);
        canBlock = true;
    }

    IEnumerator resetIsParrying()
    {
        yield return new WaitForSeconds(heavyAttackCooldown);
        isPlayerBlocking = false;
    }
    #endregion

    #region Heavy Attack
    public void HeavyAttack(InputAction.CallbackContext obj)
    {
        if (canAttack)
        {
            heavyAttack();
        }
    }

    public void heavyAttack()
    {
        isAttacking = true;
        isHeavyAttacking = true;
        canAttack = false;
        movement.Disable();
        animator.SetTrigger("HeavyAttack");
        AudioSource heavyAudio = GetComponent<AudioSource>();
        heavyAudio.PlayOneShot(heavyAttackSound);
        Invoke("delayMove", heavyAttackCooldown);
        StartCoroutine(resetHeavyAttackCoolDown());
    }

    IEnumerator resetHeavyAttackCoolDown()
    {
        StartCoroutine(resetIsHeavyAttacking());
        yield return new WaitForSeconds(heavyAttackCooldown);
        canAttack = true;
    }

    IEnumerator resetIsHeavyAttacking()
    {
        yield return new WaitForSeconds(heavyAttackCooldown);
        isAttacking = false;
        isHeavyAttacking = false;
    }
    #endregion

    #region Light Attack
    public void LightAttack(InputAction.CallbackContext obj)
    {
        if (canAttack)
        {
            lightAttack();
        }
    }

    public void lightAttack() 
    {
        isAttacking = true;
        isLightAttacking = true;
        canAttack = false;
        movement.Disable();
        animator.SetTrigger("LightAttack");
        AudioSource lightAudio = GetComponent<AudioSource>();
        lightAudio.PlayOneShot(lightAttackSound);
        Invoke("delayMove", lightAttackCooldown);
        StartCoroutine(resetLightAttackCoolDown());
    }
    IEnumerator resetLightAttackCoolDown()
    {
        StartCoroutine(resetIsLightAttacking());
        yield return new WaitForSeconds(lightAttackCooldown);
        canAttack = true;
    }

    IEnumerator resetIsLightAttacking()
    {
        yield return new WaitForSeconds(lightAttackCooldown);
        isAttacking = false;
        isLightAttacking = false;
    }
    #endregion

    #region Spell
    public void spellCast(InputAction.CallbackContext obj)
    {
        if (canCastSpell)
        {
            spellIsCasted();
        }
    }

    public void spellIsCasted()
    {
        fireSpell();
        isSpellCasting = true;
        canCastSpell = false;
        movement.Disable();
        animator.SetTrigger("Casting");
        AudioSource spellAudio = GetComponent<AudioSource>();
        spellAudio.PlayOneShot(spellSound);
        Invoke("delayMove", 1f);
        StartCoroutine(resetSpellCoolDown());
    }


    IEnumerator resetSpellCoolDown()
    {
        StartCoroutine(resetisSpellCasting());
        yield return new WaitForSeconds(spellCooldown);
        canCastSpell = true;
    }

    IEnumerator resetisSpellCasting()
    {
        yield return new WaitForSeconds(1);
        isSpellCasting = false;
    }
    public void fireSpell()
    {
        Ray ray = new Ray(casterPostion.position, casterPostion.forward);
        RaycastHit hit;

        if(Physics.Raycast(ray, out hit))
        {
            destination = hit.point;
        }
        else
        {
            destination = ray.GetPoint(10);
        }
        instantiateFire(casterPostion);
    }

    public void instantiateFire(Transform castingPoint)
    {

        GameObject fireball = Instantiate(spell, castingPoint.position, Quaternion.identity);
        fireball.GetComponent<Rigidbody>().velocity = (destination - castingPoint.position).normalized * 30;
    }
    #endregion

    public void hurt(int damage)
    {
        playerHealth -= damage;
    }

    public void dead()
    {
        if (playerHealth <= 0)
        {
            isDead = true;
            animator.SetTrigger("Dead");
            Debug.Log("Player is Dead");
            knight.enabled = false;
            archer.enabled = false;
            this.enabled = false;
            Invoke("deathScene", 2f);
        }
    }
    
    public void deathScene()
    {
        SceneManager.LoadScene("Death Scene");
    }

    private void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.name == "Arrow" || collision.gameObject.tag == "Arrow")
        {
            playerHealth -= archer.attackDamage;
            Debug.Log("Player struck by arrow!");
        }
        if (collision.gameObject.name == "Arrow" || collision.gameObject.tag == "Arrow" && isPlayerBlocking)
        {
            Debug.Log("Arrow parried!");
        }
    }
    #endregion

    #region Camera
    private void lookingAt()
    {
        Vector3 direction = rb.velocity;
        direction.y = 0;
        if (movement.ReadValue<Vector2>().sqrMagnitude > 0.1f && direction.sqrMagnitude > 0.1f)
            this.rb.rotation = Quaternion.LookRotation(direction, Vector3.up);
        else
            rb.angularVelocity = Vector3.zero;
    }

    private Vector3 GetCameraRight(Camera playerCam)
    {
        Vector3 right = playerCam.transform.right;
        right.y = 0;
        return right.normalized;
    }

    private Vector3 GetCameraForward(Camera playerCam)
    {
        Vector3 forward = playerCam.transform.forward;
        forward.y = 0;
        return forward.normalized;
    }
    #endregion

    void FixedUpdate()
    {
        forceDirection += movement.ReadValue<Vector2>().x * GetCameraRight(playerCam) * moveForce;
        forceDirection += movement.ReadValue<Vector2>().y * GetCameraForward(playerCam) * moveForce;
        rb.AddForce(forceDirection, ForceMode.Impulse);
        forceDirection = Vector3.zero;

        Vector3 horizontalVelocity = rb.velocity;
        horizontalVelocity.y = 0;
        if (horizontalVelocity.sqrMagnitude > maxSpeed * maxSpeed)
            rb.velocity = horizontalVelocity.normalized * maxSpeed + Vector3.up * rb.velocity.y;

        lookingAt();
        dead();
    }
}

Player sword collision detection玩家剑碰撞检测

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollisionDetection : MonoBehaviour
{
    public PlayerController player;
    public KnightController knight;
    public EnemyArcher archer;
    public AudioClip hit;
    public AudioClip parried;

    void OnTriggerEnter(Collider other)
    {
        if (other.tag == "EnemyKnight" || other.tag == "EnemyArcher")
        {
            AudioSource isHitting = GetComponent<AudioSource>();
            isHitting.PlayOneShot(hit);

            if(other.tag == "EnemyKnight")
            {
                if (player.isLightAttacking) 
                {
                    knight.takeDamage(5);
                    Debug.Log("Light attack on " + other.name + " For " + player.lightAttackDamage);
                }
                if (player.isHeavyAttacking)
                {
                    knight.takeDamage(7);
                    Debug.Log("Heavy attack on " + other.name + " For " + player.heavyAttackDamage);
                }
            }
            if(other.tag == "EnemyArcher"){
                if(player.isLightAttacking)
                {
                    archer.takeDamage(5);
                    Debug.Log("Light attack on " + other.name + " For " + player.lightAttackDamage);
                }
                if (player.isHeavyAttacking)
                {
                    archer.takeDamage(7);
                    Debug.Log("Heavy attack on " + other.name + " For " + player.heavyAttackDamage);
                }
            }
        }

        if(other.tag == "Blade")
        {
            if (player.isPlayerBlocking) 
            {
                AudioSource parry = GetComponent<AudioSource>();
                parry.PlayOneShot(parried);
                Debug.Log("Parried");
            }
        }
    }
}

Enemy Knight敌骑士

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class KnightController : MonoBehaviour
{
    //Enemy Stats
    public int health = 20;
    public int attackDamage = 5;
    public float attackCooldown = 1f;
    public float agroRadius = 25f;
    public bool canAttack;
    public bool isAttacking = false;
    public bool isDead;

    //Enemy AI and Animation
    public PlayerController playerController;
    Transform target;
    NavMeshAgent agentKnight;
    Animator animator;


    void Start()
    {
        this.enabled = true;
        isDead = false;
        health = 20;
        target = PlayerManager.instance.player.transform;
        agentKnight = GetComponent<NavMeshAgent>();
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        float distance = Vector3.Distance(target.position, transform.position);

        if (distance <= agroRadius)
        {
            isAgroedToPlayer();

            if (distance <= agentKnight.stoppingDistance + 0.2f)
            {
                attackPlayer();
            }
        }
        die();
    }

    #region Attack
    public void attackPlayer()
    {
        isAttacking = true;
        canAttack = false;
        agentKnight.SetDestination(transform.position);
        animator.SetBool("isAgro", false);
        animator.SetTrigger("Attacking");
        facePlayerWhenAttacking();
        StartCoroutine(resetAttackCooldown());
    }

    IEnumerator resetAttackCooldown()
    {
        StartCoroutine(resetIsAttackingPlayer());
        yield return new WaitForSeconds(attackCooldown);
        canAttack = true;
    }

    IEnumerator resetIsAttackingPlayer()
    {
        yield return new WaitForSeconds(attackCooldown);
        isAttacking = false;
    }
    #endregion

    #region Agro
    public void isAgroedToPlayer()
    {
        animator.SetBool("isAgro", true);
        agentKnight.SetDestination(target.position);
    }
    #endregion

    public void takeDamage(int damage)
    {
        health -= damage;
    }

    public void die()
    {
        if (health <= 0)
        {
            isDead = true;
            animator.SetTrigger("Dead");
            animator.SetBool("isAgro", false);
            this.GetComponent<Rigidbody>().detectCollisions = false;
            playerController.totalEnemySlain++;
            this.enabled = false;
            Debug.Log("Knight Killed!");
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.name == "Fireball" || collision.gameObject.tag == "Spell")
        {
            health -= playerController.GetComponent<PlayerController>().spellDamage;
            Debug.Log("Knight is struck by fireball for " + playerController.GetComponent<PlayerController>().spellDamage);
        }
    }

    public void facePlayerWhenAttacking()
    {
        Vector3 direction = (target.position - transform.position).normalized;
        Quaternion lookRotation = Quaternion.LookRotation(new Vector3(direction.x, 0, direction.z));
        transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * 5);
    }
}

Enemy knight sword collision detection敌人骑士剑碰撞检测

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class KnightCollisionDetector : MonoBehaviour
{
    public KnightController knight;
    public PlayerController player;
    public AudioClip playerHit;

    private void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerController>();
    }
    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player" || other.name == "Player" && knight.GetComponent<KnightController>().isAttacking)
        {
            if (player.isPlayerBlocking)
            {
                Debug.Log("Parried");
            }
            AudioSource hit = GetComponent<AudioSource>();
            hit.PlayOneShot(playerHit);
            player.hurt(knight.attackDamage);
            Debug.Log("Knight hit player for " + 5);
        }
    }
}

Enemy Archer敌射手

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class EnemyArcher : MonoBehaviour
{
    //Enemy Stats
    public int health = 20;
    public int attackDamage = 10;
    public bool isAttacking = false;
    public bool canAttack;
    public float attackCooldown = 5f;
    public float agroRadius = 50f;
    public float bowRange = 30f;
    public bool isDead = false;

    //AI
    public PlayerController playerController;
    public GameObject arrow;
    public Transform arrowPoint;
    Transform target;
    NavMeshAgent agentArcher;
    Animator animator;

    void Start()
    {
        this.enabled = true;
        isDead = false;
        health = 10;
        target = PlayerManager.instance.player.transform;
        agentArcher = GetComponent<NavMeshAgent>();
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        float distance = Vector3.Distance(target.position, transform.position);

        if (distance <= agroRadius)
        {
            isAgroedToPlayer();

            if(distance <= agroRadius && distance >= bowRange)
            {
                animator.SetBool("inRange", false);
                animator.SetTrigger("Attacking");
                agentArcher.SetDestination(target.position);
            }

            if (distance <= bowRange)
            {
                attackPlayer();
            }
        }
        die();
    }

    #region Attack
    public void attackPlayer()
    {
        isAttacking = true;
        canAttack = false;
        agentArcher.SetDestination(transform.position);
        animator.SetBool("isAgro", false);
        animator.SetBool("inRange", true);
        animator.SetTrigger("Attacking");
        facePlayerWhenAttacking();
        StartCoroutine(resetAttackCooldown());
    }


    IEnumerator resetAttackCooldown()
    {
        StartCoroutine(resetIsAttackingPlayer());
        yield return new WaitForSeconds(attackCooldown);
        canAttack = true;
    }


    IEnumerator resetIsAttackingPlayer()
    {
        yield return new WaitForSeconds(attackCooldown);
        isAttacking = false;
    }

    public void shootArrow()
    {
        GameObject arrowProjectile =  Instantiate(arrow, arrowPoint.position, transform.rotation);
        arrowProjectile.GetComponent<Rigidbody>().AddForce(transform.forward * 25f, ForceMode.Impulse);
    }
    #endregion

    #region Agro
    public void isAgroedToPlayer()
    {
        animator.SetBool("isAgro", true);
        agentArcher.SetDestination(target.position);
    }
    #endregion

    public void takeDamage(int damage)
    {
        health -= damage;
    }

    public void die()
    {
        if(health <= 0)
        {
            isDead = true;
            animator.SetTrigger("Dead");
            animator.SetBool("isAgro", false);
            this.GetComponent<Rigidbody>().detectCollisions = false;
            playerController.totalEnemySlain++;
            this.enabled = false;
            Debug.Log("Archer Killed!");
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.name == "Fireball" || collision.gameObject.tag == "Spell")
        {
            health -= playerController.GetComponent<PlayerController>().spellDamage;
            Debug.Log("Archer is struck by fireball " + playerController.GetComponent<PlayerController>().spellDamage);
        }
    }

    public void facePlayerWhenAttacking()
    {
        Vector3 direction = (target.position - transform.position).normalized;
        Quaternion lookRotation = Quaternion.LookRotation(new Vector3(direction.x, 0, direction.z));
        transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * 5);
    }
}

Random spawn enemy随机生成敌人

using System.Collections.Generic;
using UnityEngine;

public class RandomEnemySpawn : MonoBehaviour
{
    public GameObject player;
    public GameObject archer;
    public GameObject knight;
    public bool playerIsDead;
    private int xPos1;
    private int zPos1;
    private int xPos2;
    private int zPos2;
    
    void Start()
    {
        playerIsDead = player.GetComponent<PlayerController>().isDead;
        StartCoroutine(spawnEnemy1());
        StartCoroutine(spawnEnemy2());
    }

    IEnumerator spawnEnemy1()
    {
        while (!playerIsDead)
        {
            xPos1 = 97;
            zPos1 = 67;
            Instantiate(archer, new Vector3(xPos1, 0 , zPos1), Quaternion.identity);
            Instantiate(knight, new Vector3(xPos1, 0 , zPos1), Quaternion.identity);
            yield return new WaitForSeconds(20);
        }
    }

    IEnumerator spawnEnemy2()
    {
        while (!playerIsDead)
        {
            xPos1 = 50;
            zPos1 = 55;
            Instantiate(archer, new Vector3(xPos1, 0, zPos1), Quaternion.identity);
            Instantiate(knight, new Vector3(xPos1, 0, zPos1), Quaternion.identity);
            yield return new WaitForSeconds(20);
        }
    }
}

You are not getting the actual knight you're hitting, you're just damaging whatever you've put into the variable in your code:你没有得到你真正击中的骑士,你只是破坏了你在代码中放入变量的任何内容:

public KnightController knight;

If I had to guess, you've probably dropped a prefab into it from the Editor.如果我不得不猜测,您可能已经从编辑器中将预制件放入其中。 Taking a snippet from your code:从您的代码中获取一个片段:

        if(other.tag == "EnemyKnight")
        {
            if (player.isLightAttacking) 
            {
                knight.takeDamage(5);
                Debug.Log("Light attack on " + other.name + " For " + player.lightAttackDamage);
            }
            if (player.isHeavyAttacking)
            {
                knight.takeDamage(7);
                Debug.Log("Heavy attack on " + other.name + " For " + player.heavyAttackDamage);
            }
        }

you can see that "knight" has nothing to do with "other", which is the thing you've actually hit.您可以看到“骑士”与“其他”无关,这是您实际击中的东西。

My best recommendation would be to refactor your code such that your .takeDamage() method is an interface, then you can get that script from the object that's being hit, assuming the collider is attached to the same root object that has the KnightController or EnemyArcher script:我最好的建议是重构您的代码,使您的.takeDamage()方法是一个接口,然后您可以从被击中的对象中获取该脚本,假设对撞机附加到具有 KnightController 或 EnemyArcher 的同一根对象脚本:

var enemy = other.gameObject.GetComponent<ITakeDamage>();
if(enemy != null) 
{
    enemy.takeDamage(5);
}

You could just replace ITakeDamage (a proposed interface you would have to write) with KnightController or EnemyArcher, but when you're writing basically the same code over and over to do the same action with different classes, that's a code smell asking for an interface.您可以用 KnightController 或 EnemyArcher 替换 ITakeDamage(您必须编写的建议接口),但是当您一遍又一遍地编写基本相同的代码以对不同的类执行相同的操作时,这是一种要求接口的代码气味.

I'd also recommend refactoring your player to have a method like CurrentDamage, and then the player knows when they're doing a heavy attack or something else.我还建议重构您的播放器,使其具有像 CurrentDamage 这样的方法,然后播放器知道他们何时进行重击或其他操作。 This is separation of concerns - a sword collision script might need to know that it can deal damage but shouldn't have to know all the different ways that damage is calculated.这是关注点分离——剑碰撞脚本可能需要知道它可以造成伤害,但不必知道计算伤害的所有不同方式。

So it seems that the script is not detecting the tag I use, so instead I used the GetComponent method like this:所以似乎脚本没有检测到我使用的标签,所以我使用了 GetComponent 方法,如下所示:

void OnTriggerEnter(Collider other)
    {
        if (other.tag == "EnemyKnight" || other.tag == "EnemyArcher")
        {
            AudioSource isHitting = GetComponent<AudioSource>();
            isHitting.PlayOneShot(hit);

            if(other.tag == "EnemyKnight")
            {
                if (player.isLightAttacking) 
                {
                    other.GetComponent<KnightController>().takeDamage(lightDamage);
                    Debug.Log("Light attack on " + other.name + " For " + lightDamage);
                }
                if (player.isHeavyAttacking)
                {
                    other.GetComponent<KnightController>().takeDamage(heavyDamage);
                    Debug.Log("Heavy attack on " + other.name + " For " + heavyDamage);
                }
            }
            if(other.tag == "EnemyArcher"){
                if(player.isLightAttacking)
                {
                    other.GetComponent<EnemyArcher>().takeDamage(lightDamage);
                    Debug.Log("Light attack on " + other.name + " For " + lightDamage);
                }
                if (player.isHeavyAttacking)
                {
                    other.GetComponent<EnemyArcher>().takeDamage(heavyDamage);
                    Debug.Log("Heavy attack on " + other.name + " For " + heavyDamage);
                }
            }
        }
    }

And now it works现在它可以工作了

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM