更新怪物血量
在上一节,我们制作了怪物的血条UI,让其能够正确显示怪物的名字以及怪物的血量。但是血条并不具备更新血量的功能,所以本节课我们就来给血条添加更新逻辑。
1.给MonsterUI添加刷新函数
刷新血条,主要需要改变的是代表HP图片的缩放值,以及血量文本,所以这个函数编写如下:
MonsterUI脚本
此次逻辑添加的要点:
① 添加了一个属性:_maxHP【最大血量】 (因为需要使用它来和当前血量计算血量百分比)
② 添加了一个属性:_hpScale【血条图片缩放值】(因为需要频繁的对图片的缩放值进行赋值,使用缓存变量可以减少内存消耗)
ts
import MonsterUI_Generate from "../ui-generate/MonsterUI_generate";
export default class MonsterUI extends MonsterUI_Generate {
/**最大血量 */
private _maxHP: number
/**血条图片缩放值 */
private _hpScale: Vector2 = new Vector2(1, 1)
public init(name: string, hp: number) {
this.mName_txt.text = name
this.mHP_txt.text = hp + "/" + hp
this._maxHP = hp
}
/**
* 刷新血量
* @param hp 怪物当前的血量
*/
public freshHP(hp: number) {
// 更新血量文本
this.mHP_txt.text = hp + "/" + this._maxHP
// 计算血条图片的缩放值
this._hpScale.x = hp / this._maxHP
// 更改血条图片的缩放
this.mHP_img.renderScale = this._hpScale
}
}
import MonsterUI_Generate from "../ui-generate/MonsterUI_generate";
export default class MonsterUI extends MonsterUI_Generate {
/**最大血量 */
private _maxHP: number
/**血条图片缩放值 */
private _hpScale: Vector2 = new Vector2(1, 1)
public init(name: string, hp: number) {
this.mName_txt.text = name
this.mHP_txt.text = hp + "/" + hp
this._maxHP = hp
}
/**
* 刷新血量
* @param hp 怪物当前的血量
*/
public freshHP(hp: number) {
// 更新血量文本
this.mHP_txt.text = hp + "/" + this._maxHP
// 计算血条图片的缩放值
this._hpScale.x = hp / this._maxHP
// 更改血条图片的缩放
this.mHP_img.renderScale = this._hpScale
}
}
2.在客户端随机修改怪物血量
为了测试刷新函数逻辑是否正确,我们可以在 MonsterScript 脚本中添加测试代码进行测试
MonsterScript脚本
在MonsterUI初始化好之后,用setInterval开启一个间隔函数,每秒随机更新血量
ts
import MonsterUI from "./UI/MonsterUI"
@Component
export default class MonsterScirpt extends Script {
@Property({ displayName: "怪物名" })
monsterName: string = ""
@Property({ displayName: "最大血量" })
maxHP: number = 100
private monsterUI: MonsterUI = null
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
if (SystemUtil.isClient()) {
// 获取世界UI
let worldUI = this.gameObject.getChildByName("世界UI") as UIWidget
// 创建怪物UI
this.monsterUI = UIService.create(MonsterUI)
// 将怪物UI显示在世界UI上
worldUI.setTargetUIWidget(this.monsterUI.uiWidgetBase)
this.monsterUI.init(this.monsterName, this.maxHP)
setInterval(()=>{
this.monsterUI.freshHP(Math.floor(Math.random()*100))
},100)
}
}
}
import MonsterUI from "./UI/MonsterUI"
@Component
export default class MonsterScirpt extends Script {
@Property({ displayName: "怪物名" })
monsterName: string = ""
@Property({ displayName: "最大血量" })
maxHP: number = 100
private monsterUI: MonsterUI = null
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
if (SystemUtil.isClient()) {
// 获取世界UI
let worldUI = this.gameObject.getChildByName("世界UI") as UIWidget
// 创建怪物UI
this.monsterUI = UIService.create(MonsterUI)
// 将怪物UI显示在世界UI上
worldUI.setTargetUIWidget(this.monsterUI.uiWidgetBase)
this.monsterUI.init(this.monsterName, this.maxHP)
setInterval(()=>{
this.monsterUI.freshHP(Math.floor(Math.random()*100))
},100)
}
}
}
代码运行效果如下:
3.在服务端随机修改怪物血量
如果只在客户端修改血量,大家运行两个客户端后就会发现,怪物的血量是不同步的。我们希望打怪游戏中的玩家可以共享各个怪物的血量,所以要让血量同步到各个客户端,最直接的办法就是在服务端修改怪物血量。
3.1.添加"当前血量"属性
为了让各个客户端都能够获取到一个相同的值,所以需要在脚本中添加一个“当前血量”来作为各个客户端的同步目标。
MonsterScript脚本
向脚本中添加了“nowHP”来作为怪物的当前血量,并且在怪物首次初始化的时候,在服务端让这个值等于最大血量
ts
import MonsterUI from "./UI/MonsterUI"
@Component
export default class MonsterScirpt extends Script {
@Property({ displayName: "怪物名" })
monsterName: string = ""
@Property({ displayName: "最大血量" })
maxHP: number = 100
nowHP: number = 100
private monsterUI: MonsterUI = null
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
if (SystemUtil.isClient()) {
// 获取世界UI
let worldUI = this.gameObject.getChildByName("世界UI") as UIWidget
// 创建怪物UI
this.monsterUI = UIService.create(MonsterUI)
// 将怪物UI显示在世界UI上
worldUI.setTargetUIWidget(this.monsterUI.uiWidgetBase)
this.monsterUI.init(this.monsterName, this.maxHP)
}
if (SystemUtil.isServer()) {
this.nowHP = this.maxHP
}
}
}
import MonsterUI from "./UI/MonsterUI"
@Component
export default class MonsterScirpt extends Script {
@Property({ displayName: "怪物名" })
monsterName: string = ""
@Property({ displayName: "最大血量" })
maxHP: number = 100
nowHP: number = 100
private monsterUI: MonsterUI = null
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
if (SystemUtil.isClient()) {
// 获取世界UI
let worldUI = this.gameObject.getChildByName("世界UI") as UIWidget
// 创建怪物UI
this.monsterUI = UIService.create(MonsterUI)
// 将怪物UI显示在世界UI上
worldUI.setTargetUIWidget(this.monsterUI.uiWidgetBase)
this.monsterUI.init(this.monsterName, this.maxHP)
}
if (SystemUtil.isServer()) {
this.nowHP = this.maxHP
}
}
}
3.2.将属性变化同步给客户端
在一个双端脚本里,服务端的属性是不会自动同步给客户端的。在这里我们需要使用到属性装饰器的另一个设置:属性同步(replicated)
我们可以通过属性装饰器,将要进行同步的属性进行标记,这样属性就能够在服务端发生变化时,自动同步给所有客户端
MonsterScript脚本:
① 给属性装饰器的"replicated"字段赋值为true,即为开启属性同步
② 给属性装饰器的"onChanged"字段赋值为"onHPChanged",效果是让函数名为"onHPChanged"的函数,在属性发生变更时,在客户端自动执行。
③ 在onHPChanged函数中,添加了刷新血条UI的逻辑
④ 在服务端开启了一个间隔函数,来对当前血量属性进行随机更改
ts
import MonsterUI from "./UI/MonsterUI"
@Component
export default class MonsterScirpt extends Script {
@Property({ displayName: "怪物名" })
monsterName: string = ""
@Property({ displayName: "最大血量" })
maxHP: number = 100
@Property({ replicated: true, onChanged: "onHPChanged" })
nowHP: number = 100
private monsterUI: MonsterUI = null
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
if (SystemUtil.isClient()) {
// 获取世界UI
let worldUI = this.gameObject.getChildByName("世界UI") as UIWidget
// 创建怪物UI
this.monsterUI = UIService.create(MonsterUI)
// 将怪物UI显示在世界UI上
worldUI.setTargetUIWidget(this.monsterUI.uiWidgetBase)
this.monsterUI.init(this.monsterName, this.maxHP)
}
if (SystemUtil.isServer()) {
this.nowHP = this.maxHP
setInterval(() => {
// 服务端随机更新血量
this.nowHP = Math.floor(Math.random() * 100)
}, 1000)
}
}
private onHPChanged() {
// 调用血条刷新的逻辑
if (this.monsterUI) {
this.monsterUI.freshHP(this.nowHP)
}
}
}
import MonsterUI from "./UI/MonsterUI"
@Component
export default class MonsterScirpt extends Script {
@Property({ displayName: "怪物名" })
monsterName: string = ""
@Property({ displayName: "最大血量" })
maxHP: number = 100
@Property({ replicated: true, onChanged: "onHPChanged" })
nowHP: number = 100
private monsterUI: MonsterUI = null
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
if (SystemUtil.isClient()) {
// 获取世界UI
let worldUI = this.gameObject.getChildByName("世界UI") as UIWidget
// 创建怪物UI
this.monsterUI = UIService.create(MonsterUI)
// 将怪物UI显示在世界UI上
worldUI.setTargetUIWidget(this.monsterUI.uiWidgetBase)
this.monsterUI.init(this.monsterName, this.maxHP)
}
if (SystemUtil.isServer()) {
this.nowHP = this.maxHP
setInterval(() => {
// 服务端随机更新血量
this.nowHP = Math.floor(Math.random() * 100)
}, 1000)
}
}
private onHPChanged() {
// 调用血条刷新的逻辑
if (this.monsterUI) {
this.monsterUI.freshHP(this.nowHP)
}
}
}
效果演示