游戏热更新实战案例(基于xLua)

本课程基于捕鱼达人游戏,使用xLua进行热更新。涵盖lua虚拟环境搭建、补丁开发过程,还针对各版本如1.1 - 1.4及2.0版本的不同问题,像金币拥挤、子弹发射、技能扣钻等开发补丁,同时涉及鱼资源打包、新鱼生成、海浪制作等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本课程主要是基于一个捕鱼达人使用xLua进行热更新。

 

目录

lua虚拟环境的搭建

需要明确的补丁开发过程

修复1.1版本金币拥挤bug的补丁开发

解决LuaEnv释放时的报错

1.1版本金币钻石不够的处理补丁

金币钻石不够处理补丁的完善

1.2版本与UI交互时不能发射子弹的补丁开发

1.2版本技能扣钻石太多的数值修改补丁开发

1.2版本boss撞击玩家数值调整的补丁开发

1.3版本boss撞击玩家当钻石金币不够时的显示补丁开发

1.3版本子弹3的使用扣除方式的更改

用xlua修改产鱼方法

1.4版本捕鱼方式修改补丁的开发

捕捉boss方法的修改

1.4版本炮台移动方式修改补丁的开发

鱼资源的打包

需要知道的补丁开发原则

开发生成新鱼的补丁

海浪的制作

未预测新类补丁开发的处理方法

海浪类的补丁方法


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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值