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

接上文,我们定义了两个分别具有“攻击”和"被攻击"语义的组件CanFight和CanBeFighted。对于CanFight我们的设计意图是任何对单个敌人,亦或是范围中的一些敌人进行攻击的函数调用都能够通过这个组件于以实现;对于CanBeFighted,它通过维护一个受击信息的队列,保存任何攻击他的攻击信息,比如攻击的施加者、攻击的伤害值、攻击的类型等等。

很明显,CanFight组件拥有独立存在的语义和逻辑,比如我有一个"地刺"陷阱,只需要将CanFight组件挂在地刺物体上,每一帧调用对范围进行攻击的函数就可以实现地刺的效果,或者在OnXXXEnter中对主角进行针对性的伤害也是可选实现方式。但是,对于CanBeFighted,它只提供了一个受击队列,并提供了一些统计数据的方法,比如求伤害和、求最大伤害值等等,并没有定义游戏中常见的"血量""护甲""死亡"等语义,而这些逻辑都和受击组件密不可分。

所以我的设计初衷是对于不同类型的"可能被攻击到的对象",他们的共同特点是能够被攻击,也就是CanBeFighted所提供的功能。而被攻击后具体作何反应,有何逻辑,都不是底层CanBeFighted组件所关心的。

这就引出了本篇的中心——针对不同类型的对象设计不同的CanBeFighted的上层组件,用来处理不同受击后的逻辑效果。我们姑且叫他Defence组件——所有Defence子类的公共父类。

攻击-防御组件的设计逻辑

由于每次攻击到对象,该对象的CanBeFighted类就会记录下攻击信息,并改变栈顶指针,我们想要实时对于攻击做出反馈,就需要在Update函数中每一帧检测CanBeFighted的受击列表,并对每一个攻击信息做出反馈。所以基本框架差不多是这样的——

受击信息统计与响应逻辑

不同的Defence子类只是在受击响应部分逻辑有所不同而已,比如宝箱——受击后自身消失,并掉落物品,敌人——受击后进入受击状态,并生命值减少。

从设计模式上看,整个框架类似于监听者模式,受击列表或者CanBeFighted组件扮演了事件队列的角色,Defence组件扮演了对事件队列的访问器以及响应函数的角色。

下面,我们给出Defence父类和"主角"两个主要的Defence子类代码,供各位看官参考:

using System.Collections;
using System.Collections.Generic;
using UnityEngine; //Defence组件需要一个CanBeFighted组件,作为底层操作对象
[RequireComponent(typeof(CanBeFighted))]
abstract public class Defence : MonoBehaviour
{
protected CanBeFighted attackedCheck; protected int hpMax = 5;
protected int hp = 0;
protected bool isDead = false;
//保证该组件被初始化过,否则在调用方法时候报错
private bool hasBeenInitialized = false; //统计相关
protected bool hasSetStatistic = false;
//受击次数
protected int attackNum = 0;
//毛伤害总和
protected int damageSum = 0;
//最大打断类型
protected AttackInterruptType maxInterrupt = AttackInterruptType.NONE;
//净伤害总和
protected int realDamage = 0;
//血量下降值
protected int hpReduction = 0; protected virtual void Awake()
{
attackedCheck = GetComponent<CanBeFighted>();
if (attackedCheck == null)
{
Debug.LogError("在" + gameObject.name + "中,Defence组件没有找到所依赖的CanBeFighted组件");
}
}
public void SetImmune(bool isImmune)
{
attackedCheck.SetImmune(isImmune);
}
public virtual void Initialize(int hpMax)
{
hasBeenInitialized = true;
this.hpMax = hpMax;
this.hp = hpMax;
}
//子类中应该在这个方法中包含你想通过这个组件做到的全部事情,除了clear
abstract public void AttackCheck(); public int getHpMax() { return hpMax; }
public int getHp() { return hp; }
public bool getIsDead() { return isDead; }
public int getAttackNum() { return attackNum; }
public int getDamageSum() { return damageSum; }
public AttackInterruptType getMaxInterrupt() { return maxInterrupt; }
public int getRealDamage() { return realDamage; }
public int getHpReduction() { return hpReduction; } //将下一帧的hasSetStatistic设置为false,并清除CanBeFighted的数据
public virtual void Clear()
{
hasSetStatistic = false;
attackedCheck.Clear();
}
//统计CanBeFighted中的信息,包括统计相关、受击次数、毛伤害总和、最大打断类型
protected virtual void SetStatistic()
{
hasSetStatistic = true;
if(!hasBeenInitialized)
{
Debug.LogError("在Defence中,没有初始化该组件!");
} AttackInterruptType localMaxInterrupt = AttackInterruptType.NONE;
int localDamageSum = 0; if(attackedCheck.hasBeenAttacked())
{
foreach (AttackContent attack in attackedCheck.GetAttackedList())
{
if ((int)localMaxInterrupt < (int)attack.interruptType)
{
localMaxInterrupt = attack.interruptType;
}
localDamageSum += attack.damage;
} } maxInterrupt = localMaxInterrupt;
damageSum = localDamageSum;
attackNum = attackedCheck.getAttackNum();
}
//定义了伤害计算方法,通过计算出的realDamage,SetHealthStatus方法对生命值进行扣除,并计算出hpReduction
virtual protected void Damage()
{
if(hasSetStatistic)
{
realDamage = damageSum;
SetHealthStatus();
}
else
{
Debug.LogError("在" + gameObject.name + "中,setHealthStatus发生在了setStatistic之前");
}
}
//对生命值进行扣除,并计算出hpReduction;如果生命值为0,则isDead设置为true
protected virtual void SetHealthStatus()
{ if (hp < realDamage)
{
hpReduction = hp;
hp = 0;
}
else
{
hpReduction = realDamage;
hp -= realDamage;
} if (hp == 0)
{
isDead = true;
}
} }
 1 public class DefencePlayer : Defence, ClassSaver
2 {
3 private int recoveredHp = 0;
4 private MovementPlayer movementComponent;
5
6 //挨打无敌参数
7 private float immuneTotalTime = 2f;
8 private float immuneCurTime = 0f;
9 private bool isAttackedImmune = false;
10
11 private AttackAnime attackAnime;
12 protected override void Awake()
13 {
14 base.Awake();
15 movementComponent = GetComponent<MovementPlayer>();
16 attackAnime = GetComponent<AttackAnime>();
17 }
18
19
20
21
22 public void Heal(int healPoint)
23 {
24 //hp += healPoint;
25 //hp = hp > hpMax ? hpMax : hp;
26 if ((hp+healPoint)> hpMax)
27 {
28 recoveredHp = hpMax - hp;
29 hp = hpMax;
30 }
31 else
32 {
33 recoveredHp = healPoint;
34 hp += healPoint;
35 }
36 }
37
38 public override void Clear()
39 {
40 base.Clear();
41 recoveredHp = 0;
42 }
43
44 //实现了抽象方法,在这个方法中包含你想通过这个组件做到的全部事情,在Player控制脚本中逐帧调用该方法
45 public override void AttackCheck()
46 {
47 SetStatistic();
48 AttackImmuneCheck();
49 Damage();
50 GetStatisticCollector();
51 }
52
53 public override void Initialize(int hpMax)
54 {
55 base.Initialize(hpMax);
56 }
57
58 //改写父类的伤害计算方法,每次伤害都会减少1点护甲值的伤害。
59 protected override void Damage()
60 {
61 if (hasSetStatistic)
62 {
63 realDamage = damageSum - attackNum * armor;
64 SetHealthStatus();
65 }
66 else
67 {
68 Debug.LogError("在" + gameObject.name + "中,setHealthStatus发生在了setStatistic之前");
69 }
70
71 if (realDamage > 0)
72 {
73 Debug.Log("player受到了" + realDamage + "点伤害");
74 }
75 }
76
77
78 public int getArmor() { return armor; }
79 public int getRecoverdHp() { return recoveredHp; }
80 }

那么今天的分享就是这些,欢迎访问:

整个项目原型github地址:

www.gitHub.com/yunshiyue/elementgame

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

Unity2D项目-平台、解谜、战斗! 1.2战斗组件Defence、Attack的更多相关文章

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

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

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

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

  3. Unity2D项目-平台、解谜、战斗! 1.1战斗底层组件CanFight-CanBeFighted

    各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 本文对该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开源项目你知道吗?.NET平台开源文档与报表处理组件集合(三)

    在前2篇文章这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧 和这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,大伙热情高涨.再次拿出自己的私货,在.NET平台 ...

  9. .NET平台开源项目速览(8)Expression Evaluator表达式计算组件使用

    在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,给大家初步介绍了一下Expression Evaluator验证组件.那里只是概述了一下,并没有对其使用和强大功能做 ...

随机推荐

  1. 金三银四助力面试-手把手轻松读懂HashMap源码

    前言 HashMap 对每一个学习 Java 的人来说熟悉的不能再熟悉了,然而就是这么一个熟悉的东西,真正深入到源码层面却有许多值的学习和思考的地方,现在就让我们一起来探索一下 HashMap 的源码 ...

  2. (转)linux下的系统调用函数到内核函数的追踪

    转载网址:http://blog.csdn.net/maochengtao/article/details/23598433 使用的 glibc : glibc-2.17使用的 linux kerne ...

  3. 如何在一台开发机中同时配置github、gitlab等多个账户

    本文参考博文. 如果公司的代码同步环境在gitlab上,而自己有很多项目托管在github.我们需要做一些额外的配置实现两者的无缝切换. 步骤 我的开发机是macbook,如果属于不同系统,找到该系统 ...

  4. Winform 判断打印机是否可用,实现设置默认打印机功能

    Winform 判断打印机是否可用,实现设置默认打印机功能 http://www.cnblogs.com/zfanlong1314/p/3878563.html

  5. 如何使用GraphQL Client: Apollo Android

    如何使用GraphQL Client: Apollo Android 一个Android app, 如何使用GraphQL. 本文以最流行的Apollo Android为例来说明. 添加依赖 首先, ...

  6. Java Socket编程基础及深入讲解

    原文链接:https://www.cnblogs.com/yiwangzhibujian/p/7107785.html 原文写的特别好,非常详细,但是禁止转载,那我就不再复制粘贴了! socket实现 ...

  7. Vue脚手架中默认的margin怎么清除

    问题情景:开发中发现我的项目四周有白边,但是并没有设置样式 问题原因:vue脚手架中静态文件夹public中的index.html造成的 解决方案:找到vue脚手架中index.html页面,设置ma ...

  8. JAVA基础(三)—— 输入输出处理

    JAVA基础(三)-- 输入输出处理 1 输入解析 //Scanner获取输入 import java.util.Scanner; Scanner s = new Scanner(System.in) ...

  9. mysql索引的性能分析

    [前言]上一篇博客介绍了InnoDB引擎的索引机制,主要围绕B+树的建立,目录项记录里主键和页号,到页目录下的二分法定位数据:二级索引里的主键和索引列,及其回表操作.这一篇分析一下索引的性能,围绕如何 ...

  10. SpringBoot启动报错 Disconnected from the target VM, address: '127.0.0.1:2227', transport: 'socket'

    今天搭建了一个SpringBoot项目,刚启动就报错 Disconnected from the target VM, address: '127.0.0.1:2227', transport: 's ...