本文同時發布至我的個人博客,點擊進入我的個人博客閱讀。本博客供技術交流與經驗分享,可自由轉載。轉載請在評論區或私信簡單通知,感謝!
游戲需求
- 實現點擊效果。
- 用
Plane
或其他物體做地面, tag 為Finish
。 - 點擊地面后,出現一個圓形攻擊標記,兩秒后自動消失。注意:該攻擊標記不能擋住點擊。(Primitive Objects / Cylinder)
- 請使用一個簡單工廠創建、管理這些的標記,并自動收回這些標記(注意,這些對象創建后,放在列表內,不必釋放)。
效果展示:
實現過程
(一)Camera
和 Plane
的基本配置
? 設置好Camera
的位置與角度,并設置視圖為正交,如下圖:
? 為方便顯示,創建Plane
后再添加一個Material
,使其變為藍色,如下圖:
(二)定義并實現EsayFactory
類
? 首先創建一個繼承MonoBehaviour
的Basecode
類,自定義一個namespace
并引用,在該namespace
定義并實現EsayFactory
類。
? EsayFactory
類為工廠類,它負責管理Cylinder
的創建、停用和重復使用。重復使用的實現主要通過講使用完畢的Cylinder
加入一個List
中,在需要時重新拿出使用的方法來實現。這樣做的好處是,避免了重復地創建和銷毀游戲對象,能減少游戲的內存消耗。
? 需要注意的是:嚴格控制單實例,避免用戶創建多個EsayFactory
對象。
public class EsayFactory : System.Object
{
public List<GameObject> markList = new List<GameObject>();
private static EsayFactory _factory;
private Camera _camera;
//控制單實例
public static EsayFactory GetInstance()
{
if (_factory == null)
{
_factory = new EsayFactory();
_factory._camera = Camera.main;
}
return _factory;
}
public void placeAttackMark(Vector3 target)
{
Ray _ray = _camera.ScreenPointToRay(target);
RaycastHit _hit;
if (Physics.Raycast(_ray, out _hit))
{
if (_hit.collider.gameObject.tag.Contains("Finish"))
{
GameObject attackMark;
//List中若無可用對象則創建新對象
if (markList.Count == 0)
{
attackMark = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
//避免Cylinder擋住點擊
attackMark.GetComponent<Collider>().enabled = false;
}
//若有可用對象則拿出重用
else
{
attackMark = markList[0];
markList.RemoveAt(0);
}
attackMark.transform.SetParent(_hit.collider.gameObject.transform);
attackMark.transform.position = _hit.point;
//添加一個自定義的實現了延時2s刪除功能的組件
DelayToDelete _delay = attackMark.AddComponent<DelayToDelete>();
_delay.delete(markList, attackMark, 2.0f);
}
}
}
(三)實現延時刪除
? DelayToDelete
繼承MonoBehaviour
類,實現了兩個方法:私有方法delayTo()
實現了具體延時功能的實現,公有方法delete()
實現了具體的2秒后刪除的功能需求。這里采用了Action
類來代替Invoke()
方法,其好處是拓展性更強,可讀性也更高。StartCoroutine
是MonoBehaviour
中的一個調用協同程序的方法,通過使用 yield return new WaitForSeconds(delaySeconds);
可以實現延時一定實現后執行。
public class DelayToDelete : MonoBehaviour
{
public void delete(List<GameObject> markList, GameObject attackMark, float delaySeconds)
{
StartCoroutine(delayTo(() =>
{
//移出游戲范圍并加入List中等待重用
attackMark.transform.position = new Vector3(0f, -99f, 0f);
markList.Add(attackMark);
}
, 2.0f));
}
private IEnumerator delayTo(Action action, float delaySeconds)
{
//延時執行action();
yield return new WaitForSeconds(delaySeconds);
action();
}
}
(四)完成Basecode
? Basecode
類主要負責每幀檢查用戶是否做了點擊事件,若點擊則調用placeAttackMark
方法:
public class Basecode : MonoBehaviour {
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown(0))
{
Vector3 point = Input.mousePosition;
EsayFactory.GetInstance().placeAttackMark(point);
}
}
}