轻量级 Web 游戏引擎 & 平台一体化架构
┌─────────────────────────────┐
│ 游戏编辑器 (IDE) │
│ - 场景编辑器 │
│ - 脚本编辑 (JS/TS) │
│ - 动画/关卡编辑 │
│ - 资源管理 (拖拽上传) │
│ - 即时预览 (内置引擎) │
└───────────────┬─────────────┘
│
一键发布/构建
│
┌───────────────▼─────────────┐
│ 轻量级 Web 游戏引擎 │
│ - 渲染: Canvas/WebGL │
│ - 核心: 场景树/组件系统 │
│ - 物理: 碰撞/简单刚体 │
│ - 输入: 触屏/键盘/鼠标 │
│ - 平台 SDK API 接入 │
│ * 用户登录 │
│ * 广告 (激励视频/横幅) │
│ * 内购/支付 │
│ * 数据埋点 │
└───────────────┬─────────────┘
│
游戏包/资源上传
│
┌───────────────▼─────────────┐
│ 小游戏平台 (SaaS) │
│ - 游戏发布管理 │
│ - 审核/灰度/版本控制 │
│ - 数据分析 (DAU/留存/ARPU) │
│ - 广告投放/收益结算 │
│ - 社区/排行榜/成就系统 │
└───────────────┬─────────────┘
│
数据埋点/收入回流
│
┌───────────────▼─────────────┐
│ 开发者控制台 & 广告主后台 │
│ - 开发者: 游戏数据、收益结算 │
│ - 广告主: 投放、ROI 分析 │
│ - 平台方: 审核、风控、监控 │
└─────────────────────────────┘
模块关系说明
1. 游戏编辑器 (IDE)
- 可视化工具:拖拽式编辑、属性面板、动画编辑
- 内置引擎预览:快速测试效果
- 一键发布:自动构建 → 上传到平台
2. 轻量级 Web 游戏引擎
- 跨平台运行时(Web / 小程序 / PWA)
- 提供 核心功能(渲染、物理、输入、UI)
- 内置 平台 SDK 适配层(广告、支付、排行榜)
- 支持插件扩展(多人联机、AI 关卡)
3. 小游戏平台 (SaaS 层)
- 游戏管理:上传 / 审核 / 灰度发布
- 数据分析:埋点、实时报表、推荐算法
- 商业化:广告投放、支付结算、收益分成
- 社交功能:排行榜、成就、分享裂变
4. 开发者控制台 & 广告主后台
- 开发者:数据洞察 + 收益提现
- 广告主:自助投放 + ROI 实时报表
- 运营方:风控审核 + 广告库存管理
平台联动亮点
编辑器 → 平台直通
- 开发者一键发布,省去复杂构建与适配
- 游戏审核、托管、分发全自动化
引擎 → 平台能力对接
- 引擎 API 内置平台服务(广告、支付、排行榜)
- 游戏天然具备商业化与数据驱动能力
平台 → 数据回流
- 游戏内数据(埋点、收益、广告转化) → 回传平台
- 开发者可实时查看,广告主可优化投放
差异化价值
- 开发者:从开发 → 发布 → 运营 → 变现,全链路闭环
- 玩家:无缝体验(点开即玩、即更即用)
- 平台:数据和收益回流,强化生态控制力
- 广告主:精准 ROI 分析,闭环优化投放
5 代码实践:Canvas2D 渲染管线与 TypeScript 组件化引擎
本节提供引擎运行时的核心代码实现,涵盖 Canvas2D 渲染管线、场景树、组件化架构、资源预加载 与 平台 SDK 适配层。
6.1 核心渲染 Renderer.ts
export class Renderer {
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
private dpr: number;
constructor(canvasId: string, width: number, height: number) {
this.canvas = document.getElementById(canvasId) as HTMLCanvasElement;
this.dpr = window.devicePixelRatio || 1;
this.canvas.width = width * this.dpr;
this.canvas.height = height * this.dpr;
this.canvas.style.width = `${width}px`;
this.canvas.style.height = `${height}px`;
this.ctx = this.canvas.getContext('2d')!;
this.ctx.scale(this.dpr, this.dpr);
}
render(root: Node) {
this.clear();
this.drawNode(root);
}
private clear() {
this.ctx.clearRect(0, 0, this.canvas.width/this.dpr, this.canvas.height/this.dpr);
}
private drawNode(node: Node) {
if (!node.visible) return;
this.ctx.save();
this.ctx.translate(node.x, node.y);
this.ctx.rotate(node.rotation);
this.ctx.scale(node.scaleX, node.scaleY);
if (node.alpha < 1) this.ctx.globalAlpha = node.alpha;
if (node.sprite) node.sprite.draw(this.ctx);
for (const child of node.children) this.drawNode(child);
this.ctx.restore();
}
}
6.2 场景树节点 Node.ts
export class Node {
id: string; x=0; y=0; rotation=0; scaleX=1; scaleY=1; alpha=1;
visible=true; zIndex=0;
parent: Node|null=null;
children: Node[]=[];
sprite: Sprite|null=null;
components: Component[]=[];
addChild(child: Node) { child.parent=this; this.children.push(child); this.children.sort((a,b)=>a.zIndex-b.zIndex); }
removeChild(child: Node) { this.children=this.children.filter(c=>c!==child); child.parent=null; }
addComponent<T extends Component>(comp: T): T { comp.node=this; this.components.push(comp); comp.onStart?.(); return comp; }
update(delta: number) { for (const c of this.components) c.onUpdate?.(delta); for (const c of this.children) c.update(delta); }
}
6.3 组件基类
export abstract class Component {
node: Node|null=null; enabled=true;
onStart?(): void; onUpdate?(delta: number): void;
}
export class Sprite extends Component {
image: HTMLImageElement|null=null; srcRect={x:0,y:0,w:0,h:0};
draw(ctx: CanvasRenderingContext2D) {
if (!this.image) return;
ctx.drawImage(this.image, this.srcRect.x, this.srcRect.y, this.srcRect.w, this.srcRect.h,
-this.srcRect.w/2, -this.srcRect.h/2, this.srcRect.w, this.srcRect.h);
}
}
export class Collider extends Component {
width=0; height=0;
intersects(other: Collider): boolean {
if (!this.node||!other.node) return false;
const a=this.node, b=other.node;
return a.x-this.width/2 < b.x+other.width/2 && a.x+this.width/2 > b.x-other.width/2 &&
a.y-this.height/2 < b.y+other.height/2 && a.y+this.height/2 > b.y-other.height/2;
}
}
6.4 资源预加载
export class AssetManager {
private cache = new Map<string, HTMLImageElement|HTMLAudioElement>();
async loadImage(src: string): Promise<HTMLImageElement> {
if (this.cache.has(src)) return this.cache.get(src) as HTMLImageElement;
return new Promise((resolve, reject)=>{
const img=new Image();
img.onload=()=>{this.cache.set(src,img); resolve(img);};
img.onerror=reject; img.src=src;
});
}
preloadAll(manifest: Record<string,string>): Promise<void[]> {
return Promise.all(Object.entries(manifest).map(([_,src])=>
src.endsWith('.png')||src.endsWith('.jpg') ? this.loadImage(src).then(()=>{}) : Promise.resolve()));
}
}
6.5 平台 SDK 适配层
export interface PlatformSDK {
showAd(slotId: string): Promise<void>;
pay(params: {amount:number; productId:string}): Promise<string>;
submitScore(leaderboard: string, score: number): Promise<void>;
getUserInfo(): Promise<{userId:string; nickName:string}>;
}
export class WechatSDK implements PlatformSDK {
async showAd(slotId: string): Promise<void> {
return new Promise((resolve,reject)=>{
wx.createRewardedVideoAd({adUnitId:slotId}).show().then(resolve).catch(reject);
});
}
async pay(params: any) {
const res = await wx.requestPayment({...params});
return res.transactionId;
}
async submitScore(l: string, s: number) {
wx.setUserCloudStorage({KVDataList:[{key:l,value:String(s)}]});
}
async getUserInfo() {
return wx.getUserInfo({withCredentials:false});
}
}
export class WebMockSDK implements PlatformSDK {
async showAd(slotId: string) { console.log('[Mock Ad]', slotId); }
async pay(params: any) { return 'mock-'+Date.now(); }
async submitScore(l: string, s: number) { console.log('[Mock]', l, s); }
async getUserInfo() { return {userId:'web-test', nickName:'TestUser'}; }
}
6.6 使用示例
async function main() {
const renderer = new Renderer('gameCanvas', 720, 1280);
const assets = new AssetManager();
await assets.preloadAll({hero:'https://cdn.example.com/hero.png', bg:'https://cdn.example.com/bg.png'});
const root = new Node();
const hero = new Node();
hero.x = 360; hero.y = 640;
const sp = new Sprite();
sp.image = (await assets.loadImage('https://cdn.example.com/hero.png'));
sp.srcRect = {x:0,y:0,w:64,h:64};
hero.addComponent(sp);
root.addChild(hero);
const platform = typeof wx !== 'undefined' ? new WechatSDK() : new WebMockSDK();
await platform.showAd('ad-slot-001');
let last = performance.now();
function loop(now:number) {
root.update((now-last)/1000); last=now;
renderer.render(root);
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
main();
本章小结
设计了游戏引擎、平台 SaaS 层与云服务三层一体化架构,明确各层职责与模块关系,实现引擎-平台深度集成的正向飞轮。
延伸阅读
相关专题
📂 本文属于 「小游戏服务平台」完整知识体系。
如果你正在构建一个 SaaS 小游戏平台,可参考 后端架构白皮书 了解多租户、缓存、高可用等通用架构模式。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。