UI框架内容是基于之前看过的siki学院的课程,这里不多重复,本篇文章主要讲如何在lua代码中调用C#的生命周期函数和UI框架的四种状态
首先我们导入xLua插件,里面有一个脚本,LuaBehaviour.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using XLua;
using System;
namespace XLuaTest
{
[System.Serializable]
public class Injection
{
public string name;
public GameObject value;
}
[LuaCallCSharp]
public class LuaBehaviour : MonoBehaviour
{
public TextAsset luaScript;
public Injection[] injections;
internal static LuaEnv luaEnv = new LuaEnv(); //all lua behaviour shared one luaenv only!
internal static float lastGCTime = 0;
internal const float GCInterval = 1;//1 second
private Action luaStart;
private Action luaUpdate;
private Action luaOnDestroy;
private LuaTable scriptEnv;
void Awake()
{
scriptEnv = luaEnv.NewTable();
// 为每个脚本设置一个独立的环境,可一定程度上防止脚本间全局变量、函数冲突
LuaTable meta = luaEnv.NewTable();
meta.Set("__index", luaEnv.Global);
scriptEnv.SetMetaTable(meta);
meta.Dispose();
scriptEnv.Set("self", this);
foreach (var injection in injections)
{
scriptEnv.Set(injection.name, injection.value);
}
luaEnv.DoString(luaScript.text, "LuaTestScript", scriptEnv);
Action luaAwake = scriptEnv.Get("awake");
scriptEnv.Get("start", out luaStart);
scriptEnv.Get("update", out luaUpdate);
scriptEnv.Get("ondestroy", out luaOnDestroy);
if (luaAwake != null)
{
luaAwake();
}
}
// Use this for initialization
void Start()
{
if (luaStart != null)
{
luaStart();
}
}
// Update is called once per frame
void Update()
{
if (luaUpdate != null)
{
luaUpdate();
}
if (Time.time - LuaBehaviour.lastGCTime > GCInterval)
{
luaEnv.Tick();
LuaBehaviour.lastGCTime = Time.time;
}
}
void OnDestroy()
{
if (luaOnDestroy != null)
{
luaOnDestroy();
}
luaOnDestroy = null;
luaUpdate = null;
luaStart = null;
scriptEnv.Dispose();
injections = null;
}
}
}
基于这个基础的脚本,我们再结合UI框架中的BasePanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BasePanel : MonoBehaviour {
//界面显示
public virtual void OnEnter() {
}
//界面暂停
public virtual void OnPause() {
}
//界面恢复使用
public virtual void OnResume() {
}
//界面关闭
public virtual void OnExit() {
}
}
我们可以写一个可供lua代码调用C#的脚本,同时支持UI框架的4种状态接口,LuaUIPanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using XLuaTest;
using System;
[LuaCallCSharp]
public class LuaUIPanel : BasePanel
{
public TextAsset luaScript;
public Injection[] injections;
internal static LuaEnv luaEnv = new LuaEnv();
internal static float lastGCTime = 0;
internal const float GCInterval = 1;
private Action luaStart;
private Action luaUpdate;
private Action luaOnDestroy;
private Action luaOnEnter;
private Action luaOnPause;
private Action luaOnResume;
private Action luaOnExit;
private Action luaOnReciviedNotice;
private LuaTable scriptEnv;
void Awake()
{
scriptEnv = luaEnv.NewTable();
LuaTable meta = luaEnv.NewTable();
meta.Set("__index", luaEnv.Global);
scriptEnv.SetMetaTable(meta);
meta.Dispose();
scriptEnv.Set("self", this);
foreach (var injection in injections)
{
scriptEnv.Set(injection.name, injection.value);
}
luaEnv.DoString(luaScript.text, "MainMenuPanel", scriptEnv);
Action luaAwake = scriptEnv.Get("Awake");
scriptEnv.Get("Start", out luaStart);
scriptEnv.Get("Update", out luaUpdate);
scriptEnv.Get("OnDestroy", out luaOnDestroy);
scriptEnv.Get("OnEnter", out luaOnEnter);
scriptEnv.Get("OnPause", out luaOnPause);
scriptEnv.Get("OnResume", out luaOnResume);
scriptEnv.Get("OnExit", out luaOnExit);
scriptEnv.Get("OnRecievedNotice", out luaOnReciviedNotice);
if (luaAwake != null)
{
luaAwake();
}
}
void Start()
{
if (luaStart != null)
{
luaStart();
}
}
void Update()
{
if (luaUpdate != null)
{
luaUpdate();
}
if (Time.time - LuaUIPanel.lastGCTime > GCInterval)
{
luaEnv.Tick();
LuaUIPanel.lastGCTime = Time.time;
}
}
void OnDestroy()
{
if (luaOnDestroy != null)
{
luaOnDestroy();
}
luaOnDestroy = null;
luaUpdate = null;
luaStart = null;
luaOnEnter = null;
luaOnPause = null;
luaOnResume = null;
luaOnExit = null;
luaOnReciviedNotice = null;
scriptEnv.Dispose();
injections = null;
}
public override void OnEnter()
{
if (luaOnEnter != null)
{
luaOnEnter();
}
}
//界面暂停
public override void OnPause()
{
if (luaOnPause != null)
{
luaOnPause();
}
}
//界面恢复使用
public override void OnResume()
{
if (luaOnResume != null)
{
luaOnResume();
}
}
//界面关闭
public override void OnExit()
{
if (luaOnExit != null)
{
luaOnExit();
}
}
//接收到通知
public void OnRecievedNotice(string _noticeStr) {
if (luaOnReciviedNotice != null)
{
luaOnReciviedNotice(_noticeStr);
}
}
}
再贴一下UIManager和GameFacade的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
using XLua;
[LuaCallCSharp]
public class UIManager {
//单例模式
static UIManager instance;
public static UIManager Instance {
get {
if (instance == null) {
instance = new UIManager();
}
return instance;
}
}
//获取Canvas的位置作为面板的父类
Transform canvasTransform;
public Transform CanvasTransform{
get {
if (canvasTransform == null) {
canvasTransform = GameObject.Find("Canvas").transform;
}
return canvasTransform;
}
}
Dictionary panelPathDict;//存放UI面板类型和路径的字典
Dictionary panelDict;//存放已经实例化的panel预设体身上的BasePanel组件
Stack panelStack;//存放实例化出来面板的栈
UIManager() {
ParseUIPanelTypeJson();
}
//面板入栈
public void PushPanel(UIPanelType uiPanelType) {
if (panelStack == null)
panelStack = new Stack();
//判断栈内是否有页面,栈顶页面暂停
if (panelStack.Count > 0) {
BasePanel topPanel = panelStack.Peek();
topPanel.OnPause();
}
//当前页面显示并入栈
BasePanel panel = GetPanel(uiPanelType);
panel.OnEnter();
panelStack.Push(panel);
}
public void LuaPushPanel(int uiPanelType) {//写个重载给lua调用
if (panelStack == null)
panelStack = new Stack();
//判断栈内是否有页面,栈顶页面暂停
if (panelStack.Count > 0)
{
BasePanel topPanel = panelStack.Peek();
topPanel.OnPause();
}
//当前页面显示并入栈
BasePanel panel = GetPanel((UIPanelType)uiPanelType);
panel.OnEnter();
panelStack.Push(panel);
}
//面板出栈
public void PopPanel()
{
if (panelStack == null)
panelStack = new Stack();
//判断栈顶元素
if (panelStack.Count <= 0) return;
BasePanel topPanel = panelStack.Pop();
topPanel.OnExit();
if (panelStack.Count <= 0) return;
BasePanel topPanel2 = panelStack.Peek();
topPanel2.OnResume();
}
//根据面板类型,得到实例化面板方法
BasePanel GetPanel(UIPanelType uiPanelType) {
if (panelDict == null) {
panelDict = new Dictionary();
}
BasePanel panel=panelDict.TryGet(uiPanelType);//使用自定义拓展TryGet方法,简化一行代码
if (panel == null)
{
string path=panelPathDict.TryGet(uiPanelType);
GameObject instPanel =GameObject.Instantiate(Resources.Load(path)) as GameObject;
instPanel.transform.SetParent(CanvasTransform,false);
panelDict.Add(uiPanelType, instPanel.GetComponent());
return instPanel.GetComponent();
}
else {
return panel;
}
}
[Serializable]//内部类,用于将Json对象转换成对象集合
class UIPanelTypeJson {
public List infolist;
}
//解析UIPanelType.json文件
void ParseUIPanelTypeJson() {
panelPathDict = new Dictionary();
TextAsset ta = Resources.Load("UIPanelType");
UIPanelTypeJson jsonObject = JsonUtility.FromJson(ta.text);
foreach (UIPanelInfo info in jsonObject.infolist)
{
panelPathDict.Add(info.panelType,info.path);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using XLua;
[LuaCallCSharp]
public class GameFacade {
private static GameFacade _Instance;
public static GameFacade Instance {
get {
if (_Instance == null) {
_Instance = new GameFacade();
}
return _Instance;
}
}
private struct NoticeData {
public string noticeStr;
public Action noticeAction;
}
private List NoticeDataList;
public void AddNoticeListener(string _noticeStr, Action _noticeAction) {//添加通知事件监听
if (NoticeDataList == null) {
NoticeDataList = new List();
}
NoticeData _data = new NoticeData();
_data.noticeStr = _noticeStr;
_data.noticeAction = _noticeAction;
NoticeDataList.Add(_data);
}
public void SendNotice(string _noticeStr) { //发送通知
for (int i = 0; i < NoticeDataList.Count; i++)
{
NoticeData _data = NoticeDataList[i];
if (!string.IsNullOrEmpty(_data.noticeStr)) {
Action noticeAction = NoticeDataList[i].noticeAction;
if (noticeAction != null) {
noticeAction(_noticeStr);
}
}
}
}
}
顺便写了一个工具脚本,可以让lua代码调用DoTween动画,DoTweenTools.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using DG.Tweening;
using System;
[LuaCallCSharp]
public class DoTweenTools {//DoTween工具类
public static void CanvasGroupDoFade(CanvasGroup target, float endValue, float duration) {
target.DOFade(endValue, duration);
}
public static Tweener TransformDoLocalMove(Transform trans, Vector3 endValue, float duration, bool snapping = false)
{
return trans.DOLocalMove(endValue, duration, snapping);
}
}
做完以上准备工作,我们就可以把C#上写好的UI代码搬运到lua上了,以下是一些示例:
MainMenuPanel.cs和MainMenuPanel.lua.txt
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class MainMenuPanel : BasePanel {
CanvasGroup canvasGroup;
void Start()
{
canvasGroup = GetComponent();
}
public override void OnPause()
{
canvasGroup.blocksRaycasts = false;//设置页面为不可交互(即鼠标点击无效)
}
public override void OnResume()
{
canvasGroup.blocksRaycasts = true;
}
public void OnPushPanel(string panelTypeString) {
UIPanelType panelType = (UIPanelType)Enum.Parse(typeof(UIPanelType),panelTypeString);
UIManager.Instance.PushPanel(panelType);
}
}
require("LuaConfig.NoticeConst")
local canvasGroup = nil
local UIPanelType = {
ItemMessage = 0,
Knapsack = 1,
MainMenu = 2,
Shop = 3,
Skill = 4,
System = 5,
Task = 6
}
local RegisterEvent = function ()
TaskButton:GetComponent("Button").onClick:AddListener(function()
OnPushPanel(UIPanelType.Task)
end)
KnapsackButton:GetComponent("Button").onClick:AddListener(function()
OnPushPanel(UIPanelType.Knapsack)
end)
BattleButton:GetComponent("Button").onClick:AddListener(function()
print("进入战斗界面")
end)
SkillButton:GetComponent("Button").onClick:AddListener(function()
OnPushPanel(UIPanelType.Skill)
end)
ShopButton:GetComponent("Button").onClick:AddListener(function()
OnPushPanel(UIPanelType.Shop)
end)
SystemButton:GetComponent("Button").onClick:AddListener(function()
OnPushPanel(UIPanelType.System)
end)
CS.GameFacade.Instance.AddNoticeListener(NoticeConst.ItemMessageClose, OnRecievedNotice)
end
function Start()
--print("MainMenuPanel start...")
canvasGroup = self:GetComponent("CanvasGroup")
RegisterEvent()
end
function OnPause()
--print("MainMenuPanel OnPause...")
canvasGroup.blocksRaycasts = false--设置页面为不可交互(即鼠标点击无效)
end
function OnResume()
--print("MainMenuPanel OnResume...")
canvasGroup.blocksRaycasts = true
end
function OnPushPanel(panelType)
--print("MainMenuPanel OnPushPanel...")
CS.UIManager.Instance.LuaPushPanel(panelType)
end
function OnRecievedNotice(str)
print("主界面接收到通知:"..str)
end
ItemMessagePanel.cs和ItemMessagePanel.lua.txt
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class ItemMessagePanel : BasePanel {
CanvasGroup canvasGroup;
void Start()
{
if (canvasGroup == null) canvasGroup = GetComponent();
}
public override void OnEnter()
{
if (canvasGroup == null) canvasGroup = GetComponent();
canvasGroup.alpha = 1;//设置透明度为不透明
canvasGroup.blocksRaycasts = true;//设置页面为可交互
//由小变大DoTween动画
transform.localScale = Vector3.zero;
transform.DOScale(1,0.5f);
}
public override void OnExit()
{
canvasGroup.blocksRaycasts = false;
transform.DOScale(0,0.5f).OnComplete(()=> canvasGroup.alpha=0);
}
public void OnClosePanel()
{
UIManager.Instance.PopPanel();
}
}
local canvasGroup = nil
local RegisterEvent = function ()
CloseButton:GetComponent("Button").onClick:AddListener(function()
OnClosePanel()
end)
end
function Start()
--print("ItemMessagePanel start...")
canvasGroup = self:GetComponent("CanvasGroup")
canvasGroup.alpha = 0--设置透明度为透明
canvasGroup.blocksRaycasts = true--设置页面为可交互
CS.DoTweenTools.CanvasGroupDoFade(canvasGroup, 1, 0.5)
RegisterEvent()
end
function OnEnter()
--print("ItemMessagePanel OnEnter...")
if not canvasGroup then
canvasGroup = self:GetComponent("CanvasGroup")
end
canvasGroup.alpha = 0--设置透明度为透明
canvasGroup.blocksRaycasts = true--设置页面为可交互
CS.DoTweenTools.CanvasGroupDoFade(canvasGroup, 1, 0.5)
end
function OnExit()
--print("ItemMessagePanel OnExit...")
canvasGroup.blocksRaycasts = false
CS.DoTweenTools.CanvasGroupDoFade(canvasGroup, 0, 0.5)
end
function OnClosePanel()
--print("ItemMessagePanel OnClosePanel...")
print("物品界面发送通知")
CS.GameFacade.Instance.SendNotice("ItemMessageClose")
CS.UIManager.Instance.PopPanel()
end
KnapsackPanel.cs和KnapsackPanel.lua.txt
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class KnapsackPanel : BasePanel {
CanvasGroup canvasGroup;
void Start()
{
if (canvasGroup == null) canvasGroup = GetComponent();
}
public override void OnEnter()
{
if (canvasGroup == null) canvasGroup = GetComponent();
canvasGroup.alpha = 1;//设置透明度为不透明
canvasGroup.blocksRaycasts = true;//设置页面为可交互
//左端滑入DoTween动画
transform.localPosition = new Vector3(600,0,0);
transform.DOLocalMove(Vector3.zero,0.5f);
}
public override void OnPause()
{
canvasGroup.blocksRaycasts = false;
}
public override void OnResume()
{
canvasGroup.blocksRaycasts = true;
}
public override void OnExit()
{
canvasGroup.blocksRaycasts = false;
//退出显示滑出动画
transform.DOLocalMove(new Vector3(600, 0, 0), 0.5f).OnComplete(()=> canvasGroup.alpha = 0);
}
//打开物品信息页面
public void OnItemButtonClick() {
UIManager.Instance.PushPanel(UIPanelType.ItemMessage);
}
public void OnClosePanel()
{
UIManager.Instance.PopPanel();
}
}
require("LuaConfig.NoticeConst")
local Vector3 = CS.UnityEngine.Vector3
local Time = CS.UnityEngine.Time
local canvasGroup = nil
local UIPanelType = {
ItemMessage = 0,
Knapsack = 1,
MainMenu = 2,
Shop = 3,
Skill = 4,
System = 5,
Task = 6
}
local timer = 0.5
local tweenBegin = false
local RegisterEvent = function ()
CloseButton:GetComponent("Button").onClick:AddListener(function()
OnClosePanel()
end)
ItemButton:GetComponent("Button").onClick:AddListener(function()
OnItemButtonClick()
end)
CS.GameFacade.Instance.AddNoticeListener(NoticeConst.ItemMessageClose, OnRecievedNotice)
end
function Start()
--print("KnapsackPanel start...")
canvasGroup = self:GetComponent("CanvasGroup")
RegisterEvent()
end
function Update()
if tweenBegin == true then
timer = timer - Time.deltaTime
if timer < 0 then
OnTweenEnd()
tweenBegin = false
end
end
end
function OnEnter()
--print("KnapsackPanel OnEnter...")
if not canvasGroup then
canvasGroup = self:GetComponent("CanvasGroup")
end
canvasGroup.alpha = 1--设置透明度为透明
canvasGroup.blocksRaycasts = true--设置页面为可交互
self.transform.localPosition = Vector3(600, 0, 0)
CS.DoTweenTools.TransformDoLocalMove(self.transform, Vector3.zero, 0.5, false)
end
function OnPause()
canvasGroup.blocksRaycasts = false
end
function OnResume()
canvasGroup.blocksRaycasts = true
end
function OnExit()
--print("KnapsackPanel OnExit...")
canvasGroup.blocksRaycasts = false
local duration = 0.5
CS.DoTweenTools.TransformDoLocalMove(self.transform, Vector3(600, 0, 0), duration, false)
timer = duration
tweenBegin = true
end
function OnTweenEnd()
--print("========================Completed")
canvasGroup.alpha = 0
end
function OnItemButtonClick()
--print("KnapsackPanel OnItemButtonClick...")
CS.UIManager.Instance.LuaPushPanel(UIPanelType.ItemMessage)
end
function OnClosePanel()
--print("KnapsackPanel OnClosePanel...")
CS.UIManager.Instance.PopPanel()
end
function OnRecievedNotice(str)
print("背包界面接收到通知:"..str)
end
以上!