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开源项目文章目录 ...
随机推荐
- nasm aat函数 x86
xxx.asm: %define p1 ebp+8 %define p2 ebp+12 %define p3 ebp+16 section .text global dllmain dllmain: ...
- vue页面切换过渡
<!--<transition name="slide-left">--> <router-view></router-view>& ...
- java基础第11期——Stream流、方法引用、junit单元测试
1.Stream流 Stream流与io流是不同的东西,用于解决集合类库已有的弊端, 1.1 获取Stream流: Collection集合的Stream方法,注意Map集合要经过转化 default ...
- oracle之用户
命令都是在命令行窗口执行 创建用户 1)登陆管理员用户 sqlplus system/密码 sqlplus system/briup 注意不要以分号结尾 2)创建用户 create user 用户名 ...
- java实现压缩文件
原文链接:https://www.cnblogs.com/zeng1994/p/7862288.html
- 「TcaplusDB知识库」概念(表、键、记录、索引)
TcaplusDB作为一款NoSQL数据库,语法与传统的SQL关系库有所差异.本文将详细介绍TcaplusDB表.记录.索引这三个数据库中常用术语在TcaplusDB中的概念与意义. 术语\概念 ...
- 【Notes_1】现代图形学入门——计算机图形学概述
跟着闫令琪老师的课程学习,总结自己学习到的知识点 课程网址GAMES101 B站课程地址GAMES101 课程资料百度网盘[提取码:0000] 计算机图形学概述 计算机图形学是一门将模型转化到屏幕上图 ...
- 微信的两种access_token总结,不能混淆
大家需要弄清楚微信的网页授权token和公众号api调用授权token. 1.网页授权access_token 1.有效期:7200ms 2.微信网页授权是通过OAuth2.0机制实现的,在用户授权给 ...
- Django简单的使用及一些基础方法
目录 一.静态文件配置 1. 什么是静态文件 2. 静态文件的用法 3. 静态文件的动态绑定 二.请求方式与相应 1. get请求 2. post请求 3. Django后端视图函数处理请求 三.re ...
- 优化程序性能(CSAPP)
[前言]虽然现在没有接触过大型项目,但是工作了会注重性能.学习一下,应该能更好更快的理解别人写的经典优化的代码.结合CSAPP和自己的理解,总结一下. 一.程序优化综述 1.高效程序的特点 (1)适当 ...