如何正确检测游戏对象?

时间:2019-01-07 03:39:46

标签: c# unity3d

我的目标是编写一个可以在不同游戏对象上使用的脚本,并且该脚本应该在该游戏对象上具有与之相关的特定变量,而不会影响该过程中的其他脚本。

例如,如果我使用此脚本并将其放在两个游戏对象上,则每个游戏对象在同一脚本中应具有自己的唯一变量值。

如果我的问题还不够清楚,我很乐意进一步阐述。

我对Unity编辑器有很好的了解,但是,我对C#还是很陌生,所以我认为在代码中的某个地方出现菜鸟错误并不觉得不合理。

设置的方式是,我有两个单独的脚本:

  • Fighting控制诸如TeamHealthAttack DamageCool DownCooling down和{{1} }

  • Snap控制是否检测到由于敌人进入触发半径而激活的触发。

我目前遇到的问题在于我猜的TrigDetect脚本。

还应注意,有问题的每个游戏对象附带的空白都包含这两个脚本,并标记为“部队”。

TrigDetect

TrigDetect

战斗

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

public class TrigDetect : MonoBehaviour
{
    //public GameObject[] Enemy;
    bool once = false; 

    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Troop"))
        {
            //Debug.Log("Entered");
        }
    }

    void OnTriggerExit(Collider other)
    {
        if (other.CompareTag("Troop"))
        {
            //Debug.Log("Exitted");
        }
    }

    void OnTriggerStay(Collider other)
    {
        if (other.CompareTag("Troop"))
        {

            Fighting self = GetComponent<Fighting>();

            GameObject g = GameObject.Find("Detection");

            Fighting fScript = g.GetComponent<Fighting>();

            //Enemy = GameObject.FindGameObjectsWithTag("Troop");
            //Debug.Log("Staying");
            //Debug.Log(Enemy);
            //Debug.Log(self.Health);
            //Debug.Log(fScript.Health);

            if (once == false)
            {
                Debug.Log("I am the team:" + self.Team);
                Debug.Log("I have detected the team:" + fScript.Team);
                once = true;
            }

            if (self.Team != fScript.Team)
            {

                if (self.CoolingDown == false)
                {
                    self.CoolingDown = true;
                    fScript.Health -= self.AttackDamage;
                }

                else
                {
                    self.CoolDown -= Time.deltaTime;
                    if (self.CoolDown <= 0)
                    {
                        self.CoolingDown = false;
                        self.CoolDown = self.original;
                    }
                }
            }   
        }
    }
}

当我将一个游戏对象移动到另一个对象的触发半径中时,预期结果是它们应该开始基于using System.Collections; using System.Collections.Generic; using UnityEngine; public class Fighting : MonoBehaviour { public int Team = 1; public int Health = 100; public int AttackDamage = 10; public float CoolDown = 2; public float original = 2; public bool CoolingDown = false; public bool Snap = false; // Update is called once per frame void Update () { if (Snap == true || Health <= 0) { //Destroy(gameObject, .5f); Destroy(transform.parent.gameObject); } if (Input.GetKey(KeyCode.N)) Instantiate(transform.parent.gameObject); } } 值相互减去Health 。每当AttackDamage值为false时,他们就应该这样做。执行攻击后,它会翻转到CoolingDown并启动一个计时器,计时器完成后它会翻转回到true

但是,在将两个对象移动到彼此的半径中时,第一个对象的健康状况如预期的那样消失,然后继续执行任何操作,直到其健康状况达到false,然后由于对象攻击而死亡。攻击对象已成功攻击了另一个对象,但仍不受其攻击对象的影响。

2 个答案:

答案 0 :(得分:5)

基本上,g = Find(name)仅返回该名称的任何内容的第一个实例,因此几乎可以保证您的OnTriggerStay(Collider other)永远不会永远成为与您的触发/冲突条件相关的对象。 GameObject g = GameObject.Find("Detection"); Fighting fScript = g.GetComponent<Fighting>(); 已经为您提供了位于触发区域中的“其他”对撞机,因此请使用它。 :)

替换此:

    Fighting fScript = other.GetComponent<Fighting>();

与此:

#include <bitset>
...
std::bitset<128> bs;
bs[0] = 1; bs[127] = 1;
std::cout << bs.to_string() << std::endl;
// prints 128 digits with ones on both ends

答案 1 :(得分:2)

到您的问题标题:

每个不变的(非静态)值对于相应组件始终是唯一的,因此对于附加到的相应GameObject也是唯一的。您可能想换个问题,因为这实际上不是您的问题。

问题在于,当您这样做

GameObject.Find("Detection");

它实际上两次都找到了相同对象:即层次结构中的第一个对象。因此,在这两个组件之一中,您将找到自己的空对象,并在

中跳过其余部分
if(self.Team != FScript.Team) 

..您可以尝试使用

other.Find("Detection");

只能在相应的上下文中搜索。.但是,您完全不应该使用Find

  1. 这是非常强烈的表现
  2. 您应该始终重复使用引用,而不是一遍又一遍地搜索
  3. 您不需要这种情况

    由于您说两个脚本都附加到了同一对象,因此您可以简单地使用

    GetComponent<Fighting>();
    

    ,您可以在Awake中这样做,并重新使用引用:

    private Fighting myFighting;
    
    private void Awake()
    {
        myFighting = GetComponent<Fighting>();
    }
    

除了碰撞之外,您也不必使用Find,因为您已经有了与之碰撞的对象的引用:other.gameObject。我不知道您的整个设置,但是您可以在层次结构中向下搜索该组件

// the flag true is sued to also find inactive gameObjects and components
// leave it without parameters if you don't want this
var otherFighting = other.GetComponentInChildren<Fighting>(true);

或在层级中向上滑动

var otherFighting = other.GetComponentInParent<Fighting>(true);

或者如果您已经知道自己与正确的GameObject完全碰撞,则只需使用

var otherFighting = other.GetComponent<Fighting>();

在我的示例中将使用后者。

Update中的无处不在比健康更是一个巨大的性能问题。您应该有一个方法,例如TakeDamage,仅在您的健康状况实际发生变化时进行检查:

战斗

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

public class Fighting : MonoBehaviour 
{
    public int Team = 1;
    public int Health = 100;
    public int AttackDamage = 10;
    public float CoolDown = 2;
    public float original = 2;

    // you don't need that flag see below
    //public bool CoolingDown = false;
    public bool Snap = false;

    private void Update()
    {
        // you might also put this in a callback instead of update at some point later
        if(Snap == true)
        {
            Destroy(transform.parent.gameObject);
        }

        // Note: this also makes not muh sense because if you destroyed
        // the parent than you cannot instantiate it again!
        // use a prefab instead
        if (Input.GetKey(KeyCode.N)) Instantiate(transform.parent.gameObject);
    }

    public void TakeDamge(int DamageAmount)
    {
        Health -= DamageAmount;

        if (Health > 0) return;

        Destroy(transform.parent.gameObject);
    }
}

另一个普遍的性能问题:即使StartUpdate等为空,如果脚本中存在它们,Unity也会调用它们。因此,如果您不使用它们,则将它们完全删除以避免不必要的开销。

所以我会

TrigDetect

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

public class TrigDetect : MonoBehaviour
{
    bool once = false; 

    private Fighting myFighting;

    private void Awake()
    {
        myFighting = GetComponent<Fighting>();
    }

    void OnTriggerStay(Collider other)
    {
        // if wrong tag do nothing
        if (!other.CompareTag("Troop")) return;

        Fighting fScript = other.GetComponent<Fighting>();

        // here you should add a check
        if(!fScript)
        {
            // By using the other.gameObject as context you can find with which object
            // you collided exactly by clicking on the Log
            Debug.LogError("Something went wrong: Could not get the Fighting component of other", other.gameObject);
        }

        if (!once)
        {
            Debug.Log("I am the team:" + self.Team);
            Debug.Log("I have detected the team:" + fScript.Team);
            once = true;
        }

        // if same team do nothing
        if (self.Team == fScript.Team) return;

        // you don't need the CoolingDown bool at all:
        self.CoolDown -= Time.deltaTime;

        // if still cooling down do nothing
        if(self.CoolDown > 0) return;

        fScript.TakeDamage(self.AttackDamage);
        self.CoolDown = self.original;
    }
}