本课程主要是基于一个捕鱼达人使用xLua进行热更新。
目录
lua虚拟环境的搭建
创建一个HotFixScript脚本进行lua虚拟环境的搭建
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System.IO;
public class HotFixScript : MonoBehaviour
{
private LuaEnv luaEnv;
// Start is called before the first frame update
void Start()
{
luaEnv = new LuaEnv();
luaEnv.AddLoader(MyLoader);
luaEnv.DoString("require 'fish'");
}
// Update is called once per frame
void Update()
{
}
private byte[] MyLoader(ref string filePath)
{
string absPath = @"D:\u3dworkspace\FishingJoy\Assets\PlayGamePackage\" + filePath + ".lua.txt";
return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(absPath));
}
private void OnDestroy()
{
luaEnv.Dispose();
}
}
需要明确的补丁开发过程
开发过程:
首先开发业务代码->在所有可能出现问题的类上打上hotfix的标签,在所有lua调用CSharp的方法上打上LuaCallCSharp,在所有CSharp调用Lua的方法上打上CSharpCallLua->打包发布->修改bug时只需要更新Lua文件,修改资源时(声音,模型,贴图,图片,UI)只需要更新ab包。用户只需要去下载lua文件跟ab包。
后续版本更新的内容
1.1
1.点击宝箱领取的金币钻石太拥挤,分散一点。
2.玩家金币钻石不够时没有相应处理。
1.2
1.与UI交互时不能发射子弹。
2.技能扣钻石太多。
3.boss撞击玩家数值变动一样且不是减少是增加。
1.3
1.boss撞击玩家当钻石金币不够时会产生负数。
2.炮台3太强,且钻石没用处,不削弱,只有氪金才可使用。
3.大鱼太多。
1.4
1.扑鱼是考虑了鱼的血量与子弹的伤害来模拟概率,这样玩家体验不好,要使用传统的概率来扑鱼。
2.炮台移动是根据鼠标的水平数值滑动来模拟跟随的,改为玩家按下ad键来旋转炮台。
2.0
1.增加新鱼。
2.增加浪潮功能。
3.切换场景。
修复1.1版本金币拥挤bug的补丁开发
在需要热更新的类前加标签
在需要热更新的方法前加LuaCallCSharp
主要修改fish.lua文件,我们用LuaCallCSharp
--1.1 点击宝箱领取的金币钻石太拥挤,分散一点。
local UnityEngine = CS.UnityEngine
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*30,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,30,0)+ UnityEngine.Vector3(-10+i*30,0,0),self.transform.rotation)
go1.transform.SetParent(go1.transform,self.cavas)
end
end)
解决LuaEnv释放时的报错
新建一个fishDispose.lua
xlua.hotfix(CS.Treasour,'CreatePrize',nil)
在luaEnv.Dispose前调用这个方法就可以解决报错
private void nDisable()
{
luaEnv.DoString("require 'fishDispose'");
}
1.1版本金币钻石不够的处理补丁
先将Gun方法用Lua实现
--1.1 2.玩家金币钻石不够时没有相应处理。
xlua.hotfix(CS.Gun,'Attack',function(self)
if UnityEngine.Input.GetMouseButtonDown(0) then
self.bullectAudio.clip = self.bullectAudios[self.gunLevel-1]
self.bullectAudio:Play()
if self.Butterfly then
UnityEngine.GameObject.Instantiate(self.Bullects[self.gunLevel-1],self.attackPos.position,self.rotataion*UnityEngine.Quaternion.Euler(0,0,20))
UnityEngine.GameObject.Instantiate(self.Bullects[self.gunLevel-1],self.attackPos.position,self.rotataion*UnityEngine.Quaternion.Euler(0,0,-20))
end
UnityEngine.GameObject.Instiate(self.Bullects[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)
金币钻石不够处理补丁的完善
添加一个if语句
if self.gold<1+(self.gunLevel-1)*2 or gold==0 then
return
end
1.2版本与UI交互时不能发射子弹的补丁开发
--1.2 与UI交互时不能发射子弹
if UnityEngine.EventSystems.EventSystem.current:IsPointerOverGameObject() then
return
end
1.2版本技能扣钻石太多的数值修改补丁开发
首先因为修改的参数在Start函数里,所以先把HotFixScript里面的DoString方法写在Awake里面,以免造成没有调用
void Awake()
{
luaEnv = new LuaEnv();
luaEnv.AddLoader(MyLoader);
luaEnv.DoString("require 'fish'");
}
fish.lua里面修改代码
-- 1.2 技能消耗砖石太多
xlua.private_accessible(CS.Fire)
xlua.hotfix(CS.Fire,'Start',function(self)
self.reduceDiamands = 8;
end)
xlua.private_accessible(CS.Ice)
xlua.hotfix(CS.Ice,'Start',function(self)
self.reduceDiamands = 8;
end)
xlua.private_accessible(CS.ButterFly)
xlua.hotfix(CS.ButterFly,'Start',function(self)
self.reduceDiamands = 5;
end)
1.2版本boss撞击玩家数值调整的补丁开发
使用util.lua,它自带一种hotfix_ex的方法,这个可以调用原来的函数
-- boss撞击玩家数值变动一样且不是减少是增加。
local util = require 'util'
xlua.private_accessible(CS.Boss)
util.hotfix_ex(CS.Boss,'Start',function(self)
self.Start(self)
self.m_reduceGold = self.m_reduceGold - 20
end)
xlua.private_accessible(CS.DeffendBoss)
util.hotfix_ex(CS.DeffendBoss,'Start',function(self)
self.Start(self)
self.m_reduceGold = self.m_reduceGold - 30
self.m_reduceDiamond = self.m_reduceDiamond
end)
xlua.private_accessible(CS.InvisibleBoss)
util.hotfix_ex(CS.InvisibleBoss,'Start',function(self)
self.Start(self)
self.m_reduceDiamond = self.m_reduceDiamond - 5
end)
1.3版本boss撞击玩家当钻石金币不够时的显示补丁开发
-- 1.3 boss撞击玩家当砖石金币不够时会产生负数
util.hotfix_ex(CS.Gun,'GoldChange',function(self,number)
self.GoldChange(self,number)
if self.gold<-number then
self.gold = 0
return
end
end)
util.hotfix_ex(CS.Gun,'DiamandsChange',function(self,number)
self.DiamandsChange(self,number)
if self.diamands<-number then
self.diamands = 0
return
end
end)
1.3版本子弹3的使用扣除方式的更改
--1.3 炮台3太强,且砖石没用处,不削弱,只有氪金才能使用
if self.gunLevel==3 and self.diamands<3 then
return
elseif self.gunLevel~=3 then
if self.gold<1+(self.gunLevel-1)*2 or gold==0 then
return
end
end
self.bullectAudio.clip = self.bullectAudios[self.gunLevel-1]
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
if self.gunLevel == 3 then
self:DiamandsChange(-3)
else
self:GoldChange(-1-(self.gunLevel-1)*2)
end
end
self.attackCD = 0
self.attack = false
用xlua修改产鱼方法
-- 1.3 大鱼太多
xlua.private_accessible(CS.CreateFish)
xlua.hotfix(CS.CreateFish,'Update',function(self)
self:CreateALotOfFish()
--单种鱼的生成
if self.ItemtimeVal >= 0.5 then
--位置随机数
self.num = UnityEngine.Mathf.Floor(UnityEngine.Random.Range(0, 4))
--游戏物体随机数
self.ItemNum = UnityEngine.Mathf.Floor(UnityEngine.Random.Range(1, 101))
local halfLength = self.fishList.Length/2
local littlefishTypeIndex = UnityEngine.Mathf.Floor(UnityEngine.Random.Range(0,halfLength))
local bigfishTypeIndex = UnityEngine.Mathf.Floor(UnityEngine.Random.Range(halfLength,self.fishList.Length))
local itemTypeIndex = UnityEngine.Mathf.Floor(UnityEngine.Random.Range(0,self.item.Length))
--产生气泡
if self.ItemNum < 20 then
self:CreateGameObject(self.item[3])
self:CreateGameObject(self.fishList[6])
end
if self.ItemNum <= 42 then
for i=0,2,1 do
self:CreateGameObject(self.fishList[littlefishTypeIndex])
end
self:CreateGameObject(self.item[itemTypeIndex])
elseif self.ItemNum >= 43 and self.ItemNum < 72 then
for i=0,1,1 do
self:CreateGameObject(self.fishList[bigfishTypeIndex])
end
self:CreateGameObject(self.item[itemTypeIndex])
elseif self.ItemNum >= 84 and self.ItemNum < 86 then
self:CreateGameObject(self.boss2)
elseif self.ItemNum >= 86 and self.ItemNum <= 87 then
self:CreateGameObject(self.boss);
elseif ItemNum == 100 then
self:CreateGameObject(self.boss3)
else
self:CreateGameObject(self.item[0])
end
self.ItemtimeVal = 0
else
self.ItemtimeVal = self.ItemtimeVal + CS.UnityEngine.Time.deltaTime;
end
end)
1.4版本捕鱼方式修改补丁的开发
--1.4 捕鱼时考虑了鱼的血量与子弹的伤害来模拟概率,这样玩家体验不好,要使用传统的概率来捕鱼
xlua.private_accessible(CS.Fish)
xlua.hotfix(CS.Fish,'TakeDamage',function(self,attackValue)
if CS.Gun.Instance.Fire then
attackValue = attackValue*2
end
local catchValue = UnityEngine.Mathf.Floor(UnityEngine.Random.Range(0,100))
if catchValue<=(50-(self.hp-attackValue))/2 then
self.isDead = true
for i=0,8,1 do
UnityEngine.GameObject.Instantiate(self.pao, self.transform.position, UnityEngine.Quaternion.Euler(self.transform.eulerAngles + UnityEngine.Vector3(0, 45 * i, 0)))
end
self.gameObjectAni:SetTrigger("Die")
self:Invoke("Prize", 0.7)
end
end)
捕捉boss方法的修改
xlua.hotfix(CS.Boss,'TakeDamage',function(self,attackValue)
if UnityEngine.Gun.Instance.Fire then
attackValue = attackValue*2
end
local catchValue = UnityEngine.Mathf.Floor(UnityEngine.Random.Range(0,100))
if catchValue<=(attackValue*3-self.hp/10) then
UnityEngine.GameObject.Instantiate(self.deadEeffect, self.transform.position, self.transform.rotation)
CS.Gun.Instance.GoldChange(self.GetGold * 10)
CS.Gun.Instance:DiamandsChange(self.GetDiamands * 10)
for i=0,10,1 do
local itemGo = UnityEngine.GameObject.Instantiate(self.gold, self.transform.position, UnityEngine.Quaternion.Euler(self.transform.eulerAngles + UnityEngine.Vector3(0, 18 + 36 * (i - 1), 0)))
itemGo:GetComponent('Gold').bossPrize = true
end
for i=0,10,1 do
local itemGo1= UnityEngine.GameObject.Instantiate(self.gold, self.transform.position, UnityEngine.Quaternion.Euler(self.transform.eulerAngles + UnityEngine.Vector3(0, 36 + 36 * (i - 1), 0)))
itemGo1:GetComponent('Gold').bossPrize = true
end
UnityEngine.Object.Destroy(self.gameObject)
end
end)
1.4版本炮台移动方式修改补丁的开发
-- 1.4 炮台移动是根据鼠标的水平数值滑动来模拟跟随的,改为玩家按下ad键来旋转炮台
xlua.hotfix(CS.Gun,'RotateGun',function(self)
if UnityEngine.Input.GetKey(UnityEngine.KeyCode.A) then
self.transform:Rotate(UnityEngine.Vector3.forward * self.rotateSpeed)
elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.D) then
self.transform:Rotate(-UnityEngine.Vector3.forward * self.rotateSpeed)
end
self:ClampAngle()
end)
xlua.private_accessible(CS.GunImage)
xlua.hotfix(CS.GunImage,'RotateGun',function(self)
if UnityEngine.Input.GetKey(UnityEngine.KeyCode.A) then
self.transform:Rotate(UnityEngine.Vector3.forward * self.rotateSpeed)
elseif UnityEngine.Input.GetKey(UnityEngine.KeyCode.D) then
self.transform:Rotate(-UnityEngine.Vector3.forward * self.rotateSpeed)
end
self:ClampAngle()
end)
鱼资源的打包
创建一个CreateAssetBundles脚本,用来进行打包
using UnityEditor;
using System.IO;
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);
}
}
需要知道的补丁开发原则
AssetBundle ab = AssetBundle.LoadFromFile("文件路径");
GameObject go = ab.LoadAsset<GameObject>("资源名(预制体名字)");
开发生成新鱼的补丁
public static Dictionary<string, GameObject> prefabDict = new Dictionary<string, GameObject>();
public static void LoadResource(string resName,string filePath)
{
AssetBundle ab = AssetBundle.LoadFromFile(@"D:\u3dworkspace\FishingJoy\AssetBundles\" + filePath);
GameObject go = ab.LoadAsset<GameObject>(resName);
prefabDict.Add(resName, go);
}
public static GameObject GetGameObject(string goName)
{
return prefabDict[goName];
}
修改fish.lua
xlua.hotfix(CS.CreateFish,'Start',function(self)
CS.HotFixScript.LoadResource('level3fish3','gameobject\\enemy.ab')
end)
--在Update里添加
-- 2.0 增加新鱼
elseif self.ItemNum >= 72 and self.ItemNum < 84 then
newFish = CS.HotFixScript.GetGameObject('level3fish3')
self:CreateGameObject(newFish)
海浪的制作
未预测新类补丁开发的处理方法
创建一个HotFixEmpty空类,用来进行新类补丁的开发
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
[Hotfix]
public class HotFixEmpty : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter(Collider other)
{
}
private void BehaviourMethod()
{
}
}
海浪类的补丁方法
local canCreateNewFish = true
local changeMapTimeval = 0
xlua.hotfix(CS.CreateFish,'Start',function(self)
CS.HotFixScript.LoadResource('level3fish3','gameobject\\enemy.ab')
CS.HotFixScript.LoadResource('SeaWave','gameobject\\wave.ab')
end)
xlua.private_accessible(CS.CreateFish)
xlua.hotfix(CS.CreateFish,'Update',function(self)
-- 2.0 生成海浪
if canCreateNewFish then
if changeMapTimeval >= 60 then
go=CS.HotFixScript.GetGameObject('SeaWave')
UnityEngine.GameObject.Instantiate(go)
canCreateNewFish = false
changeMapTimeval = 0
else
changeMapTimeval = changeMapTimeval + UnityEngine.Time.deltaTime
end
else
return
end
end
--**********************************************************************************
-- 2.0 海浪
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)