在 Unity 中创建后引用实例化对象

Posted

技术标签:

【中文标题】在 Unity 中创建后引用实例化对象【英文标题】:Referencing Instantiated objects after their creation in Unity 【发布时间】:2022-01-20 01:45:01 【问题描述】:

嗨!

在 cmets 与 Ruzihm 讨论之后。我现在创建了一个简单的游戏版本,以便更好地提出我遇到的问题。

现在的问题是,因为我无法在检查器中手动创建与 testObject 字段的连接。我现在如何告诉 Unity 在游戏运行时使用我的实例化对象?

对于一次可能有 100 多个单元处于活动状态的 RTS 游戏来说,这是一个好的解决方案吗?这里的最终目标是将这个力施加到光标周围的半径上。我正在考虑使用Physics.OverlapSphere

这是我所拥有的最小场景:

    新的 Unity 场景 将 InputManager 附加到主摄像头。 创建了一个太空舱和一架飞机。 为 Capsule 添加了 ApplyForce 从胶囊中创建了一个预制件并将其从场景中删除。 在 InputManager 中,我添加了按空格键来实例化带有 ApplyForce 脚本的胶囊的功能.. 将胶囊预制件拖到 InputManager "objectToGenerate"
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace GL.RTS.Mites

    public class InputManager : MonoBehaviour
    
        public GameObject testObject;

        public ApplyForce onSpawnTest;
        public GameObject objectToGenerate;

        void Start()
        
            onSpawnTest = testObject.GetComponent<ApplyForce>();
        

        void Update()
        
            if(Input.GetKeyDown(KeyCode.Space))
            
            Instantiate(objectToGenerate);
            
            
            if (Input.GetMouseButton(0))
            
                onSpawnTest.PushForward();
            
        
    

我附加到 Capsule 的 ApplyForce 脚本:

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

namespace GL.RTS.Mites

    public class ApplyForce : MonoBehaviour
    
        public float moveSpeed;
        Rigidbody rb;

        void Start()
        
            rb = GetComponent<Rigidbody>();
            Debug.Log("A Mite has spawned!");
        

        public void PushForward()
        
            rb.AddRelativeForce(Vector3.up * moveSpeed * Time.deltaTime);
            Debug.Log("A force of: " + moveSpeed + " is being added.");
        
    

【问题讨论】:

这段代码是哪种编程语言?:C# 还是 UnityScript?对我来说它看起来像 C#,但你已经标记了两者。 这是c#,抱歉我会删除UnityScript标签 您要移动的对象是初始场景的一部分还是稍后生成的?如果稍后,InputManager.Start 方法可能会在生成对象实例化之前运行。 你是对的@Ruzihm,我尝试创建一个全新的 Unity 场景,将 InputManager 连接到主摄像头。创造了一个胶囊和一架飞机。将 ApplyForce 添加到 capsule,并在检查器中将 capsule 引用到 InputManager 的“testObject”。增加了一吨的力量,我确实开始飞了!这是一个很好的原因,然后我知道这不是代码的问题!我的问题是我的“真实”Unity场景中的“testObject”没有正确引用实例。所以我需要弄清楚在创建实例后如何引用它。 @Ruzihm,我终于有时间更新问题以更好地反映我的问题。我想我知道我要在代码中做什么,但是由于我在 C# 方面的经验有限,我无法引用正确的对象来增加力量。任何帮助都是极好的!我也愿意接受建议,因为我不知道这种方法是否能很好地扩展:) 【参考方案1】:

好吧,您正在创建对象的新实例,但是您的输入管理器立即忘记了它们(请注意,您对返回值什么都不做)。 InputManager 只知道在其Start 中创建的ApplyForce(然后根据鼠标输入与其交互),而您的ApplyForce 脚本对任何InputManager 一无所知。因此,只有第一个实例对鼠标输入做出反应也就不足为奇了。

因此,必须对您的InputManager 和/或您的ApplyForce 做一些事情。您的 InputManager 可以记住它创建的实例(这还不够,因为例如,如果地图触发器创建新的玩家可控单位会怎样)或者它可以每次都去寻找单位。

您的ApplyForce 可以在创建InputManager 时注册它们,但是您需要遍历这些单元并找出鼠标下的单元。

由于您只想根据光标附近或光标下的内容进行选择,并且仅在输入发生时而不是像每一帧一样,我会采用最简单的方法,让您的 InputManager 在需要时找到单位他们。如下所示,在 cmets 中进行解释:

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

namespace GL.RTS.Mites

    public class InputManager : MonoBehaviour
    
        public GameObject testObject;

        public ApplyForce onSpawnTest;
        public GameObject objectToGenerate;
        
        private Camera mainCam;

        // which layers to consider for cursor detection
        [SerializeField] LayerMask cursorLayerMask;

        // how big for cursor detection
        [SerializeField] float cursorRadius;

        void Awake()
        
            // cache main camera
            mainCam = Camera.main;
        

        void Update()
        
            if(Input.GetKeyDown(KeyCode.Space))
            
                Instantiate(objectToGenerate);
            
            
            if (Input.GetMouseButton(0))
            
                Collider[] colls = FindCollidersUnderCursor();

                // check each collider for an applyforce and use it if present
                foreach( Collider coll in colls)
                
                    ApplyForce af = coll.GetComponent<ApplyForce>();
                    if (af != null) 
                    
                        af.PushForward();
                    
                 
            
        

        Collider[] FindCollidersUnderCursor()
        
            // find ray represented by cursor position on screen
            // and find where it intersects with ground

            // This technique is great for if your camera can change
            // angle or distance from the playing field.

            // It uses mathematical rays and plane, no physics
            // calculations needed for this step. Very performant.
            Ray cursorRay = mainCam.ScreenPointToRay(Input.mousePosition);
            Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
            if (groundPlane.Raycast(cursorRay, out float cursorDist))
            
                Vector3 worldPos = cursorRay.GetPoint(cursorDist);

                // Check for triggers inside sphere that match layer mask
                return Physics.OverlapSphere(worldPos, cursorRadius, 
                        cursorLayerMask.value, QueryTriggerInteraction.Collide);
            

            // if doesn't intersect with ground, return nothing
            return new Collider[0];
        
    

当然,这将要求您有兴趣操纵的每个单位都有一个触发对撞机。

【讨论】:

我会投赞成票,但距离这样做还有 2 分。有机会我会回来的!无论如何,谢谢一百万!这给了我很多工作。我知道我的问题很混乱,但我从像这样缩小范围中学到了很多东西。对撞机的技巧太棒了!我喜欢为你的射线创建一个虚拟地面的想法。并且您将 OverlapSphere 的实现作为一个不错的奖励,这终于有意义了。

以上是关于在 Unity 中创建后引用实例化对象的主要内容,如果未能解决你的问题,请参考以下文章

转载关于对方法实例化的相关感悟以及unity的50个技巧

在 Unity 中销毁实例化游戏对象的问题

Unity Photon:实例化非预制对象?

在 Unity 中沿光线投射实例化预定义数量的对象

java中创建对象 类名 对象名=new 类名(); 后面的()什么意思

音频不会在 Unity 中的实例化对象上播放