Files
刘宇飞 d393302388 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>
2026-06-16 15:30:58 +08:00

884 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="cocos2d-js-min.js"></script>
<script src="physics-min.js"></script>
</head>
<script>
const SKIP_TYPS = [ 'number', 'string', 'boolean', 'object' ];
const NORMAL_TYPS = [ 'number', 'string', 'boolean'];
const RENAME_COMPONENT = {
'BoxCollider': 'BoxCollider2D',
'BoxCollider3D': 'BoxCollider',
'CircleCollider': 'CircleCollider2D',
'Collider': 'Collider2D',
'Collider3D': 'Collider',
'DistanceJoint': 'DistanceJoint2D',
'ClickEvent': 'EventHandler',
'MouseJoint': 'MouseJoint2D',
'WheelJoint': 'WheelJoint2D',
'PolygonCollider': 'PolygonCollider2D',
'ParticleSystem': 'ParticleSystem2D',
'ParticleSystem3D': 'ParticleSystem',
'Joint': 'Joint2D',
'RigidBody': 'RigidBody2D',
'RigidBody3D': 'RigidBody',
'SphereCollider3D': 'SphereCollider',
'RenderComponent': 'UIRenderable',
'SkeletonAnimation': 'SkeletalAnimation',
'Float': 'CCFloat',
'string': 'CCString',
'Boolean': 'CCBoolean',
'Integer': 'CCInteger',
};
let parserManager = {
importStr: '_decorator',
importOtherStr: '',
decoratorStr: 'ccclass',
reset() {
parserManager.importStr = '_decorator';
parserManager.decoratorStr = 'ccclass';
parserManager.importOtherStr = '';
window.require = function(url) {
return `require(${url})`;
};
cc.Class = function(options) {
}
},
pushImports(importClass) {
if (!importClass) {
return;
}
if (typeof importClass === 'function') {
importClass = cc.js.getClassName(importClass);
} else if (Array.isArray(importClass)) {
importClass = importClass[0];
importClass = importClass && importClass.prototype && importClass.prototype.__classname__;
} else if (importClass.name) {
switch (importClass.name) {
case 'Integer':
case 'Float':
return 'number';
case 'Boolean':
return 'boolean';
case 'String':
return 'string';
}
} else if (importClass.__classname__) {
importClass = importClass.__classname__;
}
if (typeof importClass === 'string') {
importClass = importClass.replace(/cc./, '');
if (cc[importClass]) {
// 改名
if (RENAME_COMPONENT[importClass]) {
importClass = RENAME_COMPONENT[importClass];
}
const imports = parserManager.importStr.replace(/ /g, '').split(',');
if (!imports.includes(importClass)) {
parserManager.importStr += `, ${importClass}`;
}
return importClass;
} else if (importClass.startsWith('dragonBones.') || importClass.startsWith('sp.')) {
let result = importClass.split('.');
if (result && result[0]) {
const imports = parserManager.importStr.replace(/ /g, '').split(',');
if (!imports.includes(result[0])) {
parserManager.importStr += `, ${result[0]}`;
}
}
return importClass;
} else {
return typeof importClass;
}
}
},
pushDecorators(item) {
const decorator = parserManager.decoratorStr.replace(/ /g, '').split(',');
if (!decorator.includes(item)) {
parserManager.decoratorStr += `, ${item}`;
}
return item;
},
getClassName(val) {
if (!val) {
return val;
} else if (typeof val === 'function') {
return cc.js.getClassName(val);
} else if (val.indexOf('require') > -1) {
return val.replace(/require\(/, '').replace(/\)/, '');
}
return val;
},
getType(val) {
if (val && val.type) {
return val.type;
}
return val;
},
parseTS(data) {
let { path, name, code, other, replaceScriptList } = data;
let OptionKey = 'type, visible, displayName, tooltip, multiline, readonly, min, max, step, range, slide, serializable,formerlySerializedAs, editorOnly, override, animatable';
let Options = OptionKey.split(',');
try {
const _property = cc._decorator.property;
cc._decorator.property = function(ctorProtoOrOptions, propName, desc) {
let data = {};
if (typeof ctorProtoOrOptions === 'function') {
let value = parserManager.pushImports(ctorProtoOrOptions);
if (!SKIP_TYPS.includes(value)) {
data['type'] = value;
parserManager.pushDecorators('type');
}
} else if (typeof propName === 'undefined') {
for (let option of Options) {
let value = ctorProtoOrOptions[option];
if (value !== undefined) {
if (option === 'type') {
let __classname__;
if (Array.isArray(value) && value[0]) {
__classname__ = parserManager.pushImports(value[0].prototype.__classname__);
data[option] = `[${__classname__}]`;
} else {
__classname__ = parserManager.pushImports(value.prototype.__classname__);
data[option] = __classname__;
}
} else {
data[option] = value.toString();
}
parserManager.pushDecorators(option);
}
}
}
let result = _property(ctorProtoOrOptions, propName, desc);
if (result) {
result.data = data;
}
return result;
};
// 用于解析 getset 属性
let getsetMap = new Map();
let propCode = '';
let ____decorate = window.__decorate;
let __decorate = function(decorators, target, key, desc) {
if (key) {
let decorator = decorators[0].data;
for (let key in decorator) {
if (decorator[key]) {
propCode += ` @${key}(${decorator[key]})\n`;
}
}
target.constructor.prototype.__initProps__ = function() {
};
let _this = target.constructor();
let getset = getsetMap.get(key);
if (getset) {
if (!decorator) {
propCode += ` @property\n`;
parserManager.pushDecorators('property');
}
if (getset.get) {
let splitArr = getset.get.toString().trim().split('\n');
propCode += ` get ${splitArr[0].replace('function', key)}\n`;
propCode += ` \t\t${splitArr[1].trim()}\n`;
propCode += ` }\n`;
}
if (getset.set) {
let splitArr = getset.set.toString().trim().split('\n');
propCode += ` set ${splitArr[0].replace('function', key)}\n`;
propCode += ` \t\t${splitArr[1].trim()}\n`;
propCode += ` }\n`;
}
propCode += '\n';
} else {
let value = _this[key];
if (Array.isArray(value)) {
if (!decorator) {
propCode += ` @property\n`;
parserManager.pushDecorators('property');
}
value = value.toString();
if (!value) {
propCode += ` ${key} = [];\n\n`;
} else {
propCode += ` ${key} = ${value};\n\n`;
}
} else if (typeof value === 'string') {
if (!decorator) {
propCode += ` @property\n`;
parserManager.pushDecorators('property');
}
propCode += ` ${key} = '${value}';\n\n`;
} else {
let __className__ = value && value.__classname__;
__className__ = parserManager.pushImports(__className__);
if (__className__) {
propCode += ` @property(${__className__})\n`;
parserManager.pushDecorators('property');
if (__className__ === 'Color') {
value = `new Color(${value.r},${value.g},${value.b},${value.a})`;
} else if (__className__ === 'Vec2' || __className__ === 'Vec3') {
value = `new ${__className__}(${value.x},${value.y},${value.z})`;
} else if (__className__ === 'Vec4') {
value = `new Vec4(${value.x},${value.y},${value.z}, ${value.w})`;
}
propCode += ` ${key} = ${value};\n\n`;
return;
}
if (!decorator) {
propCode += ` @property\n`;
parserManager.pushDecorators('property');
}
propCode += ` ${key} = ${value};\n\n`;
}
}
}
if (decorators !== undefined && target !== undefined && key !== undefined && desc !== undefined) {
return ____decorate(decorators, target, key, desc);
}
if (decorators !== undefined && target !== undefined && key !== undefined) {
return ____decorate(decorators, target, key);
}
if (decorators !== undefined && target !== undefined) {
return ____decorate(decorators, target);
}
if (decorators !== undefined) {
return ____decorate(decorators, target);
}
};
let defineProperty = Object.defineProperty;
Object.defineProperty = function(o, p, attributes) {
if (p !== '__esModule') {
getsetMap.set(p, attributes);
}
defineProperty(o, p, attributes);
};
let extend;
let ____extends = window.__extends;
__extends = function(d, b) {
extend = parserManager.pushImports(b);
____extends(d, b);
};
window.exports = {
default: null,
};
eval(code);
let cccclass;
if (!cccclass) {
let keys = Object.keys(window.exports);
for (let i = 0; i < keys.length; ++i) {
let value = window.exports[keys[i]];
if (value) {
cccclass = value;
break;
}
}
}
window.__initProps__ = function() {
};// 容错处理
cccclass.prototype.__initProps__ = function() {
};// 容错处理
cccclass();
// 检查文件名是否与类名重复了,如果有就加 Ctrl避免与内置类名重复
let baseName = name;
let hasRename = !!cc[baseName];
if (hasRename) {
name = baseName + 'Ctrl';
}
let importOtherCode = ``;
let editorCode = `@ccclass('${name}')\n`;
let classCode;
if (extend) {
classCode = `export class ${name} extends ${extend} {\n\n`;
} else {
classCode = `export class ${name} {\n\n`;
}
// 私有变量
const constructor = cccclass.prototype.constructor.toString();
let matchArray = constructor.match(/(?<=_this\.)_(.*)(?=\;)/g);
matchArray = constructor.match(/(?<=_this\.)(.*)(?=\;)/g);
if (matchArray) {
for (let index in matchArray) {
let str = matchArray[index];
let result = str.replace(/\s*/g, '').split('=');
let key = result[0], value = result[1];
if (propCode.indexOf(key + ' =') !== -1) {
continue;
}
if (str.indexOf('new ') !== -1) {
result = str.split('new ');
value = result[1];
const splitArr = value.split('(');
let type = '';
if (splitArr.length > 0) {
type = splitArr[0];
}
else {
type = value.replace('()', '');
}
type = parserManager.pushImports(type);
propCode += ` private ${key} = new ${value.replace('cc.', '')};\n`;
} else if (str.indexOf('cc.') === -1) {
// 表示带有特殊符号
const has = value.match(/[.|+|-|*|/]/g);
if (has && has.length > 0) {
propCode += ` private ${key} = ${cccclass.prototype[key]};\n`;
}
else {
propCode += ` private ${str};\n`;
}
} else {
const splitArr = value.split('(');
let type = '';
if (splitArr.length > 0) {
type = splitArr[0];
}
else {
type = value.replace('()', '');
}
type = parserManager.pushImports(type);
propCode += ` private ${key} = ${value.replace('cc.', '')};\n`;
}
}
propCode += '\n';
}
// 方法
let prototypeKeys = Object.keys(cccclass.prototype);
let functionCode = '';
for (let index in prototypeKeys) {
let key = prototypeKeys[index];
if (key === '__initProps__') {
continue;
} else if (key === 'constructor' && extend) {
continue;
}
let func = cccclass.prototype[key];
if (typeof func !== 'function') {
continue;
}
functionCode += ` ${key} () {\n`;
let splitArr = func.toString().trim().split('\n');
if (splitArr.length === 2) {
functionCode += '\n';
}
for (let i = 0; i < splitArr.length; ++i) {
if (i === 0 || i === splitArr.length - 1) {
continue;
}
let str = splitArr[i];
if (str) {
functionCode += ` // ${str.trim()}\n`;
}
}
functionCode += ' }\n\n';
}
let classEndCode = '}\n\n';
let otherCode = '';
for (let line of other) {
let returns = line.match(/cc\.(.*)(?=(\;|\,|\(\)))/g) || [];
let type;
for (let value of returns) {
value = value.replace(/\(\)/, '');
type = parserManager.pushImports(value);
}
line = line.replace(/cc./g, '');
let arr = line.split('=');
let skip = false;
if (arr.length > 1) {
let va = arr[0].match(/ (.*) /)[0].trim();
if (va === type) {
skip = true;
}
}
if (skip) {
continue;
}
otherCode += line;
if (line.endsWith(';')) {
otherCode += '\n';
}
}
otherCode += '\n';
let importCode = `import { ${parserManager.importStr} } from 'cc'\n`;
let decoratorCode = `const { ${parserManager.decoratorStr} } = _decorator;\n`;
let classData = {
name: name,
classCode: importCode + importOtherCode + decoratorCode + otherCode + editorCode + classCode + propCode + functionCode + classEndCode,
replaceScriptList: replaceScriptList,
};
event.source.postMessage(classData, '*');
} catch (e) {
console.error(e + ' name : ' + name);
event.source.postMessage(null, '*');
}
}
};
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
let { type } = event.data;
parserManager.reset();
if (type === 'js') {
parsingJS(event.data);
}
else if (type === 'ts') {
parserManager.parseTS(event.data);
}
}
function parsingJS(data) {
let {
type, path, name, code, classCount,
replaceScriptList,
ccKeys,
importCodeMap,
otherCodeMap,
classCodeMap,
endCodeMap,
} = data;
let importCode = ['_decorator'];
let decoratorCode = ['ccclass'];
let importOtherCode = '';
let otherCode = '';
let classCodes = '';
let baseName = undefined;
function getExtends(val) {
if (val.indexOf('require') > -1) {
return val.replace(/require\(/, '').replace(/\)/, '');
}
return val;
}
function pushImports (val) {
if (val.startsWith('cc.')) {
val = val.replace(/cc./, '');
}
if (cc[val]) {
// 改名
if (RENAME_COMPONENT[val]) {
val = RENAME_COMPONENT[val];
}
if (!importCode.includes(val)) {
importCode.push(val)
}
return val;
} else if (val.startsWith('dragonBones.') || val.startsWith('sp.')) {
let result = val.split('.');
if (result && result[0]) {
if (!importCode.includes(result[0])) {
importCode.push(result[0])
}
}
return val;
}
return val;
}
function pushDecorators(val) {
if (!decoratorCode.includes(val)) {
decoratorCode.push(val);
}
return val;
}
function getDefaultValue(value) {
if (value.includes('new ')) {
const val = value.split('new ');
return `new ${val[1]}`;
}
if (value === '') {
return value;
}
if (value === null || value === 'null') {
return null;
}
// number
if (!isNaN(Number(value))) {
return Number(value);
}
// boolean
if (value === 'false' || value === 'true') {
return value === 'true';
}
if (value === false || value === true) {
return value;
}
value = value.replace(/cc./, '');
// array
if (value.startsWith('[') && value.endsWith(']')) {
return '[]';
}
let matchArray = value.startsWith('Vec2') || value.startsWith('v2');
if (matchArray) {
if (!importCode.includes('Vec2')) {
importCode.push('Vec2')
}
if (value.includes('Vec2.')) {
return value;
}
return `new Vec2()`;
}
matchArray = value.startsWith('Vec3') || value.startsWith('v3');
if (matchArray) {
if (!importCode.includes('Vec3')) {
importCode.push('Vec3')
}
if (value.includes('Vec3.')) {
return value;
}
return `new Vec3()`;
}
matchArray = value.startsWith('Vec4') || value.startsWith('v4');
if (matchArray) {
if (!importCode.includes('Vec4')) {
importCode.push('Vec4')
}
if (value.includes('Vec4.')) {
return value;
}
return `new Vec4()`;
}
matchArray = value.startsWith('Color') || value.startsWith('color');
if (matchArray) {
if (!importCode.includes('Color')) {
importCode.push('Color')
}
if (value.includes('Color.')) {
return value;
}
return `new Color()`;
}
matchArray = value.startsWith('Size') || value.startsWith('size');
if (matchArray) {
if (!importCode.includes('Size')) {
importCode.push('Size')
}
if (value.includes('Size.')) {
return value;
}
return `new Size()`;
}
matchArray = value.startsWith('Rect') || value.startsWith('rect');
if (matchArray) {
if (!importCode.includes('Rect')) {
importCode.push('Rect')
}
if (value.includes('Rect.')) {
return value;
}
return `new Rect()`;
}
matchArray = value.startsWith('Mat3') || value.startsWith('mat3');
if (matchArray) {
if (!importCode.includes('Mat3')) {
importCode.push('Mat3')
}
if (value.includes('Mat3.')) {
return value;
}
return `new Mat3()`;
}
matchArray = value.startsWith('Mat4') || value.startsWith('mat4');
if (matchArray) {
if (!importCode.includes('Mat4')) {
importCode.push('Mat4')
}
if (value.includes('Mat4.')) {
return value;
}
return `new Mat4()`;
}
matchArray = value.startsWith('Quat') || value.startsWith('quat');
if (matchArray) {
if (!importCode.includes('Quat')) {
importCode.push('Quat')
}
if (value.includes('Quat.')) {
return value;
}
return `new Quat()`;
}
if (cc[value]) {
return null;
}
return `'${value}'`;
}
function getTypeValue(type) {
let str = type.split(':');
if (str.length > 1) {
type = str[1];
if (type.startsWith('require(')) {
let path = type.replace(/require\(/, '');
path = path.replace(/\)/, '');
const paths = path.split('/');
if (paths.length > 1) {
// 如果是 ./dd/aa 的获取最后一个
type = paths[paths.length - 1];
}
else {
type = paths[0];
}
importCodeMap.set(importCodeMap.size, `const ${type} = require('${path}')`);
}
}
return pushImports(type)
}
// 解析 cc.Class
let extendsArr = [];
classCodeMap.forEach((options) => {
let editorCode = '';
let classCode = '';
let propCode = '';
let functionCode = '';
let classEndCode = '}\n\n';
// 检查文件名是否与类名重复了,如果有就加 Ex避免与内置类名重复
name = options.name || name;
let hasRename = !!cc[name];
if (hasRename) {
name = name + 'Ex';
}
if (baseName === undefined) {
baseName = name;
}
// editor
let keys = Object.keys(options.editors);
editorCode = `@ccclass('${name}')\n`;
for (let key of keys) {
let editor = options.editors[key];
pushDecorators(key);
switch (key) {
case 'disallowMultiple':
case 'executeInEditMode':
case 'playOnFocus':
editorCode += `@${key}\n`;
break;
case 'executionOrder':
case 'menu':
case 'icon':
case 'inspector':
editorCode += `@${key}('${editor}')\n`;
break;
case 'requireComponent':
let ccclassName = pushImports(editor);
editorCode += `@requireComponent(${ccclassName})\n`;
break;
}
}
// 继承
let extendsClass = getExtends(options.extends);
if (extendsClass) {
extendsArr.push(extendsClass);
if (extendsClass.startsWith('cc.')) {
extendsClass = pushImports(extendsClass);
classCode = `export class ${name} extends ${extendsClass} {\n`;
} else {
importOtherCode += `import { @@@@@@ } from "${extendsClass}";\n`;
replaceScriptList.push({
path: path,
extendClassName: extendsClass,
});
classCode = `export class ${name} extends @@@@@@ {\n`;
}
} else {
classCode = `export class ${name} {\n`;
}
// 属性
keys = Object.keys(options.properties);
for (let key of keys) {
const fieldType = key.startsWith('_') ? 'private' : 'public';
const prop = options.properties[key];
let type = undefined;
if (prop.serializable !== undefined) {
// pushDecorators('serializable');
// propCode += ' ';
// propCode += '@serializable\n'
}
if (prop.visible !== undefined) {
// pushDecorators('visible');
// propCode += ' ';
// propCode += `@visible(${prop.visible})\n`;
}
let isArray = false;
if (fieldType === 'public') {
propCode += ' ';
pushDecorators('property');
if (prop.type !== undefined) {
let type = getTypeValue(prop.type)
if (prop.default && prop.default.startsWith('[') && prop.default.endsWith(']')) {
isArray = true;
if (type.startsWith('[') && type.endsWith(']')) {
propCode += `@property(${type})`;
} else {
propCode += `@property([${type}])`;
}
} else {
propCode += `@property(${type})`;
}
} else {
propCode += `@property`;
}
propCode += '\n';
}
if (prop.hasGet !== undefined || prop.hasGet !== undefined) {
if (prop.hasGet !== undefined) {
propCode += prop.hasGet;
propCode += '\n';
}
if (prop.hasSet !== undefined) {
propCode += prop.hasSet;
}
propCode += '\n';
}
else {
propCode += ` ${fieldType} ${key}`;
// if (prop.type !== undefined) {
// // 特殊字段array:type
// if (isArray) {
// // 如果是数组就跳过,不设置 :[]
// } else {
// propCode += `: ${getTypeValue(prop.type)}`;
// }
// }
if (prop.default !== undefined) {
prop.default = getDefaultValue(prop.default);
if (prop.default === '') {
propCode += ` = '${prop.default}'`;
}
else {
// if (prop.default === null && prop.type !== undefined) {
// propCode += ` | null = ${prop.default}`;
// }
// else {
propCode += ` = ${prop.default}`;
// }
}
}
propCode += ';\n';
}
}
// 静态
let statics = options.statics;
if (Object.keys(options.statics).length > 0) {
propCode += '\n';
}
for (let key in statics) {
const value = statics[key];
propCode += `${value.content}`;
}
// 函数
let functions = options.functions;
if (Object.keys(options.functions).length > 0) {
propCode += '\n';
}
for (let key in functions) {
const value = functions[key];
propCode += `${value.content}`;
}
classCodes += editorCode + classCode + propCode + functionCode + classEndCode + '\n';
});
// 解析导入字段
importCodeMap.forEach(item => {
if (item.includes('require')) {
let segments = item.replace(/ /g, '').split('require');
let nameArr = segments && segments[0].replace(/const|var|let|=|{|}/g, '');
let names = nameArr.split(',');
let segment = segments && segments[1];
let importPath = '';
if (segment.includes(').')) {
importPath = segment.split('.')[0];
}
importPath = segment.replace(/\(|'|"|\)|;/g, '');
//
if (extendsArr.includes(importPath)) {
return;
}
importOtherCode += `import { ${names} } from '${importPath}';\n`;
replaceScriptList.push({
path: path,
importPath: importPath,
});
}
});
// import
let content = 'import { ';
for (let i = 0; i < importCode.length; ++i) {
content += importCode[i];
if (i < importCode.length - 1) {
content += ', ';
}
}
for (let i = 0; i < ccKeys.length; ++i) {
let ccKey = ccKeys[i];
if (!content.includes(ccKey) && cc[ccKey]) {
content += ', ' + ccKey;
}
}
content += " } from 'cc';\n";
// 其他 import
content += importOtherCode;
// decorator
content += 'const { ';
for (let i = 0; i < decoratorCode.length; ++i) {
content += decoratorCode[i];
if (i < decoratorCode.length - 1) {
content += ', ';
}
}
content += " } = _decorator;\n\n";
// 其他变量定义
otherCodeMap.forEach((line) => {
content += line.replace(/var/, 'let') + '\n';
});
content += classCodes;
endCodeMap.forEach((line) => {
content += line + '\n';
});
let classData = {
name: baseName,
classCode: content,
replaceScriptList: replaceScriptList,
};
event.source.postMessage(classData, '*');
}
</script>
</html>