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)

部署操作

  1. 先把仓库git下来
    关键示例
git clone https://github.com/Quantelix-AI/Quantum-Sandbox-Game.git
cd Quantum-Sandbox-Game

直接复制下来并安装

  1. 装依赖

作者支持 npm 和 pnpm,我自己习惯 pnpm,速度快一点

pnpm install
# 或者用 npm 也行,就是慢一点
# npm install
  1. 直接开发模式启动
pnpm dev
# 或
npm run dev

Vite 会自己起一个本地服务,一般是 http://localhost:5173

打开浏览器应该就能看到游戏加载画面了。如果卡住不动,看控制台有没有报错,大概率是网络问题或者 AI 接口配不上(默认应该是不接 AI 也能跑基本世界)。

  1. 部署到线上可以直接使用雨云的游戏服务器,上传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);
  }
}
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐