• 游戏开发——战斗系统设计技巧


           :战斗系统和除战斗系统之外的(简称外围系统)。而我一直在做的是外围系统的开发,至少在6月份返校毕业答辩之前没有动过战斗系统。答辩回来之后非常长一段时间内也是在做外围系统的bug修复,但是因为种种原因项目赶不上所谓的进度了,上周五主管问我和另外一个也主要负责外围系统开发的同事谁更忙,我一句我没啥事干。结果主管说战斗系统的主动技能让我来做。这周一开会负责人一纸任务安排扔下来,上面写着XX同事这周完毕主动技能的开发及相关系统的bug修复,还说没完毕任务就XXX。此处省略XX字。

            再略微说下眼下本人的情况:不是计算机相关专业毕业的。去年学了两个月C++,一个月Cocos2dx。然后十月份入职如今的这个公司,因此相关技能水平就。。。如今在做的项目加上被砍掉的项目,共经历过两个项目开发。

    本着一腔热情加上强大的互联网资源。到眼下为止项目组安排的任务都按时完毕了。所以能够说是个略微合格的游戏开发从业者吧。

            扯了这么远回到正题吧。技能的设计,首先我来理解一番游戏中的技能。在回合制游戏中。主动技能的流程大致为:释放技能,得到技能影响值(项目组天天喊的buff)并确定技能对象,技能检測+影响值加入到技能对象。

    而这次的主动技能设计我也是照着这个思路来的,因此本文能够据此大致分为三块来讲:技能释放部分。buff生成部分(不加入至对象),技能检測部分。

    (本项目的技能相对来说较简单。技能释放不考虑魔法值,buff生成不考虑别的影响因素,技能检測不涉及命中率)


           战斗:

           己方队伍+敌方队伍。说白了就相当于一个卡牌游戏。


    一、释放技能

           本项目的释放流程大致为:战斗场景(BattleScene)中,当前操控的己方队员达到技能CD时间,玩家主动运行某个操作释放当前战斗队员的技能。释放的技能可能是一个组合技能也可能是单个技能。

           在这里要控制的是:技能信息(影响值、技能释放者、技能作用者、技能影响回合),技能管理(触发检測、产生技能影响、清除影响)。因此我首先想到的时候要在该场景里生成一个技能管理器。负责处理这里要做的技能控制。

    写了一个技能管理类:skill_command(称为技能控制器吧),大致成员例如以下:

    class skill_command
    {
    public:
        /**
         *这个成员函数返回值用来做对是否是即时生效型技能的判读
        **/
        bool init();
     private:
        skill_info			        _info;   	//技能的信息
        role*				_user;  	//技能使用者
        role*				_obj;    	//技能作用者
    }

    由于技能是在BattleScene释放,且须要在该场景下做一系列的检測,所以我的想法是将技能管理作为该场景内的成员,在该场景内运行技能管理类的检測函数。

    而考虑到技能可能是组合技能,所以在场景类中,我以一个vector存储技能管理对象。

    class BattleScene
    {
    private:
    	vector<skill_command*> vecSkillCmd;
    }

           採取了这种设计之后,每一次操作队员释放某个技能。先分解技能组合,产生一个技能信息则new一个指针存储在vecSkillCmd中:

    vec = analyze(skill)
    for(//做个循环)
    {
    	auto skill_cmd = new skill_command();
       	skill_cmd->init();//在这里还须要设置技能信息及作用对象等
       	vecSkillCmd.push_back(skill_cmd)
    }

           做这种设计考虑的是:释放一个技能,将其组合技能分解出来单独控制,化繁为简。

    也由于我已经得到了一个技能产生作用须要的信息(记录了技能信息、技能作用者和使用者),所以我在技能作用、检測阶段就不须要额外再向用户须要诸如技能对象等信息,仅仅须要在场景中提供检測条件就可以,条件达成了就能够直接在skill_command内部实现技能影响。

    想当初上一个项目我也參与到了战斗系统后期的维护工作。做的一块就是优化技能的效果表现,当初碰到最大的麻烦就是非常难确定技能对象,所以那时候好苦逼的在找技能对象。所以这一次我的想法是,无论技能怎样我首先要做的就是拆解开来。单独的确定其使用者和作用者。这样每次一次做技能检測和产生技能效果我都能找到对象。

           设计案中也提到,技能能够分为即时作用和延时作用。指的是某些技能是一使用就会产生效果(如发射子弹、发射激光等),而有些是须要达到某个条件(如碰撞到了敌方队员)才会产生效果。

    所以基于化繁为简的考虑,在分解技能信息,产生技能控制器的阶段我就将这两类技能分开处理。详细做法是在BattleScene场景内再加入一个成员专门用来存储即时作用的技能:

    class BattleScene
    {
    private:
    	vector<skill_command*> vecSkillCmd;
        vectpr<skill_command*> vecInstanceSkillCmd;
    }

           这样处理之后,在生成技能控制器阶段根据一技能生效类型分开存储至不同的管理容器内。在检測阶段就仅仅须要在不同的条件阶段。循环检測不同的容器内容,就能够达到我们的设计目的。

    如对于即时生效的技能,我仅仅须要在释放完技能后对vecInstanceSkillCmd的成员做检測就可以。当然会说直接将vecSkillCmd放在这里检測不就能够了么?这就是我怎样将一个技能管理器skill_command存储的考虑了。

            上文提到了,根据技能的作用类型分别会将技能控制器存储至vecSkillCmd和vecInstanceSkillCmd。

    这里考虑了其作用时间,同一时候我也考虑了其作用条件。

    由于有的技能是即时无条件作用。而有的是即时有条件作用。针对即使无条件作用的,由于此种技能即时无条件作用了,所以我不须要在释放完之后再做管理。因此我没有存储至vec中。

    而即时有条件作用的才会存储至vec中以备之后做检測。这里我们用了一个成语函数releaseSkill表示技能释放动作:

    void BattleScene::releaseSkill()
    {
        vec = analyze(skill);
        for(//做个循环)
        {
            auto skill_cmd = new skill_command();//生成一个技能控制器  
            skill_cmd->init();//在这里还须要设置技能信息及作用对象等
            if(//假设不是马上生效) 
            { 
                vecSkillCmd.push_back(skill_cmd); 
            }
            else//即时生效 
            {
                if(//无条件触发) //运行触发效果 
                {
                    skill_cmd->triggerSkill();
                    CC_SAFE_DELETE(_cmd);//删除该指针
                }
                else
                { 
                    vecInstanceSkillCmd.push_back(skill_cmd);
                }
            }
        }
    }

            进行到这一步,关于技能的释放部分就差点儿相同了。而在项目中我做的技能释放也就是这样一个操作。


    二、buff生成

            事实上上周五安排我做技能的时候,我一開始想的是怎样做buff的生成。同事也提到要写个通用的接口,能够做道具的处理(加血、加速等道具),这样就能够省时省力。所以首先做的工作便是做buff,当然也没办法,由于那个时候没策划跟进我该怎样做技能,直到这周三勉勉强强才把方案拿过来。此处又要省略XX字了。

            由于我们已经有了一个role角色类,我想到buff不就是加到角色上的么,因此我的设计方案是如之前写技能控制器一样,在role角色类内加入成员作为buff控制器。

    也是基于化繁为简的考虑,一个buff就生成一个控制器,buff控制器内部也是包括buff信息,作用者和使用者等信息:

    class buff_command
    {
    private:
        buff_info				_info;   	//buff的信息
        buff_user				_user;		//buff的使用者
        buff_obj				_obj;		//buff的作用者
    }

           每一次产生了一个buff。就生成一个控制器,与技能控制器的设计思路类似:在role角色类内部以容器管理这些控制器。每次使用技能或者使用道具而得到了一个buff,就生成一个buff存储至容器内,以便做后面的技能检測和buff效果生成:
    class role
    {
    public:
        void addBuff();
    private:
        vector<buff_command*> vecBuffCmd;
    }

            role角色类一个buff管理成员和一个buff的生成函数。buff的生成函数在技能控制器的初始化函数内部调用。其大致内容例如以下:

    void role::addBuff()
    {
    	//检測反复buff
    	check();
    	auto buff_cmd = new buff_command();
    	buff_cmd->init();
    
    	vecBuffCmd.push_back(buff_cmd);
    }


              跟技能控制器的生成类似。这里有一步的内容是做buff反复性检測:已经触发的buff删除其buff效果,而没有触发的则直接从容器中删除。

           结合第一部分的内容大致做个流程介绍:

           1、首先在SceneBattle场景触发技能使用:

    battle_role->releaseSkill();

                2、触发技能使用之后解析技能组合生成技能控制器,初始化技能控制器信息:
    auto skill_cmd = new skill_commant();
    skill_cmd->init();
                3、生成技能buff:
    bool skill_command::init()
    {
    	initSkillInfo();
    	//技能作用对象生成buff信息
    	skill_obj->addBuff();
    }
           经过以上三步,简单的一个buff便已生成。



    三、技能检測

           这一部分我感觉有点复杂,也是我拿不准的地方。周六刚測试了一遍整个释放-加入-检測逻辑,在现有的几套技能中是没有问题的。就是不知道之后的会怎么样。

           项目的技能检測设计为:首先要检測触发条件,再检測生效条件。既首先一个技能要因某个条件而被触发,如己方队员碰了一下对方,或者己方队员碰到了某个场景元素。那么该技能便达到了触发条件。满足了触发条件之后,接着去做技能的生效条件,如该技能是要释放者碰到了对方才会释放,或者碰撞到了己方队友才会释放等等。

    所以在技能控制器skill_command内部写了两个成员函数:

    class skill_command
    {
    public:
        //用来做触发条件的检測
        void checkTriggerCondition();
        //达到触发条件了,则触发技能
        void triggerSkill();
    private:
        bool 	_bOk;
    }

           技能的检測主要就是靠这这两个成员函数来实现,当然实际上这两个函数是带參数的。

    调用函数的地方是在BattleScene场景中:

    BattleScene:://某个动作
    for(//)
    {
    	auto skill_cmd = vecSkillCmd[i];
    	//每次先做条件检測,达到条件了之后控制器内部的bool型成员为true
    	skill_cmd->checkTriggerCondition();
    	//内部bool型成员为true的时候运行该成员函数的内部动作
    	skill_cmd->triggerSkill();
    }

           由于之前的设计中在技能控制器内部有技能作用者这一成员_obj。所以实际上技能的触发是调用_obj的成员函数来实现:

    bool buff_command::triggerSkill()
    {
    	if(_bOK)
    	{
    		_obj->triggerBuff();
    	}
    }

               而我设计的触发buff这个成员函数,里面的实际动作是对role这个类里面的buff容器进行管理:
    void role::triggerBuff(//带能找到该buff的一个參数)
    {
    	for(//)
    	{
    		if(//是该buff)
    		{
    			auto buff_cmd = vecBuffCmd[i];
    			buff_cmd->trigger();
    		}
    	}
    }
           这一步便实现了一个buff的触发。回到我们之前提到的buff_command这个控制器:
    class buff_command
    {
    public:
        void trigger();
    private:
        buff_info				_info;   	//buff的信息
        buff_user				_user;		//buff的使用者
        buff_obj				_obj;		//buff的作用者
    }
           成员函数trigger实际上是对buff的作用者_obj运行buff结算。根据buff类型(血、速)等。对角色的对应数值进行调整。而在实际的设计过程中,考虑到buff信息的独立性,以一个独立的结构体:buff_info作为role的成员变量。每次做buff结算的时候实际上调整的是该成员的值。然后再将这些值加入到角色role的对应信息上去。这样相对独立的设计。一方面适合直观的展现每次技能使用带来的技能影响(当然是对我们开发者来说)。一方面由于是改别人的代码,这样独立的设计一个buff信息不会影响到已有代码所实现的功能(改代码好苦逼。主要是easy偏离原有设计者的思路)。

            这样buff的检測+产生效果功能便已实现,最后的就是buff的清理工作了。

            回合制游戏的一大特点就是一回合一回合的走下去。buff的影响效果也是有回合影响的;而技能的释放也仅仅是会在当前回合有效:即释放了一个技能,仅仅会在当前回合做有效性的检測——vecSkillCmd/vecInstanceCmd运行成员的内部检測函数,而当前回合结束后要删除全部成员(由于释放动作已经结束了。当然还要内存释放)。

            技能控制器的控制非常easy,仅仅须要在每次回合结束的时候清理vecSkillCmd的成员及内存释放,而vecInstanceCmd因为是管理的即时生效型技能,所以这个应该在释放完技能(即技能特效播放完)就须要清理及释放。

            而对于buff控制器则略微复杂点,由于涉及到buff有没有被触发:

            1、对于触发了的buff。要做回合检測:达到了回合限制则開始清理buff——调整角色类role的成员buff_info的值(即清理buff的影响值,假如造成了10点扣血。则清理的时候要加回10点血)。这里可能须要解释一番buff的生效机制:假如触发了10点扣血的buff,那么role的成员buff_info里面的_hp数值就要调整为-10,表示扣血。每次对角色的数值信息进行结算的时候加上了这个buff值-10,就相当于实现了扣血功能。所以在清理buff的时候要加回10,buff_info里面的_hp数值就成为了0.

            2、对于没有触发的buff,则直接清理删除掉,这个跟技能控制器的清除管理思路类似。

            这个清理工作是在每回合结束时进行,清理函数设计为角色类role的成员函数,对于场景中的每一个角色都运行内部设计的清理检查函数。

            

            至此这一周所做的工作便是这些了。写这篇博客相当于是对一周工作的总结,也不知道设计上哪些地方不合理了。不合理也木有办法。我所会的就是这些了。。。事实上本来还想结合设计模式相关的知识来梳理一番这周的工作,但是还没有看相关的书籍所以不好怎么去梳理。总之设计模式相关方面的书肯定是要看的。近期在看《STL源代码剖析》和《Programming in Lua 3》这两本书。看完当中一本之后便须要看关于设计模式相关的资料了。

            

           


    总结:

           在写完之后事实上自己也想了一些改进:针对buff的终于影响类型——血、速、攻、魔等,写不同的类,然后统一让buff_command进行管理。这样是否便达到了可扩展的要求呢?今后假设还须要有额外的buff影响,那么仅仅须要再加入额外的类让buff_command类进行管理就可以?

           无论这次的设计思路怎样。总归是对自己的一种锻炼吧,之前毕竟没做过这些而今后总是会要做这些工作的~所谓的一些术语如:代码耦合度、代码复用、可扩展性等都是须要进一步的理解,在工作中须要再多注意的。




    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    启xin宝app的token算法破解——frida篇(四)
    启xin宝app的token算法破解——token分析篇(三)
    启xin宝app的token算法破解——逆向篇(二)
    启xin宝app的token算法破解——抓包分析篇(一)
    关于企查查app sign研究
    企查查app (二)
    企查查app (完结)
    企查查app 初步探索
    天眼查sign 算法破解
    企查查app新增企业数据抓取
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4669824.html
Copyright © 2020-2023  润新知