本帖最后由 空伊伊 于 2023-11-8 17:23 编辑
obby游戏三要素
老规矩,还是先看个视频吧!
<video controls src="https://forum.ark.online/forum.php?mod=attachment&aid=MTA1M3w5Y2E4MmQyYnwxNjk2OTE2NzY4fDExM3wxMjky&noupdate=yes"></video>
1.机关(DeathTrigger)
机关其实就是一个触发器,触发器会告诉GameCenter玩家需要执行死亡逻辑,然后由GameCenter来控制玩家死亡和复活。
另外由于触发器的大小和位置我们可以随意修改,所以制作一个机关,只需要拖一个模型,然后把挂有DeathTrigger脚本的触发器放在模型的位置即可完成一个机关的制作。
/**
* 死亡触发器
*/
@Component
export default class DeathTrigger extends Script {
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
if (SystemUtil.isClient()) {
let trigger = this.gameObject as Trigger
trigger.onEnter.add(this.onTriggerEnter.bind(this))
}
}
/**当进入死亡触发器 */
private onTriggerEnter(other: GameObject) {
if (other instanceof Character) {
// 只有进入的人是自己,才告诉GameCenter执行死亡逻辑
if (other == Player.localPlayer.character) {
Event.dispatchToLocal("CharacterDeath")
}
}
}
}
2.检查点(RecordTrigger)
检查点也是一个触发器,当角色碰到检查点的时候,会将自身的位置发送给GameCenter,GameCenter保存下这个位置后,会让角色下一次复活在这个位置。
/**
* 记录点触发器
*/
@Component
export default class RecordTrigger extends Script {
/**记录点位置(重生时需要这个位置信息) */
private _myLocation: Vector
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
if (SystemUtil.isClient()) {
// 一开始获取到自身位置并赋值给_myLocation(给Z轴增加100是为了防止角色掉到地面以下)
this._myLocation = this.gameObject.worldTransform.position.add(new Vector(0, 0, 100));
let trigger = this.gameObject as Trigger
trigger.onEnter.add(this.onTriggerEnter.bind(this))
}
}
/**当进入记录点触发器 */
private onTriggerEnter(other: GameObject) {
if (other instanceof Character) {
// 当只有进入的人是自己,才将记录点位置发送给GameCenter
if (other == Player.localPlayer.character) {
Event.dispatchToLocal("SaveRecordLocation", this._myLocation)
}
}
}
}
3.GameCenter(控制角色死亡和复活)
GameCenter是一个脚本,把它拖到场景上,它会开始监听死亡事件和检查点事件。收到死亡事件时,GameCenter会开启角色的布娃娃效果,然后隔3秒后,关闭角色的布娃娃效果,同时将角色设置到上一次接收到的检查点位置(如果没有接收到检查点位置,就重生在默认位置)
@Component
export default class GameCenter extends Script {
/**玩家是否死亡(加这个标记,防止角色死亡多次) */
private _isDeath: boolean = false
/**当前记录点的位置 */
private _nowRecordLocation: Vector = new Vector(0, 0, 130)
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected async onStart(): Promise<void> {
if (SystemUtil.isClient()) {
let char = (await Player.asyncGetLocalPlayer()).character
Event.addLocalListener("CharacterDeath", () => {
// 如果玩家没有死亡,但收到了执行死亡逻辑的消息
if (!this._isDeath) {
// 打开角色的布娃娃状态
char.ragdollEnabled = true
// 标记为死亡
this._isDeath = true
// 延迟3秒后复活
setTimeout(() => {
// 关闭角色的布娃娃状态
char.ragdollEnabled = false
// 将角色位置设置为当前记录点的位置
char.worldTransform.position = this._nowRecordLocation
// 标记为没死亡
this._isDeath = false
}, 3000);
}
})
Event.addLocalListener("SaveRecordLocation", (recordLocation: Vector) => {
// 收到执行保存记录点位置的消息,将传来的位置赋值给_nowRecordLocation
this._nowRecordLocation = recordLocation
})
}
}
}
4.缺陷和改进
因为代码写的很简单,所以功能上还有很多可改进的方向,这里给出一些思路:
存档:给检查点添加一个序号属性,用这个序号做为关卡号,然后每次到达检查点的时候,保存这个序号。之后就可以使用数据持久化相关的知识点,将序号永久保存,从而保存了玩家的通过进度。这样在下一次上线就可以知道玩家到第几关了~
UI表现:使用UI相关的知识点,实时显示玩家目前到达第几关了,然后每到一个新的检查点,弹出一个UI来通知玩家到达新的关卡
将机关封装成预制体:因为每次拖一个新的机关,都要先拖一个物体,再拖一个触发器,这样在搭场景的时候就会很麻烦。所以可以将机关制作成一个预制体,之后只需要在场景中添加机关,就只需要拖动预制体就Ok了
5.工程
ObbyBase.rar (62.69 KB)