Unity2D项目-平台、解谜、战斗! 1.1战斗底层组件CanFight-CanBeFighted
阅读原文时间:2023年07月10日阅读:2

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

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

实例特征分析

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

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

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

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

类图如下

小作坊,意思到了就行2333

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

最后给出代码:

using UnityEngine;

public class CanFight : MonoBehaviour
{
///

/// CanFight类中范围攻击最多一次可以攻击多少个目标 ///
public const int ENMEIES_MAX_NUM_ONEATTACK = 32;
/// /// 检测敌人碰撞体的筛选器,作为Collider2D.OverlapLayer()的第一个参数。具体作用见Unity API。 ///
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

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

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章