我正在使用 C++ 和 SDL 制作经典吃豆人游戏的副本,但我似乎无法解决导致吃豆人和幽灵穿过地图回到碰撞地点的问题。问题发生在两个事件上:
同样重要的是,吃豆人和幽灵似乎不会返回到它们死亡/碰撞位置的下一个方块,但是更改“nextTile”并不能解决问题。
我试图修改“Die()”函数,但它似乎没有做任何更改。我怀疑它可能是“GetPath()”函数,因为当我尝试运行
aWorld->GetPath(myCurrentTileX, myCurrentTileY, 13, 13, myPath);
游戏停止移动时,但是我没有看到它的实现有任何问题。
Die() 函数:
void Ghost::Die(World* aWorld)
{
myPath.clear();
myPosition = Vector2f(13 * 22, 13 * 22);
myDesiredMovementX = 0;
myDesiredMovementY = -1;
nextTileX = GetCurrentTileX() + myDesiredMovementX;
nextTileY = GetCurrentTileY() + myDesiredMovementY;
aWorld->GetPath(myCurrentTileX, myCurrentTileY, 13, 13, myPath);
}
GetPath() 函数:
void World::GetPath(int aFromX, int aFromY, int aToX, int aToY, std::list<PathmapTile*>& aList)
{
PathmapTile* fromTile = GetTile(aFromX, aFromY);
PathmapTile* toTile = GetTile(aToX, aToY);
for (std::list<PathmapTile*>::iterator list_iter = myPathmapTiles.begin(); list_iter != myPathmapTiles.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
tile->myIsVisitedFlag = false;
}
Pathfind(fromTile, toTile, aList);
}
如果您需要更多代码:
全鬼.cpp:
Ghost::Ghost(const Vector2f& aPosition)
: MovableGameEntity(aPosition, "ghost_32.png")
{
myIsClaimableFlag = false;
myIsDeadFlag = false;
myDesiredMovementX = 0;
myDesiredMovementY = -1;
myAvatar = std::make_unique<Avatar>(Vector2f(13 * 22, 22 * 22));
}
Ghost::~Ghost(void)
{
}
void Ghost::Die(World* aWorld)
{
myPath.clear();
myPosition = Vector2f(13 * 22, 13 * 22);
myDesiredMovementX = 0;
myDesiredMovementY = -1;
nextTileX = GetCurrentTileX() + myDesiredMovementX;
nextTileY = GetCurrentTileY() + myDesiredMovementY;
//aWorld->GetPath(myCurrentTileX, myCurrentTileY, 13, 13, myPath);
}
void Ghost::Update(float aTime, World* aWorld)
{
float speed = 30.f;
nextTileX = GetCurrentTileX() + myDesiredMovementX;
nextTileY = GetCurrentTileY() + myDesiredMovementY;
if (myIsDeadFlag)
speed = 30.f;
if (IsAtDestination())
{
if (!myPath.empty())
{
PathmapTile* nextTile = myPath.front();
myPath.pop_front();
SetNextTile(nextTile->myX, nextTile->myY);
}
else if (aWorld->TileIsValid(nextTileX, nextTileY))
{
SetNextTile(nextTileX, nextTileY);
}
else
{
//std::cout << GetCurrentTileX() << GetCurrentTileY() << std::endl;
float distanceToPacman = sqrt(pow(pacman_x - GetCurrentTileX(), 2) + pow(pacman_y - GetCurrentTileY(), 2));
std::cout << distanceToPacman << std::endl;
if (distanceToPacman <= -2)
{
std::cout << "distance between pacman and ghost <= 2"; // check if the condition is reached.
myPath.clear();
aWorld->GetPath(myCurrentTileX, myCurrentTileY, pacman_x, pacman_y, myPath);
if (!myPath.empty())
{
PathmapTile* nextTile = myPath.front();
myPath.pop_front();
SetNextTile(nextTile->myX, nextTile->myY);
}
}
else // Pac-Man is not within range, so move randomly
{
std::vector<std::pair<int, int>> possibleDirections;
if (aWorld->TileIsValid(GetCurrentTileX() + 1, GetCurrentTileY()))
{
possibleDirections.push_back(std::make_pair(1, 0));
}
if (aWorld->TileIsValid(GetCurrentTileX() - 1, GetCurrentTileY()))
{
possibleDirections.push_back(std::make_pair(-1, 0));
}
if (aWorld->TileIsValid(GetCurrentTileX(), GetCurrentTileY() + 1))
{
possibleDirections.push_back(std::make_pair(0, 1));
}
if (aWorld->TileIsValid(GetCurrentTileX(), GetCurrentTileY() - 1))
{
possibleDirections.push_back(std::make_pair(0, -1));
}
if (!possibleDirections.empty())
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(0, possibleDirections.size() - 1);
int randomIndex = dist(gen);
myDesiredMovementX = possibleDirections[randomIndex].first;
myDesiredMovementY = possibleDirections[randomIndex].second;
}
}
myIsDeadFlag = false;
}
}
int tileSize = 22;
Vector2f destination(myNextTileX * tileSize, myNextTileY * tileSize);
Vector2f direction = destination - myPosition;
float distanceToMove = aTime * speed;
if (distanceToMove > direction.Length())
{
myPosition = destination;
myCurrentTileX = myNextTileX;
myCurrentTileY = myNextTileY;
}
else
{
direction.Normalize();
myPosition += direction * distanceToMove;
}
}
void Ghost::SetImage(const char* anImage)
{
myImage = anImage;
}
void Ghost::Draw(Drawer* aDrawer)
{
if (myIsDeadFlag)
aDrawer->Draw("Ghost_Dead_32.png", (int)myPosition.myX + 220, (int)myPosition.myY + 60);
else if (myIsClaimableFlag)
aDrawer->Draw("Ghost_Vulnerable_32.png", (int)myPosition.myX + 220, (int)myPosition.myY + 60);
else
aDrawer->Draw(myImage, (int)myPosition.myX + 220, (int)myPosition.myY + 60);
}
完整的 Pacman.cpp:
std::unique_ptr<Pacman> Pacman::Create(Drawer* aDrawer)
{
std::unique_ptr<Pacman> pacman(new Pacman(aDrawer));
if (!pacman->Init())
{
return nullptr;
}
return pacman;
}
int pacman_x = 0;
int pacman_y = 0;
Pacman::Pacman(Drawer* aDrawer)
: myDrawer(aDrawer)
, myTimeToNextUpdate(0.f)
, myNextMovement(-1.f,0.f)
, myScore(0)
, myFps(0)
, myLives(3)
, myGhostGhostCounter(0.f)
{
myAvatar = std::make_unique<Avatar>(Vector2f(13 * 22, 22 * 22));
myGhost = std::make_unique<Ghost>(Vector2f(13 * 22, 13 * 22));
myWorld = std::make_unique<World>();
}
Pacman::~Pacman(void)
{
Free();//
}
bool Pacman::Init()
{
myWorld->Init();
return true;
}
//void Pacman::ResetNextMove()
//{
// myNextMovement = Vector2f(-1.f, 0.f);
// nextTileX = myAvatar->GetCurrentTileX() + myNextMovement.myX;
// nextTileY = myAvatar->GetCurrentTileY() + myNextMovement.myY;
//}
bool Pacman::Update(float aTime)
{
if (!UpdateInput())
return false;
if (CheckEndGameCondition())
{
myDrawer->DrawText("You win!", "freefont-ttf\\sfd\\FreeMono.ttf", 20, 70);
return true;
}
else if (myLives <= 0)
{
myDrawer->DrawText("You lose!", "freefont-ttf\\sfd\\FreeMono.ttf", 20, 70);
std::cout << "You lose!";
return true;
}
MoveAvatar();
myAvatar->Update(aTime);
myGhost->Update(aTime, myWorld.get());
if (myWorld->HasIntersectedDot(myAvatar->GetPosition()))
myScore += 10;
myGhostGhostCounter -= aTime;
pacman_x = myAvatar->GetPosition().myX / 22; // position of pacman for ghost.cpp
pacman_y = myAvatar->GetPosition().myY / 22;
if (myWorld->HasIntersectedBigDot(myAvatar->GetPosition()))
{
myScore += 20;
myGhostGhostCounter = 20.f;
myGhost->myIsClaimableFlag = true;
}
if (myGhostGhostCounter <= 0)
{
myGhost->myIsClaimableFlag = false;
}
if ((myGhost->GetPosition() - myAvatar->GetPosition()).Length() < 10.f)
{
if (myGhostGhostCounter <= 0.f)
{
std::cout << "lost a live";//check if the condition is reached
myLives--;
myAvatar->SetPosition(Vector2f(13*22,22*22));
myGhost->SetPosition(Vector2f(13*22,13*22));
//ResetNextMove();
}
else if (myGhost->myIsClaimableFlag && !myGhost->myIsDeadFlag)
{
myScore += 50;
myGhost->myIsDeadFlag = true;
std::cout << "check 1 - ghosts death";
myGhost->Die(myWorld.get());
std::cout << "check 2 - ghosts death";// Doesn't reach this point if 'aWorld->GetPath(myCurrentTileX, myCurrentTileY, 13, 13, myPath);' is executed
}
}
if (aTime > 0)
myFps = (int) (1 / aTime);
return true;
}
void Pacman::Free()
{
myAvatar.reset();
myGhost.reset();
myWorld.reset();
}
bool Pacman::UpdateInput()
{
const Uint8 *keystate = SDL_GetKeyboardState(NULL);
if (keystate[SDL_SCANCODE_UP])
myNextMovement = Vector2f(0.f, -1.f);
else if (keystate[SDL_SCANCODE_DOWN])
myNextMovement = Vector2f(0.f, 1.f);
else if (keystate[SDL_SCANCODE_RIGHT])
myNextMovement = Vector2f(1.f, 0.f);
else if (keystate[SDL_SCANCODE_LEFT])
myNextMovement = Vector2f(-1.f, 0.f);
if (keystate[SDL_SCANCODE_ESCAPE])
return false;
return true;
}
void Pacman::MoveAvatar()
{
nextTileX = myAvatar->GetCurrentTileX() + myNextMovement.myX;
nextTileY = myAvatar->GetCurrentTileY() + myNextMovement.myY;
if (myAvatar->IsAtDestination())
{
if (myWorld->TileIsValid(nextTileX, nextTileY))
{
myAvatar->SetNextTile(nextTileX, nextTileY);
}
}
}
bool Pacman::CheckEndGameCondition()
{
return false;
}
bool Pacman::Draw()
{
myWorld->Draw(myDrawer);
myAvatar->Draw(myDrawer);
myGhost->Draw(myDrawer);
std::string scoreString;
std::stringstream scoreStream;
scoreStream << myScore;
scoreString = scoreStream.str();
myDrawer->DrawText("Score", "freefont-ttf\\sfd\\FreeMono.ttf", 20, 50);
myDrawer->DrawText(scoreString.c_str(), "freefont-ttf\\sfd\\FreeMono.ttf", 90, 50);
std::string livesString;
std::stringstream liveStream;
liveStream << myLives;
livesString = liveStream.str();
myDrawer->DrawText("Lives", "freefont-ttf\\sfd\\FreeMono.ttf", 20, 80);
myDrawer->DrawText(livesString.c_str(), "freefont-ttf\\sfd\\FreeMono.ttf", 90, 80);
myDrawer->DrawText("FPS", "freefont-ttf\\sfd\\FreeMono.ttf", 880, 50);
std::string fpsString;
std::stringstream fpsStream;
fpsStream << myFps;
fpsString = fpsStream.str();
myDrawer->DrawText(fpsString.c_str(), "freefont-ttf\\sfd\\FreeMono.ttf", 930, 50);
return true;
}
World.cpp(其中 GetPath 和 Pathfind 是:
World::World(void)
{
}
World::~World(void)
{
}
void World::Init()
{
InitPathmap();
InitDots();
InitBigDots();
}
bool World::InitPathmap()
{
std::string line;
std::ifstream myfile("map.txt");
if (myfile.is_open())
{
int lineIndex = 0;
while (!myfile.eof())
{
std::getline(myfile, line);
for (unsigned int i = 0; i < line.length(); i++)
{
PathmapTile* tile = new PathmapTile(i, lineIndex, (line[i] == 'x'));
myPathmapTiles.push_back(tile);
}
lineIndex++;
}
myfile.close();
}
return true;
}
bool World::InitDots()
{
std::string line;
std::ifstream myfile("map.txt");
if (myfile.is_open())
{
int lineIndex = 0;
while (!myfile.eof())
{
std::getline(myfile, line);
for (unsigned int i = 0; i < line.length(); i++)
{
if (line[i] == '.')
{
Dot* dot = new Dot(Vector2f(i * 22, lineIndex * 22));
myDots.push_back(dot);
}
}
lineIndex++;
}
myfile.close();
}
return true;
}
bool World::InitBigDots()
{
std::string line;
std::ifstream myfile("map.txt");
if (myfile.is_open())
{
int lineIndex = 0;
while (!myfile.eof())
{
std::getline(myfile, line);
for (unsigned int i = 0; i < line.length(); i++)
{
if (line[i] == 'o')
{
BigDot* dot = new BigDot(Vector2f(i * 22, lineIndex * 22));
myBigDots.push_back(dot);
}
}
lineIndex++;
}
myfile.close();
}
return true;
}
void World::Draw(Drawer* aDrawer)
{
aDrawer->Draw("playfield.png");
for (std::list<Dot*>::iterator list_iter = myDots.begin(); list_iter != myDots.end(); list_iter++)
{
Dot* dot = *list_iter;
dot->Draw(aDrawer);
}
for (std::list<BigDot*>::iterator list_iter = myBigDots.begin(); list_iter != myBigDots.end(); list_iter++)
{
BigDot* dot = *list_iter;
dot->Draw(aDrawer);
}
}
bool World::TileIsValid(int anX, int anY)
{
for (std::list<PathmapTile*>::iterator list_iter = myPathmapTiles.begin(); list_iter != myPathmapTiles.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
if (anX == tile->myX && anY == tile->myY && !tile->myIsBlockingFlag)
return true;
}
return false;
}
bool World::HasIntersectedDot(const Vector2f& aPosition)
{
for (std::list<Dot*>::iterator list_iter = myDots.begin(); list_iter != myDots.end(); list_iter++)
{
Dot* dot = *list_iter;
if ((dot->GetPosition() - aPosition).Length() < 5.f)
{
myDots.remove(dot);
delete dot;
return true;
}
}
return false;
}
bool World::HasIntersectedBigDot(const Vector2f& aPosition)
{
for (std::list<BigDot*>::iterator list_iter = myBigDots.begin(); list_iter != myBigDots.end(); list_iter++)
{
BigDot* dot = *list_iter;
if ((dot->GetPosition() - aPosition).Length() < 5.f)
{
myBigDots.remove(dot);
delete dot;
return true;
}
}
return false;
}
bool World::HasIntersectedCherry(const Vector2f& aPosition)
{
return true;
}
**void World::GetPath**(int aFromX, int aFromY, int aToX, int aToY, std::list<PathmapTile*>& aList)
{
PathmapTile* fromTile = GetTile(aFromX, aFromY);
PathmapTile* toTile = GetTile(aToX, aToY);
for (std::list<PathmapTile*>::iterator list_iter = myPathmapTiles.begin(); list_iter != myPathmapTiles.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
tile->myIsVisitedFlag = false;
}
Pathfind(fromTile, toTile, aList);
}
PathmapTile* World::GetTile(int aFromX, int aFromY)
{
for (std::list<PathmapTile*>::iterator list_iter = myPathmapTiles.begin(); list_iter != myPathmapTiles.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
if (tile->myX == aFromX && tile->myY == aFromY)
{
return tile;
}
}
return NULL;
}
bool World::ListDoesNotContain(PathmapTile* aFromTile, std::list<PathmapTile*>& aList)
{
for (std::list<PathmapTile*>::iterator list_iter = aList.begin(); list_iter != aList.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
if (tile == aFromTile)
{
return false;
}
}
return true;
}
bool SortFromGhostSpawn(PathmapTile* a, PathmapTile* b)
{
int la = abs(a->myX - 13) + abs(a->myY - 13);
int lb = abs(b->myX - 13) + abs(b->myY - 13);
return la < lb;
}
**bool World::Pathfind**(PathmapTile* aFromTile, PathmapTile* aToTile, std::list<PathmapTile*>& aList)
{
std::priority_queue<AStarNode> open_list;
std::unordered_set<PathmapTile*> closed_set;
// create start node
AStarNode start_node(aFromTile, nullptr, 0, ManhattanDistance(aFromTile, aToTile));
open_list.push(start_node);
while (!open_list.empty())
{
// get node with lowest f value from open list
AStarNode current_node = open_list.top();
open_list.pop();
// check if goal has been reached
if (current_node.tile == aToTile)
{
// construct path by following parent pointers
while (current_node.parent != nullptr)
{
aList.push_front(current_node.tile);
current_node = *current_node.parent;
}
aList.push_front(aFromTile);
return true;
}
// add current node to closed set
closed_set.insert(current_node.tile);
// generate neighbor nodes
std::list<PathmapTile*> neighbor_list;
PathmapTile* up = GetTile(current_node.tile->myX, current_node.tile->myY - 1);
if (up && !up->myIsBlockingFlag && !closed_set.count(up))
neighbor_list.push_back(up);
PathmapTile* down = GetTile(current_node.tile->myX, current_node.tile->myY + 1);
if (down && !down->myIsBlockingFlag && !closed_set.count(down))
neighbor_list.push_back(down);
PathmapTile* right = GetTile(current_node.tile->myX + 1, current_node.tile->myY);
if (right && !right->myIsBlockingFlag && !closed_set.count(right))
neighbor_list.push_back(right);
PathmapTile* left = GetTile(current_node.tile->myX - 1, current_node.tile->myY);
if (left && !left->myIsBlockingFlag && !closed_set.count(left))
neighbor_list.push_back(left);
// add neighbor nodes to open list
for (PathmapTile* neighbor : neighbor_list)
{
float g_cost = current_node.g + 1; // assuming uniform movement cost
float h_cost = ManhattanDistance(neighbor, aToTile);
AStarNode neighbor_node(neighbor, ¤t_node, g_cost, h_cost);
open_list.push(neighbor_node);
}
}
// no path found
return false;
}