首页 > 解决方案 > 脚本不适用于多个相同的对象

问题描述

我正在开发一个 2D 游戏。游戏中有不同的敌人,但我被其中一个困住了。敌人是一个手里拿着锤子的怪物。当玩家进入它的范围时,它会跑向玩家并用锤子攻击他。一切正常。我制作了一个预制件并在我的游戏的其余部分中使用它。但后来我注意到敌人正在攻击,甚至脚本也在工作,因为我可以在敌人攻击时看到锤子对撞机。但是那个对撞机并没有对玩家造成伤害。我检查了从脚本到标签和碰撞器的所有内容,但没有任何效果。然后我创建了一个单独的场景来解决这个问题。我只是将我的播放器和敌人从预制文件夹拖到场景中,然后猜测它在那里工作。这意味着如果只有一个敌人(它的一个实例),一切正常,但当我使用相同的脚本和其他所有内容创建同一个敌人的第二个实例时却没有。就是无法解决问题。锤击敌人

怪物脚本:

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

public class Monster : MonoBehaviour {

    private static Monster instance;

    public static Monster Instance
    {
        get {
            if (instance == null) {
                instance = GameObject.FindObjectOfType<Monster> ();
            }
            return instance;
        }
    }

    //Movement Variables
    public float movementSpeed;
    private IMonsterState currentState;
    public Animator MyAnimator{ get; set;}
    public GameObject Target{ get; set;}
    bool facingRight;
    public bool Attack{ get; set;}
    public bool TakingDamage{ get; set;}
    public float meleeRange;

    public Transform leftEdge;
    public Transform rightEdge;

    public float pushBackForce;

    public EdgeCollider2D attackCollider{ get; set;}

    //public EdgeCollider2D monsterhammer;


    public bool InMeleeRange
    {
        get{
            if (Target != null) {
                return Vector2.Distance (transform.position, Target.transform.position) <= meleeRange;
            }
            return false;
        }
    }

    // Use this for initialization
    void Start () {

        ChangeState(new IdleState());
        MyAnimator = GetComponent<Animator> ();
        attackCollider = GetComponentInChildren<EdgeCollider2D> ();
        //attackCollider=monsterhammer;
        facingRight = true;
    }

    // Update is called once per frame
    void FixedUpdate () {

        if (!Attack) {
            attackCollider.enabled = false;
        }

        if (!TakingDamage) {
            currentState.Execute ();
        }
        LookAtTarget ();
    }

    private void LookAtTarget()
    {
        if (Target != null) {
            float xDir = Target.transform.position.x - transform.position.x;

            if (xDir < 0 && facingRight || xDir > 0 && !facingRight) {
                ChangeDirection ();
            }
        }
    }

    public void ChangeState(IMonsterState newState)
    {
        if (currentState != null) {
            currentState.Exit ();
        }

        currentState = newState;
        currentState.Enter (this);
    }

    public void Move()
    {
        if (!Attack) {
            if ((GetDirection ().x > 0 && transform.position.x < rightEdge.position.x) || (GetDirection ().x < 0 && transform.position.x > leftEdge.position.x)) {
                MyAnimator.SetFloat ("speed", 1);
                transform.Translate (GetDirection () * (movementSpeed * Time.deltaTime));
            } 
            else if (currentState is MonsterPatrol) 
            {
                ChangeDirection ();
            }
        }
    }

    public Vector2 GetDirection()
    {
        return facingRight ? Vector2.right : Vector2.left;
    }

    public void ChangeDirection()
    {
        facingRight = !facingRight;
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }

    public void MoveLeft()
    {
        facingRight = false;
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }

    public void MoveRight()
    {
        facingRight = true;
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        currentState.OnTriggerEnter (other);
    }

    void OnCollisionStay2D(Collision2D other)
    {
        if (other.gameObject.tag == "Player") {

            playerHealth thePlayerHealth = other.gameObject.GetComponent<playerHealth> ();
            thePlayerHealth.addDamage (2);

            //if (playerHealth.damaged) {
            pushBack (other.transform);
            //}
        }
    }

    void pushBack(Transform pushedObject)
    {
        //Vector2 pushDirection = new Vector2 (0, (pushedObject.position.y - transform.position.y)).normalized;
        //pushDirection *= pushBackForce;
        Rigidbody2D pushRB = pushedObject.gameObject.GetComponent<Rigidbody2D> ();
        pushRB.velocity = Vector2.zero;

        if (pushedObject.position.x > transform.position.x) {
            pushRB.AddRelativeForce (Vector3.right * pushBackForce);
        } else {
            pushRB.AddRelativeForce (Vector3.left * pushBackForce);
        }
    }

    public void MeleeAttack()
    {
        attackCollider.enabled = true;
    }
}

标签: unity3d

解决方案


问题很可能在于:

get {
        if (instance == null) {
            instance = GameObject.FindObjectOfType<Monster> ();
        }
        return instance;
    }

GameObject.FindObjectOfType<Monster>();将始终返回找到的第一个此类对象,如描述中的文档中所述。

这意味着当您将多个添加Monsters到场景中时,您的变量instance将为所有实例填充相同Monster的(它在层次结构中找到的第一个)

现在既然你还没有发布你的播放器脚本,我现在必须做一些假设:你可能正在检查你<Monster> instance的播放器脚本中的某个地方,看看它是否足够靠近玩家来攻击和伤害它。除了您找到的单个怪物之外,并非所有怪物都是这种情况。您可以通过手动将每个怪物放置在您的玩家旁边来测试这一点,例如,如果您的场景中有 5 个怪物,很可能 1 会攻击,而 4 不会。FindObjectOfType<Monster>()

要解决此问题,您可以:

  • 假设您希望将 Monster 脚本的当前实例instance简单地应用于this它 ( instance = this)

  • FindObjectsOfType<Monster>()使用(注意对象后的s )将所有怪物存储在一个数组中,这将返回怪物类型的所有实例。如文档中所述


推荐阅读