unity游戏热更新

什么是热更新?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--全局管理器

Image(17)

Image(18)

Image(19)

Image(20)

课时27:507-GameManager中对资源的更新处理 10:16

GameManager的工做流程

Image(21)

课时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-热更新完结篇-游戏资源更新和游戏逻辑的更新