美高梅4688.com《InsideUE4》-9-GamePlay架构(八)Player

转载请注明:http://www.cnblogs.com/thorqq/p/6323156.html

你们对力一无所知

 

引言

回顾上文,我们谈了了World和Level级别的逻辑操纵控制,如同分离组合的AController一样,UE在World的层次上吧动了一个分开之AGameMode来抽离了打关卡逻辑,从而支持了逻辑的重组。本篇我们继续稳中有升一个层次,考虑在World之上,游戏还得什么逻辑控制?
小无考虑别的功能体系(如社交系统,统计等各种),单从游戏性来谈谈,现在闭上眼睛,想象我们已藉着UE的伟力搭建了好了一个个LevelWorld,嗯,就比如《西部世界》一样,场景曾搭建好了,世界规则故事也修完善,现在要干几什么?当然是开端派出玩家进入玩啊!
大家都是老玩家了,想想咱们前玩的娱乐项目:

  • 玩家数据是单人还是差不多人数
  • 网环境是单纯本地或联网
  • 窗口展示模式是单屏还是分屏
  • 输入模式是官设施或者分别控制(比如各级发手柄)
  • 唯恐还有别的不同

使你是只出娱乐引擎的,会怎么支持这些不同之模式?以笔者见识过的大多数游乐引擎,解决这题目之思绪就是休解决,要嘛是限制作用,要嘛就是美名其名让开发者自己活决定。不过想了转,这也非能够杀他们,毕竟特别少出发动机能像UE这样历史悠久同时以会得到足够多的打磨练,才会发出功夫在GamePlay框架上雕。大部分发动机或再度关爱于贯彻各种绚丽之力量,至于怎么在上面进行打逻辑,那即便是开发者自己之从了。一个发动机的效应是否强大,是基础较并指标;而GamePlay框架作为最高层直面用户之连通接口,是一个引擎的体面。所以有趣味游戏引擎研究之情侣等,区分一个引擎是否“优秀”,第二单指标是圈她是否设计了一个雅的玩乐逻辑编写框架,一般只有功底功用已经做得多了的发动机开发者才见面来精力去开发GamePlay框架,游戏引擎不止渲染!
言归正传,按照软件工程的眼光,没有什么问题是免能够透过加一个间接层解决之,不行就加少交汇!所以既然我们在处理玩家模式之题材,理所当然的凡加个间接层,将玩家这个定义抽象出来。
那什么是玩家也?狭义的开口,玩家就真的卿,和而身旁的同伙。广义来说,按照图灵测试理论,如果您无法辨识另一样正值是AI还是人数,那他实在就是同玩家毫无区别,所以并无妨碍我们拿网络另一样端的均等修狗当作玩家。那么当戏耍引擎看来,玩家就是输入的发起者。游戏说白了,也就是承受输入有输出的一个顺序。所以来多少输入,这些输入归多少组,就出微个玩家。这里的输入不止包括地方键盘手柄等输入设备的按键,也席卷网线里传过来的信号,是广义的该打会承受到之外围输入。注意输出并无是玩家的不可或缺属性,一个玩家并不一定需要打的输出,想象你闭上眼睛玩马里奥或者来个网络连接不断发送来控制信号但是尚未接受反馈,虽然看起意义不死,但也着实不可知说立刻即不是游玩。
在UE的眼底,玩家也是如此广义的一个定义。本地的玩家是玩家,网络同步时虽看不显现对方,但是对方的网络连接也可作为是独玩家。当然的,本地玩家和网络玩家毕竟还是距离十分挺,所以UE里也本着彼此进行了别,才好更好之管制以及利用至不同状况被错过,比如网络玩家就跟当地设备的输入没多生关系了嘛。

本游戏正式放源代码啦~~

UPlayer

受咱作自己是UE,开始编制Player类吧。为了采取上UObject的那些现有特性,所以自然是得自UObject继承了。那是否是AActor呢?Actor是得在World中才能够是的,而Player却是比较World更强一级的靶子。玩游戏的进程被,LevelWorld在非鸣金收兵的切换,但是玩家的模式可是离不转移的。另外,Player也无欲给布置在Level中,也未待各种Component组装,所以从AActor继承并无得体。那要维持简单吧:
美高梅4688.com 1
假若图可见,Player和一个PlayerController关联起来,因此UE引擎就得管输入和PlayerController关联起来,这吗契合了前文说了的PlayerController接受玩家输入的叙说。因为无论是本地玩家还是长途玩家,都是得控制一个玩家Pawn的,所以当为就是得吗每个玩家分配一个PlayerController,所以把PlayerController放在UPlayer基类里是合理的。

代码库(可每当附件面临生载Windows下的试玩程序):https://git.oschina.net/thorqq/RaidenFree

ULocalPlayer

下一场是本地玩家,从Player中派生下来LocalPlayer类。对地方环境遭到,一个当地玩家关联着输入,也相似要关联着输出(无输出的玩家毕竟还是不行少见)。玩家对象的上层就是引擎了,所以会见当GameInstance里保存有LocalPlayer列表。
美高梅4688.com 2
UE4里之ULocalPlayer也如图所显现,ULocalPlayer比UPlayer多矣Viewport相关的安排(Viewport相关的始末在渲染章节讲述),也终于用SpawnPlayerActor实现了创办出PlayerController的效用。GameInstance里发出LocalPlayers的音信之后,就足以一本万利之遍历访问,来贯彻与当地玩家相关操作。
至于游戏的详细加载流程时非多讲述(按老在相应引擎流程章节讲述),现在大概了解一下LocalPlayer是怎么当戏耍的发动机的各个环节发挥作用的。UE在初始化GameInstance的时节,会先默认创建有一个GameViewportClient,然后在里边还倒车到GameInstance的CreateLocalPlayer:

ULocalPlayer* UGameInstance::CreateLocalPlayer(int32 ControllerId, FString& OutError, bool bSpawnActor)
{
    ULocalPlayer* NewPlayer = NULL;
    int32 InsertIndex = INDEX_NONE;
    const int32 MaxSplitscreenPlayers = (GetGameViewportClient() != NULL) ? GetGameViewportClient()->MaxSplitscreenPlayers : 1;
    //已略去错误验证代码,MaxSplitscreenPlayers默认为4
    NewPlayer = NewObject<ULocalPlayer>(GetEngine(), GetEngine()->LocalPlayerClass);
    InsertIndex = AddLocalPlayer(NewPlayer, ControllerId);
    if (bSpawnActor && InsertIndex != INDEX_NONE && GetWorld() != NULL)
    {
        if (GetWorld()->GetNetMode() != NM_Client)
        {
            // server; spawn a new PlayerController immediately
            if (!NewPlayer->SpawnPlayActor("", OutError, GetWorld()))
            {
                RemoveLocalPlayer(NewPlayer);
                NewPlayer = NULL;
            }
        }
        else
        {
            // client; ask the server to let the new player join
            NewPlayer->SendSplitJoin();
        }
    }
    return NewPlayer;
}

得看到,如果是以Server模式,会一直创造出ULocalPlayer,然后创建有相应的PlayerController。而若是Client(比如Play的时候选择NumberPlayer=2,则发出一个吧Client),则会先发送JoinSplit消息及服务器,在载入服务器上之Map之后,再为LocalPlayer创建出PlayerController。
假如以每个PlayerController创建的进程遭到,在那个内部会调用InitPlayerState:

void AController::InitPlayerState()
{
    if ( GetNetMode() != NM_Client )
    {
        UWorld* const World = GetWorld();
        const AGameModeBase* GameMode = World ? World->GetAuthGameMode() : NULL;
        //已省略其他验证和无关部分
        if (GameMode != NULL)
        {
            FActorSpawnParameters SpawnInfo;
            SpawnInfo.Owner = this;
            SpawnInfo.Instigator = Instigator;
            SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
            SpawnInfo.ObjectFlags |= RF_Transient;  // We never want player states to save into a map
            PlayerState = World->SpawnActor<APlayerState>(GameMode->PlayerStateClass, SpawnInfo );

            // force a default player name if necessary
            if (PlayerState && PlayerState->PlayerName.IsEmpty())
            {
                // don't call SetPlayerName() as that will broadcast entry messages but the GameMode hasn't had a chance
                // to potentially apply a player/bot name yet
                PlayerState->PlayerName = GameMode->DefaultPlayerName.ToString();
            }
        }
    }
}

如此LocalPlayer最终便跟PlayerState对承诺了四起。而网协同时其他玩家的PlayerState是经过Replicated过来的。
俺们说话了那旷日持久之玩家就是输入,体现在以每个PlayerController接受Player的时光:

void APlayerController::SetPlayer( UPlayer* InPlayer )
{
    //[...]
    // Set the viewport.
    Player = InPlayer;
    InPlayer->PlayerController = this;
    // initializations only for local players
    ULocalPlayer *LP = Cast<ULocalPlayer>(InPlayer);
    if (LP != NULL)
    {
        // Clients need this marked as local (server already knew at construction time)
        SetAsLocalPlayerController();
        LP->InitOnlineSession();
        InitInputSystem();
    }
    else
    {
        NetConnection = Cast<UNetConnection>(InPlayer);
        if (NetConnection)
        {
            NetConnection->OwningActor = this;
        }
    }
    UpdateStateInputComponents();
    // notify script that we've been assigned a valid player
    ReceivedPlayer();
}

看得出,对于ULocalPlayer,APlayerController内部见面初步InitInputSystem(),接着会创造相应的UPlayerInput,BuildInputStack等初始化出与Input相关的零部件对象。现在先明白到LocalPlayer才是PlayerController产生的源流,也为此才出了Input就够用了,特定的Input事件流程分析在持续章节再细述。

合计:为何非以LocalPlayer里编写逻辑?
作为游戏开发者,相信大家都来这么个体会,往往在玩乐逻辑代码中总会出一个好之Player类,里面放正这个玩家的相干数据与逻辑业务。可是以UE里为何就不见了这般个结构?也没见UE在文档里有叙推荐您怎么开创自己之Player。
这或许发一定量个由,一凡是UE从FPS-Specify游戏起家,不像今天之各种手游有良重的玩家系统,在UE的眼中,Level和World才是无限当关心的目标,因此UE的意见就在怎么当Level中处理好Player的逻辑,而无在World之外的附加操作。二凡盖在一个World中,上文提到其实已闹矣Pawn-PlayerController和PlayerState的重组了,表示、逻辑与数码都兼备了,也不怕没必要再于Level掺和进Player什么事了。当然你吧足以知道呢PlayerController就是Player在Level中之话事人。
成套留一丝,日后好碰到。尽管如此,UE还是为了咱们于定义ULocalPlayer子类的火候:

//class UEngine:
/** The class to use for local players. */
    UPROPERTY()
    TSubclassOf<class ULocalPlayer>  LocalPlayerClass;

    /** @todo document */
    UPROPERTY(globalconfig, noclear, EditAnywhere, Category=DefaultClasses, meta=(MetaClass="LocalPlayer", DisplayName="Local Player Class"))
    FStringClassReference LocalPlayerClassName;

卿得当配备中写及LocalPlayer的子类名称,让UE为公不行成你的子类。然后又于中间写及有些一定玩家的多寡及逻辑吗未尝不可,不过这部分额外扩张的效力就是得用C++来实现了。

脚我们继续分析这款游戏。

UNetConnection

杀有意思之凡,在UE里,一个网络连接也是只Player:
美高梅4688.com 3
含Socket的IpConnection也是玩家,甚至对于有阳台的特定实现如OculusNet的连接为得以看作玩家,因为于玩家,只要会提供输入信号,就可以当一个玩家。
追根溯源,UNetConnection的列表保存在UNetDriver,再到FWorldContext,最后为依然是UGameInstance,所以与LocalPlayer的列表一样,是在World上层之对象。
本篇先前瞻一下组织,对于网络有不再细述。

飞机

立是一律款款由飞机玩,所以主角当然是飞机。游戏里,飞机包括以下几种植:

  • 玩家飞机:玩家操纵的主飞机
  • 僚机:在玩家飞机左右的稍飞机,相对于玩家飞机的职是一贯不转换的。
  • 一般而言敌机:不见面变形的敌机
  • 变形敌机:飞到指定位置后变形,然后起发。被击落时,屏幕会激动
  • BOSS:从屏幕及方飞下来,飞到指定位置后变形,然后起左右平移,同时启幕开。boss可以产生反复变形,当血量低于一定值时会见接触变形,同时攻击力增强。Boss被击落后,飞机及会起多次爆炸,屏幕会陪伴着激动。
  • 得杀僚机:必杀技。当玩家点击必杀按钮时,会起屏幕下方出现一个庞大之飞行器,发射超级激光

总结

本篇我们抽象出了Player的概念,并根据用状况派生出了LocalPlayer和NetConnection这片只子类,从此Player就不再是一个架空的概念,而是UE里的逻辑实体。UE可以依据变化的Player对象的多少与花色的不等,在这达成实现有不同之玩家操纵模式,LocalPlayer作为源头Spawn出PlayerController继而PlayerState就是实证之一。而在网络并时,把一个网络连接看作是一个玩家这个定义,把于World之上的输入实体用Player统一了起来,从而可以兑现有灵活的本土远程不同玩家模式策略。
虽说,UPlayer却像是大藏在UE里的私自功臣,UE也并无引进直接在Player里编程,而是下Player作为源头,来发出构建平系列有关的体制。但对于我们娱乐开发者而言,知道并询问UE里的Player的定义,是管现实生活同游戏世界串联起的那个重大的典型。我们在一个个World里升华仰望,还会知晓的见一个个LocalPlayer或NetConnection仿佛在注视着这片全球,是她们吧World注入了精力。
都到头了?并从未,我们继续开拓进取逆风飞翔,终将得见游戏里的英明:GameInstance。

机的着力属性

  • 名字
  • 类型
  • 骨骼动画(飞行、左右侧飞、变形、暴走、尾部火焰)
  • 生命值
  • 攻击值:当机同飞机相撞时,对对方飞机有的伤害值
  • 碰撞体(刚体)的骨干坐标和分寸(只有当子弹的碰撞体与飞机的碰撞体发生叠时,才会发伤害)
  • 缩放比例
  • 血槽的体裁与位置
  • 飞机的开位置(对于玩家飞机来说,一般是自屏幕下方飞入;对于敌机来说,一般是由高达半独屏幕的某个地方竟然出来)
  • 子弹(弹夹BulletGroup+子弹Bullet)
  • 僚机
  • 机爆炸帧动画以及音效
  • 恢宏属性(可扩大,用map来实现)

俺们所以结构体TAircraftData来表示:

struct TAircraftData
{
    int id;
    std::string name;//名字
    std::string type;//类型,值为AircraftType
     //飞机动画即支持帧动画,又支持骨骼动画
    std::vector<std::string> styleArray;  //飞机动画
    float aniDura; //动画帧时长
    std::string armatureName; //骨骼动画名称

    int hp;           //当前生命值
    int maxHp;        //最大生命值
    float defence;    //防御
    int attack;       //攻击

    float bodyCenterX; //刚体中心坐标
    float bodyCenterY;
    float bodySizeW;   //刚体尺寸
    float bodySizeH;
    float scale;       //缩放比例

    std::string hpBgStyle; //血槽背景样式
    std::string hpStyle;   //血槽样式
    float hpPosX;          //血槽的位置
    float hpPosY;          //血槽的位置

    std::vector<std::vector<int>*> bulletIdArray;  //多种子弹

    std::vector<std::string> fireFameNameArray; //尾部火焰动画,图片列表
    float fireAniDura; //动画帧时长
    int   fireOnTop;
    float fireOffsetX;//火焰中心点相对于飞机底部中心点的偏移。如果等于0,则只有一个火焰;否则是两个火焰
    float fireOffsetY;

    float startPosX;  //飞机的起始位置
    float startPosY;

    std::vector<int> wingmanArray; //僚机

    std::vector<std::string> blastStyleArray; //飞机爆炸的动画
    float blastAniDura;
    int blastMusicId; //飞机爆炸的音效id

    std::map<std::string, std::string> paramMap; //飞机参数
}

引用

UE4.14


中心措施

  • 初始化、复位、销毁
  • 生之削减或者增、判断是否生在
  • 初始发、停止射击、是否正在放
  • 飞行
  • 爆炸

知乎专栏:InsideUE4

尖端道

  • 千帆竞发活动射击、停止活动开、自动改变子弹级别
  • 子弹升一个等级
  • 子弹升到最高阶段
  • 子弹降一个级
  • 是不是是最高级别子弹
  • 是否是低于级别子弹
  • 博子弹等
  • 重置子弹等
  • 子弹用完发生的通知
  • 创新配备
  • 防御机能的加码、去除

 详细代码如下:

//飞机的基类
class Aircraft : public GameObject, public IBulletUseUpListener
{
public:
    Aircraft();
    virtual ~Aircraft();

    virtual bool init(Node* parent, const TAircraftData* pData, const TAircraftLevelData* pLevelData = nullptr);
    virtual void reset();
    virtual void destory();

    virtual void reduceHp(int hp);
    virtual void recoverHp(int hp);
    virtual bool isAlive();

    virtual void startShoot();
    virtual void stopShoot();
    virtual bool isShooting();

    virtual void autoShoot();
    virtual void stopAutoShoot();
    virtual void autoChangeBulletGrade();

    //自动飞行
    virtual void fly(){};

    //爆炸
    virtual void blast();

    //更新装备
    bool updateEquip();

    //子弹升级
    virtual bool upgradeBullet();
    virtual bool upgradeMaxBullet();
    //子弹降级
    virtual bool downgradeBullet();
    //是否暴走
    virtual bool isMaxLevelBullet();
    //是否最低级子弹
    virtual bool isMinLevelBullet();
    //获取子弹等级
    virtual int getBulletLevel();
    //重置子弹级别
    virtual bool resetBulletLevel();

    //子弹用完通知
    virtual void bulletUseUp() override;

    //量子护盾
    void addShield();
    void removeShield();
    //防御效果结束
    virtual void defenceDone(float dt);

    //碰撞检测
    virtual bool isCollsion(const Rect& rect, Rect* pCollsionRect = nullptr);

    //获取攻击、防御、导弹、僚机、综合性能
    inline int getAttrAttack(){ return m_iAttrAttack; }
    inline int getAttrArmor() { return m_iAttrArmor; }
    inline int getAttrMissile(){ return m_iAttrMissile; }
    inline int getAttrWingman() { return m_iAttrWingman; }
    inline int getAttrTotal() { return m_iAttrTotal; }

    void calculateAttr();

    inline int getId()
    {
        return m_data.id;
    }

    inline int getLevelId()
    {
        if (m_data.pAircraftLevelData)
        {
            return m_data.pAircraftLevelData->id;
        }
        else
        {
            return -1;
        }
    }

    inline int getHp()
    {
        return m_data.hp;
    }

    inline void setHp(int hp)
    {
        m_data.hp = hp;
    }

    inline int getMaxHp()
    {
        return m_data.maxHp;
    }

    inline void setMaxHp(int max)
    {
        m_data.maxHp = max;
    }

    inline int getAttack()
    {
        //两机相撞时,对对方产生的伤害就是自己的血量
        return getHp();
    }

    inline void setAttack(int a)
    {
        m_data.attack = a;
    }

    void setNoDie(bool b);

    inline bool isNoDie()
    {
        return m_bNoDie;
    }

    inline Vector<BulletGroup*>* getBulletGroupArray()
    {
        return &m_bulletGroupArray;
    }

    inline EAircraftType getAircraftType()
    {
        return m_eAircraftType;
    }

    inline const std::string& getAttr(const std::string& key)
    {
        static std::string empty = "";
        auto it = m_data.paramMap.find(key);
        if (it != m_data.paramMap.end())
        {
            return it->second;
        }
        else
        {
            return empty;
        }
    }

    inline int getAircraftLevelId()
    {
        if (m_data.pAircraftLevelData)
        {
            return m_data.pAircraftLevelData->id;
        }
        else
        {
            return -1;
        }
    }

    Vector<Aircraft*>* getOtherSidePlane() const;
    void setOtherSidePlane(Vector<Aircraft*>* const planes);

    //回收
    virtual void recycle();
    //重用
    virtual void reuse();
    //是否已回收(是否可用)
    bool isRecycled();

protected:
    virtual void setBulletGrade(unsigned grade){ m_iBulletGrade = grade; }
    virtual void setAttackAdjust(float adjust){ m_fAttackAdjust = adjust; }
    virtual void setMissileAdjust(float adjust){ m_fMissileAdjust = adjust; }
    virtual void setBulletSpeedAdjust(float adjust){ m_fBulletSpeedAdjust = adjust; }

    virtual bool initBody(Node* parent, const TAircraftData* pData);
    virtual bool initPosition(Node* parent, const TAircraftData* pData);
    virtual bool initFire(Node* parent, const TAircraftData* pData);
    virtual bool initHpBar(Node* parent, const TAircraftData* pData);
    virtual bool initBullet(Node* parent, const TAircraftData* pData);
    virtual bool initWingman(Node* parent, const TAircraftData* pData);

    //添加尾部的左右两个火焰动画
    bool addFire(float offsetX, float offsetY, bool isFlipped);

protected:
    EAircraftType m_eAircraftType;
    TAircraftData m_data;
    const TAircraftData* m_pDataCopy;

    //战机所有的装备
    const TEquipmentData* m_pEquipAircraft;
    const TEquipmentData* m_pEquipArmature;
    const TEquipmentData* m_pEquipMissile;
    const TEquipmentData* m_pEquipWingman;

    int m_iBulletGrade;
    float m_fAttackAdjust;
    float m_fMissileAdjust;
    float m_fBulletSpeedAdjust;

    float m_fVipRelifeAttrAdjust;

    float m_fDefence;           //防御系数
    float m_fDefenceDura;       //防御持续时间
    int m_iCurBulletGrade; //当前子弹等级
    bool m_bNoDie;              //无敌
    cocostudio::Armature* m_pDefenceBall;     //护盾球
    HpBar* m_pHpBar;            //血槽精灵
    Vector<BulletGroup*> m_bulletGroupArray;  //多种子弹
    Vector<Aircraft*>    m_wingmanArray;      //僚机精灵
    Vector<Aircraft*>*   m_otherSideArray;    //对方飞机。对于玩家来说就是敌机,对于敌机来说就是玩家

    int m_iAttrAttack;
    int m_iAttrArmor;
    int m_iAttrMissile;
    int m_iAttrWingman;
    int m_iAttrTotal;

    bool m_bRecycled;

    bool m_bAutoShoot;
    int m_orignBulletGrade;
};

  为了构建不同之飞行器对象,我们加一个飞机工厂。工厂的create函数中TAircraftData参数是从布局(sqlite数据库)中落到的。

template<typename T>
class PlaneCreator
{
public:
    static T* create(Node* parent, const TAircraftData* data)
    {
        T *pRet = new(std::nothrow) T();
        if (pRet && pRet->init(parent, data))
        {
            pRet->autorelease();
            return pRet;
        }
        else
        {
            delete pRet;
            pRet = NULL;
            return NULL;
        }
    }

};

UE4深入学习QQ群: 456247757(非新手入门群,请预学了官方文档和视频教程)

私原创,未经授权,谢绝转载!

飞机池

   
当界面上的飞机于多,并且反复出现、被击落的时刻,系统会不停止的创建、销毁飞机对象,这样见面严重影响游戏的帧率,所以,我们加了一个简单易行的飞机池:当飞机让击落时,飞机对象并无叫灭绝掉,而只是停止射击、停止所有动画并躲起来。当用创造新的机时,会于池中搜寻出没发出相应的曾回收的机,如果找到,则指向之目标又展开初始化。代码如下:

class AircraftPool
{
public:
    static AircraftPool* getInstance();

    //获取一架飞机
    template<typename T>
    T* get(Node* parent, const TAircraftData* pAircraftData, const TAircraftLevelData* pAircraftLevelData)
    {
        //AircraftPool 只在急速模式下使用。其他两种模式反而会增加内存
        if (GameData::getInstance()->getValueToInt(GAMEDATA::MODE) != ModeBase::ModeRapid)
        {
            return PlaneCreator<T>::create(parent, pAircraftData, pAircraftLevelData);
        }

        auto it = m_aircraftMap.find(pAircraftLevelData->id);
        std::vector<Aircraft*>* pArray = nullptr;
        if (it != m_aircraftMap.end())
        {
            pArray = it->second;
        }
        else
        {
            pArray = new std::vector<Aircraft*>;
            m_aircraftMap.insert(std::map<int, std::vector<Aircraft*>*>::value_type(pAircraftLevelData->id, pArray));
        }

        //查找可用的飞机
        for (Aircraft* p : *pArray)
        {
            if (p && p->isRecycled())
            {
                p->reuse();
                T* ret = dynamic_cast<T*>(p);
                if (ret)
                {
                    parent->addChild(ret);
                    return ret;
                }
                else
                {
                    DEBUG_LOG("fuck error type of aircraft");
                }
            }
        }

        //没找到,新建一个
        auto p = PlaneCreator<T>::create(parent, pAircraftData, pAircraftLevelData);
        p->retain();
        for (unsigned i = 0; i < pArray->size(); i++)
        {
            if ((*pArray)[i] == nullptr)
            {
                (*pArray)[i] = p;
                return p;
            }
        }

        pArray->push_back(p);
        return p;
    }

    //回收一架飞机
    void recycle(Aircraft* pAircraft);

    //清空pool
    void release();

protected:
    AircraftPool();

private:
    static AircraftPool* m_pInstance;

    std::map<int, std::vector<Aircraft*>*> m_aircraftMap;
};

AircraftPool* AircraftPool::m_pInstance = nullptr;

AircraftPool* AircraftPool::getInstance()
{
    if (!m_pInstance)
    {
        m_pInstance = new AircraftPool();
    }

    return m_pInstance;
}

AircraftPool::AircraftPool()
{
}

//回收一架飞机
void AircraftPool::recycle(Aircraft* pAircraft)
{
    //如果在池中,则回收;否则直接销毁
    auto it = m_aircraftMap.find(pAircraft->getAircraftLevelId());
    std::vector<Aircraft*>* pArray = nullptr;
    if (it != m_aircraftMap.end())
    {
        pArray = it->second;
        for (Aircraft* p : *pArray)
        {
            if (p == pAircraft)
            {
                p->recycle();
                return;
            }
        }
    }

    pAircraft->destory();
}

//清空pool
void AircraftPool::release()
{
    for (auto it : m_aircraftMap)
    {
        bool bInUse = false;
        std::vector<Aircraft*>* pArray = it.second;
        for (Aircraft* p : *pArray)
        {
            if (p && p->isRecycled())
            {
                p->destory();
                p->release();
            }
            else if (p)
            {
                bInUse = true;
                DEBUG_LOG("Aircraft[%d] can't release", p->getId());
                //CCASSERT(false, "Release error aircraft");
            }
        }

        if (!bInUse)
        {
            pArray->clear();
            delete pArray;
        }
    }

    m_aircraftMap.clear();
}

//////////////////////////////////////////////////////////
//下面是Aircraft对象中的回收、重用等方法
//回收
void Aircraft::recycle()
{
    if (m_bRecycled)
    {
        return;
    }

    setNoDie(false);
    //for (int i = 0; i < m_bulletGroupArray.size(); i++)
    //{
    //  m_bulletGroupArray.at(i)->setPlane(NULL);
    //}
    stopShoot();

    for (int i = 0; i < m_wingmanArray.size(); i++)
    {
        //m_wingmanArray.at(i)->recycle();
        AircraftPool::getInstance()->recycle(m_wingmanArray.at(i));
    }

    pause();

    m_bRecycled = true;
}

//重用
void Aircraft::reuse()
{
    if (!m_bRecycled)
    {
        return;
    }

    if (m_pHpBar)
    {
        m_pHpBar->setMaxValue(m_data.maxHp);
        m_pHpBar->setCurValue(m_data.maxHp);
    }
    m_data.hp = m_data.maxHp;

    //for (int i = 0; i < m_bulletGroupArray.size(); i++)
    //{
    //  m_bulletGroupArray.at(i)->setPlane(this);
    //}
    m_iCurBulletGrade = 0;

    for (int i = 0; i < m_wingmanArray.size(); i++)
    {
        m_wingmanArray.at(i)->reuse();
    }

    if (m_pArmature)
    {
        m_pArmature->setColor(Color3B::WHITE);
        m_pArmature->getAnimation()->play(GlobalData::getInstance()->getArmatureData(m_data.armatureName)->defaultAction);
    }
    else
    {
        this->setColor(Color3B::WHITE);
    }

    resume();

    m_bRecycled = false;
}

//是否已回收(是否可用)
bool Aircraft::isRecycled()
{
    return m_bRecycled;
}

  

转载请注明:http://www.cnblogs.com/thorqq/p/6323156.html

源码库地址:https://git.oschina.net/thorqq/RaidenFree

发表评论

电子邮件地址不会被公开。 必填项已用*标注