Unity《ATD》塔防RPG类3D游戏架构设计(一)
目录
《ATD》 游戏简介
《ATD》 整体结构
《ATD》 游戏机制
Buff机制
Skill机制
结语
《ATD》 游戏简介
游戏类型:塔防+RPG的3D游戏
游戏要素:3D 塔防 英雄 建筑树 搭配
主体玩法:游戏里将会有一波波怪物进攻基地。玩家可以建造塔来防御敌人,同时也可以控制单独的个体英雄角色来攻击敌人。
游戏模式:
第三人称视角的RPG模式
上帝视角的建造模式
控制方式:在游戏中使用Tab按键,切换这两种操作模式:
RPG模式下:WASD控制移动,Space跳跃,鼠标左键普通攻击。
建造模式下:鼠标左键建造,E销毁已建造的建筑。
数字键1,2,3,4,5,6控制物品栏,对应英雄技能或者建筑安放。
胜利条件:消灭所有敌人 或者 坚持到时间结束
失败条件:基地生命值为0 或者 英雄死亡
《ATD》 整体结构
一般来说,整个Unity游戏项目整体结构,我比较偏向分为如下5部分:
场景对象: 不会产生互动的可视物体对象,例如地型/建筑/灯光。
游戏对象: 参与互动的游戏对象,例如英雄/怪物/塔。
游戏逻辑: 负责控制游戏的逻辑,其逻辑对象一般是单例的。
非游戏性对象: 负责增强游戏效果,但不是直接的游戏逻辑,例如UI/HUD/特效/声音。
工具: 负责辅助编码,例如日志工具,调试工具。
在《ATD》游戏项目里,我是这样设置游戏对象目录的:
注:“个体”在《ATD》里的术语表示游戏对象单位。
《ATD》 游戏机制
通过分析《ATD》策划案,确立了两种基本游戏机制:
Buff机制
Skill机制(技能机制)
Buff机制
和策划商量后,策划制作了下面一张含所有Buff属性的Excel表:
由于策划还没想好Buff名字,直接套用装备或者技能名字来命名Buff。
首先,使用了一个数据类型BuffData,用于完全映射Buff在表格的所有属性:
public class BuffData
{
public int ID;
public string Name;
public int HpChange; //血量变化
public double HpChange_p; //血量百分比变化
public int AttackChange; //攻击力变化
public double AttackChange_p; //攻击力百分比变化
public double AttSpeedChange_p; //攻击速度百分比变化
public double SpeedChange_p; //速度百分比变化
public int HpReturnChange; //血量恢复数值
public double HpReturnChange_p; //血量百分比恢复数值
public int AddReviveCount; //增加复活次数
public bool isDecelerate; //减速
public bool isVertigo; //眩晕
public bool isParalysis; //麻痹
public bool isSleep; //睡眠
public bool isBound; //束缚
public bool isBurn; //点燃
public bool isCharm; //魅惑
public bool isIncreaseAttSpeed; //攻速提高
public bool isPoisoning; //中毒
public bool isImmuneControl; //免疫控制
public bool isRevenge; //复仇
public bool isTaunt; //嘲讽
public bool isIncreaseHpReturn; //回血速度提高
public bool isIncreaseAttack; //攻击力提高
}
然后我们就可以用一个List
//全局单例类
public class BuffDataBase : MonoBehaviour
{
//读取excel插件生成的json文件
public TextAsset BuffDataJson;
//存储BuffData的列表
private List<BuffData> buffDatas;
//全局单例实现
//...
//根据ID获取相应的BuffData对象
public BuffData GetBuffData(int ID){
//...
}
}
为了表示游戏对象动态得到/失去一个Buff而从BuffDataBase找到对应并拷贝一份BuffData对象/释放掉一份BuffData对象显然是不明智的。(BuffData所占空间大,开销大)
正确的做法应该是使用索引/引用的方式,例如某个游戏对象持有3号索引,则表示它当前受一个3号Buff影响。
为了引入Buff的时间有效性,则进一步封装索引,于是编写了下面一个Buff类:
public class Buff
{
public int ID; //BuffData的ID(索引)
public double time; //持续时间
public int repeatCount; //重复次数
public bool isTrigger; //是否触发类型
}
因为每个Buff的时间有效性都有所不同:有些Buff是一次性触发Buff;也有一些是持续性Buff,持续N秒;还有一些是被动buff,永久生效。
所以我这里就总结了个规则,Buff主要分为两种类型:
持续型(Non-Trigger):开始对属性造成生效影响一次,有效时间结束时造成失效影响一次。例如一段时间内增加攻速Buff
非持续型(Trigger):有效时间内,每一帧对属性造成生效影响一次。例如一次性伤害Buff,光环Buff。
然后Buff的有效时间取决于2个属性:
持续时间(time):每帧持续时间减少DeltaTime
触发次数(repeatCount):每帧触发次数减一
当一个Buff对象,持续时间 <= 0 并且 触发次数为0,则应视为失效。特殊地,触发次数为-1时,表示无限时间。
这样Buff/BuffData/BuffDataBase基本构造就这样了:
整个游戏同种类Buff只用存储一份BuffData;但是可以有很多个对象持有索引/引用,指向这个BuffData
游戏对象持有Buff对象,通过BuffDataBase访问BuffData的数据,然后利用这些数据对游戏对象属性造成影响
看到这里,可能会有人想到前面有个问题:对于任意一种Buff,它往往有很多属性是false或者0,使用这种完全映射会不会很影响空间占用或者效率。
首先,空间占用绝对不用担心,因为前面BuffDataBase机制保证同种Buff只有唯一BuffData副本,其所有BuffData总共占用量不过几kb而已。
其次,至于效率,例如说某个Buff对某个游戏对象造成影响,因为是完全映射,所以需要对该游戏对象每个属性都要进行更新,其实这也并不是太糟糕。而且只要游戏对象有比较好的Buff计算方式,可以让一个Buff对象的整个有效周期只对对象造成两次影响计算(生效影响,失效影响),避免每帧出现影响多余的计算,这样就很不错了。
Skill机制
可以说技能是我比较头疼的部分。
看到那千奇百怪的Skill需求时,然后才总结出大概这几个分类:
主动Buff技能 = 主动释放,生成一个Buff
被动Buff技能 = 初始化时,生成一个Buff
召唤技能 = 生成一个游戏对象
指向性技能 = 主动释放,对锁定的目标生成一个Buff
最后我决定使用继承接口的方式来实现Skill:
技能接口类:
public interface ISkill
{
// 技能初始化接口
void InitSkill(Individual user);
// 使用技能接口
void ReleaseSkill(Individual user);
/// 技能每帧更新
void UpdateSkill(Individual user);
/// 技能是否冷却
bool IsColdTimeEnd();
// 技能冷却百分比
float GetColdTimePercent(www.baikayule.cn);
}
需要注意的一点是,技能并不是主动释放时调用一个自定义的技能函数即可完事:
例如持续性的范围技能,需要每帧调用散发Buff的函数。
所以一个ISkill对象 该有这3种重要的接口方法:初始化/主动释放/每帧更新
下面是其中一个派生类的具体实现:
由于进度未完,目前只有两个派生类:Buff技能类和召唤技能类。
Buff技能类暂时包含了ActiveBuff技能类和PassiveBuff技能类的功能。
// 示例:Buff技能类
public class BuffSkill : ISkill
{
public int buffID; //目的Buff
public bool isAura = true; //光环
public bool releasable = true; //是否主动释放
public float range = 0.01f; //范围
private float coldTime = 5.0f; //冷却时间
private float timer =www.baikayul.com 5.0f; //冷却计时
public BuffSkill(int buffID,bool releasable = true,bool isAura = true, float range = 0.01f)
{
this.buffID = buffID;
this.isAura = isAura;
this.range = range;
this.releasable = releasable;
}
public void InitSkill(Individual master)
{
if (!releasable && !isAura)
{
var individual =www.huashengyLe.com master.GetComponent<Individual>();
master.GetComponent<MessageSystem>(www.qifeimn.com).SendMessage(2, individual.ID,buffID);
}
}
public void ReleaseSkill(Individual master)
{
if (releasable && IsColdTimeEnd())
{
timer = 0.0f;
Factory.TraversalIndividualsInCircle(
(individual) => { master.GetComponent<MessageSystem>().SendMessage(2, individual.ID, buffID); }
, master.transform.position, range);
}
}
public void UpdateSkill(Individual master)
{
//增加计时
timer =Mathf.Min(www.feishenbo.cn timer+Time.deltaTime, coldTime+0.1f);
if (!releasable && isAura)
{
Factory.TraversalIndividualsInCircle(
(individual) => { master.GetComponent<MessageSystem>().SendMessage(2, individual.ID, buffID); }
, master.transform.position, range);
}
}
public float GetColdTimePercent(www.chuangyyuLe.com)
{
if (!releasable) return 1.0f;
return timer / coldTime;
}
public bool IsColdTimeEnd(www.xingyunylpt.com)
{
return timer > coldTime;
}
}
派生类的构造函数很重要,这样即使硬编码了4个技能派生类,通过不同的数据参数传入,也能产生更多不同的技能对象。
最后还应该再写一个SkillDataBase全局单例类,它负责读取策划写的技能配置文件,来初始化出来一些Skill对象,以供游戏对象使用。
不过项目代码还没写完,因此目前是直接在SkillDataBase的初始化函数直接硬编码3个技能。
//TODO
//目前硬编码给玩家赋予3个技能
HeroSkills.Add(new BuffSkill(6,www.qunfLtie.com true, true, 5.0f)); //主动技能:嘲讽Buff
HeroSkills.Add(new BuffSkill(0, false, false)); //被动技能:回血buff
HeroSkills.Add(new BuffSkill(14, true, false)); //主动技能:攻速戒指buff
以后的话,SkillDataBase的初始化函数应该是读取某种配置文件,然后生成若干个对应的技能对象分配给游戏对象使用:
结语
《ATD》只是社团部门内提出的一个游戏项目,而我负责这个项目的程序架构设计,然而中途开发因为不少事,我们不得不放弃了这个项目。因此才想写点东西总结一下开发这个项目时的经验。
之后还会有新博文来更新这个系列,大概涉及《ATD》的游戏对象模型,全局游戏逻辑,UI/HUD/特效/声音管理,工具等,也同时会分享一些trick。
转载于:https://www.cnblogs.com/qwangxiao/p/11191913.html
Unity《ATD》塔防RPG类3D游戏架构设计(一)相关推荐
- 使用Unity创建塔防游戏(Part2)
How to Create a Tower Defense Game in Unity – Part 2 原文地址:https://www.raywenderlich.com/107529/unity ...
- Unity 3D 环境特效||Unity 3D 游戏场景设计实例
Unity 3D 环境特效 一般情况下,要在游戏场景中添加雾特效和水特效较为困难,因为需要开发人员懂得着色器语言且能够熟练地使用它进行编程. Unity 3D 游戏开发引擎为了能够简单地还原真实世界中 ...
- Unity空间与运动(中山大学3D游戏作业3)
Unity空间与运动(中山大学3D游戏作业3) 目录 Unity空间与运动(中山大学3D游戏作业3) 一.程序验证 物体运动的本质 三种方法实现抛物线运动 实现太阳系 二.牧师与恶魔游戏 代码仓库:h ...
- 3D游戏引擎设计 实时计算机图形学的应用方法 第2版 pdf 带索引书签目录
3D游戏引擎设计 实时计算机图形学的应用方法 第2版 目录 第1章 概述 1.1 图形硬件和游戏发展史 1.2 本书版本与软件发展史 1.3 章节导读 第2章 图形系统 2.1 基础知识 2.1. ...
- 3D游戏建模设计需要美术基础吗,0基础可以学吗
经常会碰到会多同学会问游戏建模设计需要美术基础吗? 游戏建模设计需要多好的美术功底? 今天我们就聊聊3D游戏建模设计之美术基础 三维软件是工具,美术基础是内力,工具只是熟能生巧的过程,美术基础决定在建 ...
- 如何实现游戏架构设计
1.什么是架构设计 最近公司招人,我负责面试程序员,人事发给我很多简历,我在阅读他们的简历时,经常会看到说自己会客户端架构设计,但当问到他们什么是架构设计时,基本上是一无所知.出现这种情况原因:一是自 ...
- 3D引擎架构设计高级篇
3D引擎架构设计最核心的技术包括:引擎框架设计,引擎内存管理,大场景加载以及卸载,引擎的渲染,模型骨骼插件:其他的模块还有粒子,AI,行为树,UI等等吧. 市场上对于引擎开发的需求也是比较大的,而且薪 ...
- 3D引擎架构设计篇-姜雪伟-专题视频课程
3D引擎架构设计篇-169人已学习 课程介绍 本课程是针对3D引擎架构设计,涵盖引擎的基础模块,多线程基础框架,大场景加载,地形多纹理优化技术,GPU优化渲染,物理引擎,AI算法以及A ...
- 【游戏策划】《游戏架构设计与策划基础》学习收获要点-------任务与关卡的设计
游戏任务情节结构: 直线型结构 玩家的任何决定基本不会对游戏最终走向产生影响. 多分支结构 玩家的决定会影响游戏走向并且可能影响到最终的结局. 注:游戏资源中的重要部分应该被主要的情节共享,一直到它们 ...
最新文章
- vue 实践技巧合集
- 高德地图只显示一个省_浅谈当下各种导航软件:高德地图、百度地图、腾讯地图...
- 【省选2020A卷】作业题【矩阵树】【扩域】【莫比乌斯反演】
- Web Hacking 101 中文版 十一、SQL 注入
- 计算机科学中的递归算法是把问题,递归运算法
- ****** 四 ******、软设笔记【数据结构】-排序、插入排序、选择排序
- JAVA包装类及自动封包解包示例代码
- 490 - Rotating Sentences
- imx8开发之~源码编译
- YOLOv3的环境配置
- 【2019】【论文笔记】基于混合石墨烯金属结构的可重构THz Vivaldi天线——
- 跑马灯的一些使用心得
- win7连接惠普打印机p1108
- RFC7515- JSON Web Signature (JWS)(JSON Web签名)
- 转载:解决服务器上 w3wp.exe 和 sqlserver.exe 的内存占用率居高不下的方案
- 【信号处理】python按原理实现BPSK、QPSK、QAM信号调制
- 【汇编语言实战】一元二次方程ax2+bx+c=0求解(含源码与过程截屏,可修改参数)
- 用dictionary写火星文翻译器
- Dew Lab Studio
- python dll注入监听_注入方式,劫持dll注入的实现
热门文章
- NSFC: 研究意义的写法
- 简单好用的书签管理器OneNav
- C++如何使用友元类
- 根据公司名称生成base64 logo(python文字转图片)
- go语言协程和线程区别
- File zilla远程连接服务器报错:服务器发回了不可路由的地址,使用服务器地址代替...
- 电脑端音乐播放器html5,原生js的音频播放器,兼容pc端和移动端(原创)
- Unity区分安卓,苹果是平板还是手机
- 无线局域网针对共享信道问题为什么采用CSMA/CA而不是CSMA/CD
- idea git工具不见了