各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊。

本文对该2D项目中战斗底层组件的开发及设计思路做一个总结,希望各路同行多多交流,各路大佬多多指点。

实例特征分析

首先对于各个能够参加战斗的实例来说,比如能主动攻击的主角、能够和主角战斗的怪物,都需要一种手段(component in unity)来施加"攻击"这个语义的动作,以及受到来自攻击者的"被攻击"的动作。故笔者初步设计一个组件来表达这两种语义。

接着,一个显然的问题出现了,单一职责原则,战斗组件承担了攻击函数的调用和受伤函数的调用两种逻辑语义,那有没有一种需求,只需要攻击需求而不需要受击需求呢,显然,比如只能挨打的宝箱、只能攻击的机关等等。于是,设计变成了这样:

攻击组件提供对单体伤害、对范围内伤害、击退、施加异常效果、施加元素攻击等基本攻击接口。

受击组件拥有一个记录当前帧所有攻击信息的栈,以供高层组件调用,同时提供受击、以及栈操作等接口。

类图如下

小作坊,意思到了就行2333

这样,在敌人的AI脚本中,或者是主角的技能脚本中,就可以调用相应的函数来执行不同的技能逻辑伤害效果。

最后给出代码:

using UnityEngine;

public class CanFight : MonoBehaviour
{
/// <summary>
/// CanFight类中范围攻击最多一次可以攻击多少个目标
/// </summary>
public const int ENMEIES_MAX_NUM_ONEATTACK = 32;
/// <summary>
/// 检测敌人碰撞体的筛选器,作为Collider2D.OverlapLayer()的第一个参数。具体作用见Unity API。
/// </summary>
private ContactFilter2D filter = new ContactFilter2D(); private bool isInitialized = false; /// <summary>
/// 构造函数,初始化筛选器的配置
/// </summary>
public void Initiailize(string[] layerNames)
{
filter.useNormalAngle = false;
filter.useDepth = false;
filter.useOutsideDepth = false;
filter.useOutsideNormalAngle = false;
filter.useTriggers = false; filter.useLayerMask = true; LayerMask targetLayer = 0;
foreach(string layername in layerNames)
{
targetLayer ^= 1 << LayerMask.NameToLayer(layername);
}
//Debug.Log("在" + gameObject.name + "中,可攻击到的层为" + System.Convert.ToString(targetLayer,2)); filter.layerMask = targetLayer;
//32个bit表示32个层,左移表示筛选需要哪个层 isInitialized = true;
} /// <summary>
/// 对一个能够战斗的目标造成伤害,作为底层私有函数被调用
/// </summary>
/// <param name="target">造成伤害的目标</param>
/// <param name="damage">造成输入数值的伤害</param>
/// <param name="interruptType">攻击打断类型,默认为无打断</param>
/// <returns>返回造成了多少伤害,具体用法有待进一步讨论</returns>
public int Attack(CanBeFighted target, int damage, AttackInterruptType interruptType = AttackInterruptType.NONE, ElementAbilityManager.Element element = ElementAbilityManager.Element.NULL)
{
if(!isInitialized)
{
Debug.LogError("在" + gameObject.name + "物体中,CanFight组件未初始化!");
}
return target.BeAttacked(gameObject, damage, interruptType, element);
} /// <summary>
/// 范围性攻击,实现方法为输入表示范围的Collier2D,检测范围内的每个拥有CanBeFighted的敌人,调用BeAttacked
/// </summary>
/// <param name="area">表示攻击范围的collier2d, 应该为trigger态</param>
/// <param name="damage">该次范围攻击造成了多少伤害</param>
/// <param name="interruptType">该次攻击为何种打断类型</param>
/// <returns>返回攻击到的敌人对象的CanBeFighted组件的数组</returns>
public CanBeFighted[] AttackArea(Collider2D area, int damage, AttackInterruptType interruptType = AttackInterruptType.NONE, ElementAbilityManager.Element element = ElementAbilityManager.Element.NULL)
{
//输入范围需要Trigger才行
if(!area.isTrigger)
{
Debug.LogError("在" + gameObject.name + "释放范围攻击时,输入的collider2d并不是trigger态");
return null;
} Collider2D[] enemies = new Collider2D[ENMEIES_MAX_NUM_ONEATTACK];
int enemiesNumber = area.OverlapCollider(filter, enemies); if(enemiesNumber != 0)
{
Debug.Log("攻击碰到敌人"); CanBeFighted[] enemiesAttacked = new CanBeFighted[enemiesNumber];
CanBeFighted enemyBody;
//对碰到的敌人进行以下操作,如果敌人有CanBeFighted组件,则施加攻击,否则报错 for(int i = 0; i < enemiesNumber; i++)
{ if (enemies[i].TryGetComponent<CanBeFighted>(out enemyBody))
{
Attack(enemyBody, damage, AttackInterruptType.NONE, element);
enemiesAttacked[i] = enemyBody;
}
else
{
Debug.LogError("在" + gameObject.name +
"释放范围攻击时,这些物体被检测为敌人,但是没有CanBeFighted组件" + enemies[i].gameObject.name);
}
}
return enemiesAttacked;
} return null;
} public CanBeFighted[] AttackArea(Collider2D area, int damage, CanBeFighted[] hasAttacked, AttackInterruptType interruptType = AttackInterruptType.NONE, ElementAbilityManager.Element element = ElementAbilityManager.Element.NULL)
{
//输入范围需要Trigger才行
if (!area.isTrigger)
{
Debug.LogError("在" + gameObject.name + "释放范围攻击时,输入的collider2d并不是trigger态");
return null;
} Collider2D[] enemies = new Collider2D[ENMEIES_MAX_NUM_ONEATTACK];
int enemiesNumber = area.OverlapCollider(filter, enemies); if (enemiesNumber != 0)
{
Debug.Log("攻击碰到敌人"); CanBeFighted[] enemiesAttacked = new CanBeFighted[enemiesNumber];
CanBeFighted enemyBody;
//对碰到的敌人进行以下操作,如果敌人有CanBeFighted组件,则施加攻击,否则报错 for (int i = 0; i < enemiesNumber; i++)
{
if (enemies[i].TryGetComponent<CanBeFighted>(out enemyBody))
{
Attack(enemyBody, damage, AttackInterruptType.NONE, element);
enemiesAttacked[i] = enemyBody;
}
else
{
Debug.LogError("在" + gameObject.name +
"释放范围攻击时,这些物体被检测为敌人,但是没有CanBeFighted组件" + enemies[i].gameObject.name);
}
}
return enemiesAttacked;
} return null;
}

整个项目原型github地址:

www.gitHub.com/yunshiyue/elementgame

看官有何见解,有何指点,欢迎留言,也欢迎私聊~

Unity2D项目-平台、解谜、战斗! 1.1战斗底层组件CanFight-CanBeFighted的更多相关文章

  1. Unity2D项目-平台、解谜、战斗! 0.1 序言:团队、项目提出、初步设计、剧情大纲

    各位看官老爷们,这里是RuaiRuai工作室(以下简称RR社),一个做单机游戏的兴趣作坊. 本文跟大家聊一下社团内第一个游戏项目.算是从萌新项目组长的角度,从第一个里程碑的结点处,往前看总结一下项目之 ...

  2. Unity2D项目-平台、解谜、战斗! 1.2战斗组件Defence、Attack

    各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 接上文,我们定义了两个分别具有"攻击"和"被攻击"语义的组件CanFight和CanB ...

  3. Unity2D项目-平台、解谜、战斗! 1.3移动组件

    各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 在这一篇中,我们将会自顶向下地讨论本2D游戏中主角不可或缺的一个功能--移动控制. 首先我们简单分析一下2D游戏中主角与移动相 ...

  4. Unity2D项目-平台、解谜、战斗! 0.2 序言:团队在线协作方案、基线控制

    各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 本文跟大家聊一下笔者团队中所使用的在线协作的诸多工具,以及使用这些工具的目的和所记录的内容,希望这些内容在大家团队工作中有所帮 ...

  5. Unity2D项目-平台、解谜、战斗! 1.5 Player框架、技能管理组件

    各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 前文提到,凡是有"攻击"语义的对象,在游戏中,我们给予其一个"CanFight"组件予 ...

  6. eclipse里面构建maven项目详解(转载)

    本文来源于:http://my.oschina.net/u/1540325/blog/548530 eclipse里面构建maven项目详解 1       环境安装及分配 Maven是基于项目对象模 ...

  7. [转帖](整理)GNU Hurd项目详解

    (整理)GNU Hurd项目详解 http://www.ha97.com/3188.html 发表于: 开源世界 | 作者: 博客教主 标签: GNU,Hurd,详解,项目 Hurd原本是要成为GNU ...

  8. .NET平台开源项目速览(5)深入使用与扩展SharpConfig组件

    上个月在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧  和 .NET平台开源项目速览(1)SharpConfig配置文件读写组件 中都提到了SharpConfig组件,简单轻量级 ...

  9. .NET平台开源项目速览(2)Compare .NET Objects对象比较组件

    .NET平台开源项目速览今天介绍一款小巧强大的对象比较组件.可以更详细的获取2个对象的差别,并记录具体差别,比较过程和要求可以灵活配置. .NET开源目录:[目录]本博客其他.NET开源项目文章目录 ...

随机推荐

  1. 打造NGK生态星空计划,高倍币VAST即将震撼上线!

    援引华盛顿邮报.彭博社.路透社以及CNN等知名媒体的报道,NGK官方近日宣布,为了完善NGK生态星空计划,NGK官方近日即将推出SPC的子币VAST,以鼓励更多的生态建设者参与. NGK官方相关负责人 ...

  2. django中间件介绍

    在学习django中间件之前,先来认识一下django的生命周期,如下图所示: django生命周期:浏览器发送的请求会先经过wsgiref模块处理解析出request(请求数据)给到中间件,然后通过 ...

  3. C++算法代码——质因数分解[NOIP2012普及组]

    题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1102 题目描述 已知正整数 n 是两个不同的质数的乘积,试求出较大的那个质数. 输入 ...

  4. ASP.NET Core获取请求完整的Url

    在ASP.NET项目中获取请求完整的Url: 获取System.Web命名空间下的类名为HttpRequestBase的Url方法: /// <summary>在派生类中替代时,获取有关当 ...

  5. React组件复用的方式

    React组件复用的方式 现前端的工程化越发重要,虽然使用Ctrl+C与Ctrl+V同样能够完成需求,但是一旦面临修改那就是一项庞大的任务,于是减少代码的拷贝,增加封装复用能力,实现可维护.可复用的代 ...

  6. idea添加汉化包之后出现的一些问题 解决方案

    先把原链接放在这:https://jingyan.baidu.com/article/fb48e8bef2bcb66e622e14d2.html 关掉idea之后,一定要记住是在压缩软件中打开,而不是 ...

  7. Ch1-What is DAX?

    What is DAX? 数据分析表达式 (DAX) 是在 Analysis Services.Power BI 以及 Excel 中的 Power Pivot 使用的公式表达式语言.在第一版Powe ...

  8. linux开启FTP服务

    目录 打开FTP服务 客户端链接时会出现的问题 打开FTP服务 先ping,查看网络是否联通 打开ssh服务 查看一些服务的状态 #查看ssh状态 service sshd status #防火墙的状 ...

  9. 基于Hi3559AV100 RFCN实现细节解析-(1)VGS初介绍

    下面随笔系列将对Hi3559AV100 RFCN实现细节进行解析,因为RFCN用到了VGS加框,因此本篇随笔将给出VGS视频图像子系统的具体说明,便于后面RFCN的细节实现说明. VGS 是视频图形子 ...

  10. 优化自动化测试流程,使用 flask 开发一个 toy jenkins工具

    1.自动化 某一天你入职了一家高大上的科技公司,开心的做着软件测试的工作,每天点点点,下班就走,晚上陪女朋友玩王者,生活很惬意. 但是美好时光一般不长,这种生活很快被女主管打破.为了提升公司测试效率, ...