56 lines
7.5 KiB
JavaScript
56 lines
7.5 KiB
JavaScript
// Isolated UI fixture using the actual built prefab, scripts, engine and bundle assets.
|
|
// Build web-mobile first; optionally set CLOUD_RISE_BUILD, then node tools/preview-cloud-rise.cjs [port].
|
|
const http=require('node:http'),fs=require('node:fs'),path=require('node:path');
|
|
const root=path.resolve(__dirname,'..');
|
|
const built=process.env.CLOUD_RISE_BUILD||path.join(root,'build/cloud-rise-prefab-verification/web-mobile');
|
|
const port=Number(process.argv[2]||8765);
|
|
const engine=fs.readdirSync(built).find(n=>/^cocos2d-js.*\.js$/.test(n));
|
|
const html = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>cloudRise · Cocos UI preview</title><style>html,body{margin:0;background:#211c44;color:white;font:14px sans-serif}#bar{height:45px;display:flex;align-items:center;gap:8px;padding:0 12px}#game{height:calc(100vh - 45px);max-width:540px;margin:auto}canvas{outline:none}button,select{padding:6px}</style></head>
|
|
<body><div id="bar"><span>本地测试</span><select id="state"><option>报名</option><option>stage1</option><option>stage2待开始</option><option>stage2</option><option>stage3</option><option>成功</option><option>失败</option><option>超时</option></select><button id="show">查看</button><button id="win">通关</button></div><div id="game"><canvas id="GameCanvas"></canvas></div>
|
|
<script src="/engine.js"></script><script src="/preview.js"></script></body></html>`;
|
|
const boot = `
|
|
window.previewErrors=[];window.addEventListener('error',e=>previewErrors.push(e.message));
|
|
window.addEventListener('unhandledrejection',e=>previewErrors.push(String(e.reason)));
|
|
let listeners=[],data,ack=false,view;
|
|
const profile={username:'小猫玩家',useravatar:'',useravatarIcon:'icon_9'};
|
|
const makeStage=n=>({stage:n,target:[5,7,9][n-1],pool:[10000,15000,20000][n-1],success_num:0,survivors:100,status:'playing',reward:0,opponents:Array.from({length:99},(_,i)=>({...profile,username:'玩家'+(i+1),alive:true}))});
|
|
const service={error:'',data:()=>data,run:()=>data.run,stage:()=>data.run&&data.run.stages.at(-1),now:()=>Date.now(),visible:()=>true,subscribe:fn=>(listeners.push(fn),()=>listeners=listeners.filter(x=>x!==fn)),resultKey:()=>data.run&&data.run.id+':'+data.run.stage+':'+data.run.status,unseenResult:()=>!ack&&data.run&&['waiting','failed','completed','expired'].includes(data.run.status),acknowledgeResult:()=>ack=true,sync:async()=>true,
|
|
startStage:async()=>{const n=data.run&&data.run.status==='waiting'?data.run.stage+1:1;data.available=false;if(!data.run)data.run={id:'preview',stage:0,status:'playing',expiresAt:Date.now()+86400000,stages:[]};data.run.stage=n;data.run.status='playing';data.run.stages.push(makeStage(n));ack=false;listeners.forEach(fn=>fn());return true;}};
|
|
window.previewService=service;
|
|
function fixture(name){ack=false;data={available:true,pools:[10000,15000,20000],period:{enabled:true,startsAt:Date.now()-1000,endsAt:Date.now()+86400000},run:null};if(name!=='报名'){const n=name.includes('stage3')?3:name.includes('stage2')?2:1;data.available=false;data.run={id:'preview-'+name,stage:n,status:'playing',expiresAt:Date.now()+86395000,stages:[makeStage(n)]};const s=service.stage();s.success_num=2;s.survivors=47;s.opponents.forEach((o,i)=>o.alive=i<46);if(name==='stage2待开始'||name==='成功'){s.stage=1;s.target=5;s.success_num=5;s.status='won';s.rewardSaved=true;s.survivors=3;s.reward=3340;data.run.stage=1;data.run.status='waiting';s.opponents.forEach((o,i)=>o.alive=i<2);}if(name==='失败'){s.status='lost';data.run.status='failed';}if(name==='超时'){s.status='expired';data.run.status='expired';data.run.expiresAt=Date.now()-1000;}}}
|
|
fixture('报名');
|
|
|
|
cc.assetManager.init({});
|
|
const loadBundle=name=>new Promise((resolve,reject)=>cc.assetManager.loadBundle(name,(error,bundle)=>error?reject(error):resolve(bundle)));
|
|
loadBundle('internal').then(()=>cc.game.run({id:'GameCanvas',debugMode:1,frameRate:30,renderMode:0,groupList:['default'],collisionMatrix:[[true]]},async()=>{
|
|
try {
|
|
cc.view.enableRetina(true);cc.view.resizeWithBrowserSize(true);cc.view.setDesignResolutionSize(1080,2200,cc.ResolutionPolicy.SHOW_ALL);
|
|
cc.fx={GameConfig:{GM_INFO:{...profile,uid:'fixture',username:'我',coin:100,otherLevel:0}},GameTool:{getActionName:()=> 'action11'},StorageMessage:{getStorage:()=>null,setStorage:()=>{}}};
|
|
await loadBundle('resources');await loadBundle('main');const bundle=await loadBundle('cloud_rise_home');
|
|
const runtime=window.__require('CloudRiseRuntime').default;Object.assign(runtime,service);
|
|
const prefab=await new Promise((resolve,reject)=>bundle.load('prefab/CloudRise',cc.Prefab,(error,asset)=>error?reject(error):resolve(asset)));
|
|
const scene=new cc.Scene(),canvas=new cc.Node('Canvas');scene.addChild(canvas);const comp=canvas.addComponent(cc.Canvas);comp.designResolution=cc.size(1080,2200);comp.fitHeight=true;comp.fitWidth=true;cc.director.runSceneImmediate(scene);
|
|
const backdrop=new cc.Node('PreviewHome');canvas.addChild(backdrop);const bg=backdrop.addComponent(cc.Graphics);bg.fillColor=cc.color(240,224,188);bg.rect(-540,-1100,1080,2200);bg.fill();
|
|
for(const [text,x,y] of [['首页 · 本地预览',0,1010],['体力 5',-290,880],['金币 563',290,880],['关卡地图',0,-920]]){const n=new cc.Node();backdrop.addChild(n);n.setPosition(x,y);const l=n.addComponent(cc.Label);l.string=text;l.fontSize=44;n.color=cc.color(94,68,42);}
|
|
let panelNode=null;
|
|
view={close(){if(panelNode){panelNode.destroy();panelNode=null;}},async open(){panelNode=cc.instantiate(prefab);canvas.addChild(panelNode);const panel=panelNode.getComponent('CloudRisePanel');const mode=service.unseenResult()?'result':service.run()&&service.run().status==='playing'?'progress':'overview';await panel.preparePage(mode);panel.initialize(mode,false,()=>view.close(),()=>console.log('进入主线'));}};
|
|
await view.open();window.previewReady=true;
|
|
document.getElementById('show').onclick=async()=>{view.close();fixture(document.getElementById('state').value);listeners.forEach(fn=>fn());await view.open();};
|
|
document.getElementById('win').onclick=()=>{const s=service.stage();if(!s||s.status!=='playing')return;s.success_num++;s.survivors=Math.max(1,s.survivors-8);if(s.success_num>=s.target){s.status='won';s.rewardSaved=true;s.reward=Math.ceil(Math.ceil(s.pool/s.survivors)/10)*10;data.run.status=data.run.stage===3?'completed':'waiting';}listeners.forEach(fn=>fn());};
|
|
}catch(error){console.error('Prefab preview failed',error);}
|
|
})).catch(console.error);
|
|
`;
|
|
|
|
http.createServer((req,res)=>{
|
|
const url=new URL(req.url,'http://localhost').pathname;
|
|
if(url==='/'){res.setHeader('Content-Type','text/html; charset=utf-8');return res.end(html);}
|
|
if(url==='/preview.js'){res.setHeader('Content-Type','text/javascript; charset=utf-8');return res.end(boot);}
|
|
let relative=url==='/engine.js'?engine:decodeURIComponent(url).replace(/^\//,'');
|
|
const match=relative.match(/^assets\/([^/]+)\/(config\.json|index\.js)$/);
|
|
if(match){const dir=path.join(built,'assets',match[1]);if(fs.existsSync(dir)){const name=fs.readdirSync(dir).find(n=>match[2]==='config.json'?/^config.*\.json$/.test(n):/^index.*\.js$/.test(n));if(name)relative='assets/'+match[1]+'/'+name;}}
|
|
const file=path.resolve(built,relative);
|
|
if(!file.startsWith(path.resolve(built)+path.sep)||!fs.existsSync(file)||!fs.statSync(file).isFile()){res.writeHead(404);return res.end();}
|
|
res.setHeader('Content-Type',file.endsWith('.js')?'text/javascript':file.endsWith('.json')?'application/json':file.endsWith('.png')?'image/png':'application/octet-stream');fs.createReadStream(file).pipe(res);
|
|
}).listen(port,'127.0.0.1',()=>console.log('cloudRise prefab preview http://127.0.0.1:'+port));
|