经过上周组件模块的实现后,这周我将把这些组件集成到我的前端可视化界面中。在开发这个沉浸式穿书叙事游戏的过程中,我首先构建了两个核心入口界面:世界观选择页面和角色创建页面。这两个页面是整个游戏体验的起点,直接影响玩家对游戏的第一印象和沉浸感。

 世界观选择页面

由于世界观选择界面比较简单,因此在前面的组件设计中我没有针对这个界面进行组件的设计。我们小队经过讨论后选择并设计了以下四个初始世界观,每个都有独特的视觉标识和氛围描述:

世界观 关键词 视觉元素
古代宫廷 权谋 · 宫斗 · 江湖 宫殿、折扇、玉佩
修仙世界 仙侠 · 修真 · 宗门 仙鹤、飞剑、莲台
现代校园 青春 · 梦想 · 成长 书本、吉他、篮球
末世生存 废土 · 变异 · 希望 防护面罩、齿轮、藤蔓

设计原则

简洁大方

确定了白色主色调 + 渐变装饰条的视觉风格:

  • 背景:浅灰白渐变(#f8f9fc),干净清爽

  • 卡片:纯白色背景,细腻边框,轻盈阴影

  • 装饰:每个卡片顶部有一条彩色装饰条,代表不同世界的主题色

信息层级清晰

每个世界卡片都包含以下信息:图标,名称以及简单描述。

渐进式增强
  • 基础功能:点击卡片选择世界

  • 增强功能:悬停动画、选中高亮、平滑过渡

视觉规范

元素 规范 示例
主标题 42px, 600, #1a202c "选择你的世界"
副标题 18px, 400, #4a5568 "每个世界都有独特的规则与命运"
卡片圆角 28px 柔和舒适
卡片内边距 32px 24px 28px 充足留白
图标大小 64px 视觉焦点
名称字号 26px, 600 醒目
描述字号 15px 清晰可读
元数据字号 13px 辅助信息

不同世界观为整个故事的发展框定范围,这个界面的整体布局如下图所示:

角色创建页面

在穿书叙事游戏中,角色创建界面是玩家正式踏入故事世界的起点。这个页面需要承载:角色信息录入、AI剧情生成、人物关系展示三大核心功能。

页面功能需求
  1. 基础信息采集:玩家输入角色名称、4种身份选择(主角/配角/反派/路人)影响叙事视角、同时支持角色属性自定义

  2. AI内容生成:入场剧情(500+字)、角色背景故事

  3. 人物关系展示:盟友、宿敌、隐藏羁绊三层关系网

  4. 角色交互:查看角色详情

  5. 世界跳转:确认后进入主聊天界面

页面采用白色简约风格,与世界观选择页面保持一致的视觉语言:

.character-create {
  min-height: 100vh;
  padding: 60px 24px;
  background: #f8f9fc;  /* 柔和背景 */
}

.container {
  max-width: 1000px;
  margin: 0 auto;
  background: #ffffff;
  border-radius: 36px;   /* 大圆角现代感 */
  padding: 40px 48px;
  box-shadow: 0 20px 35px -12px rgba(0,0,0,0.05);
}
核心功能实现

 身份选择系统

四种身份对应不同的叙事视角,下图展示了设计的卡片:

交互设计:点击卡片选中,高亮显示当前选中的身份,并重置已生成的内容。

为了让用户创建的角色更加独特,我还开发了角色自定义功能,实现初始属性等自定义选项的表单,提供丰富选择,包括基础属性(不同天赋点,用户可以自行分配有限的点数),天赋特长以及出身背景,不同的选项可能会有不同属性的加成,用户还可以自行选择角色的性格,以及穿书缘由。

通过这些详细的自定义內容,更有助于ai掌握角色内核特质,按照用户的想法生成可读性高的剧情背景故事。因此我还设置了表单的验证,如果用户有没有填的选项,就无法生成剧情,会弹出提示哪一部分的选项没有选择。但此过程我也遇到了问题

问题:生成按钮一直禁用

现象:用户填写完所有表单后,生成按钮仍然不可点击

原因分析

  • isFormValid 计算属性中未调用 validateForm()

  • 验证错误状态未实时更新

// 移除按钮的 :disabled 绑定,改为点击时验证
<button @click="generateEntryStory" :disabled="generating">

// 在 generateEntryStory 开头增加验证
async generateEntryStory() {
  if (!this.checkFormComplete()) return
  // ... 继续执行
}
AI剧情生成
async generateEntryStory() {
  this.generating = true;
  
  try {
    // 第一步:生成入场剧情
    const storyResponse = await fetch('/api/ai/generate-entry-story', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        worldName: this.worldName,
        identity: this.selectedIdentity,
        characterName: this.characterName
      })
    });
    
    // 第二步:生成角色背景
    const profileResponse = await fetch('/api/ai/generate-character-profile', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        ...requestData,
        entryStory: this.entryStory
      })
    });
    
  } catch (error) {
    // 降级使用本地模板
    this.fallbackGenerateEntryStory();
  }
}

由于前后端分离开发,没办法调用api通过ai生成角色剧情背景故事,因此我使用本地模板生成内容。穿书记录,角色背景,人物关系三个模块,其中穿书记录和角色背景后续将由由ai辅助生成,人物关系中的角色从我预设的角色库中选择。

人物关系系统

这个界面封装了前面实现的CharacterCard组件,会在人物关系网显示与用户创建角色有特殊关系的角色,支持:

  • 显示角色名称、状态、身份标签

  • 显示关系值和关系描述(盟友/敌对/隐藏)

  • 点击查看详情

这是整个页面最复杂的部分,需要保证:

  • 三个关系组的角色互不重复

  • 用户创建的角色人物关系网中的角色符合当前世界观

  • 为保证体验感,暗线隐藏身份角色将保密

以下为玩家创建角色时的核心算法:

buildRelationships(Character) {
  // 构建排除名单
  const excludeNames = new Set([
    this.characterName          // 玩家自己 
  ]);
  
  // 过滤可用角色池
  let availableTemplates = this.currentWorldTemplates.filter(
    t => !excludeNames.has(t.name)
  );
  
  // 角色不足时,复制同世界观角色补充
  if (availableTemplates.length < 7) {
    availableTemplates = this.supplementCharacters(availableTemplates, excludeNames);
  }
  
  // 去重并打乱
  const uniqueTemplates = this.deduplicateByName(availableTemplates);
  const shuffled = this.shuffleArray(uniqueTemplates);
  
  // 分配角色(3盟友 + 2宿敌 + 2隐藏)
  const allies = shuffled.slice(0, 3);
  const rivals = shuffled.slice(3, 5);
  const hidden = shuffled.slice(5, 7);
  
  // 验证无重复
  if (this.hasDuplicateAcrossGroups(allies, rivals, hidden)) {
    return this.buildRelationships(Character); // 递归重试
  }
  
  return { allies, rivals, hidden };
}

在这部分实现过程中我遇到了很多问题:

问题1:跨世界观角色混入

现象:古代宫廷世界观中出现了末世生存的"艾薇"角色

原因:fallback机制从混合了所有世界观的allTemplates补充角色

解决:移除跨世界观补充逻辑,改为复制同世界观角色

initCurrentWorldTemplates() {
  // 只从当前世界观获取角色
  if (characterTemplates[this.worldKey]) {
    this.currentWorldTemplates = [...characterTemplates[this.worldKey]];
  } else {
    // 降级到默认世界观,绝不混合
    this.currentWorldTemplates = [...characterTemplates['古代宫廷']];
  }
  
  // 补充角色时复制同世界观角色
  while(this.currentWorldTemplates.length < 10) {
    const templateToCopy = this.currentWorldTemplates[
      Math.floor(Math.random() * this.currentWorldTemplates.length)
    ];
    this.currentWorldTemplates.push({
      ...templateToCopy,
      id: Date.now() + this.currentWorldTemplates.length,
      name: `${templateToCopy.name}·影`
    });
  }
}

问题2:关系组角色重复

现象:同一角色同时出现在盟友和宿敌中

原因:随机分配时没有跨组去重

解决:

  • 统一分配后再验证

  • 发现重复则递归重试

  • 缓存成功的关系结果

问题3:暗线关系需要保密

需求:隐藏角色关系不应直接显示,需用户主动揭示

解决方案:

  1. 添加 showHiddenRelations 状态控制显示

  2. 默认显示神秘迷雾区域

  3. 揭示前弹出确认提示

  4. 揭示后显示角色卡片

经过这周的调试,我已经基本实现并完善了这两大界面,在接下来的一周我将实现主对话界面,整个系统最核心的界面。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐