不会吧,start 函数怎么会被重复执行,你是不是写 update 里了
大佬 催更 萌新学习
不是写到update里,就是start里,我打印了调用堆栈,但是看不懂, 我查了一下,说是我又其他的Node多处引用了ts脚本导致,但是没找到,我抽空把你的教程重新做一次看看到底是哪一步出问题了
Start function is called by Boost [Boost.ts:25:16](file:///Users/kangming/CCYang/assets/Boost.ts)
Call stack: start@http://localhost:7456/scripting/x/chunks/51/510049438992d1a34c87556d49a3f481cee5ec6a.js:68:38 execute/invokeStart<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:42390:16 createInvokeImpl/<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:42205:17 invoke@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:42329:16 startPhase@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:42565:29 tick@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:16668:35 _updateCallback@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:17701:22 updateCallback@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:91108:22 execute/Pacer/this._handleRAF@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:91088:23 FrameRequestCallback*_stTime@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:91132:28 start@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:91112:33 resume@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:17236:103 @http://localhost:7456/preview-app/main.js:1:4370 runSceneImmediate@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:16384:13 @http://localhost:7456/preview-app/main.js:1:4332 execute/loadWithJson/task<.onComplete<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:185849:23 asyncify/</<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:193104:11 callInNextTick/<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:145498:18 handleRAF@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:5935:18 FrameRequestCallback*setTimeoutRAF@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:5938:12 callInNextTick@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:145497:20 asyncify/<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:193100:12 dispatch@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:192463:22 execute/_flow/<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:191345:22 load/<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:190289:7 cb@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:193003:21 onComplete@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:190276:13 dispatch@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:192463:22 execute/_flow/<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:191345:22 onComplete@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:190374:14 dispatch@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:192463:22 execute/_flow/<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:191345:22 load/<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:190289:7 cb@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:193003:21 onComplete@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:190276:13 dispatch@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:192463:22 execute/_flow/<@http://localhost:7456/scripting/engine/bin/.cache/dev/preview/bundled/index.js:191345:2
感谢大佬无私分享
感谢大佬,萌新催更
引入界面管理器 UIManager 的缘由
完成模拟登录后,我们将控制权交给了 Match3Entry,Match3Entry 通过以下方式打开三消界面:
ResManager.getInstance().loadPrefab("Match3BN", "Match3UI", prefab => {
let match3Node = instantiate(prefab);
this.node.addChild(match3Node);
match3Node.getComponent(UITransform).setContentSize(G_VIEW_SIZE.clone());
});
1. 创建 Match3UI 脚本组件并绑定到 Match3UI.prefab 预制体
1.1 这里我们先在 Match3/Script 下创建一个 Match3UI 的脚本组件,用于和预制体绑定:
移除 update 方法,在 start 方法中添加一条控制台打印,内容如下:
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Match3UI')
export class Match3UI extends Component {
start() {
console.log(`主玩法界面`)
}
}
1.2 将该脚本绑定到预制体上
这类操作后续省略,见图:
1.3 运行测试
控制台输出了:“主玩法界面” 的字样,符合预期
2. 打开界面的方式需要重构
之前打开界面的方式其实是不方便的: 开发侧需要先调用 ResManager 加载资源,然后才创建实例,并添加到目标节点。(说白了这种方式是:每次在使用的时候,需要先考虑依赖,然后才能执行目标,对心智的负担是很重的)
我们从需求侧入手思考更直观的调用方式是什么?
我希望的方式凝练出来就是一句话:打开界面!(界面依赖的资源是确定的,请你自动帮我加载好!别让我每次使用的时候调用!)
所以我希望的调用方式应该是这样的:
import { _decorator, Component } from 'cc';
import { UIManager } from '../../fw/ui/UIManager';
import { Match3UI } from './Match3UI';
const { ccclass } = _decorator;
@ccclass('Match3Entry')
export class Match3Entry extends Component {
async init() {
UIManager.getInstance().open(Match3UI)
}
}
因此我们将要引入 UIManager 这个对象(假设其位置在 fw/ui/UIManager.ts),来控制界面的打开操作。这将在下一节实现。
UIManager 初步实现
1. 新建 UIManager
在 assets/fw 下新建文件夹 ui,ui 文件夹下新建 UIManager.ts 文件:
UIManager.ts 中的内容更改为以下内容:
import { instantiate, UITransform } from 'cc';
import { G_VIEW_SIZE } from '../../Boost';
import { ResManager } from '../res/ResManager';
export class UIManager {
private static _instance: UIManager = null!;
/** 获取单例的接口 */
static getInstance() {
if (this._instance === null) {
this._instance = new UIManager();
}
return this._instance;
}
private constructor() {
// 私有化的构造函数
}
open(uiClass: any) {
ResManager.getInstance().loadPrefab("Match3BN", "Match3UI", prefab => {
let match3Node = instantiate(prefab);
this.node.addChild(match3Node);
match3Node.getComponent(UITransform).setContentSize(G_VIEW_SIZE.clone());
});
}
}
这时候,我们面临着几个问题(假设 uiClass 的值是类 Match3UI ):
1、如何根据 Match3UI 获得资源所在的包名 Match3BN 和资源预制体 Match3UI.prefab 的 url ?
2、this.node.addChild(match3Node); 中的 this.node 已经不复存在,我们的这个 this.node 要如何改造?
解决了这 2 个问题,就能够完成 UIManager 的初步设计。
而这些内容,我将在后续跟贴中逐一解决。
UIManager 中的层级管理
我们先解决第二个问题,让程序先运行起来。
界面一般是分层级的。为了让大家有一个直观的印象。先展示一下,我线上《经典休闲益智单机游戏合集》这款产品的界面层级的分类以及运行时各个层级的节点示意图,其中 MainUI 是游戏列表界面:
/** UI 层类型 */
export enum EViewLayer {
/** 场景层 */
Scene,
/** 默认层 */
UI,
/** 引导层 */
Guide,
/** 表现层 */
Anim,
/** 切换层 */
Transform,
/** 转圈层、遮挡层 */
Loading,
/** 模态对话框 */
Modal,
/** 提示 */
Toast,
}
即 UIMananger 需要在画布 Canvas 下新建各个层级的根节点,然后不同的界面显示在不同层中。
比如 MainUI 是要显示在 UI 默认层,也就是 EViewLayer.UI 这一层的。
现在,知道我们要实现的目标,因此让我们改造一下 UIManager 类,它需要初步实现节点的分层逻辑。
1、首先在 fw/ui/ 下创建 EViewLayer.ts 文件,其内容为
/** UI 层类型 */
export enum EViewLayer {
/** 场景层 */
Scene,
/** 默认层 */
UI,
/** 引导层 */
Guide,
/** 表现层 */
Anim,
/** 切换层 */
Transform,
/** 转圈层、遮挡层 */
Loading,
/** 模态对话框 */
Modal,
/** 提示 */
Toast,
}
如图:
2、UIManager 定义初始化接口以及创建各个层级根节点
private m_Canvas: Canvas = null;
private m_Layers: MyLayer[] = []
init(canvas: Canvas) {
this.m_Canvas = canvas;
for (let layer = EViewLayer.Scene, maxLayer = EViewLayer.Toast; layer <= maxLayer; ++layer) {
this.m_Layers.push(new MyLayer(layer, canvas, EViewLayer[layer]));
}
}
其中 MyLayer 定义如下:
class MyLayer {
public readonly node: Node;
constructor(
public readonly layer: EViewLayer,
public readonly canvas: Canvas,
name: string,
) {
const node = this.node = new Node(name);
node.layer = Layers.Enum.UI_2D;
node.addComponent(UITransform);
canvas.node.addChild(node);
}
}
此类写在 UIManager 类上方即可。
其中 Layers.Enum.UI_2D 需要从 ‘cc’ 模块 import.
3、修改 open 方法中的 this.node.addChild(match3Node)
将 open 方法内容修改如下:
open(uiClass: any) {
ResManager.getInstance().loadPrefab("Match3BN", "Match3UI", prefab => {
let match3Node = instantiate(prefab);
this.m_Layers[EViewLayer.UI].node.addChild(match3Node);
match3Node.getComponent(UITransform).setContentSize(G_VIEW_SIZE.clone());
});
}
其中 this.node 被替换成了 this.m_Layers[EViewLayer.UI].node
4、初始化 UIManager 的 Canvas
现在我们只差最后一步,就是调用 init 函数
首先,在 Boost.ts 中,添加一条属性定义:
@property(Canvas) private canvas2d: Canvas = null;
然后在 Boost 的 start 方法中的 this.adapterScreen() 下面添加 UIManager 的初始化调用:
UIManager.getInstance().init(this.canvas2d);
最后,在场景中,选中 Canvas,绑定 canvas2d 属性:
运行后,表现如初!
可以看到分层和界面,都和预期一致!(这里用到了预览插件 preview-template,是从论坛里搜的,大家可以自行搜索)
现在我们先解决了第一个问题:界面所属分层和父节点。第二个问题是资源依赖,我们下一个帖子解决它。
原先了个
最火的是省份地区的排名竞争,现在微信不返回了这块地区信息,大佬有啥不依赖服务器获取省份地区的做法吗
这个你自己研究吧。无非 3 条路:
1、 要么平台渠道(微信)给你 API
2、 要么网络IP或者定位相关 API 。
3、 要么玩家设置选择自己参与竞争的省份地区。
你最好克隆下工程运行,如果还是这样,有可能是你改到引擎了。建议卸载重装。
这节课开始看不懂了,看来得学习一下es6才能跟上后面的课程了
找到原因了,我在Match3UI中添加了boost的脚本导致多出引用了
每日催更!!!
别秀了 大佬快更新新手教程
哈哈,过 2 天。
必须点赞,这是讲解最详细的教程,估计工作很多年的都不知道这个技巧!跟着大佬学习!
mark 大佬大佬
感谢大佬的分享,手把手教学。少有的优质教程。