cocosCreator版本: 3.7
简介
cocosCreator的工作流程是通过节点–组件来实现的,节点作为场景中的基本单位, 而不同功能的组件挂载在节点之上,来完成页面的搭建。
简单的理解: 场景由多个节点构成,节点作为实体,不同功能的组件挂载到节点上。
更多参考:节点和组件
比如Canvas
节点的组成组件部分:
- UITransform UI变换组件,设置组件的尺寸、锚点以及点击事件相关
- Canvas 画布组件,继承于
RenderRoot2D
组件,作为渲染和数据收集入口 - Widget UI布局组件,用于屏幕适配相关
- mainScene脚本 必须继承于
Component
,才能作为脚本组件添加到编译器中
在3.x中,当添加不同类型的UI组件和对象时,编译器都会默认的增加UITransform
组件。
脚本访问节点及组件
访问当前节点和节点下的组件
访问当前脚本的所在节点,可以使用this.node
来访问, 而获取节点下的组件可以使用getComponent
来访问
更多参考:访问节点和其他组件
// 获取当前节点
let node = this.node;
// 获取当前节点下的其他组件
let button = this.node.getComponent(Button);
let label = this.node.getComponent(Label);
继承于Component的脚本也可以通过getComponent
来获取,获取成功后可以调用脚本中public
属性的方法。
// 直接输入脚本的名字即可, 脚本自动添加import获取脚本的路径相关
// 脚本名也可以使用字符串,比如:"DemoScript",但vsCode代码编译器会提示找不到脚本的对应方法,虽不影响运行,但不推荐
let script = this.node.getComponent(DemoScript);
if (script) {
script:refresh();
}
节点基本属性
在2.x版本中,没有UITransform
的组件, 像大小,锚点相关直接通过Node就可以获取到。
在3.x版本中,官方新增了该组件,在设置大小的同时,也处理触摸事件等机制。
参照着上图,其实可以知道,通过this.node
可以获取位置,旋转,缩放等信息;
// 激活相关,假设为激活状态
// 由true设为false, 生命周期回调为onDisalbe
// 由false设为true, 生命周期回调为onEnable, update, lateUpdate
this.node.active = true;
// 节点是否被销毁或有效
let isValid = this.node.isValid;
// 位置相关
this.node.setPosition(new Vec3(100, 50, 0));
this.node.position = new Vec3(100, 50, 0);
const pos = this.node.position;
this.node.setPosition(pos.x, pos.y, 100);
// 旋转相关
this.node.setRotationFromEuler(90, 0, 0);
// 缩放相关
this.node.setScale(2, 2, 2);
通过UITransform
获取大小,锚点等信息
import { _decorator, Component, Node, UITransform } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Demo')
export class Demo extends Component {
onLoad() {
// 获取UITransform组件
let transform = this.node.getComponent(UITransform);
// 获取节点宽度
let width = transform.width;
// 获取节点锚点
let anchorX = transform.anchorX;
// 通过节点大小包围盒获取大小等
let size = transform.getBoundingBox().size;
}
}
针对于getBoundingBox()
这里稍微说明下,如果物体是静态的,通过它获取大小是没有问题的;
但如果项目中使用了物理引擎,且物体保持旋转,不要使用它,会出现获取大小错误的情况, 简单了解下就行了。
透明度
在cocos2d-x中,透明度的获取在节点中通过 setOpacity
就可以获取到,但在creator中,透明度的获取有两种方式:
- 针对于精灵组件相关,通过颜色的设置就来获取透明度相关
- 针对于非精灵组件,需要通过属性检查器 -> 添加组件 -> 添加UIOpactiy组件来设置透明度
这种的设计方式可能是为了渲染的优化。
层级
在编译器中,层级的设定通过层级管理器会进行默认的排版,但如果在脚本中设定的话,注意:
// 2.x版本, 通过Node下的 zIndex 属性来设置
this.node.zIndex = 10;
// 3.x版本,通过Node下的 setSiblingIndex 方法来设置
this.node.setSiblingIndex(10);
节点的层级修改影响的是子节点在父节点中的顺序,而不是节点的渲染顺序。频繁的调用会带来一些性能损耗,在循环遍历中容易导致子节点未被遍历的情况存在。
let children = this.node.children;
for (let i = 0; i < children.length; i++) {
let zOrder = (i%2 == 0) ? 999 : 0;
children[i].setSiblingIndex(zOrder);
}
层级的动态改变,一般多用于触摸移动事件中。
节点相关
节点的子节点获取,主要接口有:
- getChildByUuid(uuid: string): Node 通过uuid获取子节点
- getChildByName(name: string): Node 通过名称获取子节点
- getChildByPath(path: string): Node 通过路径获取子节点
对于节点的操作主要有:
// 获取子节点的数组相关,不包含子节点的子节点
let children = this.node.children;
for (let i = 0; i < children.length; ++i) {
let child = children[i];
}
// 获取当前节点的子节点
let childNode = this.node.getChildByName("titleText");
// 获取当前节点的子节点的某个组件
let labelComponent = childNode.getComponent(Label);
let transform = childNode.getComponent(UITransform);
// 创建节点,但它不会主动添加到场景中,需要手动添加
let node = new Node("Box");
node.setPostion(0, 0, 0);
director.getScene().addChild(node);
注: 在实际的项目开发中,推荐使用
@property(Node)
获取节点, 而不是使用getChildByName
,原因在于后者必须在代码运行的过程中才能获取
克隆节点,接口使用instantiate
,多用于对预制体Prefab的克隆,放置到列表容器中
import { _decorator, Component, instantiate, Prefab, ScrollView } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('UI_ShopLayer')
export class UI_ShopLayer extends Component {
// 在编译器中声明属性,
@property(Prefab)
itemPrefab: Prefab = null;
@property(ScrollView)
scroll: ScrollView = null;
onLoad() {
// 删除容器的子元素
this.scroll.content.removeAllChildren();
// 遍历进行克隆节点
for (let i = 0; i < 10; ++i) {
let item = instantiate(this.itemPrefab);
// 方式1,通过addChild进行添加
this.scroll.content.addChild(item);
// 方式2,通过parent来设置
item.parent = this.scroll.content;
}
}
}
注: 两种方式从性能角度来考虑,没有明显的却别,本质上调用的底层方法都是一样的。
addChild
直观易懂,node.parent
简洁; 增加子节点都需要注意避免频繁的变更和克隆,这样会出现效率问题
更换节点的父节点相关
// 父节点为parentNode, 子节点为this.node
// 方法1
this.node.parent = parentNode;
// 方法2, 注意removeFromParent的参数要为false, 否则会清空节点上绑定的事件和action等
this.node.removeFromParent(false);
parentNode.addChild(this.node);
销毁节点相关
/*
它并不是立即被移除,而是在当前帧逻辑更新结束后,执行
不要使用removeFromParent,它并不会从内存中释放,引擎内部仍会持有它的数据
如果一个节点不再使用,使用destroy就对了,否则会导致内存泄漏
需要注意:
* 如果逻辑比较复杂,销毁的话,可以通过setTimeout设置毫秒添加下延迟
* 如果使用的是定时器相关,建议增加判定: if (this.node && this.node.isValid) 的判定
* 总之一句话:小心无大错
*/
setTimeout(() => {
this.node.destroy();
}, 1000);
注: 调用
onDestory
根据生命周期回调,先onDisable
后onDestory
;如果是定时器的销毁就放在onDestory
中,因为节点未销毁中还可能会使用
最后祝大家生活愉快!