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开源项目文章目录 ...
 
随机推荐
- vue mixin执行覆盖
			
https://cn.vuejs.org/v2/guide/mixins.html 完整代码 vue-option-overwrite-strategies vue 自带的mixin如果钩子函数重复会 ...
 - NGK项目与其他项目相比有哪些优势?
			
一个项目运行这么久,难免不被其他项目比来比去.NGK项目之所以能被很多人关注,是因为NGK具有独特的优势,NGK具有很高的性能,在智能合约上有多种应用,而且NGK具有独特的跨链技术.转账没有手续费,在 ...
 - 「NGK每日快讯」11.19日NGK第16期官方快讯!
 - 自定义Edit 样式 简便写法
			
1 <?xml version="1.0" encoding="utf-8"?> 2 <selector xmlns:android=&quo ...
 - 【python接口自动化】- 正则用例参数化
			
 我们在做接口自动化的时候,处理接口依赖的相关数据时,通常会使用正则表达式来进行提取相关的数据.  正则表达式,又称正规表示式.正规表示法.正规表达式.规则表达式.常规表示法(Regular Ex ...
 - 12_MySQL如何对查询结果进行排序
			
本节所涉及的sql语句: -- 排序关键字 SELECT empno,ename,hiredate FROM t_emp ORDER BY hiredate DESC; -- 排序字段相同的情况 SE ...
 - Nifi组件脚本开发—ExecuteScript 使用指南(三)
			
上一篇:Nifi组件脚本开发-ExecuteScript 使用指南(二) Part 3 - 高级特征 本系列的前两篇文章涵盖了 flow file 的基本操作, 如读写属性和内容, 以及使用" ...
 - 在js中如何将字符串类型的日期("2020-11-30T02:21:42.000+0000")进行格式化
			
1.引入方法 import { formatDateNew } from '@/utils' 2.在方法中使用,注意要先将字符串进行new Date(),否则报错date.getFullYear is ...
 - Centos7安装Docker&镜像加速
			
目录 Docker Docker安装 方式一 方式二 docker 镜像加速 Docker Docker安装 Docker安装 方式一 step1: 删除老版本(Uninstall old versi ...
 - 【pytest官方文档】解读fixtures - 1.什么是fixtures
			
在深入了解fixture之前,让我们先看看什么是测试. 一.测试的构成 其实说白了,测试就是在特定的环境.特定的场景下.执行特定的行为,然后确认结果与期望的是否一致. 就拿最常见的登录来说,完成一次正 ...