实现购买物品功能
预计阅读时间 20 分钟
本章我们要将玩家的金币与解锁建筑关联起来,实现扣钱才能解锁物品的功能。
制作显示玩家拥有金币数 UI
- 在 UI 下新建 UI,更名为 MainUI,并进入 MainUI 设计器界面
- 拖入一个容器控件,放在合适位置,取名为 GoldCanvas
- 拖入两个个图片控件,一个文本控件到 GoldCanvas 中,调整到合适位置
- 大一点的图片控件作为背景(131231),小一点的图片控件作为金币图标(162901)
- 金币数量文本控件对象更名为 goldTxt
- 点击导出所有脚本,之后点击确认导出,导出的 UI 脚本之后会用到
- 可以在工程内容的脚本中看到,多出了一个 ui-generate 文件夹,这里就是导出的脚本所存放的地方。要注意的是这个目录每次重新导出都会被覆盖,所以我们不要将功能逻辑放在该目录下任何脚本中,需要使用请直接继承。
模块化脚本
关于模块化可以提前阅读,了解更多用法 :【模块管理和数据中心】——为什么需要模块管理?① 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online)
- 在脚本下新建一个文件夹,命名为
ui
,这个文件夹表示存放 UI 相关逻辑脚本的地方。 - 在 ui 文件夹下新建一个 UI 脚本,命名为
MainUI
- 将 MainUI 中的代码清空,将以下代码全拷贝到 MainUI 中,这里我们继承了
MainUI_Generate
这个类。这个类就是刚刚通过 UI 编辑器一键导出的,导出后类名为UI名字_generate
。
TypeScript
import MainUI_Generate from "../ui-generate/MainUI_generate";
// 继承的 MainUI_Generate 就是从 UI 编辑器器中导出来的脚本
export default class MainUI extends MainUI_Generate {
}
import MainUI_Generate from "../ui-generate/MainUI_generate";
// 继承的 MainUI_Generate 就是从 UI 编辑器器中导出来的脚本
export default class MainUI extends MainUI_Generate {
}
- 在脚本下新建一个 "modules" 的文件夹
- 在 modules 目录下再新建一个 “ player ” 的文件夹。
- 在 player 目录下新建 “PlayerModuleC” 、”PlayerModuleS“、”PlayerData“脚本,分别代表:玩家模块客户端脚本、玩家模块服务端脚本、玩家模块数据脚本。
- PlayerData 脚本中定义 PlayerData 类,继承 Subdata,用来存放模块的数据。
- 定义一个公共变量 gold,并初始化金币为 55 。
- 定义一个监听金币数量改变的回调 Action 。
- 定义一个改变金币数量的方法。
TypeScript
export class PlayerData extends Subdata {
/** 金币数量 */
public gold: number = 55;
/** 金币改变的回调,在需要知道金币改变的地方监听即可 */
public onGoldChange: Action = new Action();
/**
* 改变金币的数量
* @param deltaNum 改变值,为负数就是减
*/
public changeGold(deltaNum: number) {
this.gold += deltaNum;
// 服务端改变金币,将这个操作同步给客户端
this.syncToClient();
this.onGoldChange.call(this.gold);
}
}
export class PlayerData extends Subdata {
/** 金币数量 */
public gold: number = 55;
/** 金币改变的回调,在需要知道金币改变的地方监听即可 */
public onGoldChange: Action = new Action();
/**
* 改变金币的数量
* @param deltaNum 改变值,为负数就是减
*/
public changeGold(deltaNum: number) {
this.gold += deltaNum;
// 服务端改变金币,将这个操作同步给客户端
this.syncToClient();
this.onGoldChange.call(this.gold);
}
}
- PlayerModuleC 脚本中定义 PlayerModuleC 类,继承 ModuleC,客户端的操作放在这里完成
- 在 onStart 时使用 UIManager 显示 MainUI 。
- onStart 会在这个模块被注册成功后,启动时调用。
TypeScript
import MainUI from "../../ui/MainUI";
import { PlayerData } from "./PlayerData";
import { PlayerModuleS } from "./PlayerModuleS";
export class PlayerModuleC extends ModuleC<PlayerModuleS, PlayerData> {
protected override onStart(): void {
// 显示 MainUI 这里传递的是我们创建出的 UI 脚本的类
UIService.show(MainUI);
}
}
import MainUI from "../../ui/MainUI";
import { PlayerData } from "./PlayerData";
import { PlayerModuleS } from "./PlayerModuleS";
export class PlayerModuleC extends ModuleC<PlayerModuleS, PlayerData> {
protected override onStart(): void {
// 显示 MainUI 这里传递的是我们创建出的 UI 脚本的类
UIService.show(MainUI);
}
}
- PlayerModuleS 脚本中定义 PlayerModuleS 类,继承 ModuleS,服务端的操作放在这儿完成
TypeScript
import HomeHelper from "../../utils/HomeHelper";
import { PlayerData } from "./PlayerData";
import { PlayerModuleC } from "./PlayerModuleC";
export class PlayerModuleS extends ModuleS<PlayerModuleC, PlayerData> {
/**
* 改变金币
* @param pid 要改变金币数量的玩家id
* @param deltaNum 改变的数量
*/
public changeGold(pid: number, deltaNum: number): boolean {
// 获取玩家pid的数据
const data = this.getPlayerData(pid);
// 要改变的值是负数,且钱不够
if (deltaNum < 0 && data.gold < Math.abs(deltaNum)) {
return false;
}
data.changeGold(deltaNum);
return true;
}
}
import HomeHelper from "../../utils/HomeHelper";
import { PlayerData } from "./PlayerData";
import { PlayerModuleC } from "./PlayerModuleC";
export class PlayerModuleS extends ModuleS<PlayerModuleC, PlayerData> {
/**
* 改变金币
* @param pid 要改变金币数量的玩家id
* @param deltaNum 改变的数量
*/
public changeGold(pid: number, deltaNum: number): boolean {
// 获取玩家pid的数据
const data = this.getPlayerData(pid);
// 要改变的值是负数,且钱不够
if (deltaNum < 0 && data.gold < Math.abs(deltaNum)) {
return false;
}
data.changeGold(deltaNum);
return true;
}
}
- 在脚本下新建一个
GameStart
的脚本(游戏所有逻辑的入口)- 定义一个“registerModule”方法,并在 onStart 方法中调用,在其中使用 ModuleManager 将 PlayerModule 注册
TypeScript
import { PlayerData } from "./modules/player/PlayerData";
import { PlayerModuleC } from "./modules/player/PlayerModuleC";
import { PlayerModuleS } from "./modules/player/PlayerModuleS";
@Component
export default class GameStart extends Script {
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
this.registerModule();
}
/** 注册模块 */
protected registerModule() {
ModuleService.registerModule(PlayerModuleS, PlayerModuleC, PlayerData);
}
}
import { PlayerData } from "./modules/player/PlayerData";
import { PlayerModuleC } from "./modules/player/PlayerModuleC";
import { PlayerModuleS } from "./modules/player/PlayerModuleS";
@Component
export default class GameStart extends Script {
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
this.registerModule();
}
/** 注册模块 */
protected registerModule() {
ModuleService.registerModule(PlayerModuleS, PlayerModuleC, PlayerData);
}
}
- 将 GameStart 脚本拖入到对象列表
- 更新 MainUI 的脚本
TypeScript
import { PlayerData } from "../modules/player/PlayerData";
import MainUI_Generate from "../ui-generate/MainUI_generate";
export default class MainUI extends MainUI_Generate {
protected onAwake(): void {
// 初始化金币数量
this.goldTxt.text = DataCenterC.getData(PlayerData).gold + "";
// 监听金币数量改变,及时更新金币数量显示
DataCenterC.getData(PlayerData).onGoldChange.add((goldNum: number) => {
this.goldTxt.text = goldNum + "";
});
}
}
import { PlayerData } from "../modules/player/PlayerData";
import MainUI_Generate from "../ui-generate/MainUI_generate";
export default class MainUI extends MainUI_Generate {
protected onAwake(): void {
// 初始化金币数量
this.goldTxt.text = DataCenterC.getData(PlayerData).gold + "";
// 监听金币数量改变,及时更新金币数量显示
DataCenterC.getData(PlayerData).onGoldChange.add((goldNum: number) => {
this.goldTxt.text = goldNum + "";
});
}
}
- 最后更新 BuildInfo 脚本中的
_unlockBuildFun
方法,需要在金币足够且扣钱成功的前提下才能解锁显示建筑
TIP
但别忘了在文件开头导入 PlayerModuleS 的依赖
typescript
import { PlayerModuleS } from "../modules/player/PlayerModuleS";
import { PlayerModuleS } from "../modules/player/PlayerModuleS";
TypeScript
this._unlockBuildFun = (other: GameObject) => {
// 判断进入的对象是一个Character实例才创建
if (other instanceof Character) {
// 钱购吗
const isGoldEnough = ModuleService.getModule(PlayerModuleS).changeGold(other.player.playerId, -this.unlockPrice);
// 扣钱成功才显示
if (isGoldEnough) {
// 用完了就先取消绑定
trigger.onEnter.remove(this._unlockBuildFun);
// 对象池回收解锁按钮
GameObjPool.despawn(this._unlockBtn);
// 显示这个模型
// this.gameObject.setVisibility(PropertyStatus.On);
this.showBuild();
} else {
console.error("钱不够!");
}
}
}
this._unlockBuildFun = (other: GameObject) => {
// 判断进入的对象是一个Character实例才创建
if (other instanceof Character) {
// 钱购吗
const isGoldEnough = ModuleService.getModule(PlayerModuleS).changeGold(other.player.playerId, -this.unlockPrice);
// 扣钱成功才显示
if (isGoldEnough) {
// 用完了就先取消绑定
trigger.onEnter.remove(this._unlockBuildFun);
// 对象池回收解锁按钮
GameObjPool.despawn(this._unlockBtn);
// 显示这个模型
// this.gameObject.setVisibility(PropertyStatus.On);
this.showBuild();
} else {
console.error("钱不够!");
}
}
}
- 运行游戏查看效果