什麼是熱更新?html
舉例來講編程
遊戲上線後,玩家下載第一個版本(70M左右或者更大),在運營的過程當中,若是須要更換UI顯示,或者修改遊戲的邏輯,這個時候,若是不使用熱更新,就須要從新打包,而後讓玩家從新下載(浪費流量和時間,體驗很差)。c#
熱更新能夠在不從新下載客戶端的狀況下,更新遊戲的內容。windows
熱更新通常應用在手機網遊上。數組
爲何C#腳本不能夠直接更新?數據結構
C#是一門編程語言,它運行以前須要進行編譯,而這個編譯的過程在移動平臺沒法完成,因此當咱們遊戲的邏輯更改,C#代碼發生改變的時候,咱們就須要從新在開發環境下編譯,而後從新打包,而後讓玩家去下載更新最新的版本。app
這個體驗差:包下載須要的時間長,並且不少資源沒有更新,也須要從新下載,浪費流量。框架
熱更新有哪些實現方式?dom
1,使用Lua腳本編寫遊戲的UI或者其餘的邏輯編程語言
Lua是一個精悍小巧的腳本語言,能夠跨平臺運行解析,並且不須要編譯的過程
2,使用C#Light
3,使用C#反射技術
什麼是AssetBundle?
Unity提供了一個資源更新技術,就是經過AssetBundle,咱們能夠經過AssetBundle更新遊戲UI,也能夠把腳本或者其餘代碼當成資源打包成AssetBundle而後更新到客戶端。
在全部的熱更新技術中都須要AssetBundle
如何利用Lua進行熱更新?
在移動端能夠編寫Lua的解析器,經過這個解析器,能夠運行最新的Lua腳本,而後咱們把控制遊戲邏輯的代碼都寫成Lua腳本。
Lua的解析技術有哪些?
1,uLua
駿擎【CP】 ulua.org
2,Nlua
unity支持Riley G nlua.org
3,UniLua
阿楠同窗
4,sLua
如何學習熱更新技術?
1,學習Lua編程
2,學習經過LuaInterface和luanet進行Lua和C#的交互通訊
3,學習使用AssetBundle進行資源更新
4,學習uLua SimpleFramework
利用us建立本身的熱更新遊戲
第 2 章 : Lua
課時2:201-Lua介紹和luaforwindows的安裝 07:36
Lua 是一個小巧的腳本語言。是巴西里約熱內盧天主教大學(Pontifical Catholic University of Rio de Janeiro)裏的一個研究小組,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所組成並於1993年開發。 其設計目的是爲了嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。Lua由標準C編寫而成,幾乎在全部操做系統和平臺上均可以編譯,運行。Lua並無提供強大的庫,這是由它的定位決定的。因此Lua不適合做爲開發獨立應用程序的語言。Lua 有一個同時進行的JIT項目,提供在特定平臺上的即時編譯功能。
1,Lua的官網 lua.org
2,Luaforwindows
http://luaforge.net/projects/luaforwindows/
http://luaforwindows.luaforge.net/ (可安裝的exe文件,一整套的Lua開發環境,有Lua的解釋器,參考手冊,範例和庫,文檔,和編輯器)
3,安裝Luaforwindows,關於Luaforwindows的目錄介紹
1,找到luaforwindows的安裝目錄,找到SciTE
2,打開SciTE,寫入第一行Lua代碼
print("Hello World")
3,保存代碼,保存爲HelloWorld.lua
4,按下F5運行
課時3:202-編寫第一個Lua程序HelloWorld 03:58
程序分析:
1,print()是Lua內置的方法
2,在Lua中字符串用 "" 或者 '' 均可以表示
3,Lua中每一條語句後面是沒有;號的
如何定義變量?
num = 100
這裏定義了一個全局變量叫作num,賦值爲100
在Lua中定義變量是沒有類型的,根據存儲什麼數據,來決定是什麼類型
變量的命名不能以數字開頭
儘可能避免下劃線加大寫字母開頭,這種格式Lua自身保留
推薦使用C#中的命名規範和駝峯命名
如何添加註釋?
1,單行註釋 --註釋內容
2,多行註釋 --[[ 這裏是註釋內容 ]]--
課時4:203-變量的定義 10:03
Lua變量類型以下:
1,nil表示空數據,等同於null
2,boolean 布爾類型,存儲true和false
3,string 字符串類型,字符串能夠用雙引號也可使用單引號表示
4,number小數類型(Lua中沒有整數類型)
5,table表類型
myTable = {34,,34,2,342,4}
myTable[3] --從1開始
咱們可使用type()來取得一個變量的類型
注:漢字爲兩個字節,因此刪除要按兩下
注意事項:
默認定義的變量都是全局的,定義局部變量須要在前面加一個local;
在代碼塊中聲明的局部變量,當代碼塊運行結束的時候,這個變量就會被釋放;
temp = 34
local var = 345
課時5:204-運算符和流程控制語句if語句 07:31
Lua運算符有哪些?
1,算數運算符 + - * / % (Lua中沒++ -- 這樣是運算符)
2,關係運算符 <= < > >= ==
3,邏輯運算符 and or not 分別表示 與 或 非(相似於C#中的 && || !)
if語句的三種用法
1, if [condition] then
end
2, if [condition] then
else
end
3, if [condition] then
elseif [condition]
else
end
num1=10
num2=20
num3=30
res1=num1 and num2
res2=true or num2
res3=not num3
print(res1,res2,res3)
local hp=40
if hp<=0 then
print("die")
elseif hp>=50 then
print("good")
else
print("bad")
end
課時6:205- 循環結構之while循環和repeat循環 07:27 Lua中沒有+=這個運算符
循環結構while循環
1,while語法結構
while [condition] do
end
2,輸出1到100
index=1
while index<=100 do
print(index)
index=index+1
end
3,實現1加到100
sum=0
index=1
while index<=100 do
sum=sum+index
index=index+1
end
print(sum)
4,遍歷1-100中全部的奇數的和
sum=0
index=1
while index<=100 do
if index%2==1 then
sum=sum+index
end
index=index+1
end
print(sum)
循環結構repeat循環(至關於do - while)
1,語法結構
repeat
[code to execute]
until [condition]
2,輸出1到100
index=1
repeat
print(index)
index=index+1
until index>100
3,實現1加到100
sum=0
index=1
repeat
sum=sum+index
index=index+1
until index>100
print(sum)
4,遍歷1-100中全部的奇數的和
sum=0
index=1
repeat
if index%2==1 then
sum=sum+index
end
index=index+1
until index>100
print(sum)
課時7:206- 循環結構之for循環 02:51
for循環結構
1,語法結構
for index = [start],[end] do
[code to execute]
end
2,輸出1到100
for index=1,100 do
print(index)
end
3,實現1加到100
sum=0
for index=1,100 do
sum=sum+index
end
print(sum)
4,遍歷1-100中全部的奇數的和
sum=0
for index=1,100 do
if index%2==1 then
sum=sum+index
end
end
print(sum)
break能夠終止循環 沒有continue語法
課時8:207- 函數的定義和math數學函數 05:16
函數(方法)
1,如何定義函數
function [function name](param1,param2)
[function code]
end
2,定義一個函數用來求得兩個數字的和
function Plus(num1,num2)
return num1+num2
end
標準庫(標準函數)
Lua內置提供了一些經常使用的函數幫助咱們開發
1,數學處理的math相關函數
2,字符串處理的string相關函數
3,表處理的table相關函數
4,文件操做的io相關函數
數學運算函數
math.abs
math.cos
math.max
math.maxinteger
math.min
math.random
math.sin
math.sqrt
math.tan
print(math.abs(-90))
print(math.max(12,34,56,76,43,2))
print(math.random())
課時9:208- 字符串處理 03:14
字符串處理相關函數
string.byte
string.char
string.find
sting.format
string.lower
string.sub
string.upper
.. 字符串相加
tostring() 把一個數字轉化成字符串
tonumber() 把一個字符串轉化成數字
name="kerHHHHHven"
print(string.lower(name))
print(string.sub(name,1,4))
print("http://"..name)
課時10:209- Lua中的table表 10:02
在Lua中的table相似C#中的字典,其實就是一個 key-value鍵值對的數據結構。
1,table的建立
myTable = {}
表名後面使用{}賦值,表示一個空的表
2,table的賦值
myTable[3]=34 當鍵是一個數字的時候的賦值方式
myTable["name"]="taikr" 當鍵是一個字符串的賦值方式
myTable.name = "siki"當鍵是一個字符串的賦值方式
3,table的訪問
myTable[3] 當鍵是數字的時候,只有這一種訪問方式
myTable.name 當鍵是字符串的時候有兩種訪問方式
myTable["name"]
4,table的第二種建立方式
myTable = {name="taikr",age=18,isMan = false}
(表建立以後依然能夠添加數據)
數據訪問
myTable.name
myTable["name"]
5,table的第三種方式(相似數組的使用)
myTable = {34,34,34,3,4,"sdfdsf"}
當沒有鍵的時候,編譯器會默認給每個值,添加一個數字的鍵,該鍵從1開始
課時11:210- 表相關函數,使用表實現面向對象編程 08:07
表的遍歷
表的遍歷分爲兩種
1,若是是隻有數字鍵,而且是連續的可使用下面的遍歷
for index = 1,table.getn(myTable) do
[code to execute]
end
2,全部的表均可以經過下面的方式遍歷
for index,value in pairs(myNames) do
print(index,value)
end
表相關的函數
1.table.concat
把表中全部數據連成一個字符串
2,table.insert
向指定位置插入一個數據
3,table.move
移動數據
4,table.pack
包裝成一個表
5,table.remove
移除指定位置的數據
6,table.sort
排序
7,table.unpack
返回一個數組,指定範圍的數組
經過表來實現面向對象
myTable={} 申明對象
local this = myTable聲明this關鍵字表明當前對象
--定義並聲明對象中的屬性
myTable.name="siki"
myTable.age = 110
--定義並聲明對象中的方法
myTable.function = function ()
[code to execute]
end
function myTable.function ()
[code to execute]
end
第 3 章 :
課時12:301- LuaInterface學習,在CLR(C#)中執行lua代碼 05:07(只須要引入LuaInterface.dll)
什麼是LuaInterface
LuaInterface包括兩個核心庫一個是LuaInterface.dll,一個是Luanet.dll,咱們能夠經過LuaInterface完成Lua和C#(CLR)之間的互相調用
在CLR(C#)中執行lua代碼
Lua lua = new Lua(); //建立Lua解析器
lua["num"]=2; //定義一個num
lua["str"]="a string"; //定義一個字符串
lua.newTable("tab"); //建立一個表 tab={}
取得Lua環境
double num = (double)lua["num"];
string str = (string)lua["str"];
課時13:302-在C#中執行Lua腳本文件,或者腳本字符串 13:02
lua.DoFile("script.lua");//執行script.lua腳本
lua.DoString("num=2");
lua.DoString("str='a string'");
object[] retVals = lua.DoString("return num,str");
在熱更新中,只須要寫好解析lua腳本的代碼,而後c#代碼不須要變更,只須要修改lua腳本就好,經過lua腳本控制遊戲邏輯。lua腳本和使用的C#腳本須要在同一個目錄下,既是在Debug目錄下
using System;
namespace LuaInterface
{
class Program
{
static void Main(string[] args)
{
Lua lua = new Lua();//建立Lua的解釋器
lua["num"] = 34;
Console.WriteLine(lua["num"]);
lua.DoString("num=2");
lua.DoString("str='a string'");
object[] retVals = lua.DoString("return num,str");
foreach (object obj in retVals)
{
Console.WriteLine(obj);
}
lua.DoFile("hello.lua");
Console.ReadKey();
}
}
}
課時14:303-把一個C#方法註冊進Lua的一個全局方法 07:26
Lua和C#中對應的類型
nil null
string System.String
number System.Double
boolean System.Boolean
table LuaInterface.LuaTable
function LuaInterface.LuaFunction
把一個C#方法註冊進Lua的一個全局方法
//把一個類中的普通方法註冊進去
lua.RegisterFunction("NormalMethod",obj,obj.GetType().GetMethod("NormalMethod"))
lua.DoString(" NormalMethod()");
using System;
namespace LuaInterface
{
class Program
{
static void Main(string[] args)
{
Lua lua = new Lua();//建立Lua的解釋器
#region把一個類中的普通方法註冊進去
Program p = new Program();
lua.RegisterFunction("CLRMethod", p, p.GetType().GetMethod("CLRMethod"));
lua.DoString("CLRMethod()");
#endregion
Console.ReadKey();
}
public void CLRMethod()
{
Console.WriteLine("普通c#方法");
}
}
}
// 把一個類的靜態方法註冊進去
lua.RegisterFunction("StaticMethod",null,typeof(ClassName).GetMethod("StaticMethod"))
lua.DoString(" StaticMethod()")
using System;
namespace LuaInterface
{
class Program
{
static void Main(string[] args)
{
Lua lua = new Lua();//建立Lua的解釋器
#region 把一個類的靜態方法註冊進去
lua.RegisterFunction("MyStaticMethod", null, typeof(Program).GetMethod("MyStaticMethod"));
lua.DoString("MyStaticMethod()");
#endregion
Console.ReadKey();
}
public static void MyStaticMethod()
{
Console.WriteLine("靜態方法");
}
}
}
課時15:304-在Lua中使用c#中的類 08:43
require "luanet"
--加載CLR的類型、實例化CLR對象
luanet.load_assembly("System.Windows.Forms")
luanet.load_assembly("System.Drawing")
Form = luanet.import_type("System.Windows.Forms.Form")
StartPosition = luanet.import_type("System.Windows.Forms.FormStartPosition")
print(Form)
print(StartPosition)
在Lua中使用C#中的類建立對象的時候,會自動匹配最合適的構造方法
require "luanet"
luanet.load_assembly("System")
Int32=luanet.import_type("System.Int32")
num=Int32.Parse("3435")
print(Int32)
print(num)
課時16:305-在Lua中訪問C#中的屬性和方法 03:25
Lua代碼中,訪問C#對象的屬性的方式和訪問table的鍵索引同樣,好比obj.name 或者 obj["name"]
Lua代碼中,訪問C#對象的普通函數的方式和調用table的函數同樣,好比obj:method1()
require "luanet"
luanet.load_assembly("System")
luanet.load_assembly("testLuaInterface")
Program =luanet.import_type("testLuaInterface.Program")
program1= Program()
print(program1.name)
program1:Method()
課時17:306-在Lua中訪問C#中的屬性和方法-特殊狀況-帶有out和ref關鍵字 07:01
當函數中有out或ref參數時,out參數和ref參數和函數的返回值一塊兒返回,而且調用的時候,out參數不須要傳入
C#函數定義
class Obj{
int OutMethod1(int parameter1,out parameter2,out parameter3){
parameter2=34;parameter3=213;
return parameter1;
}
int OutMethod2(int parameter1,ref parameter2){
parameter2=parameter2+2;
return parameter1+parameter2;
Lua中的調用和返回值
obj:OutMethod1(34)
--out參數不須要參數,這個返回一個table,裏面的值爲parameter1,parameter2,parameter3
(34,34,213)
obj:OutMethod2(10,10)
--ref參數須要傳入,返回一個table有兩個值(value1,value2)
require "luanet"
luanet.load_assembly("System")
luanet.load_assembly("testLuaInterface")
Program =luanet.import_type("testLuaInterface.Program")
program1= Program()
void,strLength=program1:TestOut("www.kerven.com.cn")
print(void,strLength)
void,count=program1:TestRef("www.kerven.com",20)
print(void,count)
當有重載函數的時候,調用函數會自動匹配第一個能匹配的函數
可使用get_method_bysig函數獲得C#中指定類的指定參數的函數用法
luaMethod = get_method_bysig(Obj,"CSharpMethod","System.String")
luaMethod("siki")
在Lua中註冊C#中的事件委託(event delegate)
在Lua中經過Add方法或者Remove方法把一個Lua的函數註冊或者註銷從C#中的事件委託中
function method()
end
obj.SomeEvent:Add(methodname(不用帶引號))
第 4 章 : AssetBundle
課時18:401-什麼是AssetBundle以及如何打包AssetBundle 11:04
利用AssetBundle進行簡單的打包
using System.IO;
using UnityEditor;
public class CreateAssetBundles{
[MenuItem("Assets/Build AssetBundles")]
static void BuildAllAssetBundles()
{
string dir = "AssetBundles";
if (Directory.Exists(dir) == false)
{
Directory.CreateDirectory(dir);
}
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None,
BuildTarget.StandaloneWindows64);
}
}
課時19:402-Manifest文件介紹 04:52
AssetBundles.manifest記錄裏面的資源
課時20:403-如何下載並記載AssetBundle 10:07
using System.Collections;
using UnityEngine;
public class LoadAssetBundle : MonoBehaviour {
/// <summary>
/// 第三種加載方式:www
/// </summary>
/// <returns></returns>
IEnumerator Start()
{
string path = @"file:///F:\經典學習案例,忘記了能夠回頭看的案例\siki的熱更新專題\HotUpdateProject\AssetBundles\player.unity3d";
while (Caching.ready == false)
{
yield return null;
}
WWW www = WWW.LoadFromCacheOrDownload(path, 1);
yield return www;
if (string.IsNullOrEmpty(www.error) == false)
{
Debug.Log(www.error);
yield break;
}
AssetBundle ab = www.assetBundle;
//使用裏面的資源
var wallPrefab = ab.LoadAsset<GameObject>("player");
Instantiate(wallPrefab);
}
}
第 5 章 : ulua
課時21:501-ulua介紹和uLua SimpleFramework下載 06:29
看官網的撕逼文章 http://ulua.org/index.html
課時22:502-ulua simpleframework目錄介紹 05:09
導入工程到unity,介紹目錄結構
課時23:503-案例解釋LuaState和LuaScriptMgr 13:10
using UnityEngine;
using System.Collections;
using LuaInterface;
public class HelloWorld : MonoBehaviour {
// Use this for initialization
void Start () {
LuaState l = new LuaState();
string str = "print('hello world 世界')";
l.DoString(str);
}
}
using UnityEngine;
using System.Collections;
using LuaInterface;
public class CreateGameObject01 : MonoBehaviour {
private string script = @"
luanet.load_assembly('UnityEngine')
GameObject = luanet.import_type('UnityEngine.GameObject')
ParticleSystem = luanet.import_type('UnityEngine.ParticleSystem')
local newGameObj = GameObject('NewObj')
newGameObj:AddComponent(luanet.ctype(ParticleSystem))
";
//反射調用
void Start () {
LuaState lua = new LuaState();
lua.DoString(script);
}
}
using UnityEngine;
using System.Collections;
using LuaInterface;
public class CreateGameObject02 : MonoBehaviour {
private string script = @"
luanet.load_assembly('UnityEngine')
GameObject = UnityEngine.GameObject
ParticleSystem = UnityEngine.ParticleSystem
local newGameObj = GameObject('NewObj')
newGameObj:AddComponent(ParticleSystem.GetClassType())
";
//非反射調用
void Start () {
LuaScriptMgr lua = new LuaScriptMgr();
lua.Start();
lua.DoString(script);
}
}
課時24:504-在Unity中訪問Lua中的變量 07:01
using UnityEngine;
using System.Collections;
using LuaInterface;
public class AccessingLuaVariables01 : MonoBehaviour {
private string script = @"
luanet.load_assembly('UnityEngine')
GameObject = luanet.import_type('UnityEngine.GameObject')
ParticleSystem = luanet.import_type('UnityEngine.ParticleSystem')
particles = {}
for i = 1, Objs2Spawn, 1 do
local newGameObj = GameObject('NewObj' .. tostring(i))
local ps = newGameObj:AddComponent(luanet.ctype(ParticleSystem))
ps:Stop()
table.insert(particles, ps)
end
var2read = 42
";
// Use this for initialization
void Start () {
LuaState l = new LuaState();
// Assign to global scope variables as if they're keys in a dictionary (they are really)
l["Objs2Spawn"] = 5;
l.DoString(script);
// Read from the global scope the same way
print("Read from lua: " + l["var2read"].ToString());
// Get the lua table as LuaTable object
LuaTable particles = (LuaTable)l["particles"];
// Typical foreach over values in table
foreach( ParticleSystem ps in particles.Values )
{
ps.Play();
}
}
}
using UnityEngine;
using System.Collections;
using LuaInterface;
public class AccessingLuaVariables02 : MonoBehaviour
{
//cstolua要求必需要先定義變量才能使用
private string var = @"Objs2Spawn = 0";
private string script = @"
particles = {}
ParticleSystem = luanet.import_type('UnityEngine.ParticleSystem')
for i = 1, Objs2Spawn, 1 do
local newGameObj = GameObject('NewObj' .. tostring(i))
local ps = newGameObj:AddComponent(luanet.ctype(ParticleSystem))
ps:Stop()
table.insert(particles, ps)
end
var2read = 42
";
// Use this for initialization
void Start () {
LuaScriptMgr mgr = new LuaScriptMgr();
mgr.Start();
// Assign to global scope variables as if they're keys in a dictionary (they are really)
LuaState l = mgr.lua;
l.DoString(var);
l["Objs2Spawn"] = 5;
l.DoString(script);
// Read from the global scope the same way
print("Read from lua: " + l["var2read"].ToString());
// Get the lua table as LuaTable object
LuaTable particles = (LuaTable)l["particles"];
// Typical foreach over values in table
foreach( ParticleSystem ps in particles.Values )
{
ps.Play();
}
}
}
課時25:505-執行Lua腳本文件,調用Lua方法,在Lua中使用協程 07:24
講述了第4、5、六個例子。
using UnityEngine;
using System.Collections;
using LuaInterface;
public class LuaCoroutines : MonoBehaviour
{
private string script = @"
function fib(n)
local a, b = 0, 1
while n > 0 do
a, b = b, a + b
n = n - 1
end
return a
end
function CoFunc()
print('Coroutine started')
local i = 0
for i = 0, 10, 1 do
print(fib(i))
coroutine.wait(1)
end
print('Coroutine ended')
end
function myFunc()
coroutine.start(CoFunc)
end
";
private LuaScriptMgr lua = null;
void Awake ()
{
lua = new LuaScriptMgr();
lua.Start();
lua.DoString(script);
LuaFunction f = lua.GetLuaFunction("myFunc");
f.Call();
f.Release();
}
// Update is called once per frame
void Update ()
{
lua.Update();
}
void LateUpdate()
{
lua.LateUpate();
}
void FixedUpdate()
{
lua.FixedUpdate();
}
}
課時26:506-框架啓動第一步GlobalGenerator,生成appview和gamemanager 12:43
GlobalGenerator--全局管理器
課時27:507-GameManager中對資源的更新處理 10:16
GameManager的工做流程
課時28:508-GameManager處理Lua的View的加載和初始化 11:51
課時29:509-Lua代碼中的結構和調用順序和對資源的處理和對遊戲邏輯的控制 14:19
課時30:510-建立開發UI界面 08:18
課時31:511-打包資源,建立GameManager的lua腳本 10:33
課時32:512-開發View視圖層下的Lua代碼,來獲取UI中的組件 14:31
課時33:513-開發Controller控制層下的Lua代碼,控制UI控件的產生和事件監聽 23:32
課時34:514-發佈到手機上,啓動Server,進行Lua代碼的更新 15:24
課時35:515-熱更新完結篇-遊戲資源更新和遊戲邏輯的更新