简体   繁体   English

如何正确检测游戏对象?

[英]How do I properly detect game objects?

My goal is to write one script that I can use on different game objects and it should have specific variables tied to it on that game object only without affecting other scripts in the process. 我的目标是编写一个可以在不同游戏对象上使用的脚本,并且该脚本应该将特定变量绑定到该游戏对象上,而不会影响过程中的其他脚本。

For example, if I take this script and put it on two game objects each game object should have their own unique variable value in that same script. 例如,如果我采用此脚本并将其放在两个游戏对象上,则每个游戏对象在同一脚本中应具有自己的唯一变量值。

If my question is not clear enough, I'm more than happy to elaborate further. 如果我的问题还不够清楚,我很乐意进一步阐述。

I have a good understanding of the Unity Editor, however, I'm pretty new to C# so I don't think it's unreasonable that I made a rookie mistake somewhere in my code. 我对Unity编辑器有很好的了解,但是,我对C#还是很陌生,所以我认为在代码中的某个地方出现菜鸟错误并不觉得不合理。

The way I've got things setup is that I have two separate scripts: 我进行设置的方法是,我有两个单独的脚本:

  • Fighting controls the values like the Team , Health , Attack Damage , Cool Down , Cooling down and Snap Fighting控制,如价值观TeamHealthAttack DamageCool DownCooling downSnap

  • TrigDetect controls the detection of a trigger being activated as a result of an enemy entering the trigger radius. TrigDetect控制检测到由于敌人进入触发半径而激活的触发。

The problem I'm currently having lies in the TrigDetect script I guess. 我目前遇到的问题在于我猜的TrigDetect脚本。

It should also be noted that an empty attached to each game object in question contains both of these scripts and is tagged as "Troop". 还应注意,每个有问题的游戏对象附带的空白包含这两个脚本,并标记为“部队”。

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;
                    }
                }
            }   
        }
    }
}

Fighting 战斗

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);
    }
}

The expected result when I move one game object into the trigger radius of the other is that they should both start subtracting Health from each other based on the AttackDamage value. 当我移动一个游戏对象到其他的触发半径预期的结果是,他们应该开始减去Health基础上,相互AttackDamage值。 They should do this every time the CoolingDown value is false. 每当CoolingDown值为false时,他们都应该这样做。 When an attack is executed, it's flipped to true and a timer starts, when the timer is done it's flipped back to false . 执行攻击时,它将翻转为true然后启动计时器;完成计时器后,它将翻转为false

However, upon moving the two objects into each other's radius', the first object has its health taken away as expected and then proceeds to do nothing until it's health reaches 0 then it dies because of the object attacking it. 但是,在将两个对象移动到彼此的半径中时,第一个对象的运行状况将按预期方式移除,然后继续执行任何操作,直到其运行状况达到0然后由于对象攻击而死亡。 The object attacking is successfully attacking the other object but, is still not being affected by the object it's attacking. 对象攻击正在成功攻击其他对象,但是仍然不受其攻击的对象的影响。

Basically, Find(name) only returns the first instance of anything by that name, thus your g = Find(name) is almost guaranteed to never be the object related to your trigger/collision condition. 基本上, Find(name)仅返回该Find(name)的任何内容的第一个实例,因此几乎可以保证您的g = Find(name) 永远不会成为与触发器/碰撞条件相关的对象。 The OnTriggerStay(Collider other) already gives you the 'other' collider that's in your trigger zone, so use it. OnTriggerStay(Collider other)已经为您提供了位于触发区域中的“ other”对撞机,因此请使用它。 :) :)

Replace this: 替换为:

    GameObject g = GameObject.Find("Detection");
    Fighting fScript = g.GetComponent<Fighting>();

with this: 有了这个:

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

To your question header: 到您的问题标题:

Every instaced (non-static) value is allways unique to the according component and thereby to the according GameObject it is attached to. 每instaced(非静态)值是永诺特有的根据组分和由此将根据GameObject它连接到。 You might want to refrase the question because this is actually not your issue. 您可能想换个问题,因为这实际上不是您的问题。

The problem is that when you do 问题是当你这样做

GameObject.Find("Detection");

it actually finds the same object both times: Namely the first one in the hierarchy. 实际上,它两次都找到了相同的对象:即层次结构中的第一个对象。 So in one of of the two components you find your own empty object and skip the rest in 因此,在两个组件之一中,您将找到自己的空对象,然后跳过其余部分

if(self.Team != FScript.Team) 

.. you could try to use ..您可以尝试使用

other.Find("Detection");

instead to only search in the according context .. However, you should not use Find at all! 而是只在相应的上下文中搜索..但是,您根本应该使用“ Find

  1. It is very performance intense 性能非常好
  2. You should allways reuse references and not search them over and over again 您应该始终重复使用引用,而不是一遍又一遍地搜索它们
  3. You don't need it in your case 您不需要这种情况

    Since you say both scripts are attached to the same object you can simply use 由于您说两个脚本都附加到同一对象,因此您可以简单地使用

     GetComponent<Fighting>(); 

    and you can do so already in Awake and reuse the reference instead: 并且您已经可以在Awake执行此操作并改为重用引用:

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

Than for the collision you don't have to use Find either because you already have the reference of the object you collide with: other.gameObject . 除了碰撞之外,您也不必使用Find ,因为您已经有了other.gameObject碰撞的对象的引用: other.gameObject I don't know your entire setup but you can search for the component either downwards in the hierachy 我不知道您的整个设置,但是您可以在层次结构中向下搜索组件

// 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);

or searcg upwards in the hierachy 或在层级中向上抬起

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

or if you already know you collide exactly with the correct GameObject anyway simply use 或者,如果您已经知道自己与正确的GameObject完全碰撞,则只需使用

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

I will use the latter in my example. 在我的示例中将使用后者。

Than cheking the health all the time in Update is a huge perfomance issue. Update比始终保持健康是一个巨大的性能问题。 You should rather have a method eg TakeDamage and do your check only if your health is actually changed: 您应该有一个方法,例如TakeDamage并且仅当您的健康状况实际发生变化时才进行检查:

Fighting 战斗

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);
    }
}

Another performance issue in general: Even if Start , Update etc are empty, if they are present in your script Unity will call them. 一般而言,另一个性能问题是:即使StartUpdate等为空,如果脚本中存在它们,Unity也会调用它们。 So if you don't use them then completely remove them to avoid that useless overhead. 因此,如果您不使用它们,则将它们完全删除以避免不必要的开销。

So I would have 所以我会

TrigDetect 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;
    }
}

暂无
暂无

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

相关问题 如何正确创建游戏对象 - How to properly create Game Objects 我试图创建不应该碰撞的随机放置的对象。 我的对撞机似乎无法正确检测到。 我如何解决它? - I was trying to create randomly positioned objects that should not collide. My collider does not seem to detect properly. How do I fix it? 如何在 Unity 中启用和禁用多个游戏对象? - How do I enable and disable multiple game objects in Unity? 如何从游戏对象列表中删除游戏 object,其中每个游戏对象都是实例化的“棋子”或 class - How do I remove a game object from a list of game objects where each gameobject is an instantiated 'piece' or a class 如何确保在.NET中正确处理对象? - How do I ensure that objects are disposed of properly in .NET? 如何正确清理 Excel 互操作对象? - How do I properly clean up Excel interop objects? 如何让一个游戏对象的 x 位置等于另一个对象的 x 位置? (2D Unity 游戏) - How do I get one game objects x position to equal another objects x position? (2D Unity Game) 如何正确地将“if-statement”中的函数应用于 C# 和 Unity 中的特定游戏 object 标签? - How do I properly apply functions in 'if-statement' to a specific game object tag in C# & Unity? 重新加载游戏场景时,对象不会统一加载 - When I reload my game scene the objects do not reload in unity 如何检测我的游戏文件夹中是否有不同的文件夹? - how can i detect if there are different folders in the my game folder?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM