资讯动态

【StoryEcho】用户系统与角色创建

发布时间:2026/9/12 1:13:49 来源:尧图企业网站定制
目录一、登录与注册功能实现说明Tips二、角色创建1. 功能介绍2. 角色创建3. 代码3.1 前端3.2 后端4. 总结三、后续计划一、登录与注册功能实现说明以下是实现的用户系统流程未登录状态下的访问拦截用户在首页点击故事卡片等其他按钮时系统不会立即进入游戏而是先检查本地是否存储了有效的用户令牌。如果没有则自动跳转到登陆页面。这样可以保证所有游戏数据都能关联到具体用户便于后续的存档管理和多周目继承等功能登录页面与注册入口登录页面包含“邮箱”和“密码”两个输入框以及“登录”按钮。没有账号的用户可以点击下方的“立即注册”链接切换到注册页面。注册流程注册页面需要用户填写昵称、邮箱和密码然后点击“发送验证码”按钮。系统会调用后端接口通过我的邮箱服务我使用了 SMTP 协议的 QQ 邮箱此处可以换成其他邮箱向该地址发送一封包含6位数字确认码的邮件。用户收到后填写验证码点击“注册”。后端验证验证码正确后将用户信息存入数据库并返回注册成功。注册成功后的登录与跳转注册成功后页面会自动跳回登录页系统自动输入刚才注册的邮箱和密码用户直接点击“登录”按钮即可登录。登录成功后后端返回一个 JWT token前端将其保存在 localStorage 中。之后所有的游戏 API 请求都会在请求头中带上 Authorization: Bearer token后端通过中间件验证 token 的有效性。Tips安装邮件发送库pip install aiosmtplib邮件发送配置该功能需要配置 SMTP 服务对于 QQ 邮箱正式的游戏使用的发送邮箱不建议使用 QQ 邮箱这里我只是用来测试需要自己打开 SMTP 服务开启之后会有一个授权码该授权码会用于配置 SMTP 服务完整的配置内容如下$env:SMTP_SERVER smtp.xx $env:SMTP_PORT 465 $env:SMTP_SENDER_EMAIL 你的邮箱 $env:SMTP_SENDER_PASSWORD 你的邮箱SMTP授权码 $env:SMTP_SENDER_NAME StoryEcho关于 SMTP 服务授权码的获取二、角色创建1. 功能介绍当进入 StoryEcho 平台并选择一个故事进行冒险时系统会根据历史记录呈现两种选择。如果已经在该故事中创建过角色系统会展示一个角色选择面板列出之前创建的所有角色可以直接选择其中一个开始冒险。如果想要体验不同的角色配置或游戏风格或者是首次进入该故事也可以选择创建新角色来进行角色创建。2. 角色创建确定基本信息需要为冒险者设定最基础的身份信息。首先需要在文本框中输入一个不超过20个字符的角色名称这将是在整个故事中的代称会在所有对话和叙述中被使用。其次需要选择角色性别选择的性别会影响游戏中某些 NPC 的对话内容和互动方式。最后是性格倾向滑块这个滑块从善良到叛逆范围内滑动用 0 到 100 的数值表达角色性格。这个性格值会影响游戏中的对话选项、NPC 的反应以及某些故事分支的开启条件。选择天赋第二步是一个天赋卡片的展示这些天赋代表了角色天生具备的特殊能力和技能优势。每张天赋卡片都展示了天赋的名称、稀有度等级用不同颜色和标签区分、详细描述以及它会给予的属性加成。例如战斗天才这个天赋会提升力量值 5 和敏捷值 3而博学者则会增加智力 8 和魔法值 20。需要注意的是并非所有天赋都是可选的——有些天赋会显示一个锁标这意味着它们还未被解锁需要在多周目游戏中通过特定的条件才能够在这里选择。选择出身背景第三步是多个出身背景中进行选择。与天赋不同的是出身背景不仅会提升属性还会为初始背包中装备初始物品。每张背景卡片会展示背景的名称、详细的背景描述例如你来自一个古老的骑士家族从小就接受武术训练列出你会获得的初始物品清单如长剑面包×3等以及这个背景会给予的属性加成。特别值得注意的是出身背景的属性加成是与天赋加成相叠加的这意味着如果你的天赋提供 5 力量你的出身背景也提供 3 力量那么你最终会获得 8 的力量加成。同时系统会智能地确保你的最终属性不会超过故事设定的上限值如果某个属性会超过上限系统会自动进行限制防止属性数值失衡。3. 代码3.1 前端多步骤表单管理app.js 文件// 角色创建表单的响应式状态 const step ref(1); // 当前步骤 (1-3) const characterName ref(); // 角色名称 const gender ref(male); // 性别选择 const personality ref(50); // 性格倾向 (0-100) const selectedTalent ref(null); // 选中的天赋对象 const selectedBackground ref(null); // 选中的出身对象 // 实时计算的属性预览天赋 出身加成 基础属性 const previewAttributes computed(() { const config currentStoryConfig.value; const base { ...config.baseAttributes }; // 应用天赋效果 if (selectedTalent.value?.effects) { Object.entries(selectedTalent.value.effects).forEach(([attr, val]) { base[attr] (base[attr] || 0) val; }); } // 应用出身加成 if (selectedBackground.value?.attribute_bonus) { Object.entries(selectedBackground.value.attribute_bonus).forEach(([attr, val]) { base[attr] (base[attr] || 0) val; }); } // 属性值不超过故事设定的最大值 const maxValues config.maxValues; Object.entries(base).forEach(([attr, val]) { base[attr] Math.min(val, maxValues[attr] || 100); }); return base; }); // 验证是否可以进行下一步 const canProceed computed(() { if (step.value 1) return characterName.value.trim().length 0; if (step.value 2) return selectedTalent.value ! null; return false; }); // 验证是否可以创建角色 const canCreate computed(() { return selectedTalent.value ! null selectedBackground.value ! null; });创建角色方法const createCharacter async () { if (!canCreate.value) return; const activeUserId getActiveUserId(); if (!activeUserId) { alert(登录状态已失效); return; } try { // 调用后端 API 创建角色 const createData { user_id: activeUserId, story_id: selectedStory.value.story_id, name: characterName.value, gender: gender.value, personality: personality.value, talent_id: selectedTalent.value.talent_id, background_id: selectedBackground.value.background_id }; const result await apiClient.createCharacter(createData); if (result.success) { // 关闭创建面板 showCharacterCreation.value false; // 立即使用创建的角色进入游戏 await startGameWithCharacter({ id: result.character_id, name: characterName.value }); } } catch (error) { alert(创建角色失败${error.message}); } };3.2 后端模型定义from pydantic import BaseModel class CharacterCreateRequest(BaseModel): 角色创建请求模型 user_id: str # 用户ID story_id: str # 故事ID name: str # 角色名称 gender: str # 性别 (male/female) personality: int # 性格倾向 (0-100) talent_id: str # 天赋ID background_id: str # 出身ID创建角色的接口from fastapi import APIRouter, HTTPException import json import sqlite3 router.post(/create, response_modelDict) async def create_character(req: CharacterCreateRequest): 创建新角色 # 1. 参数验证 if not req.name or len(req.name) 50: raise HTTPException(status_code400, detail角色名称无效) if req.personality 0 or req.personality 100: raise HTTPException(status_code400, detail性格值必须在0-100之间) # 2. 查询天赋信息 cursor.execute( SELECT effects FROM talents WHERE talent_id ? AND story_id ?, (req.talent_id, req.story_id) ) talent_row cursor.fetchone() if not talent_row: raise HTTPException(status_code400, detail所选天赋不存在或不属于当前故事) talent_effects json.loads(talent_row[0]) if talent_row[0] else {} # 3. 查询出身信息 cursor.execute( SELECT attribute_bonus FROM backgrounds WHERE background_id ? AND story_id ?, (req.background_id, req.story_id) ) bg_row cursor.fetchone() if not bg_row: raise HTTPException(status_code400, detail所选出身不存在或不属于当前故事) bg_bonus json.loads(bg_row[0]) if bg_row[0] else {} # 4. 计算最终的基础属性 story_config STORY_CONFIGS[req.story_id] base_attrs dict(story_config[baseAttributes]) # 深拷贝基础属性 # 合并天赋加成 for attr, val in talent_effects.items(): base_attrs[attr] base_attrs.get(attr, 0) val # 合并出身加成 for attr, val in bg_bonus.items(): base_attrs[attr] base_attrs.get(attr, 0) val # 属性值不超过上限 max_values story_config[maxValues] for attr in base_attrs: base_attrs[attr] min(base_attrs[attr], max_values.get(attr, 100)) base_attrs[attr] max(base_attrs[attr], 0) # 不低于0 # 5. 生成角色ID并插入数据库 character_id str(uuid.uuid4()) cursor.execute( INSERT INTO characters (character_id, user_id, story_id, talent_id, background_id, name, gender, personality, base_attributes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) , ( character_id, req.user_id, req.story_id, req.talent_id, req.background_id, req.name, req.gender, req.personality, json.dumps(base_attrs, ensure_asciiFalse), # 转为JSON存储 datetime.now().isoformat() )) conn.commit() return { success: True, character_id: character_id, message: f角色 {req.name} 创建成功 }4. 总结从用户点击选择故事开始整个角色创建的流程是这样进行的首先前端向后端请求该故事的所有可用天赋和出身背景列表这些数据会被渲染成卡片展示给用户。用户在第一步输入角色基本信息系统实时更新预览面板虽然这一步还没有选择天赋和出身所以只显示基础属性。用户进入第二步选择天赋每次点击天赋卡片时selectedTalent 变量会被更新预览面板会立即重新计算属性值显示加上天赋加成后的属性。用户进入第三步选择出身背景同样地selectedBackground 的更新会触发属性值的重新计算。整个过程中用户可以随时通过上一步按钮返回之前的步骤修改选择预览面板会实时反映每次修改的结果。当用户完成所有三步的选择并点击开始冒险后前端会收集所有信息并发送到后端。后端接收到这个请求后会进行完整的验证参数格式、天赋/出身有效性等然后查询天赋和出身的详细信息计算最终的属性值并进行限制。计算完成后所有信息被写入数据库。如果创建成功后端返回新的角色ID。前端接收到这个ID后关闭角色创建面板立即调用故事开始端点使用这个新角色进入游戏。三、后续计划基于项目当前已实现的功能下一步将重点完善故事中的角色成长与成就系统。具体来说需要设计一套由对话和行动驱动的动态属性机制当玩家在故事中做出不同的选择例如勇敢战斗或冷静分析时系统会利用 LangGraph 工作流解析行动意图并相应地调整力量、智力、生命值等属性。这些属性变化会实时反映在界面上并记录完整的事件日志。同时需要实现多维度的成就系统包括简单属性阈值成就如生命值达到 120 解锁“钢铁战士”、复合条件成就如多项属性同时达标解锁“全能者”以及基于事件累积的成就如完成三次战斗解锁“战斗老手”。成就解锁时前端会弹出通知所有进度将持久化存储为后续的跨周目继承打好基础。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价