2012-06-10 4 views
0

Я работаю над простой 2D-игрой Zelda в стиле С-2, но у меня возникают проблемы с появлением нескольких экземпляров вражеского класса. Когда я появляюсь больше, чем один из врагов, только первый регистрирует любое обнаружение столкновения; все остальные враги кажутся просто визуальными «призраками», которые отображаются на экране. Когда первый враг умрет, единственный, который может, тогда все остальные «призраки» исчезают вместе с ним.Enemy Ghost Instances/Неповторимые объекты

Я создал класс вражеского менеджера, который использует векторный список, чтобы удерживать активных врагов, проверять каждое столкновение против любого переданного ящика и обновлять/отображать врагов.

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 и motion, чтобы он мог сканировать вокруг экрана, чтобы попытаться столкнуться с аватаром игрока, а также отреагировать на атаку игрока. Все это прекрасно работает, просто, когда я пытаюсь иметь нескольких врагов, только первый из них создается правильно, а остальные - пустые оболочки, просто текстуры на экране. Не имеет значения, быстро ли я вызываю явные вызовы на 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"); 
} 

Как вы можете сказать, что я не то, что испытал еще , Это мой первый проект для школы. Я просто должен сказать, что я немного смущен тем, где я должен закрывать текстуры и удалять объекты. Спасибо за ваше время, ребята!

+0

Нам нужно увидеть код для объектов Enemy. Кроме того, вы, кажется, просачиваете память, если 'die' не приводит к разрушению объекта. – tmpearce

+0

Пожалуйста, отправьте код, который проверит класс checkCollisions() –

+0

Вы правы, я не уничтожаю каждый экземпляр после его создания. Die() просто меняет соответствующие очки очков и пороговые значения толпы в настоящее время. Я делаю это, используя что-то вроде delete mobList [i] в ​​инструкции if, где hp <= 0? – DanTheMan

ответ

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; 
} 

Надеется, что это помогает!

EDIT:

Обратите внимание, что когда вы удаляете враг из списка, если это 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; 
    } 
}); 
+0

Он может вызывать CheckCollisions, удаляя соответствующий призрак, а затем вызывает его снова. Это может лишь частично решить проблему. Но хороший улов. –

+0

Это частично устраняет проблему! Я могу ударить и ударить по всем экземплярам, ​​но похоже, что враг, который уничтожается, принимает все, что создано после него вместе с ним. – DanTheMan

+0

@DanTheMan Нам нужно будет увидеть ваш код вызова, чтобы потом помочь. –

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); 
      } 
     } 
    } 

Благодарим всех вас за помощь, это очень ценно!