So verwalten Sie den vertikalen Versatz für Geister, die zwischen den Kacheln im Pac-Man-Geisterhaus hin- und herpendelnC++

Programme in C++. Entwicklerforum
Anonymous
 So verwalten Sie den vertikalen Versatz für Geister, die zwischen den Kacheln im Pac-Man-Geisterhaus hin- und herpendeln

Post by Anonymous »

Ich implementieren Geisterverhalten in einem Pac-Man-Klon, insbesondere das Verhalten, wenn sich Geister während der Wartephase im Geisterhaus aufhalten.
Das Problem besteht darin, dass sich Geister im Geisterhaus nicht im Standardkachelraster bewegen. Stattdessen oszillieren sie vertikal zwischen der Mitte zweier Kacheln, was einen vertikalen Versatz von etwa 8 Pixeln (eine halbe Kachel) zu ihrer Y-Position erfordert. Dieser Versatz ist notwendig, da die Tür des Geisterhauses so positioniert ist, dass die Geister sich scheinbar innerhalb des Raums zwischen zwei Kacheln bewegen.
Ich habe jedoch Schwierigkeiten, die korrekte Positionierung während der Funktion „wait()“ beizubehalten. Wenn ich den 8-Pixel-Vertikalversatz anwende, um die aktuelle Kachelposition zu berechnen und den Geist nach Abschluss eines Bewegungszyklus wieder an seine beabsichtigte Position zu bringen, driften die Geister entweder aus ihren richtigen Grenzen oder kollidieren mit Wänden.

Code: Select all

#include "../include/Ghost.h"
#include "../include/TextureManager.h"
#include "../include/Map.h"
#include "../include/Direction.h"
#include "../include/GhostManager.h"
#include "../include/GameRules.h"
#include 
#include 
#include 
#include 

Ghost::Ghost(float initX, float initY, int w, int h)
: initialTileX(initX), initialTileY(initY), w(w), h(h), pixelsMoved(0.0f),
posX(initX * 16 + 8 - w / 2), posY(initY * 16 + 3*16 + 8 - h/2),
speed(GameRules::GHOST_SPEED_NORMAL)
{
endOfFrightening = false;
canGotoGhostHouse = true;
readyToExit = false;
ghostEaten = false;
state = WAIT;
rect = {static_cast(posX) - 8, static_cast(posY) + 8, w, h};
updateHitbox();
targetTexture = nullptr;
currentTile = {(int)initX, (int)initY};
targetTile = currentTile;
currentDirection = STOP;
scoreTexture200 = nullptr;
scoreTexture400 = nullptr;
scoreTexture800 = nullptr;
scoreTexture1600 = nullptr;
showingScore = false;
scoreDisplayDuration = 1000;
currentScoreValue = 0;
}

//---------------- Score Handling ----------------

void Ghost::loadScoreTextures(TextureManager* textureManager,
const std::string& score200Path,
const std::string& score400Path,
const std::string& score800Path,
const std::string& score1600Path) {
scoreTexture200 = textureManager->loadTexture(score200Path);
scoreTexture400 = textureManager->loadTexture(score400Path);
scoreTexture800 = textureManager->loadTexture(score800Path);
scoreTexture1600 = textureManager->loadTexture(score1600Path);
}

void Ghost::startShowingScore(int score) {
showingScore = true;
scoreBeingDisplayed = true;
scoreDisplayStartTime = SDL_GetTicks();
currentScoreValue = score;
}

void Ghost::updateScoreDisplay() {
if(showingScore) {
Uint32 now = SDL_GetTicks();
if(now - scoreDisplayStartTime >= scoreDisplayDuration) {
showingScore = false;
scoreBeingDisplayed = false;
setState(EATEN);
}
}
}

void Ghost::renderScore(SDL_Renderer* renderer) {
if(!showingScore) return;
SDL_Texture* scoreTex = nullptr;
switch(currentScoreValue) {
case 200: scoreTex = scoreTexture200; break;
case 400: scoreTex = scoreTexture400; break;
case 800: scoreTex = scoreTexture800; break;
case 1600: scoreTex = scoreTexture1600; break;
}
if(scoreTex) {
SDL_Rect dstRect = rect;
SDL_RenderCopy(renderer, scoreTex, nullptr, &dstRect);
}
}

//---------------- State / Reset ----------------

bool Ghost::ghostInGhostHouse() {
const SDL_Rect ghostHouseTiles = {11, 16, 8, 5};
const int tileSize = 16;
SDL_Rect ghostHouse = {
ghostHouseTiles.x * tileSize,
ghostHouseTiles.y * tileSize,
ghostHouseTiles.w * tileSize,
ghostHouseTiles.h * tileSize
};
SDL_Rect ghostRect = rect;
return (ghostRect.x + ghostRect.w > ghostHouse.x &&
ghostRect.x < ghostHouse.x + ghostHouse.w &&
ghostRect.y + ghostRect.h > ghostHouse.y &&
ghostRect.y <  ghostHouse.y + ghostHouse.h);
}

bool Ghost::checkCollisionWithPacman(Pacman* pacman) {
SDL_Rect pacHitbox = pacman->getHitbox();
if(SDL_HasIntersection(&hitbox, &pacHitbox)) {
if(state == FRIGHTENED) {
ghostEaten = true;
return true;
} else if(state == CHASE || state == SCATTER) {
pacman->isAlive = false;
}
return true;
}
return false;
}

void Ghost::reset() {
endOfFrightening = false;
canGotoGhostHouse = true;
readyToExit = false;
ghostEaten = false;
state = WAIT;
pixelsMoved = 0.0f;
currentDirection = STOP;
speed = GameRules::GHOST_SPEED_NORMAL;
posX = initialTileX * 16 + 8 - w/2;
posY = initialTileY * 16 + 3*16 + 8 - h/2;
rect = {static_cast(posX) - 8, static_cast(posY) + 8, w, h};
updateHitbox();
currentTile = {(int)initialTileX, (int)initialTileY};
targetTile = currentTile;
}

//---------------- Wait State ----------------

void Ghost::wait() {
const float tileSize = 16.0f;
const int offset = 8; // وسط تایل

// تعیین جهت اولیه
if(currentDirection == STOP) currentDirection = UP;

// حرکت واقعی
switch(currentDirection) {
case UP:
posY -= speed;
currentEye = eyeUp;
break;
case DOWN:
posY += speed;
currentEye = eyeDown;
break;
default:
break;
}

pixelsMoved += speed;

if(pixelsMoved >= tileSize) {
pixelsMoved = 0.0f;
currentDirection = (currentDirection == UP) ? DOWN : UP;

// تراز کردن در وسط تایل
currentTile.x = static_cast((posX + offset) / tileSize);
currentTile.y = static_cast((posY + offset) / tileSize);

posX = currentTile.x * tileSize + offset - w / 2;
posY = currentTile.y * tileSize + offset - h / 2;
}

rect.x = static_cast(posX);
rect.y = static_cast(posY);
updateHitbox();
}

//---------------- Update ----------------

void Ghost::update(const Map& map) {
if(frozen) return;

switch(state) {
case WAIT:
wait();
if(readyToExit) state = EXIT;
break;
case EXIT:
speed = GameRules::GHOST_SPEED_NORMAL;
getOutOfHouse(map);
canGotoGhostHouse = true;
break;
case CHASE:
case SCATTER:
speed = GameRules::GHOST_SPEED_NORMAL;
canGotoGhostHouse = false;
updateChaseScatter(map);
break;
case FRIGHTENED:
speed = GameRules::GHOST_SPEED_FRIGHTENED;
canGotoGhostHouse = false;
updateFrightened(map);
break;
case EATEN:
speed = GameRules::GHOST_SPEED_EATEN;
canGotoGhostHouse = true;
updateChaseScatter(map);
if(currentTile.x == 13 && currentTile.y == 14) {
speed = 1.0f;
setMode(EXIT);
}
break;
}
updateBodyAnimation();
}

//---------------- Movement ----------------

void Ghost::getOutOfHouse(const Map& map) {
const float tileSize = 16.0f;
const int mapWidthTiles = 28;
const int mapHeightTiles = 31;
const int exitTileX = 14;
const int exitTileY = 11;
const int offsetX = -8;

if(pixelsMoved == 0.0f) {
int rawTileX = static_cast((posX + 8 - offsetX) / tileSize);
currentTile.x = ((rawTileX % mapWidthTiles) + mapWidthTiles) % mapWidthTiles;
currentTile.y = static_cast((posY + 8 - 3 * tileSize) / tileSize);

Direction nextDir = STOP;
if(currentTile.x < exitTileX) nextDir = RIGHT;
else if(currentTile.x > exitTileX) nextDir = LEFT;
else if(currentTile.y >  exitTileY) nextDir = UP;
else if(currentTile.y == exitTileY) {
state = SCATTER;
pixelsMoved = 0.0f;
currentDirection = LEFT;
currentEye = eyeLeft;
return;
}
currentDirection = nextDir;
}

if(currentDirection != STOP) {
switch(currentDirection) {
case UP: posY -= speed; currentEye = eyeUp; break;
case DOWN: posY += speed; currentEye = eyeDown; break;
case LEFT: posX -= speed; currentEye = eyeLeft; break;
case RIGHT: posX += speed; currentEye = eyeRight; break;
default: break;
}
pixelsMoved += speed;
}

if(pixelsMoved >= tileSize) {
pixelsMoved = 0.0f;
currentTile.x = static_cast((posX + 8 - offsetX) / tileSize);
currentTile.y = static_cast((posY + 8 - 3*tileSize) / tileSize);
posX = currentTile.x * tileSize + 8 - w/2 + offsetX;
posY = currentTile.y * tileSize + 3*tileSize + 8 - h/2;
}

rect.x = static_cast(posX);
rect.y = static_cast(posY);
updateHitbox();
}

void Ghost::updateChaseScatter(const Map& map) {
const int mapWidthTiles = 28;
const int mapHeightTiles = 31;
const float tileSize = 16.0f;
const int tunnelRow = 14;

if(pixelsMoved == 0.0f) {
currentTile.x = static_cast((posX + 8) / tileSize) % mapWidthTiles;
currentTile.y = static_cast((posY + 8 - 3*tileSize) / tileSize);

int adjustedTargetX = targetTile.x;
int adjustedTargetY = targetTile.y;

bool targetInTunnel = (targetTile.y == tunnelRow) &&
(targetTile.x == 0 || targetTile.x == mapWidthTiles - 1);
if(targetInTunnel) adjustedTargetX = (targetTile.x == 0) ? mapWidthTiles - 1 : 0;

Direction reverseDir;
switch(currentDirection) {
case UP: reverseDir = DOWN; break;
case DOWN: reverseDir = UP; break;
case LEFT: reverseDir = RIGHT; break;
case RIGHT: reverseDir = LEFT; break;
default: reverseDir = STOP; break;
}

const SDL_Point dirs[4] = { {0,-1}, {-1,0}, {0,1}, {1,0} };
Direction dirMap[4] = { UP, LEFT, DOWN, RIGHT };
Direction bestDir = STOP;
int bestDist = INT_MAX;

for(int i=0;i= mapWidthTiles) actualNx = 0;
isValidTile = map.isWalkable(actualNx, ny);
} else {
if(nx>=0 && nx=0 && ny0)? -wrapDist : wrapDist;
}

int dist = dx*dx + dy*dy;
if(dist < bestDist) {
bestDist = dist;
bestDir = dir;
}
}
if(bestDir == STOP && reverseDir != STOP) bestDir = reverseDir;
currentDirection = bestDir;
}

if(currentDirection != STOP) {
switch(currentDirection) {
case UP: posY -= speed; currentEye = eyeUp; break;
case DOWN: posY += speed; currentEye = eyeDown; break;
case LEFT: posX -= speed; currentEye = eyeLeft; break;
case RIGHT: posX += speed; currentEye = eyeRight;  break;
}
pixelsMoved += speed;
}

if(pixelsMoved >= tileSize) {
pixelsMoved = 0.0f;
currentTile.x = static_cast((posX + 8) / tileSize);
currentTile.y = static_cast((posY + 8 - 3*tileSize) / tileSize);
posX = currentTile.x * tileSize + 8 - w/2;
posY = currentTile.y * tileSize + 3*tileSize + 8 - h/2;
}

int mapWidth = mapWidthTiles * static_cast(tileSize);
if(posX + w/2 < 0) posX = mapWidth - w/2;
else if(posX + w/2 >= mapWidth) posX = -w/2;

rect.x = static_cast(posX);
rect.y = static_cast(posY);
updateHitbox();
}

//---------------- Update Frightened ----------------
void Ghost::updateFrightened(const Map& map) {
const int mapWidthTiles = 28;
const int mapHeightTiles = 31;
const float tileSize = 16.0f;
const int tunnelRow = 14;

if(pixelsMoved == 0.0f) {
currentTile.x = static_cast((posX + 8) / tileSize);
currentTile.y = static_cast((posY + 8 - 3*tileSize) / tileSize);

Direction reverseDir;
switch(currentDirection) {
case UP: reverseDir = DOWN; break;
case DOWN: reverseDir = UP; break;
case LEFT: reverseDir = RIGHT; break;
case RIGHT: reverseDir = LEFT; break;
default: reverseDir = STOP; break;
}

std::vector possibleDirs;
const SDL_Point dirs[4] = { {0,-1}, {-1,0}, {0,1}, {1,0} };
Direction dirMap[4] = { UP, LEFT, DOWN, RIGHT };

for(int i=0;i=0 && nx=0 && ny= tileSize) {
pixelsMoved = 0.0f;
currentTile.x = static_cast((posX + 8) / tileSize);
currentTile.y = static_cast((posY + 8 - 3*tileSize) / tileSize);
posX = currentTile.x*tileSize + 8 - w/2;
posY = currentTile.y*tileSize + 3*tileSize + 8 - h/2;
}

int mapWidth = mapWidthTiles * static_cast(tileSize);
if(posX + w/2 < 0) posX = mapWidth - w/2;
else if(posX + w/2 >= mapWidth) posX = -w/2;

rect.x = static_cast(posX);
rect.y = static_cast(posY);
updateHitbox();
}

//---------------- Render ----------------
void Ghost::render(SDL_Renderer* renderer) {
if(showingScore) { renderScore(renderer); return; }

SDL_Texture* texToRender = (bodyFrame==0)? bodyTex1: bodyTex2;
SDL_Texture* frightenedToRender = (bodyFrame==0)? frightenedTex: frightenedTex2;
SDL_Texture* endFrightenedToRender = (bodyFrame==0)? endFrightened: endFrightened2;
SDL_Texture* endFrightToRender = (bodyFrame==0)? frightenedToRender:endFrightenedToRender;

if(getState() != FRIGHTENED &&  getState() != EATEN) {
if(texToRender) SDL_RenderCopy(renderer, texToRender, nullptr, &rect);
if(currentEye) SDL_RenderCopy(renderer, currentEye, nullptr, &rect);
}
if(getState() == FRIGHTENED) {
if(frightenedToRender) SDL_RenderCopy(renderer, frightenedToRender, nullptr, &rect);
if(endOfFrightening) SDL_RenderCopy(renderer, endFrightToRender, nullptr, &rect);
}
if(getState() == EATEN) {
if(currentEye) SDL_RenderCopy(renderer, currentEye, nullptr, &rect);
}
renderTarget(renderer);
}

//---------------- Load Textures ----------------
bool Ghost::loadTextures(TextureManager* texManager, const std::vector& paths) {
if(paths.size() < 10) return false;

eyeUp    = texManager->loadTexture(paths[0]);
eyeDown  = texManager->loadTexture(paths[1]);
eyeLeft  = texManager->loadTexture(paths[2]);
eyeRight = texManager->loadTexture(paths[3]);
bodyTex1 = texManager->loadTexture(paths[4]);
bodyTex2 = texManager->loadTexture(paths[5]);
frightenedTex = texManager->loadTexture(paths[6]);
frightenedTex2 = texManager->loadTexture(paths[7]);
endFrightened = texManager->loadTexture(paths[8]);
endFrightened2 = texManager->loadTexture(paths[9]);
currentEye = eyeDown;

if(paths.size()>=14){
scoreTexture200 = texManager->loadTexture(paths[10]);
scoreTexture400 = texManager->loadTexture(paths[11]);
scoreTexture800 = texManager->loadTexture(paths[12]);
scoreTexture1600 = texManager->loadTexture(paths[13]);
}

bool mainLoaded = eyeUp && eyeDown && eyeLeft && eyeRight &&
bodyTex1 && bodyTex2 &&
frightenedTex && frightenedTex2 &&
endFrightened && endFrightened2;

if(paths.size()>=14){
return mainLoaded && scoreTexture200 && scoreTexture400 &&
scoreTexture800 && scoreTexture1600;
}
return mainLoaded;
}
// -------------------- Target Handling --------------------
bool Ghost::loadTargetTexture(SDL_Renderer* renderer, const std::string& path) {
if(targetTexture) SDL_DestroyTexture(targetTexture);
targetTexture = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP(path.c_str()));
return targetTexture != nullptr;
}

void Ghost::renderTarget(SDL_Renderer* renderer) {
if(targetTexture) SDL_RenderCopy(renderer, targetTexture, nullptr, &rect);
}

void Ghost::clearTargetTexture() {
if(targetTexture) {
SDL_DestroyTexture(targetTexture);
targetTexture = nullptr;
}
}

// -------------------- State & Mode --------------------
void Ghost::setState(GhostState ghostState) {
state = ghostState;
updateHitbox();
}

void Ghost::setMode(GhostState mode) {
state = mode;
}

// -------------------- Hitbox --------------------
void Ghost::updateHitbox() {
hitbox.x = static_cast(posX);
hitbox.y = static_cast(posY);
hitbox.w = w;
hitbox.h = h;
}

SDL_Rect* Ghost::getHitBox() { return &hitbox; }

// -------------------- Position / Target --------------------
void Ghost::setPosition(int x, int y) {
posX = static_cast(x);
posY = static_cast(y);
rect.x = x;
rect.y = y;
updateHitbox();
}

SDL_Point Ghost::getCurrentTile() const { return currentTile; }

void Ghost::setTarget(const SDL_Rect& targetRect) {
targetTile.x = targetRect.x / 16;
targetTile.y = targetRect.y / 16;
}

void Ghost::setTargetTile(int tileX, int tileY) {
targetTile.x = tileX;
targetTile.y = tileY;
}

void Ghost::updateBodyAnimation() {
frameCounter++;
if(frameCounter >= frameSpeed) {
frameCounter = 0;
bodyFrame = (bodyFrame + 1) % 2;
}
bodyTex = (bodyFrame == 0) ? bodyTex1 : bodyTex2;
}
Geben Sie hier eine Bildbeschreibung ein

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post