mirror of
https://github.com/potato47/ccc-devtools.git
synced 2025-10-13 10:35:46 +00:00
改版;适配creator3.5
This commit is contained in:
39
src/App.vue
Normal file
39
src/App.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import TreePanel from './components/TreePanel.vue';
|
||||
import { ref } from 'vue';
|
||||
let showTree = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<vue-final-modal v-model="showTree" classes="modal-container" content-class="modal-content" :hide-overlay="true"
|
||||
:click-to-close="false" :prevent-click="true" :drag="true" :fit-parent="true" drag-selector=".modal-drag">
|
||||
<TreePanel :show="showTree"></TreePanel>
|
||||
</vue-final-modal>
|
||||
<el-button size="small" @click="showTree = !showTree">节点树</el-button>
|
||||
<el-button size="small" @click="showTree = !showTree">自定义监听</el-button>
|
||||
<el-button size="small" @click="showTree = !showTree">设置</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.modal-container) {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.modal-content) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 0.25rem;
|
||||
background: #171920;
|
||||
min-width: 400px;
|
||||
height: 80%;
|
||||
}
|
||||
</style>
|
||||
|
24
src/components/CCComponent.vue
Normal file
24
src/components/CCComponent.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<el-checkbox v-model="component!.enabled" size="small" style="margin-right: 10px;" />
|
||||
<span style="flex: 1;">{{ name }}</span>
|
||||
<el-button size="small" @click="Utils.outputToConsole(component)">></el-button>
|
||||
</div>
|
||||
<PropItem v-if="model" v-for="prop in model.props" :key="prop.key" :model="prop.custom ? model : component" :prop-name="prop.name" :prop-key="prop.key"
|
||||
:update-key="updateKey!"></PropItem>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import PropItem from './PropItem.vue';
|
||||
import Utils from '../misc/Utils';
|
||||
import { ComponentManager } from '../misc/ComponentManager';
|
||||
|
||||
const props = defineProps({
|
||||
name: String,
|
||||
component: Object,
|
||||
updateKey: Number,
|
||||
});
|
||||
|
||||
const model = ComponentManager.getViewModel(props.name!, () => props.component)!;
|
||||
|
||||
</script>
|
101
src/components/CCNode.vue
Normal file
101
src/components/CCNode.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<el-checkbox v-model="ccNode!.active" size="small" style="margin-right: 10px;" />
|
||||
<span class="header-title" style="flex: 1;">Node</span>
|
||||
<el-button size="small">+</el-button>
|
||||
<el-button size="small" @click="Utils.outputToConsole(ccNode)">></el-button>
|
||||
</div>
|
||||
<PropItem v-for="prop in NodeModel.props" :key="prop.key" :model="NodeModel" :prop-name="prop.name"
|
||||
:prop-key="prop.key" :update-key="updateKey!"></PropItem>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import PropItem from './PropItem.vue';
|
||||
import Utils from '../misc/Utils';
|
||||
|
||||
const props = defineProps({
|
||||
ccNode: Object,
|
||||
updateKey: Number,
|
||||
});
|
||||
|
||||
class NodeModel {
|
||||
|
||||
static props = [
|
||||
{ name: 'Name', key: 'nodeName' },
|
||||
{ name: 'X', key: 'x' },
|
||||
{ name: 'Y', key: 'y' },
|
||||
{ name: 'Z', key: 'z' },
|
||||
{ name: 'Scale X', key: 'scaleX' },
|
||||
{ name: 'Scale Y', key: 'scaleY' },
|
||||
{ name: 'Scale Z', key: 'scaleZ' },
|
||||
]
|
||||
|
||||
static get ccNode(): any {
|
||||
return props.ccNode;
|
||||
}
|
||||
|
||||
static get nodeName() {
|
||||
return this.ccNode.name;
|
||||
}
|
||||
|
||||
static set nodeName(value: number) {
|
||||
this.ccNode.name = value;
|
||||
}
|
||||
|
||||
static get x() {
|
||||
return this.ccNode.getPosition().x;
|
||||
}
|
||||
|
||||
static set x(value: number) {
|
||||
const originPos = this.ccNode.getPosition();
|
||||
this.ccNode.setPosition(value, originPos.y, originPos.z);
|
||||
}
|
||||
|
||||
static get y() {
|
||||
return this.ccNode.getPosition().y;
|
||||
}
|
||||
|
||||
static set y(value: number) {
|
||||
const originPos = this.ccNode.getPosition();
|
||||
this.ccNode.setPosition(originPos.x, value, originPos.z);
|
||||
}
|
||||
|
||||
static get z() {
|
||||
return this.ccNode.getPosition().z;
|
||||
}
|
||||
|
||||
static set z(value: number) {
|
||||
const originPos = this.ccNode.getPosition();
|
||||
this.ccNode.setPosition(originPos.x, originPos.y, value);
|
||||
}
|
||||
|
||||
static get scaleX() {
|
||||
return this.ccNode.getScale().x;
|
||||
}
|
||||
|
||||
static set scaleX(value: number) {
|
||||
const originScale = this.ccNode.getScale();
|
||||
this.ccNode.setScale(value, originScale.y, originScale.z);
|
||||
}
|
||||
|
||||
static get scaleY() {
|
||||
return this.ccNode.getScale().y;
|
||||
}
|
||||
|
||||
static set scaleY(value: number) {
|
||||
const originScale = this.ccNode.getScale();
|
||||
this.ccNode.setScale(originScale.x, value, originScale.z);
|
||||
}
|
||||
|
||||
static get scaleZ() {
|
||||
return this.ccNode.getScale().z;
|
||||
}
|
||||
|
||||
static set scaleZ(value: number) {
|
||||
const originScale = this.ccNode.getScale();
|
||||
this.ccNode.setScale(originScale.x, originScale.y, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
43
src/components/PropItem.vue
Normal file
43
src/components/PropItem.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<span style="flex: 1">{{ propName }}</span>
|
||||
<el-input-number v-model="model[propKey]" :precision="2" size="small" controls-position="right" style="flex: 1"
|
||||
v-if="getPropType() == 'number'" />
|
||||
<el-input size="small" v-model="model[propKey]" style="flex: 1" v-else-if="getPropType() == 'string'" />
|
||||
<el-checkbox v-model="model[propKey]" size="small" style="margin-left: 10px"
|
||||
v-else-if="getPropType() == 'boolean'" />
|
||||
<el-color-picker v-model="CustomModel.color" size="small" style="flex: 1" color-format="hex" show-alpha
|
||||
v-else-if="getPropType() == 'cc.Color'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
model: any;
|
||||
propName: string;
|
||||
propKey: string;
|
||||
updateKey: number;
|
||||
}>();
|
||||
|
||||
function getPropType() {
|
||||
const data = props.model[props.propKey];
|
||||
const dataType = typeof data;
|
||||
if (dataType === "object" && data.__classname__) {
|
||||
return data.__classname__;
|
||||
}
|
||||
return dataType;
|
||||
}
|
||||
|
||||
class CustomModel {
|
||||
static get color() {
|
||||
const origin = props.model[props.propKey];
|
||||
const hexA = origin.a.toString(16);
|
||||
return `#${origin.toHEX()}${hexA.length === 1 ? "0" + hexA : hexA}`;
|
||||
}
|
||||
|
||||
static set color(v: string) {
|
||||
// @ts-ignore
|
||||
props.model[props.propKey] = new cc.Color().fromHEX(v);
|
||||
}
|
||||
}
|
||||
</script>
|
169
src/components/TreePanel.vue
Normal file
169
src/components/TreePanel.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div style="width: 100%;height: 30px;background-color: #26282f;display: flex;align-items: center;justify-content: center;color: white;" class="modal-drag">
|
||||
节点树
|
||||
</div>
|
||||
<div style="width: 100%;overflow: auto;" :style="{ height: treeViewHeight }">
|
||||
<el-tree-v2 ref="treeView" :props="defaultProps" empty-text="正在加载场景" :highlight-current="true"
|
||||
:expand-on-click-node="false" :default-expanded-keys="expandedKeys" @current-change="handleCurrentNodeChange"
|
||||
@node-expand="handleNodeExpand" @node-collapse="handleNodeCollapse" :height="treeViewHeight">
|
||||
<template #default="{ node }">
|
||||
<span :class="{ 'node-hide': !node.data.active }">{{ node.label }}</span>
|
||||
</template>
|
||||
</el-tree-v2>
|
||||
</div>
|
||||
<div style="width: 100%;border-top: 2px solid #1d1e21;overflow: auto;flex: 1;">
|
||||
<template v-if="updateKey !== 0 && Utils.checkNodeValid(currentNode)">
|
||||
<el-scrollbar>
|
||||
<CCNode :cc-node="currentNode" :update-key="updateKey"></CCNode>
|
||||
<div class="row" style="height: 2px;background-color: #1d1e21"></div>
|
||||
<template v-for="component in Utils.getComponents(currentNode)" :key="component.name">
|
||||
<CCComponent v-if="component.name.startsWith('cc.')" :component="component.target" :name="component.name"
|
||||
:update-key="updateKey"></CCComponent>
|
||||
<UserComponent v-else :component="component.target" :name="component.name" :update-key="updateKey">
|
||||
</UserComponent>
|
||||
<div class="row" style="height: 2px;background-color: #1d1e21"></div>
|
||||
</template>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { ref } from 'vue-demi';
|
||||
import CCNode from './CCNode.vue';
|
||||
import Utils from '../misc/Utils';
|
||||
import CCComponent from './CCComponent.vue';
|
||||
import UserComponent from './UserComponent.vue';
|
||||
|
||||
const props = defineProps({
|
||||
show: Boolean,
|
||||
});
|
||||
|
||||
interface TreeNode {
|
||||
name: string;
|
||||
uuid: string;
|
||||
active: boolean;
|
||||
children?: TreeNode[];
|
||||
path: string[];
|
||||
}
|
||||
|
||||
let updateKey = ref(1);
|
||||
let currentNode: any;
|
||||
const expandedNodeMap = new Map();
|
||||
let expandedKeys: string[] = [];
|
||||
const defaultProps = {
|
||||
value: 'uuid',
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
};
|
||||
|
||||
const treeViewHeight = window.innerHeight * 0.4;
|
||||
const treeView = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
console.log('ccc-devtools init');
|
||||
});
|
||||
|
||||
function getChildByUuidPath(node: any, path: string[], index: number): any {
|
||||
if (index >= path.length) {
|
||||
return node;
|
||||
}
|
||||
node = node.getChildByUuid(path[index]);
|
||||
return getChildByUuidPath(node, path, index + 1);
|
||||
}
|
||||
|
||||
function handleCurrentNodeChange(data: TreeNode) {
|
||||
console.log(data);
|
||||
// @ts-ignore
|
||||
const ccNode = getChildByUuidPath(cc.director.getScene(), data.path, 0);
|
||||
if (data) {
|
||||
currentNode = ccNode;
|
||||
} else {
|
||||
currentNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
function handleNodeExpand(data: TreeNode) {
|
||||
expandedNodeMap.set(data.uuid, true);
|
||||
expandedKeys = [...expandedNodeMap.keys()];
|
||||
}
|
||||
|
||||
function handleNodeCollapse(data: TreeNode) {
|
||||
expandedNodeMap.delete(data.uuid);
|
||||
expandedKeys = [...expandedNodeMap.keys()];
|
||||
}
|
||||
|
||||
function setChildren(container: TreeNode[], children: any[], path: string[]) {
|
||||
children.forEach(ccNode => {
|
||||
const childPath = path.concat(ccNode.uuid);
|
||||
const node = {
|
||||
uuid: ccNode.uuid,
|
||||
name: ccNode.name,
|
||||
active: ccNode.activeInHierarchy,
|
||||
children: [],
|
||||
path: childPath,
|
||||
};
|
||||
if (ccNode.children && ccNode.children.length > 0) {
|
||||
setChildren(node.children, ccNode.children, childPath);
|
||||
}
|
||||
container.push(node);
|
||||
});
|
||||
}
|
||||
|
||||
function refreshTree() {
|
||||
if (props.show) {
|
||||
let value: TreeNode[] = [];
|
||||
//@ts-ignore
|
||||
setChildren(value, cc.director.getScene().children, []);
|
||||
(treeView.value as any).setData(value);
|
||||
updateKey.value = -updateKey.value;
|
||||
}
|
||||
window.requestAnimationFrame(refreshTree);
|
||||
}
|
||||
|
||||
function init() {
|
||||
refreshTree();
|
||||
}
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
// @ts-ignore
|
||||
if (window['cc'] && cc.director.getScene()) {
|
||||
init();
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.el-input__inner {
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.el-input__wrapper {
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
|
||||
.el-color-picker {
|
||||
flex: 1 !important;
|
||||
}
|
||||
|
||||
.el-color-picker__trigger {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #cfd3dc;
|
||||
}
|
||||
|
||||
.node-hide {
|
||||
opacity: 0.3;
|
||||
}
|
||||
</style>
|
18
src/components/UserComponent.vue
Normal file
18
src/components/UserComponent.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<el-checkbox v-model="component!.enabled" size="small" style="margin-right: 10px;" />
|
||||
<span class="header-title" style="flex: 1;">{{ name }}</span>
|
||||
<el-button size="small" @click="Utils.outputToConsole(component)">></el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Utils from '../misc/Utils';
|
||||
|
||||
defineProps({
|
||||
name: String,
|
||||
component: Object,
|
||||
updateKey: Number,
|
||||
});
|
||||
|
||||
</script>
|
12
src/env.d.ts
vendored
Normal file
12
src/env.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
declare module "ccc" {
|
||||
function hehe(): void;
|
||||
}
|
6
src/main.ts
Normal file
6
src/main.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||
import vfmPlugin from 'vue-final-modal'
|
||||
|
||||
createApp(App).use(vfmPlugin).mount('#app');
|
100
src/misc/ComponentManager.ts
Normal file
100
src/misc/ComponentManager.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
interface IComponentProp {
|
||||
name: string;
|
||||
key: string;
|
||||
custom?: boolean;
|
||||
}
|
||||
|
||||
interface IComponentViewModel {
|
||||
props: IComponentProp[];
|
||||
}
|
||||
|
||||
export class ComponentManager {
|
||||
static getViewModel(name: string, componentGetter: any) {
|
||||
switch (name) {
|
||||
case 'cc.UITransform':
|
||||
return new CCUITransformModel(componentGetter);
|
||||
case 'cc.Label':
|
||||
return new CCLabelModel();
|
||||
case 'cc.Sprite':
|
||||
return new CCSpriteModel();
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CCUITransformModel implements IComponentViewModel {
|
||||
|
||||
private componentGetter: any;
|
||||
|
||||
props: IComponentProp[] = [
|
||||
{ name: 'Width', key: 'width', custom: true },
|
||||
{ name: 'Height', key: 'height', custom: true },
|
||||
{ name: 'Anchor X', key: 'anchorX', custom: true },
|
||||
{ name: 'Anchor Y', key: 'anchorY', custom: true },
|
||||
]
|
||||
|
||||
constructor(componentGetter: any) {
|
||||
this.componentGetter = componentGetter;
|
||||
}
|
||||
|
||||
get component(): any {
|
||||
return this.componentGetter();
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this.componentGetter().contentSize.width;
|
||||
}
|
||||
|
||||
set width(value: number) {
|
||||
const origin = this.component.contentSize;
|
||||
this.component.setContentSize(value, origin.height);
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.component.contentSize.height;
|
||||
}
|
||||
|
||||
set height(value: number) {
|
||||
const origin = this.component.contentSize;
|
||||
this.component.setContentSize(origin.width, value);
|
||||
}
|
||||
|
||||
get anchorX() {
|
||||
return this.component.anchorPoint.x;
|
||||
}
|
||||
|
||||
set anchorX(value: number) {
|
||||
const origin = this.component.anchorPoint;
|
||||
this.component.setAnchorPoint(value, origin.y);
|
||||
}
|
||||
|
||||
get anchorY() {
|
||||
return this.component.anchorPoint.y;
|
||||
}
|
||||
|
||||
set anchorY(value: number) {
|
||||
const origin = this.component.anchorPoint;
|
||||
this.component.setAnchorPoint(origin.x, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CCLabelModel implements IComponentViewModel {
|
||||
|
||||
props: IComponentProp[] = [
|
||||
{ name: 'String', key: 'string' },
|
||||
{ name: 'Color', key: 'color' },
|
||||
{ name: 'Font Size', key: 'fontSize' },
|
||||
{ name: 'Line Height', key: 'lineHeight' },
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
class CCSpriteModel implements IComponentViewModel {
|
||||
|
||||
props: IComponentProp[] = [
|
||||
{ name: 'Color', key: 'color' },
|
||||
];
|
||||
|
||||
}
|
31
src/misc/Utils.ts
Normal file
31
src/misc/Utils.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export default class Utils {
|
||||
|
||||
static checkNodeValid(ccNode: any) {
|
||||
// @ts-ignore
|
||||
return ccNode && cc.isValid(ccNode)
|
||||
}
|
||||
|
||||
static outputToConsole(target: any) {
|
||||
let i = 1;
|
||||
// @ts-ignore
|
||||
while (window['temp' + i] !== undefined) {
|
||||
i++;
|
||||
}
|
||||
// @ts-ignore
|
||||
window['temp' + i] = target;
|
||||
console.log('temp' + i);
|
||||
// @ts-ignore
|
||||
console.log(window['temp' + i]);
|
||||
}
|
||||
|
||||
static getComponentName(component: any) {
|
||||
return component.__classname__;
|
||||
}
|
||||
|
||||
static getComponents(ccNode: any) {
|
||||
return ccNode.components.map((component: any) => {
|
||||
return { name: component.__classname__, target: component }
|
||||
});
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user