Skip to content
实现家园系统(1)

实现家园系统(1)

预计阅读时间 15 分钟

本章开始我们要将之前制作的单机游戏改为联机,本节会先将房屋模型修改为预制体,并修改 HomeInfo 脚本来适配多人游戏。

前面内容我们已经实现了单个玩家的所有游戏功能,接下来我们将它改成多人游戏。本章将会展示在口袋方舟中制作多人游戏有多么简单。

接下来我们需要将房子打包为“家园”预制体, 一个玩家对应一个家园。

  1. 拖入一个逻辑对象空锚点到场景中,将它命名为 "家园"。
  2. 将之前制作好的建筑选中包括信箱,全部拖到空锚点下,作为空锚点的子物体。
  3. 右键,生成预制体。
  4. 在工程内容中右键这个预制体,改名为”家园“。
  5. 双击家园预制体,进入到预制体编辑界面。
  6. 在脚本目录下的 prefab 目录中新建一个”HomeInfo“脚本。
  7. 将"HomeInfo"脚本拖入”家园“预制体下面。

img

  • "HomeInfo"脚本主要用来规划家园的归属,绑定玩家的,代码如下
TypeScript
@Component
export default class HomeInfo extends Script {

    /** 家园唯一ID 用来确定是哪一个预制体 */
    @Property({ displayName: "家园序号" })
    public homeId: number = 1;

    /** 绑定 解绑 家园的回调 */
    public bindAction: Action = new Action();

    /** 家园拥有者的id 默认为 -1 代表没有拥有者 */
    public ownerId = -1;

    /** 当脚本被实例后,会在第一帧更新前调用此函数 */
    protected onStart(): void {
        if (SystemUtil.isClient()) return;
        this.gameObject.setVisibility(PropertyStatus.Off, true);
        // 监听绑定家园事件
        Event.addLocalListener("BindHome" + this.homeId, this.bindHome.bind(this));
        // 监听解绑家园事件
        Event.addLocalListener("DeBindHome" + this.homeId, this.deBindHome.bind(this));
    }

    /** 绑定家园 */
    private bindHome(pid: number) {
        this.ownerId = pid;
        this.gameObject.setVisibility(PropertyStatus.On, false);
        this.bindAction.call(true);
    }

    /** 解绑家园 */
    private deBindHome() {
        this.ownerId = -1;
        this.gameObject.setVisibility(PropertyStatus.Off, true);
        this.bindAction.call(false);
    }
}
@Component
export default class HomeInfo extends Script {

    /** 家园唯一ID 用来确定是哪一个预制体 */
    @Property({ displayName: "家园序号" })
    public homeId: number = 1;

    /** 绑定 解绑 家园的回调 */
    public bindAction: Action = new Action();

    /** 家园拥有者的id 默认为 -1 代表没有拥有者 */
    public ownerId = -1;

    /** 当脚本被实例后,会在第一帧更新前调用此函数 */
    protected onStart(): void {
        if (SystemUtil.isClient()) return;
        this.gameObject.setVisibility(PropertyStatus.Off, true);
        // 监听绑定家园事件
        Event.addLocalListener("BindHome" + this.homeId, this.bindHome.bind(this));
        // 监听解绑家园事件
        Event.addLocalListener("DeBindHome" + this.homeId, this.deBindHome.bind(this));
    }

    /** 绑定家园 */
    private bindHome(pid: number) {
        this.ownerId = pid;
        this.gameObject.setVisibility(PropertyStatus.On, false);
        this.bindAction.call(true);
    }

    /** 解绑家园 */
    private deBindHome() {
        this.ownerId = -1;
        this.gameObject.setVisibility(PropertyStatus.Off, true);
        this.bindAction.call(false);
    }
}
  • 删除场景中原来的房屋模型,从预制体中拖出四份家园,并分别命名为”家园 1“、”家园 2“、”家园 3“、”家园 4“,将各家园中的 HomeInfo 脚本中的家园序号 homeId 属性改为"1"、"2"、”3“、"4"。

img

  • 接下来我们要实现如下功能:

    1. 家园中已解锁的建筑对其他玩家可见。

      • 在服务端控制已解锁的建筑显隐,即可自动同步给其他玩家。
    2. 解锁按钮仅对自己可见,仅对自己触发,将按钮改为仅客户端。

      • 进入解锁按钮预制体编辑界面,将网络状态全改为 "C" 端。

img

  • 邮箱仅自己可领取可查看已积累的金币,我们在家园预制体中,将信箱下面的触发器改为单端

img

  • 在脚本目录下新建一个 utils 目录 ,在 utils 目录中新建HomeHelper脚本,这是一个用来规划家园、管理家园中玩家的工具
TypeScript
export default class HomeHelper {

    /** 家园最大数量 */
    public static readonly MAX_HOME_NUM = 4;

    /** 家园与拥有者id的键值对 value 为-1 代表没人拥有 */
    protected homeMap: Map<number, number> = new Map();

    private static _instance: HomeHelper;

    private constructor() {
        // 初始化家园Map
        for (let index = 1; index <= HomeHelper.MAX_HOME_NUM; index++) {
            this.homeMap.set(index, -1);
        }
    }

    public static getInstance() {
        if (!this._instance) {
            this._instance = new HomeHelper();
        }
        return this._instance;
    }

    /**
     * 绑定玩家到一个家园
     * @param pid 玩家id
     */
    public bindPlayer(pid: number) {
        for (const v of this.homeMap) {
            /** 没玩家就绑定 */
            if (v[1] === -1) {
                this.homeMap.set(v[0], pid);
                Event.dispatchToLocal("BindHome" + v[0], pid);
                break;
            }
        }
    }

    /**
     * 解绑玩家的家园
     * @param pid 玩家id
     */
    public deBindPlayer(pid) {
        for (const v of this.homeMap) {
            /** 找到玩家解绑 */
            if (v[1] === pid) {
                this.homeMap.set(v[0], -1);
                Event.dispatchToLocal("DeBindHome" + v[0], pid);
                break;
            }
        }
    }
}
export default class HomeHelper {

    /** 家园最大数量 */
    public static readonly MAX_HOME_NUM = 4;

    /** 家园与拥有者id的键值对 value 为-1 代表没人拥有 */
    protected homeMap: Map<number, number> = new Map();

    private static _instance: HomeHelper;

    private constructor() {
        // 初始化家园Map
        for (let index = 1; index <= HomeHelper.MAX_HOME_NUM; index++) {
            this.homeMap.set(index, -1);
        }
    }

    public static getInstance() {
        if (!this._instance) {
            this._instance = new HomeHelper();
        }
        return this._instance;
    }

    /**
     * 绑定玩家到一个家园
     * @param pid 玩家id
     */
    public bindPlayer(pid: number) {
        for (const v of this.homeMap) {
            /** 没玩家就绑定 */
            if (v[1] === -1) {
                this.homeMap.set(v[0], pid);
                Event.dispatchToLocal("BindHome" + v[0], pid);
                break;
            }
        }
    }

    /**
     * 解绑玩家的家园
     * @param pid 玩家id
     */
    public deBindPlayer(pid) {
        for (const v of this.homeMap) {
            /** 找到玩家解绑 */
            if (v[1] === pid) {
                this.homeMap.set(v[0], -1);
                Event.dispatchToLocal("DeBindHome" + v[0], pid);
                break;
            }
        }
    }
}
  • 玩家上线,对自己显示其他玩家的家园,对其他玩家同步显示该上线玩家已解锁的建筑
    • 在 PlayerModuleS 中监听玩家上线
    • 用 HomeHelper 为其绑定一个空的家园
TypeScript
import HomeHelper from "../../utils/HomeHelper";
export class PlayerModuleS extends ModuleS<PlayerModuleC, PlayerData> {

    /** 玩家进入房间时给他绑定家园 */
    protected onPlayerEnterGame(player: Player): void {
        HomeHelper.getInstance().bindPlayer(player.playerId);
    }
    // 之前存在的方法 ...
}
import HomeHelper from "../../utils/HomeHelper";
export class PlayerModuleS extends ModuleS<PlayerModuleC, PlayerData> {

    /** 玩家进入房间时给他绑定家园 */
    protected onPlayerEnterGame(player: Player): void {
        HomeHelper.getInstance().bindPlayer(player.playerId);
    }
    // 之前存在的方法 ...
}
  • 玩家下线,对其他玩家隐藏自己的家园
    • 在 PlayerModuleS 中监听玩玩家下线
    • 用 HomeHelper 解绑下线的这个玩家的家园
TypeScript
export class PlayerModuleS extends ModuleS<PlayerModuleC, PlayerData> {

    /** 玩家下线,解绑这个家园 */
    protected onPlayerLeft(player: Player): void {
        HomeHelper.getInstance().deBindPlayer(player.playerId);
    }
    
    // 之前存在的方法 ... 
}
export class PlayerModuleS extends ModuleS<PlayerModuleC, PlayerData> {

    /** 玩家下线,解绑这个家园 */
    protected onPlayerLeft(player: Player): void {
        HomeHelper.getInstance().deBindPlayer(player.playerId);
    }
    
    // 之前存在的方法 ... 
}

本节我们创建了家园预制体,修改了 HomeInfo 脚本来适配多人游戏,下一节我们将会修改原有的代码实现多人联机功能。