vue-visual-novel-engine
v0.1.1
Published
A visual novel / interactive storytelling engine for Vue 3 — dialogue system, save/load, character management, choice branching, audio control
Downloads
347
Maintainers
Readme
vue-visual-novel-engine
A visual novel / interactive storytelling engine for Vue 3 + TypeScript + Pinia.
English
Features
- 📖 Typewriter Effect — Adjustable speed, click to skip
- 🌳 Branching Choices — Multiple choice options with bond/affection tracking
- 💾 Save System — 6 manual save slots + auto-save + quick-save
- 🎭 Character Sprites — Multi-character display with themed styling
- 🎵 Audio Control — BGM + SFX, auto-switch on scene change
- 🔓 Chapter Unlock — Choice-based chapter progression
- ❤️ Affection System — Choices affect character bond points
- ⚡ TypeScript — Full type definitions, IDE-friendly
- 🎨 Customizable — Override styles via CSS or scoped components
Installation
npm install vue-visual-novel-engine pinia
# or
pnpm add vue-visual-novel-engine piniaQuick Start
1. Register Pinia
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')2. Prepare Game Data
// game-data.ts
export const gameData = {
prologue: {
bg: '/images/bg-room.jpg',
dialogues: [
{ speaker: 'Narrator', text: 'Morning sunlight filters through the curtains.' },
{ speaker: 'Tang Xin', text: 'A new day begins.' },
{
speaker: 'Narrator',
text: 'You decide...',
choices: [
{ text: 'Take a walk in the park', next: 'park', bond: { linwanxing: 2 } },
{ text: 'Rest at home', next: 'home' },
],
},
],
},
park: {
bg: '/images/bg-park.jpg',
dialogues: [
{ speaker: 'Narrator', text: 'The park is bustling with people.' },
{ speaker: 'Lin Wanxing', text: 'Hey! You came for a walk too?' },
],
},
}3. Use VNScreen Component
<template>
<VNScreen :game-data="gameData" initial-chapter="prologue" />
</template>
<script setup lang="ts">
import { VNScreen } from 'vue-visual-novel-engine'
import { gameData } from './game-data'
</script>API Reference
VNScreen Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| gameData | Record<string, ChapterData> | Required | Chapter data |
| initialChapter | string | First chapter | Initial chapter ID |
| config | VNEngineConfig | - | Engine configuration |
VNScreen Events
| Event | Description |
|-------|-------------|
| complete | Emitted when game completes |
Store Methods
import { useVNStore } from 'vue-visual-novel-engine'
const store = useVNStore()
// Initialize
store.init(gameData, 'prologue')
// Navigation
store.nextDialogue() // Next dialogue
store.completeText() // Skip typewriter
store.makeChoice(0) // Select choice
// Save/Load
store.saveProgress() // Save to storage
store.loadFromStorage() // Load from storage
// Settings
store.setTextSpeed(50)
store.setAutoPlayInterval(3000)
store.toggleAutoPlay()
// Cleanup
store.cleanup()Save Manager
import { SaveManager } from 'vue-visual-novel-engine'
const saveManager = new SaveManager('my-game')
// Save
saveManager.saveToSlot(0, {
chapterId: 'chapter1',
dialogueIndex: 5,
flags: { met_late_star: true },
bond: { linwanxing: 10 },
})
// Load
const data = saveManager.loadFromSlot(0)
// Auto-save
saveManager.autoSave(gameState)
// Chapter unlock
saveManager.unlockChapter('chapter2')
const unlocked = saveManager.getUnlockedChapters()Audio Manager
import { AudioManager } from 'vue-visual-novel-engine'
const audio = new AudioManager()
// Play
audio.playBGM('/music/theme.mp3')
audio.playSFX('/sfx/click.mp3')
// Control
audio.pauseBGM()
audio.resumeBGM()
audio.setBGMVolume(0.7)
audio.setSFXVolume(0.8)
// Cleanup
audio.destroy()Custom Styling
Components use scoped CSS. Override globally:
.vn-screen {
font-family: 'Your Font', sans-serif;
}
.vn-dialogue-box {
background: rgba(0, 0, 0, 0.9);
}Project Structure
vue-visual-novel-engine/
├── src/
│ ├── engine/
│ │ ├── DialogueEngine.ts # Core dialogue engine
│ │ ├── SaveManager.ts # Save/load manager
│ │ └── AudioManager.ts # Audio manager
│ ├── stores/
│ │ └── useVNStore.ts # Pinia store
│ ├── components/
│ │ ├── VNScreen.vue # Main screen component
│ │ ├── DialogueBox.vue # Dialogue box
│ │ ├── CharacterSlot.vue # Character sprite
│ │ └── ChoicePanel.vue # Choice panel
│ ├── types.ts # TypeScript types
│ └── index.ts # Entry point
├── vite.config.ts
├── tsconfig.json
└── package.jsonDevelopment
# Install dependencies
npm install
# Dev mode
npm run dev
# Build
npm run build
# Type check
npm run type-checkRelated Projects
- Mountain and City Echo — A visual novel built with this engine
License
MIT © chuangzaozhe1
中文文档
特性
- 📖 打字机效果 — 可调节速度,支持点击跳过
- 🌳 分支选择 — 支持多选项剧情分支,可关联好感度
- 💾 存档系统 — 6个手动存档槽位 + 自动存档 + 快速存档
- 🎭 角色立绘 — 支持多角色同时显示,自动主题色
- 🎵 音频管理 — BGM + 音效,场景切换自动切换音乐
- 🔓 章节解锁 — 基于选择的章节解锁机制
- ❤️ 好感度系统 — 选择影响角色好感度
- ⚡ TypeScript — 完整类型定义,IDE 友好
- 🎨 可定制 — 组件样式可通过 CSS 变量或 scoped 覆盖
安装
npm install vue-visual-novel-engine pinia
# 或
pnpm add vue-visual-novel-engine pinia快速开始
1. 注册 Pinia
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')2. 准备游戏数据
// game-data.ts
export const gameData = {
prologue: {
bg: '/images/bg-room.jpg',
dialogues: [
{ speaker: '旁白', text: '清晨的阳光透过窗帘照进来。' },
{ speaker: '唐信', text: '新的一天开始了。' },
{
speaker: '旁白',
text: '你决定...',
choices: [
{ text: '去公园散步', next: 'park', bond: { linwanxing: 2 } },
{ text: '在家休息', next: 'home' },
],
},
],
},
park: {
bg: '/images/bg-park.jpg',
dialogues: [
{ speaker: '旁白', text: '公园里人来人往。' },
{ speaker: '林晚星', text: '嘿!你也来散步?' },
],
},
}3. 使用 VNScreen 组件
<template>
<VNScreen :game-data="gameData" initial-chapter="prologue" />
</template>
<script setup lang="ts">
import { VNScreen } from 'vue-visual-novel-engine'
import { gameData } from './game-data'
</script>API 文档
VNScreen 属性
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| gameData | Record<string, ChapterData> | 必填 | 章节数据 |
| initialChapter | string | 第一个章节 | 初始章节 ID |
| config | VNEngineConfig | - | 引擎配置 |
VNScreen 事件
| 事件 | 说明 |
|------|------|
| complete | 游戏完成时触发 |
Store 方法
import { useVNStore } from 'vue-visual-novel-engine'
const store = useVNStore()
// 初始化
store.init(gameData, 'prologue')
// 导航
store.nextDialogue() // 下一句
store.completeText() // 跳过打字
store.makeChoice(0) // 选择选项
// 存档
store.saveProgress() // 保存到本地
store.loadFromStorage() // 从本地加载
// 设置
store.setTextSpeed(50)
store.setAutoPlayInterval(3000)
store.toggleAutoPlay()
// 清理
store.cleanup()存档管理
import { SaveManager } from 'vue-visual-novel-engine'
const saveManager = new SaveManager('my-game')
// 保存
saveManager.saveToSlot(0, {
chapterId: 'chapter1',
dialogueIndex: 5,
flags: { met_late_star: true },
bond: { linwanxing: 10 },
})
// 加载
const data = saveManager.loadFromSlot(0)
// 自动存档
saveManager.autoSave(gameState)
// 章节解锁
saveManager.unlockChapter('chapter2')
const unlocked = saveManager.getUnlockedChapters()音频管理
import { AudioManager } from 'vue-visual-novel-engine'
const audio = new AudioManager()
// 播放
audio.playBGM('/music/theme.mp3')
audio.playSFX('/sfx/click.mp3')
// 控制
audio.pauseBGM()
audio.resumeBGM()
audio.setBGMVolume(0.7)
audio.setSFXVolume(0.8)
// 清理
audio.destroy()自定义样式
组件使用 scoped CSS,可通过以下方式覆盖:
/* 全局覆盖 */
.vn-screen {
font-family: 'Your Font', sans-serif;
}
.vn-dialogue-box {
background: rgba(0, 0, 0, 0.9);
}项目结构
vue-visual-novel-engine/
├── src/
│ ├── engine/
│ │ ├── DialogueEngine.ts # 对话引擎核心
│ │ ├── SaveManager.ts # 存档管理
│ │ └── AudioManager.ts # 音频管理
│ ├── stores/
│ │ └── useVNStore.ts # Pinia 状态管理
│ ├── components/
│ │ ├── VNScreen.vue # 主屏幕组件
│ │ ├── DialogueBox.vue # 对话框组件
│ │ ├── CharacterSlot.vue # 角色立绘组件
│ │ └── ChoicePanel.vue # 选择面板组件
│ ├── types.ts # TypeScript 类型定义
│ └── index.ts # 入口文件
├── vite.config.ts # Vite 构建配置
├── tsconfig.json # TypeScript 配置
└── package.json开发
# 安装依赖
npm install
# 开发模式
npm run dev
# 构建
npm run build
# 类型检查
npm run type-check相关项目
- 山与城的回响 — 使用此引擎开发的视觉小说游戏
许可证
MIT © chuangzaozhe1
