Unity2D项目-平台、解谜、战斗! 1.1战斗底层组件CanFight-CanBeFighted
各位看官老爷们,这里是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的更多相关文章
- Unity2D项目-平台、解谜、战斗! 0.1 序言:团队、项目提出、初步设计、剧情大纲
各位看官老爷们,这里是RuaiRuai工作室(以下简称RR社),一个做单机游戏的兴趣作坊. 本文跟大家聊一下社团内第一个游戏项目.算是从萌新项目组长的角度,从第一个里程碑的结点处,往前看总结一下项目之 ...
- Unity2D项目-平台、解谜、战斗! 1.2战斗组件Defence、Attack
各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 接上文,我们定义了两个分别具有"攻击"和"被攻击"语义的组件CanFight和CanB ...
- Unity2D项目-平台、解谜、战斗! 1.3移动组件
各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 在这一篇中,我们将会自顶向下地讨论本2D游戏中主角不可或缺的一个功能--移动控制. 首先我们简单分析一下2D游戏中主角与移动相 ...
- Unity2D项目-平台、解谜、战斗! 0.2 序言:团队在线协作方案、基线控制
各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 本文跟大家聊一下笔者团队中所使用的在线协作的诸多工具,以及使用这些工具的目的和所记录的内容,希望这些内容在大家团队工作中有所帮 ...
- Unity2D项目-平台、解谜、战斗! 1.5 Player框架、技能管理组件
各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 前文提到,凡是有"攻击"语义的对象,在游戏中,我们给予其一个"CanFight"组件予 ...
- eclipse里面构建maven项目详解(转载)
本文来源于:http://my.oschina.net/u/1540325/blog/548530 eclipse里面构建maven项目详解 1 环境安装及分配 Maven是基于项目对象模 ...
- [转帖](整理)GNU Hurd项目详解
(整理)GNU Hurd项目详解 http://www.ha97.com/3188.html 发表于: 开源世界 | 作者: 博客教主 标签: GNU,Hurd,详解,项目 Hurd原本是要成为GNU ...
- .NET平台开源项目速览(5)深入使用与扩展SharpConfig组件
上个月在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧 和 .NET平台开源项目速览(1)SharpConfig配置文件读写组件 中都提到了SharpConfig组件,简单轻量级 ...
- .NET平台开源项目速览(2)Compare .NET Objects对象比较组件
.NET平台开源项目速览今天介绍一款小巧强大的对象比较组件.可以更详细的获取2个对象的差别,并记录具体差别,比较过程和要求可以灵活配置. .NET开源目录:[目录]本博客其他.NET开源项目文章目录 ...
随机推荐
- JavaScriptBOM操作
BOM(浏览器对象模型)主要用于管理浏览器窗口,它提供了大量独立的.可以与浏览器窗口进行互动的功能,这些功能与任何网页内容无关.浏览器窗口的window对象是BOM的顶层对象,其他对象都是该对象的子对 ...
- Vue学习笔记-Vue.js-2.X 学习(二)===>组件化开发
===重点重点开始 ========================== (三) 组件化开发 1.创建组件构造器: Vue.extends() 2.注册组件: Vue.component() 3.使用 ...
- Dubbo之高级特性
Dubbo 注意当启动服务时,该服务会占用本机一个端口号,故在一台电脑启动多个服务时需要在配置文件中更占用本机的端口号 <!--服务占用本机的端口-当本机启动多个服务时须保持不同--> & ...
- Docker SDK for Python
一.概述 Docker引擎API的Python库.它允许您执行docker命令所做的任何操作,但可以在Python应用程序中运行容器.管理容器.管理群集等. 官方文档: https://docker- ...
- POJ-3259(最短路+Bellman-Ford算法判负圈)
Wormholes POJ-3259 这题是最短路问题中判断是否存在负圈的模板题. 判断负圈的一个关键就是理解:如果在图中不存在从s可达的负圈,最短路径不会经过一个顶点两次.while循环最多执行v- ...
- 《Selenium自动化测试实战》新书上市,有需要朋友们可以了解下,欢迎大家多提宝贵意见
京东:https://item.jd.com/13123910.html当当:http://product.dangdang.com/29204520.html 1. 本书基于 Python 3.8 ...
- js浅拷贝(地址引用)和深拷贝(克隆)
浅拷贝和深拷贝相对于引用类型而言的. js有两大类型值类型(基本数据类型)和引用类型(object,function,array): 值类型保存在栈上,引用类型保存在堆上. 浅拷贝只是单纯的拷贝对象的 ...
- IDEA中便捷内存数据库H2的最简使用方式
在IDEA中有时候为了练习,需要使用到数据库,但如果自己工作或开发机子上本来没有安装数据库,也没有可用的远程数据库时,我们可以直接在IDEA环境上使用便捷式的内存数据库H2,关于H2更多知识就自己去找 ...
- R绘图(3): 散点图添加文本注释
这里以火山图为例进行说明,在转录组分析中,火山图是很常见的一类图,纵轴表示p_value,横轴表示log (fold change).单一的散点图绘制很简单,火山图比较难处理的地方就是一些基因的注释, ...
- Windows-Redis占用C盘系统空间
发现redis在电脑死机蓝屏的情况下,就是非正常退出redis会导致redis的缓存文件不会回收,占用系统空间, 下次在启动的时候,会再次创建一个10G多的缓存文件,极度占用磁盘空间. 现说明解决办法 ...