Skip to content
检查点

检查点

目前游戏是不管在哪个位置角色死亡,都需要复活到起点重新开始闯关,这会大大增加游戏难度。所以我们需要利用检查点将整个关卡拆分成多个小关卡,然后按关卡号保存游戏进度。

1.布置检查点

检查点也是通过触发器来实现功能的,所以我们需要在场景上布置触发器。并且由于触发器是透明的,在游戏中玩家是看不见的,所以需要用一个特效或者模型来标记出触发器的位置。

拖动一个触发器到场景中,并将网络状态修改为客户端

在美术对象中选择“粒子特效”分类

将特效挂载到触发器下(方便同时移动)

调整相对位置,让特效位置与触发器位置相吻合

image-20230828171507063

2.CheckPointTrigger脚本

接下来我们需要编写检查点脚本来进行关卡进度存储的逻辑。

2.1.创建并挂载脚本

点击“新建脚本”

将脚本命名为CheckPointTrigger

将脚本挂载到触发器上

image-20230828172319055

2.2.自定义属性

CheckPointTrigger脚本中编写逻辑如下:

装饰器:@Property()

@Property()是一个属性装饰器,它能够让脚本里的属性字段具备一些特殊功能,例如进行属性同步、在属性面板上进行显示等。

ts
@Component
export default class CheckPointTrigger extends Script {

    @Property({displayName:"序号"})
    public pointNumber:number = 0

}
@Component
export default class CheckPointTrigger extends Script {

    @Property({displayName:"序号"})
    public pointNumber:number = 0

}

在上述逻辑中,我们给pointNumber属性添加了装饰器,并且将装饰器里的displayName参数赋值为“序号”,那么脚本就能在属性面板上展示出这个序号。我们就可以直接在属性面板上对pointNumber进行赋值。具体效果如下:

在对象管理器中选中CheckPointTrigger脚本

在属性面板上将“序号”修改为1(这个检查点放在起点作为第一关)

image-20230828175103724

2.3.编写检查点逻辑

在CheckPointTrigger脚本中添加逻辑如下:

通过this.gameObject获取到脚本挂载的物体(这就是脚本需要挂载到触发器上的原因)

给触发器添加进入逻辑,通过instanceof判断进入的物体是否为角色类型

通过Player.localPlayer.character获取到当前客户端的角色,实现只有当进入的角色是当前客户端角色,才进行检查点事件的派发。

通过Event.dispatchToLocal这个API,向本地派发"CheckPoint"事件。

ts
@Component
export default class CheckPointTrigger extends Script {

    @Property({ displayName: "序号" })
    public pointNumber: number = 0

    /** 当脚本被实例后,会在第一帧更新前调用此函数 */ 
    protected onStart(): void { 
 
        let trigger = this.gameObject as Trigger 
        trigger.onEnter.add((other: GameObject) => { 
            // 进入的物体是否是角色 
            if (other instanceof Character) { 
 
                // 进入的角色 是否是 当前客户端角色 
                if (other == Player.localPlayer.character) { 
                    // 本地事件通信(派发) 
                    Event.dispatchToLocal("CheckPoint", this) 
                } 
 
            } 
        }) 
    } 
}
@Component
export default class CheckPointTrigger extends Script {

    @Property({ displayName: "序号" })
    public pointNumber: number = 0

    /** 当脚本被实例后,会在第一帧更新前调用此函数 */ 
    protected onStart(): void { 
 
        let trigger = this.gameObject as Trigger 
        trigger.onEnter.add((other: GameObject) => { 
            // 进入的物体是否是角色 
            if (other instanceof Character) { 
 
                // 进入的角色 是否是 当前客户端角色 
                if (other == Player.localPlayer.character) { 
                    // 本地事件通信(派发) 
                    Event.dispatchToLocal("CheckPoint", this) 
                } 
 
            } 
        }) 
    } 
}

3.改写复活逻辑

目前角色都是复活在一个固定位置,我们需要在关卡管理器脚本中添加监听事件,并且配合检查点派发的事件来动态的修改角色下一次的复活位置。这样就能实现局内的进度保存。

这次修改只涉及到关卡管理器脚本。

修改后的LevelManager脚本:

此次修改有如下几个要点:

在init函数中添加对"CheckPoint"事件的监听

当监听到CheckPoint事件时,将检查点的位置赋值给重生位置

角色复活时,将角色的位置修改为重生位置

ts
import CheckPointTrigger from "./CheckPointTrigger"

/**
 * 关卡管理器
 */
export class LevelManager {
    // 单例模式
    private static _instacne: LevelManager
    public static get instance(): LevelManager {
        if (LevelManager._instacne == null) {
            LevelManager._instacne = new LevelManager()
        }
        return LevelManager._instacne
    }

    /**死亡触发器 */
    private _deathTrigger: Trigger

    /**复活位置 */
    private _rebornPosition: Vector = new Vector(10, 0, 420)

    public async init() {
        this._deathTrigger = await GameObject.asyncFindGameObjectById("299CDDA6") as Trigger
        this._deathTrigger.onEnter.add((other: GameObject) => {
            // 当进入的物体是角色类型
            if (other instanceof Character) {
                // 让角色死亡
                this.charDeath(other)
            }
        })


        Event.addLocalListener("CheckPoint", (checkPointTrigger: CheckPointTrigger) => { 
            this._rebornPosition = checkPointTrigger.gameObject.worldTransform.position.clone() 
        }) 
    }

    /**让角色死亡 */
    public charDeath(char: Character) {
        // 开启布娃娃属性
        char.ragdollEnabled = true

        setTimeout(() => {
            // 让角色复活
            this.charReborn(char)
        }, 3000);
    }

    /**让角色复活 */ 
    public charReborn(char: Character) { 
        if (char == Player.localPlayer.character) { 
            // 将角色的位置改变到复活点 
            char.worldTransform.position = this._rebornPosition.clone() 
        } 

        // 关闭布娃娃属性
        char.ragdollEnabled = false
    }
}
import CheckPointTrigger from "./CheckPointTrigger"

/**
 * 关卡管理器
 */
export class LevelManager {
    // 单例模式
    private static _instacne: LevelManager
    public static get instance(): LevelManager {
        if (LevelManager._instacne == null) {
            LevelManager._instacne = new LevelManager()
        }
        return LevelManager._instacne
    }

    /**死亡触发器 */
    private _deathTrigger: Trigger

    /**复活位置 */
    private _rebornPosition: Vector = new Vector(10, 0, 420)

    public async init() {
        this._deathTrigger = await GameObject.asyncFindGameObjectById("299CDDA6") as Trigger
        this._deathTrigger.onEnter.add((other: GameObject) => {
            // 当进入的物体是角色类型
            if (other instanceof Character) {
                // 让角色死亡
                this.charDeath(other)
            }
        })


        Event.addLocalListener("CheckPoint", (checkPointTrigger: CheckPointTrigger) => { 
            this._rebornPosition = checkPointTrigger.gameObject.worldTransform.position.clone() 
        }) 
    }

    /**让角色死亡 */
    public charDeath(char: Character) {
        // 开启布娃娃属性
        char.ragdollEnabled = true

        setTimeout(() => {
            // 让角色复活
            this.charReborn(char)
        }, 3000);
    }

    /**让角色复活 */ 
    public charReborn(char: Character) { 
        if (char == Player.localPlayer.character) { 
            // 将角色的位置改变到复活点 
            char.worldTransform.position = this._rebornPosition.clone() 
        } 

        // 关闭布娃娃属性
        char.ragdollEnabled = false
    }
}

小提示

大家不要盲目进行复制粘贴哦,这里还是不要忘了将代码中的"299CDDA6"替换为自己触发器的对象ID

4.完善检查点

这一步我们需要在场景中按照自己的需求给关卡布置上检查点,并给检查点脚本的序号进行正确的赋值。其中终点的序号需要填入-1,下一节将会使用到它。

在场景中布置检查点触发器,并给检查点脚本的序号进行正确的赋值

给终点检查点脚本的序号赋值为-1

image-20230828182022450