This commit is contained in:
yuanfengxin 2022-07-14 17:42:02 +08:00
parent 5f06585aca
commit 1b1aacfc9a
28 changed files with 44189 additions and 1566 deletions

View File

@ -61,7 +61,11 @@ export class BezierCurveAnimation extends Component {
tweenSwitchEvent = new cc.EventHandler();
/** 更新事件 */
@property({ displayName: '更新事件', tooltip: '(曲线Y_yN)', type: cc.EventHandler })
@property({
displayName: '更新事件',
tooltip: '(总曲线Y_yN, 当前缓动下标_indexN, 当前缓动曲线Y_yN)',
type: cc.EventHandler
})
updateEvent = new cc.EventHandler();
/** 结束事件 */
@ -69,25 +73,36 @@ export class BezierCurveAnimation extends Component {
endEvent = new cc.EventHandler();
/* --------------- private --------------- */
/* ------------------------------- segmentation ------------------------------- */
/** 开始缓动 */
startTween(): cc.Tween<any> {
/**
*
* @param startIndexN_
* @param endIndexN_
* @returns
*/
startTween(startIndexN_?: number, endIndexN_ = (startIndexN_ ?? 0) + 1): cc.Tween<any> {
let tweenUnitAs = this.tweenUnitAs;
if (startIndexN_ !== undefined) {
tweenUnitAs = tweenUnitAs.slice(startIndexN_, endIndexN_);
}
/** 总时间(秒) */
let totalTimeSN = this.tweenUnitAs.reduce((preValue, currValue) => preValue + currValue.timeSN, 0);
let totalTimeSN = tweenUnitAs.reduce((preValue, currValue) => preValue + currValue.timeSN, 0);
/** 时间占比 */
let timeRatioNs: number[] = [];
{
let currN = 0;
this.tweenUnitAs.forEach((v, kN) => {
tweenUnitAs.forEach((v, kN) => {
let ratioN = v.timeSN / totalTimeSN;
currN += ratioN;
timeRatioNs.push(currN);
});
}
/** 曲线函数 */
let curveFS = this.tweenUnitAs.map((v) => {
let curveFS = tweenUnitAs.map((v) => {
if (v.customCurveB) {
let curve = new BezierCurve(v.controlPointV3S);
return curve.point.bind(curve) as (kN: number) => number;
return (kN: number) => {
return curve.point(kN).y;
};
} else {
return cc.easing[easingEnum[v.easing]].bind(cc.easing) as (kN: number) => number;
}
@ -118,13 +133,17 @@ export class BezierCurveAnimation extends Component {
/** 曲线位置 */
let posN = (ratioN - lastTimeRatioN) / timeRangeN;
/** 曲线位置 */
let yN = curveFS[tweenIndexN](posN) * timeRangeN + lastTimeRatioN;
let yN = curveFS[tweenIndexN](posN);
let y2N = yN * timeRangeN + lastTimeRatioN;
// 缓动切换事件触发
if (lastTweenIndexN !== tweenIndexN) {
this.tweenSwitchEvent?.emit([lastTweenIndexN]);
}
if (y2N === undefined) {
debugger;
}
// 更新事件触发
this.updateEvent?.emit([yN]);
this.updateEvent?.emit([y2N, tweenIndexN, yN]);
// 更新缓动下标
lastTweenIndexN = tweenIndexN;
}

View File

@ -1,7 +1,8 @@
import { _decorator, Component, Node } from 'cc';
import * as cc from 'cc';
import { BezierCurveAnimation } from './BezierCurveAnimation';
const { ccclass, property, requireComponent } = _decorator;
import { EDITOR } from 'cc/env';
const { ccclass, property, requireComponent, executeInEditMode } = _decorator;
/** 旋转抽奖方向 */
export enum RollingLotteryDirection {
@ -30,19 +31,27 @@ export class RollingLottery extends Component {
/** transform 组件 */
private _uiTransform: cc.UITransform;
/** 周长 */
private _perimeterN: number;
/** 当前距离 */
private _currDistN = 0;
private _perimeterV3 = cc.v3();
/** 总距离 */
private _totalDistN: number;
private _totalDistV3: cc.Vec3;
/** 当前下标 */
private _currIndexN: number;
private _currIndexN = 0;
/** 子节点大小 */
private _ItemSize: cc.Size;
/** 动状态 */
/** 动状态 */
private _scrollB = false;
/** 循环状态 */
private _loopScrollB = false;
/** 循环缓动 */
private _loopTween: cc.Tween<any>;
/** 自己矩形 */
private _selfRect = cc.rect();
/** 上次曲线 Y */
private _lastCurveYN = 0;
/** 当前目标 */
private _currTargetN: number;
/** 当前缓动下标 */
private _currTweenN = 0;
/* --------------- 临时变量 --------------- */
private _tempM4 = cc.mat4();
private _temp2M4 = cc.mat4();
@ -53,92 +62,175 @@ export class RollingLottery extends Component {
this._initEvent();
}
/* ------------------------------- 功能 ------------------------------- */
/**
*
* @param currIndexN_
* @param targetIndexN_
* @returns
*/
private _getItemMovePos(currIndexN_: number, targetIndexN_: number): void {
/** 当前节点 */
let currNode = this.node.getChildByName(currIndexN_ + '');
/** 节点矩形 */
let currRect = this._getBoundingBoxToWorld(currNode);
/** 移动距离 */
let moveDistV3 = cc.v3(this._ItemSize.x, this._ItemSize.y).multiplyScalar(targetIndexN_ - currIndexN_);
/** 移动距离 */
let moveDistN: number;
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
moveDistV3.y = 0;
moveDistN = moveDistV3.x;
} else {
moveDistV3.x = 0;
moveDistN = moveDistV3.y;
}
currRect.x += moveDistV3.x;
currRect.y += moveDistV3.y;
/** 终点坐标 */
let endPosV3 = this.node
.getChildByName(currIndexN_ + '')
.worldPosition.clone()
.add(moveDistV3);
/** 圈数 */
let circleN = Math.floor(moveDistN / this._perimeterN);
// /** 额外移动距离 */
// let extraMoveDistN = moveDistN - circleN * this._perimeterN;
}
/** 获取在世界坐标系下的节点包围盒(不包含自身激活的子节点范围) */
private _getBoundingBoxToWorld(node_: cc.Node): cc.Rect {
node_.getWorldMatrix(this._temp2M4);
cc.Mat4.fromRTS(this._tempM4, this.node.getRotation(), this.node.getPosition(), this.node.getScale());
let width = this._uiTransform.contentSize.width;
let height = this._uiTransform.contentSize.height;
let rect = new cc.Rect(
-this._uiTransform.anchorPoint.x * width,
-this._uiTransform.anchorPoint.y * height,
width,
height
);
let uiTransform = node_.getComponent(cc.UITransform);
cc.Mat4.fromRTS(this._tempM4, node_.getRotation(), node_.getPosition(), node_.getScale());
const width = uiTransform.contentSize.width;
const height = uiTransform.contentSize.height;
const rect = cc.rect(-uiTransform.anchorPoint.x * width, -uiTransform.anchorPoint.y * height, width, height);
node_.parent.getWorldMatrix(this._temp2M4);
cc.Mat4.multiply(this._temp2M4, this._temp2M4, this._tempM4);
rect.transformMat4(this._temp2M4);
return rect;
}
/** 检测参数节点是否与当前节点碰撞 */
private _checkCollision(node_: cc.Node): boolean {
let rect = this._getBoundingBoxToWorld(this.node);
let rect2 = this._getBoundingBoxToWorld(node_);
// 增加保险范围
rect.width += rect.width * 0.5;
rect.height += rect.height * 0.5;
rect.x -= rect.width * 0.25;
rect.y -= rect.height * 0.25;
return rect.intersects(rect2);
/**
*
* @param distV3_
*/
private _scrollChild(distV3_: cc.Vec3): void {
/** 当前节点矩形 */
let currNodeRect: cc.Rect;
/** 更新节点坐标 */
let updatePosB = false;
/** 当前节点 UITransform */
let currTransform: cc.UITransform;
/** 头节点 */
let headNode = this.node.children[0];
/** 尾节点 */
let tailNode = this.node.children[this.node.children.length - 1];
/** 头节点矩形 */
let headNodeRect: cc.Rect;
/** 尾节点矩形 */
let tailNodeRect: cc.Rect;
// 左右滚动
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
}
// 上下滚动
else {
// 从上往下滚动
if (distV3_.y < 0) {
this.node.children.forEach((v, kN) => {
currTransform = v.getComponent(cc.UITransform);
updatePosB = false;
currNodeRect = this._getBoundingBoxToWorld(v);
// 移动坐标
currNodeRect.y += distV3_.y;
// 相交则更新节点坐标
if (currNodeRect.intersects(this._selfRect)) {
updatePosB = true;
}
// 若不相交则超出范围
else {
// 若节点在上方则跳过更新
if (currNodeRect.yMin > this._selfRect.yMax) {
updatePosB = true;
} else {
// setTimeout 防止获取节点坐标错误、防止 setSiblingIndex 后遍历错误
setTimeout(() => {
// 切换位置到头节点上方
headNodeRect = this._getBoundingBoxToWorld(headNode);
v.worldPosition = cc.v3(
v.worldPosition.x,
headNodeRect.yMax + currNodeRect.height * currTransform.anchorY
);
// 更新 item
let indexN = Number(headNode.name) - 1;
v.name = indexN + '';
this.itemUpdateEvent.emit([v, indexN]);
// 更新 item 下标
headNode = v;
v.setSiblingIndex(0);
}, 0);
}
}
// 更新节点坐标
if (updatePosB) {
v.worldPosition = cc.v3(
v.worldPosition.x,
currNodeRect.y + currNodeRect.height * currTransform.anchorY
);
}
});
}
// 从下往上滚动
else {
this.node.children.forEach((v, kN) => {
currTransform = v.getComponent(cc.UITransform);
updatePosB = false;
currNodeRect = this._getBoundingBoxToWorld(v);
// 移动坐标
currNodeRect.y += distV3_.y;
// 相交则更新节点坐标
if (currNodeRect.intersects(this._selfRect)) {
updatePosB = true;
}
// 若不相交则超出范围
else {
// 若节点在下方则跳过更新
if (this._selfRect.yMin > currNodeRect.yMax) {
updatePosB = true;
} else {
// setTimeout 防止获取节点坐标错误、防止 setSiblingIndex 后遍历错误
setTimeout(() => {
// 切换位置到尾节点下方
tailNodeRect = this._getBoundingBoxToWorld(tailNode);
v.worldPosition = cc.v3(
v.worldPosition.x,
tailNodeRect.yMin - currNodeRect.height * (1 - currTransform.anchorY)
);
// 更新 item
let indexN = Number(tailNode.name) + 1;
v.name = indexN + '';
this.itemUpdateEvent.emit([v, indexN]);
// 更新 item 下标
tailNode = v;
v.setSiblingIndex(this.node.children.length - 1);
}, 0);
}
}
// 更新节点坐标
if (updatePosB) {
v.worldPosition = cc.v3(
v.worldPosition.x,
currNodeRect.y + currNodeRect.height * currTransform.anchorY
);
}
});
}
}
}
/** 更新运动距离 */
private _updateMoveDist(indexN_: number): void {
/** 当前节点 */
let currNode = this.node.getChildByName(this._currIndexN + '');
/** 间隔格子 */
let intervalN = indexN_ - this._currIndexN;
/** 格子距离 */
let boxDistN = this.dire === RollingLotteryDirection.HORIZONTAL ? this._ItemSize.width : this._ItemSize.height;
/** 超出当前格子距离 */
let overDistN = this._currDistN - boxDistN * this._currIndexN;
/** 当前格子距父节点(0, 0)的偏移坐标 */
let offsetDistV3 = this.node.worldPosition.clone().subtract(currNode.worldPosition);
// 设置总距离
this._totalDistN = intervalN * boxDistN - overDistN;
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
this._totalDistV3 = cc.v3(intervalN * boxDistN + offsetDistV3.x);
} else {
this._totalDistV3 = cc.v3(0, intervalN * boxDistN + offsetDistV3.y);
}
}
/** 更新数据 */
private _updateData(): void {
// 单圈长度
this._perimeterN = 0;
this._perimeterV3.set(cc.Vec3.ZERO);
let size: cc.Size;
this.node.children.forEach((v1) => {
this._perimeterN += v1.getComponent(cc.UITransform).height;
size = v1.getComponent(cc.UITransform).contentSize;
this._perimeterV3.add3f(size.x, size.y, 0);
});
// 重置距离
this._currDistN = 0;
}
/** 初始化数据 */
@ -147,29 +239,31 @@ export class RollingLottery extends Component {
this._uiTransform = this.node.getComponent(cc.UITransform);
this._ItemSize = this.node.children[0].getComponent(cc.UITransform).contentSize.clone();
// 自己矩形
this._selfRect = this._getBoundingBoxToWorld(this.node);
// 设置更新事件
this._curveComp.updateEvent.component = cc.js.getClassName(this);
this._curveComp.updateEvent.handler = 'updateEvent';
this._curveComp.updateEvent.target = this.node;
// 设置结束事件
this._curveComp.endEvent.component = cc.js.getClassName(this);
this._curveComp.endEvent.handler = 'updateEvent';
// 更新当前距离
{
let distV3 = this.node.worldPosition.clone().subtract(this.node.children[0].worldPosition);
this._currDistN = this.dire === RollingLotteryDirection.HORIZONTAL ? distV3.x : distV3.y;
}
// 自己矩形
this._selfRect = this._getBoundingBoxToWorld(this.node);
this._curveComp.endEvent.handler = 'endEvent';
this._curveComp.endEvent.target = this.node;
this._updateData();
}
/** 初始化视图 */
private _initView(): void {
this.scroll(0);
// 重置子节点
this.node.children.forEach((v, kN) => {
v.name = kN + this._currIndexN + '';
this.itemUpdateEvent.emit([v, kN + this._currIndexN]);
});
this.jump(this._currIndexN);
}
/** 初始化事件 */
@ -179,41 +273,105 @@ export class RollingLottery extends Component {
/**
*
* @param speedN_
* @param speedN_ /
* @param timeSN_
*/
loop(speedN_: number, timeSN_?: number): void {}
/** 滚动到指定下标 */
scroll(indexN_: number, timeSN_?: number): void {
loop(speedN_: number, timeSN_?: number): void {
if (this._scrollB) {
return;
}
this._scrollB = true;
this._updateMoveDist(indexN_);
/** 移动距离 */
let moveDistN = this._totalDistN - this._currDistN;
// 直接跳转
if (!timeSN_) {
/** 圈数 */
let circleN = Math.floor(moveDistN / this._perimeterN);
/** 额外移动距离 */
let extraMoveDistN = moveDistN - circleN * this._perimeterN;
this._loopScrollB = true;
let distV3 = cc.v3();
this._loopTween = cc
.tween({ valueN: 0, lastValueN: 0 })
.repeatForever(
cc.tween().by(
1,
{
valueN: 1
},
{
onUpdate: (target?: any, ratioN?: number) => {
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
distV3.x = (target.valueN - target.lastValueN) * speedN_;
} else {
distV3.y = (target.valueN - target.lastValueN) * speedN_;
}
this._scrollChild(distV3);
target.lastValueN = target.valueN;
}
}
)
)
.start();
if (timeSN_ !== undefined) {
this.scheduleOnce(() => {
this._loopTween.stop();
this._loopTween = null;
this._loopScrollB = false;
}, timeSN_);
}
}
/* ------------------------------- 自定义事件 ------------------------------- */
tweenSwitchEvent(indexN_: number): void {
// cc.log('缓动切换', indexN_);
}
updateEvent(yN_: number): void {
// cc.log('缓动更新', yN_);
}
endEvent(): void {
// cc.log('缓动结束');
/** 跳转到指定下标 */
jump(indexN_: number): void {
if (this._scrollB) {
return;
}
this._scrollB = true;
// 更新移动距离
this._updateMoveDist(indexN_);
// 开始滚动
this._scrollChild(this._totalDistV3);
// 更新状态
this._currIndexN = indexN_;
this._scrollB = false;
}
/** 子节点更新 */
childUpdate(node_: cc.Node, indexN_: number): void {
node_.getComponentInChildren(cc.Label).string = indexN_ + '';
/** 滚动到指定下标 */
scroll(indexN_: number): void {
if (this._scrollB && !this._loopScrollB) {
return;
}
this._scrollB = true;
// 停止循环滚动
if (this._loopScrollB) {
this._loopTween.stop();
this._loopTween = null;
this._loopScrollB = false;
}
this._currTargetN = indexN_;
this._lastCurveYN = 0;
// 更新移动距离
this._updateMoveDist(indexN_);
// 开始滚动
this._currTweenN = 0;
this._curveComp.startTween(this._currTweenN);
}
/* ------------------------------- 自定义事件 ------------------------------- */
updateEvent(yN_: number, indexN_: number, y2N_: number): void {
if (this.dire === RollingLotteryDirection.HORIZONTAL) {
} else {
this._scrollChild(cc.v3(0, (yN_ - this._lastCurveYN) * this._totalDistV3.y));
}
this._lastCurveYN = yN_;
if (yN_ === undefined) {
debugger;
}
// cc.log('缓动更新', yN_, indexN_, y2N_, yN_ - this._lastCurveYN);
}
endEvent(): void {
this._scrollB = false;
// 继续缓动
this._currTweenN++;
if (this._currTweenN < this._curveComp.tweenUnitAs.length) {
this._curveComp.startTween(this._currTweenN);
}
}
/* ------------------------------- 节点事件 ------------------------------- */
private _nodeSiblingOrderChanged(): void {

File diff suppressed because it is too large Load Diff

21
preview-template/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018
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.

View File

@ -0,0 +1,61 @@
# ccc-devtools v3.0.0
Cocos Creator 网页调试工具,运行时查看、修改节点树,实时更新节点属性,可视化缓存资源。
## 功能
- 场景节点树实时显示,节点、组件属性实时显示更改
![preview](./screenshots/preview1.gif)
- 可视化缓存资源
![preview](./screenshots/preview2.png)
- 标记场景中节点位置
![preview](./screenshots/preview3.png)
- 输出节点、组件引用到控制台
![preview](./screenshots/preview4.png)
- cc控制台功能扩展
![preview](./screenshots/preview5.png)
## 全局使用
Cocos Creator 3.0暂不支持全局使用
## 项目使用
- `cd PROJECT_PATH && git clone -b v3.0.0 https://github.com/potato47/ccc-devtools.git preview-template`
- 或者手动将本项目对应分支下载到项目目录后,将名字改为 `preview-template`
## 自定义
- 本项目使用了 vue 和 vuetify可根据 [vuetify 文档](https://vuetifyjs.com/en/getting-started/quick-start/) 对页面进行修改
- 节点、组件显示属性可在 `config.js` 里配置,目前支持 textnumbertextareacolorbool 几种类型
## 需求、更新
https://github.com/potato47/ccc-devtools
如果没有更改源码,可直接在目录下 git pull
论坛讨论地址https://forum.cocos.com/t/creator-20190201/71578
## 贡献指南
- 版本号命名规则 https://semver.org/lang/zh-CN/ ,简单来讲新功能第二位加一修复bug第三位加一
- 如果新增功能请在README中添加预览截图说明
- 记得更新version.json中的版本号
## 前人种树
- https://github.com/vuejs/vue
- https://github.com/vuetifyjs/vuetify

View File

@ -0,0 +1,68 @@
const NEX_CONFIG = {
nodeSchema: {
node: {
title: 'Node',
key: 'cc.Node',
rows: [
{ name: 'Name', key: 'name', type: 'text' },
{ name: 'Position.X', parentKey: 'position', key: 'x', type: 'object_number', method: 'setPosition' },
{ name: 'Position.Y', parentKey: 'position', key: 'y', type: 'object_number', method: 'setPosition' },
{ name: 'Position.Z', parentKey: 'position', key: 'z', type: 'object_number', method: 'setPosition' },
{ name: 'Angle.X', parentKey: 'eulerAngles', key: 'x', type: 'object_number', method: 'setRotationFromEuler' },
{ name: 'Angle.Y', parentKey: 'eulerAngles', key: 'y', type: 'object_number', method: 'setRotationFromEuler' },
{ name: 'Angle.Z', parentKey: 'eulerAngles', key: 'z', type: 'object_number', method: 'setRotationFromEuler' },
{ name: 'Scale.X', parentKey: 'scale', key: 'x', type: 'object_number', method: 'setScale' },
{ name: 'Scale.Y', parentKey: 'scale', key: 'y', type: 'object_number', method: 'setScale' },
{ name: 'Scale.Z', parentKey: 'scale', key: 'z', type: 'object_number', method: 'setScale' },
]
},
},
componentsSchema: {
'cc.Camera': {
title: 'cc.Camera',
key: 'cc.Camera',
rows: [
{ name: 'ClearDepth', key: 'clearDepth', type: 'number' },
{ name: 'ClearColor', key: 'hex_clearColor', rawKey: 'clearColor', type: 'color' },
]
},
'cc.DirectionalLight': {
title: 'cc.DirectionalLight',
key: 'cc.DirectionalLight',
rows: [
{ name: 'UseColorTemperature', key: 'useColorTemperature', type: 'bool' },
{ name: 'ColorTemperature', key: 'colorTemperature', type: 'number' },
{ name: 'Illuminance', key: 'illuminance', type: 'number' },
{ name: 'Color', key: 'hex_color', rawKey: 'color', type: 'color' },
]
},
'cc.UITransform': {
key: 'cc.UITransform',
title: 'cc.UITransform',
rows: [
{ name: 'Width', key: 'width', type: 'number' },
{ name: 'Height', key: 'height', type: 'number' },
{ name: 'AnchorX', key: 'anchorX', type: 'number' },
{ name: 'AnchorY', key: 'anchorY', type: 'number' },
{ name: 'Priority', key: 'priority', type: 'number' },
]
},
'cc.Sprite': {
key: 'cc.Sprite',
title: 'cc.Sprite',
rows: [
{ name: 'Color', key: 'hex_color', rawKey: 'color', type: 'color' },
]
},
'cc.Label': {
title: 'cc.Label',
key: 'cc.Label',
rows: [
{ name: 'String', key: 'string', type: 'textarea' },
{ name: 'Font Size', key: 'fontSize', type: 'number' },
{ name: 'Line Height', key: 'lineHeight', type: 'number' },
{ name: 'Color', key: 'hex_color', rawKey: 'color', type: 'color' },
]
}
}
}

View File

@ -0,0 +1,257 @@
<link href="./libs/css/materialdesignicons.min.css" rel="stylesheet"
type="text/css">
<link href="./libs/css/vuetify.min.css" rel="stylesheet" type="text/css">
<style>
html {
overflow-y: auto;
}
</style>
<v-app id="app">
<v-app-bar app clipped-left color="gray" dense v-if="isShowTop">
<v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
<div id="recompiling"><span>Recompiling...</span></div>
<v-spacer></v-spacer>
<%- include(cocosToolBar, {config: config}) %>
<div class="toolbar">
<!-- <div class="toolbar disabled"> -->
<!-- <div class="item">
<select id="opts-device" value="<%=config.device%>">
<% Object.keys(devices).forEach((key) => {%>
<option value="<%=key%>"><%=devices[key].name%>(<%=devices[key].width%> X <%=devices[key].height%>)</option>
<% }) %>
</select>
</div>
<div class="item"><button id="btn-rotate" class="<%=config.rotate ? 'checked' : ''%>">Rotate</button></div>
<span style="font-size: small;" class="item">Debug Mode:</span>
<div class="item">
<select id="opts-debug-mode" value="<%=config.debugMode%>">
<option value="0">None</option>
<option value="1">Info</option>
<option value="2">Warn</option>
<option value="3">Error</option>
<option value="4">Info For Web Page</option>
<option value="5">Warn For Web Page</option>
<option value="6">Error For Web Page</option>
</select>
</div>
<div class="item"><button id="btn-show-fps" class="<%=config.showFps ? 'checked' : ''%>">Show FPS</button></div>
<div class="item">
<span style="font-size: small;" class="item">FPS:</span><input id="input-set-fps" type="number" value="<%=config.fps%>" />
</div>
<div style="margin-right: 0;" class="item"><button id="btn-pause">Pause</button><button id="btn-step">Step</button></div>
<div class="item"><button id="btn-step" style="display: none;">Step</button></div> -->
<v-icon @click="openCocosDocs" small>mdi-cloud-search</v-icon>
<v-icon @click="openCocosForum" small>mdi-forum</v-icon>
<v-icon @click="openCacheDialog" small>mdi-table</v-icon>
<v-icon @click="openGithub" small>mdi-home</v-icon>
</div>
</v-app-bar>
<div v-if="!isShowTop">
<div id="recompiling"><span>Recompiling...</span></div>
<div class="toolbar">
<div class="item">
<select id="opts-device">
<option value="0">Default</option>
</select>
</div>
<div class="item">
<v-btn id="btn-rotate" small height="25"><span style="color: #aaa;">Rotate</span></v-btn>
</div>
<span style="font-size: small;display: none;" class="item">Debug Mode:</span>
<div class="item" style="display: none;">
<select id="opts-debug-mode">
<option value="0">None</option>
<option value="1">Info</option>
<option value="2">Warn</option>
<option value="3">Error</option>
<option value="4">Info For Web Page</option>
<option value="5">Warn For Web Page</option>
<option value="6">Error For Web Page</option>
</select>
</div>
<div class="item">
<v-btn id="btn-show-fps" small height="25"><span style="color: #aaa;">Show FPS</span></v-btn>
</div>
<div class="item">
<span style="font-size: small;color: #aaa;" class="item">FPS:</span><input id="input-set-fps"
type="number" />
</div>
<div style="margin-right: 0px;" class="item">
<v-btn id="btn-pause" small height="25"><span style="color: #aaa;">Pause</span></v-btn>
</div>
<div class="item">
<v-btn id="btn-step" style="display: none;" small height="25">
<span style="color: #aaa;">Step</span>
</v-btn>
</div>
<div class="item">
<v-btn id="btn-recompile" small height="25"><span style="color: #aaa;">Recompile</span></v-btn>
</div>
</div>
</div>
<v-navigation-drawer v-model="drawer" app clipped fixed width="512" v-if="isShowTop">
<v-container style="height: 50%;overflow: auto;">
<v-text-field v-model="treeSearchText" dense label="Search Node or Component" dark flat solo-inverted
hide-details clearable clear-icon="mdi-close-circle-outline"></v-text-field>
<v-treeview :items="treeData" item-key="id" dense activatable :search="treeSearchText"
:active.sync="selectedNodes">
<template v-slot:label="{ item, active, open }">
<label v-if="item.active" style="color: white;">{{ item.name }}</label>
<label v-else style="color: gray;">{{ item.name }}</label>
</template>
</v-treeview>
</v-container>
<v-container style="border-top: 2px solid darkgray;height: 50%;overflow-y: auto;">
<template v-if="selectedNode && selectedNode.parent">
<!-- Node -->
<table style="width: 100%;color: white;" border="1">
<thead>
<tr>
<th colspan="2" style="text-align: left; padding: 10px;">
<div class="float-left" style="display:inline-flex;">
<v-simple-checkbox v-model="selectedNode.active"></v-simple-checkbox>
<span style="margin-left: 10px;">{{ nodeSchema.title }}</span>
</div>
<div class="float-right">
<v-icon style="margin-left: 10px;margin-right: 10px;" @click="drawNodeRect()">
mdi-adjust</v-icon>
<v-icon @click="outputNodeHandler()">mdi-send</v-icon>
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in nodeSchema.rows" :key="row.name">
<td style="padding: 10px;width: 40%;">{{ row.name }}</td>
<td style="width: 60%;">
<v-color-picker v-if="row.type == 'color'" class="ma-2" canvas-height="80" width="259"
v-model="selectedNode[row.key]"></v-color-picker>
<v-simple-checkbox v-else-if="row.type == 'bool'" v-model="selectedNode[row.key]"
style="padding: 10px;width: 100%;"></v-simple-checkbox>
<input v-else-if="row.type == 'object_number'" type="number" v-model.number="selectedNode[row.parentKey][row.key]"
style="padding: 10px;width: 100%;" @input="onSubPropInput(selectedNode, row)"></input>
<input v-else :type="row.type" v-model="selectedNode[row.key]"
style="padding: 10px;width: 100%;"></input>
</td>
</tr>
</tbody>
</table>
<!-- Components -->
<table v-for="component in componentsSchema" style="width: 100%;color: white;" border="1">
<thead>
<tr>
<th colspan="2" style="text-align: left; padding: 10px;">
<div class="float-left" style="display:inline-flex;">
<v-simple-checkbox v-model="selectedNode[component.key].enabled">
</v-simple-checkbox>
<span style="margin-left: 10px;">{{ component.title }}</span>
</div>
<div class="float-right">
<v-icon @click="outputComponentHandler(component.key)">mdi-send</v-icon>
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in component.rows" :key="row.key">
<td style="padding: 10px;width: 40%;">{{ row.name }}</td>
<td style="width: 60%;">
<v-color-picker v-if="row.type == 'color'" class="ma-2" canvas-height="80" width="259"
v-model="selectedNode[component.key][row.key]"></v-color-picker>
<textarea v-else-if="row.type == 'textarea'" rows="1"
v-model="selectedNode[component.key][row.key]" style="padding: 10px;width: 100%;">
</textarea>
<v-simple-checkbox v-else-if="row.type == 'bool'"
v-model="selectedNode[component.key][row.key]" style="padding: 10px;width: 100%;">
</v-simple-checkbox>
<input v-else-if="row.type == 'number'" type="number" v-model.number="selectedNode[component.key][row.key]"
style="padding: 10px;width: 100%;"></input>
<input v-else :type="row.type" v-model="selectedNode[component.key][row.key]"
style="padding: 10px;width: 100%;"></input>
</td>
</tr>
</tbody>
</table>
</template>
<template v-else-if="selectedNode && !selectedNode.parent">
<!-- TODO:场景根节点 -->
</template>
</v-container>
</v-navigation-drawer>
<v-content>
<v-container fill-height>
<div id="content" class="content">
<div class="contentWrap">
<div id="GameDiv" class="wrapper">
<canvas id="GameCanvas"></canvas>
<div id="splash">
<div class="progress-bar stripes"><span></span></div>
</div>
<div id="bulletin">
<div id="sceneIsEmpty" class="inner"><%=tip_sceneIsEmpty%></div>
</div>
<div class="error" id="error">
<div class="title">Error <i>(Please open the console to see detailed errors)</i></div>
<div class="error-main"></div>
</div>
</div>
</div>
<p class="footer">
Created with <a href="https://www.cocos.com/products" target="_blank" title="Cocos Creator 3D">Cocos Creator 3D</a>
</p>
</div>
</v-container>
</v-content>
<v-dialog v-model="cacheDialog" persistent scrollable>
<v-card>
<v-card-title>
{{ cacheTitle }}
<v-spacer></v-spacer>
<v-text-field v-model="cacheSearchText" append-icon="mdi-magnify" label="Search" single-line
hide-details>
</v-text-field>
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<v-data-table :headers="cacheHeaders" :items="cacheData" :search="cacheSearchText" :sort-by="['size']"
click:row="openGithub"
:sort-desc="[true]" :footer-props="{
showFirstLastPage: true,
firstIcon: 'mdi-chevron-double-left',
lastIcon: 'mdi-chevron-double-right',
}">
<template v-slot:item.size="{ item }">
{{ item.size == -1 ? '_' : (item.size +'MB') }}
</template>
<template v-slot:item.preview="{ item }">
<div style="height: 60px;display: flex;align-items: center;">
<img :src="window.location.protocol + '//' + window.location.host + '/' + item.preview"
style="max-height: 60px;max-width: 120px;" v-if="item.preview">
<template v-else>_</template>
</div>
</template>
</v-data-table>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn color="blue darken-1" text @click="cacheDialog = false">Close</v-btn>
<v-spacer></v-spacer>
<v-switch v-model="cacheOnlyTexture" label="只显示纹理"></v-switch>
</v-card-actions>
</v-card>
</v-dialog>
</v-app>
<script src="./libs/js/vue.min.js"></script>
<script src="./libs/js/vuetify.js"></script>
<script src="./config.js"></script>
<script src="./libs/js/cc-console-utils.js"></script>
<script src="./preview.js"></script>
<%- include(cocosTemplate, {}) %>

View File

@ -0,0 +1,23 @@
<html>
<head>
<link rel="icon" href="./favicon.ico" />
<meta charset="utf-8" />
<title><%=title%></title>
<meta
name="viewport"
content="width=device-width,user-scalable=no,initial-scale=1, minimum-scale=1,maximum-scale=1"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="full-screen" content="yes" />
<meta name="screen-orientation" content="portrait" />
<meta name="x5-fullscreen" content="true" />
<meta name="360-fullscreen" content="true" />
<meta name="renderer" content="webkit" />
<meta name="force-rendering" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link rel="stylesheet" type="text/css" href="./index.css" />
</head>
<body>
<%- include("./custom.html") %>
</body>
</html>

View File

@ -0,0 +1,336 @@
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff22) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/googlefonts-base.woff2format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/googlefonts-base.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/googlefonts-base.woff2rmat('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/googlefonts-base.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/googlefonts-base.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2f2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(../fonts/googlefonts-base.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,178 @@
const initConsoleUtil = function () {
if (cc.tree) return;
cc.tree = function (key) {
let index = key || 0;
let treeNode = function (node) {
let nameStyle =
`color: ${node.parent === null || node.activeInHierarchy ? 'green' : 'grey'}; font-size: 14px;font-weight:bold`;
let propStyle =
`color: black; background: lightgrey;margin-left: 5px;border-radius:3px;padding: 0 3px;font-size: 10px;font-weight:bold`;
let indexStyle =
`color: orange; background: black;margin-left: 5px;border-radius:3px;padding:0 3px;fonrt-size: 10px;font-weight:bold;`
let nameValue = `%c${node.name}`;
let propValue =
`%c${node.position.x.toFixed(0) + ',' + node.position.y.toFixed(0)}`
let indexValue = `%c${index++}`;
if (node.children.length > 0) {
console.groupCollapsed(nameValue + propValue + indexValue, nameStyle,
propStyle, indexStyle);
for (let i = 0; i < node.children.length; i++) {
treeNode(node.children[i]);
}
console.groupEnd();
} else {
console.log(nameValue + propValue + indexValue, nameStyle, propStyle,
indexStyle);
}
}
if (key) {
let node = cc.cat(key);
index = node['tempIndex'];
treeNode(node);
} else {
let scene = cc.director.getScene();
treeNode(scene);
}
return '属性依次为x,y,width,height,scale.使用cc.cat(id)查看详细属性.';
}
cc.cat = function (key) {
let index = 0;
let target;
let sortId = function (node) {
if (target) return;
if (cc.js.isNumber(key)) {
if (key === index++) {
target = node;
return;
}
} else {
if (key.toLowerCase() === node.name.toLowerCase()) {
target = node;
return;
} else {
index++;
}
}
if (node.children.length > 0) {
for (let i = 0; i < node.children.length; i++) {
sortId(node.children[i]);
}
}
}
let scene = cc.director.getScene();
sortId(scene);
target['tempIndex'] = cc.js.isNumber(key) ? key : index;
return target;
}
cc.list = function (key) {
let targets = [];
let step = function (node) {
if (node.name.toLowerCase().indexOf(key.toLowerCase()) > -1) {
targets.push(node);
}
if (node.children.length > 0) {
for (let i = 0; i < node.children.length; i++) {
step(node.children[i]);
}
}
}
let scene = cc.director.getScene();
step(scene);
if (targets.length === 1) {
return targets[0];
} else {
return targets;
}
}
cc.where = function (key) {
let target = key.name ? key : cc.cat(key);
if (!target) {
return null;
}
let rect;
let transform = target.getComponent(cc.UITransformComponent);
if (transform) {
rect = getSelfBoundingBoxToWold(transform);
} else {
let worldPos = cc.v3();
target.getWorldPosition(worldPos);
rect = cc.rect(worldPos.x, worldPos.y, 0, 0);
}
let canvasNode = new cc.Node('Canvas');
let scene = cc.director.getScene();
scene.addChild(canvasNode);
canvasNode.addComponent(cc.Canvas);
let bgNode = new cc.Node();
let graphics = bgNode.addComponent(cc.GraphicsComponent);
let bgTransform = bgNode.addComponent(cc.UITransformComponent);
canvasNode.addChild(bgNode);
let centerPos = cc.v3(rect.center.x, rect.center.y, 0);
let localPos = cc.v3();
canvasNode.getComponent(cc.UITransformComponent).convertToNodeSpaceAR(centerPos, localPos);
bgNode.setPosition(localPos);
bgNode.layer = target.layer;
let isZeroSize = rect.width === 0 || rect.height === 0;
if (isZeroSize) {
graphics.circle(0, 0, 100);
graphics.fillColor = cc.Color.GREEN;
graphics.fill();
} else {
bgTransform.width = rect.width;
bgTransform.height = rect.height;
graphics.rect(-bgTransform.width / 2, -bgTransform.height / 2, bgTransform.width, bgTransform.height);
graphics.fillColor = new cc.Color().fromHEX('#E91E6390');
graphics.fill();
}
setTimeout(() => {
if (cc.isValid(canvasNode)) {
canvasNode.destroy();
}
}, 2000);
return target;
}
cc.cache = function () {
let rawCacheData = cc.loader._cache;
let cacheData = [];
let totalTextureSize = 0;
for (let k in rawCacheData) {
let item = rawCacheData[k];
let preview = '';
let type = item.__classname__;
let formatSize = -1;
if (type === 'cc.Texture2D') {
let image = item.mipmaps[0]
preview = image.url;
let textureSize = image.width * image.height * ((image._native === '.jpg' ? 3 : 4) / 1024 / 1024);
totalTextureSize += textureSize;
// sizeStr = textureSize.toFixed(3) + 'M';
formatSize = Math.round(textureSize * 1000) / 1000;
}
cacheData.push({
type: type,
preview: preview,
id: item._uuid,
size: formatSize
});
}
let cacheTitle = `缓存 [文件总数:${cacheData.length}][纹理缓存:${totalTextureSize.toFixed(2) + 'M'}]`;
return [cacheData, cacheTitle];
}
}
function getSelfBoundingBoxToWold(transform) {
let _worldMatrix = cc.mat4();
if (transform.node.parent) {
transform.node.parent.getWorldMatrix(_worldMatrix);
let parentMat = _worldMatrix;
let _matrix = cc.mat4();
cc.Mat4.fromRTS(_matrix, transform.node.getRotation(), transform.node.getPosition(), transform.node.getScale());
const width = transform._contentSize.width;
const height = transform._contentSize.height;
const rect = cc.rect(-transform._anchorPoint.x * width, -transform._anchorPoint.y * height, width, height);
cc.Mat4.multiply(_worldMatrix, parentMat, _matrix);
rect.transformMat4(_worldMatrix);
return rect;
} else {
return transform.getBoundingBox();
}
}

6
preview-template/libs/js/vue.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

201
preview-template/preview.js Normal file
View File

@ -0,0 +1,201 @@
const app = new Vue({
el: '#app',
vuetify: new Vuetify({
theme: { dark: true }
}),
data: {
isShowTop: true,
drawer: false,
cacheDialog: false,
cacheTitle: '',
cacheHeaders: [
{ text: 'Type', value: 'type' },
{ text: 'Preivew', value: 'preview' },
{ text: 'ID', value: 'id' },
{ text: 'Size', value: 'size' },
],
cacheRawData: [],
cacheData: [],
cacheSearchText: null,
cacheOnlyTexture: true,
treeData: [],
selectedNodes: [],
intervalId: -1,
treeSearchText: null,
nodeSchema: {},
componentsSchema: [],
},
created() {
if (window.innerHeight === window.outerHeight) { // 手机端chrome device模式
this.isShowTop = false;
}
this.waitCCInit().then(() => {
if (this.isShowTop) {
this.startUpdateTree();
}
initConsoleUtil();
});
},
watch: {
cacheOnlyTexture() {
this.updateCacheData();
}
},
computed: {
treeFilter() {
return (item, search, textKey) => item[textKey].indexOf(search) > -1;
},
selectedNode() {
if (!this.selectedNodes.length) return undefined
let node = getNodeById(this.selectedNodes[0]);
if (node) {
let superPreLoad = node._onPreDestroy;
node._onPreDestroy = () => {
superPreLoad.apply(node);
if (this.selectedNodes.length > 0 && this.selectedNodes[0] === node._id) {
this.selectedNodes.pop();
}
}
this.nodeSchema = NEX_CONFIG.nodeSchema.node;
let componentsSchema = [];
for (let component of node._components) {
let schema = NEX_CONFIG.componentsSchema[component.__classname__];
if (schema) {
node[schema.key] = node.getComponent(schema.key);
for (let i = 0; i < schema.rows.length; i++) {
if (schema.rows[i].type === 'color') {
if (!node[schema.key][schema.rows[i].key]) {
cc.js.getset(node[schema.key], schema.rows[i].key, () => {
return '#' + node.getComponent(schema.key)[schema.rows[i].rawKey].toHEX('#rrggbb');
}, (hex) => {
node.getComponent(schema.key)[schema.rows[i].rawKey] = new cc.Color().fromHEX(hex);
}, false, true);
}
}
}
} else {
schema = {
title: component.__classname__,
key: component.__classname__
};
node[schema.key] = node.getComponent(schema.key);
}
componentsSchema.push(schema);
}
this.componentsSchema = componentsSchema;
}
return node;
},
},
methods: {
waitCCInit() {
return new Promise((resolve, reject) => {
let id = setInterval(() => {
if (window.cc) {
resolve();
clearInterval(id);
}
}, 500);
});
},
refreshTree: function () {
if (!this.$data.drawer || !window.cc) {
return;
}
let currentScene = cc.director.getScene();
if (!currentScene || !currentScene.children) {
return;
}
// 包含场景根节点
// this.$data.treeData = [{ id: currentScene._id, name: 'Scene', active: currentScene.activeInHierarchy, open: true, children: getChildren(currentScene) }];
this.$data.treeData = getChildren(currentScene);
},
startUpdateTree: function () {
this.$data.intervalId = setInterval(() => {
this.refreshTree();
}, 200);
},
stopUpdateTree: function () {
clearInterval(this.$data.intervalId);
},
onSubPropInput: function (target, row) {
let value = target[row.parentKey][row.key];
if (typeof value === 'number' && !isNaN(value)) {
if (row.method === 'setRotationFromEuler') {
target.setRotationFromEuler(target.eulerAngles.x, target.eulerAngles.y, target.eulerAngles.z);
} else {
target[row.method](target[row.parentKey]);
}
}
},
outputNodeHandler(id) {
let i = 1;
while (window['temp' + i] !== undefined) {
i++;
}
window['temp' + i] = this.selectedNode;
console.log('temp' + i);
console.log(window['temp' + i]);
},
outputComponentHandler(component) {
let i = 1;
while (window['temp' + i] !== undefined) {
i++;
}
window['temp' + i] = this.selectedNode.getComponent(component);
console.log('temp' + i);
console.log(window['temp' + i]);
},
drawNodeRect() {
cc.where(this.selectedNode);
},
updateCacheData() {
if (this.$data.cacheOnlyTexture) {
this.$data.cacheData = this.$data.cacheRawData.filter(item => item.type === 'cc.Texture2D');
} else {
this.$data.cacheData = this.$data.cacheRawData;
}
},
openCacheDialog() {
[this.$data.cacheRawData, this.$data.cacheTitle] = cc.cache();
this.updateCacheData();
this.$data.cacheDialog = true;
},
openGithub() {
window.open('https://github.com/potato47/ccc-devtools');
},
openCocosForum() {
window.open('https://forum.cocos.com/');
},
openCocosDocs() {
window.open('https://docs.cocos.com/');
}
}
});
function getChildren(node) {
return node.children.map(child => {
let children = (child.children && child.children.length > 0) ? getChildren(child) : [];
return { id: child._id, name: child.name, active: child.activeInHierarchy, children };
});
}
function getNodeById(id) {
let target;
const search = function (node) {
if (node._id === id) {
target = node;
return;
}
if (node.children.length) {
for (let i = 0; i < node.children.length; i++) {
if (!target) {
search(node.children[i]);
}
}
}
}
const scene = cc.director.getScene();
search(scene);
return target;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB