码迷,mamicode.com
首页 > 编程语言 > 详细

二、unity游戏热更新专题

时间:2017-12-26 16:09:50      阅读:613      评论:0      收藏:0      [点我收藏+]

标签:事件   关于   适合   关系   round   until   adf   frame   insert   

第 1 章 : 热更新技术学习介绍

课时1:101-热更新技术学习介绍 11:55

什么是热更新?

举例来说

     游戏上线后,玩家下载第一个版本(70M左右或者更大),在运营的过程中,如果需要更换UI显示,或者修改游戏的逻辑,这个时候,如果不使用热更新,就需要重新打包,然后让玩家重新下载(浪费流量和时间,体验不好)。

     热更新可以在不重新下载客户端的情况下,更新游戏的内容。

     热更新一般应用在手机网游上。

为什么C#脚本不可以直接更新?

     C#是一门编程语言,它运行之前需要进行编译,而这个编译的过程在移动平台无法完成,所以当我们游戏的逻辑更改,C#代码发生改变的时候,我们就需要重新在开发环境下编译,然后重新打包,然后让玩家去下载更新最新的版本。

     这个体验差:包下载需要的时间长,而且很多资源没有更新,也需要重新下载,浪费流量。

热更新有哪些实现方式?

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

讲述了第四、五、六个例子。

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

二、unity游戏热更新专题

标签:事件   关于   适合   关系   round   until   adf   frame   insert   

原文地址:https://www.cnblogs.com/kerven/p/8118223.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!