LuaFramework实现一个排行榜

本文深入探讨了热更新技术在游戏开发中的应用,通过一个具体的案例,详细介绍了如何利用Lua脚本和LuaComponent组件实现资源和代码的热更新。文章涵盖了从JSON数据解析到面板动态刷新的全过程,并分享了作者在实现过程中的心得与挑战。

唉,我好菜啊,本来想自己实现一套热更的,但是毫无思路,还是一步一步来吧,先抄抄看看别人的东西怎么样吧,写了个helloworld,支持资源和代码热更。
在这里插入图片描述
读取的json数据,在公司里面应该是读取服务器下发的协议数据,应该更麻烦。
json:

{
  "nodes": [
    {
      "Name": "kk",
      "Exp": 100,
      "Job": "AK1",
      "CombatPower": 300
    },
    {
      "Name": "lisi",
      "Exp": 1000,
      "Job": "AK2",
      "CombatPower": 15
    },
    {
      "Name": "lufei",
      "Exp": 200,
      "Job": "AK3",
      "CombatPower": 150
    },
    {
      "Name": "solong",
      "Exp": 300,
      "Job": "AK4",
      "CombatPower": 2
    }
  ]
}

define.lua:
在这里插入图片描述
我添加了RankView和RankCtrl
在这里插入图片描述
这里没有操作,其实可以把后面常用的luacomponent(都是静态方法)添加进来;
functions.lua
在这里插入图片描述
添加了全局函数,方便查找子节点
注意这里用cjson来操控json的加载和序列化。
json就放到指定文件夹下就可以了,看下面代码路径。
RankCtrl:

RankCtrl = {};
local this = RankCtrl;

local luaBehaviour;
local transform;
local gameObject;
local data;
local json = require "cjson"
local util = require "3rd/cjson/util"
local isExp = false;
require "Controller/RankItem"
--构建函数--
function RankCtrl.New()
    logWarn("RankCtrl.New--->>");
    return this;
end

function RankCtrl.Awake()
    logWarn("RankCtrl.Awake--->>");
    panelMgr:CreatePanel('Rank', this.OnCreate);
end

--启动事件--
function RankCtrl.OnCreate(obj)
    gameObject = obj;

    luaBehaviour = gameObject:GetComponent('LuaBehaviour');
    logWarn("Start lua--->>"..gameObject.name);
    resMgr:LoadPrefab('rank', { 'RankItem' }, this.InitPanel);

    -- 事件注册
    luaBehaviour:AddClick(RankPanel.btnClose, this.OnClick);
    luaBehaviour:AddClick(RankPanel.btnExp,function()
        RankPanel.txtRankValue:GetComponent("Text").text = "经验值";
        this.isExp = true;
        this.Refresh();
    end);
    luaBehaviour:AddClick(RankPanel.btnCombatPower,function ()
        RankPanel.txtRankValue:GetComponent("Text").text = "战力";
        this.isExp = false;
        this.Refresh();
    end);
end

--初始化面板--
function RankCtrl.InitPanel(objs)
    ---@type table
    this.data = this.LoadData();
    table.sort(this.data.nodes,function (d1,d2)
        return d1.CombatPower<d2.CombatPower;
    end);
    local parent =RankPanel.RankItemParent;
    for i=1,#this.data.nodes do
        local go = newObject(objs[0])
        go.name = 'RankItem';
        go.transform:SetParent(parent.transform,false);
        go.transform.localScale = Vector3.one;
        go.transform.localPosition = Vector3.zero;
        local item = LuaFramework.LuaComponent.Add(go,RankItem);
        item.obj = go;
        item.rank = i;
        item.name = this.data.nodes[i]["Name"];
        item.job = this.data.nodes[i]["Job"];
        item.rankValue = this.data.nodes[i].CombatPower;
    end
end

function RankCtrl.Refresh()
    if this.isExp then
        table.sort(this.data.nodes,function (d1,d2)
            return d1.Exp<d2.Exp;
        end);
    else
        table.sort(this.data.nodes,function (d1,d2)
            return d1.CombatPower<d2.CombatPower;
        end);
    end
    ---@type UnityEngine.GameObject
    local parent =RankPanel.RankItemParent;
    -- 先删再加
    local tmp= {};
    for i = 1, parent.transform.childCount do
        tmp[i] = parent.transform:GetChild(i-1)
    end
    for i=1,#tmp do
        destroy(tmp[i].gameObject)
    end
    tmp = nil;
    resMgr:LoadPrefab('rank', { 'RankItem' }, this.CallBack);
end

function RankCtrl.CallBack(objs)
    ---@type UnityEngine.GameObject
    local parent =RankPanel.RankItemParent;
    for i=1,#this.data.nodes do
        local go = newObject(objs[0])
        go.name = 'RankItem';
        go.transform:SetParent(parent.transform,false);
        go.transform.localScale = Vector3.one;
        go.transform.localPosition = Vector3.zero;
        local item = LuaFramework.LuaComponent.Add(go,RankItem);
        item.obj = go;
        item.rank = i;
        item.name = this.data.nodes[i]["Name"];
        item.job = this.data.nodes[i]["Job"];
        if this.isExp then
            item.rankValue = this.data.nodes[i].Exp;
        else
            item.rankValue = this.data.nodes[i].CombatPower;
        end
    end
end
--cjson--
function RankCtrl.LoadData()
    local path = Util.DataPath.."lua/3rd/cjson/rank.json";
    local text =util.file_load(path);
    local t = json.decode(text)
    return t;
end

function RankCtrl.OnClick()
    destroy(gameObject)
end
--关闭事件--
function RankCtrl.Close()
    LuaFramework.Util.Log("Rank 销毁!!!")
    panelMgr:ClosePanel(CtrlNames.Rank);
end

唉。
RankView:

local transform;
local gameObject;

RankPanel = {};
local this = RankPanel;

--启动事件--
function RankPanel.Awake(obj)
    gameObject = obj;
    transform = obj.transform;
    this.InitPanel();
    logWarn("Awake lua--->>"..gameObject.name);
end

--初始化面板--
function RankPanel.InitPanel()
    this.btnClose = FindChildNode(transform,"btnClose").gameObject;
    this.txtRankValue=  FindChildNode(transform,"txtRankValue").gameObject;
    this.RankItemParent=FindChildNode(transform,"LstItemParent").gameObject;
    this.btnCombatPower = FindChildNode(transform,"btnCombatPower").gameObject;
    this.btnExp = FindChildNode(transform,"btnExp").gameObject;
end

--单击事件--
function RankPanel.OnDestroy()
    logWarn("OnDestroy---->>>");
end

RankItem:
每个Item保存自己的数据

RankItem = {
    --里面可以放一些属性
    name = "RankItem",
    rank = -1, --索引
    job = "ice",
    rankValue = 100,
    obj = nil,
}
function RankItem:Start()
    -- 设置Id
    FindChildNode(self.obj.transform,"txtRank"):GetComponent("Text").text = self.rank;
    -- 设置name
    FindChildNode(self.obj.transform,"txtName"):GetComponent("Text").text = self.name;
    -- 设置score
    FindChildNode(self.obj.transform,"txtJob"):GetComponent("Text").text = self.job;
    FindChildNode(self.obj.transform,"txtRankValue"):GetComponent("Text").text = self.rankValue;
end

--创建对象
function RankItem:New(obj)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    return o
end

还有events这个鬼东西没有用到。
用到网上他们常用的一个LuaComponent类。

/*
 * 让Lua脚本也能挂载到游戏物体上的组件
 * 
 * LuaComponent主要有Get和Add两个静态方法,其中Get相当于UnityEngine中的GetComponent方法,Add相当于AddComponent方法,
 * 只不过这里添加的是lua组件不是c#组件。每个LuaComponent拥有一个LuaTable(lua表)类型的变量table,它既引用上述的Component表。
 * Add方法使用AddComponent添加LuaComponent,调用参数中lua表的New方法,将其返回的表赋予table。
 * Get方法使用GetComponents获取游戏对象上的所有LuaComponent(一个游戏对象可能包含多个lua组件,由参数table决定需要获取哪一个),
 * 通过元表地址找到对应的LuaComponent,返回lua表
 * 
 * Add by TYQ
 */

using LuaInterface;
using UnityEngine;

namespace LuaFramework
{
    public class LuaComponent : View
    {
        //Lua表
        public LuaTable table;
        //添加LUA组件  
        public static LuaTable Add(GameObject go, LuaTable tableClass)
        {
            LuaFunction fun = tableClass.GetLuaFunction("New");
            if (fun == null)
                return null;
            //lua升级后不,Call方法不再返回对象,因此改为Invoke方法实现
            object rets = fun.Invoke<LuaTable, object>(tableClass);
            if (rets == null)
            {
                return null;
            }
            LuaComponent cmp = go.AddComponent<LuaComponent>();
            cmp.table = (LuaTable)rets;

            cmp.CallAwake();
            return cmp.table;
        }

        //添加LUA组件,允许携带额外一个参数(args)
        public static LuaTable Add(GameObject go, LuaTable tableClass, LuaTable args)
        {
            LuaFunction fun = tableClass.GetLuaFunction("New");
            if (fun == null)
                return null;

            object rets = fun.Invoke<LuaTable, object>(tableClass);
            if (rets == null)
            {
                return null;
            }
            LuaComponent cmp = go.AddComponent<LuaComponent>();
            cmp.table = (LuaTable)rets;

            cmp.CallAwake(args);
            return cmp.table;
        }

        //添加LUA组件  
        // isAllowOneComponent为true时,表示只添加一次组件,如果已存在,就不再添加
        public static LuaTable Add(GameObject go, LuaTable tableClass, bool isAllowOneComponent)
        {
            //如果已存在,则不再添加
            LuaComponent luaComponent = go.GetComponent<LuaComponent>();
            if (luaComponent != null)
            {
                return null;
            }

            LuaFunction fun = tableClass.GetLuaFunction("New");

            if (fun == null)
                return null;

            object rets = fun.Invoke<LuaTable, object>(tableClass);
            if (rets == null)
            {
                return null;
            }
            LuaComponent cmp = go.AddComponent<LuaComponent>();
            cmp.table = (LuaTable)rets;

            cmp.CallAwake();
            return cmp.table;
        }

        //获取lua组件

        public static LuaTable Get(GameObject go, LuaTable table)

        {
            LuaComponent cmp = go.GetComponent<LuaComponent>();
            string mat1 = table.ToString();
            string mat2 = cmp.table.GetMetaTable().ToString();
            if (mat1 == mat2)
            {
                return cmp.table;
            }

            return null;

        }

        //删除LUA组件的方法略,调用Destory()即可  

        //调用lua表的Awake方法
        void CallAwake()
        {

            LuaFunction fun = table.GetLuaFunction("Awake");

            if (fun != null)
                fun.Call(table, gameObject);
        }

        //调用lua表的Awake方法(携带一个参数)
        void CallAwake(LuaTable args)
        {

            LuaFunction fun = table.GetLuaFunction("Awake");
            if (fun != null)
                fun.Call(table, gameObject, args);
        }


        private void OnEnable()
        {
            // Debug.Log("================================================================================");
            //Debug.Log(table);

            if (table == null)
            {
                //Debug.LogWarning("Table is Null---------------------");
                return;
            }

            LuaFunction fun = table.GetLuaFunction("OnEnable");


            if (fun != null)
            {
                fun.Call(table, gameObject);
            }
        }

        void Start()
        {
            LuaFunction fun = table.GetLuaFunction("Start");
            if (fun != null)
                fun.Call(table, gameObject);
        }

        void Update()
        {
            //效率问题有待测试和优化
            //可在lua中调用UpdateBeat替代
            LuaFunction fun = table.GetLuaFunction("Update");

            if (fun != null)

                fun.Call(table, gameObject);
        }


        private void FixedUpdate()
        {
            LuaFunction fun = table.GetLuaFunction("FixedUpdate");

            if (fun != null)

                fun.Call(table, gameObject);
        }

        private void LateUpdate()
        {
            LuaFunction fun = table.GetLuaFunction("LateUpdate");

            if (fun != null)

                fun.Call(table, gameObject);
        }

        //更多函数略

        private void OnDisable()
        {
            if (table != null)
            {
                LuaFunction fun = table.GetLuaFunction("OnDisable");

                if (fun != null)
                {
                    fun.Call(table, gameObject);
                }
            }
        }

        private void OnDestroy()
        {
#if ASYNC_MODE
            string abName = name.ToLower().Replace("item", "");
            ResManager.UnloadAssetBundle(abName + AppConst.ExtName);
#endif
            Util.ClearMemory();
            Debug.Log("~" + name + " was destroy!");

            if (table != null)
            {
                LuaFunction fun = table.GetLuaFunction("OnDestroy");
                if (fun != null)
                {
                    fun.Call(table, gameObject);
                }
            }
        }

    }
}


其实这代码写的也不全面,他原作者的ondetory就写的很low,完全没有资源销毁之后item的ab包引用计数的问题,我这里补充了一下。
还有ctrlmanager里面ctrl全部初始化new,返回this,以及initallpanels,包括你rankitem实例化出来的名称问题这些细节要注意一下。
想看源码这里:helloworld

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JustEasyCode

谢谢您

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值