本篇笔记是记录"游戏热更新实战案例(基于xLua)"的学习笔记。
1、Xlua的环境搭建
1)导入Xlua插件
上Github上下载Xlua插件,将Xlua解压,将Asset下的所有文件拷贝到当前项目目录Asset下。拷贝与Asset同级目录下的Tools到该该项目中一样的位置。
2)unity菜单栏 File=》BuildSetting=》playsetting =》OtherSettings=》Scripting Define Symbols 输入HOTFIX_ENABLE
搭建功能就完成了。
3)复制相关DLL文件
PS:该捕鱼项目是2017的uinity版本,我用2018打开Xlua部署环境报错,用2017则无问题,原因可能是Xlua版本没有更新,大家学习的时候注意一下版本问题。
2、使用Xlua修复游戏中存在的BUG。
1)如何通过lua文件实现热更新?
首先创建一个调用脚本,挂在物体上,代码如下。这个是固定格式,相关API不明白的话可以自行查阅。
public class HotFixScript : MonoBehaviour {
private LuaEnv luaEnv;
void Start () {
luaEnv = new LuaEnv();
luaEnv.AddLoader(MyLoader);
luaEnv.DoString("require'fish'");
}
void Update () {
}
private byte[] MyLoader(ref string filePath)
{
string absPath = @"C:\Users\Administrator\Desktop\XluaProjects\PlayerGamePackage\"+filePath+".lua.txt";
return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(absPath));
}
//在OnDestroy前调用,释放已经注册的委托
private void OnDisable()
{
luaEnv.DoString("require'fishDispose'");
}
private void OnDestroy()
{
luaEnv.Dispose();
}
}
然后在绝对路径创建lua文件。
然后在可能会出现问题的类上打上[Hotfix]标签
在需要修改的函数方法上打上[LuaCallCSharp]标签
执行到这里的时候就会调用Lua脚本。
功能需求以及解决方法的相关代码。
1.1点击宝箱领取的金币钻石太拥挤,分散一点
--1.1点击宝箱领取的金币钻石太拥挤,分散一点
--常用的变量可以定义一个局部变量去接收
--[[
Lua调用C#中的类要添加前缀CS.
C#中调用自身对象是this.XXX
但是lua中需要加上self.
此处想调用自身的金币, C#完整代码是this.gold,但是在lua中要写全,为self.gold。--]]
local UnityEngine = CS.UnityEngine
--标准格式,第一个参数是C#中打上标签的类(即你想修改的类,第二个参数是要修改的方法,第三个参数是自身)
--此处金币拥挤,我们只需查看代码,将相关间隔的相关代码数值增大即可。
xlua.hotfix(CS.Treasour,'CreatePrize',function(self)
for i=0,4,1 do
local go = UnityEngine.GameObject.Instantiate(self.gold,self.transform.position + UnityEngine.Vector3(-10+i*40,0,0),self.transform.rotation)
go.transform.SetParent(go.transform,self.cavas)
local go1 = UnityEngine.GameObject.Instantiate(self.diamands,self.transform.position + UnityEngine.Vector3(0,40,0)+ UnityEngine.Vector3(-10+i*40,0,0),self.transform.rotation)
go1.transform.SetParent(go1.transform,self.cavas)
end
end)
lua中代码将30改为40,即可完成这个初步的简单任务。
1.1玩家金币钻石不够时没有相关处理
这个问题处理起来有个小坑,C#代码中调用了私有变量,lua中直接调用会报错,需要加上相关代码,如下。
另外lua中调用自身的方法为XX.Func(self) 或者XX:Func()
--如果想访问私有变量需要加上下面这一行代码,此处bullectAudio是私有变量
xlua.private_accessible(CS.Gun)
xlua.hotfix(CS.Gun,'Attack',function(self)
if UnityEngine.Input.GetMouseButtonDown(0) then
if UnityEngine.EventSystems.EventSystem.current:IsPointerOverGameObject() then
return
end
if self.gold<1+(self.gunLevel-1)*2 or self.gold==0 then
return
end
self.bullectAudio.clip = self.bullectAudios[self.gunLevel-1]
--self.bullectAudio.Play(self) 两种调用自身方法的形式
self.bullectAudio:Play()
if self.Butterfly then
UnityEngine.GameObject.Instantiate(self.Bullects[self.gunLevel-1],self.attackPos.position,self.attackPos.rotation*UnityEngine.Quaternion.Euler(0,0,20))
UnityEngine.GameObject.Instantiate(self.Bullects[self.gunLevel-1],self.attackPos.position,self.attackPos.rotation*UnityEngine.Quaternion.Euler(0,0,-20))
end
UnityEngine.GameObject.Instantiate(self.Bullects[self.gunLevel-1],self.attackPos.position,self.attackPos.rotation)
if not self.canShootForFree then
self:GoldChange(-1-(self.gunLevel-1)*2)
end
self.attackCD =0
self.attack = false
end
end)
–1.2 技能扣除钻石太多
解决方法,一样查看相关代码,然后修改
发现代码写在Start中,这里要注意,我们调用luaEnv的脚本要写在Awake中,补丁需要在代码前执行。写在Start顺序可能会出问题从而导致无效。
xlua.private_accessible(CS.Fire)
xlua.hotfix(CS.Fire,'Start',function(self)
self.reduceDiamands = 8
end)
–1.2boss撞击玩家数值变动一样且不是减少是增加
依旧和之前一样,找到相关代码,用lua修改
但这次使用更简便的方法,不需要重写全部,而是在执行完之后再执行我们的lua代码
需要注意,util的这个文件要和当前的lua脚本在同级目录下,这个文件可以下xlua文件下种搜索到。复制过来粘贴即可。注销委托的时候格式与之前一样,依旧是xlua.hotfix(CS.Boss,‘Start’,nil)
local util = require 'util'
xlua.private_accessible(CS.Boss)
util.hotfix_ex(CS.Boss,'Start',function(self)
self:Start()
self.m_reduceGold = self.m_reduceGold-10;
end)
–1.3 boss撞击玩家当钻石金币不够时产生负数
这个与之前有点区别,需要传参,改动方法如下
util.hotfix_ex(CS.Gun,'GoldChange',function(self,number)
self:GoldChange(number)
if self.gold <-number then
self.gold = 0
return
end
end)
–1.4捕鱼概率修改
捕鱼代码中用到了GetComponent方法,但是lua中不支持泛型,好在C#中也支持参数为string的GetComponent方法,穿的参数改为string类型即可。
itemGo:GetComponent('Gold').bossPrize = true
–1.4游戏操作方式更改
无新的知识点,lua重写代码即可。
–2.0新增海浪
这个比较麻烦,首先需要一个预制体,其次这个预制体上还要有脚本代码。解决方法,先创建一个空的脚本,里面有几个常用的空方法,挂在预制体上,然后打包成AssetBundle文件上传至服务器,加载的时候可以先从服务器下载到本地指定位置,然后再读取。
//空的C#脚本,留给lua脚本实现
[Hotfix]
public class HotFixEmpty : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
private void BehaviourMethod()
{
}
private void OnTriggerEnter(Collider other)
{
}
}
--2.0新增海浪
--lua脚本实现功能。
xlua.private_accessible(CS.HotFixEmpty)
xlua.hotfix(CS.HotFixEmpty,'Start',function(self)
self:Invoke('BehaviourMethod',8)
end)
xlua.hotfix(CS.HotFixEmpty,'Update',function(self)
self.transform:Translate(-self.transform.right*4*UnityEngine.Time.deltaTime,UnityEngine.Space.World)
end)
xlua.hotfix(CS.HotFixEmpty,'OnTriggerEnter',function(self,other)
if other.tag~='Untagged' and other.tag~='Wall' then
UnityEngine.Object.Destroy(other.gameObject)
end
end)
xlua.hotfix(CS.HotFixEmpty,'BehaviourMethod',function(self)
CS.Gun.Instance.level = CS.Gun.Instance.level + 1
if CS.Gun.Instance.level == 4 then
CS.Gun.Instance.level = 1
end
canCreateNewFish = true
CS.Gun.Instance.changeAudio = true
UnityEngine.Object.Destroy(self.gameObject)
end)
总结:使用Xlua时需要注意的坑。
1、普通成员方法与静态成员方法调用的区别。
静态成员方法直接XX.Function()即可。有参数直接写在括号里 XX.Function(a,b…)
普通成员方法调用格式为 XX.Function(self)有参数写在self后面 XX.Function(self,a,b)
或者XX:Function()有参数直接写在括号里XX:Function(a,b)
2、lua中不存在new,如果C#脚本中存在new,直接无视即可。
3、lua中不支持泛型,如果C#中用到了泛型,请替代为string参数,或者单独写一个方法获取值,lua调用方法获得返回值
4、过于复杂的代码建议C#实现,lua直接调用即可,虽然lua也能实现,但是比较麻烦且性能消耗不如C#,例如
[LuaCallCSharp]
public void LoadResource(string resName,string filePath)
{
StartCoroutine(LoadResourceCorotine(resName, filePath));
}
IEnumerator LoadResourceCorotine(string resName, string filePath)
{
UnityWebRequest request= UnityWebRequest.GetAssetBundle(@"http://localhost/AssetBundles/" + filePath);
yield return request.SendWebRequest();
//AssetBundle ab = AssetBundle.LoadFromFile(@"C:\Users\Administrator\Desktop\XluaProjects\FishingJoy\AssetBundles\" + filePath);
AssetBundle ab =(request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
GameObject gameObject = ab.LoadAsset<GameObject>(resName);
prefabDict.Add(resName, gameObject);
}
xlua.hotfix(CS.CreateFish,'Start',function(self)
self.hotFixScript:LoadResource('level3fish3','gameobject\\enemy.ab')
self.hotFixScript:LoadResource('SeaWave','gameobject\\wave.ab')
end)
这里lua调用起来就很方便。
5、lua主要是用来修复紧急BUG,不建议做大改动,例如2.0版本开发的海浪功能,先把资源打包好上传到服务器,代码全部是lua写,开发效率较低。
如果预制体上挂有脚本,本地一定要先有脚本在解压,不然AssetBundle解压出来没有脚本则会丢失,无法完成相应的功能。
资源图片较多,文件较大,想看源码的建议移步siki学院搜索热更新