Skip to content
实现邮箱产出金币功能

实现邮箱产出金币功能

预计阅读时间 15 分钟

本章我们要创建一个邮箱模型,并实现在玩家每次碰到它的时候获得一定的金币逻辑。

前面我们讲了购买建筑的功能,现在玩家还没有能赚取金币得途径,本章节我们要制作一个能持续产出金币的邮箱,它的产出速率固定,产出基数随解锁的建筑增多而增多。

创建邮箱模型

  • 找一个合适的模型(52241)或者自己拼一个,拖入场中合适位置,并调整为适当大小

img

  • 在 ui 下的 prefab 目录中新建"GoldInfo"ui,并进入 UI 设计器界面。
  • 拖入两个文本控件,并分别命名为”goldNumTxt“,”addGoldPerSecTxt“。
  • 将 goldNumTxt 文本控件对象中的文本更改为”0“,并设置字体颜色为金黄色,将文本大小改为 300 * 100。
  • 将 addGoldPerSecTxt 文本控件对象中的文本更改为”1/秒“,并设置字体颜色为绿色,将文本大小改为 300 * 100。
  • 将 Root 的大小改为 300 * 200,保存。

  • 给邮箱创建世界 UI,并绑定 GoldInfo,调整大小和位置

  • 右键信箱,创建一个触发器游戏功能对象

img

  • 在 prefab 脚本目录下新建脚本“MailBox”,并拖入到信箱下

实现“MailBox”脚本客户端逻辑

  • PlayerModuleC 中增加改变金币的方法
TypeScript
/**
 * 客户端改变金币
 * @param deltaNum 要改变的数量
 */
public async changeGold(deltaNum: number): Promise<boolean> {
    return this.server.net_changeGold(deltaNum);
}
/**
 * 客户端改变金币
 * @param deltaNum 要改变的数量
 */
public async changeGold(deltaNum: number): Promise<boolean> {
    return this.server.net_changeGold(deltaNum);
}
  • PlayerModuleS 中增加增加金币的方法,这个方法以 net_ 开头,在 module 中可以支持 RPC 调用。
TypeScript
/**
 * 服务端改变金币
 * @param deltaNum 要改变的数量
 */
public net_changeGold(deltaNum: number):boolean {
    return this.changeGold(this.currentPlayerId, deltaNum);
}
/**
 * 服务端改变金币
 * @param deltaNum 要改变的数量
 */
public net_changeGold(deltaNum: number):boolean {
    return this.changeGold(this.currentPlayerId, deltaNum);
}
  • 在客户端拿到世界 UI 的 targetUI 对象
  • 根据 targetUI 拿到两个文本控件对象
  • 定时器根据基数每秒钟增加金币
  • 已积累的金币数量与增加基数回显
  • 客户端拿到触发器对象
  • 绑定玩家进入事件
  • 玩家进入时,将已积累的金币全部取出
  • 监听金币基数改变的事件
TypeScript
import { PlayerModuleC } from "../modules/player/PlayerModuleC";

@Component
export default class MailBox extends Script {

    /** 当前金币数量  */
    private _goldNum: number = 0;

    /** 金币数量增加基数 */
    private _alterNum: number = 1;

    /** 金币数量文本控件对象 */
    private _goldNumTxt: TextBlock;

    /** 增加金币数量文本控件对象 */
    private _addGoldTxt: TextBlock;

    /** 定时器对象 */
    private inter: any;

    /** 当脚本被实例后,会在第一帧更新前调用此函数 */
    protected onStart(): void {
        // 如果是在服务端,直接退出
        if (SystemUtil.isServer()) return;
        // 异步初始化
        this.init();
    }

    public async init() {
        //  等待这个模型在客户端加载好
        await this.gameObject.asyncReady();
        // 拿到世界UI
        const worldUI = this.gameObject.getChildByName("世界UI") as UIWidget;
        // 拿到targetUI
        const targetUI = worldUI.getTargetUIWidget();
        // 拿到文本控件
        this._goldNumTxt = targetUI.findChildByPath("RootCanvas/goldNumTxt") as TextBlock;
        this._addGoldTxt = targetUI.findChildByPath("RootCanvas/addGoldPerSecTxt") as TextBlock;
        // 初始化文本
        this._goldNumTxt.text = this._goldNum.toString();
        this._addGoldTxt.text = this._alterNum + "/秒";
        // 定时器
        this.inter = setInterval(() => {
            this._goldNum += this._alterNum;
            this._goldNumTxt.text = this._goldNum.toString();
        }, 1000);

        const trigger = this.gameObject.getChildByName("触发器") as Trigger;
        trigger.onEnter.add(() => {
            ModuleService.getModule(PlayerModuleC).changeGold(this._goldNum);
            this._goldNum = 0;
            this._goldNumTxt.text = this._goldNum.toString();
        });

        // 监听金币基数改变的事件
        Event.addServerListener("GoldGrowthRate", (deltaNum: number) => {
            this._alterNum += deltaNum;
            this._addGoldTxt.text = this._alterNum + "/秒";
        })
    }

    protected onDestroy(): void {
        // 销毁时,清除时器
        if (this.inter) {
            clearInterval(this.inter);
            this.inter = null;
        }
    }
}
import { PlayerModuleC } from "../modules/player/PlayerModuleC";

@Component
export default class MailBox extends Script {

    /** 当前金币数量  */
    private _goldNum: number = 0;

    /** 金币数量增加基数 */
    private _alterNum: number = 1;

    /** 金币数量文本控件对象 */
    private _goldNumTxt: TextBlock;

    /** 增加金币数量文本控件对象 */
    private _addGoldTxt: TextBlock;

    /** 定时器对象 */
    private inter: any;

    /** 当脚本被实例后,会在第一帧更新前调用此函数 */
    protected onStart(): void {
        // 如果是在服务端,直接退出
        if (SystemUtil.isServer()) return;
        // 异步初始化
        this.init();
    }

    public async init() {
        //  等待这个模型在客户端加载好
        await this.gameObject.asyncReady();
        // 拿到世界UI
        const worldUI = this.gameObject.getChildByName("世界UI") as UIWidget;
        // 拿到targetUI
        const targetUI = worldUI.getTargetUIWidget();
        // 拿到文本控件
        this._goldNumTxt = targetUI.findChildByPath("RootCanvas/goldNumTxt") as TextBlock;
        this._addGoldTxt = targetUI.findChildByPath("RootCanvas/addGoldPerSecTxt") as TextBlock;
        // 初始化文本
        this._goldNumTxt.text = this._goldNum.toString();
        this._addGoldTxt.text = this._alterNum + "/秒";
        // 定时器
        this.inter = setInterval(() => {
            this._goldNum += this._alterNum;
            this._goldNumTxt.text = this._goldNum.toString();
        }, 1000);

        const trigger = this.gameObject.getChildByName("触发器") as Trigger;
        trigger.onEnter.add(() => {
            ModuleService.getModule(PlayerModuleC).changeGold(this._goldNum);
            this._goldNum = 0;
            this._goldNumTxt.text = this._goldNum.toString();
        });

        // 监听金币基数改变的事件
        Event.addServerListener("GoldGrowthRate", (deltaNum: number) => {
            this._alterNum += deltaNum;
            this._addGoldTxt.text = this._alterNum + "/秒";
        })
    }

    protected onDestroy(): void {
        // 销毁时,清除时器
        if (this.inter) {
            clearInterval(this.inter);
            this.inter = null;
        }
    }
}
  • 最后在 BuildInfo 中增加收益的参数,以及玩家解锁建筑时,为该玩家发送金币增长基数增长的事件
TypeScript
@Property({ group: "基本信息", displayName: "每秒带来的收益" })
public profit: number = 1;


/**
 * 初始化解锁建筑按钮
 */
protected async initUnlockBtn() {
    // 注意这儿spawn的guid是解锁按钮预制体的id,第二个参数指资源类型,这儿因为是预制体的资源所以传递GameObjPoolSourceType.Prefab
    this._unlockBtn = await GameObjPool.asyncSpawn("D442F26A43DED08F57F592B57CC2B56E", GameObjPoolSourceType.Prefab);

    // 初始化所有玩家的世界UI
    this.initWorldUIAllPlayer(this._unlockBtn.guid);

    // 设置按钮的父节点为当前对象
    this._unlockBtn.parent = this.gameObject;
    // 设置按钮的相对位置
    this._unlockBtn.relativeLocation = this.unlockBtnLoc;

    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.showBuild();
                // 金币增长基数
                Event.dispatchToClient(other.player, "GoldGrowthRate", this.profit);
            } else {
                console.error("钱不够!");
            }
        }
    }

    // 拿到解锁按钮预制体下面的触发器
    const trigger = this._unlockBtn.getChildByName("触发器") as Trigger;
    // 绑定触发器的进入事件
    trigger.onEnter.add(this._unlockBuildFun);
}
}
@Property({ group: "基本信息", displayName: "每秒带来的收益" })
public profit: number = 1;


/**
 * 初始化解锁建筑按钮
 */
protected async initUnlockBtn() {
    // 注意这儿spawn的guid是解锁按钮预制体的id,第二个参数指资源类型,这儿因为是预制体的资源所以传递GameObjPoolSourceType.Prefab
    this._unlockBtn = await GameObjPool.asyncSpawn("D442F26A43DED08F57F592B57CC2B56E", GameObjPoolSourceType.Prefab);

    // 初始化所有玩家的世界UI
    this.initWorldUIAllPlayer(this._unlockBtn.guid);

    // 设置按钮的父节点为当前对象
    this._unlockBtn.parent = this.gameObject;
    // 设置按钮的相对位置
    this._unlockBtn.relativeLocation = this.unlockBtnLoc;

    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.showBuild();
                // 金币增长基数
                Event.dispatchToClient(other.player, "GoldGrowthRate", this.profit);
            } else {
                console.error("钱不够!");
            }
        }
    }

    // 拿到解锁按钮预制体下面的触发器
    const trigger = this._unlockBtn.getChildByName("触发器") as Trigger;
    // 绑定触发器的进入事件
    trigger.onEnter.add(this._unlockBuildFun);
}
}
  • 运行游戏,查看效果

完整代码

当前 MailBox 脚本完整代码(点击展开)
typescript
import PlayerModuleC from "../modules/player/PlayerModuleC";

@Component
export default class MailBox extends Script {

    /** 当前金币 */
    private _goldNum: number = 0;

    /** 金币增加基数 */
    private _alterNum: number = 1;

    /** 金币数量文本控件 */
    private _goldNumTxt: TextBlock;

    /** 金币增加基数文本控件 */
    private _addGoldTxt: TextBlock;

    /** 定时器对象 */
    private inter: any;

    protected onStart(): void {
        // 如果是服务端直接退出
        if (SystemUtil.isServer()) return;

        this.init();
    }

    public async init() {
        // 等待邮箱模型加载好
        await this.gameObject.asyncReady();
        // 拿到世界UI逻辑对象
        const worldUI = this.gameObject.getChildByName("世界UI") as UIWidget;
        // 拿到targetUI
        const targetUI = worldUI.getTargetUIWidget();
        // 拿到文本控件
        this._goldNumTxt = targetUI.findChildByPath("RootCanvas/goldNumTxt") as TextBlock;
        this._addGoldTxt = targetUI.findChildByPath("RootCanvas/addGoldPerSecTxt") as TextBlock;
        // 初始化文本控件内容
        this._goldNumTxt.text = this._goldNum.toString();
        this._addGoldTxt.text = this._alterNum + "/秒";
        // 创建定时器
        this.inter = setInterval(() => {
            this._goldNum += this._alterNum;
            this._goldNumTxt.text = this._goldNum.toString();
        }, 1000);

        // 获取触发器 添加进入事件 进入之后获取金币
        const trigger = this.gameObject.getChildByName("触发器") as Trigger;
        trigger.onEnter.add(() => {
            ModuleService.getModule(PlayerModuleC).changeGold(this._goldNum);
            this._goldNum = 0;
            this._goldNumTxt.text = this._goldNum.toString();
        });

        // 监听金币增加基数改变的事件
        Event.addServerListener("GoldGrowthRate", (deltaNum: number) => {
            this._alterNum += deltaNum;
            this._addGoldTxt.text = this._alterNum + "/秒";
        })
    }

    protected onDestroy(): void {
        // 销毁计时器
        if(this.inter){
            clearInterval(this.inter);
            this.inter = null;
        }
    }

}
import PlayerModuleC from "../modules/player/PlayerModuleC";

@Component
export default class MailBox extends Script {

    /** 当前金币 */
    private _goldNum: number = 0;

    /** 金币增加基数 */
    private _alterNum: number = 1;

    /** 金币数量文本控件 */
    private _goldNumTxt: TextBlock;

    /** 金币增加基数文本控件 */
    private _addGoldTxt: TextBlock;

    /** 定时器对象 */
    private inter: any;

    protected onStart(): void {
        // 如果是服务端直接退出
        if (SystemUtil.isServer()) return;

        this.init();
    }

    public async init() {
        // 等待邮箱模型加载好
        await this.gameObject.asyncReady();
        // 拿到世界UI逻辑对象
        const worldUI = this.gameObject.getChildByName("世界UI") as UIWidget;
        // 拿到targetUI
        const targetUI = worldUI.getTargetUIWidget();
        // 拿到文本控件
        this._goldNumTxt = targetUI.findChildByPath("RootCanvas/goldNumTxt") as TextBlock;
        this._addGoldTxt = targetUI.findChildByPath("RootCanvas/addGoldPerSecTxt") as TextBlock;
        // 初始化文本控件内容
        this._goldNumTxt.text = this._goldNum.toString();
        this._addGoldTxt.text = this._alterNum + "/秒";
        // 创建定时器
        this.inter = setInterval(() => {
            this._goldNum += this._alterNum;
            this._goldNumTxt.text = this._goldNum.toString();
        }, 1000);

        // 获取触发器 添加进入事件 进入之后获取金币
        const trigger = this.gameObject.getChildByName("触发器") as Trigger;
        trigger.onEnter.add(() => {
            ModuleService.getModule(PlayerModuleC).changeGold(this._goldNum);
            this._goldNum = 0;
            this._goldNumTxt.text = this._goldNum.toString();
        });

        // 监听金币增加基数改变的事件
        Event.addServerListener("GoldGrowthRate", (deltaNum: number) => {
            this._alterNum += deltaNum;
            this._addGoldTxt.text = this._alterNum + "/秒";
        })
    }

    protected onDestroy(): void {
        // 销毁计时器
        if(this.inter){
            clearInterval(this.inter);
            this.inter = null;
        }
    }

}