refactor(arch): 改进 ServiceToken 设计,统一服务获取模式 (#300)
* refactor(arch): 移除全局变量,使用 ServiceToken 模式 - 创建 PluginServiceRegistry 类,提供类型安全的服务注册/获取 - 添加 ProfilerServiceToken 和 CollisionLayerConfigToken - 重构所有 __PROFILER_SERVICE__ 全局变量访问为 getProfilerService() - 重构 __PHYSICS_RAPIER2D__ 全局变量访问为 CollisionLayerConfigToken - 在 Core 类添加 pluginServices 静态属性 - 添加 getService.ts 辅助模块简化服务获取 这是 ServiceToken 模式重构的第一阶段,移除了最常用的两个全局变量。 后续可继续应用到其他模块(Camera/Audio 等)。 * refactor(arch): 改进 ServiceToken 设计,移除重复常量 - tokens.ts: 从 engine-core 导入 createServiceToken(符合规范) - tokens.ts: Token 使用接口 IProfilerService 而非具体类 - 移除 AssetPickerDialog 和 ContentBrowser 中重复的 MANAGED_ASSET_DIRECTORIES - 统一从 editor-core 导入 MANAGED_ASSET_DIRECTORIES * fix(type): 修复 IProfilerService 接口与实现类型不匹配 - 将 ProfilerData 等数据类型移到 tokens.ts 以避免循环依赖 - ProfilerService 显式实现 IProfilerService 接口 - 更新使用方使用 IProfilerService 接口类型而非具体类 * refactor(type): 移除类型重导出,改进类型安全 - 删除 ProfilerService.ts 中的类型重导出,消费方直接从 tokens.ts 导入 - PanelDescriptor 接口添加 titleZh 属性,移除 App.tsx 中的 as any - 改进 useDynamicIcon.ts 的类型安全,使用正确的 Record 类型 * refactor(arch): 为模块添加 ServiceToken 支持 - Material System: 创建 tokens.ts,定义 IMaterialManager 接口和 MaterialManagerToken - Audio: 创建预留 tokens.ts 文件,为未来 AudioManager 服务扩展做准备 - Camera: 创建预留 tokens.ts 文件,为未来 CameraManager 服务扩展做准备 遵循"谁定义接口,谁导出 Token"原则,统一服务访问模式
This commit is contained in:
@@ -55,105 +55,283 @@ for (const file of filesToCopy) {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 game.js
|
||||
// 创建 game.js - 完整物理球可视化演示
|
||||
// Create game.js - Full physics ball visualization demo
|
||||
const gameJs = `/**
|
||||
* ESEngine Worker System 微信小游戏测试
|
||||
* ESEngine Worker System WeChat Mini Game Test
|
||||
* ESEngine Worker System 微信小游戏物理演示
|
||||
* ESEngine Worker System WeChat Mini Game Physics Demo
|
||||
*
|
||||
* 演示 Worker 线程处理物理计算,主线程渲染
|
||||
* Demonstrates Worker thread physics + main thread rendering
|
||||
*/
|
||||
|
||||
console.log('====================================');
|
||||
console.log('ESEngine Worker 微信小游戏测试');
|
||||
console.log('====================================');
|
||||
// ============ 配置 | Configuration ============
|
||||
var CONFIG = {
|
||||
BALL_COUNT: 20, // 球数量 | Number of balls
|
||||
GRAVITY: 400, // 重力 | Gravity
|
||||
GROUND_FRICTION: 0.98, // 地面摩擦 | Ground friction
|
||||
BALL_BOUNCE: 0.85, // 弹性系数 | Bounce factor
|
||||
MIN_RADIUS: 8, // 最小半径 | Min radius
|
||||
MAX_RADIUS: 20, // 最大半径 | Max radius
|
||||
COLORS: ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F']
|
||||
};
|
||||
|
||||
// 检查 Worker API
|
||||
console.log('\\n[1] 检查环境...');
|
||||
console.log('wx.createWorker:', typeof wx.createWorker);
|
||||
|
||||
// 创建 Worker
|
||||
console.log('\\n[2] 创建 Worker...');
|
||||
// ============ 全局状态 | Global State ============
|
||||
var canvas = wx.createCanvas();
|
||||
var ctx = canvas.getContext('2d');
|
||||
var worker = null;
|
||||
var entities = [];
|
||||
var lastTime = Date.now();
|
||||
var frameCount = 0;
|
||||
var fps = 0;
|
||||
var workerReady = false;
|
||||
var pendingRequest = false;
|
||||
|
||||
try {
|
||||
worker = wx.createWorker('workers/physics-worker.js', {
|
||||
useExperimentalWorker: true
|
||||
});
|
||||
console.log('Worker 创建成功!');
|
||||
} catch (error) {
|
||||
console.error('Worker 创建失败:', error.message);
|
||||
}
|
||||
console.log('====================================');
|
||||
console.log('ESEngine Worker 物理演示');
|
||||
console.log('Canvas:', canvas.width, 'x', canvas.height);
|
||||
console.log('====================================');
|
||||
|
||||
if (worker) {
|
||||
// 设置消息处理
|
||||
worker.onMessage(function(res) {
|
||||
console.log('\\n[4] 收到 Worker 响应!');
|
||||
|
||||
if (res.error) {
|
||||
console.error('Worker 错误:', res.error);
|
||||
} else if (res.result) {
|
||||
console.log('Worker 处理成功!');
|
||||
console.log('实体数量:', res.result.length);
|
||||
|
||||
// 显示前 3 个实体
|
||||
for (var i = 0; i < Math.min(3, res.result.length); i++) {
|
||||
var e = res.result[i];
|
||||
console.log(' 实体 ' + e.id + ': (' + e.x.toFixed(1) + ', ' + e.y.toFixed(1) + ')');
|
||||
}
|
||||
|
||||
console.log('\\n========== Worker 测试成功! ==========');
|
||||
}
|
||||
});
|
||||
|
||||
// 创建测试数据
|
||||
console.log('\\n[3] 发送测试数据...');
|
||||
|
||||
var entities = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
// ============ 初始化实体 | Initialize Entities ============
|
||||
function initEntities() {
|
||||
entities = [];
|
||||
for (var i = 0; i < CONFIG.BALL_COUNT; i++) {
|
||||
var radius = CONFIG.MIN_RADIUS + Math.random() * (CONFIG.MAX_RADIUS - CONFIG.MIN_RADIUS);
|
||||
entities.push({
|
||||
id: i + 1,
|
||||
x: Math.random() * 300 + 37,
|
||||
y: Math.random() * 400 + 100,
|
||||
dx: (Math.random() - 0.5) * 100,
|
||||
dy: (Math.random() - 0.5) * 50,
|
||||
mass: 1 + Math.random(),
|
||||
bounce: 0.8,
|
||||
friction: 0.98,
|
||||
radius: 5 + Math.random() * 5
|
||||
x: radius + Math.random() * (canvas.width - radius * 2),
|
||||
y: radius + Math.random() * (canvas.height * 0.5), // 上半部分生成
|
||||
dx: (Math.random() - 0.5) * 200,
|
||||
dy: Math.random() * 100,
|
||||
mass: radius * 0.1,
|
||||
bounce: CONFIG.BALL_BOUNCE,
|
||||
friction: CONFIG.GROUND_FRICTION,
|
||||
radius: radius,
|
||||
color: CONFIG.COLORS[i % CONFIG.COLORS.length]
|
||||
});
|
||||
}
|
||||
console.log('Created', entities.length, 'balls');
|
||||
}
|
||||
|
||||
// ============ 创建 Worker | Create Worker ============
|
||||
function createWorker() {
|
||||
try {
|
||||
worker = wx.createWorker('workers/physics-worker.js', {
|
||||
useExperimentalWorker: true
|
||||
});
|
||||
|
||||
worker.onMessage(function(res) {
|
||||
pendingRequest = false;
|
||||
|
||||
if (res.error) {
|
||||
console.error('Worker error:', res.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.result && Array.isArray(res.result)) {
|
||||
// 更新实体位置(保留颜色等渲染属性)
|
||||
// Update entity positions (keep rendering properties like color)
|
||||
for (var i = 0; i < res.result.length; i++) {
|
||||
var updated = res.result[i];
|
||||
var entity = entities[i];
|
||||
if (entity && updated) {
|
||||
entity.x = updated.x;
|
||||
entity.y = updated.y;
|
||||
entity.dx = updated.dx;
|
||||
entity.dy = updated.dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
workerReady = true;
|
||||
console.log('Worker created successfully!');
|
||||
} catch (error) {
|
||||
console.error('Worker creation failed:', error.message);
|
||||
workerReady = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 发送物理更新到 Worker | Send Physics Update to Worker ============
|
||||
function sendToWorker(deltaTime) {
|
||||
if (!worker || !workerReady || pendingRequest) return;
|
||||
|
||||
// 准备发送数据(只发送物理相关属性)
|
||||
// Prepare data (only physics-related properties)
|
||||
var physicsData = [];
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var e = entities[i];
|
||||
physicsData.push({
|
||||
id: e.id,
|
||||
x: e.x,
|
||||
y: e.y,
|
||||
dx: e.dx,
|
||||
dy: e.dy,
|
||||
mass: e.mass,
|
||||
bounce: e.bounce,
|
||||
friction: e.friction,
|
||||
radius: e.radius
|
||||
});
|
||||
}
|
||||
|
||||
pendingRequest = true;
|
||||
worker.postMessage({
|
||||
id: Date.now(),
|
||||
entities: entities,
|
||||
deltaTime: 0.016,
|
||||
entities: physicsData,
|
||||
deltaTime: deltaTime,
|
||||
systemConfig: {
|
||||
gravity: 200,
|
||||
canvasWidth: 375,
|
||||
canvasHeight: 667,
|
||||
groundFriction: 0.98
|
||||
gravity: CONFIG.GRAVITY,
|
||||
canvasWidth: canvas.width,
|
||||
canvasHeight: canvas.height,
|
||||
groundFriction: CONFIG.GROUND_FRICTION
|
||||
}
|
||||
});
|
||||
|
||||
console.log('已发送 ' + entities.length + ' 个实体');
|
||||
}
|
||||
|
||||
// 创建 Canvas 显示
|
||||
var canvas = wx.createCanvas();
|
||||
var ctx = canvas.getContext('2d');
|
||||
// ============ 渲染 | Render ============
|
||||
function render() {
|
||||
// 清屏 - 深色背景
|
||||
// Clear screen - dark background
|
||||
ctx.fillStyle = '#1a1a2e';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.fillStyle = '#1a1a2e';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
// 绘制地面
|
||||
// Draw ground
|
||||
ctx.fillStyle = '#2d3436';
|
||||
ctx.fillRect(0, canvas.height - 10, canvas.width, 10);
|
||||
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.font = '18px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText('ESEngine Worker 测试', canvas.width / 2, 50);
|
||||
// 绘制所有球
|
||||
// Draw all balls
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var e = entities[i];
|
||||
|
||||
ctx.font = '14px Arial';
|
||||
ctx.fillStyle = '#aaaaaa';
|
||||
ctx.fillText('查看控制台日志', canvas.width / 2, 80);
|
||||
// 球体渐变效果
|
||||
// Ball gradient effect
|
||||
var gradient = ctx.createRadialGradient(
|
||||
e.x - e.radius * 0.3, e.y - e.radius * 0.3, 0,
|
||||
e.x, e.y, e.radius
|
||||
);
|
||||
gradient.addColorStop(0, '#ffffff');
|
||||
gradient.addColorStop(0.3, e.color);
|
||||
gradient.addColorStop(1, shadeColor(e.color, -30));
|
||||
|
||||
ctx.fillStyle = worker ? '#00ff00' : '#ff0000';
|
||||
ctx.fillText('Worker: ' + (worker ? '已创建' : '创建失败'), canvas.width / 2, 110);
|
||||
ctx.beginPath();
|
||||
ctx.arc(e.x, e.y, e.radius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fill();
|
||||
|
||||
// 球体边框
|
||||
// Ball border
|
||||
ctx.strokeStyle = shadeColor(e.color, -50);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// 绘制 UI
|
||||
// Draw UI
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.font = '14px Arial';
|
||||
ctx.textAlign = 'left';
|
||||
ctx.fillText('ESEngine Worker Physics Demo', 10, 25);
|
||||
ctx.fillText('FPS: ' + fps + ' | Balls: ' + entities.length, 10, 45);
|
||||
ctx.fillText('Worker: ' + (workerReady ? 'Active' : 'Failed'), 10, 65);
|
||||
|
||||
// 提示文字
|
||||
// Hint text
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillStyle = '#888888';
|
||||
ctx.font = '12px Arial';
|
||||
ctx.fillText('Physics calculated in Worker thread', canvas.width / 2, canvas.height - 20);
|
||||
}
|
||||
|
||||
// 颜色加深/减淡工具函数
|
||||
// Color shade utility function
|
||||
function shadeColor(color, percent) {
|
||||
var num = parseInt(color.replace('#', ''), 16);
|
||||
var amt = Math.round(2.55 * percent);
|
||||
var R = (num >> 16) + amt;
|
||||
var G = (num >> 8 & 0x00FF) + amt;
|
||||
var B = (num & 0x0000FF) + amt;
|
||||
R = Math.max(0, Math.min(255, R));
|
||||
G = Math.max(0, Math.min(255, G));
|
||||
B = Math.max(0, Math.min(255, B));
|
||||
return '#' + (0x1000000 + R * 0x10000 + G * 0x100 + B).toString(16).slice(1);
|
||||
}
|
||||
|
||||
// ============ 游戏循环 | Game Loop ============
|
||||
function gameLoop() {
|
||||
var now = Date.now();
|
||||
var deltaTime = (now - lastTime) / 1000;
|
||||
lastTime = now;
|
||||
|
||||
// 限制 deltaTime 防止跳帧
|
||||
// Clamp deltaTime to prevent frame skip
|
||||
if (deltaTime > 0.1) deltaTime = 0.1;
|
||||
|
||||
// FPS 计算
|
||||
// FPS calculation
|
||||
frameCount++;
|
||||
if (frameCount >= 30) {
|
||||
fps = Math.round(30 / ((now - (lastTime - deltaTime * 1000 * 30)) / 1000));
|
||||
frameCount = 0;
|
||||
}
|
||||
|
||||
// 发送物理计算到 Worker
|
||||
// Send physics calculation to Worker
|
||||
sendToWorker(deltaTime);
|
||||
|
||||
// 渲染
|
||||
// Render
|
||||
render();
|
||||
|
||||
// 下一帧
|
||||
// Next frame
|
||||
requestAnimationFrame(gameLoop);
|
||||
}
|
||||
|
||||
// ============ 启动 | Start ============
|
||||
initEntities();
|
||||
createWorker();
|
||||
|
||||
// 简单的 FPS 计算变量
|
||||
var fpsLastTime = Date.now();
|
||||
var fpsFrameCount = 0;
|
||||
|
||||
// 覆盖 FPS 计算逻辑
|
||||
setInterval(function() {
|
||||
var now = Date.now();
|
||||
fps = Math.round(fpsFrameCount * 1000 / (now - fpsLastTime));
|
||||
fpsLastTime = now;
|
||||
fpsFrameCount = 0;
|
||||
}, 1000);
|
||||
|
||||
// 修改 gameLoop 中的帧计数
|
||||
var originalGameLoop = gameLoop;
|
||||
gameLoop = function() {
|
||||
fpsFrameCount++;
|
||||
|
||||
var now = Date.now();
|
||||
var deltaTime = (now - lastTime) / 1000;
|
||||
lastTime = now;
|
||||
|
||||
if (deltaTime > 0.1) deltaTime = 0.1;
|
||||
|
||||
sendToWorker(deltaTime);
|
||||
render();
|
||||
requestAnimationFrame(gameLoop);
|
||||
};
|
||||
|
||||
// 开始游戏循环
|
||||
// Start game loop
|
||||
console.log('Starting game loop...');
|
||||
requestAnimationFrame(gameLoop);
|
||||
|
||||
// 触摸重置
|
||||
// Touch to reset
|
||||
wx.onTouchStart(function(e) {
|
||||
console.log('Touch detected - resetting balls');
|
||||
initEntities();
|
||||
});
|
||||
`;
|
||||
|
||||
const gameJsPath = path.join(WECHAT_PROJECT, 'game.js');
|
||||
|
||||
Reference in New Issue
Block a user