《坦克大战》ulua+unity

一、定义一些简单的基本需求:

逻辑需求.png

二、一些资源等...准备工作:

[ulua](http://www.ulua.org/index.html

[模型](http://www.aigei.com/s?q=%E5%9D%A6%E5%85%8B&type=3d

三、Unity(2017)UI搭建(UGUI)

场景01.png

场景02.png

选择角色界面我用的是RawImage+RenderTexture将模型渲染到Panel
image.png

image.png

四、代码编写(这里用的是ulua提供的框架)
1、为了方便我们先把AssetBundle模式关掉(关于AssetBundle我会后面另开文章单独介绍)
image.png

image.png

2、场景管理器(场景跳转)
lua-view-新建StartPanel
image.png

image.png

3、初始化
当运行的时候加载出所有Manager
image.png

游戏初始化管理器Game Manager
image.png

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using LuaInterface;
using System.Reflection;
using System.IO;


namespace LuaFramework {
    public class GameManager : Manager {
        protected static bool initialize = false;
        private List<string> downloadFiles = new List<string>();

        /// <summary>
        /// 初始化游戏管理器
        /// </summary>
        void Awake() {
            Init();
        }

        /// <summary>
        /// 初始化
        /// </summary>
        void Init() {
            DontDestroyOnLoad(gameObject);  //防止销毁自己

            CheckExtractResource(); //释放资源
            Screen.sleepTimeout = SleepTimeout.NeverSleep;
            Application.targetFrameRate = AppConst.GameFrameRate;
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void CheckExtractResource() {
            bool isExists = Directory.Exists(Util.DataPath) &&
              Directory.Exists(Util.DataPath + "lua/") && File.Exists(Util.DataPath + "files.txt");
            if (isExists || AppConst.DebugMode) {
                StartCoroutine(OnUpdateResource());
                return;   //文件已经解压过了,自己可添加检查文件列表逻辑
            }
            StartCoroutine(OnExtractResource());    //启动释放协成 
        }

        IEnumerator OnExtractResource() {
            string dataPath = Util.DataPath;  //数据目录
            string resPath = Util.AppContentPath(); //游戏包资源目录

            if (Directory.Exists(dataPath)) Directory.Delete(dataPath, true);
            Directory.CreateDirectory(dataPath);

            string infile = resPath + "files.txt";
            string outfile = dataPath + "files.txt";
            if (File.Exists(outfile)) File.Delete(outfile);

            string message = "正在解包文件:>files.txt";
            Debug.Log(infile);
            Debug.Log(outfile);
            if (Application.platform == RuntimePlatform.Android) {
                WWW www = new WWW(infile);
                yield return www;

                if (www.isDone) {
                    File.WriteAllBytes(outfile, www.bytes);
                }
                yield return 0;
            } else File.Copy(infile, outfile, true);
            yield return new WaitForEndOfFrame();

            //释放所有文件到数据目录
            string[] files = File.ReadAllLines(outfile);
            foreach (var file in files) {
                string[] fs = file.Split('|');
                infile = resPath + fs[0];  //
                outfile = dataPath + fs[0];

                message = "正在解包文件:>" + fs[0];
                Debug.Log("正在解包文件:>" + infile);
                facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

                string dir = Path.GetDirectoryName(outfile);
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

                if (Application.platform == RuntimePlatform.Android) {
                    WWW www = new WWW(infile);
                    yield return www;

                    if (www.isDone) {
                        File.WriteAllBytes(outfile, www.bytes);
                    }
                    yield return 0;
                } else {
                    if (File.Exists(outfile)) {
                        File.Delete(outfile);
                    }
                    File.Copy(infile, outfile, true);
                }
                yield return new WaitForEndOfFrame();
            }
            message = "解包完成!!!";
            facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
            yield return new WaitForSeconds(0.1f);

            message = string.Empty;
            //释放完成,开始启动更新资源
            StartCoroutine(OnUpdateResource());
        }

        /// <summary>
        /// 启动更新下载,这里只是个思路演示,此处可启动线程下载更新
        /// </summary>
        IEnumerator OnUpdateResource() {
            if (!AppConst.UpdateMode) {
                OnResourceInited();
                yield break;
            }
            string dataPath = Util.DataPath;  //数据目录
            string url = AppConst.WebUrl;
            string message = string.Empty;
            string random = DateTime.Now.ToString("yyyymmddhhmmss");
            string listUrl = url + "files.txt?v=" + random;
            Debug.LogWarning("LoadUpdate---->>>" + listUrl);

            WWW www = new WWW(listUrl); yield return www;
            if (www.error != null) {
                OnUpdateFailed(string.Empty);
                yield break;
            }
            if (!Directory.Exists(dataPath)) {
                Directory.CreateDirectory(dataPath);
            }
            File.WriteAllBytes(dataPath + "files.txt", www.bytes);
            string filesText = www.text;
            string[] files = filesText.Split('\n');

            for (int i = 0; i < files.Length; i++) {
                if (string.IsNullOrEmpty(files[i])) continue;
                string[] keyValue = files[i].Split('|');
                string f = keyValue[0];
                string localfile = (dataPath + f).Trim();
                string path = Path.GetDirectoryName(localfile);
                if (!Directory.Exists(path)) {
                    Directory.CreateDirectory(path);
                }
                string fileUrl = url + f + "?v=" + random;
                bool canUpdate = !File.Exists(localfile);
                if (!canUpdate) {
                    string remoteMd5 = keyValue[1].Trim();
                    string localMd5 = Util.md5file(localfile);
                    canUpdate = !remoteMd5.Equals(localMd5);
                    if (canUpdate) File.Delete(localfile);
                }
                if (canUpdate) {   //本地缺少文件
                    Debug.Log(fileUrl);
                    message = "downloading>>" + fileUrl;
                    facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
                    /*
                    www = new WWW(fileUrl); yield return www;
                    if (www.error != null) {
                        OnUpdateFailed(path);   //
                        yield break;
                    }
                    File.WriteAllBytes(localfile, www.bytes);
                     */
                    //这里都是资源文件,用线程下载
                    BeginDownload(fileUrl, localfile);
                    while (!(IsDownOK(localfile))) { yield return new WaitForEndOfFrame(); }
                }
            }
            yield return new WaitForEndOfFrame();

            message = "更新完成!!";
            facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

            OnResourceInited();
        }

        void OnUpdateFailed(string file) {
            string message = "更新失败!>" + file;
            facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
        }

        /// <summary>
        /// 是否下载完成
        /// </summary>
        bool IsDownOK(string file) {
            return downloadFiles.Contains(file);
        }

        /// <summary>
        /// 线程下载
        /// </summary>
        void BeginDownload(string url, string file) {     //线程下载
            object[] param = new object[2] { url, file };

            ThreadEvent ev = new ThreadEvent();
            ev.Key = NotiConst.UPDATE_DOWNLOAD;
            ev.evParams.AddRange(param);
            ThreadManager.AddEvent(ev, OnThreadCompleted);   //线程下载
        }

        /// <summary>
        /// 线程完成
        /// </summary>
        /// <param name="data"></param>
        void OnThreadCompleted(NotiData data) {
            switch (data.evName) {
                case NotiConst.UPDATE_EXTRACT:  //解压一个完成
                //
                break;
                case NotiConst.UPDATE_DOWNLOAD: //下载一个完成
                downloadFiles.Add(data.evParam.ToString());
                break;
            }
        }

        /// <summary>
        /// 资源初始化结束
        /// </summary>
        public void OnResourceInited() {
#if ASYNC_MODE
            ResManager.Initialize(AppConst.AssetDir, delegate() {
                Debug.Log("Initialize OK!!!");
                this.OnInitialize();
            });
#else
            ResManager.Initialize();
            this.OnInitialize();
#endif
        }

        void OnInitialize() {
            LuaManager.InitStart();
            LuaManager.DoFile("Logic/Game");         //加载游戏
            LuaManager.DoFile("Logic/Network");      //加载网络
            NetManager.OnInit();                     //初始化网络
            Util.CallMethod("Game", "OnInitOK");     //初始化完成

            initialize = true;

            //类对象池测试
            var classObjPool = ObjPoolManager.CreatePool<TestObjectClass>(OnPoolGetElement, OnPoolPushElement);
            //方法1
            //objPool.Release(new TestObjectClass("abcd", 100, 200f));
            //var testObj1 = objPool.Get();

            //方法2
            ObjPoolManager.Release<TestObjectClass>(new TestObjectClass("abcd", 100, 200f));
            var testObj1 = ObjPoolManager.Get<TestObjectClass>();

            Debugger.Log("TestObjectClass--->>>" + testObj1.ToString());

            //游戏对象池测试
            var prefab = Resources.Load("TestGameObjectPrefab", typeof(GameObject)) as GameObject;
            var gameObjPool = ObjPoolManager.CreatePool("TestGameObject", 5, 10, prefab);

            var gameObj = Instantiate(prefab) as GameObject;
            gameObj.name = "TestGameObject_01";
            gameObj.transform.localScale = Vector3.one;
            gameObj.transform.localPosition = Vector3.zero;

            ObjPoolManager.Release("TestGameObject", gameObj);
            var backObj = ObjPoolManager.Get("TestGameObject");
            backObj.transform.SetParent(null);

            Debug.Log("TestGameObject--->>>" + backObj);
        }

        /// <summary>
        /// 当从池子里面获取时
        /// </summary>
        /// <param name="obj"></param>
        void OnPoolGetElement(TestObjectClass obj) {
            Debug.Log("OnPoolGetElement--->>>" + obj);
        }

        /// <summary>
        /// 当放回池子里面时
        /// </summary>
        /// <param name="obj"></param>
        void OnPoolPushElement(TestObjectClass obj) {
            Debug.Log("OnPoolPushElement--->>>" + obj);
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        void OnDestroy() {
            if (NetManager != null) {
                NetManager.Unload();
            }
            if (LuaManager != null) {
                LuaManager.Close();
            }
            Debug.Log("~GameManager was destroyed");
        }
    }
}

这里是lua层的初始化 ↓


image.png

加载获取所有view文件夹文件:


image.png
image.png

image.png

新建并注册一个ctrl:


image.png

加载构造:
image.png
image.png

image.png

指定加载面板:


image.png
StartCtrl = {}

local this = StartCtrl

function StartCtrl.New()
    return this
end

function StartCtrl.Awake()
    panelMgr:CreatePanel('Start', InitEnd);
end

function InitEnd()
    log("开始面板创建成功")
end

对应的调用方法在c#PanelManager中的CreatePanel方法

image.png

四、Assetbundle打包登录场景加载
image.png

AddBuildMap("Start" + AppConst.ExtName, "*.prefab", "Assets/Resources/Panels/Starpanel");
image.png

运行测试下:


ezgif.com-video-to-gif.gif

五、业务层逻辑的处理(实现场景跳转)
先说一个简单的跳转处理(这里我们需要写自己的需求所以自己写一个管理器)

image.png

这里用到了LuaBehaviour中的一些回调

using UnityEngine;
using LuaInterface;
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine.UI;

namespace LuaFramework {
    public class LuaBehaviour : View {
        private string data = null;
        private Dictionary<string, LuaFunction> buttons = new Dictionary<string, LuaFunction>();

        protected void Awake() {
            Util.CallMethod(name, "Awake", gameObject);
        }

        protected void Start() {
            Util.CallMethod(name, "Start");
        }

        protected void OnClick() {
            Util.CallMethod(name, "OnClick");
        }

        protected void OnClickEvent(GameObject go) {
            Util.CallMethod(name, "OnClick", go);
        }

        /// <summary>
        /// 添加单击事件
        /// </summary>
        public void AddClick(GameObject go, LuaFunction luafunc) {
            if (go == null || luafunc == null) return;
            buttons.Add(go.name, luafunc);
            go.GetComponent<Button>().onClick.AddListener(
                delegate() {
                    luafunc.Call(go);
                }
            );
        }

        /// <summary>
        /// 删除单击事件
        /// </summary>
        /// <param name="go"></param>
        public void RemoveClick(GameObject go) {
            if (go == null) return;
            LuaFunction luafunc = null;
            if (buttons.TryGetValue(go.name, out luafunc)) {
                luafunc.Dispose();
                luafunc = null;
                buttons.Remove(go.name);
            }
        }

        /// <summary>
        /// 清除单击事件
        /// </summary>
        public void ClearClick() {
            foreach (var de in buttons) {
                if (de.Value != null) {
                    de.Value.Dispose();
                }
            }
            buttons.Clear();
        }

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

StartCtrl (加载面板)

StartCtrl = {}

local this = StartCtrl

function StartCtrl.New()
    logWarn("StartCtrl.New--->>");
    return this
end

function StartCtrl.Awake()
    logWarn("StartCtrl.Awake--->>");
    panelMgr:CreatePanel('Start', InitEnd);
end

function InitEnd(go)
    log("开始面板创建成功")
    behaviour = go:GetComponent("LuaBehaviour")
    --(按钮实例,回调函数)
    behaviour:AddClick(StartPanel.startBtn,OnStartBtnClick)
    behaviour:AddClick(StartPanel.exitBtn,OnExitButtonClick)
end

function OnStartBtnClick()
    log("开始按钮被点击了")
end
function OnExitButtonClick()
    log("退出按钮被点击了")
end

StartPanel (获取组件)

--处理业务逻辑
StartPanel = { }

local this = StartPanel

function StartPanel.Awake(go)

    gameObject = go
    transform = go.transform
    this.init()
end

function StartPanel.init()
    
    --获取组件
    this.startBtn = transform:Find("StartButton").gameObject
    this.exitBtn = transform:Find("ExitButton").gameObject
    
end

六、场景管理器(管理页面跳转)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneMgr : MonoBehaviour {

    //同步跳转
    public void LoadScene(int index)
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(index);
    }
    public void LoadScene(string index)
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(index);
    }
    //异步跳转
    public void LoadSceneAsync(int index)
    {
        StartCoroutine(LoadSceneA(index));
    }
    private IEnumerator LoadSceneA(int index)
    {
       AsyncOperation asyncOperation =  UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(index);

        yield return asyncOperation;
    }
}

image.png
image.png

image.png

image.png

lua中define:

sceneMgr = LuaHelper.GetSceneManager();

StartPanel点击事件:

function OnStartBtnClick()
    log("开始按钮被点击了")
    sceneMgr:LoadScene(1)
end

运行效果如下:


ezgif.com-video-to-gif(1).gif

//cjson(lua表转json)

#!/usr/bin/env lua

-- usage: json2lua.lua [json_file]
--
-- Eg:
-- echo '[ "testing" ]' | ./json2lua.lua
-- ./json2lua.lua test.json

local json = require "cjson"
local util = require "cjson.util"

local json_text = util.file_load(arg[1])
local t = json.decode(json_text)
print(util.serialise_value(t))

七、场景二和场景一相同:

image.png

image.png

image.png

1.首先我们要用一个空物体初始化这个场景
image.png

image.png

我们这里没有用这种方法因为需要加载面板很多话不方便管理

2.需要注意的是OnLevelLoaded
我们需要去注册一个事件它才会去通知跳转(5.4以及高版本以上是需要注册一个事件,才会在跳转场景的时候发送通知低版本相反)


image.png
using UnityEngine;
using System.Collections;
using LuaInterface;
using UnityEngine.SceneManagement;

namespace LuaFramework {
    public class LuaManager : Manager {
        private LuaState lua;
        private LuaLoader loader;
        private LuaLooper loop = null;
        LuaFunction levelLoaded;

        // Use this for initialization
        void Awake() {
            loader = new LuaLoader();
            lua = new LuaState();
            this.OpenLibs();
            lua.LuaSetTop(0);

            LuaBinder.Bind(lua);
            DelegateFactory.Init();
            LuaCoroutine.Register(lua, this);

            //注册事件
            SceneManager.sceneLoaded += OnSceneLoaded;
        }

        public void InitStart() {
            InitLuaPath();
            InitLuaBundle();
            this.lua.Start();    //启动LUAVM
            this.StartMain();
            this.StartLooper();
        }

        void StartLooper() {
            loop = gameObject.AddComponent<LuaLooper>();
            loop.luaState = lua;
        }

        //cjson 比较特殊,只new了一个table,没有注册库,这里注册一下
        protected void OpenCJson() {
            lua.LuaGetField(LuaIndexes.LUA_REGISTRYINDEX, "_LOADED");
            lua.OpenLibs(LuaDLL.luaopen_cjson);
            lua.LuaSetField(-2, "cjson");

            lua.OpenLibs(LuaDLL.luaopen_cjson_safe);
            lua.LuaSetField(-2, "cjson.safe");
        }

        void StartMain() {
            lua.DoFile("Main.lua");

            levelLoaded = lua.GetFunction("OnLevelWasLoaded");
            //main.Call();
            //main.Dispose();
            //main = null;    
        }
        //监听场景跳转事件的方法
        void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            OnLevelLoaded(scene.buildIndex);
        }
        //场景跳转回调
        void OnLevelLoaded(int level)
        {
            if (levelLoaded != null)
            {
                levelLoaded.BeginPCall();
                levelLoaded.Push(level);
                levelLoaded.PCall();
                levelLoaded.EndPCall();
            }

            if (lua != null)
            {
                lua.RefreshDelegateMap();
            }
        }

        /// <summary>
        /// 初始化加载第三方库
        /// </summary>
        void OpenLibs() {
            lua.OpenLibs(LuaDLL.luaopen_pb);      
            lua.OpenLibs(LuaDLL.luaopen_sproto_core);
            lua.OpenLibs(LuaDLL.luaopen_protobuf_c);
            lua.OpenLibs(LuaDLL.luaopen_lpeg);
            lua.OpenLibs(LuaDLL.luaopen_bit);
            lua.OpenLibs(LuaDLL.luaopen_socket_core);

            this.OpenCJson();
        }

        /// <summary>
        /// 初始化Lua代码加载路径
        /// </summary>
        void InitLuaPath() {
            if (AppConst.DebugMode) {
                string rootPath = AppConst.FrameworkRoot;
                lua.AddSearchPath(rootPath + "/Lua");
                lua.AddSearchPath(rootPath + "/ToLua/Lua");
            } else {
                lua.AddSearchPath(Util.DataPath + "lua");
            }
        }

        /// <summary>
        /// 初始化LuaBundle
        /// </summary>
        void InitLuaBundle() {
            if (loader.beZip) {
                loader.AddBundle("lua/lua.unity3d");
                loader.AddBundle("lua/lua_math.unity3d");
                loader.AddBundle("lua/lua_system.unity3d");
                loader.AddBundle("lua/lua_system_reflection.unity3d");
                loader.AddBundle("lua/lua_unityengine.unity3d");
                loader.AddBundle("lua/lua_common.unity3d");
                loader.AddBundle("lua/lua_logic.unity3d");
                loader.AddBundle("lua/lua_view.unity3d");
                loader.AddBundle("lua/lua_controller.unity3d");
                loader.AddBundle("lua/lua_misc.unity3d");

                loader.AddBundle("lua/lua_protobuf.unity3d");
                loader.AddBundle("lua/lua_3rd_cjson.unity3d");
                loader.AddBundle("lua/lua_3rd_luabitop.unity3d");
                loader.AddBundle("lua/lua_3rd_pbc.unity3d");
                loader.AddBundle("lua/lua_3rd_pblua.unity3d");
                loader.AddBundle("lua/lua_3rd_sproto.unity3d");
            }
        }

        public void DoFile(string filename) {
            lua.DoFile(filename);
        }

        // Update is called once per frame
        public object[] CallFunction(string funcName, params object[] args) {
            LuaFunction func = lua.GetFunction(funcName);
            if (func != null) {
                return func.LazyCall(args);
            }
            return null;
        }

        public void LuaGC() {
            lua.LuaGC(LuaGCOptions.LUA_GCCOLLECT);
        }

        public void Close() {
            //取消注册
            SceneManager.sceneLoaded -= OnSceneLoaded; 

            loop.Destroy();
            loop = null;

            lua.Dispose();
            lua = null;
            loader = null;
        }
    }
}

3.在lua Main函数中接受回调

--主入口函数。从这里开始lua逻辑
function Main()                 
    print("logic start")            
end

--场景切换通知
function OnLevelWasLoaded(level)
    print("回调函数执行了 --------"..level)

    if level == 1 then--选塔克场景
       --调用此面板点击事件
       panelMgr:CreatePanel("SelectTank",SelectTankCtrl.CreateTankHandler)

    end
    
    collectgarbage("collect")
    Time.timeSinceLevelLoad = 0
end



function OnApplicationQuit()
end
function SelectTankCtrl.CreateTankHandler(go)
    log("开始面板创建成功")
    behaviour = go:GetComponent("LuaBehaviour")
    --(按钮实例,回调函数)
    behaviour:AddClick(SelectTankPanel.LeftBtn,OnLeftBtnClick)
    behaviour:AddClick(SelectTankPanel.RigthBtn,OnRigthButtonClick)
    behaviour:AddClick(SelectTankPanel.EnterBtn,OnEnterButtonClick)
end
function OnLeftBtnClick()
    log("向左按钮被点击了")
end
function OnRigthButtonClick()
    log("向右按钮被点击了")
end
function OnEnterButtonClick()
    log("进入游戏按钮被点击了")
end
image.png

4.根据情况角色模型是分开打包或者打包到一起


image.png

5.SelectTankPanel 获取相机

--获取Camera
    this.Camera = transform:Find("Camera").gameObject

6.SelectTankCtrl(角色选择与加载逻辑代码)


SelectTankCtrl = {}

local this = SelectTankCtrl

local tanks = { } 

local disPlayTankIndex = 1


function SelectTankCtrl.New()
    logWarn("SelectTankCtrl.New--->>");
    return this
end


function SelectTankCtrl.Awake()
    
    logWarn("SelectTankCtrl.Awake--->>");
    --panelMgr:CreatePanel('SelectTank', this.InitEnd);
    panelMgr:CreatePanel("SelectTank",SelectTankCtrl.CreateTankHandler)
    
end


function SelectTankCtrl.CreateTankHandler(go)
    
    log("开始面板创建成功")
    behaviour = go:GetComponent("LuaBehaviour")
    --(按钮实例,回调函数)
    behaviour:AddClick(SelectTankPanel.LeftBtn,OnLeftBtnClick)
    behaviour:AddClick(SelectTankPanel.RigthBtn,OnRigthButtonClick)
    behaviour:AddClick(SelectTankPanel.EnterBtn,OnEnterButtonClick)
    
    --获取加载角色
    tankNames = {"Buggy","Cube"}
    resMgr:LoadPrefab("Tanks",tankNames,this.InitTankHandler)
    
end

function SelectTankCtrl.InitTankHandler(gos)
    
    print(gos[0].name)
    print(gos[1].name)
    
    local go = newObject(gos[0]);
    --将预制体加载到相机并且固定坐标
    go.transform:SetParent(SelectTankPanel.Camera.transform,false)
    
    local go1 = newObject(gos[1]);
    go1.transform:SetParent(SelectTankPanel.Camera.transform,false)
    
    tanks[1] = go
    tanks[2] = go1
    
    for i=1, #tanks  do
        tanks[i]:SetActive(false)
    end
    
    ShowTank()
    
end

function ShowTank()
    
    tanks[disPlayTankIndex]:SetActive(true)
    
    for i = 1, #tanks do
        if i ~= disPlayTankIndex then
            tanks[i]:SetActive(false)
        end
    end
end


function OnLeftBtnClick()
    log("向左按钮被点击了")
    
    disPlayTankIndex = disPlayTankIndex - 1
    if disPlayTankIndex == 0 then
        disPlayTankIndex = #tanks
    end
    
    ShowTank()
    
end



function OnRigthButtonClick()
    log("向右按钮被点击了")
    
    disPlayTankIndex = disPlayTankIndex + 1 
    if disPlayTankIndex == #tanks + 1 then
        disPlayTankIndex = 1
    end
    
    ShowTank()
end


function OnEnterButtonClick()
    log("进入游戏按钮被点击了")
end
ezgif.com-video-to-gif.gif

八、游戏场景

[场景搭建](https://blog.csdn.net/nicolelili1/article/details/72843163

image.png

lua TankCtrl

TankCtrl = {}

local this = TankCtrl

local gameObject

local transform

function TankCtrl.GetInstance()
     return this
end

function TankCtrl.Awake(go)
    
    gameObject = go
    
    transform = go.transform
    
end
image.png

c#(挂在角色上的)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LuaFramework;

public class TankCtrl : MonoBehaviour {

    public void Awake()
    {
        Util.CallMethod("Tankctrl","Awake",gameObject);
    }
    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        
    }
}

1.角色移动


image.png

image.png

2.角色控制器

TankCtrl = {}

local this = TankCtrl

local gameObject

local transform
--角色控制器
local characterController

function TankCtrl.GetInstance()
     return this
end

function TankCtrl.Awake(go)
    
    gameObject = go
    
    transform = go.transform
    
    --characterController = go:GetComponent("CharacterController")
    characterController = gameObject:GetComponent("CharacterController")
    
end

function TankCtrl.Update()
    horizontal = UnityEngine.Input.GetAxis("Horizontal")
    vertical = UnityEngine.Input.GetAxis("Vertical")
    
    --[[if horizontal ~= 0 or vertical ~= 0 then
        dir = Vector3.New()
        dir.x = horizontal
        dir.y = -vertical
        transform:Translate(dir)
    end]]
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.W) then
        characterController:SimpleMove(-transform.up * UnityEngine.Time.deltaTime * 650)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.S) then
        characterController:SimpleMove(transform.up * UnityEngine.Time.deltaTime * 420)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.A) then
        transform:Rotate(Vector3.back * 2)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.D) then
        transform:Rotate(Vector3.forward * 2)
    end
end

c#接收

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LuaFramework;

public class TankCtrl : MonoBehaviour {

    public void Awake()
    {
        Util.CallMethod("TankCtrl", "Awake",gameObject);
    }
    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        Util.CallMethod("TankCtrl", "Update", gameObject);
    }
}

2.子弹发射


image.png

image.png

tankctrl

TankCtrl = {}

local this = TankCtrl

local gameObject

local transform

local bullet--子弹

local firPos--父物体

--角色控制器
local characterController

function TankCtrl.GetInstance()
     return this
end

function TankCtrl.Awake(go)
    
    gameObject = go
    
    transform = go.transform
    
    --characterController = go:GetComponent("CharacterController")
    characterController = gameObject:GetComponent("CharacterController")
    
    --子弹加载(返回到回调函数中)
    resMgr:LoadPrefab("Bullet",{"RedBullet"},TankCtrl.loadHandler)
    
    --firPos = child("FirePos")

    firPos = transform:Find("FirePos")
    
    print(firPos)
    
end
--返回的子弹
function TankCtrl.loadHandler(gos)
    print(gos[0])
    bullet = gos[0]
end

function TankCtrl.Update()
    horizontal = UnityEngine.Input.GetAxis("Horizontal")
    vertical = UnityEngine.Input.GetAxis("Vertical")
    
    --[[if horizontal ~= 0 or vertical ~= 0 then
        dir = Vector3.New()
        dir.x = horizontal
        dir.y = -vertical
        transform:Translate(dir)
    end]]
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.W) then
        characterController:SimpleMove(-transform.up * UnityEngine.Time.deltaTime * 650)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.S) then
        characterController:SimpleMove(transform.up * UnityEngine.Time.deltaTime * 420)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.A) then
        transform:Rotate(Vector3.back * 2)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.D) then
        transform:Rotate(Vector3.forward * 2)
    end
    
    --加载子弹(按下鼠标左键)
    if UnityEngine.Input.GetMouseButtonDown(0) then
        print(bullet)
        --获取子弹
        temp = newObject(bullet)
        --设置父物体
        temp.transform:SetParent(firPos,false)
        --temp.transform.localScale = Vector3(1,1,1)
        temp.transform.localScale = Vector3.one
        temp.transform.localPosition = Vector3(0,0,0)
        
    end
end

3.给子弹挂一个脚本

image.png

image.png

4.子弹的生成移动

BulletCtrl = {}

local this = BulletCtrl

BulletCtrl.gameObject = nil

BulletCtrl.transform = nil

--[[local gameObject = nil

local transform = nil--]]

function BulletCtrl:New(o,go)
    --print(o)
    o = o or{}
    
    o = setmetatable(o,self)
    
    self.__index = self
    
    o.gameObject = go
    
    o.transform = o.gameObject.transform
    
    return o
end 

--[[function BulletCtrl.Awake(go)
    gameObject = go
    
    transform = gameObject.transform
end--]]

function BulletCtrl:Update()
    
    self.transform:Translate(-self.transform.forward * UnityEngine.Time.deltaTime * 50)
    
end

5.坦克发射子弹调用

require "Battle/BulletCtrl"

TankCtrl = {}

local this = TankCtrl

local gameObject

local transform

local bullet--子弹

local firPos--父物体

--储存子弹的表
local bullets = { }

--角色控制器
local characterController

function TankCtrl.GetInstance()
     return this
end

function TankCtrl.Awake(go)
    
    gameObject = go
    
    transform = go.transform
    
    --characterController = go:GetComponent("CharacterController")
    characterController = gameObject:GetComponent("CharacterController")
    
    --子弹加载(返回到回调函数中)
    resMgr:LoadPrefab("Bullet",{"RedBullet"},TankCtrl.loadHandler)
    
    --firPos = child("FirePos")

    firPos = transform:Find("FirePos")
    
    print(firPos)
    
end
function TankCtrl.loadHandler(gos)
    print(gos[0])
    bullet = gos[0]
end

function TankCtrl.Update()
    horizontal = UnityEngine.Input.GetAxis("Horizontal")
    vertical = UnityEngine.Input.GetAxis("Vertical")
    
    --[[if horizontal ~= 0 or vertical ~= 0 then
        dir = Vector3.New()
        dir.x = horizontal
        dir.y = -vertical
        transform:Translate(dir)
    end]]
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.W) then
        characterController:SimpleMove(-transform.up * UnityEngine.Time.deltaTime * 250)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.S) then
        characterController:SimpleMove(transform.up * UnityEngine.Time.deltaTime * 220)
    end
    
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.A) then
        transform:Rotate(Vector3.back * 2)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.D) then
        transform:Rotate(Vector3.forward * 2)
    end
    
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.P) then
    --加载子弹(按下鼠标左键)
    --if UnityEngine.Input.GetMouseButtonDown(0) then
        print(bullet)
        --获取子弹
        temp = newObject(bullet)
        
        --把每一个发射的子弹存入战斗子弹管理类
        BattleCtrl.GetInstance().Bullets[temp] = bc
        
        --将每个子弹存储
        bc =  BulletCtrl:New(nil,temp)

        table.insert(bullets,bc)
        
        --设置父物体
        temp.transform:SetParent(firPos,false)
        temp.transform.localScale = Vector3(1,1,1)
        --temp.transform.localScale = Vector3.one
        temp.transform.localPosition = Vector3(0,0,0)
        
        temp.transform.parent = nil
        --取消设置父物体
        temp.transform.DetachChildren(firPos)
        
        
    end
    
    if table.getn(bullets) > 0 then
        for i,v in ipairs(bullets) do
            v:Update()
        end
    end
    
end

6.战斗时子弹射中敌人的管理类BattleCtrl


image.png
--模拟一个子弹类
BattleCtrl = {}

local this = BattleCtrl

BattleCtrl.Bullets = {}

function BattleCtrl.GetInstance()
    return this
end

function BattleCtrl.OnTriggerEnter(go,other)
     
    local enemyTank = go
    
    bulletCtrl = this.Bullets[other.gameObject]
    --把子弹和野怪(被击中的敌人)传递给bulletCtrl
    bulletCtrl.Attack(other.gameObject, enemyTank)
end

添加子弹碰撞管理及销毁

require "Battle/BulletCtrl"

TankCtrl = {}

local this = TankCtrl

local gameObject

local transform

local bullet--子弹

local firPos--父物体

--储存子弹的表
local bullets = { }

--角色控制器
local characterController

function TankCtrl.GetInstance()
     return this
end

function TankCtrl.Awake(go)
    
    gameObject = go
    
    transform = go.transform
    
    --characterController = go:GetComponent("CharacterController")
    characterController = gameObject:GetComponent("CharacterController")
    
    --子弹加载(返回到回调函数中)
    resMgr:LoadPrefab("Bullet",{"RedBullet"},TankCtrl.loadHandler)
    
    --firPos = child("FirePos")

    firPos = transform:Find("FirePos")
    
    print(firPos)
    
end
function TankCtrl.loadHandler(gos)
    print(gos[0])
    bullet = gos[0]
end

function TankCtrl.Update()
    horizontal = UnityEngine.Input.GetAxis("Horizontal")
    vertical = UnityEngine.Input.GetAxis("Vertical")
    
    --[[if horizontal ~= 0 or vertical ~= 0 then
        dir = Vector3.New()
        dir.x = horizontal
        dir.y = -vertical
        transform:Translate(dir)
    end]]
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.W) then
        characterController:SimpleMove(-transform.up * UnityEngine.Time.deltaTime * 250)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.S) then
        characterController:SimpleMove(transform.up * UnityEngine.Time.deltaTime * 220)
    end
    
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.A) then
        transform:Rotate(Vector3.back * 2)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.D) then
        transform:Rotate(Vector3.forward * 2)
    end
    
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.P) then
    --加载子弹(按下鼠标左键)
    --if UnityEngine.Input.GetMouseButtonDown(0) then
        print(bullet)
        --获取子弹
        temp = newObject(bullet)
        
        --把每一个发射的子弹存入战斗子弹管理类
        BattleCtrl.GetInstance().Bullets[temp] = bc
        
        --将每个发射的子弹单个实例
        bc =  BulletCtrl:New(nil,temp)

        table.insert(bullets,bc)
        
        --设置父物体
        temp.transform:SetParent(firPos,false)
        temp.transform.localScale = Vector3(1,1,1)
        --temp.transform.localScale = Vector3.one
        temp.transform.localPosition = Vector3(0,0,0)
        
        temp.transform.parent = nil
        --取消设置父物体
        temp.transform.DetachChildren(firPos)
        
        
    end
    
    if table.getn(bullets) > 0 then
        for i,v in ipairs(bullets) do
            v:Update()
        end
    end
    
end

7.挂在所有敌人身上用于反馈自身及撞击子弹


image.png

using LuaFramework;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyTankCtrl : MonoBehaviour {

    //tank检测到碰撞
    private void OnTriggerEnter(Collider other)
    {
        //调用乱函数获取射击的子弹以及被击中的坦克
        Util.CallMethod("BattleCtrl", "OnTriggerEnter", new object[] { this.gameObject, other });

        //将other传给lua

    }
}

8.子弹挂的脚本


image.png
using LuaFramework;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletCtrl : MonoBehaviour {

    public void Awake()
    {
        Util.CallMethod("BulletCtrl", "Awake", gameObject);
    }
    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    //void Update()
    //{
    //    //Util.CallMethod("BulletCtrl", "Update", gameObject);
    //    //if (gameObject != null)
    //    //{
    //    //    Destroy(gameObject,5);
    //    //}

    //}
}

BulletCtrl = {}

local this = BulletCtrl

BulletCtrl.gameObject = nil

BulletCtrl.transform = nil

--[[local gameObject = nil

local transform = nil--]]

function BulletCtrl:New(o,go)
    --print(o)
    o = o or{}
    
    o = setmetatable(o,self)
    
    self.__index = self
    
    o.gameObject = go
    
    o.transform = o.gameObject.transform
    
    return o
end 

--[[function BulletCtrl.Awake(go)
    gameObject = go
    
    transform = gameObject.transform
end--]]

function BulletCtrl:Update()
    
    self.transform:Translate(-self.transform.forward * UnityEngine.Time.deltaTime * 50)
    
end

--[[--碰撞完处理攻击业务
function BulletCtrl.Attack(bullet,enemy)
    --bullet.self = nil
    destroy(bullet)
    destroy(enemy)
end--]]
--模拟一个子弹类
BattleCtrl = {}

local this = BattleCtrl

BattleCtrl.Bullets = {}

function BattleCtrl.GetInstance()
    return this
end

function BattleCtrl.OnTriggerEnter(...)
    local res = {...}
     --tanke
    local enemyTank = res[1].gameObject
    print("我是坦克.................."..enemyTank.name)
    --zidan
    local bullet = res[2].gameObject
    print("我是子弹.................."..bullet.name)
    
    
    destroy(bullet)
    --print("我是子弹.................."..bullet.name)
    destroy(enemyTank)
    --print("我是坦克.................."..enemyTank.name)
    
    
    
    --[[bulletCtrl = this.Bullets[bullet]
    
    
    
        --把子弹和野怪(被击中的敌人)传递给bulletCtrl
        bulletCtrl.Attack(bullet, enemyTank)--]]
    
    
    
end

9.坦克管理类

require "Battle/BulletCtrl"

TankCtrl = {}

local this = TankCtrl

local gameObject

local transform

local bullet--子弹

local firPos--父物体

--储存子弹的表
local bullets = { }

--角色控制器
local characterController

function TankCtrl.GetInstance()
     return this
end

function TankCtrl.Awake(go)
    
    gameObject = go
    
    transform = go.transform
    
    characterController = gameObject:GetComponent("CharacterController")
    
    --子弹加载(返回到回调函数中)
    resMgr:LoadPrefab("Bullet",{"bc"},TankCtrl.loadHandler)

    firPos = transform:Find("FirePos")
    
    print(firPos)
    
end


function TankCtrl.loadHandler(gos)
    print(gos[0])
    bullet = gos[0]
end

function TankCtrl.Update()
    horizontal = UnityEngine.Input.GetAxis("Horizontal")
    vertical = UnityEngine.Input.GetAxis("Vertical")
    
    --[[if horizontal ~= 0 or vertical ~= 0 then
        dir = Vector3.New()
        dir.x = horizontal
        dir.y = -vertical
        transform:Translate(dir)
    end]]
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.W) then
        characterController:SimpleMove(-transform.up * UnityEngine.Time.deltaTime * 250)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.S) then
        characterController:SimpleMove(transform.up * UnityEngine.Time.deltaTime * 220)
    end
    
    if UnityEngine.Input.GetKey(UnityEngine.KeyCode.A) then
        transform:Rotate(Vector3.back * 2)
    elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.D) then
        transform:Rotate(Vector3.forward * 2)
    end
    
    --if UnityEngine.Input.GetKey(UnityEngine.KeyCode.P) then
    --加载子弹(按下鼠标左键)
    if UnityEngine.Input.GetMouseButtonDown(0) then
        print(bullet)
        --获取子弹
        temp = newObject(bullet)
        
        --把每一个发射的子弹存入战斗子弹管理类
        BattleCtrl.GetInstance().Bullets[temp] = bc
        
        --将每个发射的子弹单个实例
        bc =  BulletCtrl:New(nil,temp)

        table.insert(bullets,bc)
        
        --设置父物体
        temp.transform:SetParent(firPos,false)
        temp.transform.localScale = Vector3(1,1,1)
        --temp.transform.localScale = Vector3.one
        temp.transform.localPosition = Vector3(0,0,0)
        
        temp.transform.parent = nil
        --取消设置父物体
        temp.transform.DetachChildren(firPos)
        
        
    end
    
    if table.getn(bullets) > 0 then
        for i,v in ipairs(bullets) do
            v:Update()
        end
    end
    
end
ezgif.com-video-to-gif.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 148,637评论 1 318
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 63,443评论 1 266
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 99,164评论 0 218
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,075评论 0 188
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,080评论 1 266
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,365评论 1 184
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,901评论 2 283
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,649评论 0 176
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,122评论 0 223
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,734评论 2 225
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,093评论 1 236
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,548评论 2 222
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,028评论 3 216
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,765评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,291评论 0 178
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,162评论 2 239
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,293评论 2 242

推荐阅读更多精彩内容