这个作业属于哪个课程 | 福州大学-202302软件工程实践 |
---|---|
这个作业要求在哪里 | 团队作业 概要设计和数据库设计 |
这个作业的目标 | 完善用况图和类图、设计系统和数据库、制定计划和分工 |
其他参考文献 | 《构建之法》 |
一、开发计划和人员分工
1.1 开发计划
日期 | 计划 | 里程碑 |
---|---|---|
3.11-3.19 | 组队完毕,确定游戏主题和基本玩法 | |
3.27-4.2 | 完成游戏的基本原型设计和需求分析,选择合适的Unity插件和工具库 | |
4.17-4.23 | 完成概要设计和数据库设计 | |
4.24-4.30 | 进行初步的UI设计和玩家交互界面设计 | 遗忘之海v1.0 |
5.1-5.7 | 开发卡牌系统和游戏逻辑 | 遗忘之海v1.1 |
5.8-5.14 | 完成游戏内核心场景的设计和布局 | 遗忘之海v1.2 |
5.15-5.21 | 实现卡牌的动画效果和声音效果,优化用户体验 | 遗忘之海v1.3 |
5.22-5.28 | 对游戏进行内部测试,识别并修复BUG | 遗忘之海v1.4 |
5.29-6.4 | 进行游戏数值平衡测试,确保游戏的策略性 | 遗忘之海v1.5 |
6.5-6.11 | 对游戏进行全面测试,修正发现的BUG,并对游戏进行最后的优化 | 遗忘之海v1.6 |
1.2 人员分工
学号 | 职位 | 工作内容 |
---|---|---|
222100409 | 策划组 | 项目进度安排、进度监督 |
222100408 | 美术组 | 角色设计、UI界面和动画效果的制作 |
222100412 | 策划组 | 游戏的核心机制、规则设计 |
222100435 | 程序组 | 实现游戏设计中的技术需求,编写核心游戏代码 |
222100436 | 程序组 | 开发游戏中的交互逻辑,进行代码维护和技术支持 |
222100115 | 美术组 | 创建游戏内的2D/3D图形资源,如贴图、模型和动作动画 |
222100236 | 测试组 | 编写测试用例,记录并报告游戏中的错误和问题 |
222100425 | 程序组 | 优化游戏性能,处理软件的bug修复 |
二、团队绩效
-
工作流程
-
团队协作
-
组员分工及贡献度比例
学号 | 工作内容 | 贡献度 |
---|---|---|
222100409 | 系统设计说明书修正、PPT的制作、答辩 | 19% |
222100408 | 系统设计说明书、系统安全和权限设计 | 13% |
222100412 | 数据库设计说明书 | 7% |
222100435 | ER图、表结构设计 | 14% |
222100436 | 类图、接口设计 | 14% |
222100115 | 数据库设计说明书修正 | 7% |
222100236 | 博客撰写、PPT修正、系统设计说明书 | 19% |
222100425 | 数据库设计说明书 | 7% |
- 考核方式
考核项目 | 项目细则 | 说明 |
---|---|---|
配合 | 队内沟通情况 | 是否与队友交流确认工作细则 |
求同存异 | 出现分歧时,是否求同存异,和谐沟通 | |
任务完成度 | 完成分配工作 | 基本实现需求 |
完成质量 | 即使能力有限,也要认真完成 | |
任务完成过程性评价 | 阶段性成果 | 阶段性检查进度 |
态度 | 参与线下讨论 | 出勤率与发言贡献 |
参加线上会议 | ||
按时交付 | 在deadline前交,得实际得分 * 100%; 在deadline 后一天内提交视为补交,得实际得分 * 50%; 在deadline 之后一天未补交视为缺交,分数为0分。 | |
额外绩效 | 超额完成 | 在完成分配任务的基础上实现额外的契合项目的工作或提出想法 |
帮助其他成员 | 帮助其他成员完成分配工作 |
三、系统和数据库设计
3.1 概念结构设计
3.1.1 角色模块
角色(角色ID,初始血量,角色名称,初始牌组ID,卡池ID)
每个角色拥有自己的初始牌组和卡池
3.1.2 卡组构建模块
-
卡牌(ID,名称,卡面,自遗忘系数)
-
词条(词条ID,词条描述)
-
卡牌-词条(卡牌ID,词条ID,词条Value1,词条Value2,词条Value3)
卡牌和词条是一对多的关系,通过关联表 卡牌-词条 关联在一起。
词条value1/2/3,是词条的具体数值,可以为空,以此填入(即value1不为空才能填value2)
根据分析,词条可变具体数值不超过3个。
例:卡牌: id:1,名称:攻击卡牌
词条: id:1, 名称:造成x点伤害。
卡牌-词条: 卡牌id:1 词条id:1 Value1:5 Value2:Null Value3:Null。
这样的设计,可以通过直接修改数据库的方式,修改卡牌
-
卡池-卡牌(卡池ID,卡牌ID)
卡池与卡牌为多对多关系
一个卡池对应多张卡牌,一张卡牌对应多个卡池。
一局游戏中,获得的卡都是卡池中的。
-
初始牌组-卡牌(初始牌组ID,卡牌ID)
同卡池-卡牌
3.1.3 敌人模块
敌人同角色模块、卡组构建模块差距不大,但其具体实现代码有区别。
敌人拥有固定的行为树,这意味着意图组的改变(即增加/删除)是不被允许的。
3.1.4 奖励模块
奖励组和奖励是多对多关系
奖励组拥有0个或多个奖励,奖励对应多个奖励组。
奖励描述 属性约束 : 卡牌、遗物等
3.1.5 遗物模块
3.1.6 地图模块
地图节点类型 类型名称属性约束如下:普通战斗、事件、宝箱、Boss、精英敌人等。
根据具体类型去读取对应的表
如下面的战斗节点模块和事件模块
3.1.7 战斗节点模块
3.1.8 事件模块
3.2 逻辑结构设计
3.1中出现的ER图已为转换过后的关系模型。
事件-奖励组(事件ID,奖励组ID)
战斗节点-敌人(战斗节点ID,敌人ID)
卡池-卡牌(卡池ID,卡牌ID)
初始卡组-卡牌(初始卡组ID,卡牌ID)
卡牌-词条(卡牌ID,卡牌词条ID)
遗物-词条(遗物ID,遗物词条ID)
意图-词条(意图ID,意图词条ID)
3.3 物理结构设计
- 角色表
Role表
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
ID | int | 是 | 是 | 自增 | |
Health | int | 是 | 角色血量 | ||
Name | 字符串 | 20 | 是 | 角色名称 | |
InitCardDeckID | int | 是 | 初始卡组ID | ||
CardPoolID | int | 是 | 卡池ID |
- 卡牌表
Card表
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
ID | int | 是 | 是 | 自增 | |
Name | 字符串 | 20 | 是 | 角色名称 | |
CardFace | URL | 是 | 卡面在资源中的地址 | ||
ForgettenNum | int | 是 | 自遗忘系数(游戏特殊机制) |
- 卡牌词条表
CardWord
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
ID | int | 是 | 是 | 自增 | |
Description | 字符串 | 20 | 是 | 词条描述 |
- 卡牌-词条关联表
Card_CardWord
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
Card_ID | int | 联合主键 | 是 | 卡牌ID | |
CardWord_ID | int | 联合主键 | 是 | 词条ID | |
Value1 | int | 词条数值1 | |||
Value2 | int | 词条数值2 | |||
Value3 | int | 词条数值3 |
- 卡池-卡牌关联表
(初始牌组-卡牌关联表类似)
CardPool_Card
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
CardPool_ID | int | 联合主键 | 是 | 卡池ID | |
Card_ID | int | 联合主键 | 是 | 卡牌ID |
- 奖励表
Reword
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
ID | int | 是 | 是 | 自增 | |
type | 字符串 | 是 | 奖励类型 |
- 奖励组表
RewordCombine
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
ID | int | 联合主键 | 是 | 奖励组ID | |
Reword_Id | int | 联合主键 | 是 | 奖励ID |
- 地图节点类型
MapNode
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
ID | int | 是 | 是 | 自增 | |
type | 字符串 | 是 | 节点类型描述 |
- 事件
Event
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
ID | int | 是 | 是 | 自增 | |
Description | 字符串 | 是 | 事件描述 |
- 事件-奖励
Event_Reword
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
Event_ID | int | 联合主键 | 是 | 事件ID | |
RewordCombine_ID | int | 联合主键 | 是 | 奖励组ID |
- 战斗节点
MapNodeBattle
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
ID | int | 是 | 是 | 自增 |
- 战斗节点-奖励组联合表
MapNodeBattle_RewordCombine
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
MapNodeBattle_ID | int | 联合主键 | 是 | 战斗节点ID | |
RewordCombine_ID | int | 联合主键 | 是 | 奖励组ID |
- 战斗节点-敌人联合表
MapNodeBattle_Enemy
字段名 | 数据类型 | 长度 | 主键 | 非空 | 描述 |
---|---|---|---|---|---|
MapNodeBattle_ID | int | 联合主键 | 是 | 战斗节点ID | |
Enemy_ID | int | 联合主键 | 是 | 敌人ID |
3.4 体系结构设计图
3.5 功能模块层次图
四、类图的完善
- 词条
- 词条是卡牌的组成部分,表示卡牌的能力。使用组合模式的好处是:一个卡牌可以有多种能力,而且这种能力可以被任意卡牌所持有,实现了功能的任意组合。
- 接口INeedTarget:有些词条的的生效,需要指定一个作用目标(通常是一个敌人)。实现这个接口的词条,可以为其传入一个目标,例如单体攻击词条SingleAttack,它需要一个敌人目标以对其造成伤害。
- 接口IExecuteDirectly:实现这一接口的词条,表明其不需要指定一个特定目标。通常这种词条可以访问单例类BattleMgr来获取自己需要的信息来实现自己的功能。
- 卡牌
- 正如词条部分所述,卡牌是由若干词条组成的。
- AddWords函数:虚函数,Card子类可以重写该函数,为自身添加词条。
- 卡牌享元
由于每一类卡牌都有自己的名称、描述、品质、图片等等数据,故需要一个类表示这些共享的数据,以节约内存。CardData类由此而设计。 - Buff
- BuffComp:封装与Buff相关的操作,可以增删Buff。
- ITurnCount:实现该接口的Buff表示其拥有持续回合数。
- IHasPoint:实现该接口的Buff表示其拥有点数。
- 敌人和意图
敌人Enemy可以在某一时刻持有一个或多个意图Intention。和词条类似,Intention也派生出若干的子类,表示不同意图。
- 战斗模块
一场战斗包含敌人、抽牌堆、手牌对、弃牌堆等个个要素。将它们组合于战斗模块BattleMgr中是一个好的做法。
战斗模块还包含了回合切换、抽牌弃牌等各个动作,以推动战斗进程和记录战斗状态。 - 地图
地图由各个不同类型的节点组成,每个节点拥有OnSelected函数以监听被点击时要处理的逻辑。
五、通信图
针对上一个阶段中老师助教提出的要求——给出对象之间的交互顺序,我们做出了一些反馈与改进。我们决定使用通信图作为来描述对象之间交互的方式,因为通信图清晰地展现了程序中对象之间的交互、调用关系,还便于用户阅读和理解。为了避免通信图过于复杂庞大,我们针对程序中不同逻辑和步骤单独绘制了通信图。
5.1 战斗通信图
-
战斗初始化
首先,将创建敌人;然后初始化抽牌堆(Drawer),具体来说就是先从背包中获取所有卡牌,经过洗牌后放入到抽牌堆中,以作为这次战斗的初始的抽牌堆。
-
进入玩家回合
从抽牌堆中抽若干张卡(一般是从牌堆顶部开始抽),然后将其放入手牌堆(HandCardBox)中。 -
卡牌生效
玩家点击UI界面上的某张手牌,会调用UseCard方法,传入指定的敌人对象;然后再调用调用SingleAttackWord的UseCard方法,将敌人对象进一步传递;然后,更具指定敌人和玩家自身的Buff计算最终伤害,最后调用Enemy的受伤函数,将最终计算的伤害传入。
-
退出玩家回合
一般情况下会将手牌中的所有卡牌放入到弃牌堆(UsedCardBox)中。
5.2 地图通信图
- 选择一个节点
地图部分最重要的就是玩家对节点的选择,这体现了玩家的策略。玩家选择UI上的一个地图节点元素(UiNode),然后会调用这个UI元素对于的节点的OnSelected方法。例如普通敌人节点NormalEnemyNode,其OnSelected方法的实现就是调用BattleMgr的StartBattle方法,由此整个游戏进入战斗状态,此时可以参见之前的战斗通信图部分。
六、改进分析
6.1 问题一
- 作为一款游戏,需要重点体现交互的部分,你们的游戏是如何体现的
- 增强反馈机制:确保每个玩家动作都有清晰且富有吸引力的视觉和音效反馈。例如,当玩家使用一张卡牌时,可以展示动画效果和特殊音效来强化行动感。
- 优化用户界面:界面应直观易用,确保玩家能轻松理解可用的操作和当前的游戏状态。采用自定义UI元素,提供帮助提示和教程,使新手玩家能够快速上手。
- 多样化的交互方式:除了基本的点击和拖动操作,引入更多交互形式,如手势识别或设备震动,以提供更丰富的游戏体验。
6.2 问题二
- 策略类游戏的平衡与战略深度是至关重要的,游戏如何保证这一点
- 数据驱动的平衡调整:通过收集游戏测试阶段的详尽数据,分析哪些卡牌被过度使用或者几乎不被使用。使用这些数据来调整卡牌的能力,确保没有单一的策略或卡牌主导整个游戏。
- 引入多样化的战略元素:设计多样化的卡牌和技能组合,鼓励玩家探索不同的战略路线。这不仅可以增加游戏的深度,也可以提高每局游戏的独特性和重玩价值。
- 持续的玩家反馈循环:建立持续的反馈机制,让玩家能够直接影响游戏的平衡调整。通过社区论坛、调查问卷和定期更新,让玩家感觉他们的意见被重视,并真正参与到游戏的发展中。
- 设置不同难度级别:为不同水平的玩家提供多个难度级别,确保新手和经验丰富的玩家都能找到合适的挑战,同时保持游戏的竞争性和趣味性。
6.3 问题三
- 单机游戏的生命力很大程度上依赖于内容更新和扩展包,游戏靠什么来保持新鲜感
- 预计划的内容发布:开发团队应该创建并公布一条详细的内容更新路线图,包括新卡牌、新挑战和新剧情的发布计划。这不仅能够保持现有玩家的兴趣,也能吸引新玩家。
- 扩展包和DLC:设计扩展包,包括新的游戏模式、扩展故事线和独特的游戏机制。通过这些扩展包提供丰富的内容,可以提高游戏重玩性。
- 模组支持:尽管是单机游戏,但提供官方支持的模组工具可以极大地扩展游戏的生命周期。允许玩家创建和分享自己的卡牌、敌人或甚至完整的故事线,可以激发社区的创造力和参与。
6.4 问题四
- 游戏中的对象的交互顺序是怎样的?关于这一问题的改进我们决定使用通信图进行分析,详见通信图部分。
七、系统安全和权限设计
7.1 系统安全设计
为防止用户对游戏代码随意查看修改,导致代码泄露,或游戏无法运行,使用Unity打包,保证系统安全。Unity打包有如下优势:
代码混淆:为了防止反编译,Unity支持Mono运行时的代码混淆。这可以提高逆向工程的难度,使得攻击者难以理解和修改游戏的执行逻辑。
资源加密:Unity允许开发者使用AES(高级加密标准)对AssetBundle资源进行加密。这是一种广泛使用的对称加密方法,可以有效防止未授权的用户访问游戏资源。
7.2 权限设计
对于数据库权限方面。作为单机游戏,我们采用的是嵌入式数据库。对于嵌入式数据库,明确区分玩家可以访问和修改的数据库表与那些需要保护的表是必要的。对于开放的权限,确保提供足够的API支持,使玩家能够方便地开发第三方模组,同时用严格的接口管理来控制数据的一致性和安全性。
对于关键数据表(如敌人AI意图组-意图表),可以采用角色基础的访问控制(RBAC),确保只有验证过的开发者和维护人员才能访问这些表。此外,还可以引入审计日志,以监控对这些敏感表的所有访问和修改操作,从而增加透明度和安全性。