基于UGUI和xLua的一个简单的UI框架

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

以上!

你可能感兴趣的:(C#,Unity,Lua,ui,lua,开发语言)