Complete Cocos Creator port with level bundles, themes, and tooling.

Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-16 15:30:58 +08:00
parent cba5105908
commit d393302388
6248 changed files with 17322729 additions and 11036 deletions

View File

@@ -0,0 +1,287 @@
'use strict';
import { ImporterBase } from '../common/base';
import { AnimationClip } from "../components/AnimationClip";
export class AnimImporter extends ImporterBase {
type: string = 'anim';
// 合并 commonTarget
mergersCommonTarget(key: string, curve :any, commonTargets: any[], pathKey?: any) {
const commonTarget: any = {
modifiers: [],
};
pathKey && commonTarget.modifiers.push(pathKey);
if (key === 'x' || key === 'y' || key === 'z') {
commonTarget.modifiers.push('position');
const idx = commonTargets.findIndex((target) => {
return JSON.stringify(target.modifiers) === JSON.stringify(commonTarget.modifiers);
});
if (idx === -1) {
curve.commonTarget = commonTargets.length;
commonTargets.push(commonTarget);
} else {
curve.commonTarget = idx;
}
curve.modifiers.push(this.getModifier(key));
} else if (key === 'scaleX' || key === 'scaleY' || key === 'scaleZ') {
commonTarget.modifiers.push('scale');
const idx = commonTargets.findIndex((target) => {
return JSON.stringify(target.modifiers) === JSON.stringify(commonTarget.modifiers);
});
if (idx === -1) {
curve.commonTarget = commonTargets.length;
commonTargets.push(commonTarget);
} else {
curve.commonTarget = idx;
}
curve.modifiers.push(this.getModifier(key));
} else {
pathKey && curve.modifiers.push(pathKey);
curve.modifiers.push(this.getModifier(key));
}
}
// 转换 Modifier
getModifier(key: string) {
if (key === 'angle' || key === 'rotation') {
return 'eulerAngles';
}
else if (key === 'scaleX') {
return 'x';
}
else if (key === 'scaleY') {
return 'y';
}
else if (key === 'scaleZ') {
return 'z';
}
else if (key === 'top' || key === 'bottom' ||
key === 'left' || key === 'right' ||
key === 'horizontalCenter' ||
key === 'verticalCenter') {
return 'editor' + key.substring(0, 1).toLocaleUpperCase() + key.substring(key.length, 1);
}
return key;
}
// 转换 value 数值
async getValue(key: string, value: any) {
if (key === 'angle' || key === 'rotation') {
return {
__type__: "cc.Vec3",
x: 0,
y: 0,
z: value !== undefined ? value : 0,
};
}
else if (key === 'scale') {
return {
__type__: "cc.Vec3",
x: value.x !== undefined ? value.x : 1,
y: value.y !== undefined ? value.y : 1,
z: value.z !== undefined ? value.z : 1,
};
}
else if (key === 'position') {
return {
__type__: "cc.Vec3",
x: value[0] !== undefined ? value[0] : 0,
y: value[1] !== undefined ? value[1] : 0,
z: 0,
};
}
if (value && value.__uuid__) {
if (key === 'spriteFrame' || key === 'texture') {
value = await ImporterBase.getUuid(value.__uuid__, key);
}
else {
value = await ImporterBase.getUuid(value.__uuid__);
}
return {
__uuid__: value
}
}
return value;
}
async getPropsForCurveData(props: any, animationClipOrArray: any, pathKey?: any) {
const animationClip = Array.isArray(animationClipOrArray) ? animationClipOrArray[0] : animationClipOrArray;
for (const key in props) {
const prop = props[key];
const frames: number[] = [];
const values: any[] = [];
const curve: any = {
modifiers: [],
data: {
keys: animationClip._keys.length,
values: values,
easingMethods: {},
},
};
if (key === 'opacity') {
const component = {
__type__: "cc.animation.ComponentPath",
component: 'cc.UIOpacity',
};
pathKey && curve.modifiers.push(pathKey);
curve.modifiers.push({
__id__: animationClipOrArray.length,
});
curve.modifiers.push(this.getModifier(key));
animationClipOrArray.push(component);
} else if (key === 'width' || key === 'height') {
const component = {
__type__: "cc.animation.ComponentPath",
component: 'cc.UITransform',
};
let compIdx = animationClipOrArray.findIndex((item: any) => {
return item.__type__ === component.__type__ && item.component === component.component;
});
if (compIdx === -1) {
compIdx = animationClipOrArray.length;
animationClipOrArray.push(component);
}
const commonTarget: any = {
modifiers: [],
};
pathKey && commonTarget.modifiers.push(pathKey);
commonTarget.modifiers.push({
__id__: compIdx,
});
commonTarget.modifiers.push('contentSize');
let idx = animationClip._commonTargets.findIndex((target: any) => {
return JSON.stringify(target.modifiers) === JSON.stringify(commonTarget.modifiers);
});
if (idx === -1) {
idx = animationClip._commonTargets.length;
animationClip._commonTargets.push(commonTarget);
}
curve.commonTarget = idx;
curve.modifiers.push(this.getModifier(key));
}
else {
this.mergersCommonTarget(key, curve, animationClip._commonTargets, pathKey);
}
for (let k = 0; k < prop.length; k++) {
const item = prop[k];
frames.push(item.frame);
const value = await this.getValue(key, item.value);
curve.data.values.push(value);
if (item.curve) {
curve.data.easingMethods[k] = item.curve;
}
}
animationClip._keys.push(frames);
animationClip._curves.push(curve);
}
return animationClipOrArray.length > 1 ? animationClipOrArray : animationClip;
}
async getCurveDataForComps(comps: any, animationClipArray: any, pathKey?: any) {
for (const key in comps) {
// component
const comp = comps[key];
const component = {
__type__: "cc.animation.ComponentPath",
component: key,
};
for (const propKey in comp) {
// component 内部属性
const props = comp[propKey];
const frames: number[] = [];
const values: any[] = [];
const curve: any = {
modifiers: [],
data: {
keys: animationClipArray[0]._keys.length,
values: values,
easingMethods: {},
},
};
pathKey && curve.modifiers.push(pathKey);
curve.modifiers.push({
// 下标从 1 开始
__id__: animationClipArray.length,
});
curve.modifiers.push(this.getModifier(propKey));
for (let k = 0; k < props.length; k++) {
const prop = props[k];
frames.push(prop.frame);
const value = await this.getValue(propKey, prop.value);
curve.data.values.push(value);
if (prop.curve) {
curve.data.easingMethods[k] = prop.curve;
}
}
animationClipArray[0]._keys.push(frames);
animationClipArray[0]._curves.push(curve);
}
animationClipArray.push(component);
}
return animationClipArray;
}
async getCurveDataForPaths(paths: any, animationClipArray: any[]) {
for (const path in paths) {
const hierarchyPaths = {
"__type__": "cc.animation.HierarchyPath",
"path": path,
};
animationClipArray.push(hierarchyPaths);
const data = paths[path];
if (data.props) {
animationClipArray = await this.getPropsForCurveData(data.props, animationClipArray, {
"__id__": animationClipArray.length - 1,
});
}
if (data.comps) {
const __id__ = animationClipArray.findIndex(item => {
return item.path === path;
});
animationClipArray = await this.getCurveDataForComps(data.comps, animationClipArray, {
"__id__": __id__,
});
}
}
return animationClipArray;
}
async getCurveData(curveData: any, animationClip: any) {
if (curveData.props) {
animationClip = Array.isArray(animationClip) ? animationClip : [animationClip];
animationClip = await this.getPropsForCurveData(curveData.props, animationClip);
}
if (curveData.comps) {
animationClip = Array.isArray(animationClip) ? animationClip : [animationClip];
animationClip = await this.getCurveDataForComps(curveData.comps, animationClip);
}
if (curveData.paths) {
animationClip = Array.isArray(animationClip) ? animationClip : [animationClip];
animationClip = await this.getCurveDataForPaths(curveData.paths, animationClip);
}
return animationClip;
}
async import(): Promise<boolean> {
const anim2D = this.readJSONSync();
const animationClip = AnimationClip.create();
animationClip._name = anim2D._name;
animationClip.sample = anim2D.sample;
animationClip.speed = anim2D.speed;
animationClip.wrapMode = anim2D.wrapMode;
animationClip.events = anim2D.events;
animationClip._duration = anim2D._duration;
this._2dTo3dSource = await this.getCurveData(anim2D.curveData, animationClip);
this._3dMeta.ver = '1.0.6';
this._3dMeta.importer = "animation-clip";
return true;
}
}

View File

@@ -0,0 +1,9 @@
'use strict';
import { ImporterBase } from '../common/base';
export class AtlasImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
return true;
}
}

View File

@@ -0,0 +1,11 @@
'use strict';
import { ImporterBase } from '../common/base';
export class AudioImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = "audio-clip";
return true;
}
}

View File

@@ -0,0 +1,9 @@
'use strict';
import { ImporterBase } from '../common/base';
export class BinImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
return true;
}
}

View File

@@ -0,0 +1,11 @@
'use strict';
import { ImporterBase } from '../common/base';
export class DbbinImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = 'dragonbones';
return true;
}
}

View File

@@ -0,0 +1,67 @@
'use strict';
import { ImporterBase } from '../common/base';
import { chunksCacheBy2D } from '../common/utlis';
import {basename, dirname} from "path";
// @ts-ignore
import { ensureDirSync } from "fs-extra";
export class EffectImporter extends ImporterBase {
type: string = 'effect';
async import(main: any): Promise<boolean> {
let effect = this.readFileSync();
if (effect) {
effect = this._replaceKeyword(effect);
effect = this._replace(effect);
}
this._2dTo3dSource = effect;
this._3dMeta.ver = '1.3.7';
this._3dMeta.importer = "effect";
console.warn(Editor.I18n.t('plugin-import-2x.effect_tips', { name: basename(this.destFsPath), uuid: this._3dMeta.uuid }));
return true;
}
_replaceKeyword(effect: string) {
effect = effect.replace(/texture:/g, 'mainTexture:');
effect = effect.replace(/texture;/g, 'mainTexture;');
effect = effect.replace(/texture,/g, 'mainTexture,');
effect = effect.replace(/vs/g, 'unlit-vs');
effect = effect.replace(/fs/g, 'unlit-fs');
return effect;
}
_replace(effect: string): string {
const includes = effect.match(/#include +[<]([^>]+)[>]/g) || [];
for (const include of includes) {
const results = include.match(/(?<=#include <)(.*)(?=>)/g);
if (results && results[0]) {
const value = results[0] as string;
const info = chunksCacheBy2D.get(value);
if (info) {
// this._writeChunksSync(info.from, info.to);
effect = effect.replace(include, include);
}
}
}
return effect;
}
_writeChunksSync(from: string, to: string) {
const content = this.readFileSync(from);
if (content) {
const includes = content.match(/#include +[<]([^>]+)[>]/g) || [];
for (const include of includes) {
const results = include.match(/(?<=#include <)(.*)(?=>)/g);
if (results && results[0]) {
const value = results[0] as string;
const info = chunksCacheBy2D.get(value);
if (info) {
this._writeChunksSync(info.from, info.to);
}
}
}
ensureDirSync(dirname(to));
this.writeFileSync(to, content);
}
}
}

View File

@@ -0,0 +1,76 @@
'use strict';
import { ImporterBase } from '../common/base';
import {
replaceFbxUuidMap,
getFBXSubMetaNewName,
isFbxMultKey,
importSubAssets, importProjectAssets,
} from '../common/utlis';
import {basename, extname} from "path";
export class FbxImporter extends ImporterBase {
type: string = 'fbx';
async import(): Promise<boolean> {
if (!this._2dMeta) {
return true;
}
this._3dMeta.ver = '2.0.8';
this._3dMeta.importer = 'fbx';
this._3dMeta.userData.imageMetas = [];
this._3dMeta.userData.legacyFbxImporter = true;
this._3dMeta.userData.disableMeshSplit = true;
this._3dMeta.uuid = this._2dMeta.uuid;
const subMetas: any = [];
for (let key in this._2dMeta.subMetas) {
const subMeta2D = this._2dMeta.subMetas[key];
const isMult = isFbxMultKey(this._2dMeta.subMetas, key);
key = getFBXSubMetaNewName(this.destFsPath, key, isMult);
const name = ImporterBase.getNameByID(key);
const newUuid = `${this._2dMeta.uuid}@${name}`;
subMetas.push({
key: key,
name: name,
uuid: newUuid,
});
importSubAssets.set(subMeta2D.uuid, {
baseUuid: this._2dMeta.uuid,
uuid: newUuid,
meta: subMeta2D,
});
}
const metaContent = JSON.stringify(this._2dMeta.subMetas);
let uuids = metaContent.match(/(?<=uuid":")([a-zA-Z0-9-]+)(?=")/g) || [];
uuids = uuids.concat(metaContent.match(/(?<=__uuid__":")([a-zA-Z0-9-]+)(?=")/g) || []);
let useSprites: any[] = [];
for (let uuid of uuids) {
const asset = importProjectAssets.get(uuid);
const meta = asset && asset.meta;
if (meta && (meta.type === 'sprite' || meta.type === 'raw')) {
if (!useSprites.find(item => item && item.uuid === uuid)) {
useSprites.push({
uuid: uuid,
name: basename(asset.basePath),
});
}
}
}
if (useSprites.length > 0) {
let textureAssetsStr = '';
for (let useSprite of useSprites) {
textureAssetsStr += `${useSprite.name} {asset(${useSprite.uuid})}`;
}
console.warn(Editor.I18n.t('plugin-import-2x.fbx_tips', {
fbxName: basename(this.destFsPath) || '',
fbxUuid: this._3dMeta.uuid,
textureAssets: textureAssetsStr,
}));
}
if (subMetas.length > 0) {
replaceFbxUuidMap.set(this.destMetaFsPath, subMetas);
}
return true;
}
}

View File

@@ -0,0 +1,27 @@
'use strict';
import { ImporterBase } from '../common/base';
import { Canvas } from "../components/Canvas";
import { MigrateManager } from "../components/MigrateManager";
import { TrailModule } from "../components/TrailModule";
import { Camera } from "../components/Camera";
export class FireImporter extends ImporterBase {
type: string = 'fire';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.1.26';
this._3dMeta.importer = "scene";
const json2D = this.readJSONSync();
const json3D = new Array(json2D.length);
for (let i = 0; i < json2D.length; ++i) {
await MigrateManager.migrate(i, json2D, json3D);
}
// 插入 Canvas
await Canvas.updateCameraComponent(json3D, json2D);
await Canvas.insert(json3D);
await Canvas.checkDesignResolution(json3D, this.pathInfo?.name);
TrailModule.setParticleSystem(json3D);
this.ensureDefaultSprite2DFor3D(json3D);
this._2dTo3dSource = json3D;
return true;
}
}

View File

@@ -0,0 +1,11 @@
'use strict';
import { ImporterBase } from '../common/base';
export class BitmapImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = "bitmap-font";
return true;
}
}

View File

@@ -0,0 +1,14 @@
'use strict';
import { ImporterBase } from '../common/base';
export class GltfImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '2.0.8';
this._3dMeta.importer = 'gltf';
this._3dMeta.userData.imageMetas = [];
this._3dMeta.userData.legacyFbxImporter = false;
this._3dMeta.userData.disableMeshSplit = true;
return true;
}
}

View File

@@ -0,0 +1,86 @@
import { ImporterBase } from '../common/base';
import { importSubAssets } from "../common/utlis";
export class ImageImporter extends ImporterBase {
type: string = 'image';
async import(): Promise<boolean> {
if (!this._2dMeta) {
return true;
}
this._3dMeta.ver = '1.0.13';
this._3dMeta.importer = 'image';
// copy base asset and meta
const userData = this._3dMeta.userData;
if (this._2dMeta.type === 'sprite') {
userData.type = 'sprite-frame';
// texture
const textureMeta = this.createNewMeta();
textureMeta.importer = 'texture';
const textureID = ImporterBase.getNameByID('texture');
const filterMode = this._2dMeta['filterMode'];
// @ts-ignore
textureMeta.userData['minfilter'] = filterMode === 'point' ? 'nearest' : 'linear';
// @ts-ignore
textureMeta.userData['magfilter'] = filterMode === 'point' ? 'nearest' : 'linear';
const wrapMode = this._2dMeta['wrapMode'];
// @ts-ignore
textureMeta.userData['wrapModeS'] = textureMeta.userData['wrapModeT'] = wrapMode === 'clamp' ? 'clamp-to-edge' : wrapMode;
//
const genMipmaps = this._2dMeta['genMipmaps'];
let mipfilter = 'none';
if (genMipmaps) {
mipfilter = (filterMode === 'bilinear' || filterMode === 'point') ? 'nearest' : 'linear';
}
// @ts-ignore
textureMeta.userData['mipfilter'] = mipfilter;
this._3dMeta.subMetas[textureID] = textureMeta;
// sprite frame
const spriteFrameMeta = this.createNewMeta();
const _2dSubMeta = this._2dMeta.subMetas[this.pathInfo!.name];
for (const key in _2dSubMeta) {
if (key === 'ver' || key === 'uuid' ||
key === 'subMetas' || key === 'rawTextureUuid') {
continue;
}
else {
// @ts-ignore
spriteFrameMeta.userData[key] = _2dSubMeta[key];
}
}
spriteFrameMeta.importer = 'sprite-frame';
// @ts-ignore
spriteFrameMeta.userData.imageUuidOrDatabaseUri = `${this._3dMeta.uuid}@${textureID}`;
//
const spriteFrameID = ImporterBase.getNameByID('spriteFrame');
this._3dMeta.subMetas[spriteFrameID] = spriteFrameMeta;
} else {
userData.type = 'raw';
}
// 是否开启缓存
const compressSettings = await this.migratePlatformSettings(this._2dMeta.platformSettings);
if (compressSettings) {
userData.compressSettings = compressSettings;
}
this._3dMeta.userData = userData;
//
if (this._2dMeta.type === 'sprite') {
for (const key in this._2dMeta.subMetas) {
const subMeta = this._2dMeta.subMetas[key];
if (subMeta) {
importSubAssets.set(subMeta.uuid, {
baseUuid: this._2dMeta.uuid,
uuid: `${this._2dMeta.uuid}@${ImporterBase.getNameByID('spriteFrame')}`,
});
}
}
}
else if (this._2dMeta.type === 'raw') {
importSubAssets.set(this._2dMeta.uuid, {
baseUuid: this._2dMeta.uuid,
uuid: `${this._2dMeta.uuid}@${ImporterBase.getNameByID('texture')}`,
});
}
return true;
}
}

View File

@@ -0,0 +1,114 @@
'use strict';
import { init2DChunks } from '../common/utlis';
import { initDiff } from "../common/diff";
import { ImageImporter } from './image';
import { BitmapImporter } from "./fnt";
import { TextImporter } from "./text";
import { AudioImporter } from "./audio";
import { PlistImporter } from "./plist";
import { JSONImporter } from "./json";
import { TmxImporter } from "./tmx";
import { MaterialImporter } from "./mtl";
import { FbxImporter } from "./fbx";
import { TTFFontImporter } from "./ttf";
import { AutoAtlasImporter } from "./pac";
import { AnimImporter } from "./anim";
import { FireImporter } from "./fire";
import { PrefabImporter } from './prefab';
import { JSImporter } from "./js";
import { TSImporter } from "./ts";
import { TsxImporter } from "./tsx";
import { BinImporter } from "./bin";
import { DbbinImporter } from "./dbbin";
import { EffectImporter } from "./effect";
import { GltfImporter } from "./gltf";
import { LabelAtlasImporter } from "./label-atlas";
import { AtlasImporter } from "./atlas";
import { SacImporter } from "./sac";
import { PhysicsMaterialImporter } from "./pmtl";
import { SkelImporter } from "./skel";
import { SkeletonImporter } from "./skeleton";
import { VideoImporter } from "./video";
import { ImporterBase } from "../common/base";
import { extname } from "path";
// 存储需要转换的列表
const keys: string[] = [];
export const converterMap: Map<string, ImporterBase> = new Map<string, ImporterBase>();
function register(Importer: any, extnames: string[]) {
extnames.forEach(extname => {
if (!keys.includes(extname)) {
keys.push(extname);
}
converterMap.set(extname, new Importer());
});
}
function initConvert() {
keys.length = 0;
converterMap.clear();
}
/**
* 排序
* @param {object} tree
*/
export function bubbleSort(tree: any) {
let i, j, stop;
const len = tree.length;
for (i = 0; i < len; i++) {
for (j = 0, stop = len - i; j < stop - 1; j++) {
const a = tree[j], b = tree[j + 1];
const aIndex = keys.indexOf(extname(a.detail.value));
const bIndex = keys.indexOf(extname(b.detail.value));
if (aIndex > bIndex) {
const temp = tree[j];
tree[j] = tree[j + 1];
tree[j + 1] = temp;
}
}
}
return tree;
}
export function getConverter(exatname: string) {
return converterMap.get(exatname);
}
export function registerConverter() {
initConvert();
initDiff();
init2DChunks();
register(ImageImporter, ['.png', '.jpg', '.jpeg', '.webp']);// 完成
register(TextImporter, ['.pem', '.txt', '.html', '.htm', '.xml', '.css', '.less', '.scss', '.styl', '.stylus', '.yaml', '.ini', '.csv', '.proto', '.md', '.markdown']);// 完成
register(AudioImporter, ['.mp3', '.wav', '.ogg', '.aac', '.pcm', '.m4a']);// 完成
register(VideoImporter, ['.mp4']);// 完成
register(TTFFontImporter, ['.ttf']);// 完成
register(BitmapImporter, ['.fnt']);// 完成
register(BinImporter, ['.bin']);// 完成
register(DbbinImporter, ['.dbbin']);// 完成
register(SacImporter, ['.sac']);// 完成
register(JSONImporter, ['.json']);// 完成
register(AtlasImporter, ['.atlas']);// 完成
register(PhysicsMaterialImporter, ['.pmtl']);
register(GltfImporter, ['.gltf']);// 完成
register(SkelImporter, ['.skel']);
register(SkeletonImporter, ['.skeleton']);
register(TsxImporter, ['.tsx']);// 完成
register(TmxImporter, ['.tmx']);// 完成
register(FbxImporter, ['.fbx', '.FBX']);// 完成
register(PlistImporter, ['.plist']);// 完成
register(AutoAtlasImporter, ['.pac']);// 完成
register(LabelAtlasImporter, ['.labelatlas']);// 完成
register(EffectImporter, ['.effect']);
register(MaterialImporter, ['.mtl']);
register(AnimImporter, ['.anim']);
register(PrefabImporter, ['.prefab']);
register(FireImporter, ['.fire']);
// 代码最后导入
register(JSImporter, ['.js']);// 完成
register(TSImporter, ['.ts']);// 完成
}

View File

@@ -0,0 +1,92 @@
import { ImporterBase } from '../common/base';
import { scriptList, replaceScriptList, updateReplaceScriptList, SKIPS_SCRIPT, scriptName } from "../common/utlis";
import { parseJSCode } from "../common/parseCode";
export class JSImporter extends ImporterBase {
type: string = 'script';
async beforeImport(projectRoot: string, sourceFsPath: string): Promise<boolean> {
const skip = SKIPS_SCRIPT.find(value => {
return sourceFsPath.endsWith(value);
});
if (skip) {
return false;
}
return await super.beforeImport(projectRoot, sourceFsPath);
}
async import(main: any): Promise<boolean> {
this._3dMeta.ver = '4.0.21';
this._3dMeta.importer = 'javascript';
let name = this.pathInfo!.name;
// 类名只保留字母
name = scriptName.getValidClassName(name);
const baseCode = this.readFileSync();
if (baseCode) {
let needParse = false;
let commentCode = '';
baseCode.trim().split('\n').forEach((line: string) => {
commentCode += '// ' + line + `\n`;
if (line.includes('cc.Class(')) {
needParse = true;
}
});
let code = '';
if (this._2dMeta && this._2dMeta.isPlugin) {
this._3dMeta.userData = {
isPlugin: this._2dMeta.isPlugin,
loadPluginInWeb: this._2dMeta.loadPluginInWeb,
loadPluginInNative: this._2dMeta.loadPluginInNative,
loadPluginInEditor: this._2dMeta.loadPluginInEditor,
importAsPlugin: this._2dMeta.isPlugin,
}
} else {
if (needParse) {
const data = await parseJSCode(this.sourceFsPath, name);
code += data.topNote;
if (baseCode.includes('cc.Class(')) {
const classInfo: any = await this.queryCCClass(main.$.engine2D, {
type: 'js',
path: this.destFsPath,
name: name,
code: baseCode,
classCount: 1,
ccKeys: data.ccKeys,
importCodeMap: data.importCodeMap,
otherCodeMap: data.otherCodeMap,
classCodeMap: data.classCodeMap,
endCodeMap: data.endCodeMap,
replaceScriptList: replaceScriptList,
});
if (classInfo) {
if (name !== classInfo.name) {
console.warn(Editor.I18n.t('plugin-import-2x.script_rename_tips', {
old: name,
new: classInfo.name
}));
}
updateReplaceScriptList(classInfo.replaceScriptList);
code += classInfo.classCode;
}
} else {
console.warn(Editor.I18n.t('plugin-import-2x.skip_script_warn', {
name: this.pathInfo!.name,
}));
}
code += `/**\n`;
code += ` * ${Editor.I18n.t('plugin-import-2x.plugin_js_tips')}\n`;
code += ` */\n`;
code += commentCode;
this._2dTo3dSource = code;
} else {
this._2dTo3dSource = baseCode;
}
scriptList.set(this.pathInfo!.name, this.destFsPath);
}
}
return true;
}
}

View File

@@ -0,0 +1,92 @@
'use strict';
import { ImporterBase } from '../common/base';
export class JSONImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
const isSpine = this.validateSpine();
if (isSpine) {
this._3dMeta.ver = '1.2.3';
this._3dMeta.importer = 'spine-data';
}
else if (this.validateDragonBones()) {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = 'dragonbones';
}
else if (this.validateDragonBonesAtlasAsset()) {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = 'dragonbones-atlas';
}
else {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = 'json';
}
return true;
}
validateDragonBonesAtlasAsset() {
let json;
const text = this.readFileSync(this.sourceFsPath);
try {
if (text) {
json = JSON.parse(text);
}
}
catch (e) {
return false;
}
return typeof json.imagePath === 'string' && Array.isArray(json.SubTexture);
}
validateDragonBones() {
var json;
if (this.sourceFsPath.endsWith('.json')) {
const text = this.readFileSync(this.sourceFsPath);
try {
if (text) {
json = JSON.parse(text);
}
}
catch (e) {
return false;
}
} else {
const bin = this.readFileSync(this.sourceFsPath);
try {
// @ts-expect-error
json = dragonBones.BinaryDataParser.getInstance().parseDragonBonesData(bin.buffer);
} catch (e) {
return false;
}
}
return Array.isArray(json.armature) || !!json.armatures;
}
validateSpine() {
if (this.sourceFsPath.endsWith('.skel')) {
return true;
}
let json;
const text = this.readFileSync(this.sourceFsPath);
if (text) {
const fastTest = text.slice(0, 30);
const maybe = (fastTest.indexOf('slots') > 0 ||
fastTest.indexOf('skins') > 0 ||
fastTest.indexOf('events') > 0 ||
fastTest.indexOf('animations') > 0 ||
fastTest.indexOf('bones') > 0 ||
fastTest.indexOf('skeleton') > 0 ||
fastTest.indexOf('"ik"') > 0
);
if (maybe) {
try {
json = JSON.parse(text);
} catch (e) {
return false;
}
return Array.isArray(json.bones);
}
}
return false;
}
}

View File

@@ -0,0 +1,20 @@
'use strict';
import { ImporterBase } from '../common/base';
export class LabelAtlasImporter extends ImporterBase {
type: string = 'label-atlas';
async import(): Promise<boolean> {
if (!this._2dMeta) {
return true;
}
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = 'label-atlas';
const userData = this._3dMeta.userData;
userData.itemWidth = this._2dMeta.itemWidth;
userData.itemHeight = this._2dMeta.itemHeight;
userData.startChar = this._2dMeta.startChar;
userData.spriteFrameUuid = `${this._2dMeta.rawTextureUuid}@${ImporterBase.getNameByID('spriteFrame')}`;
userData.fontSize = this._2dMeta.fontSize;
return true;
}
}

View File

@@ -0,0 +1,13 @@
'use strict';
import { ImporterBase } from '../common/base';
import { Material } from "../components/Material";
export class MaterialImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.9';
this._3dMeta.importer = 'material';
this._2dTo3dSource = await Material.migrate(this.readJSONSync());
return true;
}
}

View File

@@ -0,0 +1,39 @@
'use strict';
import { ImporterBase } from '../common/base';
// 2d 自动图集已有的数据
const KEYS = [
'maxWidth',
'maxHeight',
'padding',
'allowRotation',
'forceSquared',
'powerOfTwo',
'algorithm',
'format',
'quality',
'contourBleed',
'paddingBleed',
'filterUnused',
];
export class AutoAtlasImporter extends ImporterBase {
type: string = 'pac';
async import(): Promise<boolean> {
if (!this._2dMeta) {
return true;
}
this._3dMeta.ver = '1.0.5';
this._3dMeta.importer = 'auto-atlas';
for (const key in this._2dMeta) {
if (KEYS.includes(key)) {
this._3dMeta.userData[key] = this._2dMeta[key];
}
}
const compressSettings = await this.migratePlatformSettings(this._2dMeta.platformSettings);
if (compressSettings) {
this._3dMeta.userData.compressSettings = compressSettings;
}
return true;
}
}

View File

@@ -0,0 +1,118 @@
'use strict';
import { ImporterBase } from '../common/base';
// @ts-ignore
import { readFile, writeJSONSync } from 'fs-extra';
import { basename, extname, parse } from 'path';
import { getBlendFactor2DTo3D, importProjectAssets, importSubAssets } from "../common/utlis";
const plist = require('plist');
export class PlistImporter extends ImporterBase {
type: string = 'plist';
public async import(): Promise<boolean> {
if (!this._2dMeta) {
return true;
}
if (this._2dMeta.type === 'Texture Packer') {
this._3dMeta.ver = '1.0.6';
this._3dMeta.importer = 'sprite-atlas';
return this.importTexturePacker();
} else {
this._3dMeta.ver = '1.0.2';
this._3dMeta.importer = 'particle';
return this.importParticle();
}
}
async importTexturePacker() {
try {
let userData = this._3dMeta.userData;
const file = await readFile(this.sourceFsPath, 'utf8');
const data = plist.parse(file);
userData.atlasTextureName = data.metadata.realTextureFileName;
userData.format = data.metadata.format;
userData.textureUuid = `${this._2dMeta.rawTextureUuid}@${ImporterBase.getNameByID('texture')}`;
userData = this._3dMeta.userData;
//
for (const key in data.frames) {
const _2dSubMeta = this._2dMeta.subMetas[key];
const id = ImporterBase.getNameByID(parse(key).name);
const subMeta = this.createNewMeta();
subMeta.importer = "sprite-frame";
for (const key in _2dSubMeta) {
switch (key) {
case 'ver':
case 'uuid':
case 'subMetas':
case 'spriteType':
// continue
break;
case 'rawTextureUuid':
// @ts-ignore
subMeta.userData.atlasUuid = _2dSubMeta[key];
break;
default:
// @ts-ignore
subMeta.userData[key] = _2dSubMeta[key];
break;
}
}
// @ts-ignore
subMeta.imageUuidOrDatabaseUri = userData.textureUuid;
this._3dMeta.subMetas[id] = subMeta;
// 存储
for (let key in this._2dMeta.subMetas) {
const subMeta = this._2dMeta.subMetas[key];
if (subMeta) {
key = basename(key, extname(key));
importSubAssets.set(subMeta.uuid, {
baseUuid: this._2dMeta.uuid,
uuid: `${this._2dMeta.uuid}@${ImporterBase.getNameByID(key)}`,
});
}
}
// 修改 plist 依赖图片类型设置为 texture
const image = importProjectAssets.get(this._2dMeta.rawTextureUuid);
if (image) {
const meta = this.readJSONSync(image.outPath);
if (meta.userData.type !== 'texture') {
meta.userData.type = 'texture';
writeJSONSync(image.outPath, meta, {spaces: 2});
}
}
}
return true;
}
catch (e) {
console.error(e);
return false;
}
}
async importParticle() {
try {
const file = await readFile(this.sourceFsPath, 'utf8');
const data = plist.parse(file);
if (data.spriteFrameUuid) {
data.spriteFrameUuid = await ImporterBase.getUuid(data.spriteFrameUuid, 'spriteFrame');
}
else if (data.textureUuid) {
// textureUuid 转成 spriteFrameUuid
data.spriteFrameUuid = await ImporterBase.getUuid(data.textureUuid, 'spriteFrame');
delete data.textureUuid;
}
if (data.blendFuncSource) {
data.blendFuncSource = getBlendFactor2DTo3D(data.blendFuncSource);
}
if (data.blendFuncDestination) {
data.blendFuncDestination = getBlendFactor2DTo3D(data.blendFuncDestination);
}
this._2dTo3dSource = plist.build(data);
return true;
}
catch (e) {
console.log(e);
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
'use strict';
import { ImporterBase } from '../common/base';
export class PhysicsMaterialImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = 'physics-material';
return true;
}
}

View File

@@ -0,0 +1,36 @@
'use strict';
import { ImporterBase } from '../common/base';
import { MigrateManager } from "../components/MigrateManager";
import { TrailModule } from "../components/TrailModule";
function getOptimizationPolicy(value: string) {
switch (value) {
case 'AUTO':
return 0;
case 'SINGLE_INSTANCE':
return 1;
case 'MULTI_INSTANCE':
return 2;
}
}
export class PrefabImporter extends ImporterBase {
type: string = 'prefab';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.1.26';
this._3dMeta.importer = "prefab";
const json2D = this.readJSONSync();
const json3D = new Array(json2D.length);
for (let i = 0; i < json2D.length; ++i) {
const obj = await MigrateManager.migrate(i, json2D, json3D);
if (obj.__type__ === 'cc.Prefab') {
obj.optimizationPolicy = getOptimizationPolicy(this._2dMeta.optimizationPolicy);
obj.asyncLoadAssets = this._2dMeta.asyncLoadAssets;
}
}
TrailModule.setParticleSystem(json3D);
this.ensureDefaultSprite2DFor3D(json3D);
this._2dTo3dSource = json3D;
return true;
}
}

View File

@@ -0,0 +1,9 @@
'use strict';
import { ImporterBase } from '../common/base';
export class SacImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
return true;
}
}

View File

@@ -0,0 +1,9 @@
'use strict';
import { ImporterBase } from '../common/base';
export class SkelImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
return true;
}
}

View File

@@ -0,0 +1,11 @@
'use strict';
import { ImporterBase } from '../common/base';
export class SkeletonImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = 'instantiation-skeleton';
return true;
}
}

View File

@@ -0,0 +1,11 @@
'use strict';
import { ImporterBase } from '../common/base';
export class TextImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = 'text';
return true;
}
}

View File

@@ -0,0 +1,40 @@
'use strict';
import { ImporterBase } from '../common/base';
import { importProjectAssets, searchTmxDependImages } from "../common/utlis";
// @ts-ignore
import { writeJSONSync } from "fs-extra";
const IMAGE_EXNAME = ['.png', '.jpg', '.jpeg', '.webp'];
export class TmxImporter extends ImporterBase {
type: string = 'tmx';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = 'tiled-map';
const tmxFileData = this.readFileSync();
if (tmxFileData) {
const imageFullPaths = await searchTmxDependImages(this.sourceFsPath, tmxFileData) || [];
const images: any[] = [];
imageFullPaths.forEach((imagePath: string) => {
importProjectAssets.forEach((value) => {
if (IMAGE_EXNAME.includes(value.type)) {
if (value.basePath === imagePath) {
images.push(value);
return;
}
}
});
});
images.forEach(info => {
const meta = this.readJSONSync(info.outPath);
if (meta) {
if (meta.userData.type !== 'sprite-frame') {
meta.userData.type = 'sprite-frame';
writeJSONSync(info.outPath, meta, {spaces: 2});
}
}
});
}
return true;
}
}

View File

@@ -0,0 +1,47 @@
import { ImporterBase } from '../common/base';
import { scriptList, scriptName} from "../common/utlis";
import {parseTSCode} from "../common/parseCode";
export class TSImporter extends ImporterBase {
type: string = 'script';
async import(main: any): Promise<boolean> {
try {
this._3dMeta.ver = '4.0.21';
this._3dMeta.importer = 'typescript';
let name = this.pathInfo!.name;
// 类名只保留字母
name = scriptName.getValidClassName(name);
let code = this.readFileSync();
if (code) {
let needParse = false;
let commentCode = '';
code.trim().split('\n').forEach((line: string) => {
commentCode += '// ' + line + `\n`;
if (line.includes('export default class ') ||
line.includes('export class ') ||
line.includes('@ccclass')
) {
needParse = true;
}
});
if (needParse) {
const data = await parseTSCode(name, this.sourceFsPath);
this._2dTo3dSource = data.content;
this._2dTo3dSource += `/**\n`;
this._2dTo3dSource += ` * ${Editor.I18n.t('plugin-import-2x.plugin_js_tips')}\n`;
this._2dTo3dSource += ` */\n`;
this._2dTo3dSource += commentCode;
} else {
this._2dTo3dSource = code;
}
scriptList.set(name, this.destFsPath);
}
return true;
}
catch (e) {
console.error(e + ',\n this file path: ' + this.sourceFsPath);
return false;
}
}
}

View File

@@ -0,0 +1,9 @@
'use strict';
import { ImporterBase } from '../common/base';
export class TsxImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
return true;
}
}

View File

@@ -0,0 +1,11 @@
'use strict';
import { ImporterBase } from '../common/base';
export class TTFFontImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = "ttf-font";
return true;
}
}

View File

@@ -0,0 +1,11 @@
'use strict';
import { ImporterBase } from '../common/base';
export class VideoImporter extends ImporterBase {
type: string = 'base';
async import(): Promise<boolean> {
this._3dMeta.ver = '1.0.0';
this._3dMeta.importer = "video-clip";
return true;
}
}