cocos-enhance-kit/engine/cocos2d/core/CCNode.js
2022-06-25 00:23:03 +08:00

3995 lines
139 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****************************************************************************
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
'use strict';
import { Mat4, Vec2, Vec3, Quat, Trs } from './value-types';
import { approx } from './value-types/utils'
const BaseNode = require('./utils/base-node');
const PrefabHelper = require('./utils/prefab-helper');
const nodeMemPool = require('./utils/trans-pool').NodeMemPool;
const AffineTrans = require('./utils/affine-transform');
const eventManager = require('./event-manager');
const macro = require('./platform/CCMacro');
const js = require('./platform/js');
const Event = require('./event/event');
const EventTarget = require('./event/event-target');
const RenderFlow = require('./renderer/render-flow');
const Flags = cc.Object.Flags;
const Destroying = Flags.Destroying;
const ERR_INVALID_NUMBER = CC_EDITOR && 'The %s is invalid';
const ONE_DEGREE = Math.PI / 180;
var ActionManagerExist = !!cc.ActionManager;
var emptyFunc = function () {};
// getWorldPosition temp var
var _gwpVec3 = new Vec3();
var _gwpQuat = new Quat();
// _invTransformPoint temp var
var _tpVec3a = new Vec3();
var _tpVec3b = new Vec3();
var _tpQuata = new Quat();
var _tpQuatb = new Quat();
// setWorldPosition temp var
var _swpVec3 = new Vec3();
// getWorldScale temp var
var _gwsVec3 = new Vec3();
// setWorldScale temp var
var _swsVec3 = new Vec3();
// getWorldRT temp var
var _gwrtVec3a = new Vec3();
var _gwrtVec3b = new Vec3();
var _gwrtQuata = new Quat();
var _gwrtQuatb = new Quat();
// lookAt temp var
var _laVec3 = new Vec3();
var _laQuat = new Quat();
//up、right、forward temp var
var _urfVec3 = new Vec3();
var _urfQuat = new Quat();
// _hitTest temp var
var _htVec3a = new Vec3();
var _htVec3b = new Vec3();
// getWorldRotation temp var
var _gwrQuat = new Quat();
// setWorldRotation temp var
var _swrQuat = new Quat();
var _quata = new Quat();
var _mat4_temp = cc.mat4();
var _vec3_temp = new Vec3();
const POSITION_ON = 1 << 0;
const SCALE_ON = 1 << 1;
const ROTATION_ON = 1 << 2;
const SIZE_ON = 1 << 3;
const ANCHOR_ON = 1 << 4;
const COLOR_ON = 1 << 5;
let _cachedPool = new js.Pool();
_cachedPool.get = function () {
return this._get() || [];
};
let BuiltinGroupIndex = cc.Enum({
DEBUG: 31
});
/**
* !#en Node's local dirty properties flag
* !#zh Node 的本地属性 dirty 状态位
* @enum Node._LocalDirtyFlag
* @static
* @private
* @namespace Node
*/
var LocalDirtyFlag = cc.Enum({
/**
* !#en Flag for position dirty
* !#zh 位置 dirty 的标记位
* @property {Number} POSITION
* @static
*/
POSITION: 1 << 0,
/**
* !#en Flag for scale dirty
* !#zh 缩放 dirty 的标记位
* @property {Number} SCALE
* @static
*/
SCALE: 1 << 1,
/**
* !#en Flag for rotation dirty
* !#zh 旋转 dirty 的标记位
* @property {Number} ROTATION
* @static
*/
ROTATION: 1 << 2,
/**
* !#en Flag for skew dirty
* !#zh skew dirty 的标记位
* @property {Number} SKEW
* @static
*/
SKEW: 1 << 3,
/**
* !#en Flag for rotation, scale or position dirty
* !#zh 旋转,缩放,或位置 dirty 的标记位
* @property {Number} TRS
* @static
*/
TRS: 1 << 0 | 1 << 1 | 1 << 2,
/**
* !#en Flag for rotation or scale dirty
* !#zh 旋转或缩放 dirty 的标记位
* @property {Number} RS
* @static
*/
RS: 1 << 1 | 1 << 2,
/**
* !#en Flag for rotation, scale, position, skew dirty
* !#zh 旋转,缩放,位置,或斜角 dirty 的标记位
* @property {Number} TRS
* @static
*/
TRSS: 1 << 0 | 1 << 1 | 1 << 2 | 1 << 3,
/**
* !#en Flag for physics position dirty
* !#zh 物理位置 dirty 的标记位
* @property {Number} PHYSICS_POSITION
* @static
*/
PHYSICS_POSITION: 1 << 4,
/**
* !#en Flag for physics scale dirty
* !#zh 物理缩放 dirty 的标记位
* @property {Number} PHYSICS_SCALE
* @static
*/
PHYSICS_SCALE: 1 << 5,
/**
* !#en Flag for physics rotation dirty
* !#zh 物理旋转 dirty 的标记位
* @property {Number} PHYSICS_ROTATION
* @static
*/
PHYSICS_ROTATION: 1 << 6,
/**
* !#en Flag for physics trs dirty
* !#zh 物理位置旋转缩放 dirty 的标记位
* @property {Number} PHYSICS_TRS
* @static
*/
PHYSICS_TRS: 1 << 4 | 1 << 5 | 1 << 6,
/**
* !#en Flag for physics rs dirty
* !#zh 物理旋转缩放 dirty 的标记位
* @property {Number} PHYSICS_RS
* @static
*/
PHYSICS_RS: 1 << 5 | 1 << 6,
/**
* !#en Flag for node and physics position dirty
* !#zh 所有位置 dirty 的标记位
* @property {Number} ALL_POSITION
* @static
*/
ALL_POSITION: 1 << 0 | 1 << 4,
/**
* !#en Flag for node and physics scale dirty
* !#zh 所有缩放 dirty 的标记位
* @property {Number} ALL_SCALE
* @static
*/
ALL_SCALE: 1 << 1 | 1 << 5,
/**
* !#en Flag for node and physics rotation dirty
* !#zh 所有旋转 dirty 的标记位
* @property {Number} ALL_ROTATION
* @static
*/
ALL_ROTATION: 1 << 2 | 1 << 6,
/**
* !#en Flag for node and physics trs dirty
* !#zh 所有trs dirty 的标记位
* @property {Number} ALL_TRS
* @static
*/
ALL_TRS: 1 << 0 | 1 << 1 | 1 << 2 | 1 << 4 | 1 << 5 | 1 << 6,
/**
* !#en Flag for all dirty properties
* !#zh 覆盖所有 dirty 状态的标记位
* @property {Number} ALL
* @static
*/
ALL: 0xffff,
});
/**
* !#en The event type supported by Node
* !#zh Node 支持的事件类型
* @class Node.EventType
* @static
* @namespace Node
*/
// Why EventType defined as class, because the first parameter of Node.on method needs set as 'string' type.
var EventType = cc.Enum({
/**
* !#en The event type for touch start event, you can use its value directly: 'touchstart'
* !#zh 当手指触摸到屏幕时。
* @property {String} TOUCH_START
* @static
*/
TOUCH_START: 'touchstart',
/**
* !#en The event type for touch move event, you can use its value directly: 'touchmove'
* !#zh 当手指在屏幕上移动时。
* @property {String} TOUCH_MOVE
* @static
*/
TOUCH_MOVE: 'touchmove',
/**
* !#en The event type for touch end event, you can use its value directly: 'touchend'
* !#zh 当手指在目标节点区域内离开屏幕时。
* @property {String} TOUCH_END
* @static
*/
TOUCH_END: 'touchend',
/**
* !#en The event type for touch end event, you can use its value directly: 'touchcancel'
* !#zh 当手指在目标节点区域外离开屏幕时。
* @property {String} TOUCH_CANCEL
* @static
*/
TOUCH_CANCEL: 'touchcancel',
/**
* !#en The event type for mouse down events, you can use its value directly: 'mousedown'
* !#zh 当鼠标按下时触发一次。
* @property {String} MOUSE_DOWN
* @static
*/
MOUSE_DOWN: 'mousedown',
/**
* !#en The event type for mouse move events, you can use its value directly: 'mousemove'
* !#zh 当鼠标在目标节点在目标节点区域中移动时,不论是否按下。
* @property {String} MOUSE_MOVE
* @static
*/
MOUSE_MOVE: 'mousemove',
/**
* !#en The event type for mouse enter target events, you can use its value directly: 'mouseenter'
* !#zh 当鼠标移入目标节点区域时,不论是否按下。
* @property {String} MOUSE_ENTER
* @static
*/
MOUSE_ENTER: 'mouseenter',
/**
* !#en The event type for mouse leave target events, you can use its value directly: 'mouseleave'
* !#zh 当鼠标移出目标节点区域时,不论是否按下。
* @property {String} MOUSE_LEAVE
* @static
*/
MOUSE_LEAVE: 'mouseleave',
/**
* !#en The event type for mouse up events, you can use its value directly: 'mouseup'
* !#zh 当鼠标从按下状态松开时触发一次。
* @property {String} MOUSE_UP
* @static
*/
MOUSE_UP: 'mouseup',
/**
* !#en The event type for mouse wheel events, you can use its value directly: 'mousewheel'
* !#zh 当鼠标滚轮滚动时。
* @property {String} MOUSE_WHEEL
* @static
*/
MOUSE_WHEEL: 'mousewheel',
/**
* !#en The event type for position change events.
* Performance note, this event will be triggered every time corresponding properties being changed,
* if the event callback have heavy logic it may have great performance impact, try to avoid such scenario.
* !#zh 当节点位置改变时触发的事件。
* 性能警告:这个事件会在每次对应的属性被修改时触发,如果事件回调损耗较高,有可能对性能有很大的负面影响,请尽量避免这种情况。
* @property {String} POSITION_CHANGED
* @static
*/
POSITION_CHANGED: 'position-changed',
/**
* !#en The event type for rotation change events.
* Performance note, this event will be triggered every time corresponding properties being changed,
* if the event callback have heavy logic it may have great performance impact, try to avoid such scenario.
* !#zh 当节点旋转改变时触发的事件。
* 性能警告:这个事件会在每次对应的属性被修改时触发,如果事件回调损耗较高,有可能对性能有很大的负面影响,请尽量避免这种情况。
* @property {String} ROTATION_CHANGED
* @static
*/
ROTATION_CHANGED: 'rotation-changed',
/**
* !#en The event type for scale change events.
* Performance note, this event will be triggered every time corresponding properties being changed,
* if the event callback have heavy logic it may have great performance impact, try to avoid such scenario.
* !#zh 当节点缩放改变时触发的事件。
* 性能警告:这个事件会在每次对应的属性被修改时触发,如果事件回调损耗较高,有可能对性能有很大的负面影响,请尽量避免这种情况。
* @property {String} SCALE_CHANGED
* @static
*/
SCALE_CHANGED: 'scale-changed',
/**
* !#en The event type for size change events.
* Performance note, this event will be triggered every time corresponding properties being changed,
* if the event callback have heavy logic it may have great performance impact, try to avoid such scenario.
* !#zh 当节点尺寸改变时触发的事件。
* 性能警告:这个事件会在每次对应的属性被修改时触发,如果事件回调损耗较高,有可能对性能有很大的负面影响,请尽量避免这种情况。
* @property {String} SIZE_CHANGED
* @static
*/
SIZE_CHANGED: 'size-changed',
/**
* !#en The event type for anchor point change events.
* Performance note, this event will be triggered every time corresponding properties being changed,
* if the event callback have heavy logic it may have great performance impact, try to avoid such scenario.
* !#zh 当节点锚点改变时触发的事件。
* 性能警告:这个事件会在每次对应的属性被修改时触发,如果事件回调损耗较高,有可能对性能有很大的负面影响,请尽量避免这种情况。
* @property {String} ANCHOR_CHANGED
* @static
*/
ANCHOR_CHANGED: 'anchor-changed',
/**
* !#en The event type for color change events.
* Performance note, this event will be triggered every time corresponding properties being changed,
* if the event callback have heavy logic it may have great performance impact, try to avoid such scenario.
* !#zh 当节点颜色改变时触发的事件。
* 性能警告:这个事件会在每次对应的属性被修改时触发,如果事件回调损耗较高,有可能对性能有很大的负面影响,请尽量避免这种情况。
* @property {String} COLOR_CHANGED
* @static
*/
COLOR_CHANGED: 'color-changed',
/**
* !#en The event type for new child added events.
* !#zh 当新的子节点被添加时触发的事件。
* @property {String} CHILD_ADDED
* @static
*/
CHILD_ADDED: 'child-added',
/**
* !#en The event type for child removed events.
* !#zh 当子节点被移除时触发的事件。
* @property {String} CHILD_REMOVED
* @static
*/
CHILD_REMOVED: 'child-removed',
/**
* !#en The event type for children reorder events.
* !#zh 当子节点顺序改变时触发的事件。
* @property {String} CHILD_REORDER
* @static
*/
CHILD_REORDER: 'child-reorder',
/**
* !#en The event type for node group changed events.
* !#zh 当节点归属群组发生变化时触发的事件。
* @property {String} GROUP_CHANGED
* @static
*/
GROUP_CHANGED: 'group-changed',
/**
* !#en The event type for node's sibling order changed.
* !#zh 当节点在兄弟节点中的顺序发生变化时触发的事件。
* @property {String} SIBLING_ORDER_CHANGED
* @static
*/
SIBLING_ORDER_CHANGED: 'sibling-order-changed',
});
var _touchEvents = [
EventType.TOUCH_START,
EventType.TOUCH_MOVE,
EventType.TOUCH_END,
EventType.TOUCH_CANCEL,
];
var _mouseEvents = [
EventType.MOUSE_DOWN,
EventType.MOUSE_ENTER,
EventType.MOUSE_MOVE,
EventType.MOUSE_LEAVE,
EventType.MOUSE_UP,
EventType.MOUSE_WHEEL,
];
var _skewNeedWarn = true;
var _skewWarn = function (value, node) {
if (value !== 0) {
var nodePath = "";
if (CC_EDITOR) {
var NodeUtils = Editor.require('scene://utils/node');
nodePath = `Node: ${NodeUtils.getNodePath(node)}.`
}
_skewNeedWarn && cc.warn("`cc.Node.skewX/Y` is deprecated since v2.2.1, please use 3D node instead.", nodePath);
!CC_EDITOR && (_skewNeedWarn = false);
}
}
var _currentHovered = null;
var _touchStartHandler = function (touch, event) {
var pos = touch.getLocation();
var node = this.owner;
if (node._hitTest(pos, this)) {
event.type = EventType.TOUCH_START;
event.touch = touch;
event.bubbles = true;
node.dispatchEvent(event);
return true;
}
return false;
};
var _touchMoveHandler = function (touch, event) {
var node = this.owner;
event.type = EventType.TOUCH_MOVE;
event.touch = touch;
event.bubbles = true;
node.dispatchEvent(event);
};
var _touchEndHandler = function (touch, event) {
var pos = touch.getLocation();
var node = this.owner;
if (node._hitTest(pos, this)) {
event.type = EventType.TOUCH_END;
}
else {
event.type = EventType.TOUCH_CANCEL;
}
event.touch = touch;
event.bubbles = true;
node.dispatchEvent(event);
};
var _touchCancelHandler = function (touch, event) {
var pos = touch.getLocation();
var node = this.owner;
event.type = EventType.TOUCH_CANCEL;
event.touch = touch;
event.bubbles = true;
node.dispatchEvent(event);
};
var _mouseDownHandler = function (event) {
var pos = event.getLocation();
var node = this.owner;
if (node._hitTest(pos, this)) {
event.type = EventType.MOUSE_DOWN;
event.bubbles = true;
node.dispatchEvent(event);
}
};
var _mouseMoveHandler = function (event) {
var pos = event.getLocation();
var node = this.owner;
var hit = node._hitTest(pos, this);
if (hit) {
if (!this._previousIn) {
// Fix issue when hover node switched, previous hovered node won't get MOUSE_LEAVE notification
if (_currentHovered && _currentHovered._mouseListener) {
event.type = EventType.MOUSE_LEAVE;
_currentHovered.dispatchEvent(event);
_currentHovered._mouseListener._previousIn = false;
}
_currentHovered = this.owner;
event.type = EventType.MOUSE_ENTER;
node.dispatchEvent(event);
this._previousIn = true;
}
event.type = EventType.MOUSE_MOVE;
event.bubbles = true;
node.dispatchEvent(event);
}
else if (this._previousIn) {
event.type = EventType.MOUSE_LEAVE;
node.dispatchEvent(event);
this._previousIn = false;
_currentHovered = null;
}
else {
// continue dispatching
return;
}
// Event processed, cleanup
event.stopPropagation();
};
var _mouseUpHandler = function (event) {
var pos = event.getLocation();
var node = this.owner;
if (node._hitTest(pos, this)) {
event.type = EventType.MOUSE_UP;
event.bubbles = true;
node.dispatchEvent(event);
event.stopPropagation();
}
};
var _mouseWheelHandler = function (event) {
var pos = event.getLocation();
var node = this.owner;
if (node._hitTest(pos, this)) {
event.type = EventType.MOUSE_WHEEL;
event.bubbles = true;
node.dispatchEvent(event);
event.stopPropagation();
}
};
function _searchComponentsInParent (node, comp) {
if (comp) {
let index = 0;
let list = null;
for (var curr = node; curr && cc.Node.isNode(curr); curr = curr._parent, ++index) {
if (curr.getComponent(comp)) {
let next = {
index: index,
node: curr,
};
if (list) {
list.push(next);
} else {
list = [next];
}
}
}
return list;
}
return null;
}
function _checkListeners (node, events) {
if (!(node._objFlags & Destroying)) {
if (node._bubblingListeners) {
for (let i = 0, l = events.length; i < l; ++i) {
if (node._bubblingListeners.hasEventListener(events[i])) {
return true;
}
}
}
if (node._capturingListeners) {
for (let i = 0, l = events.length; i < l; ++i) {
if (node._capturingListeners.hasEventListener(events[i])) {
return true;
}
}
}
return false;
}
return true;
}
function _doDispatchEvent (owner, event, cachedArray) {
var target, i;
event.target = owner;
// Event.CAPTURING_PHASE
cachedArray.length = 0;
owner._getCapturingTargets(event.type, cachedArray);
// capturing
event.eventPhase = 1;
for (i = cachedArray.length - 1; i >= 0; --i) {
target = cachedArray[i];
if (target._capturingListeners) {
event.currentTarget = target;
// fire event
target._capturingListeners.emit(event.type, event, cachedArray);
// check if propagation stopped
if (event._propagationStopped) {
cachedArray.length = 0;
return;
}
}
}
cachedArray.length = 0;
// Event.AT_TARGET
// checks if destroyed in capturing callbacks
event.eventPhase = 2;
event.currentTarget = owner;
if (owner._capturingListeners) {
owner._capturingListeners.emit(event.type, event);
}
if (!event._propagationImmediateStopped && owner._bubblingListeners) {
owner._bubblingListeners.emit(event.type, event);
}
if (!event._propagationStopped && event.bubbles) {
// Event.BUBBLING_PHASE
owner._getBubblingTargets(event.type, cachedArray);
// propagate
event.eventPhase = 3;
for (i = 0; i < cachedArray.length; ++i) {
target = cachedArray[i];
if (target._bubblingListeners) {
event.currentTarget = target;
// fire event
target._bubblingListeners.emit(event.type, event);
// check if propagation stopped
if (event._propagationStopped) {
cachedArray.length = 0;
return;
}
}
}
}
cachedArray.length = 0;
}
// traversal the node tree, child cullingMask must keep the same with the parent.
function _getActualGroupIndex (node) {
let groupIndex = node.groupIndex;
if (groupIndex === 0 && node.parent) {
groupIndex = _getActualGroupIndex(node.parent);
}
return groupIndex;
}
function _updateCullingMask (node) {
let index = _getActualGroupIndex(node);
node._cullingMask = 1 << index;
if (CC_JSB && CC_NATIVERENDERER) {
node._proxy && node._proxy.updateCullingMask();
}
for (let i = 0; i < node._children.length; i++) {
_updateCullingMask(node._children[i]);
}
}
// 2D/3D matrix functions
function updateLocalMatrix3D () {
if (this._localMatDirty & LocalDirtyFlag.TRSS) {
// Update transform
let t = this._matrix;
let tm = t.m;
Trs.toMat4(t, this._trs);
// skew
if (this._skewX || this._skewY) {
let a = tm[0], b = tm[1], c = tm[4], d = tm[5];
let skx = Math.tan(this._skewX * ONE_DEGREE);
let sky = Math.tan(this._skewY * ONE_DEGREE);
if (skx === Infinity)
skx = 99999999;
if (sky === Infinity)
sky = 99999999;
tm[0] = a + c * sky;
tm[1] = b + d * sky;
tm[4] = c + a * skx;
tm[5] = d + b * skx;
}
this._localMatDirty &= ~LocalDirtyFlag.TRSS;
// Register dirty status of world matrix so that it can be recalculated
this._worldMatDirty = true;
}
}
function updateLocalMatrix2D () {
let dirtyFlag = this._localMatDirty;
if (!(dirtyFlag & LocalDirtyFlag.TRSS)) return;
// Update transform
let t = this._matrix;
let tm = t.m;
let trs = this._trs;
if (dirtyFlag & (LocalDirtyFlag.RS | LocalDirtyFlag.SKEW)) {
let rotation = -this._eulerAngles.z;
let hasSkew = this._skewX || this._skewY;
let sx = trs[7], sy = trs[8];
if (rotation || hasSkew) {
let a = 1, b = 0, c = 0, d = 1;
// rotation
if (rotation) {
let rotationRadians = rotation * ONE_DEGREE;
c = Math.sin(rotationRadians);
d = Math.cos(rotationRadians);
a = d;
b = -c;
}
// scale
tm[0] = a *= sx;
tm[1] = b *= sx;
tm[4] = c *= sy;
tm[5] = d *= sy;
// skew
if (hasSkew) {
let a = tm[0], b = tm[1], c = tm[4], d = tm[5];
let skx = Math.tan(this._skewX * ONE_DEGREE);
let sky = Math.tan(this._skewY * ONE_DEGREE);
if (skx === Infinity)
skx = 99999999;
if (sky === Infinity)
sky = 99999999;
tm[0] = a + c * sky;
tm[1] = b + d * sky;
tm[4] = c + a * skx;
tm[5] = d + b * skx;
}
}
else {
tm[0] = sx;
tm[1] = 0;
tm[4] = 0;
tm[5] = sy;
}
}
// position
tm[12] = trs[0];
tm[13] = trs[1];
this._localMatDirty &= ~LocalDirtyFlag.TRSS;
// Register dirty status of world matrix so that it can be recalculated
this._worldMatDirty = true;
}
function calculWorldMatrix3D () {
// Avoid as much function call as possible
if (this._localMatDirty & LocalDirtyFlag.TRSS) {
this._updateLocalMatrix();
}
if (this._parent) {
let parentMat = this._parent._worldMatrix;
Mat4.mul(this._worldMatrix, parentMat, this._matrix);
}
else {
Mat4.copy(this._worldMatrix, this._matrix);
}
this._worldMatDirty = false;
}
function calculWorldMatrix2D () {
// Avoid as much function call as possible
if (this._localMatDirty & LocalDirtyFlag.TRSS) {
this._updateLocalMatrix();
}
// Assume parent world matrix is correct
let parent = this._parent;
if (parent) {
this._mulMat(this._worldMatrix, parent._worldMatrix, this._matrix);
}
else {
Mat4.copy(this._worldMatrix, this._matrix);
}
this._worldMatDirty = false;
}
function mulMat2D (out, a, b) {
let am = a.m, bm = b.m, outm = out.m;
let aa=am[0], ab=am[1], ac=am[4], ad=am[5], atx=am[12], aty=am[13];
let ba=bm[0], bb=bm[1], bc=bm[4], bd=bm[5], btx=bm[12], bty=bm[13];
if (ab !== 0 || ac !== 0) {
outm[0] = ba * aa + bb * ac;
outm[1] = ba * ab + bb * ad;
outm[4] = bc * aa + bd * ac;
outm[5] = bc * ab + bd * ad;
outm[12] = aa * btx + ac * bty + atx;
outm[13] = ab * btx + ad * bty + aty;
}
else {
outm[0] = ba * aa;
outm[1] = bb * ad;
outm[4] = bc * aa;
outm[5] = bd * ad;
outm[12] = aa * btx + atx;
outm[13] = ad * bty + aty;
}
}
const mulMat3D = Mat4.mul;
/**
* !#en
* Class of all entities in Cocos Creator scenes.<br/>
* For events supported by Node, please refer to {{#crossLink "Node.EventType"}}{{/crossLink}}
* !#zh
* Cocos Creator 场景中的所有节点类。<br/>
* 支持的节点事件,请参阅 {{#crossLink "Node.EventType"}}{{/crossLink}}。
* @class Node
* @extends _BaseNode
*/
let NodeDefines = {
name: 'cc.Node',
extends: BaseNode,
properties: {
// SERIALIZABLE
_opacity: 255,
_color: cc.Color.WHITE,
_contentSize: cc.Size,
_anchorPoint: cc.v2(0.5, 0.5),
_position: undefined,
_scale: undefined,
_trs: null,
_eulerAngles: cc.Vec3,
_skewX: 0.0,
_skewY: 0.0,
_zIndex: {
default: undefined,
type: cc.Integer
},
_localZOrder: {
default: 0,
serializable: false
},
_is3DNode: false,
// internal properties
/**
* !#en
* Group index of node.<br/>
* Which Group this node belongs to will resolve that this node's collision components can collide with which other collision componentns.<br/>
* !#zh
* 节点的分组索引。<br/>
* 节点的分组将关系到节点的碰撞组件可以与哪些碰撞组件相碰撞。<br/>
* @property groupIndex
* @type {Integer}
* @default 0
*/
_groupIndex: {
default: 0,
formerlySerializedAs: 'groupIndex'
},
groupIndex: {
get () {
return this._groupIndex;
},
set (value) {
this._groupIndex = value;
_updateCullingMask(this);
this.emit(EventType.GROUP_CHANGED, this);
}
},
/**
* !#en
* Group of node.<br/>
* Which Group this node belongs to will resolve that this node's collision components can collide with which other collision componentns.<br/>
* !#zh
* 节点的分组。<br/>
* 节点的分组将关系到节点的碰撞组件可以与哪些碰撞组件相碰撞。<br/>
* @property group
* @type {String}
*/
group: {
get () {
return cc.game.groupList[this.groupIndex] || '';
},
set (value) {
// update the groupIndex
this.groupIndex = cc.game.groupList.indexOf(value);
}
},
//properties moved from base node begin
/**
* !#en The position (x, y) of the node in its parent's coordinates.
* !#zh 节点在父节点坐标系中的位置x, y
* @property {Vec3} position
* @example
* cc.log("Node Position: " + node.position);
*/
/**
* !#en x axis position of node.
* !#zh 节点 X 轴坐标。
* @property x
* @type {Number}
* @example
* node.x = 100;
* cc.log("Node Position X: " + node.x);
*/
x: {
get () {
return this._trs[0];
},
set (value) {
let trs = this._trs;
if (value !== trs[0]) {
if (!CC_EDITOR || isFinite(value)) {
let oldValue;
if (CC_EDITOR) {
oldValue = trs[0];
}
trs[0] = value;
this.setLocalDirty(LocalDirtyFlag.ALL_POSITION);
// fast check event
if (this._eventMask & POSITION_ON) {
// send event
if (CC_EDITOR) {
this.emit(EventType.POSITION_CHANGED, new cc.Vec3(oldValue, trs[1], trs[2]));
}
else {
this.emit(EventType.POSITION_CHANGED);
}
}
}
else {
cc.error(ERR_INVALID_NUMBER, 'new x');
}
}
},
},
/**
* !#en y axis position of node.
* !#zh 节点 Y 轴坐标。
* @property y
* @type {Number}
* @example
* node.y = 100;
* cc.log("Node Position Y: " + node.y);
*/
y: {
get () {
return this._trs[1];
},
set (value) {
let trs = this._trs;
if (value !== trs[1]) {
if (!CC_EDITOR || isFinite(value)) {
let oldValue;
if (CC_EDITOR) {
oldValue = trs[1];
}
trs[1] = value;
this.setLocalDirty(LocalDirtyFlag.ALL_POSITION);
// fast check event
if (this._eventMask & POSITION_ON) {
// send event
if (CC_EDITOR) {
this.emit(EventType.POSITION_CHANGED, new cc.Vec3(trs[0], oldValue, trs[2]));
}
else {
this.emit(EventType.POSITION_CHANGED);
}
}
}
else {
cc.error(ERR_INVALID_NUMBER, 'new y');
}
}
},
},
/**
* !#en z axis position of node.
* !#zh 节点 Z 轴坐标。
* @property z
* @type {Number}
*/
z: {
get () {
return this._trs[2];
},
set (value) {
let trs = this._trs;
if (value !== trs[2]) {
if (!CC_EDITOR || isFinite(value)) {
let oldValue;
if (CC_EDITOR) {
oldValue = trs[2];
}
trs[2] = value;
this.setLocalDirty(LocalDirtyFlag.ALL_POSITION);
!CC_NATIVERENDERER && (this._renderFlag |= RenderFlow.FLAG_WORLD_TRANSFORM);
// fast check event
if (this._eventMask & POSITION_ON) {
if (CC_EDITOR) {
this.emit(EventType.POSITION_CHANGED, new cc.Vec3(trs[0], trs[1], oldValue));
}
else {
this.emit(EventType.POSITION_CHANGED);
}
}
}
else {
cc.error(ERR_INVALID_NUMBER, 'new z');
}
}
}
},
/**
* !#en Rotation of node.
* !#zh 该节点旋转角度。
* @property rotation
* @type {Number}
* @deprecated since v2.1
* @example
* node.rotation = 90;
* cc.log("Node Rotation: " + node.rotation);
*/
rotation: {
get () {
if (CC_DEBUG) {
cc.warn("`cc.Node.rotation` is deprecated since v2.1.0, please use `-angle` instead. (`this.node.rotation` -> `-this.node.angle`)");
}
return -this.angle;
},
set (value) {
if (CC_DEBUG) {
cc.warn("`cc.Node.rotation` is deprecated since v2.1.0, please set `-angle` instead. (`this.node.rotation = x` -> `this.node.angle = -x`)");
}
this.angle = -value;
}
},
/**
* !#en
* Angle of node, the positive value is anti-clockwise direction.
* !#zh
* 该节点的旋转角度,正值为逆时针方向。
* @property angle
* @type {Number}
*/
angle: {
get () {
return this._eulerAngles.z;
},
set (value) {
Vec3.set(this._eulerAngles, 0, 0, value);
Trs.fromAngleZ(this._trs, value);
this.setLocalDirty(LocalDirtyFlag.ALL_ROTATION);
if (this._eventMask & ROTATION_ON) {
this.emit(EventType.ROTATION_CHANGED);
}
}
},
/**
* !#en The rotation as Euler angles in degrees, used in 3D node.
* !#zh 该节点的欧拉角度,用于 3D 节点。
* @property eulerAngles
* @type {Vec3}
* @example
* node.is3DNode = true;
* node.eulerAngles = cc.v3(45, 45, 45);
* cc.log("Node eulerAngles (X, Y, Z): " + node.eulerAngles.toString());
*/
/**
* !#en Rotation on x axis.
* !#zh 该节点 X 轴旋转角度。
* @property rotationX
* @type {Number}
* @deprecated since v2.1
* @example
* node.is3DNode = true;
* node.eulerAngles = cc.v3(45, 0, 0);
* cc.log("Node eulerAngles X: " + node.eulerAngles.x);
*/
rotationX: {
get () {
if (CC_DEBUG) {
cc.warn("`cc.Node.rotationX` is deprecated since v2.1.0, please use `eulerAngles.x` instead. (`this.node.rotationX` -> `this.node.eulerAngles.x`)");
}
return this._eulerAngles.x;
},
set (value) {
if (CC_DEBUG) {
cc.warn("`cc.Node.rotationX` is deprecated since v2.1.0, please set `eulerAngles` instead. (`this.node.rotationX = x` -> `this.node.is3DNode = true; this.node.eulerAngles = cc.v3(x, 0, 0)`");
}
if (this._eulerAngles.x !== value) {
this._eulerAngles.x = value;
// Update quaternion from rotation
if (this._eulerAngles.x === this._eulerAngles.y) {
Trs.fromAngleZ(this._trs, -value);
}
else {
Trs.fromEulerNumber(this._trs, value, this._eulerAngles.y, 0);
}
this.setLocalDirty(LocalDirtyFlag.ALL_ROTATION);
if (this._eventMask & ROTATION_ON) {
this.emit(EventType.ROTATION_CHANGED);
}
}
},
},
/**
* !#en Rotation on y axis.
* !#zh 该节点 Y 轴旋转角度。
* @property rotationY
* @type {Number}
* @deprecated since v2.1
* @example
* node.is3DNode = true;
* node.eulerAngles = cc.v3(0, 45, 0);
* cc.log("Node eulerAngles Y: " + node.eulerAngles.y);
*/
rotationY: {
get () {
if (CC_DEBUG) {
cc.warn("`cc.Node.rotationY` is deprecated since v2.1.0, please use `eulerAngles.y` instead. (`this.node.rotationY` -> `this.node.eulerAngles.y`)");
}
return this._eulerAngles.y;
},
set (value) {
if (CC_DEBUG) {
cc.warn("`cc.Node.rotationY` is deprecated since v2.1.0, please set `eulerAngles` instead. (`this.node.rotationY = y` -> `this.node.is3DNode = true; this.node.eulerAngles = cc.v3(0, y, 0)`");
}
if (this._eulerAngles.y !== value) {
this._eulerAngles.y = value;
// Update quaternion from rotation
if (this._eulerAngles.x === this._eulerAngles.y) {
Trs.fromAngleZ(this._trs, -value);
}
else {
Trs.fromEulerNumber(this._trs, this._eulerAngles.x, value, 0);
}
this.setLocalDirty(LocalDirtyFlag.ALL_ROTATION);
if (this._eventMask & ROTATION_ON) {
this.emit(EventType.ROTATION_CHANGED);
}
}
},
},
eulerAngles: {
get () {
if (CC_EDITOR) {
return this._eulerAngles;
}
else {
return Trs.toEuler(this._eulerAngles, this._trs);
}
}, set (v) {
if (CC_EDITOR) {
this._eulerAngles.set(v);
}
Trs.fromEuler(this._trs, v);
this.setLocalDirty(LocalDirtyFlag.ALL_ROTATION);
!CC_NATIVERENDERER && (this._renderFlag |= RenderFlow.FLAG_TRANSFORM);
if (this._eventMask & ROTATION_ON) {
this.emit(EventType.ROTATION_CHANGED);
}
}
},
// This property is used for Mesh Skeleton Animation
// Should be removed when node.rotation upgrade to quaternion value
quat: {
get () {
let trs = this._trs;
return new Quat(trs[3], trs[4], trs[5], trs[6]);
}, set (v) {
this.setRotation(v);
}
},
/**
* !#en The local scale relative to the parent.
* !#zh 节点相对父节点的缩放。
* @property scale
* @type {Number}
* @example
* node.scale = 1;
*/
scale: {
get () {
return this._trs[7];
},
set (v) {
this.setScale(v);
}
},
/**
* !#en Scale on x axis.
* !#zh 节点 X 轴缩放。
* @property scaleX
* @type {Number}
* @example
* node.scaleX = 0.5;
* cc.log("Node Scale X: " + node.scaleX);
*/
scaleX: {
get () {
return this._trs[7];
},
set (value) {
if (this._trs[7] !== value) {
this._trs[7] = value;
this.setLocalDirty(LocalDirtyFlag.ALL_SCALE);
if (this._eventMask & SCALE_ON) {
this.emit(EventType.SCALE_CHANGED);
}
}
},
},
/**
* !#en Scale on y axis.
* !#zh 节点 Y 轴缩放。
* @property scaleY
* @type {Number}
* @example
* node.scaleY = 0.5;
* cc.log("Node Scale Y: " + node.scaleY);
*/
scaleY: {
get () {
return this._trs[8];
},
set (value) {
if (this._trs[8] !== value) {
this._trs[8] = value;
this.setLocalDirty(LocalDirtyFlag.ALL_SCALE);
if (this._eventMask & SCALE_ON) {
this.emit(EventType.SCALE_CHANGED);
}
}
},
},
/**
* !#en Scale on z axis.
* !#zh 节点 Z 轴缩放。
* @property scaleZ
* @type {Number}
*/
scaleZ: {
get () {
return this._trs[9];
},
set (value) {
if (this._trs[9] !== value) {
this._trs[9] = value;
this.setLocalDirty(LocalDirtyFlag.ALL_SCALE);
!CC_NATIVERENDERER && (this._renderFlag |= RenderFlow.FLAG_TRANSFORM);
if (this._eventMask & SCALE_ON) {
this.emit(EventType.SCALE_CHANGED);
}
}
}
},
/**
* !#en Skew x
* !#zh 该节点 X 轴倾斜角度。
* @property skewX
* @type {Number}
* @example
* node.skewX = 0;
* cc.log("Node SkewX: " + node.skewX);
* @deprecated since v2.2.1
*/
skewX: {
get () {
return this._skewX;
},
set (value) {
_skewWarn(value, this);
this._skewX = value;
this.setLocalDirty(LocalDirtyFlag.SKEW);
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy.updateSkew();
}
}
},
/**
* !#en Skew y
* !#zh 该节点 Y 轴倾斜角度。
* @property skewY
* @type {Number}
* @example
* node.skewY = 0;
* cc.log("Node SkewY: " + node.skewY);
* @deprecated since v2.2.1
*/
skewY: {
get () {
return this._skewY;
},
set (value) {
_skewWarn(value, this);
this._skewY = value;
this.setLocalDirty(LocalDirtyFlag.SKEW);
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy.updateSkew();
}
}
},
/**
* !#en Opacity of node, default value is 255.
* !#zh 节点透明度,默认值为 255。
* @property opacity
* @type {Number}
* @example
* node.opacity = 255;
*/
opacity: {
get () {
return this._opacity;
},
set (value) {
value = cc.misc.clampf(value, 0, 255);
if (this._opacity !== value) {
this._opacity = value;
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy.updateOpacity();
}
this._renderFlag |= RenderFlow.FLAG_OPACITY_COLOR;
}
},
range: [0, 255]
},
/**
* !#en Color of node, default value is white: (255, 255, 255).
* !#zh 节点颜色。默认为白色数值为255255255
* @property color
* @type {Color}
* @example
* node.color = new cc.Color(255, 255, 255);
*/
color: {
get () {
return this._color.clone()
},
set (value) {
if (!this._color.equals(value)) {
this._color.set(value);
if (CC_DEV && value.a !== 255) {
cc.warnID(1626);
}
this._renderFlag |= RenderFlow.FLAG_COLOR;
if (this._eventMask & COLOR_ON) {
this.emit(EventType.COLOR_CHANGED, value);
}
}
},
},
/**
* !#en Anchor point's position on x axis.
* !#zh 节点 X 轴锚点位置。
* @property anchorX
* @type {Number}
* @example
* node.anchorX = 0;
*/
anchorX: {
get () {
return this._anchorPoint.x;
},
set (value) {
var anchorPoint = this._anchorPoint;
if (anchorPoint.x !== value) {
anchorPoint.x = value;
if (this._eventMask & ANCHOR_ON) {
this.emit(EventType.ANCHOR_CHANGED);
}
}
},
},
/**
* !#en Anchor point's position on y axis.
* !#zh 节点 Y 轴锚点位置。
* @property anchorY
* @type {Number}
* @example
* node.anchorY = 0;
*/
anchorY: {
get () {
return this._anchorPoint.y;
},
set (value) {
var anchorPoint = this._anchorPoint;
if (anchorPoint.y !== value) {
anchorPoint.y = value;
if (this._eventMask & ANCHOR_ON) {
this.emit(EventType.ANCHOR_CHANGED);
}
}
},
},
/**
* !#en Width of node.
* !#zh 节点宽度。
* @property width
* @type {Number}
* @example
* node.width = 100;
*/
width: {
get () {
return this._contentSize.width;
},
set (value) {
if (value !== this._contentSize.width) {
if (CC_EDITOR) {
var clone = cc.size(this._contentSize.width, this._contentSize.height);
}
this._contentSize.width = value;
if (this._eventMask & SIZE_ON) {
if (CC_EDITOR) {
this.emit(EventType.SIZE_CHANGED, clone);
}
else {
this.emit(EventType.SIZE_CHANGED);
}
}
}
},
},
/**
* !#en Height of node.
* !#zh 节点高度。
* @property height
* @type {Number}
* @example
* node.height = 100;
*/
height: {
get () {
return this._contentSize.height;
},
set (value) {
if (value !== this._contentSize.height) {
if (CC_EDITOR) {
var clone = cc.size(this._contentSize.width, this._contentSize.height);
}
this._contentSize.height = value;
if (this._eventMask & SIZE_ON) {
if (CC_EDITOR) {
this.emit(EventType.SIZE_CHANGED, clone);
}
else {
this.emit(EventType.SIZE_CHANGED);
}
}
}
},
},
/**
* !#en zIndex is the 'key' used to sort the node relative to its siblings.<br/>
* The value of zIndex should be in the range between cc.macro.MIN_ZINDEX and cc.macro.MAX_ZINDEX.<br/>
* The Node's parent will sort all its children based on the zIndex value and the arrival order.<br/>
* Nodes with greater zIndex will be sorted after nodes with smaller zIndex.<br/>
* If two nodes have the same zIndex, then the node that was added first to the children's array will be in front of the other node in the array.<br/>
* Node's order in children list will affect its rendering order. Parent is always rendering before all children.
* !#zh zIndex 是用来对节点进行排序的关键属性,它决定一个节点在兄弟节点之间的位置。<br/>
* zIndex 的取值应该介于 cc.macro.MIN_ZINDEX 和 cc.macro.MAX_ZINDEX 之间
* 父节点主要根据节点的 zIndex 和添加次序来排序,拥有更高 zIndex 的节点将被排在后面,如果两个节点的 zIndex 一致,先添加的节点会稳定排在另一个节点之前。<br/>
* 节点在 children 中的顺序决定了其渲染顺序。父节点永远在所有子节点之前被渲染
* @property zIndex
* @type {Number}
* @example
* node.zIndex = 1;
* cc.log("Node zIndex: " + node.zIndex);
*/
zIndex: {
get () {
return this._localZOrder >> 16;
},
set (value) {
if (value > macro.MAX_ZINDEX) {
cc.warnID(1636);
value = macro.MAX_ZINDEX;
}
else if (value < macro.MIN_ZINDEX) {
cc.warnID(1637);
value = macro.MIN_ZINDEX;
}
if (this.zIndex !== value) {
this._localZOrder = (this._localZOrder & 0x0000ffff) | (value << 16);
this.emit(EventType.SIBLING_ORDER_CHANGED);
this._onSiblingIndexChanged();
}
}
},
/**
* !#en
* Switch 2D/3D node. The 2D nodes will run faster.
* !#zh
* 切换 2D/3D 节点2D 节点会有更高的运行效率
* @property {Boolean} is3DNode
* @default false
*/
is3DNode: {
get () {
return this._is3DNode;
}, set (v) {
if (this._is3DNode === v) {
return;
}
this._is3DNode = v;
this._update3DFunction();
}
},
/**
* !#en Returns a normalized vector representing the up direction (Y axis) of the node in world space.
* !#zh 获取节点正上方y 轴)面对的方向,返回值为世界坐标系下的归一化向量
*
* @property up
* @type {Vec3}
*/
up: {
get () {
var _up = Vec3.transformQuat(_urfVec3, Vec3.UP, this.getWorldRotation(_urfQuat));
return _up.clone();
}
},
/**
* !#en Returns a normalized vector representing the right direction (X axis) of the node in world space.
* !#zh 获取节点正右方x 轴)面对的方向,返回值为世界坐标系下的归一化向量
*
* @property right
* @type {Vec3}
*/
right: {
get () {
var _right = Vec3.transformQuat(_urfVec3, Vec3.RIGHT, this.getWorldRotation(_urfQuat));
return _right.clone();
}
},
/**
* !#en Returns a normalized vector representing the forward direction (Z axis) of the node in world space.
* !#zh 获取节点正前方z 轴)面对的方向,返回值为世界坐标系下的归一化向量
*
* @property forward
* @type {Vec3}
*/
forward: {
get () {
var _forward = Vec3.transformQuat(_urfVec3, Vec3.FORWARD, this.getWorldRotation(_urfQuat));
return _forward.clone();
}
},
},
/**
* @method constructor
* @param {String} [name]
*/
ctor () {
this._reorderChildDirty = false;
// cache component
this._widget = null;
// fast render component access
this._renderComponent = null;
// Event listeners
this._capturingListeners = null;
this._bubblingListeners = null;
// Touch event listener
this._touchListener = null;
// Mouse event listener
this._mouseListener = null;
this._initDataFromPool();
this._eventMask = 0;
this._cullingMask = 1;
this._childArrivalOrder = 1;
// Proxy
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy = new renderer.NodeProxy(this._spaceInfo.unitID, this._spaceInfo.index, this._id, this._name);
this._proxy.init(this);
}
// should reset _renderFlag for both web and native
this._renderFlag = RenderFlow.FLAG_TRANSFORM | RenderFlow.FLAG_OPACITY_COLOR;
},
statics: {
EventType,
_LocalDirtyFlag: LocalDirtyFlag,
// is node but not scene
isNode (obj) {
return obj instanceof Node && (obj.constructor === Node || !(obj instanceof cc.Scene));
},
BuiltinGroupIndex
},
// OVERRIDES
_onSiblingIndexChanged () {
// update rendering scene graph, sort them by arrivalOrder
if (this._parent) {
this._parent._delaySort();
}
},
_onPreDestroy () {
var destroyByParent = this._onPreDestroyBase();
// Actions
if (ActionManagerExist) {
cc.director.getActionManager().removeAllActionsFromTarget(this);
}
// Remove Node.currentHovered
if (_currentHovered === this) {
_currentHovered = null;
}
this._bubblingListeners && this._bubblingListeners.clear();
this._capturingListeners && this._capturingListeners.clear();
// Remove all event listeners if necessary
if (this._touchListener || this._mouseListener) {
eventManager.removeListeners(this);
if (this._touchListener) {
this._touchListener.owner = null;
this._touchListener.mask = null;
this._touchListener = null;
}
if (this._mouseListener) {
this._mouseListener.owner = null;
this._mouseListener.mask = null;
this._mouseListener = null;
}
}
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy.destroy();
this._proxy = null;
}
this._backDataIntoPool();
if (this._reorderChildDirty) {
cc.director.__fastOff(cc.Director.EVENT_AFTER_UPDATE, this.sortAllChildren, this);
}
if (!destroyByParent) {
// simulate some destruct logic to make undo system work correctly
if (CC_EDITOR) {
// ensure this node can reattach to scene by undo system
this._parent = null;
}
}
},
_onPostActivated (active) {
var actionManager = ActionManagerExist ? cc.director.getActionManager() : null;
if (active) {
// Refresh transform
this._renderFlag |= RenderFlow.FLAG_WORLD_TRANSFORM;
// ActionManager & EventManager
actionManager && actionManager.resumeTarget(this);
eventManager.resumeTarget(this);
// Search Mask in parent
this._checkListenerMask();
} else {
// deactivate
actionManager && actionManager.pauseTarget(this);
eventManager.pauseTarget(this);
}
},
_onHierarchyChanged (oldParent) {
this._updateOrderOfArrival();
// Fixed a bug where children and parent node groups were forced to synchronize, instead of only synchronizing `_cullingMask` value
_updateCullingMask(this);
if (this._parent) {
this._parent._delaySort();
}
this._renderFlag |= RenderFlow.FLAG_WORLD_TRANSFORM | RenderFlow.FLAG_OPACITY_COLOR;
this._onHierarchyChangedBase(oldParent);
if (cc._widgetManager) {
cc._widgetManager._nodesOrderDirty = true;
}
if (oldParent && this._activeInHierarchy) {
//TODO: It may be necessary to update the listener mask of all child nodes.
this._checkListenerMask();
}
// Node proxy
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy.updateParent();
}
},
// INTERNAL
_update3DFunction () {
if (this._is3DNode) {
this._updateLocalMatrix = updateLocalMatrix3D;
this._calculWorldMatrix = calculWorldMatrix3D;
this._mulMat = mulMat3D;
}
else {
this._updateLocalMatrix = updateLocalMatrix2D;
this._calculWorldMatrix = calculWorldMatrix2D;
this._mulMat = mulMat2D;
}
if (this._renderComponent && this._renderComponent._on3DNodeChanged) {
this._renderComponent._on3DNodeChanged();
}
this._renderFlag |= RenderFlow.FLAG_TRANSFORM;
this._localMatDirty = LocalDirtyFlag.ALL;
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy.update3DNode();
}
},
_initDataFromPool () {
if (!this._spaceInfo) {
if (CC_EDITOR || CC_TEST) {
this._spaceInfo = {
trs: new Float64Array(10),
localMat: new Float64Array(16),
worldMat: new Float64Array(16),
};
} else {
this._spaceInfo = nodeMemPool.pop();
}
}
let spaceInfo = this._spaceInfo;
this._matrix = cc.mat4(spaceInfo.localMat);
Mat4.identity(this._matrix);
this._worldMatrix = cc.mat4(spaceInfo.worldMat);
Mat4.identity(this._worldMatrix);
this._localMatDirty = LocalDirtyFlag.ALL;
this._worldMatDirty = true;
let trs = this._trs = spaceInfo.trs;
trs[0] = 0; // position.x
trs[1] = 0; // position.y
trs[2] = 0; // position.z
trs[3] = 0; // rotation.x
trs[4] = 0; // rotation.y
trs[5] = 0; // rotation.z
trs[6] = 1; // rotation.w
trs[7] = 1; // scale.x
trs[8] = 1; // scale.y
trs[9] = 1; // scale.z
},
_backDataIntoPool () {
if (!(CC_EDITOR || CC_TEST)) {
// push back to pool
nodeMemPool.push(this._spaceInfo);
this._matrix = null;
this._worldMatrix = null;
this._trs = null;
this._spaceInfo = null;
}
},
_toEuler () {
if (this.is3DNode) {
Trs.toEuler(this._eulerAngles, this._trs);
}
else {
let z = Math.asin(this._trs[5]) / ONE_DEGREE * 2;
Vec3.set(this._eulerAngles, 0, 0, z);
}
},
_fromEuler () {
if (this.is3DNode) {
Trs.fromEuler(this._trs, this._eulerAngles);
}
else {
Trs.fromAngleZ(this._trs, this._eulerAngles.z);
}
},
_initProperties () {
if (this._is3DNode) {
this._update3DFunction();
}
let trs = this._trs;
if (trs) {
let desTrs = trs;
trs = this._trs = this._spaceInfo.trs;
// just adapt to old trs
if (desTrs.length === 11) {
trs.set(desTrs.subarray(1));
} else {
trs.set(desTrs);
}
} else {
trs = this._trs = this._spaceInfo.trs;
}
if (CC_EDITOR) {
if (this._skewX !== 0 || this._skewY !== 0) {
var NodeUtils = Editor.require('scene://utils/node');
cc.warn("`cc.Node.skewX/Y` is deprecated since v2.2.1, please use 3D node instead.", `Node: ${NodeUtils.getNodePath(this)}.`);
}
}
this._fromEuler();
if (CC_JSB && CC_NATIVERENDERER) {
this._renderFlag |= RenderFlow.FLAG_TRANSFORM | RenderFlow.FLAG_OPACITY_COLOR;
}
},
/*
* The initializer for Node which will be called before all components onLoad
*/
_onBatchCreated (dontSyncChildPrefab) {
this._initProperties();
// Fixed a bug where children and parent node groups were forced to synchronize, instead of only synchronizing `_cullingMask` value
this._cullingMask = 1 << _getActualGroupIndex(this);
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy && this._proxy.updateCullingMask();
}
if (!this._activeInHierarchy) {
if (CC_EDITOR ? cc.director.getActionManager() : ActionManagerExist) {
// deactivate ActionManager and EventManager by default
cc.director.getActionManager().pauseTarget(this);
}
eventManager.pauseTarget(this);
}
let children = this._children;
for (let i = 0, len = children.length; i < len; i++) {
let child = children[i];
if (!dontSyncChildPrefab) {
// sync child prefab
let prefabInfo = child._prefab;
if (prefabInfo && prefabInfo.sync && prefabInfo.root === child) {
PrefabHelper.syncWithPrefab(child);
}
child._updateOrderOfArrival();
}
child._onBatchCreated(dontSyncChildPrefab);
}
if (children.length > 0) {
this._renderFlag |= RenderFlow.FLAG_CHILDREN;
}
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy.initNative();
}
},
// EVENT TARGET
_checkListenerMask () {
// Because Mask may be nested, need to find all the Mask components in the parent node.
// The click area must satisfy all Masks to trigger the click.
if (this._touchListener) {
var mask = this._touchListener.mask = _searchComponentsInParent(this, cc.Mask);
if (this._mouseListener) {
this._mouseListener.mask = mask;
}
} else if (this._mouseListener) {
this._mouseListener.mask = _searchComponentsInParent(this, cc.Mask);
}
},
_checknSetupSysEvent (type) {
let newAdded = false;
let forDispatch = false;
if (_touchEvents.indexOf(type) !== -1) {
if (!this._touchListener) {
this._touchListener = cc.EventListener.create({
event: cc.EventListener.TOUCH_ONE_BY_ONE,
swallowTouches: true,
owner: this,
mask: _searchComponentsInParent(this, cc.Mask),
onTouchBegan: _touchStartHandler,
onTouchMoved: _touchMoveHandler,
onTouchEnded: _touchEndHandler,
onTouchCancelled: _touchCancelHandler
});
eventManager.addListener(this._touchListener, this);
newAdded = true;
}
forDispatch = true;
}
else if (_mouseEvents.indexOf(type) !== -1) {
if (!this._mouseListener) {
this._mouseListener = cc.EventListener.create({
event: cc.EventListener.MOUSE,
_previousIn: false,
owner: this,
mask: _searchComponentsInParent(this, cc.Mask),
onMouseDown: _mouseDownHandler,
onMouseMove: _mouseMoveHandler,
onMouseUp: _mouseUpHandler,
onMouseScroll: _mouseWheelHandler,
});
eventManager.addListener(this._mouseListener, this);
newAdded = true;
}
forDispatch = true;
}
if (newAdded && !this._activeInHierarchy) {
cc.director.getScheduler().schedule(function () {
if (!this._activeInHierarchy) {
eventManager.pauseTarget(this);
}
}, this, 0, 0, 0, false);
}
return forDispatch;
},
/**
* !#en
* Register a callback of a specific event type on Node.<br/>
* Use this method to register touch or mouse event permit propagation based on scene graph,<br/>
* These kinds of event are triggered with dispatchEvent, the dispatch process has three steps:<br/>
* 1. Capturing phase: dispatch in capture targets (`_getCapturingTargets`), e.g. parents in node tree, from root to the real target<br/>
* 2. At target phase: dispatch to the listeners of the real target<br/>
* 3. Bubbling phase: dispatch in bubble targets (`_getBubblingTargets`), e.g. parents in node tree, from the real target to root<br/>
* In any moment of the dispatching process, it can be stopped via `event.stopPropagation()` or `event.stopPropagationImmidiate()`.<br/>
* It's the recommended way to register touch/mouse event for Node,<br/>
* please do not use cc.eventManager directly for Node.<br/>
* You can also register custom event and use `emit` to trigger custom event on Node.<br/>
* For such events, there won't be capturing and bubbling phase, your event will be dispatched directly to its listeners registered on the same node.<br/>
* You can also pass event callback parameters with `emit` by passing parameters after `type`.
* !#zh
* 在节点上注册指定类型的回调函数,也可以设置 target 用于绑定响应函数的 this 对象。<br/>
* 鼠标或触摸事件会被系统调用 dispatchEvent 方法触发,触发的过程包含三个阶段:<br/>
* 1. 捕获阶段:派发事件给捕获目标(通过 `_getCapturingTargets` 获取),比如,节点树中注册了捕获阶段的父节点,从根节点开始派发直到目标节点。<br/>
* 2. 目标阶段:派发给目标节点的监听器。<br/>
* 3. 冒泡阶段:派发事件给冒泡目标(通过 `_getBubblingTargets` 获取),比如,节点树中注册了冒泡阶段的父节点,从目标节点开始派发直到根节点。<br/>
* 同时您可以将事件派发到父节点或者通过调用 stopPropagation 拦截它。<br/>
* 推荐使用这种方式来监听节点上的触摸或鼠标事件,请不要在节点上直接使用 cc.eventManager。<br/>
* 你也可以注册自定义事件到节点上,并通过 emit 方法触发此类事件,对于这类事件,不会发生捕获冒泡阶段,只会直接派发给注册在该节点上的监听器<br/>
* 你可以通过在 emit 方法调用时在 type 之后传递额外的参数作为事件回调的参数列表
* @method on
* @param {String|Node.EventType} type - A string representing the event type to listen for.<br>See {{#crossLink "Node/EventTyupe/POSITION_CHANGED"}}Node Events{{/crossLink}} for all builtin events.
* @param {Function} callback - The callback that will be invoked when the event is dispatched. The callback is ignored if it is a duplicate (the callbacks are unique).
* @param {Event|any} [callback.event] event or first argument when emit
* @param {any} [callback.arg2] arg2
* @param {any} [callback.arg3] arg3
* @param {any} [callback.arg4] arg4
* @param {any} [callback.arg5] arg5
* @param {Object} [target] - The target (this object) to invoke the callback, can be null
* @param {Boolean} [useCapture=false] - When set to true, the listener will be triggered at capturing phase which is ahead of the final target emit, otherwise it will be triggered during bubbling phase.
* @return {Function} - Just returns the incoming callback so you can save the anonymous function easier.
* @typescript
* on<T extends Function>(type: string, callback: T, target?: any, useCapture?: boolean): T
* @example
* this.node.on(cc.Node.EventType.TOUCH_START, this.memberFunction, this); // if "this" is component and the "memberFunction" declared in CCClass.
* node.on(cc.Node.EventType.TOUCH_START, callback, this);
* node.on(cc.Node.EventType.TOUCH_MOVE, callback, this);
* node.on(cc.Node.EventType.TOUCH_END, callback, this);
* node.on(cc.Node.EventType.TOUCH_CANCEL, callback, this);
* node.on(cc.Node.EventType.ANCHOR_CHANGED, callback);
* node.on(cc.Node.EventType.COLOR_CHANGED, callback);
*/
on (type, callback, target, useCapture) {
let forDispatch = this._checknSetupSysEvent(type);
if (forDispatch) {
return this._onDispatch(type, callback, target, useCapture);
}
else {
switch (type) {
case EventType.POSITION_CHANGED:
this._eventMask |= POSITION_ON;
break;
case EventType.SCALE_CHANGED:
this._eventMask |= SCALE_ON;
break;
case EventType.ROTATION_CHANGED:
this._eventMask |= ROTATION_ON;
break;
case EventType.SIZE_CHANGED:
this._eventMask |= SIZE_ON;
break;
case EventType.ANCHOR_CHANGED:
this._eventMask |= ANCHOR_ON;
break;
case EventType.COLOR_CHANGED:
this._eventMask |= COLOR_ON;
break;
}
if (!this._bubblingListeners) {
this._bubblingListeners = new EventTarget();
}
return this._bubblingListeners.on(type, callback, target);
}
},
/**
* !#en
* Register an callback of a specific event type on the Node,
* the callback will remove itself after the first time it is triggered.
* !#zh
* 注册节点的特定事件类型回调,回调会在第一时间被触发后删除自身。
*
* @method once
* @param {String} type - A string representing the event type to listen for.
* @param {Function} callback - The callback that will be invoked when the event is dispatched.
* The callback is ignored if it is a duplicate (the callbacks are unique).
* @param {Event|any} [callback.event] event or first argument when emit
* @param {any} [callback.arg2] arg2
* @param {any} [callback.arg3] arg3
* @param {any} [callback.arg4] arg4
* @param {any} [callback.arg5] arg5
* @param {Object} [target] - The target (this object) to invoke the callback, can be null
* @typescript
* once<T extends Function>(type: string, callback: T, target?: any, useCapture?: boolean): T
* @example
* node.once(cc.Node.EventType.ANCHOR_CHANGED, callback);
*/
once (type, callback, target, useCapture) {
let forDispatch = this._checknSetupSysEvent(type);
let listeners = null;
if (forDispatch && useCapture) {
listeners = this._capturingListeners = this._capturingListeners || new EventTarget();
}
else {
listeners = this._bubblingListeners = this._bubblingListeners || new EventTarget();
}
listeners.once(type, callback, target);
listeners.once(type, () => {
this.off(type, callback, target);
}, undefined);
},
_onDispatch (type, callback, target, useCapture) {
// Accept also patameters like: (type, callback, useCapture)
if (typeof target === 'boolean') {
useCapture = target;
target = undefined;
}
else useCapture = !!useCapture;
if (!callback) {
cc.errorID(6800);
return;
}
var listeners = null;
if (useCapture) {
listeners = this._capturingListeners = this._capturingListeners || new EventTarget();
}
else {
listeners = this._bubblingListeners = this._bubblingListeners || new EventTarget();
}
if ( !listeners.hasEventListener(type, callback, target) ) {
listeners.on(type, callback, target);
if (target && target.__eventTargets) {
target.__eventTargets.push(this);
}
}
return callback;
},
/**
* !#en
* Removes the callback previously registered with the same type, callback, target and or useCapture.
* This method is merely an alias to removeEventListener.
* !#zh 删除之前与同类型,回调,目标或 useCapture 注册的回调。
* @method off
* @param {String} type - A string representing the event type being removed.
* @param {Function} [callback] - The callback to remove.
* @param {Object} [target] - The target (this object) to invoke the callback, if it's not given, only callback without target will be removed
* @param {Boolean} [useCapture=false] - When set to true, the listener will be triggered at capturing phase which is ahead of the final target emit, otherwise it will be triggered during bubbling phase.
* @example
* this.node.off(cc.Node.EventType.TOUCH_START, this.memberFunction, this);
* node.off(cc.Node.EventType.TOUCH_START, callback, this.node);
* node.off(cc.Node.EventType.ANCHOR_CHANGED, callback, this);
*/
off (type, callback, target, useCapture) {
let touchEvent = _touchEvents.indexOf(type) !== -1;
let mouseEvent = !touchEvent && _mouseEvents.indexOf(type) !== -1;
if (touchEvent || mouseEvent) {
this._offDispatch(type, callback, target, useCapture);
if (touchEvent) {
if (this._touchListener && !_checkListeners(this, _touchEvents)) {
eventManager.removeListener(this._touchListener);
this._touchListener = null;
}
}
else if (mouseEvent) {
if (this._mouseListener && !_checkListeners(this, _mouseEvents)) {
eventManager.removeListener(this._mouseListener);
this._mouseListener = null;
}
}
}
else if (this._bubblingListeners) {
this._bubblingListeners.off(type, callback, target);
var hasListeners = this._bubblingListeners.hasEventListener(type);
// All listener removed
if (!hasListeners) {
switch (type) {
case EventType.POSITION_CHANGED:
this._eventMask &= ~POSITION_ON;
break;
case EventType.SCALE_CHANGED:
this._eventMask &= ~SCALE_ON;
break;
case EventType.ROTATION_CHANGED:
this._eventMask &= ~ROTATION_ON;
break;
case EventType.SIZE_CHANGED:
this._eventMask &= ~SIZE_ON;
break;
case EventType.ANCHOR_CHANGED:
this._eventMask &= ~ANCHOR_ON;
break;
case EventType.COLOR_CHANGED:
this._eventMask &= ~COLOR_ON;
break;
}
}
}
},
_offDispatch (type, callback, target, useCapture) {
// Accept also patameters like: (type, callback, useCapture)
if (typeof target === 'boolean') {
useCapture = target;
target = undefined;
}
else useCapture = !!useCapture;
if (!callback) {
this._capturingListeners && this._capturingListeners.removeAll(type);
this._bubblingListeners && this._bubblingListeners.removeAll(type);
}
else {
var listeners = useCapture ? this._capturingListeners : this._bubblingListeners;
if (listeners) {
listeners.off(type, callback, target);
if (target && target.__eventTargets) {
js.array.fastRemove(target.__eventTargets, this);
}
}
}
},
/**
* !#en Removes all callbacks previously registered with the same target.
* !#zh 移除目标上的所有注册事件。
* @method targetOff
* @param {Object} target - The target to be searched for all related callbacks
* @example
* node.targetOff(target);
*/
targetOff (target) {
let listeners = this._bubblingListeners;
if (listeners) {
listeners.targetOff(target);
// Check for event mask reset
if ((this._eventMask & POSITION_ON) && !listeners.hasEventListener(EventType.POSITION_CHANGED)) {
this._eventMask &= ~POSITION_ON;
}
if ((this._eventMask & SCALE_ON) && !listeners.hasEventListener(EventType.SCALE_CHANGED)) {
this._eventMask &= ~SCALE_ON;
}
if ((this._eventMask & ROTATION_ON) && !listeners.hasEventListener(EventType.ROTATION_CHANGED)) {
this._eventMask &= ~ROTATION_ON;
}
if ((this._eventMask & SIZE_ON) && !listeners.hasEventListener(EventType.SIZE_CHANGED)) {
this._eventMask &= ~SIZE_ON;
}
if ((this._eventMask & ANCHOR_ON) && !listeners.hasEventListener(EventType.ANCHOR_CHANGED)) {
this._eventMask &= ~ANCHOR_ON;
}
if ((this._eventMask & COLOR_ON) && !listeners.hasEventListener(EventType.COLOR_CHANGED)) {
this._eventMask &= ~COLOR_ON;
}
}
if (this._capturingListeners) {
this._capturingListeners.targetOff(target);
}
if (target && target.__eventTargets) {
js.array.fastRemove(target.__eventTargets, this);
}
if (this._touchListener && !_checkListeners(this, _touchEvents)) {
eventManager.removeListener(this._touchListener);
this._touchListener = null;
}
if (this._mouseListener && !_checkListeners(this, _mouseEvents)) {
eventManager.removeListener(this._mouseListener);
this._mouseListener = null;
}
},
/**
* !#en Checks whether the EventTarget object has any callback registered for a specific type of event.
* !#zh 检查事件目标对象是否有为特定类型的事件注册的回调。
* @method hasEventListener
* @param {String} type - The type of event.
* @return {Boolean} True if a callback of the specified type is registered; false otherwise.
*/
hasEventListener (type) {
let has = false;
if (this._bubblingListeners) {
has = this._bubblingListeners.hasEventListener(type);
}
if (!has && this._capturingListeners) {
has = this._capturingListeners.hasEventListener(type);
}
return has;
},
/**
* !#en
* Trigger an event directly with the event name and necessary arguments.
* !#zh
* 通过事件名发送自定义事件
*
* @method emit
* @param {String} type - event type
* @param {*} [arg1] - First argument in callback
* @param {*} [arg2] - Second argument in callback
* @param {*} [arg3] - Third argument in callback
* @param {*} [arg4] - Fourth argument in callback
* @param {*} [arg5] - Fifth argument in callback
* @example
*
* eventTarget.emit('fire', event);
* eventTarget.emit('fire', message, emitter);
*/
emit (type, arg1, arg2, arg3, arg4, arg5) {
if (this._bubblingListeners) {
this._bubblingListeners.emit(type, arg1, arg2, arg3, arg4, arg5);
}
},
/**
* !#en
* Dispatches an event into the event flow.
* The event target is the EventTarget object upon which the dispatchEvent() method is called.
* !#zh 分发事件到事件流中。
*
* @method dispatchEvent
* @param {Event} event - The Event object that is dispatched into the event flow
*/
dispatchEvent (event) {
var _array = _cachedPool.get();
_doDispatchEvent(this, event, _array);
_cachedPool.put(_array);
},
/**
* !#en Pause node related system events registered with the current Node. Node system events includes touch and mouse events.
* If recursive is set to true, then this API will pause the node system events for the node and all nodes in its sub node tree.
* Reference: http://docs.cocos2d-x.org/editors_and_tools/creator-chapters/scripting/internal-events/
* !#zh 暂停当前节点上注册的所有节点系统事件,节点系统事件包含触摸和鼠标事件。
* 如果传递 recursive 为 true那么这个 API 将暂停本节点和它的子树上所有节点的节点系统事件。
* 参考https://www.cocos.com/docs/creator/scripting/internal-events.html
* @method pauseSystemEvents
* @param {Boolean} recursive - Whether to pause node system events on the sub node tree.
* @example
* node.pauseSystemEvents(true);
*/
pauseSystemEvents (recursive) {
eventManager.pauseTarget(this, recursive);
},
/**
* !#en Resume node related system events registered with the current Node. Node system events includes touch and mouse events.
* If recursive is set to true, then this API will resume the node system events for the node and all nodes in its sub node tree.
* Reference: http://docs.cocos2d-x.org/editors_and_tools/creator-chapters/scripting/internal-events/
* !#zh 恢复当前节点上注册的所有节点系统事件,节点系统事件包含触摸和鼠标事件。
* 如果传递 recursive 为 true那么这个 API 将恢复本节点和它的子树上所有节点的节点系统事件。
* 参考https://www.cocos.com/docs/creator/scripting/internal-events.html
* @method resumeSystemEvents
* @param {Boolean} recursive - Whether to resume node system events on the sub node tree.
* @example
* node.resumeSystemEvents(true);
*/
resumeSystemEvents (recursive) {
eventManager.resumeTarget(this, recursive);
},
_hitTest (point, listener) {
let w = this._contentSize.width,
h = this._contentSize.height,
cameraPt = _htVec3a,
testPt = _htVec3b;
let camera = cc.Camera.findCamera(this);
if (camera) {
camera.getScreenToWorldPoint(point, cameraPt);
}
else {
cameraPt.set(point);
}
this._updateWorldMatrix();
// If scale is 0, it can't be hit.
if (!Mat4.invert(_mat4_temp, this._worldMatrix)) {
return false;
}
Vec2.transformMat4(testPt, cameraPt, _mat4_temp);
testPt.x += this._anchorPoint.x * w;
testPt.y += this._anchorPoint.y * h;
let hit = false;
if (testPt.x >= 0 && testPt.y >= 0 && testPt.x <= w && testPt.y <= h) {
hit = true;
if (listener && listener.mask) {
let mask = listener.mask;
let parent = this;
let length = mask ? mask.length : 0;
// find mask parent, should hit test it
for (let i = 0, j = 0; parent && j < length; ++i, parent = parent.parent) {
let temp = mask[j];
if (i === temp.index) {
if (parent === temp.node) {
let comp = parent.getComponent(cc.Mask);
if (comp && comp._enabled && !comp._hitTest(cameraPt)) {
hit = false;
break
}
j++;
} else {
// mask parent no longer exists
mask.length = j;
break
}
} else if (i > temp.index) {
// mask parent no longer exists
mask.length = j;
break
}
}
}
}
return hit;
},
/**
* Get all the targets listening to the supplied type of event in the target's capturing phase.
* The capturing phase comprises the journey from the root to the last node BEFORE the event target's node.
* The result should save in the array parameter, and MUST SORT from child nodes to parent nodes.
*
* Subclasses can override this method to make event propagable.
* @method _getCapturingTargets
* @private
* @param {String} type - the event type
* @param {Array} array - the array to receive targets
* @example {@link cocos2d/core/event/_getCapturingTargets.js}
*/
_getCapturingTargets (type, array) {
var parent = this.parent;
while (parent) {
if (parent._capturingListeners && parent._capturingListeners.hasEventListener(type)) {
array.push(parent);
}
parent = parent.parent;
}
},
/**
* Get all the targets listening to the supplied type of event in the target's bubbling phase.
* The bubbling phase comprises any SUBSEQUENT nodes encountered on the return trip to the root of the tree.
* The result should save in the array parameter, and MUST SORT from child nodes to parent nodes.
*
* Subclasses can override this method to make event propagable.
* @method _getBubblingTargets
* @private
* @param {String} type - the event type
* @param {Array} array - the array to receive targets
*/
_getBubblingTargets (type, array) {
var parent = this.parent;
while (parent) {
if (parent._bubblingListeners && parent._bubblingListeners.hasEventListener(type)) {
array.push(parent);
}
parent = parent.parent;
}
},
// ACTIONS
/**
* !#en
* Executes an action, and returns the action that is executed.<br/>
* The node becomes the action's target. Refer to cc.Action's getTarget() <br/>
* Calling runAction while the node is not active won't have any effect. <br/>
* NoteYou shouldn't modify the action after runAction, that won't take any effect.<br/>
* if you want to modify, when you define action plus.
* !#zh
* 执行并返回该执行的动作。该节点将会变成动作的目标。<br/>
* 调用 runAction 时,节点自身处于不激活状态将不会有任何效果。<br/>
* 注意:你不应该修改 runAction 后的动作,将无法发挥作用,如果想进行修改,请在定义 action 时加入。
* @method runAction
* @param {Action} action
* @return {Action} An Action pointer
* @example
* var action = cc.scaleTo(0.2, 1, 0.6);
* node.runAction(action);
* node.runAction(action).repeatForever(); // fail
* node.runAction(action.repeatForever()); // right
*/
runAction: ActionManagerExist ? function (action) {
if (!this.active)
return;
cc.assertID(action, 1618);
let am = cc.director.getActionManager();
if (!am._suppressDeprecation) {
am._suppressDeprecation = true;
cc.warnID(1639);
}
am.addAction(action, this, false);
return action;
} : emptyFunc,
/**
* !#en Pause all actions running on the current node. Equals to `cc.director.getActionManager().pauseTarget(node)`.
* !#zh 暂停本节点上所有正在运行的动作。和 `cc.director.getActionManager().pauseTarget(node);` 等价。
* @method pauseAllActions
* @example
* node.pauseAllActions();
*/
pauseAllActions: ActionManagerExist ? function () {
cc.director.getActionManager().pauseTarget(this);
} : emptyFunc,
/**
* !#en Resume all paused actions on the current node. Equals to `cc.director.getActionManager().resumeTarget(node)`.
* !#zh 恢复运行本节点上所有暂停的动作。和 `cc.director.getActionManager().resumeTarget(node);` 等价。
* @method resumeAllActions
* @example
* node.resumeAllActions();
*/
resumeAllActions: ActionManagerExist ? function () {
cc.director.getActionManager().resumeTarget(this);
} : emptyFunc,
/**
* !#en Stops and removes all actions from the running action list .
* !#zh 停止并且移除所有正在运行的动作列表。
* @method stopAllActions
* @example
* node.stopAllActions();
*/
stopAllActions: ActionManagerExist ? function () {
cc.director.getActionManager().removeAllActionsFromTarget(this);
} : emptyFunc,
/**
* !#en Stops and removes an action from the running action list.
* !#zh 停止并移除指定的动作。
* @method stopAction
* @param {Action} action An action object to be removed.
* @example
* var action = cc.scaleTo(0.2, 1, 0.6);
* node.stopAction(action);
*/
stopAction: ActionManagerExist ? function (action) {
cc.director.getActionManager().removeAction(action);
} : emptyFunc,
/**
* !#en Removes an action from the running action list by its tag.
* !#zh 停止并且移除指定标签的动作。
* @method stopActionByTag
* @param {Number} tag A tag that indicates the action to be removed.
* @example
* node.stopActionByTag(1);
*/
stopActionByTag: ActionManagerExist ? function (tag) {
if (tag === cc.Action.TAG_INVALID) {
cc.logID(1612);
return;
}
cc.director.getActionManager().removeActionByTag(tag, this);
} : emptyFunc,
/**
* !#en Returns an action from the running action list by its tag.
* !#zh 通过标签获取指定动作。
* @method getActionByTag
* @see cc.Action#getTag and cc.Action#setTag
* @param {Number} tag
* @return {Action} The action object with the given tag.
* @example
* var action = node.getActionByTag(1);
*/
getActionByTag: ActionManagerExist ? function (tag) {
if (tag === cc.Action.TAG_INVALID) {
cc.logID(1613);
return null;
}
return cc.director.getActionManager().getActionByTag(tag, this);
} : function () {
return null;
},
/**
* !#en
* Returns the numbers of actions that are running plus the ones that are schedule to run (actions in actionsToAdd and actions arrays).<br/>
* Composable actions are counted as 1 action. Example:<br/>
* If you are running 1 Sequence of 7 actions, it will return 1. <br/>
* If you are running 7 Sequences of 2 actions, it will return 7.</p>
* !#zh
* 获取运行着的动作加上正在调度运行的动作的总数。<br/>
* 例如:<br/>
* - 如果你正在运行 7 个动作中的 1 个 Sequence它将返回 1。<br/>
* - 如果你正在运行 2 个动作中的 7 个 Sequence它将返回 7。<br/>
*
* @method getNumberOfRunningActions
* @return {Number} The number of actions that are running plus the ones that are schedule to run
* @example
* var count = node.getNumberOfRunningActions();
* cc.log("Running Action Count: " + count);
*/
getNumberOfRunningActions: ActionManagerExist ? function () {
return cc.director.getActionManager().getNumberOfRunningActionsInTarget(this);
} : function () {
return 0;
},
// TRANSFORM RELATED
/**
* !#en
* Returns a copy of the position (x, y, z) of the node in its parent's coordinates.
* You can pass a cc.Vec2 or cc.Vec3 as the argument to receive the return values.
* !#zh
* 获取节点在父节点坐标系中的位置x, y, z
* 你可以传一个 cc.Vec2 或者 cc.Vec3 作为参数来接收返回值。
* @method getPosition
* @param {Vec2|Vec3} [out] - The return value to receive position
* @return {Vec2|Vec3} The position (x, y, z) of the node in its parent's coordinates
* @example
* cc.log("Node Position: " + node.getPosition());
*/
getPosition (out) {
out = out || new Vec3();
return Trs.toPosition(out, this._trs);
},
/**
* !#en
* Sets the position (x, y, z) of the node in its parent's coordinates.<br/>
* Usually we use cc.v2(x, y) to compose cc.Vec2 object, in this case, position.z will become 0.<br/>
* and passing two numbers (x, y) is more efficient than passing cc.Vec2 object, in this case, position.z will remain unchanged.
* For 3D node we can use cc.v3(x, y, z) to compose cc.Vec3 object,<br/>
* and passing three numbers (x, y, z) is more efficient than passing cc.Vec3 object.
* !#zh
* 设置节点在父节点坐标系中的位置。<br/>
* 可以通过下面的方式设置坐标点:<br/>
* 1. 传入 2 个数值 x, y (此时不会改变 position.z 的值)。<br/>
* 2. 传入 cc.v2(x, y) 类型为 cc.Vec2 的对象 (此时 position.z 的值将被设置为0)。
* 3. 对于 3D 节点可以传入 3 个数值 x, y, z。<br/>
* 4. 对于 3D 节点可以传入 cc.v3(x, y, z) 类型为 cc.Vec3 的对象。
* @method setPosition
* @param {Vec2|Vec3|Number} x - X coordinate for position or the position object
* @param {Number} [y] - Y coordinate for position
* @param {Number} [z] - Z coordinate for position
*/
setPosition (newPosOrX, y, z) {
let x;
if (y === undefined) {
x = newPosOrX.x;
y = newPosOrX.y;
z = newPosOrX.z;
}
else {
x = newPosOrX;
}
let trs = this._trs;
if (z === undefined) {
z = trs[2];
}
if (trs[0] === x && trs[1] === y && trs[2] === z) {
return;
}
if (CC_EDITOR) {
var oldPosition = new cc.Vec3(trs[0], trs[1], trs[2]);
}
trs[0] = x;
trs[1] = y;
trs[2] = z;
this.setLocalDirty(LocalDirtyFlag.ALL_POSITION);
!CC_NATIVERENDERER && (this._renderFlag |= RenderFlow.FLAG_WORLD_TRANSFORM);
// fast check event
if (this._eventMask & POSITION_ON) {
if (CC_EDITOR) {
this.emit(EventType.POSITION_CHANGED, oldPosition);
}
else {
this.emit(EventType.POSITION_CHANGED);
}
}
},
/**
* !#en
* Returns the scale factor of the node.
* Need pass a cc.Vec2 or cc.Vec3 as the argument to receive the return values.
* !#zh 获取节点的缩放,需要传一个 cc.Vec2 或者 cc.Vec3 作为参数来接收返回值。
* @method getScale
* @param {Vec2|Vec3} out
* @return {Vec2|Vec3} The scale factor
* @example
* cc.log("Node Scale: " + node.getScale(cc.v3()));
*/
getScale (out) {
if (out !== undefined) {
return Trs.toScale(out, this._trs);
}
else {
cc.errorID(1400, 'cc.Node.getScale', 'cc.Node.scale or cc.Node.getScale(cc.Vec3)');
return this._trs[7];
}
},
/**
* !#en
* Sets the scale of axis in local coordinates of the node.
* You can operate 2 axis in 2D node, and 3 axis in 3D node.
* When only (x, y) is passed, the value of scale.z will not be changed.
* When a Vec2 is passed in, the value of scale.z will be set to 0.
* !#zh
* 设置节点在本地坐标系中坐标轴上的缩放比例。
* 2D 节点可以操作两个坐标轴,而 3D 节点可以操作三个坐标轴。
* 当只传入 (x, y) 时scale.z 的值不会被改变。
* 当只传入 Vec2 对象时scale.z 的值将被设置为0。
* @method setScale
* @param {Number|Vec2|Vec3} x - scaleX or scale object
* @param {Number} [y]
* @param {Number} [z]
* @example
* node.setScale(cc.v2(2, 2)); // Notice: scaleZ will be 0
* node.setScale(cc.v3(2, 2, 2)); // for 3D node
* node.setScale(2);
*/
setScale (newScaleOrX, y, z) {
let x;
// only one parameter, and it's a Vec2/Vec3:
if (newScaleOrX && typeof newScaleOrX !== 'number') {
x = newScaleOrX.x;
y = newScaleOrX.y;
z = newScaleOrX.z;
}
// only one parameter, and it's a number:
else if (newScaleOrX !== undefined && y === undefined) {
x = newScaleOrX;
y = newScaleOrX;
z = newScaleOrX;
}
// two or three paramters:
else {
x = newScaleOrX;
}
let trs = this._trs;
if (z === undefined) {
z = trs[9];
}
if (trs[7] !== x || trs[8] !== y || trs[9] !== z) {
trs[7] = x;
trs[8] = y;
trs[9] = z;
this.setLocalDirty(LocalDirtyFlag.ALL_SCALE);
!CC_NATIVERENDERER && (this._renderFlag |= RenderFlow.FLAG_TRANSFORM);
if (this._eventMask & SCALE_ON) {
this.emit(EventType.SCALE_CHANGED);
}
}
},
/**
* !#en
* Get rotation of node (in quaternion).
* Need pass a cc.Quat as the argument to receive the return values.
* !#zh
* 获取该节点的 quaternion 旋转角度,需要传一个 cc.Quat 作为参数来接收返回值。
* @method getRotation
* @param {Quat} out
* @return {Quat} Quaternion object represents the rotation
*/
getRotation (out) {
if (out instanceof Quat) {
return Trs.toRotation(out, this._trs);
}
else {
if (CC_DEBUG) {
cc.warn("`cc.Node.getRotation()` is deprecated since v2.1.0, please use `-cc.Node.angle` instead. (`this.node.getRotation()` -> `-this.node.angle`)");
}
return -this.angle;
}
},
/**
* !#en Set rotation of node (in quaternion).
* !#zh 设置该节点的 quaternion 旋转角度。
* @method setRotation
* @param {cc.Quat|Number} quat Quaternion object represents the rotation or the x value of quaternion
* @param {Number} [y] y value of quternion
* @param {Number} [z] z value of quternion
* @param {Number} [w] w value of quternion
*/
setRotation (rotation, y, z, w) {
if (typeof rotation === 'number' && y === undefined) {
if (CC_DEBUG) {
cc.warn("`cc.Node.setRotation(degree)` is deprecated since v2.1.0, please set `-cc.Node.angle` instead. (`this.node.setRotation(x)` -> `this.node.angle = -x`)");
}
this.angle = -rotation;
}
else {
let x = rotation;
if (y === undefined) {
x = rotation.x;
y = rotation.y;
z = rotation.z;
w = rotation.w;
}
let trs = this._trs;
if (trs[3] !== x || trs[4] !== y || trs[5] !== z || trs[6] !== w) {
trs[3] = x;
trs[4] = y;
trs[5] = z;
trs[6] = w;
this.setLocalDirty(LocalDirtyFlag.ALL_ROTATION);
if (this._eventMask & ROTATION_ON) {
this.emit(EventType.ROTATION_CHANGED);
}
if (CC_EDITOR) {
this._toEuler();
}
}
}
},
/**
* !#en
* Returns a copy the untransformed size of the node. <br/>
* The contentSize remains the same no matter the node is scaled or rotated.<br/>
* All nodes has a size. Layer and Scene has the same size of the screen by default. <br/>
* !#zh 获取节点自身大小,不受该节点是否被缩放或者旋转的影响。
* @method getContentSize
* @return {Size} The untransformed size of the node.
* @example
* cc.log("Content Size: " + node.getContentSize());
*/
getContentSize () {
return cc.size(this._contentSize.width, this._contentSize.height);
},
/**
* !#en
* Sets the untransformed size of the node.<br/>
* The contentSize remains the same no matter the node is scaled or rotated.<br/>
* All nodes has a size. Layer and Scene has the same size of the screen.
* !#zh 设置节点原始大小,不受该节点是否被缩放或者旋转的影响。
* @method setContentSize
* @param {Size|Number} size - The untransformed size of the node or The untransformed size's width of the node.
* @param {Number} [height] - The untransformed size's height of the node.
* @example
* node.setContentSize(cc.size(100, 100));
* node.setContentSize(100, 100);
*/
setContentSize (size, height) {
var locContentSize = this._contentSize;
var clone;
if (height === undefined) {
if (approx(size.width, locContentSize.width) && approx(size.height, locContentSize.height))
return;
if (CC_EDITOR) {
clone = cc.size(locContentSize.width, locContentSize.height);
}
locContentSize.width = size.width;
locContentSize.height = size.height;
} else {
if (approx(size, locContentSize.width) && approx(height, locContentSize.height))
return;
if (CC_EDITOR) {
clone = cc.size(locContentSize.width, locContentSize.height);
}
locContentSize.width = size;
locContentSize.height = height;
}
if (this._eventMask & SIZE_ON) {
if (CC_EDITOR) {
this.emit(EventType.SIZE_CHANGED, clone);
}
else {
this.emit(EventType.SIZE_CHANGED);
}
}
},
/**
* !#en
* Returns a copy of the anchor point.<br/>
* Anchor point is the point around which all transformations and positioning manipulations take place.<br/>
* It's like a pin in the node where it is "attached" to its parent. <br/>
* The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner. <br/>
* But you can use values higher than (1,1) and lower than (0,0) too. <br/>
* The default anchor point is (0.5,0.5), so it starts at the center of the node.
* !#zh
* 获取节点锚点,用百分比表示。<br/>
* 锚点应用于所有变换和坐标点的操作,它就像在节点上连接其父节点的大头针。<br/>
* 锚点是标准化的,就像百分比一样。(00) 表示左下角,(11) 表示右上角。<br/>
* 但是你可以使用比11更高的值或者比00更低的值。<br/>
* 默认的锚点是0.50.5),因此它开始于节点的中心位置。<br/>
* 注意Creator 中的锚点仅用于定位所在的节点,子节点的定位不受影响。
* @method getAnchorPoint
* @return {Vec2} The anchor point of node.
* @example
* cc.log("Node AnchorPoint: " + node.getAnchorPoint());
*/
getAnchorPoint () {
return cc.v2(this._anchorPoint);
},
/**
* !#en
* Sets the anchor point in percent. <br/>
* anchor point is the point around which all transformations and positioning manipulations take place. <br/>
* It's like a pin in the node where it is "attached" to its parent. <br/>
* The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner.<br/>
* But you can use values higher than (1,1) and lower than (0,0) too.<br/>
* The default anchor point is (0.5,0.5), so it starts at the center of the node.
* !#zh
* 设置锚点的百分比。<br/>
* 锚点应用于所有变换和坐标点的操作,它就像在节点上连接其父节点的大头针。<br/>
* 锚点是标准化的,就像百分比一样。(00) 表示左下角,(11) 表示右上角。<br/>
* 但是你可以使用比11更高的值或者比00更低的值。<br/>
* 默认的锚点是0.50.5),因此它开始于节点的中心位置。<br/>
* 注意Creator 中的锚点仅用于定位所在的节点,子节点的定位不受影响。
* @method setAnchorPoint
* @param {Vec2|Number} point - The anchor point of node or The x axis anchor of node.
* @param {Number} [y] - The y axis anchor of node.
* @example
* node.setAnchorPoint(cc.v2(1, 1));
* node.setAnchorPoint(1, 1);
*/
setAnchorPoint (point, y) {
var locAnchorPoint = this._anchorPoint;
if (y === undefined) {
if ((point.x === locAnchorPoint.x) && (point.y === locAnchorPoint.y))
return;
locAnchorPoint.x = point.x;
locAnchorPoint.y = point.y;
} else {
if ((point === locAnchorPoint.x) && (y === locAnchorPoint.y))
return;
locAnchorPoint.x = point;
locAnchorPoint.y = y;
}
this.setLocalDirty(LocalDirtyFlag.ALL_POSITION);
if (this._eventMask & ANCHOR_ON) {
this.emit(EventType.ANCHOR_CHANGED);
}
},
/*
* Transforms position from world space to local space.
* @method _invTransformPoint
* @param {Vec3} out
* @param {Vec3} vec3
*/
_invTransformPoint (out, pos) {
if (this._parent) {
this._parent._invTransformPoint(out, pos);
} else {
Vec3.copy(out, pos);
}
let ltrs = this._trs;
// out = parent_inv_pos - pos
Trs.toPosition(_tpVec3a, ltrs);
Vec3.sub(out, out, _tpVec3a);
// out = inv(rot) * out
Trs.toRotation(_tpQuata, ltrs);
Quat.conjugate(_tpQuatb, _tpQuata);
Vec3.transformQuat(out, out, _tpQuatb);
// out = (1/scale) * out
Trs.toScale(_tpVec3a, ltrs);
Vec3.inverseSafe(_tpVec3b, _tpVec3a);
Vec3.mul(out, out, _tpVec3b);
return out;
},
/*
* Calculate and return world position.
* This is not a public API yet, its usage could be updated
* @method getWorldPosition
* @param {Vec3} out
* @return {Vec3}
*/
getWorldPosition (out) {
Trs.toPosition(out, this._trs);
let curr = this._parent;
let ltrs;
while (curr) {
ltrs = curr._trs;
// out = parent_scale * pos
Trs.toScale(_gwpVec3, ltrs);
Vec3.mul(out, out, _gwpVec3);
// out = parent_quat * out
Trs.toRotation(_gwpQuat, ltrs);
Vec3.transformQuat(out, out, _gwpQuat);
// out = out + pos
Trs.toPosition(_gwpVec3, ltrs);
Vec3.add(out, out, _gwpVec3);
curr = curr._parent;
}
return out;
},
/*
* Set world position.
* This is not a public API yet, its usage could be updated
* @method setWorldPosition
* @param {Vec3} pos
*/
setWorldPosition (pos) {
let ltrs = this._trs;
if (CC_EDITOR) {
var oldPosition = new cc.Vec3(ltrs[0], ltrs[1], ltrs[2]);
}
// NOTE: this is faster than invert world matrix and transform the point
if (this._parent) {
this._parent._invTransformPoint(_swpVec3, pos);
}
else {
Vec3.copy(_swpVec3, pos);
}
Trs.fromPosition(ltrs, _swpVec3);
this.setLocalDirty(LocalDirtyFlag.ALL_POSITION);
// fast check event
if (this._eventMask & POSITION_ON) {
// send event
if (CC_EDITOR) {
this.emit(EventType.POSITION_CHANGED, oldPosition);
}
else {
this.emit(EventType.POSITION_CHANGED);
}
}
},
/*
* Calculate and return world rotation
* This is not a public API yet, its usage could be updated
* @method getWorldRotation
* @param {Quat} out
* @return {Quat}
*/
getWorldRotation (out) {
Trs.toRotation(_gwrQuat, this._trs);
Quat.copy(out, _gwrQuat);
let curr = this._parent;
while (curr) {
Trs.toRotation(_gwrQuat, curr._trs);
Quat.mul(out, _gwrQuat, out);
curr = curr._parent;
}
return out;
},
/*
* Set world rotation with quaternion
* This is not a public API yet, its usage could be updated
* @method setWorldRotation
* @param {Quat} val
*/
setWorldRotation (val) {
if (this._parent) {
this._parent.getWorldRotation(_swrQuat);
Quat.conjugate(_swrQuat, _swrQuat);
Quat.mul(_swrQuat, _swrQuat, val);
}
else {
Quat.copy(_swrQuat, val);
}
Trs.fromRotation(this._trs, _swrQuat);
if (CC_EDITOR) {
this._toEuler();
}
this.setLocalDirty(LocalDirtyFlag.ALL_ROTATION);
},
/*
* Calculate and return world scale
* This is not a public API yet, its usage could be updated
* @method getWorldScale
* @param {Vec3} out
* @return {Vec3}
*/
getWorldScale (out) {
Trs.toScale(_gwsVec3, this._trs);
Vec3.copy(out, _gwsVec3);
let curr = this._parent;
while (curr) {
Trs.toScale(_gwsVec3, curr._trs);
Vec3.mul(out, out, _gwsVec3);
curr = curr._parent;
}
return out;
},
/*
* Set world scale with vec3
* This is not a public API yet, its usage could be updated
* @method setWorldScale
* @param {Vec3} scale
*/
setWorldScale (scale) {
if (this._parent) {
this._parent.getWorldScale(_swsVec3);
Vec3.div(_swsVec3, scale, _swsVec3);
}
else {
Vec3.copy(_swsVec3, scale);
}
Trs.fromScale(this._trs, _swsVec3);
this.setLocalDirty(LocalDirtyFlag.ALL_SCALE);
},
getWorldRT (out) {
let opos = _gwrtVec3a;
let orot = _gwrtQuata;
let ltrs = this._trs;
Trs.toPosition(opos, ltrs);
Trs.toRotation(orot, ltrs);
let curr = this._parent;
while (curr) {
ltrs = curr._trs;
// opos = parent_lscale * lpos
Trs.toScale(_gwrtVec3b, ltrs);
Vec3.mul(opos, opos, _gwrtVec3b);
// opos = parent_lrot * opos
Trs.toRotation(_gwrtQuatb, ltrs);
Vec3.transformQuat(opos, opos, _gwrtQuatb);
// opos = opos + lpos
Trs.toPosition(_gwrtVec3b, ltrs);
Vec3.add(opos, opos, _gwrtVec3b);
// orot = lrot * orot
Quat.mul(orot, _gwrtQuatb, orot);
curr = curr._parent;
}
Mat4.fromRT(out, orot, opos);
return out;
},
/**
* !#en Set rotation by lookAt target point, normally used by Camera Node
* !#zh 通过观察目标来设置 rotation一般用于 Camera Node 上
* @method lookAt
* @param {Vec3} pos
* @param {Vec3} [up] - default is (0,1,0)
*/
lookAt (pos, up) {
this.getWorldPosition(_laVec3);
Vec3.sub(_laVec3, _laVec3, pos); // NOTE: we use -z for view-dir
Vec3.normalize(_laVec3, _laVec3);
Quat.fromViewUp(_laQuat, _laVec3, up);
this.setWorldRotation(_laQuat);
},
_updateLocalMatrix: updateLocalMatrix2D,
_calculWorldMatrix () {
// Avoid as much function call as possible
if (this._localMatDirty & LocalDirtyFlag.TRSS) {
this._updateLocalMatrix();
}
// Assume parent world matrix is correct
let parent = this._parent;
if (parent) {
this._mulMat(this._worldMatrix, parent._worldMatrix, this._matrix);
}
else {
Mat4.copy(this._worldMatrix, this._matrix);
}
this._worldMatDirty = false;
},
_mulMat: mulMat2D,
_updateWorldMatrix () {
if (this._parent) {
this._parent._updateWorldMatrix();
}
if (this._worldMatDirty) {
this._calculWorldMatrix();
// Sync dirty to children
let children = this._children;
for (let i = 0, l = children.length; i < l; i++) {
children[i]._worldMatDirty = true;
}
}
},
setLocalDirty (flag) {
this._localMatDirty |= flag;
this._worldMatDirty = true;
if (flag === LocalDirtyFlag.ALL_POSITION || flag === LocalDirtyFlag.POSITION) {
this._renderFlag |= RenderFlow.FLAG_WORLD_TRANSFORM;
}
else {
this._renderFlag |= RenderFlow.FLAG_TRANSFORM;
}
},
setWorldDirty () {
this._worldMatDirty = true;
},
/**
* !#en
* Get the local transform matrix (4x4), based on parent node coordinates
* !#zh 返回局部空间坐标系的矩阵,基于父节点坐标系。
* @method getLocalMatrix
* @param {Mat4} out The matrix object to be filled with data
* @return {Mat4} Same as the out matrix object
* @example
* let mat4 = cc.mat4();
* node.getLocalMatrix(mat4);
*/
getLocalMatrix (out) {
this._updateLocalMatrix();
return Mat4.copy(out, this._matrix);
},
/**
* !#en
* Get the world transform matrix (4x4)
* !#zh 返回世界空间坐标系的矩阵。
* @method getWorldMatrix
* @param {Mat4} out The matrix object to be filled with data
* @return {Mat4} Same as the out matrix object
* @example
* let mat4 = cc.mat4();
* node.getWorldMatrix(mat4);
*/
getWorldMatrix (out) {
this._updateWorldMatrix();
return Mat4.copy(out, this._worldMatrix);
},
/**
* !#en
* Converts a Point to node (local) space coordinates.
* !#zh
* 将一个点转换到节点 (局部) 空间坐标系。
* @method convertToNodeSpaceAR
* @param {Vec3|Vec2} worldPoint
* @param {Vec3|Vec2} [out]
* @return {Vec3|Vec2}
* @typescript
* convertToNodeSpaceAR<T extends cc.Vec2 | cc.Vec3>(worldPoint: T, out?: T): T
* @example
* var newVec2 = node.convertToNodeSpaceAR(cc.v2(100, 100));
* var newVec3 = node.convertToNodeSpaceAR(cc.v3(100, 100, 100));
*/
convertToNodeSpaceAR (worldPoint, out) {
this._updateWorldMatrix();
Mat4.invert(_mat4_temp, this._worldMatrix);
if (worldPoint instanceof cc.Vec2) {
out = out || new cc.Vec2();
return Vec2.transformMat4(out, worldPoint, _mat4_temp);
}
else {
out = out || new cc.Vec3();
return Vec3.transformMat4(out, worldPoint, _mat4_temp);
}
},
/**
* !#en
* Converts a Point in node coordinates to world space coordinates.
* !#zh
* 将节点坐标系下的一个点转换到世界空间坐标系。
* @method convertToWorldSpaceAR
* @param {Vec3|Vec2} nodePoint
* @param {Vec3|Vec2} [out]
* @return {Vec3|Vec2}
* @typescript
* convertToWorldSpaceAR<T extends cc.Vec2 | cc.Vec3>(nodePoint: T, out?: T): T
* @example
* var newVec2 = node.convertToWorldSpaceAR(cc.v2(100, 100));
* var newVec3 = node.convertToWorldSpaceAR(cc.v3(100, 100, 100));
*/
convertToWorldSpaceAR (nodePoint, out) {
this._updateWorldMatrix();
if (nodePoint instanceof cc.Vec2) {
out = out || new cc.Vec2();
return Vec2.transformMat4(out, nodePoint, this._worldMatrix);
}
else {
out = out || new cc.Vec3();
return Vec3.transformMat4(out, nodePoint, this._worldMatrix);
}
},
// OLD TRANSFORM ACCESS APIs
/**
* !#en Converts a Point to node (local) space coordinates then add the anchor point position.
* So the return position will be related to the left bottom corner of the node's bounding box.
* This equals to the API behavior of cocos2d-x, you probably want to use convertToNodeSpaceAR instead
* !#zh 将一个点转换到节点 (局部) 坐标系,并加上锚点的坐标。<br/>
* 也就是说返回的坐标是相对于节点包围盒左下角的坐标。<br/>
* 这个 API 的设计是为了和 cocos2d-x 中行为一致,更多情况下你可能需要使用 convertToNodeSpaceAR。
* @method convertToNodeSpace
* @deprecated since v2.1.3
* @param {Vec2} worldPoint
* @return {Vec2}
* @example
* var newVec2 = node.convertToNodeSpace(cc.v2(100, 100));
*/
convertToNodeSpace (worldPoint) {
this._updateWorldMatrix();
Mat4.invert(_mat4_temp, this._worldMatrix);
let out = new cc.Vec2();
Vec2.transformMat4(out, worldPoint, _mat4_temp);
out.x += this._anchorPoint.x * this._contentSize.width;
out.y += this._anchorPoint.y * this._contentSize.height;
return out;
},
/**
* !#en Converts a Point related to the left bottom corner of the node's bounding box to world space coordinates.
* This equals to the API behavior of cocos2d-x, you probably want to use convertToWorldSpaceAR instead
* !#zh 将一个相对于节点左下角的坐标位置转换到世界空间坐标系。
* 这个 API 的设计是为了和 cocos2d-x 中行为一致,更多情况下你可能需要使用 convertToWorldSpaceAR
* @method convertToWorldSpace
* @deprecated since v2.1.3
* @param {Vec2} nodePoint
* @return {Vec2}
* @example
* var newVec2 = node.convertToWorldSpace(cc.v2(100, 100));
*/
convertToWorldSpace (nodePoint) {
this._updateWorldMatrix();
let out = new cc.Vec2(
nodePoint.x - this._anchorPoint.x * this._contentSize.width,
nodePoint.y - this._anchorPoint.y * this._contentSize.height
);
return Vec2.transformMat4(out, out, this._worldMatrix);
},
/**
* !#en
* Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.<br/>
* The matrix is in Pixels.
* !#zh 返回这个将节点(局部)的空间坐标系转换成父节点的空间坐标系的矩阵。这个矩阵以像素为单位。
* @method getNodeToParentTransform
* @deprecated since v2.0
* @param {AffineTransform} [out] The affine transform object to be filled with data
* @return {AffineTransform} Same as the out affine transform object
* @example
* let affineTransform = cc.AffineTransform.create();
* node.getNodeToParentTransform(affineTransform);
*/
getNodeToParentTransform (out) {
if (!out) {
out = AffineTrans.identity();
}
this._updateLocalMatrix();
var contentSize = this._contentSize;
_vec3_temp.x = -this._anchorPoint.x * contentSize.width;
_vec3_temp.y = -this._anchorPoint.y * contentSize.height;
Mat4.copy(_mat4_temp, this._matrix);
Mat4.transform(_mat4_temp, _mat4_temp, _vec3_temp);
return AffineTrans.fromMat4(out, _mat4_temp);
},
/**
* !#en
* Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.<br/>
* The matrix is in Pixels.<br/>
* This method is AR (Anchor Relative).
* !#zh
* 返回这个将节点(局部)的空间坐标系转换成父节点的空间坐标系的矩阵。<br/>
* 这个矩阵以像素为单位。<br/>
* 该方法基于节点坐标。
* @method getNodeToParentTransformAR
* @deprecated since v2.0
* @param {AffineTransform} [out] The affine transform object to be filled with data
* @return {AffineTransform} Same as the out affine transform object
* @example
* let affineTransform = cc.AffineTransform.create();
* node.getNodeToParentTransformAR(affineTransform);
*/
getNodeToParentTransformAR (out) {
if (!out) {
out = AffineTrans.identity();
}
this._updateLocalMatrix();
return AffineTrans.fromMat4(out, this._matrix);
},
/**
* !#en Returns the world affine transform matrix. The matrix is in Pixels.
* !#zh 返回节点到世界坐标系的仿射变换矩阵。矩阵单位是像素。
* @method getNodeToWorldTransform
* @deprecated since v2.0
* @param {AffineTransform} [out] The affine transform object to be filled with data
* @return {AffineTransform} Same as the out affine transform object
* @example
* let affineTransform = cc.AffineTransform.create();
* node.getNodeToWorldTransform(affineTransform);
*/
getNodeToWorldTransform (out) {
if (!out) {
out = AffineTrans.identity();
}
this._updateWorldMatrix();
var contentSize = this._contentSize;
_vec3_temp.x = -this._anchorPoint.x * contentSize.width;
_vec3_temp.y = -this._anchorPoint.y * contentSize.height;
Mat4.copy(_mat4_temp, this._worldMatrix);
Mat4.transform(_mat4_temp, _mat4_temp, _vec3_temp);
return AffineTrans.fromMat4(out, _mat4_temp);
},
/**
* !#en
* Returns the world affine transform matrix. The matrix is in Pixels.<br/>
* This method is AR (Anchor Relative).
* !#zh
* 返回节点到世界坐标仿射变换矩阵。矩阵单位是像素。<br/>
* 该方法基于节点坐标。
* @method getNodeToWorldTransformAR
* @deprecated since v2.0
* @param {AffineTransform} [out] The affine transform object to be filled with data
* @return {AffineTransform} Same as the out affine transform object
* @example
* let affineTransform = cc.AffineTransform.create();
* node.getNodeToWorldTransformAR(affineTransform);
*/
getNodeToWorldTransformAR (out) {
if (!out) {
out = AffineTrans.identity();
}
this._updateWorldMatrix();
return AffineTrans.fromMat4(out, this._worldMatrix);
},
/**
* !#en
* Returns the matrix that transform parent's space coordinates to the node's (local) space coordinates.<br/>
* The matrix is in Pixels. The returned transform is readonly and cannot be changed.
* !#zh
* 返回将父节点的坐标系转换成节点(局部)的空间坐标系的矩阵。<br/>
* 该矩阵以像素为单位。返回的矩阵是只读的,不能更改。
* @method getParentToNodeTransform
* @deprecated since v2.0
* @param {AffineTransform} [out] The affine transform object to be filled with data
* @return {AffineTransform} Same as the out affine transform object
* @example
* let affineTransform = cc.AffineTransform.create();
* node.getParentToNodeTransform(affineTransform);
*/
getParentToNodeTransform (out) {
if (!out) {
out = AffineTrans.identity();
}
this._updateLocalMatrix();
Mat4.invert(_mat4_temp, this._matrix);
return AffineTrans.fromMat4(out, _mat4_temp);
},
/**
* !#en Returns the inverse world affine transform matrix. The matrix is in Pixels.
* !#en 返回世界坐标系到节点坐标系的逆矩阵。
* @method getWorldToNodeTransform
* @deprecated since v2.0
* @param {AffineTransform} [out] The affine transform object to be filled with data
* @return {AffineTransform} Same as the out affine transform object
* @example
* let affineTransform = cc.AffineTransform.create();
* node.getWorldToNodeTransform(affineTransform);
*/
getWorldToNodeTransform (out) {
if (!out) {
out = AffineTrans.identity();
}
this._updateWorldMatrix();
Mat4.invert(_mat4_temp, this._worldMatrix);
return AffineTrans.fromMat4(out, _mat4_temp);
},
/**
* !#en convenience methods which take a cc.Touch instead of cc.Vec2.
* !#zh 将触摸点转换成本地坐标系中位置。
* @method convertTouchToNodeSpace
* @deprecated since v2.0
* @param {Touch} touch - The touch object
* @return {Vec2}
* @example
* var newVec2 = node.convertTouchToNodeSpace(touch);
*/
convertTouchToNodeSpace (touch) {
return this.convertToNodeSpace(touch.getLocation());
},
/**
* !#en converts a cc.Touch (world coordinates) into a local coordinate. This method is AR (Anchor Relative).
* !#zh 转换一个 cc.Touch世界坐标到一个局部坐标该方法基于节点坐标。
* @method convertTouchToNodeSpaceAR
* @deprecated since v2.0
* @param {Touch} touch - The touch object
* @return {Vec2}
* @example
* var newVec2 = node.convertTouchToNodeSpaceAR(touch);
*/
convertTouchToNodeSpaceAR (touch) {
return this.convertToNodeSpaceAR(touch.getLocation());
},
/**
* !#en
* Returns a "local" axis aligned bounding box of the node. <br/>
* The returned box is relative only to its parent.
* !#zh 返回父节坐标系下的轴向对齐的包围盒。
* @method getBoundingBox
* @return {Rect} The calculated bounding box of the node
* @example
* var boundingBox = node.getBoundingBox();
*/
getBoundingBox () {
this._updateLocalMatrix();
let width = this._contentSize.width;
let height = this._contentSize.height;
let rect = cc.rect(
-this._anchorPoint.x * width,
-this._anchorPoint.y * height,
width,
height);
return rect.transformMat4(rect, this._matrix);
},
/**
* !#en
* Returns a "world" axis aligned bounding box of the node.<br/>
* The bounding box contains self and active children's world bounding box.
* !#zh
* 返回节点在世界坐标系下的对齐轴向的包围盒AABB。<br/>
* 该边框包含自身和已激活的子节点的世界边框。
* @method getBoundingBoxToWorld
* @return {Rect}
* @example
* var newRect = node.getBoundingBoxToWorld();
*/
getBoundingBoxToWorld () {
if (this._parent) {
this._parent._updateWorldMatrix();
return this._getBoundingBoxTo();
}
else {
return this.getBoundingBox();
}
},
_getBoundingBoxTo () {
let width = this._contentSize.width;
let height = this._contentSize.height;
let rect = cc.rect(
-this._anchorPoint.x * width,
-this._anchorPoint.y * height,
width,
height);
this._calculWorldMatrix();
rect.transformMat4(rect, this._worldMatrix);
//query child's BoundingBox
if (!this._children)
return rect;
var locChildren = this._children;
for (var i = 0; i < locChildren.length; i++) {
var child = locChildren[i];
if (child && child.active) {
var childRect = child._getBoundingBoxTo();
if (childRect)
rect.union(rect, childRect);
}
}
return rect;
},
_updateOrderOfArrival () {
var arrivalOrder = this._parent ? ++this._parent._childArrivalOrder : 0;
this._localZOrder = (this._localZOrder & 0xffff0000) | arrivalOrder;
this.emit(EventType.SIBLING_ORDER_CHANGED);
},
/**
* !#en
* Set Group index of node without children.<br/>
* Which Group this node belongs to will resolve that this node's collision components can collide with which other collision componentns.<br/>
* !#zh
* 设置节点本身的分组索引。不影响子节点<br/>
* 节点的分组将关系到节点的碰撞组件可以与哪些碰撞组件相碰撞。<br/>
* @property groupIndex
* @type {Integer}
* @default 0
*/
setSelfGroupIndex (groupIndex) {
this._groupIndex = groupIndex || 0;
this._cullingMask = 1 << groupIndex;
if (CC_JSB && CC_NATIVERENDERER) {
this._proxy && this._proxy.updateCullingMask();
}
},
/**
* !#en
* Adds a child to the node with z order and name.
* !#zh
* 添加子节点,并且可以修改该节点的 局部 Z 顺序和名字。
* @method addChild
* @param {Node} child - A child node
* @param {Number} [zIndex] - Z order for drawing priority. Please refer to zIndex property
* @param {String} [name] - A name to identify the node easily. Please refer to name property
* @example
* node.addChild(newNode, 1, "node");
*/
addChild (child, zIndex, name) {
if (CC_DEV && !cc.Node.isNode(child)) {
return cc.errorID(1634, cc.js.getClassName(child));
}
cc.assertID(child, 1606);
cc.assertID(child._parent === null, 1605);
// invokes the parent setter
child.parent = this;
if (zIndex !== undefined) {
child.zIndex = zIndex;
}
if (name !== undefined) {
child.name = name;
}
},
/**
* !#en Stops all running actions and schedulers.
* !#zh 停止所有正在播放的动作和计时器。
* @method cleanup
* @example
* node.cleanup();
*/
cleanup () {
// actions
ActionManagerExist && cc.director.getActionManager().removeAllActionsFromTarget(this);
// event
eventManager.removeListeners(this);
// children
var i, len = this._children.length, node;
for (i = 0; i < len; ++i) {
node = this._children[i];
if (node)
node.cleanup();
}
},
/**
* !#en Sorts the children array depends on children's zIndex and arrivalOrder,
* normally you won't need to invoke this function.
* !#zh 根据子节点的 zIndex 和 arrivalOrder 进行排序,正常情况下开发者不需要手动调用这个函数。
*
* @method sortAllChildren
*/
sortAllChildren () {
if (this._reorderChildDirty) {
this._reorderChildDirty = false;
// delay update arrivalOrder before sort children
var _children = this._children, child;
// reset arrivalOrder before sort children
this._childArrivalOrder = 1;
for (let i = 0, len = _children.length; i < len; i++) {
child = _children[i];
child._updateOrderOfArrival();
}
// Optimize reordering event code to fix problems with setting zindex
// https://github.com/cocos-creator/2d-tasks/issues/1186
eventManager._setDirtyForNode(this);
if (_children.length > 1) {
// insertion sort
let child, child2;
for (let i = 1, count = _children.length; i < count; i++) {
child = _children[i];
let j = i;
for (; j > 0 &&
(child2 = _children[j - 1])._localZOrder > child._localZOrder; j--) {
_children[j] = child2;
}
_children[j] = child;
}
this.emit(EventType.CHILD_REORDER, this);
}
cc.director.__fastOff(cc.Director.EVENT_AFTER_UPDATE, this.sortAllChildren, this);
}
},
_delaySort () {
if (!this._reorderChildDirty) {
this._reorderChildDirty = true;
cc.director.__fastOn(cc.Director.EVENT_AFTER_UPDATE, this.sortAllChildren, this);
}
},
_restoreProperties: CC_EDITOR && function () {
/*
* TODO: Refine this code after completing undo/redo 2.0.
* The node will be destroyed when deleting in the editor,
* but it will be reserved and reused for undo.
*/
// restore 3d node
this.is3DNode = this.is3DNode;
if (!this._matrix) {
this._matrix = cc.mat4(this._spaceInfo.localMat);
Mat4.identity(this._matrix);
}
if (!this._worldMatrix) {
this._worldMatrix = cc.mat4(this._spaceInfo.worldMat);
Mat4.identity(this._worldMatrix);
}
this._localMatDirty = LocalDirtyFlag.ALL;
this._worldMatDirty = true;
this._fromEuler();
this._renderFlag |= RenderFlow.FLAG_TRANSFORM | RenderFlow.FLAG_OPACITY_COLOR;
if (this._renderComponent) {
this._renderComponent.markForRender(true);
}
if (this._children.length > 0) {
this._renderFlag |= RenderFlow.FLAG_CHILDREN;
}
},
onRestore: CC_EDITOR && function () {
this._onRestoreBase();
this.emit(EventType.GROUP_CHANGED, this);
this.emit(EventType.POSITION_CHANGED, this.position.clone());
this.emit(EventType.SIZE_CHANGED, this._contentSize.clone());
this.emit(EventType.ROTATION_CHANGED);
this.emit(EventType.SCALE_CHANGED)
this.emit(EventType.COLOR_CHANGED, this._color.clone());
this.emit(EventType.ANCHOR_CHANGED);
this._restoreProperties();
var actionManager = cc.director.getActionManager();
if (this._activeInHierarchy) {
actionManager && actionManager.resumeTarget(this);
eventManager.resumeTarget(this);
}
else {
actionManager && actionManager.pauseTarget(this);
eventManager.pauseTarget(this);
}
},
};
if (CC_EDITOR) {
// deprecated, only used to import old data in editor
js.mixin(NodeDefines.properties, {
_scaleX: {
default: undefined,
type: cc.Float,
editorOnly: true
},
_scaleY: {
default: undefined,
type: cc.Float,
editorOnly: true
},
});
}
let Node = cc.Class(NodeDefines);
// 3D Node Property
// Node Event
/**
* !#en
* The position changing event, you can listen to this event through the statement this.node.on(cc.Node.EventType.POSITION_CHANGED, callback, this);
* !#zh
* 位置变动监听事件, 通过 this.node.on(cc.Node.EventType.POSITION_CHANGED, callback, this); 进行监听。
* @event position-changed
* @param {Vec2} oldPos - The old position, but this parameter is only available in editor!
*/
/**
* !#en
* The size changing event, you can listen to this event through the statement this.node.on(cc.Node.EventType.SIZE_CHANGED, callback, this);
* !#zh
* 尺寸变动监听事件,通过 this.node.on(cc.Node.EventType.SIZE_CHANGED, callback, this); 进行监听。
* @event size-changed
* @param {Size} oldSize - The old size, but this parameter is only available in editor!
*/
/**
* !#en
* The anchor changing event, you can listen to this event through the statement this.node.on(cc.Node.EventType.ANCHOR_CHANGED, callback, this);
* !#zh
* 锚点变动监听事件,通过 this.node.on(cc.Node.EventType.ANCHOR_CHANGED, callback, this); 进行监听。
* @event anchor-changed
*/
/**
* !#en
* The adding child event, you can listen to this event through the statement this.node.on(cc.Node.EventType.CHILD_ADDED, callback, this);
* !#zh
* 增加子节点监听事件,通过 this.node.on(cc.Node.EventType.CHILD_ADDED, callback, this); 进行监听。
* @event child-added
* @param {Node} child - child which have been added
*/
/**
* !#en
* The removing child event, you can listen to this event through the statement this.node.on(cc.Node.EventType.CHILD_REMOVED, callback, this);
* !#zh
* 删除子节点监听事件,通过 this.node.on(cc.Node.EventType.CHILD_REMOVED, callback, this); 进行监听。
* @event child-removed
* @param {Node} child - child which have been removed
*/
/**
* !#en
* The reordering child event, you can listen to this event through the statement this.node.on(cc.Node.EventType.CHILD_REORDER, callback, this);
* !#zh
* 子节点顺序变动监听事件,通过 this.node.on(cc.Node.EventType.CHILD_REORDER, callback, this); 进行监听。
* @event child-reorder
* @param {Node} node - node whose children have been reordered
*/
/**
* !#en
* The group changing event, you can listen to this event through the statement this.node.on(cc.Node.EventType.GROUP_CHANGED, callback, this);
* !#zh
* 节点分组变动监听事件,通过 this.node.on(cc.Node.EventType.GROUP_CHANGED, callback, this); 进行监听。
* @event group-changed
* @param {Node} node - node whose group has changed
*/
// Deprecated APIs
/**
* !#en
* Returns the displayed opacity of Node,
* the difference between displayed opacity and opacity is that displayed opacity is calculated based on opacity and parent node's opacity when cascade opacity enabled.
* !#zh
* 获取节点显示透明度,
* 显示透明度和透明度之间的不同之处在于当启用级连透明度时,
* 显示透明度是基于自身透明度和父节点透明度计算的。
*
* @method getDisplayedOpacity
* @return {number} displayed opacity
* @deprecated since v2.0, please use opacity property, cascade opacity is removed
*/
/**
* !#en
* Returns the displayed color of Node,
* the difference between displayed color and color is that displayed color is calculated based on color and parent node's color when cascade color enabled.
* !#zh
* 获取节点的显示颜色,
* 显示颜色和颜色之间的不同之处在于当启用级连颜色时,
* 显示颜色是基于自身颜色和父节点颜色计算的。
*
* @method getDisplayedColor
* @return {Color}
* @deprecated since v2.0, please use color property, cascade color is removed
*/
/**
* !#en Cascade opacity is removed from v2.0
* Indicate whether node's opacity value affect its child nodes, default value is true.
* !#zh 透明度级联功能从 v2.0 开始已移除
* 节点的不透明度值是否影响其子节点,默认值为 true。
* @property cascadeOpacity
* @deprecated since v2.0
* @type {Boolean}
*/
/**
* !#en Cascade opacity is removed from v2.0
* Returns whether node's opacity value affect its child nodes.
* !#zh 透明度级联功能从 v2.0 开始已移除
* 返回节点的不透明度值是否影响其子节点。
* @method isCascadeOpacityEnabled
* @deprecated since v2.0
* @return {Boolean}
*/
/**
* !#en Cascade opacity is removed from v2.0
* Enable or disable cascade opacity, if cascade enabled, child nodes' opacity will be the multiplication of parent opacity and its own opacity.
* !#zh 透明度级联功能从 v2.0 开始已移除
* 启用或禁用级连不透明度,如果级连启用,子节点的不透明度将是父不透明度乘上它自己的不透明度。
* @method setCascadeOpacityEnabled
* @deprecated since v2.0
* @param {Boolean} cascadeOpacityEnabled
*/
/**
* !#en Opacity modify RGB have been removed since v2.0
* Set whether color should be changed with the opacity value,
* useless in ccsg.Node, but this function is override in some class to have such behavior.
* !#zh 透明度影响颜色配置已经被废弃
* 设置更改透明度时是否修改RGB值
* @method setOpacityModifyRGB
* @deprecated since v2.0
* @param {Boolean} opacityValue
*/
/**
* !#en Opacity modify RGB have been removed since v2.0
* Get whether color should be changed with the opacity value.
* !#zh 透明度影响颜色配置已经被废弃
* 获取更改透明度时是否修改RGB值。
* @method isOpacityModifyRGB
* @deprecated since v2.0
* @return {Boolean}
*/
let _p = Node.prototype;
js.getset(_p, 'position', _p.getPosition, _p.setPosition, false, true);
if (CC_EDITOR) {
let vec3_tmp = new Vec3();
cc.js.getset(_p, 'worldEulerAngles', function () {
let angles = new Vec3(this._eulerAngles);
let parent = this.parent;
while (parent) {
angles.addSelf(parent._eulerAngles);
parent = parent.parent;
}
return angles;
}, function (v) {
vec3_tmp.set(v);
let parent = this.parent;
while (parent) {
vec3_tmp.subSelf(parent._eulerAngles);
parent = parent.parent;
}
this.eulerAngles = vec3_tmp;
});
}
cc.Node = module.exports = Node;