【问题标题】:Unity Engine - Instantiate a prefab by collisionUnity Engine - 通过碰撞实例化预制件
【发布时间】:2018-05-29 02:30:09
【问题描述】:

我有一个名为 Player 的游戏对象。 附加到 Player 有一个名为 Player 的脚本组件(相同) 在 Player 的脚本中,我有一个名为 _weaponPrefab 的字段(GameObject 类型)

在 Inspector 中,我可以轻松地从 Prefab 文件夹中的 _weaponPrefab 变量中拖放任何预制件。

到目前为止一切顺利。我想要归档的是:能够基于 Collision2D 添加我的预制件。因此,如果我的 Player 与 Prefab(比如说一把剑)发生碰撞,那把剑的 prefab 将自动附加并插入到 Player 脚本内的 _weaponPrefab 字段中。

在我的播放器脚本下方:

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

public class Player : MonoBehaviour
{
    [SerializeField] private float _speed = 5.0f;
    [SerializeField] private float _fireRate = 0.2f;
                     private bool _canFire = true;


    [SerializeField] private GameObject _weaponPrefab; <- to populate runtime


    // Use this for initialization
    void Start ()
    {
        transform.position = Vector3.zero;
    }

    // Update is called once per frame
    void Update ()
    {
        if (Input.GetKeyDown(KeyCode.Space) && _canFire)
            StartCoroutine(Shoot());
    }


    void OnTriggerEnter2D(Collider2D other)
    {
        //here i don't know how to continue.

    }

    public IEnumerator Shoot()
    {
        _canFire = false;
        Instantiate(_weaponPrefab, transform.position + new Vector3(0, 1, 0), Quaternion.identity);
        yield return new WaitForSeconds(_fireRate);
        _canFire = true;
    }
}

编辑:我刚刚写了一条评论,说明我不知道该怎么做。

【问题讨论】:

  • 问题出在哪里?
  • 碰撞时变量获取游戏对象的引用,不好!因为如果我销毁对象,我的脚本中会出现 Missing 游戏对象。
  • 嗯,你可以在这里做的是,当它与武器发生碰撞时,你可以将 prefab weapon 的位置设置为 _weaponPrefab.
  • @ErnestoCampese 在销毁之前将其实例化。将其设置为禁用。需要时激活它。
  • 我不能在变量中实例化一个对象,可以吗?所以我应该这样做:_weaponPrefab = Instantiate(other.gameObject) ??

标签: c# unity3d collision-detection


【解决方案1】:

有很多方法可以实现你的愿望。

我的建议一开始可能超出了你的舒适范围,但它会为你提供最大的灵活性,最终让你的游戏更容易编程/设计/维护(在我看来)。

首先制作一个可编写脚本的对象 (what is a scriptable object and how do i use it?)

using UnityEngine;
using System.Collections;
using UnityEditor;

public class Item
{
    [CreateAssetMenu(menuName = "new Item")]
    public static void CreateMyAsset()
    {
        public GameObject prefab;
        // you can add other variables here aswell, like cost, durability etc.
    }
}

创建一个新项目(在 Unity 中,Assets/新项目)

然后创建一个可以容纳该项目的单一行为脚本。在您的情况下,我们将其命名为“Pickup”。

public class Pickup : MonoBehaviour
{
    public Item item;
}

最后在您的播放器脚本中,将您的 on TriggerEnter 更改为:

void OnTriggerEnter2D(Collider2D other)
{
    if(other.GetComponentInChildren<Pickup>())
    {
        var item = other.GetComponentInChildren<Pickup>().item;
        _weaponPrefab = item.prefab;
    }

}

【讨论】:

  • 这可以工作,我认为对我来说是最好的解决方案。我学会了如何创建可编写脚本的对象,因为我不明白如何在脚本运行时应用它们,你的最后一个例子是other.GetComponentInChildren&lt;Pickup&gt;().prefab 我只是让项目成为我的主要对象的子对象,不是吗?
  • 假设:public class Player : MonoBehavior { public ItemObject item; //the scriptable object void Start(){ // do stuff} } 通常我会将 ScriptableObject 的资产拖放到 inspector 中。如何在 runtime 期间通过脚本查找和分配资产(来自 Project View),并可能将其添加到 List 、 Array[] 或仅添加到变量?
  • 我编辑了我的答案。我在拾取脚本中做了一个小错字。您可以通过 Pickup.item 调用“项目”,然后可以将其添加到您想要的任何内容中
【解决方案2】:

当你实例化一个游戏对象时,你基本上需要传递 3 个参数(但并非所有参数都是强制性的,check):

  • 预制件
  • 位置
  • 旋转

假设您的手或角色背后有一个占位符,以便在收集武器后将其保存在那里。这个占位符可以是一个空的游戏对象(没有附加预制件,但会有一个 transform.position 组件)

现在让我们假设您有一个场景中的武器列表,其中包含它们的预制件,并且每个都有不同的标签。然后你可以这样做:

GameObject weapon;
GameObject placeholder;

public Transform sword;
public Transform bow;
...


void OnTriggerEnter2D(Collider2D other)
{
    //You can use a switch/case instead
    if(other.gameObject.tag == "sword"){
        Instantiate(sword, placeholder.transform.position, Quaternion.identity);
    }else if(other.gameObject.tag == "bow"){
        Instantiate(bow, placeholder.transform.position, Quaternion.identity);
    }...

}

【讨论】:

  • 是的,这是一个可能的解决方案,但我必须一直添加 Transform 项目并使用标签:/
  • @ErnestoCampese 是的,这将需要更多时间在编辑器上,但稍后它会在计算上更容易/更快。这将始终取决于您计划拥有的游戏对象的数量。
【解决方案3】:

我要做的是首先在资源文件夹中的Dictionary&lt;string, GameObject&gt; 中加载预制件。

首先,我使用Start 方法中的所有武器预制件填充字典,其中对象的标签作为键。然后在OnTriggerEnter2D 中检查标签是否在字典中,然后从那里实例化它。

看一下代码:

private Dictionary<string, GameObject> prefabs;

// Use this for initialization
void Start () {
    var sword = (GameObject) Resources.Load("Weapons/sword", typeof(GameObject));
    prefabs.Add("sword", sword);
    // Add other prefabs
};

void OnTriggerEnter2D(Collider2D other)
{
    if (prefabs.ContainsKey(other.tag))
        Instantiate(prefabs[other.tag]);
}

注意:你的预制件需要在文件夹Assets/Resources/Weapons,因为我使用了Resources.Load("Weapons/sword", typeof(GameObject));

【讨论】:

  • 有人说使用 Resources.load 不是一个好习惯=\
  • @ErnestoCampese Resources 已被弃用,取而代之的是一个名为 Asset Bundles 的新系统。不幸的是,我(目前)对它们的工作原理一无所知。
猜你喜欢
  • 2017-09-23
  • 2016-11-04
  • 2021-10-12
  • 1970-01-01
  • 2021-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-28
相关资源
最近更新 更多