Unity中的对象引用-如何在脚本之间通信
事先声明
- 我们有一个单元管理节点(包含Transform和UnitFactory组件)和一个Unit预制体节点(包含Transform和Unit组件)
- 要完成对实例化Unit预制体节点中Unit组件的获取
如果你是新游戏开发者那么你可能会通过以下代码实现
using UnityEngine;
public class UnitFactory : MonoBehaviour
{
[SerializeField] private GameObject _unitPrefab;
private GameObject _unitControler;
private Unit _unitInstance;
private void Start()
{
_unitControler = Instantiate(_unitPrefab);
_unitInstance = _unitControler.GetComponent<Unit>();
}
}
但是这样实现方式是慢的,尽管现在计算机运算速度很快,但我还是推荐你用下面这种方法
using UnityEngine;
public class UnitFactory : MonoBehaviour
{
[SerializeField] private Unit _unitPrefab;
private Unit _unitInstance;
private void Start()
{
_unitInstance = Instantiate(_unitPrefab);
}
}
只要脚本上继承自MonoBehaviour那么都是可以通过这种方法来获取组件的
如果我们在此基础上对更多单位进行管理,那么我们可以:
在UnitManager类中添加一个私有列表
private List<Unit> _units = new List<Unit>();
注意:我们并没有引入List的命名空间,所以要在UnitManager脚本顶部加上:
using System.Collections.Generic;
然后我们在UnitFactory.Start函数中为List添加一些单位对象
for (int i = 0; i < 10; i++)
{
_units.Add(Instantiate(_unitPrefab));
}
假设我们想对单位位置进行实时控制,那么我们可以中UnitFactory.Update函数中加入
foreach (var unit in _units)
{
unit.transform.position = Random.insideUnitSphere;
}
嗯现在就很好,那么如果我们想在新脚本MapManager.cs中获取这些列表并且出于任何原因更改单元对象的位置,我们要做什么?
- 在Hierarchy窗口中新建一个空对象,并且为它添加MapManager脚本
如果是这样的话很多东西都想访问,我们可以把UnitFactory变为静态单例
在UnitFactory中添加以下代码
public static UnitFactory Instance;
private void Awake() => Instance = this;
并且修改_units访问级别并修改名称为Units
public List<Unit> Units = new List<Unit>();
如果你想保留_units原有数据可以把上面的代码修改为
[FormerlySerializedAs("_units")]
public List<Unit> Units = new List<Unit>();
//这一行引用必须要加否则会报错[FormerlySerializedAs("_units")]特性不存在
using UnityEngine.Serialization;
然后在MapManager中添加以下代码:
using UnityEngine;
public class MapManager : MonoBehaviour
{
// Update is called once per frame
void Update()
{
foreach (var unit in UnitFactory.Instance.Units)
{
unit.transform.position = Random.insideUnitSphere;
}
}
}
当然我们还可以为了不让UnitFactory.Instance.Units不让外部脚本设置它,只是获取,那么
我们可以把UnitFactory中的Units字段修改为属性的形式
public List<Unit> Units { get; private set; } = new List<Unit>();
另外我们通常写碰撞函数为了检测碰撞对象是否有Unit脚本
using UnityEngine;
public class Unit : MonoBehaviour
{
private void OnCollisionEnter(Collision other)
{
var unit = other.gameObject.GetComponent<Unit>();
if (unit != null)
{
//Do something.
}
}
}
我更推荐大家像下面这样做
using UnityEngine;
public class Unit : MonoBehaviour
{
private void OnCollisionEnter(Collision other)
{
Unit unit;
if (other.gameObject.TryGetComponent(out unit))
{
//Do something.
}
}
}
千万不要像下面这样调用对象中所有对应函数,这是非常不推荐的做法,影响性能
using UnityEngine;
public class Unit : MonoBehaviour
{
public void Example(int i){}
private void OnCollisionEnter(Collision other)
{
other.gameObject.SendMessage("Example", 10);
}
}
今天的感悟就写到这里了,bye~