实现家园系统(1)
预计阅读时间 15 分钟
本章开始我们要将之前制作的单机游戏改为联机,本节会先将房屋模型修改为预制体,并修改 HomeInfo 脚本来适配多人游戏。
前面内容我们已经实现了单个玩家的所有游戏功能,接下来我们将它改成多人游戏。本章将会展示在口袋方舟中制作多人游戏有多么简单。
接下来我们需要将房子打包为“家园”预制体, 一个玩家对应一个家园。
- 拖入一个逻辑对象
空锚点
到场景中,将它命名为 "家园"。 - 将之前制作好的建筑选中包括信箱,全部拖到空锚点下,作为空锚点的子物体。
- 右键,生成预制体。
- 在工程内容中右键这个预制体,改名为”家园“。
- 双击家园预制体,进入到预制体编辑界面。
- 在脚本目录下的 prefab 目录中新建一个”HomeInfo“脚本。
- 将"HomeInfo"脚本拖入”家园“预制体下面。
- "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"。
接下来我们要实现如下功能:
家园中已解锁的建筑对其他玩家可见。
- 在服务端控制已解锁的建筑显隐,即可自动同步给其他玩家。
解锁按钮仅对自己可见,仅对自己触发,将按钮改为仅客户端。
- 进入解锁按钮预制体编辑界面,将网络状态全改为 "C" 端。
- 邮箱仅自己可领取可查看已积累的金币,我们在家园预制体中,将信箱下面的触发器改为单端
- 在脚本目录下新建一个 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 脚本来适配多人游戏,下一节我们将会修改原有的代码实现多人联机功能。