0

C ++でシンプルな2Dトップダウンゼルダスタイルのゲームに取り組んでいますが、敵クラスの複数のインスタンスをスポーンさせるのに問題があります。複数の敵をスポーンするときは常に、最初の1つだけが登録します。衝突検出; 他のすべての敵は、画面に表示される単なる視覚的な「幽霊」のようです。最初の敵が死ぬと、それができる唯一の敵、そして他のすべての「幽霊」が一緒に消えます。

ベクトルリストを使用してアクティブな敵を保持し、渡されたボックスとの衝突をチェックし、敵を更新/レンダリングする敵マネージャークラスを作成しました。

class cEnemyMgr {
public:
std::vector<cEnemy*> mobList;

cEnemyMgr(){}
~cEnemyMgr(){
    for (int i=0; i < mobList.size(); i++) {
        mobList[i]->texture.Close();
            //delete mobList[i];
    } 
}

    void render() {
        for (int i=0; i < mobList.size(); i++) {
            mobList[i]->render();
        }
    }

    void update(float dt){
        for (int i=0; i < mobList.size(); i++) {
            if ( mobList[i]->hp <= 0 ){
                mobList[i]->die();
                mobList.pop_back();
            } else {
                mobList[i]->update(dt);
            }
        }
    }

    void spawnMob(int x, int y){
        cEnemy* pEnemy = new cMeleeEnemy();
        pEnemy->init(x, y);
        mobList.push_back(pEnemy);
    }

    cEnemy* checkCollisions(int x, int y, int wd, int ht){
        for (int i=0; i < mobList.size(); i++) {
            int left1, left2;
            int right1, right2;
            int top1, top2;
            int bottom1, bottom2;

            left1 = x;
            right1 = x + wd;
            top1 = y;
            bottom1 = y + ht;

            left2 = mobList[i]->pos.x;
            right2 = mobList[i]->pos.x + 64;
            top2 = mobList[i]->pos.y;       
            bottom2 = mobList[i]->pos.y + 64;

            if ( bottom1 < top2 ) { return NULL; }
            if ( top1 > bottom2 ) { return NULL; }
            if ( left1 > right2 ) { return NULL; }
            if ( right1 < left2 ) { return NULL; }

            return mobList[i];
        }
    }
};

敵のクラス自体はかなり基本的です。cEnemyは、cMeleeEnemyの派生元である基本クラスです。標準のhp、dmg、および移動変数を備えているため、画面を這い回ってプレーヤーのアバターと衝突したり、プレーヤーからの攻撃に応答したりできます。これはすべて正常に機能します。複数の敵を配置しようとすると、最初の1つだけが正しく機能し、残りは空のシェルで、画面上のテクスチャだけが機能します。同じブロック内でspawnMobを明示的に呼び出すか、タイマーを使用して動的に間隔を空けるかは関係ありません。結果は同じです。誰かが私を正しい方向に向けることができますか?

--EDIT--これがenemy.hのコードです:

#ifndef ENEMY_H
#define ENEMY_H

#include "texture.h"
#include "timer.h"

#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)

class cEnemy {
public:
int hp;
int dmg;
D3DXVECTOR2 pos;
D3DXVECTOR2 fwd;
D3DXVECTOR2 vel;
D3DCOLOR color;
int speed;
float rotate;
bool hitStun;
float hitTime;
CTexture texture;

virtual void init(int x, int y) = 0;
virtual void update(float dt) = 0;
virtual void die() = 0;

void render(){
    texture.Blit(pos.x, pos.y, color, rotate);
}

void takeDamage(int dmg) {
    if (hitStun == false){
        extern CTimer Timer;        
        hitTime = Timer.GetElapsedTime();
        hp -= dmg;
        color = 0xFFFF0000;
        hitStun = true;
    }
}

void hitStunned(float duration) {
    extern CTimer Timer;
    float elapsedTime = Timer.GetElapsedTime();
    if ( elapsedTime - hitTime > duration ){
        color = 0xFFFFFFFF;
        hitStun = false;
    }
}

};

class cPlayer : public cEnemy {
public:
int facing;

void init(int x, int y);
void update(float dt);
void die();

};

class cMeleeEnemy : public cEnemy {
public:
cMeleeEnemy(){}
~cMeleeEnemy(){
    texture.Close();
}

void init(int x, int y);
void update(float dt);
void die();

};
#endif

そしてenemy.cpp:

#include "enemy.h"

void cPlayer::update(float dt){
// Player Controls
if ( KEY_DOWN('W') ) {
    pos.y -= speed * dt;
    facing = 0;
} else if( KEY_DOWN('S') ) {
    pos.y += speed * dt;
    facing = 2;
}

if ( KEY_DOWN('A') ) {
    pos.x -= speed * dt;
    facing = 3;
} else if( KEY_DOWN('D') ) {
    pos.x += speed * dt;
    facing = 1;
}

// Hit Recovery
if ( hitStun == true ) {    
    hitStunned(1.0);
}
}

void cMeleeEnemy::update(float dt){
extern cPlayer player1;
extern int ScreenWd;
extern int ScreenHt;

D3DXVECTOR2 dir;
dir = player1.pos - pos;
D3DXVec2Normalize(&dir, &dir);
//fwd = (fwd * 0.2) + (dir * 0.8);
fwd = dir;
vel = vel + fwd * speed * dt;

pos = pos + vel * dt;

//keep em on screen
if ( pos.x < 0 ) { pos.x = 0; }
if ( pos.x > ScreenWd - 64 ) { pos.x = ScreenWd - 64; }
if ( pos.y < 0 ) { pos.y = 0; }
if ( pos.y > ScreenHt - 64 ) { pos.y = ScreenHt - 64; }

// Hit Recovery
if ( hitStun == true ) {    
    hitStunned(0.5);
}

}

void cMeleeEnemy::die(){
extern int score;
extern int numMobs;
score += 1;
numMobs -= 1;
//texture.Close();
}

void cPlayer::die(){
extern char gameState[256];
sprintf(gameState, "GAMEOVER");
}

void cMeleeEnemy::init(int x, int y){
hp = 6;
dmg = 1;
speed = 25;
fwd.x = 1;
fwd.y = 1;
vel.x = 0;
vel.y = 0;
pos.x = x;
pos.y = y;
rotate = 0.0;
color = 0xFFFFFFFF;
hitStun = false;
texture.Init("media/vader.bmp");
}

void cPlayer::init(int x, int y){
facing = 0;
hp = 10;
dmg = 2;
color = 0xFFFFFFFF;
speed = 100;
fwd.x = 1;
fwd.y = 1;
vel.x = 0;
vel.y = 0;
pos.x = x;
pos.y = y;
rotate = 0.0;
hitStun = false;
texture.Init("media/ben.bmp");
}

お分かりのように、私はまだそれほど経験がありません。これは私の学校での最初の独自のプロジェクトです。テクスチャを閉じてオブジェクトを削除する場所について少し混乱していると言わざるを得ません。お時間をいただきありがとうございます。

4

2 に答える 2

2

checkCollisions関数ではNULL、ループごとに、、または敵ベクトルの最初のインデックスの位置にあるオブジェクトを返します。

したがって、最初のゴーストがヒットされない場合、checkCollisions関数はNULL、ベクトル内の後続の各ゴーストを反復処理する代わりに戻ります。

これを修正するには、checkCollisions関数を次のように変更します。

cEnemy* checkCollisions(int x, int y, int wd, int ht){
    for (int i=0; i < mobList.size(); i++) {
        int left1, left2;
        int right1, right2;
        int top1, top2;
        int bottom1, bottom2;

        left1 = x;
        right1 = x + wd;
        top1 = y;
        bottom1 = y + ht;

        left2 = mobList[i]->pos.x;
        right2 = mobList[i]->pos.x + 64;
        top2 = mobList[i]->pos.y;       
        bottom2 = mobList[i]->pos.y + 64;

        if ( bottom1 < top2 ) { continue; }
        if ( top1 > bottom2 ) { continue; }
        if ( left1 > right2 ) { continue; }
        if ( right1 < left2 ) { continue; }

        return mobList[i];
    }

    return NULL;
}

お役に立てれば!

編集:

HPが0以下の場合にリストから敵を削除するときは、を使用していることに注意してくださいmobList.pop_back()。ただし、これによりベクトルから最後の要素が削除されます。次のようなものを使用して、リストから目的の敵を削除する必要があります。

std::remove_if( mobList.begin(), mobList.end() []( cEnemy* pEnemy )->bool
{
     if( pEnemy->hp <= 0 )
     {
         pEnemy->die();
         return true;
     }
     else
     {
         pEnemy->update();
         return false;
     }
});
于 2012-06-10T21:14:34.007 に答える
0

問題が解決しました!pop_back()mobList.erase()メソッドに置き換えました。

void update(float dt){
        for (int i=0; i < mobList.size(); i++) {
            if ( mobList[i]->hp <= 0 ){
                mobList[i]->die();
                mobList.erase(mobList.begin() + i);
            } else {
                mobList[i]->update(dt);
            }
        }
    }

皆様のご協力、誠にありがとうございました!

于 2012-06-11T19:34:35.543 に答える