Skip to content
显示可购买物品功能

显示可购买物品功能

预计阅读时间 20 分钟

本章我们要实现玩家踩踏购买按钮,显示对应物品的功能。

本章我们将实现,购买建筑后,显示下一组可购买建筑的购买按钮,的脚本逻辑。

  • 右键脚本,创建一个名为 prefab 的文件夹

img

  • 点击 prefab,再点击新建脚本,创建一个名为 BuildInfo 的脚本

img

  • 配置解锁组、节点编号、解锁按钮相对位置、显示下一组解锁按钮组别 id 等属性
    • @Property()装饰器,加上这个装饰器的变量,将会在编辑器属性面板显示
    • 参数 group,将显示在属性面板的值,按 group 值分组
    • 参数 displayName,另设这个变量在属性面板的显示名,如果不设置将默认显示变量作为属性名
    • 参数 tooltip,当鼠标放在这个参数上时显示提示
  • 双击 BuildInfo 脚本,清空其中代码,并将以下代码复制进去
TypeScript
@Component
export default class BuildInfo extends Script {

    /** 显示创建按钮的组别 */
    @Property({ group: "基本信息", tooltip: "组号,用来确认显示建造按钮的组,配置时需保证组号之间是衔接的,即第一组从0开始,第二组就是1" })
    public groupId: number = 0;

    /** 这个建筑解锁按钮的相对位置 */
    @Property({ group: "基本信息", displayName: "解锁按钮的相对位置", tooltip: "指当将这个建筑设置为父节点时,子节点的相对位置relativeLocation" })
    public unlockBtnLoc: Vector = Vector.zero;

    @Property({ group: "基本信息", displayName: "需要数量", tooltip: "显示这个解锁按钮组,需要多少前置解锁" })
    public needs: number = 1;

    /** 当前显示解锁按钮组进度 */
    private _curPro: number = 0;
}
@Component
export default class BuildInfo extends Script {

    /** 显示创建按钮的组别 */
    @Property({ group: "基本信息", tooltip: "组号,用来确认显示建造按钮的组,配置时需保证组号之间是衔接的,即第一组从0开始,第二组就是1" })
    public groupId: number = 0;

    /** 这个建筑解锁按钮的相对位置 */
    @Property({ group: "基本信息", displayName: "解锁按钮的相对位置", tooltip: "指当将这个建筑设置为父节点时,子节点的相对位置relativeLocation" })
    public unlockBtnLoc: Vector = Vector.zero;

    @Property({ group: "基本信息", displayName: "需要数量", tooltip: "显示这个解锁按钮组,需要多少前置解锁" })
    public needs: number = 1;

    /** 当前显示解锁按钮组进度 */
    private _curPro: number = 0;
}
  • 将 BuildInfo 脚本拖到木地板下面

  • 查看属性面板,在组别为"基本信息"的属性信息中可以看到刚才代码中配置的相关属性

img

  • 鼠标停放在 groupId 上,显示代码中配置的 tooltip

img

  • 创建解锁按钮预制体
    • 将之前制作的按钮改为双端
    • 右键宽胶囊,重命名为”解锁按钮“,因为生成的预制体会根据此命名
    • 右键”解锁按钮“,找到最下面的生成预制体并点击

  • 打开解锁按钮预制件,先将解锁按钮预制体相对位置的 x,y,z 调成 0

  • 找到解锁按钮模型的碰撞属性并将其关闭,否则玩家可能碰不到触发器(被遮挡住)

img

  • 因为后面需要动态创建解锁按钮预制体,所以将这个预制体资源拖入到优先加载中

img

  • 工程内容点击预制体,右键解锁按钮,拿到解锁按钮的 AssetID,注意每个生成的预制体按钮 AssetID 都是唯一的

img

  • 处理建筑隐藏逻辑以及按钮显示逻辑
    • 将所有需要解锁的建筑隐藏
    • 将组号为 0 的解锁建筑按钮显示
    • 显示解锁按钮的逻辑实际上使用对象池 GameObjPool.spawn(AssetID)动态创建一个解锁按钮预制体,将其父节点设置为这个建筑,同时根据预先设置好的解锁按钮相对位置属性设置解锁按钮的相对位置
    • 绑定解锁按钮的触发器进入事件,当玩家进入时,显示这个建筑,然后使用对象池 GameObjPool.despawn(obj)回收这个触发器
    • 将以下代码以追加的方式放入 BuildInfo 脚本的 BuildInfo 类中,注意:之前复制的预制体 AssetID 应该是自己创建的预制体的 id,否则可能会创建不了解锁按钮
TypeScript
/** 解锁按钮 */
private _unlockBtn: GameObject;

/** 解锁建筑的方法 */
private _unlockBuildFun;

/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
    // 在服务端做
    if (SystemUtil.isClient()) return;
    // 关闭碰撞
    this.gameObject.setCollision(PropertyStatus.Off);
    // 关闭显示
    this.gameObject.setVisibility(PropertyStatus.Off);
    // 显示组ID是0的组
    if (this.groupId === 0) {
        this.initUnlockBtn();
    }
}

/**
 * 初始化解锁建筑按钮
 */
private async initUnlockBtn() {
    // 注意这儿spawn的 AssetID 是解锁按钮预制体的id,第二个参数指资源类型,这儿因为是预制体的资源所以传递GameObjPoolSourceType.Prefab
    this._unlockBtn = await GameObjPool.asyncSpawn("D442F26A43DED08F57F592B57CC2B56E", GameObjPoolSourceType.Prefab);
    // 设置按钮的父节点为当前对象
    this._unlockBtn.parent = this.gameObject;
    // 设置按钮的相对位置
    this._unlockBtn.localTransform.position = this.unlockBtnLoc;
    // 拿到解锁按钮预制体下面的解锁按钮模型,最后拿到触发器
    const trigger = this._unlockBtn.getChildByName("触发器") as Trigger;
    this._unlockBuildFun = (other: GameObject) => {
        // 判断进入的对象是一个Character实例才创建
        if (other instanceof Character) {
            // 用完了就先取消绑定
            trigger.onEnter.remove(this._unlockBuildFun);
            // 对象池回收解锁按钮
            GameObjPool.despawn(this._unlockBtn);
            // 显示这个模型
            this.gameObject.setVisibility(PropertyStatus.On);
            // 打开碰撞
            (this.gameObject as Model).setCollision(PropertyStatus.On);
        }
    }
    // 绑定触发器的进入事件
    trigger.onEnter.add(this.unlockBuildFun);
}
/** 解锁按钮 */
private _unlockBtn: GameObject;

/** 解锁建筑的方法 */
private _unlockBuildFun;

/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
    // 在服务端做
    if (SystemUtil.isClient()) return;
    // 关闭碰撞
    this.gameObject.setCollision(PropertyStatus.Off);
    // 关闭显示
    this.gameObject.setVisibility(PropertyStatus.Off);
    // 显示组ID是0的组
    if (this.groupId === 0) {
        this.initUnlockBtn();
    }
}

/**
 * 初始化解锁建筑按钮
 */
private async initUnlockBtn() {
    // 注意这儿spawn的 AssetID 是解锁按钮预制体的id,第二个参数指资源类型,这儿因为是预制体的资源所以传递GameObjPoolSourceType.Prefab
    this._unlockBtn = await GameObjPool.asyncSpawn("D442F26A43DED08F57F592B57CC2B56E", GameObjPoolSourceType.Prefab);
    // 设置按钮的父节点为当前对象
    this._unlockBtn.parent = this.gameObject;
    // 设置按钮的相对位置
    this._unlockBtn.localTransform.position = this.unlockBtnLoc;
    // 拿到解锁按钮预制体下面的解锁按钮模型,最后拿到触发器
    const trigger = this._unlockBtn.getChildByName("触发器") as Trigger;
    this._unlockBuildFun = (other: GameObject) => {
        // 判断进入的对象是一个Character实例才创建
        if (other instanceof Character) {
            // 用完了就先取消绑定
            trigger.onEnter.remove(this._unlockBuildFun);
            // 对象池回收解锁按钮
            GameObjPool.despawn(this._unlockBtn);
            // 显示这个模型
            this.gameObject.setVisibility(PropertyStatus.On);
            // 打开碰撞
            (this.gameObject as Model).setCollision(PropertyStatus.On);
        }
    }
    // 绑定触发器的进入事件
    trigger.onEnter.add(this.unlockBuildFun);
}
  • 运行游戏查看效果,可以看到地板已经被隐藏了,控制玩家踩解锁按钮,按钮隐藏,地板显示

  • 优化解锁按钮显示位置
    • 将解锁按钮拖到场景中,再将其拖在木地板下面,让其成为木地板的子对象
    • 将解锁按钮拖到适当位置,将相对位置的坐标轴复制入 BuildInfo 脚本中的解锁按钮相对位置属性中
    • 删除这个测试位置的解锁按钮

  • 按 "Ctrl + S " 保存之后,运行游戏,查看效果

完整脚本如下:

typescript
@Component
export default class BuildInfo extends Script {
    // 显示创建按钮得组别
    @Property({ group: "基本信息", tooltip: "组号,用来显示建组按钮得组,配置时需要保证组号之间是连续的" })
    public groupId: number = 0;
    // 这个建筑解锁按钮与建筑的相对位置
    @Property({ group: "基本信息", displayName: "解锁按钮的相对位置", tooltip: "将这个建筑设置为父节点时,子节点相对位置" })
    public unlockBtnLoc: Vector = Vector.zero;
    // 解锁这个按钮时需要前置多少解锁
    @Property({ group: "基本信息", displayName: "需要的前置解锁数量", tooltip: "显示这个解锁按钮需要提前解锁多少家具" })
    public needs: number = 1;
    // 当前解锁按钮的进度
    private _curPro: number = 0;
    // 解锁按钮
    private _unlockBtn: GameObject;
    // 进入触发器事件
    private _unlockBuildFun;

    protected onStart(): void {
        // 判断是否是服务端
        if (SystemUtil.isClient()) return;
        // 关闭碰撞
        (this.gameObject as Model).setCollision(PropertyStatus.Off);
        // 关闭显示
        this.gameObject.setVisibility(PropertyStatus.Off);

        // 显示组ID为 0 的 组
        if (this.groupId === 0) {
            this.initUnlockBtn();
        }
    }

    // 初始化解锁建筑按钮
    private async initUnlockBtn() {
        // 初始化建筑按钮 这里使用对象池生成方式来生成 AssetID 记得换成项目中的 
        this._unlockBtn = await GameObjPool.asyncSpawn("AssetID", GameObjPoolSourceType.Prefab);
        // 将按钮的父节点设置为当前对象
        this._unlockBtn.parent = this.gameObject;
        // 设置按钮的相对位置
        this._unlockBtn.localTransform.position = this.unlockBtnLoc;
        // 获取预制体下的触发器
        const trigger = this._unlockBtn.getChildByName("触发器") as Trigger;
        this._unlockBuildFun = (other: GameObject) => {
            // 判断进入的对象是 Character 才执行
            if (other instanceof Character) {
                // 用完之后就取消绑定
                trigger.onEnter.remove(this._unlockBuildFun);
                // 对象池回收对象
                GameObjPool.despawn(this._unlockBtn);
                // 显示建筑模型
                this.gameObject.setVisibility(PropertyStatus.On);
            }
        }
        // 绑定触发器进入事件
        trigger.onEnter.add(this._unlockBuildFun);
    }
}
@Component
export default class BuildInfo extends Script {
    // 显示创建按钮得组别
    @Property({ group: "基本信息", tooltip: "组号,用来显示建组按钮得组,配置时需要保证组号之间是连续的" })
    public groupId: number = 0;
    // 这个建筑解锁按钮与建筑的相对位置
    @Property({ group: "基本信息", displayName: "解锁按钮的相对位置", tooltip: "将这个建筑设置为父节点时,子节点相对位置" })
    public unlockBtnLoc: Vector = Vector.zero;
    // 解锁这个按钮时需要前置多少解锁
    @Property({ group: "基本信息", displayName: "需要的前置解锁数量", tooltip: "显示这个解锁按钮需要提前解锁多少家具" })
    public needs: number = 1;
    // 当前解锁按钮的进度
    private _curPro: number = 0;
    // 解锁按钮
    private _unlockBtn: GameObject;
    // 进入触发器事件
    private _unlockBuildFun;

    protected onStart(): void {
        // 判断是否是服务端
        if (SystemUtil.isClient()) return;
        // 关闭碰撞
        (this.gameObject as Model).setCollision(PropertyStatus.Off);
        // 关闭显示
        this.gameObject.setVisibility(PropertyStatus.Off);

        // 显示组ID为 0 的 组
        if (this.groupId === 0) {
            this.initUnlockBtn();
        }
    }

    // 初始化解锁建筑按钮
    private async initUnlockBtn() {
        // 初始化建筑按钮 这里使用对象池生成方式来生成 AssetID 记得换成项目中的 
        this._unlockBtn = await GameObjPool.asyncSpawn("AssetID", GameObjPoolSourceType.Prefab);
        // 将按钮的父节点设置为当前对象
        this._unlockBtn.parent = this.gameObject;
        // 设置按钮的相对位置
        this._unlockBtn.localTransform.position = this.unlockBtnLoc;
        // 获取预制体下的触发器
        const trigger = this._unlockBtn.getChildByName("触发器") as Trigger;
        this._unlockBuildFun = (other: GameObject) => {
            // 判断进入的对象是 Character 才执行
            if (other instanceof Character) {
                // 用完之后就取消绑定
                trigger.onEnter.remove(this._unlockBuildFun);
                // 对象池回收对象
                GameObjPool.despawn(this._unlockBtn);
                // 显示建筑模型
                this.gameObject.setVisibility(PropertyStatus.On);
            }
        }
        // 绑定触发器进入事件
        trigger.onEnter.add(this._unlockBuildFun);
    }
}