如何不绑定脚本且不继承MonoBehaviour做U3D的开发

2015年09月11日 13:41 1 点赞 0 评论 更新于 2025-11-21 18:26

作者:大帅纷纭

泰斗原文链接:http://www.taidous.com/forum.php?mod=viewthread&tid=33049

要点

通常我们会把脚本绑定在对象上,换个思路,为何不把对象抓到脚本里进行处理呢?

问题提出

若不继承MonoBehaviour,协同、实例化以及每帧运算该如何实现呢?最近看到一个关于技能冷却的话题,便想尝试一下。

项目相关介绍

1. 界面

包含2D和3D摄像机。

2. 对象排布

暂未详细说明对象具体排布情况。

3. 代码实现

(1)主要负责UI界面的代码
using Assets.Classes.com.system;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Assets.Classes.com.view.ui
{
// 技能类型枚举,每个枚举值代表该技能的冷却时间
enum SKILLTYPE
{
Q_SKILL = 10,
W_SKILL = 20,
E_SKILL = 30,
R_SKILL = 100
}

class SkillsMenu_UI
{
private SKILLTYPE skillType; // 技能类型
private bool isSkill = false; // 是否正在释放技能
private bool isCooling = false; // 是否正在冷却
private float coolingTime; // 冷却时间
private GameObject currentObj;

public SkillsMenu_UI(GameObject skillsObj)
{
// 为各个技能按钮添加事件监听
HandlerListener("Q_Skill", skillsObj);
HandlerListener("W_Skill", skillsObj);
HandlerListener("E_Skill", skillsObj);
HandlerListener("R_Skill", skillsObj);
}

private void HandlerListener(string skillName, GameObject go)
{
// 查找技能按钮的Transform
Transform Skill = go.transform.FindChild(skillName);
// 为技能按钮添加点击事件监听
UIEventListener.Get(Skill.gameObject).onClick = OnClick;
}

private void OnClick(GameObject go)
{
Debug.Log("点击了技能按钮 : " + go.name);
switch (go.name)
{
case "Q_Skill":
skillType = SKILLTYPE.Q_SKILL;
HandlerSkill(skillType, go);
break;
case "W_Skill":
skillType = SKILLTYPE.W_SKILL;
HandlerSkill(skillType, go);
break;
case "E_Skill":
skillType = SKILLTYPE.E_SKILL;
HandlerSkill(skillType, go);
break;
case "R_Skill":
skillType = SKILLTYPE.R_SKILL;
HandlerSkill(skillType, go);
break;
}
}

private void HandlerSkill(SKILLTYPE type, GameObject go)
{
// 判断技能是否冷却或者是否有技能未释放
if (!isCooling || !isSkill)
{
currentObj = go;
skillType = type;
isSkill = true;
isCooling = true;
HandlerCoolingTimer(type);
Debug.Log("可以释放该技能了");
}
else
{
Debug.Log("不可释放技能");
}
}

private void HandlerCoolingTimer(SKILLTYPE type)
{
coolingTime = (float)type;
// 设置技能冷却进度条的初始填充量
currentObj.transform.FindChild("Fore").gameObject.GetComponent<UISprite>().fillAmount = 1;
// 添加每帧回调函数
GameController.getInstance().addInterval(TimerCutting);
Debug.Log("开始倒计时");
}

private void TimerCutting(float t)
{
// 减少技能冷却进度条的填充量
currentObj.transform.FindChild("Fore").gameObject.GetComponent<UISprite>().fillAmount -= (1.0f / coolingTime) * Time.deltaTime;
float tempTime = currentObj.transform.FindChild("Fore").gameObject.GetComponent<UISprite>().fillAmount;
Debug.Log("------------coolingTime : " + tempTime);
if (tempTime <= 0.01f)
{
isSkill = false;
isCooling = false;
// 移除每帧回调函数
GameController.getInstance().removedInterval(TimerCutting);
}
}
}
}

设计思想:当外界需要处理这个界面时,只需声明对象并调用该类的有参构造函数,即可默认进行一系列初始化动作。此代码采用事件监听机制替代了自带的OnClick()事件。

(2)每帧运算的实现
/* 游戏主循环 */
private static GameController m_instance;
List<Action<float>> intervalCallFuns;

public void mainLoop()
{
interval(Time.deltaTime);
}

public void interval(float t)
{
List<Action<float>> tempIntervalCallFuns = intervalCallFuns;
foreach (Action<float> intervalCallFun in tempIntervalCallFuns)
{
intervalCallFun(t);
}
tempIntervalCallFuns = null;
}

/* 添加每帧回调
* @param: 需要回调的函数
*/
public void addInterval(Action<float> callFun)
{
if (!intervalCallFuns.Contains(callFun))
{
intervalCallFuns.Add(callFun);
}
else
{
Debug.LogWarning("add interval already exists!");
}
}

/* 移除每帧回调函数
* @param: 需要移除的回调函数
*/
public void removedInterval(Action<float> callFun)
{
List<Action<float>> tempIntervalCallFuns = new List<Action<float>>();
foreach (Action<float> tempFun in intervalCallFuns)
{
if (tempFun != callFun)
tempIntervalCallFuns.Add(tempFun);
}
intervalCallFuns = tempIntervalCallFuns;
tempIntervalCallFuns = null;
}

设计思想:该类使用列表存储需要进行每帧运算的函数,默认每帧遍历列表并执行函数,同时具备从列表中移除函数的功能。

(3)启动程序,启动游戏主循环
using UnityEngine;

public class Main : MonoBehaviour
{
void Start()
{
// 可添加其他初始化操作
}

void Update()
{
// 调用游戏主循环
GameController.getInstance().mainLoop();
}
}

在需要的时候,通过new SkillsMenu_UI(gameObject.transform.FindChild("Skills").gameObject);来创建SkillsMenu_UI对象,整个程序即可完美启动。

总结

这种设计方式多用于解耦和模块划分,无需频繁绑定脚本,也无需为public对象绑定对象,所有操作都在代码中完成。

转载请注明出处。

作者信息

洞悉

洞悉

共发布了 3994 篇文章