最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Unity3D熱更新技術點——ToLua(下)

2018-10-08 15:35 作者:皮皮關做游戲  | 我要投稿

作者:朔宇


在上一篇文章中我們通過一個小的案例,介紹了ToLua在Unity中的基本使用方法,而這次,我們將通過一個更為復雜的例子,繼續(xù)深入了解ToLua的使用方法及其原理。

上篇傳送門:Unity3D熱更新技術點——ToLua(上)


ToLua文件目錄

我們首先來了解一下ToLua的文件目錄。

Tolua集成主要分為兩部分,一部分是運行時需要的代碼包括一些手寫的和自動生成的綁定代碼,另一部分是編輯器相關代碼,主要提供代碼生成、編譯lua文件等操作,具體就是Unity編輯器中提供的功能。

接下來我們具體介紹一下Tolua文件列表中文件的用途:

1.Editor

Editor下Custom/CustomSettings.cs 自定義配置文件,用于定義哪些類作為靜態(tài)類型、哪些類需要導出、哪些附加委托需要導出等

我們需要注冊到Lua中的類型也都需要在這里導入,在Tolua中已經(jīng)為我們提供了Unity大部分基礎類型,若我們需要導入自己的類型或Tolua沒有導入的類型可以在其中添加,如下圖所示:?



2.Source

在Source文件夾中有Generate文件夾及LuaConst.cs腳本,Generate中主要是生成用于交互的綁定代碼wrap腳本,LuaConst.cs是一些lua路徑等配置文件。 若在CustomSettings中做了修改,需要在菜單欄的Lua選項中,重新生成的Wrap文件,當重新生成Wrap文件后,會發(fā)現(xiàn)我們新添加類型也生成了相應的Wrap文件,如下圖所示:


Clear wrap files后會自動重新生成wrap文件

3.ToLua

Tolua文件夾中有如下文件

1)BaseType: 一些基礎類型的綁定代碼

2)Core: 提供的一些核心功能,包括封裝的「LuaFunction」「LuaTable」 「LuaThread」「LuaState」「LuaEvent」、調(diào)用tolua原生代碼等等。

3)Examples: Tolua示例

4)Misc: 雜項,包含LuaClient,LuaCoroutine(協(xié)程),LuaLooper(用于tick),LuaResLoader(用于加載lua文件)

5)Reflection: 反射相關

我們這里只了解一下Tolua中的文件結(jié)構(gòu)及相關文件的作用,具體的腳本綁定及生成流程我們不做過多贅述,如需要了解可以查詢相關資料,若有較多反饋,在之后我們可以開一篇新的文章,具體介紹tolua?


Tolua跳一跳

現(xiàn)在我們已經(jīng)大致了解了Tolua這個方案,接下來我們通過一個Demo,來看在Unity中,我們?nèi)绾问褂肨olua開發(fā)項目。

本文中以一個仿照微信跳一跳的小游戲作為案例來講解,案例非常簡單,但希望讀者有unity基礎,本文主要講解Tolua的用法,代碼邏輯方面的講解可能會相對偏少?



一.開發(fā)前準備

在之前,我們已經(jīng)導入了Tolua資源。在這個項目中,我們需要使用到DoTween插件,可以在Asset Store自行下載。


二.Lua虛擬機管理器

  • 我們需要用C#腳本來開啟Lua虛擬機并調(diào)用Lua模塊,那么不同的邏輯就會有不同的C#腳本來開啟虛擬機并調(diào)用Lua模塊,這無疑是很耗費性能且繁瑣的,所以我們可以自己做一些封裝,先將C#腳本中所必須的方法做一個緩存,如下代碼所示: LuaManager.cs?

public class LuaManager : MonoBehaviour {

??? private static LuaManager _instance;

??? public static LuaManager Instance{

??????? get{

??????????? return _instance;

????? ??}

??? }

??? private LuaClient _luaClient;

??? public LuaClient LuaClient

??? {

??????? get

??????? {

??????????? return _luaClient;

??????? }

??? }

??? void Awake () {

??????? _instance = this;

??????? //跨場景不銷毀

??????? DontDestroyOnLoad(this.gameObject);

??????? _luaClient = this.gameObject.AddComponent<LuaClient>();

??? }

}

?

在代碼中,我們直接使用LuaClient,LuaClient我們可以理解成是ToLua內(nèi)部對自己的一種封裝,可以視為tolua環(huán)境的一個啟動。我們需要將LuaClient中的protected LuaState luaState = null;改為public,同時我們可以在LuaClient中再封裝一個調(diào)用Lua模塊函數(shù)的方法。

public virtual void CallFunc(string func, GameObject obj)

??? {

??????? LuaFunction luaFunc = luaState.GetFunction(func);

??????? luaFunc.Call(obj);

??????? luaFunc.Dispose();

?????? ?luaFunc = null;

??? }

?

然后我們在場景中創(chuàng)建一個空物體,添加LuaManager.cs腳本

三.自建C#方法工具類

  • 在使用Tolua開發(fā)中,ToLua提供的方法有限,有時我們可能找不到很好的方法來代替C#中的功能,或者不清楚某個功能的使用方法,這時候我們可以在C#中封裝好一些功能,然后導入到Lua中,便可以直接在Lua中使用。這里可以創(chuàng)建一個C#腳本,我們命名為Util.cs,因為Lua中數(shù)值只存在number類型,如果需要我們可以封裝int和float類型

public static int Int(object o)

??? {

??????? return Convert.ToInt32(o);

??? }

public static float Float(object o)

??? {

??????? return (float)Math.Round(Convert.ToSingle(o), 2);

??? }

??

  • 同時還有我們可能需要使用到的Dotween的部分方法

? ? public static void DoMove(GameObject obj, Vector3 vec, float time)

??? {

??????? obj.transform.DOMove(vec, time);

??? }

?

??? public static void DoScale(GameObject obj, float f, float time)

??? {

??????? obj.transform.DOScale(f, time);

??? }

?

??? public static void DoScale(GameObject obj, Vector3 vec, float time)

??? {

??????? obj.transform.DOScale(vec, time);

??? }

?

之前我們介紹過,如果需要添加導入lua的類型,需要在CustomSettings.cs中添加



可以看到我們在其中加入了 _GT(typeof(Util)),然后重新生成wrap文件?

如圖所示,Source/Generate中生成了UtilWrap文件


四.開始界面

再之前的動圖中可以看到我們的項目中的開始界面,只搭建了背景及一個開始按鈕,讀者也可以自行擴展。 * 這里我們需要用到Button事件,我們同樣可以通過封裝一個C#腳本給lua提供一個按鈕事件

BtnEvent.cs?

?

public class UIEvent : MonoBehaviour {

??? public static void AddButtonOnClick(GameObject game, LuaFunction function)

??? {

??????? if (game == null)

??????????? return;

??????? Button btn = game.GetComponent<Button>();

??????? btn.onClick.AddListener(

??????????? delegate () {

??????????????? function.Call(game);

??????????? }

??????? );

??? }

}

?

接下來就是在lua中的調(diào)用了。 我們可以在Project面板中找到lua文件夾,我們可以把我們的Lua腳本文件放在這個文件夾下(當然,也可以根據(jù)自己的習慣修改Lua文件夾,但我們查找Lua文件的路徑就需要修改),文件夾下有名為Main.lua的Lua文件,在這個腳本中,我們可以定義我們所需的全局類型:


--主入口函數(shù)。從這里開始lua邏輯

--這里定義我們所需的全局類型

function Main()

??? GameObject = UnityEngine.GameObject

??? Transform = UnityEngine.Transform

??? ParticleSystem = UnityEngine.ParticleSystem

??? Color = UnityEngine.Color

??? Util = Util.New()

??? SceneManagement = UnityEngine.SceneManagement

??? Input = UnityEngine.Input

??? KeyCode = UnityEngine.KeyCode

??? Time = UnityEngine.Time

??? Camera = UnityEngine.Camera

??? AudioSource = UnityEngine.AudioSource

??? Resources = UnityEngine.Resources

??? www = UnityEngine.WWW

??? print("logic start")

end

?

--場景切換通知

function OnLevelWasLoaded(level)

??? collectgarbage("collect")

??? Time.timeSinceLevelLoad = 0

end

?

function OnApplicationQuit()

end

?

  • 然后我們創(chuàng)建Login.lua,我們的開始界面邏輯將會寫在這個腳本中:

Login = {}--定義Login類

local this = Login

require('Music')--加載Music模塊

local ui

local manager

function this.Awake(object)

??? manager = GameObject.Find('Manager')

??? manager : AddComponent(typeof(AudioSource))

??? coroutine.start(Music.PlaySound)--開啟協(xié)程

??? ui = object

??? local loginBtn = ui.transform : Find("Login").gameObject

??? UIEvent.AddButtonOnClick(loginBtn, LoginOnClick)

end

?

function LoginOnClick()

????? --場景切換

??? SceneManagement.SceneManager.LoadScene("Jump")

?

end


在這個lua腳本中,可以看出,其實Lua中調(diào)用unity的方法和C#十分相似,想必有Unity基礎的讀者很容易看明白以上的代碼。?
其中要注意的是,unity中的物體無法綁定lua腳本,所以無法通過如c#中定義public的值可以在Inspector面板進行賦值,所以代碼中我們必須通過GameObject.Find()或者Transform:Find()來找到物體?
還有一個重要的地方,需要注意我們調(diào)用類的方法、屬性、字段時「.」和「:」的區(qū)別


  • 我們希望在游戲中有背景音樂,所以在這里,我們使用協(xié)程來下載一首音樂,并掛之前創(chuàng)建的在Manager上

--協(xié)程下載

--這里使用Tolua中提供的coroutine.www

Music = {}

local this = Music

function this.PlaySound()

??? local audio = GameObject.Find('Manager') : GetComponent('AudioSource')

??? local url = www('https://etnly.oss-cn-shanghai.aliyuncs.com/%E5%B2%A1%E9%83%A8%E5%95%93%E4%B8%80%20-%20%E9%81%BA%E3%82%B5%E3%83%AC%E3%82%BF%E5%A0%B4%E6%89%80%EF%BC%8F%E6%96%9C%E5%85%89.ogg')

??? coroutine.www(url)

??? audio.clip = url : GetAudioClip()

??? audio : Play()

end

?

  • 接下來我們來看如何在C#中調(diào)用剛才寫好的lua模塊:

public class Login : MonoBehaviour {

??? void Start () {

??????? LuaManager.Instance.LuaClient.luaState.DoFile("Login.lua");

??????? LuaManager.Instance.LuaClient.CallFunc("Login.Awake", this.gameObject);

??? }

?

}

?


四.角色邏輯

開始游戲界面完成后,我們就著手于游戲主場景,首先我們可以新建場景,使用plane、cube等基礎物體,搭建一個簡單的跳一跳場景。


  • 接下來,我們用Lua實現(xiàn)角色的跳躍,這里我們可以使用剛體來讓角色實現(xiàn)向前跳躍的動作:

local player

local rigidbody

function this.Awake(obj)

??? player = obj

??? rigidbody = player : GetComponent('Rigidbody')

end

function this.StartJump(time)

??? --跳躍邏輯,這里的time可以理解為我們按下按鈕的時間

??? rigidbody : AddForce(Vector3(1, 1, 0) * time * 7,

??? UnityEngine.ForceMode.Impulse)

end

?

同樣的,首先我們要找到角色,然后獲取其中的剛體組建。?
  • 在跳一跳游戲中,我們通過某一個按鍵(這里我們使用空格鍵)讓角色開始跳躍,角色跳躍的力度是更具按下屏幕或者按鈕的時間來決定的,所以這里我們需要獲取到我們按下按鈕的時間

function this.Update()

??? if Input.GetKeyDown(KeyCode.Space) then

??????? startTime = Time.time--獲取按下空格時的時間

??? end

??? if Input.GetKeyUp(KeyCode.Space) then

??????? endTime = Time.time - startTime--計算按下空格至松開的時間

??????? this.StartJump(endTime)

??? end

end

?

然后實現(xiàn)角色在蓄力(也就是按住按鈕)時的動作,以及粒子效果,如下圖所示


  • 我們的角色是由一個圓柱體和一個球體組成,所以在蓄力時,我們需要壓縮圓柱體同時球體的位置也要下移:

if Input.GetKey(KeyCode.Space) then

??? --角色壓縮效果

??? if body.transform.localScale.y < 0.11 and body.transform.localScale.y > 0.05 then

??????? body.transform.localScale = body.transform.localScale + Vector3(1, -1, 1) * 0.05 * Time.deltaTime

??????? head.transform.localPosition = head.transform.localPosition + Vector3(0, -1, 0) * 0.05 * Time.deltaTime

??? end

end

?

在這里,body時角色的身體,head時頭部,我們都需要先找到物體然后在對其進行操作?
  • 蓄力時的粒子效果,讀者可以自行在unity中編輯,這里就不做過多贅述,相關代碼如下:

particle : GetComponent(‘ParticleSystem’) : Play()


我們應該在按下空格時就開啟粒子,所以這里應該放在判斷按下空格中?


  • 當我們松開空格時,角色需要恢復之前的大小及位置,粒子效果也需要停止,同時角色跳躍。

--DoTween恢復角色

Util.DoScale(body, 0.1, 0.5)

Util.DoLocalMoveY(head, 0.8, 0.5)

particle : GetComponent('ParticleSystem') : Stop()

?

這里我們用DoTween來恢復角色大小。?
  • 關于角色的邏輯,最后一步就是判定角色是否跳到了下一個盒子

--如果跳到此盒子,便給該盒子添加腳本,并移動攝像機

function this.OnCollisionStay(object)

??? if object.transform.tag == 'Cube' then

??????? if(object : GetComponent('BoxControl') == nil) then

??????????? this.CameraMove()

??????????? object : AddComponent(typeof(BoxControl))

??????? end

??? elseif object.transform.tag == 'Plane' then

??????? --重新開始游戲

??????? Time.timeScale = 0

??????? ui : SetActive(true)

??????? Continue.ReStart(ui)

??? end

?

end

?

function this.CameraMove()

??? --DoTween控制攝像機移動效果

??? Util.DoMove(Camera.main, (player.transform.position + cameraRelativePosition), 1)

end

?

在這里我們角色如果跳到了下一個盒子,就會給當前盒子添加一個腳本,并移動攝像機,如果沒有跳到則打開一個ui界面,并調(diào)用一個叫Continue的模塊?


Continue.lua?


Continue = {}

local this = Continue

function this.ReStart(obj)

??? local reStartBtn = obj.transform : Find("ReStart").gameObject

??? UIEvent.AddButtonOnClick(reStartBtn, ReStartOnClick)

end

?

function ReStartOnClick ()

??? SceneManagement.SceneManager.LoadScene("Jump")

end

?

最后就是角色控制的C#代碼,和之前開始界面類似,不過這里多了Update和OnCollisionStay


void Start () {????

??????? LuaManager.Instance.LuaClient.luaState.DoFile("Player.lua");

??????? LuaManager.Instance.LuaClient.CallFunc("Play.Awake", this.gameObject);

}

void Update () {

??????? LuaManager.Instance.LuaClient.CallFunc("Play.Update", gameObject);

}

private void OnCollisionStay(Collision collision)?? {???????

??????? LuaManager.Instance.LuaClient.CallFunc("Play.OnCollisionStay", collision.gameObject);

}

?

上面提到了會給跳到的盒子加入一個腳本,那么接下來我們就來看關于盒子的邏輯?


五.盒子邏輯

  • 首先,當角色跳到當前盒子上,我們就應該把這個腳本綁定到該盒子,再之前角色的邏輯中已經(jīng)有說明。我們可以在圖中可以看到,當按下空格,角色壓縮是盒子也會同時被壓縮,松開空格后盒子復原

if Input.GetKey(KeyCode.Space) then

??? --盒子壓縮效果

??? if currentBox.transform.localScale.y < 0.51 and currentBox.transform.localScale.y > 0.3 then

??????? currentBox.transform.localScale = currentBox.transform.localScale + Vector3(0, -1, 0) * 0.15 * Time.deltaTime

??????? currentBox.transform.localPosition = currentBox.transform.localPosition + Vector3(0, -1, 0) * 0.15 * Time.deltaTime

??? end

end

if Input.GetKeyUp(KeyCode.Space) then

??? --DoTween恢復盒子

??? Util.DoLocalMoveY(currentBox, 0.25, 0.2)

??? Util.DoScale(currentBox, Vector3(oldScale.x, oldScale.y, oldScale.z), 0.2)

end

?

然后當角色跳到新的盒子上,就應該根據(jù)當前位置,生成一個新的大小和顏色隨機的盒子,具體代碼邏輯如下


function this.GenerateBox()

??? boxPrefab = Resources.Load('Prefabs/Cube')

??? local newBox = GameObject.Instantiate(boxPrefab)

??? --盒子隨機位置、大小、顏色

??? randomScale = Util.Random(0.5, 1)

??? newBox.transform.position = currentBox.transform.position + Vector3(Util.Random(1.5, maxDistance), 0, 0)

??? plane.transform.localPosition = plane.transform.localPosition + Vector3(Util.Random(1.5, maxDistance), 0, 0)

??? newBox.transform.localScale = Vector3(randomScale, 0.5, randomScale)

??? newBox : GetComponent('Renderer').material.color = Color(Util.Random(0.0, 1.0), Util.Random(0.0, 1.0), Util.Random(0.0, 1.0))

end

?

最后一步,就是刪除之前的盒子,我們可以判斷盒子是否在攝像機范圍內(nèi)(在本項目中,我們攝像機范圍內(nèi)應該只有兩個盒子,當前和新生成的盒子),不在攝像機范圍內(nèi)的盒子,我們可以將其刪除


function this.Update(obj)

??? if this.IsInView(obj.transform.position) then

??????? GameObject.Destroy(obj, 1)

??? end

end

--判斷盒子是否在攝像機范圍內(nèi),如果不在,便將其銷毀

function this.IsInView(worldPos)

??? local cameraTrans = Camera.main.transform

??? local viewPos = Camera.main : WorldToViewportPoint(worldPos)

??? local dir = (worldPos - cameraTrans.position).normalized

??? local dot = Vector3.Dot(cameraTrans.forward, dir)

??? if dot > 0 and viewPos.x > 0 and viewPos.x < 1 and viewPos.y > 0 and viewPos.y < 1 then

??????? return false

??? end

??? return true

end

?

BoxControl.cs?



void Start () {??????

??????? LuaManager.Instance.LuaClient.luaState.DoFile("BoxControl.lua");

??????? LuaManager.Instance.LuaClient.CallFunc("Box.Awake", this.gameObject);

?

??? }

void Update () {

??????? LuaManager.Instance.LuaClient.CallFunc("Box.Update", this.gameObject);

??? }

?

到此為止,我們使用Tolua制作的跳一跳小游戲就完成了。項目雖小,但包含了Tolua的大部分功能的常見使用方法。

我們的項目工程會放在github中,大家可自行下載測試及擴展,也可以根據(jù)本篇文章制作其他項目來熟悉Tolua的使用。

完整工程在此 :https://github.com/Etnly/ToLuaDemo


想系統(tǒng)學習游戲開發(fā)的童鞋,歡迎訪問?http://levelpp.com/? ? ? ????

游戲開發(fā)攪基QQ群:869551769? ? ? ? ????

微信公眾號:皮皮關

Unity3D熱更新技術點——ToLua(下)的評論 (共 條)

分享到微博請遵守國家法律
裕民县| 肥城市| 浪卡子县| 阿克| 云和县| 玉溪市| 灵丘县| 大竹县| 沙田区| 巧家县| 宜兰县| 磴口县| 丰原市| 平遥县| 伊川县| 嵊泗县| 额济纳旗| 新和县| 天镇县| 环江| 华阴市| 黑河市| 台安县| 新平| 双鸭山市| 丰顺县| 翁牛特旗| 益阳市| 岑溪市| 昌乐县| 西充县| 宜阳县| 界首市| 翁牛特旗| 康乐县| 衡东县| 马尔康县| 伊宁县| 克拉玛依市| 公安县| 鹤山市|