Quantum Sandbox Game 本地部署笔记
《Quantum Sandbox Game本地部署指南》摘要: 本文介绍了2D沙盒生存游戏demo Quantum Sandbox Game的本地部署方法。该项目采用TypeScript+PixiJS开发,核心特色包括AI驱动的NPC、程序化无限世界和物理碰撞系统。部署需要Node.js≥18和现代浏览器支持,通过git克隆仓库后安装依赖即可运行开发服务器。文章详细说明了项目结构、系统配置要求、部
Quantum Sandbox Game 本地部署笔记
本次教程以 雨云RainYun 服务器为主。
点击享受一杯奶茶钱购买 雨云 海外云服务器进行部署
点击查看详细的服务器开通教程
点击查看详细的宝塔面板安装教程
更多优质文章请前往 Flow Ciotter 论坛
最近刷到 Quantelix-AI 的这个 Quantum Sandbox Game,名字里有 Quantum 但其实和量子计算没什么关系,其实就是一个挺有野心的 2D 沙盒生存游戏demo。
这个项目的核心卖点是 AI 驱动的 NPC + 程序化无限世界 + Matter.js 的物理碰撞战斗,整体用 TypeScript + PixiJS 搭的,目前还在非常早期的阶段,但框架和想法都挺比较有意思,适合喜欢自己魔改游戏的人玩,或者正处于学习阶段的朋友研究研究。
简单说就是个像素风 Terraria / Minecraft 2D 版雏形,但 NPC 是基于语言模型沟通的、有记忆、会自己给自己安排目标,还能动态生成任务,天气也会影响大家的行为。作者留了很多 AI 接口(DeepSeek、Kimi、通义、OpenAI 什么都能接),所以理论上后面可以越接越聪明。
项目结构
quantum-sandbox-game/
├── src/
│ ├── ai/ # AI systems and integrations
│ │ ├── AIManager.ts # Central AI management
│ │ ├── BehaviorTree.ts # NPC behavior trees
│ │ ├── DeepSeekController.ts # DeepSeek AI integration
│ │ └── KimiController.ts # Kimi AI integration
│ ├── core/ # Core game engine
│ │ ├── GameEngine.ts # Main game engine
│ │ ├── InputManager.ts # Input handling system
│ │ ├── SystemManager.ts # System lifecycle management
│ │ └── EventBus.ts # Event system
│ ├── entities/ # Game entities
│ │ ├── Player.ts # Player character
│ │ ├── NPC.ts # Non-player characters
│ │ ├── Enemy.ts # Enemy entities
│ │ └── EntityManager.ts # Entity lifecycle
│ ├── physics/ # Physics simulation
│ │ ├── PhysicsEngine.ts # Matter.js integration
│ │ └── CollisionManager.ts # Collision detection
│ ├── rendering/ # Graphics and rendering
│ │ ├── RenderingSystem.ts # Main rendering pipeline
│ │ ├── CameraSystem.ts # Camera controls
│ │ ├── ParticleSystem.ts # Particle effects
│ │ └── TerrainRenderer.ts # World rendering
│ ├── world/ # World systems
│ │ ├── WorldManager.ts # World state management
│ │ ├── ChunkManager.ts # Terrain chunking
│ │ ├── BiomeSystem.ts # Biome generation
│ │ └── WeatherSystem.ts # Dynamic weather
│ ├── ui/ # User interface
│ │ ├── UIManager.ts # UI management
│ │ ├── DialogSystem.ts # Dialogue interface
│ │ └── Inventory.ts # Inventory system
│ └── utils/ # Utility functions
│ ├── MathUtils.ts # Mathematical utilities
│ ├── ObjectPool.ts # Object pooling
│ └── PerlinNoise.ts # Noise generation
├── assets/ # Game assets
│ ├── sprites/ # 2D sprites and textures
│ ├── sounds/ # Audio files
│ └── data/ # Configuration data
├── docs/ # Documentation
└── tests/ # Test files
系统配置
我本地是 Mac + Node 20,跑起来没遇到大坑。理论上 Windows / Linux 也差不多,只要 Node 不太古老就行。
需要的东西就只要两样:
- Node.js ≥ 18(建议 20 或 22 LTS)
- 现代浏览器(Chrome/Edge 之类,要支持 WebGL)
部署操作
- 先把仓库git下来

git clone https://github.com/Quantelix-AI/Quantum-Sandbox-Game.git
cd Quantum-Sandbox-Game
直接复制下来并安装
- 装依赖
作者支持 npm 和 pnpm,我自己习惯 pnpm,速度快一点
pnpm install
# 或者用 npm 也行,就是慢一点
# npm install
- 直接开发模式启动
pnpm dev
# 或
npm run dev
Vite 会自己起一个本地服务,一般是 http://localhost:5173
打开浏览器应该就能看到游戏加载画面了。如果卡住不动,看控制台有没有报错,大概率是网络问题或者 AI 接口配不上(默认应该是不接 AI 也能跑基本世界)。
- 部署到线上可以直接使用雨云的游戏服务器,上传dist目录,然后启用部署即可。
先打包
pnpm build
# 或 npm run build
会在项目根目录生成 dist 文件夹,里面就是静态文件。
然后随便找个地方托管就行:
- Vercel / Netlify:直接把 dist 拖进去或者连 GitHub 仓库自动部署,最简单
- Nginx / Caddy:把 dist 丢到服务器静态目录
- GitHub Pages:新建 gh-pages 分支,把 dist 内容推上去也行
想要持久化部署,可以在雨云购买相应的服务器进行部署。
一点小建议
- 第一次跑可能会比较卡,因为程序化生成世界 + Perlin 噪声计算量不小。等区块加载完就好很多。
- 现在 NPC 的 AI 对话功能默认是 mock 的,想真用就需要自己去 .env 或配置文件里填 API Key(README 有说明,支持 DeepSeek 和 Kimi ,推荐dp,价格很便宜)。
- 物理系统用 Matter.js,战斗手感还行,但技能特效和连击基本没做完,可以进行一些简单的操作。
- 移动端也可以跑,就是 UI 没怎么适配,建议使用电脑游玩。
总的来说这个项目现在更像一个可玩的引擎雏形,而不是完整游戏。想学习 AI + 沙盒方向的话,可以 clone 下来研究一下。后面作者如果持续更新(希望吧),接上大模型后 NPC 应该会越来越有灵魂。
有空我也想自己加点 mod,比如让 NPC 能种地、吵架、组队什么的,都是可以去实现的。
欢迎去仓库点个 star 鼓励一下作者:
https://github.com/Quantelix-AI/Quantum-Sandbox-Game
以下是该项目的api参考,大家可以根据自己需求直接copy:
核心 API
GameEngine
游戏引擎的主入口点。
class GameEngine {
constructor(options: GameEngineOptions)
async initialize(): Promise<void>
start(): void
stop(): void
getEventBus(): EventBus
getEntityManager(): EntityManager
getWorldManager(): WorldManager
getAIManager(): AIManager
getUIManager(): UIManager
}
参数:
options: GameEngineOptions- 引擎配置
示例:
const game = new GameEngine({
container: document.getElementById('game-container'),
seed: 12345,
maxAiCallsPerHour: 100,
deepSeekApiKey: 'your-api-key',
kimiApiKey: 'your-api-key'
});
await game.initialize();
game.start();
GameEngineOptions
interface GameEngineOptions {
container: HTMLElement;
width?: number;
height?: number;
seed: number;
debug?: boolean;
maxAiCallsPerHour: number;
deepSeekApiKey?: string;
deepSeekBaseUrl?: string;
kimiApiKey?: string;
kimiBaseUrl?: string;
}
EventBus
用于解耦通信的全局事件系统。
class EventBus {
emit<T>(event: string, data: T): void
on<T>(event: string, handler: (data: T) => void): void
off(event: string, handler: Function): void
once<T>(event: string, handler: (data: T) => void): void
}
事件:
entity:added-{ entity: BaseEntity }entity:removed-{ entityId: string }world:state-{ state: WorldState }npc:behavior-{ npc: NPC, decision: NPCBehaviorDecision }player:damage-{ amount: number, source: string }world:new-day-{ day: number }
示例:
// 监听实体创建
game.getEventBus().on('entity:added', ({ entity }) => {
console.log(`实体 ${entity.id} 已创建`);
});
// 触发自定义事件
game.getEventBus().emit('custom:event', { data: 'value' });
SystemManager
管理游戏系统生命周期和执行顺序。
class SystemManager {
register(system: GameSystem): void
async initializeAll(): Promise<void>
updateAll(deltaMs: number): void
destroyAll(): void
}
GameSystem
所有游戏系统的基础接口。
interface GameSystem {
readonly name: string;
readonly priority: number;
initialize(): void | Promise<void>;
update(deltaMs: number): void;
destroy(): void;
}
实体 API
BaseEntity
所有游戏实体的基类。
abstract class BaseEntity {
readonly id: string;
readonly type: string;
protected body: Matter.Body;
protected sprite: Sprite;
constructor(x: number, y: number, type: string)
abstract createPhysicsBody(): Matter.Body
abstract createSprite(): Sprite
getBody(): Matter.Body
getSprite(): Sprite
getPosition(): Vector2
setPosition(x: number, y: number): void
update(delta: number): void
destroy(): void
}
示例:
class MyEntity extends BaseEntity {
createPhysicsBody(): Matter.Body {
return Matter.Bodies.rectangle(0, 0, 32, 32);
}
createSprite(): Sprite {
const graphics = new Graphics();
graphics.rect(0, 0, 32, 32);
graphics.fill(0xff0000);
return graphics;
}
}
Player
带有输入处理的玩家角色实体。
class Player extends BaseEntity {
constructor(x: number, y: number, inputManager: InputManager)
handleInput(): void
moveUp(): void
moveDown(): void
moveLeft(): void
moveRight(): void
jump(): void
interact(): void
}
NPC
具有 AI 能力的非玩家角色。
class NPC extends BaseEntity {
constructor(x: number, y: number, name: string)
setDialogueProvider(provider: DialogueProvider): void
setBehaviorProvider(provider: BehaviorProvider): void
requestDialogue(playerMessage: string): Promise<DialogueResponse>
evaluateBehavior(): Promise<NPCBehaviorDecision>
readonly name: string;
readonly personality: string;
}
提供者:
type DialogueProvider = (npc: NPC) => Promise<DialogueResponse>;
type BehaviorProvider = (npc: NPC) => Promise<NPCBehaviorDecision>;
Enemy
敌对 NPC 实体。
class Enemy extends NPC {
constructor(x: number, y: number, name: string)
attack(target: BaseEntity): void
patrol(): void
chase(target: BaseEntity): void
}
EntityManager
管理所有游戏实体。
class EntityManager implements GameSystem {
addEntity(entity: BaseEntity): void
removeEntity(entityId: string): void
getPlayer(): Player
listEntities(): BaseEntity[]
getNPCs(): NPC[]
findNearestNPC(maxDistance: number): NPC | null
spawnDefaultNPCs(): void
}
世界 API
WorldManager
管理世界状态、时间和天气。
class WorldManager implements GameSystem {
async preloadMapAndRender(renderer: TerrainRenderer): Promise<void>
focusPosition(position: Vector2): void
getChunkAt(position: Vector2): WorldChunk
getWeatherState(): WeatherState
getState(): WorldState
}
WorldState
interface WorldState {
timeOfDay: number; // 0-24
dayCount: number;
weather: WeatherState;
activeChunks: WorldChunk[];
}
WeatherState
interface WeatherState {
type: WeatherType;
intensity: number;
duration: number;
}
type WeatherType = 'clear' | 'rain' | 'snow' | 'storm' | 'fog';
ChunkManager
管理世界区块和生成。
class ChunkManager {
async preloadMap(): Promise<void>
getChunk(x: number, z: number): WorldChunk | undefined
getChunkAt(position: Vector2): WorldChunk
getAllChunks(): WorldChunk[]
getActiveChunks(): WorldChunk[]
isMapPreloaded(): boolean
}
WorldChunk
interface WorldChunk {
id: string;
gridX: number;
gridY: number;
biome: BiomeType;
elevation: number;
moisture: number;
temperature: number;
}
BiomeType
type BiomeType = 'forest' | 'desert' | 'tundra' | 'swamp' | 'plains';
AI API
AIManager
管理 AI 驱动的 NPC 行为和对话。
class AIManager implements GameSystem {
async requestDialogue(npc: NPC, playerMessage: string): Promise<DialogueResponse>
async evaluateBehavior(state: NPCState): Promise<NPCBehaviorDecision>
getRemainingBudget(): number
}
AIManagerOptions
interface AIManagerOptions {
maxCallsPerHour: number;
deepSeek: DeepSeekConfig;
kimi: KimiConfig;
}
DialogueResponse
interface DialogueResponse {
speaker: string;
text: string;
emotion: EmotionType;
context?: Record<string, any>;
}
type EmotionType = 'happy' | 'sad' | 'angry' | 'neutral' | 'excited' | 'fearful';
NPCBehaviorDecision
interface NPCBehaviorDecision {
action: BehaviorAction;
target?: Vector2;
duration: number;
priority: number;
reason?: string;
}
type BehaviorAction = 'idle' | 'move' | 'attack' | 'flee' | 'patrol' | 'interact';
BehaviorContext
interface BehaviorContext {
npcId: string;
hunger: number;
health: number;
fatigue: number;
worldTime: number;
distanceToPlayer: number;
weather: WeatherState;
}
DialogueContext
interface DialogueContext {
npcName: string;
profession: string;
personality: string;
backstory: string;
playerMessage: string;
affection: number;
mood: string;
}
DeepSeekController
class DeepSeekController {
constructor(config: DeepSeekConfig)
isEnabled(): boolean
async decideBehavior(context: BehaviorContext, fallback: () => NPCBehaviorDecision): Promise<NPCBehaviorDecision>
}
KimiController
class KimiController {
constructor(config: KimiConfig)
isEnabled(): boolean
async generateDialogue(context: DialogueContext): Promise<DialogueResponse>
}
物理 API
PhysicsEngine
封装 Matter.js 物理模拟。
class PhysicsEngine {
readonly engine: Matter.Engine;
addBody(body: Matter.Body): void
removeBody(body: Matter.Body): void
update(deltaMs: number): void
getGravity(): Vector2
setGravity(x: number, y: number): void
}
CollisionManager
管理碰撞检测和响应。
class CollisionManager {
constructor(engine: Matter.Engine, eventBus: EventBus)
initialize(): void
destroy(): void
onCollisionStart(handler: (event: CollisionEvent) => void): void
onCollisionEnd(handler: (event: CollisionEvent) => void): void
}
CollisionEvent
interface CollisionEvent {
bodyA: Matter.Body;
bodyB: Matter.Body;
entityA?: BaseEntity;
entityB?: BaseEntity;
collision: Matter.Collision;
}
渲染 API
RenderingSystem
管理 PixiJS 渲染管线。
class RenderingSystem implements GameSystem {
constructor(options: RenderingOptions)
getApplication(): PIXI.Application
getStage(): PIXI.Container
getCamera(): Camera
getTerrainRenderer(): TerrainRenderer
screenToWorld(screenX: number, screenY: number): Vector2
worldToScreen(worldX: number, worldY: number): Vector2
}
RenderingOptions
interface RenderingOptions {
container: HTMLElement;
width: number;
height: number;
background?: number;
antialias?: boolean;
}
Camera
用于视口管理的 2D 相机。
class Camera {
follow(target: DisplayObject): void
unfollow(): void
setPosition(x: number, y: number): void
getPosition(): Vector2
zoom(factor: number): void
getZoom(): number
shake(intensity: number, duration: number): void
}
TerrainRenderer
渲染程序化地形。
class TerrainRenderer {
constructor(stage: Container)
renderChunk(chunk: WorldChunk, chunkSize?: number): void
renderChunks(chunks: WorldChunk[], chunkSize?: number): void
removeChunk(chunkId: string): void
clear(): void
destroy(): void
}
事件 API
InputManager
处理键盘和鼠标输入。
class InputManager {
initialize(canvas: HTMLCanvasElement): void
destroy(): void
isKeyPressed(key: string): boolean
isKeyJustPressed(key: string): boolean
getMousePosition(): Vector2
onKeyDown(key: string, handler: () => void): void
onKeyUp(key: string, handler: () => void): void
onMouseMove(handler: (position: Vector2) => void): void
onClick(handler: (position: Vector2) => void): void
}
UIManager
管理用户界面元素。
class UIManager {
constructor(aiManager: AIManager, eventBus: EventBus)
showDialogue(npc: NPC, message: string): Promise<void>
hideDialogue(): void
showNotification(message: string, type?: NotificationType): void
hideNotification(): void
updateInputDisplay(inputs: InputState): void
}
工具 API
PerlinNoise
生成 Perlin 噪声用于地形生成。
class PerlinNoise {
constructor(seed: number)
getElevation(x: number, z: number): number
getMoisture(x: number, z: number): number
getTemperature(x: number, z: number): number
getBiome(x: number, z: number): BiomeType
}
MathUtils
数学工具函数。
class MathUtils {
static clamp(value: number, min: number, max: number): number
static lerp(start: number, end: number, factor: number): number
static distance(a: Vector2, b: Vector2): number
static angle(a: Vector2, b: Vector2): number
static randomRange(min: number, max: number): number
}
类型定义
Vector2
interface Vector2 {
x: number;
y: number;
}
GameConfig
interface GameConfig {
worldSizeKm: number;
chunkSizeMeters: number;
seed: number;
maxAiCallsPerHour: number;
enableDebug: boolean;
}
NPCState
interface NPCState {
npc: NPC;
decisionCooldown: number;
hunger: number;
health: number;
fatigue: number;
}
DeepSeekConfig
interface DeepSeekConfig {
apiKey?: string;
baseUrl?: string;
}
KimiConfig
interface KimiConfig {
apiKey?: string;
baseUrl?: string;
}
错误处理
错误类型
class GameEngineError extends Error {
constructor(message: string, public code: string)
}
class AIError extends GameEngineError {
constructor(message: string, public aiProvider: string)
}
class PhysicsError extends GameEngineError {
constructor(message: string, public body?: Matter.Body)
}
class RenderError extends GameEngineError {
constructor(message: string, public sprite?: DisplayObject)
}
错误处理模式
try {
await game.initialize();
} catch (error) {
if (error instanceof AIError) {
console.error(`AI 错误来自 ${error.aiProvider}: ${error.message}`);
// 回退到默认行为
} else if (error instanceof PhysicsError) {
console.error(`物理错误: ${error.message}`);
// 检查物体有效性
} else {
console.error(`游戏错误: ${error.message}`);
}
}
优雅降级
// AI 不可用时回退
async requestDialogue(npc: NPC, message: string): Promise<DialogueResponse> {
try {
if (!this.kimi.isEnabled() || this.aiCallBudget <= 0) {
throw new AIError('AI 服务不可用', 'kimi');
}
return await this.kimi.generateDialogue(context);
} catch (error) {
// 回退到预定义响应
return this.generateFallbackDialogue(context);
}
}
更多推荐

所有评论(0)