<think>根据您的要求,我们需要解决两个问题:1.恢复碰到星星就自由落体的功能(目前失效)2.将星星往上移一点分析可能的原因:1.自由落体失效:可能是碰撞检测条件不满足,导致`checkStarCollision`返回false。在您的代码中,星星的位置计算为:floatstarX= lastLand.x+ scene.im_star.getwidth();//注意这里加了星星的宽度,可能是问题所在floatstarY= lastLand.top_y -scene.im_star.getheight();而碰撞检测时,玩家与星星的碰撞条件为:playerRight >starX&& playerLeft <starX+ starWidth注意:starX是星星的左上角x坐标,而starWidth是星星的宽度,所以星星的右边界是starX+starWidth。但是,这里starX的计算方式为:lastLand.x +scene.im_star.getwidth(),这会导致星星被绘制在lastLand.x的右侧一个星星宽度的位置,而碰撞检测区域也是从这个位置开始。这可能是导致碰撞检测失败的原因。2.星星往上移:我们可以通过调整星星的y坐标来实现,例如在原来的基础上再减去一定值。解决方案:1.修改星星的位置计算:星星应该绘制在最后一块地面的中心位置上方,所以:starX= lastLand.x- scene.im_star.getwidth()/2;//这样星星的中心就在lastLand.xstarY =lastLand.top_y- scene.im_star.getheight()- offset;// offset是向上移动的量,比如20像素在Scene的draw函数中,我们修改星星的绘制位置。2.在Player::checkStarCollision函数中,星星的位置也要用同样的方式计算,以确保碰撞检测区域与绘制区域一致。具体修改步骤:步骤1:修改Scene::draw函数中星星的绘制位置(将星星往上移,并居中)原代码:float starX =lastLand.x +scene.im_star.getwidth();float starY =lastLand.top_y- scene.im_star.getheight();修改为(将星星放在地面的正上方,并向上偏移20像素):floatstarX= lastLand.x- scene.im_star.getwidth()/2;//居中floatstarY= lastLand.top_y -scene.im_star.getheight() -20;//再往上20像素步骤2:修改Player::checkStarCollision函数中星星位置的计算方式,与绘制方式保持一致原代码:float starX =lastLand.x +scene.im_star.getwidth();float starY =lastLand.top_y- scene.im_star.getheight();修改为:floatstarX= lastLand.x- scene.im_star.getwidth()/2;float starY =lastLand.top_y- scene.im_star.getheight()-20;步骤3:调整盘子位置的计算,因为星星位置变了,盘子位置也要相应调整(如果需要的话)在Scene::draw中,盘子位置计算基于星星位置:floatpanX= starX +scene.im_star.getwidth()/2 -panWidth/2;floatpanY= starY +800; //在星星下方800像素处由于星星位置已经调整,盘子位置也会自动调整。步骤4:确保碰撞检测函数中的星星位置与绘制位置一致在Player::checkStarCollision中,我们使用和Scene::draw中相同的计算方式。另外,注意在Player::checkStarCollision中,星星的宽度和高度使用的是scene.im_star.getwidth()和getheight(),这是正确的。修改后的关键代码片段:在Scene::draw中:```cpp//绘制星星(终点)Land&lastLand= land[land.size()-1];floatstarX= lastLand.x- im_star.getwidth() /2; //星星中心与地面中心对齐floatstarY= lastLand.top_y -im_star.getheight()-20;//向上移动20像素transparentimage3(NULL, starX -px,starY- py, &im_star);//注意:这里要减去px和py,因为场景绘制需要根据玩家位置偏移//绘制底部的盘子(放在星星正下方)float panX =starX+im_star.getwidth()/2 -panWidth/2;//盘子中心与星星中心对齐floatpanY= starY +800; //在星星下方800像素处transparentimage3(NULL,panX- px, panY -py,&im_pan); //同样减去px,py```在Player::checkStarCollision中:
```cppLand&lastLand= scene.land.back();floatstarX= lastLand.x- scene.im_star.getwidth()/2;//与绘制时一致floatstarY= lastLand.top_y -scene.im_star.getheight() -20; //与绘制时一致floatstarWidth= scene.im_star.getwidth();floatstarHeight= scene.im_star.getheight();```注意:在Scene::draw中,我们绘制星星和盘子时,位置都减去了px和py(玩家的偏移量),因为整个场景是随着玩家移动的。而在Player::checkStarCollision中,我们计算星星位置时,不需要考虑玩家的偏移,因为玩家位置是绝对坐标,星星的位置也是绝对坐标(相对于整个场景的坐标)。但是,在Player类中,玩家的位置(x_left,y_bottom)是场景中的绝对坐标,星星的位置也是场景中的绝对坐标(starX, starY),所以可以直接进行碰撞检测。另外,我们还需要注意,在Scene::draw中,我们修改了星星的绘制位置,同时也修改了盘子的位置(因为盘子位置基于星星位置),所以盘子也会跟着星星上移而一起上移。由于盘子位置计算为:starY+800,所以盘子相对于星星的位置没有变,还是在其下方800像素处。修改后的代码:我们将在Scene::draw函数和Player::checkStarCollision函数中按照上述修改。同时,为了确保自由落体功能恢复,我们还需要检查碰撞检测的条件是否满足。在Player::isOnLand函数中,我们之前修改了垂直检测范围,现在为了确保玩家能站在地面上,我们可能需要调整。但根据您的描述,自由落体失效是指碰到星星后没有触发自由落体,所以重点在星星碰撞检测。我们按照上述修改后,应该能解决问题。由于您提供的代码较长,下面只给出修改的部分(Scene::draw和Player::checkStarCollision)。修改Scene::draw中的星星和盘子绘制部分:```cpp//绘制星星(终点)Land&lastLand= land[land.size()-1];floatstarX= lastLand.x- im_star.getwidth() /2; //星星中心与地面中心对齐floatstarY= lastLand.top_y -im_star.getheight()-20;//向上移动20像素transparentimage3(NULL, starX -px,starY- py, &im_star);//绘制底部的盘子(放在星星正下方)float panX =starX+ im极_star.getwidth()/2 -panWidth/2;//盘子中心与星星中心对齐float panY =starY+800;//在星星下方800像素处transparentimage3(NULL, panX -px,panY- py, &im_pan);```修改Player::checkStarCollision中的星星位置计算:```cpp//星星的位置在最后一块地面上方Land& lastLand =scene.land.back();float starX =lastLand.x -scene.im_star.getwidth()/2;//与绘制时一致floatstarY= lastLand.top_y -scene.im_star.getheight() -20; //向上20像素floatstarWidth= scene.im_star.getwidth();floatstarHeight= scene.im_star.getheight();```注意:在Player::checkStarCollision函数中,我们计算星星位置时,没有减去玩家的偏移(px,py),因为星星位置是场景绝对坐标,玩家的位置(x_left, y_bottom)也是场景绝对坐标,所以可以直接比较。最后,请确保在Scene::draw中,星星的绘制位置(starX,starY)减去px和py,是因为整个场景的绘制需要根据玩家位置进行偏移。我们进行这样的修改后,星星的位置被上移了20像素,并且星星的中心位于最后一块地面的中心正上方。同时,星星的碰撞检测区域也做了同样的调整,应该能正确触发自由落体。如果问题仍然存在,可以尝试输出玩家和星星的位置信息进行调试。由于代码较长,这里只给出修改的关键部分,您需要将原代码中的对应部分替换掉。注意:在Scene::draw函数中,我们修改了星星和盘子的位置计算,并且绘制时减去了玩家的偏移(px,py)。在Player::checkStarCollision中,星星位置计算与绘制时的绝对位置(未减去px,py)相同。希望这能解决您的问题。</think>我注意到两个关键问题:1) 碰到星星后自由落体功能失效 2) 星星位置需要上移。以下是修复后的完整代码:
```cpp
#include<graphics.h>
#include<conio.h>
#include<stdio.h>
#include <time.h>
#include "Timer.h"
#include <atomic>
#include <thread>
#include <windows.h>
#include <mmsystem.h>
#include <vector>
#include <string>
#include<cmath>
#pragma comment( lib, "MSIMG32.LIB")
#pragma comment(lib, "win极mm.lib")
using namespace std;
#define WIDTH 1080
#define HEIGHT 600
atomic<bool> stopPlayback(false);
thread* mp3Thread = nullptr;
Timer timer;
clock_t gameStartTime = 0;
void playMP3(const char* filename) {
if (stopPlayback.load()) return;
char cmd[256] = { 0 };
sprintf_s(cmd, "open \"%s\" alias mp3", filename);
mciSendStringA(cmd, NULL, 0, NULL);
sprintf_s(cmd, "play mp3 repeat");
mciSendStringA(cmd, NULL, 0, NULL);
while (!stopPlayback.load()) {
if (_kbhit()) {
char key = _getch();
if (key == 'w' || GetAsyncKeyState(VK_UP)) {
PlaySoundA("music/jumpmusic.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
}
}
this_thread::sleep_for(chrono::milliseconds(100));
}
sprintf_s(cmd, "close mp3");
mciSendStringA(cmd, NULL, 0, NULL);
}
void stopBackgroundMusic() {
if (!stopPlayback.load()) {
stopPlayback.store(true);
mciSendStringA("close mp3", NULL, 0, NULL);
}
}
enum PlayerStatus {
stillleft, stillright, walkleft, walkright,
jumpleft, jumpright, die
};
void transparentimage3(IMAGE* dstimg, int x, int y, IMAGE* srcimg) {
HDC dstDC = GetImageHDC(dstimg);
HDC srcDC = GetImageHDC(srcimg);
int w = srcimg->getwidth();
int h = srcimg->getheight();
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}
class Land {
public:
IMAGE im_land;
float land_width, land_height;
float x, left_x, top_y;
void initialize(int width = 400, int height = 80) {
loadimage(&im_land, _T("land1.png"), width, height);
land_height = im_land.getheight();
land_width = im_land.getwidth();
top_y = HEIGHT / 2;
}
void draw(float px, float py) {
transparentimage3(NULL, x - land_width / 2 - px, top_y - py - 10, &im_land);
///调试
setlinecolor(GREEN);
rectangle(x - land_width / 2 - px, top_y - py - 10,
x + land_width / 2 - px, top_y - py + land_height);
}
};
class Key {
public:
极 IMAGE im_key;
float x, y;
float width, height;
bool active;
void initialize() {
loadimage(&im_key, _T("money.png"), 35, 35);
width = im_key.getwidth();
height = im_key.getheight();
active = true;
}
void draw(float px, float py) {
if (active)
transparentimage3(NULL, x - px, y - py, &im_key);
}
bool checkCollision(float playerX, float playerY, float playerWidth, float playerHeight) {
if (!active) return false;
float playerRight = playerX + playerWidth;
float playerBottom = playerY + playerHeight;
float keyRight = x + width;
float keyBottom = y + height;
return (playerX < keyRight &&
playerRight > x &&
playerY < keyBottom &&
playerBottom > y);
}
};
class Enemy {
public:
IMAGE im_enemy;
float e_height, e_width;
float vx, vy;
float x, y;
float x_min, x_max;
float g;
bool isAl极ive;
bool isOnGround;
void initialize() {
loadimage(&im_enemy, _T("monster.png"), 40, 40);
e_height = im_enemy.getheight();
e_width = im_enemy.getwidth();
vy = 0;
g = 3; // 减小重力
isOnGround = false;
isAlive = true;
}
void draw(float px, float py) {
if (isAlive) {
transparentimage3(NULL, x - e_width / 2 - px, y - e_height / 2 - py, &im_enemy);
}
}
void update(vector<Land>& lands) {
if (!isAlive) return;
// 水平移动
x += vx;
if (x > x_max || x < x_min) {
vx = -vx;
}
// 应用重力
vy += g;
y += vy;
// 检测与地面的碰撞
isOnGround = false;
for (auto& land : lands) {
float enemyBottom = y + e_height / 2;
float enemyLeft = x - e_width / 2;
float enemyRight = x + e_width / 2;
float landLeft = land.x - land.land_width / 2;
float landRight = land.x + land.land_width / 2;
float landTop = land.top_y;
// 检查是否在地面上方且垂直距离接近
if (enemyBottom >= landTop - 5 && enemyBottom <= landTop + 10 &&
enemyRight > landLeft && enemyLeft < landRight) {
y = landTop - e_height / 2; // 调整敌人位置到地面
vy = 0; // 重置垂直速度
isOnGround = true;
break;
}
}
}
};
class Scene {
public:
IMAGE im_bk;
IMAGE im_star;
IMAGE im_pan;
IMAGE victoryImg;;
IMAGE gameOverImg;
vector<Land> land;
vector<Enemy> enemies;
vector<Key> moneys;
float panX, panY;
float panWidth, panHeight;
void draw(float px, float py) {
// 修改背景绘制方式,使其随玩家移动
// 背景移动速度为玩家移动速度的一半
float bgOffsetX = px / 4; // 调整这个值可以改变背景移动速度
float bgOffsetY = py / 4; // 调整这个值可以改变背景移动速度
// 计算背景位置,确保不会超出边界
int bgX = -bgOffsetX;
int bgY = -bgOffsetY;
// 确保背景不会显示空白区域
if (bgX > 0) bgX = 0;
if (bgY > 0) bgY = 0;
if (bgX < WIDTH - im_bk.getwidth()) bgX = WIDTH - im_bk.getwidth();
if (bgY < HEIGHT - im_bk.getheight()) bgY = HEIGHT - im_bk.getheight();
putimage(bgX, bgY, &im_bk);
for (int i = 0; i < land.size(); i++) {
land[i].draw(px, py);
}
for (int i = 0; i < enemies.size(); i++) {
enemies[i].draw(px, py);
}
for (int i = 0; i < moneys.size(); i++) {
moneys[i].draw(px, py);
}
// 绘制星星(终点)
Land& lastLand = land[land.size() - 1];
// 星星位置上移30像素
float starX = lastLand.x - im_star.getwidth() / 2 - px;
float starY = lastLand.top_y - im_star.getheight() - 30 - py; // 上移30像素
transparentimage3(NULL, starX, starY, &im_star);
// 绘制底部的盘子(放在星星正下方)
float panX = starX + im_star.getwidth() / 2 - panWidth / 2;
float panY = starY + 800; // 在星星下方800像素处
transparentimage3(NULL, panX, panY, &im_pan);
}
void initialize() {
loadimage(&im_bk, _T("bk.jpg"), 1080, 800); // 依次导入各张背景图片
loadimage(&im_star, _T("star.png"), 40, 40);
loadimage(&im_pan, _T("pan.png"), 400, 100); // 加载盘子图片
loadimage(&victoryImg, _T("chip.png"), 300, 300); // 加载胜利结算图片
loadimage(&gameOverImg, _T("gameover.png"), WIDTH, HEIGHT); // 加载游戏结束图片
// 初始化盘子
panWidth = im_pan.getwidth();
panHeight = im_pan.getheight();
land.clear();
// 有序排列地面 - 阶梯式,确保不重叠
Land l1;
l1.initialize(400, 80); // 宽度400,高度80
l1.x = 200;
l1.top_y = HEIGHT - 100;
land.push_back(l1);
Land l2;
l2.initialize(300, 60);
l2.x = 700; // 从l1的右侧开始 (200 + 400/2 + 300/2 + 间隙)
l2.top_y = HEIGHT - 200;
land.push_back(l2);
Land l3;
l3.initialize(350, 70);
l3.x = 1100;
l3.top_y = HEIGHT - 150;
land.push_back(l3);
Land l4;
l4.initialize(300, 60);
l4.x = 1500;
l4.top_y = HEIGHT - 250;
land.push_back(l4);
Land l5;
l5.initialize(400, 80);
l5.x = 1900;
l5.top_y = HEIGHT - 100;
land.push_back(l5);
// 确定玩家初始位置(中心点)
float playerCenterX = land[0].x; // 玩家初始位置中心
float safeDistance = 200.0f; // 安全距离
enemies.clear();
for (int i = 0; i < 3; ++i) {
Enemy enemy;
enemy.initialize();
// 尝试找到安全位置
int attempts = 0;
bool positionSafe = false;
int idx = 0;
while (!positionSafe && attempts < 20) {
idx = rand() % land.size();
enemy.x = land[idx].x;
// 检查是否在安全距离外
float distance = fabs(enemy.x - playerCenterX);
positionSafe = (distance > safeDistance);
attempts++;
}
enemy.y = land[idx].top_y - enemy.e_height / 2;
enemy.x_min = land[idx].x - 100;
enemy.x_max = land[idx].x + 100;
enemy.vx = 2 + rand() % 3;
enemies.push_back(enemy);
}
moneys.clear();
for (int i = 0; i < 10; ++i) {
Key money;
money.initialize();
int idx = rand() % land.size();
money.x = land[idx].x + rand() % ((int)land[idx].land_width - 40);
money.y = land[idx].top_y - 50;
moneys.push_back(money);
}
}
void showGameOver() {
cleardevice();
transparentimage3(NULL, 0, 0, &gameOverImg);
FlushBatchDraw();
Sleep(3000); // 显示3秒
}
void showVictoryScreen(int score,double timeTaken) {
cleardevice();
// 绘制胜利结算图片
int chipX = (WIDTH - victoryImg.getwidth()) / 2;
int chipY = HEIGHT / 4;
transparentimage3(NULL, chipX, chipY, &victoryImg);
// 在图片下方显示分数
TCHAR buffer[100];
wsprintf(buffer, _T("得分: %d"), score);
setbkmode(TRANSPARENT);
settextcolor(WHITE);
settextstyle(36, 0, _T("宋体"));
outtextxy((WIDTH - textwidth(buffer)) / 2, chipY + victoryImg.getheight() + 20, buffer);
// 显示通关时间
TCHAR timeBuffer[100];
wsprintf(timeBuffer, _T("通关时间: %.2f 秒"), timeTaken);
outtextxy((WIDTH - textwidth(timeBuffer)) / 2, chipY + victoryImg.getheight() + 100, timeBuffer);
FlushBatchDraw();
Sleep(5000); // 显示5秒
}
void showLifeInfo(int lives) {
cleardevice();
setbkmode(TRANSPARENT);
settextcolor(WHITE);
settextstyle(50, 0, _T("宋体"));
IMAGE im_lives;
loadimage(&im_lives, _T("life.png"), 150, 150);
TCHAR buffer[100];
transparentimage3(NULL, WIDTH / 2 - 100, HEIGHT / 2 - 25, &im_lives);
wsprintf(buffer, _T(" X %d"), lives);
outtextxy(WIDTH / 2 + 90, HEIGHT / 2 + 20, buffer);
FlushBatchDraw();
Sleep(2000); // 显示2秒
}
};
Scene scene;
class Player {
public:
IMAGE im_now;
IMAGE im_stillleft;
IMAGE im_stillright;
IMAGE im_jumpleft;
IMAGE im_jumpright;
IMAGE im_walkright;
IMAGE im_walkleft;
PlayerStatus playerStatus;
float x_left, y_bottom;
float vx, vy;
float width, height;
float g;
float fallingG; // 自由落体时的重力加速度
int score = 0;
int lives = 3;
bool isWin = false; // 是否触碰到星星
bool isFalling = false; // 是否处于自由落体状态
void draw() {
transparentimage3(NULL, WIDTH / 2, HEIGHT / 2 - height, &im_now);
}
void initialize() {
loadimage(&im_stillleft, _T("kim_left.png"), 60, 80);
loadimage(&im_stillright, _T("kim_right.png"), 60, 80);
loadimage(&im_jumpleft, _T("kim_jumpleft.png"), 60, 80);
loadimage(&im_jumpright, _T("kim_jumpright.png"), 60, 80);
loadimage(&im_walkright, _T("kim_right.png"), 60, 80);
loadimage(&im_walkleft, _T("kim_left.png"), 极60, 80);
playerStatus = stillright;
im_now = im_stillright;
width = im_stillright.getwidth();
height = im_still极right.getheight();
if (!scene.land.empty()) {
x_left = scene.land[0].x - width / 2;
y_bottom = scene.land[0].top_y;
}
else {
x_left = WIDTH / 2;
y_bottom = HEIGHT - 100;
}
vx = 15; // 水平速度
vy = 0;
g = 5;
fallingG = 2; // 自由落体时的重力加速度(更慢)
}
void update(float x, float y) {
x_left = x;
y_bottom = y;
}
void walkRight(Scene& scene) {
if (isFalling) return; // 自由落体时不能移动
x_left += vx;
if (x_left < 0) x_left = 0;
if (isNotOnAllLands(scene.land, vy)) {
im_now = im_jumpright;
playerStatus = jumpright;
return;
}
if (playerStatus == jumpleft || playerStatus == jumpright) {
im_now = im_jumpright;
}
else {
playerStatus = walkright;
im_now = im_walkright;
}
}
void walkLeft(Scene& scene) {
if (isFalling) return; // 自由落体时不能移动
x_left -= vx;
if (x_left < 0) x_left = 0;
if (isNotOnAllLands(scene.land, vy)) {
im_now = im_jumpleft;
playerStatus = jumpleft;
return;
}
if (playerStatus == jumpleft || playerStatus == jumpright) {
im_now = im_jumpleft;
}
else {
playerStatus = walkleft;
im_now = im_walkleft;
}
}
void standstill() {
if (isFalling) return; // 自由落体时不能移动
if (playerStatus == stillleft || playerStatus == walkleft) {
im_now = im_stillleft;
}
else if (playerStatus == stillright || playerStatus == walkright) {
im_now = im_stillright;
}
}
void beginjump() {
if (isFalling) return; // 自由落体时不能移动
if (playerStatus != jumpleft && playerStatus != jumpright) {
if (playerStatus == walkleft || playerStatus == stillleft) {
im_now = im_jumpleft;
playerStatus = jumpleft;
}
else if (playerStatus == walkright || playerStatus == stillright) {
im_now = im_jumpright;
playerStatus = jumpright;
}
vy = -50;
}
}
bool isOnLand(Land& land, float ySpeed) {
float playerRight = x_left + width;
float playerBottom = y_bottom;
float playerTop = y_bottom - height;
float landLeft = land.x - land.land_width / 2;
float landRight = land.x + land.land_width / 2;
float landTop = land.top_y;
// 检查是否在地面上方且垂直距离接近
bool verticalOverlap = (playerBottom >= landTop - 10 && playerBottom <= landTop + 10);
bool horizontalOverlap = (x_left < landRight && playerRight > landLeft);
if (verticalOverlap && horizontalOverlap) {
y_bottom = landTop; // 将玩家放在地面上
return true;
}
return false;
}
bool isNotOnAllLands(vector<Land>& lands, float speed) {
for (auto& land : lands) {
if (isOnLand(land, speed)) {
return false;
}
}
return true;
}
void updateKeycode(Scene& scene) {
// 应用重力
float currentG = isFalling ? fallingG : g;
vy += currentG;
y_bottom += vy; // 检测与地面的碰撞
bool onGround = false;
for (auto& land : scene.land) {
if (isOnLand(land, vy)) {
onGround = true;
vy = 0; // 重置垂直速度
// 落地后恢复行走状态
if (playerStatus == jumpleft) {
playerStatus = stillleft;
}
else if (playerStatus == jumpright) {
playerStatus = stillright;
}
break;
}
}
// 如果不在任何地面上且速度向下,保持跳跃状态
if (!onGround && vy > 0) {
if (playerStatus == stillleft || playerStatus == walkleft) {
playerStatus = jumpleft;
}
else if (playerStatus == stillright || playerStatus == walkright) {
playerStatus = jumpright;
}
}
}
bool stompEnemy(Enemy& enemy) {
if (!enemy.isAlive) return false;
float playerBottom = y_bottom;
float playerLeft = x_left;
float playerRight = x_left + width;
float enemyTop = enemy.y - enemy.e_height / 2;
float enemyLeft = enemy.x - enemy.e_width / 2;
float enemyRight = enemy.x + enemy.e_width / 2;
// 检查玩家是否在敌人上方
if (playerBottom <= enemyTop + 10 &&
playerRight > enemyLeft &&
playerLeft < enemyRight &&
vy > 0) {
enemy.isAlive = false; // 敌人死亡
vy = -15; // 玩家弹起
score += 100;
// 播放敌人死亡音效
PlaySoundA("music/monstermusic.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
return true;
}
return false;
}
// 检查是否碰到星星
bool checkStarCollision() {
if (isWin || isFalling) return false;
// 星星的位置在最后一块地面上方
Land& lastLand = scene.land.back();
// 星星位置上移30像素
float starX = lastLand.x - scene.im_star.getwidth()/2; // 居中
float starY = lastLand.top_y - scene.im_star.getheight() - 30; // 上移30像素
float starWidth = scene.im_star.getwidth();
float starHeight = scene.im_star.getheight();
// 玩家位置
float playerLeft = x_left;
float playerRight = x_left + width;
float playerTop = y_bottom - height;
float playerBottom = y_bottom;
// 检测碰撞
if (playerRight > starX && playerLeft < starX + starWidth &&
playerBottom > starY && playerTop < starY + starHeight) {
isWin = true;
isFalling = true;
return true;
}
return false;
}
// 检查是否碰到盘子
bool checkPanCollision() {
if (!isFalling) return false;
Land& lastLand = scene.land.back();
// 星星位置上移30像素
float starX = lastLand.x;
float starY = lastLand.top_y - scene.im_star.getheight() - 30;
// 盘子位置在星星正下方
float panX = starX - scene.panWidth / 2 + scene.im_star.getwidth() / 2;
float panY = starY + 800; // 在星星下方800像素处
// 玩家位置
float playerLeft = x_left;
float playerRight = x_left + width;
float playerBottom = y_bottom;
// 检测碰撞(从上方碰到盘子)
if (playerBottom >= panY - 5 && playerBottom <= panY + 10 &&
playerRight > panX && playerLeft < panX + scene.panWidth) {
// 碰到盘子,停止自由落体
isFalling = false;
PlaySoundA("music/victory.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
return true;
}
return false;
}
};
Player player;
bool gameOver = false;
bool gameWin = false;
void startup() {
srand(time(0));
scene.initialize();
player.initialize();
initgraph(WIDTH, HEIGHT, SHOWCONSOLE);
BeginBatchDraw();
gameStartTime = clock(); // 记录游戏开始时间
}
void show() {
if (gameOver) return;
cleardevice();
scene.draw(player.x_left - WIDTH / 2, player.y_bottom - HEIGHT / 2);
player.draw();
TCHAR buffer[100];
wsprintf(buffer, _T("分数: %d | 生命: %d"), player.score, player.lives);
setbkmode(TRANSPARENT);
settextcolor(RED);
settextstyle(20, 0, _T("宋体"));
outtextxy(10, 10, buffer);
FlushBatchDraw();
Sleep(30);
}
void withinput() {
if (gameOver) return;
player.standstill();
if (_kbhit()) {
if (GetAsyncKeyState(VK_RIGHT) || GetAsyncKeyState('D'))
player.walkRight(scene);
else if (GetAsyncKeyState(VK_LEFT) || GetAsyncKeyState('A'))
player.walkLeft(scene);
if (GetAsyncKeyState(VK_UP) || GetAsyncKeyState('W'))
player.beginjump();
}
}
void withoutinput() {
if (gameOver) return;
// 检查是否碰到星星
if (player.checkStarCollision()) {
// 碰到星星时播放音效
PlaySoundA("music/star.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
}
// 如果是自由落体状态
if (player.isFalling) {
player.updateKeycode(scene); // 使用自由落体的重力加速度
// 检查是否碰到盘子
if (player.checkPanCollision()) {
gameWin = true;
// 停止背景音乐
stopBackgroundMusic();
// 播放胜利结算音效
PlaySoundA("music/victory.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
return;
}
}
else {
player.updateKeycode(scene);
// 更新敌人位置
for (int i = 0; i < scene.enemies.size(); i++) {
scene.enemies[i].update(scene.land);
}
// 检查玩家与敌人碰撞
for (int i = 0; i < scene.enemies.size(); i++) {
if (player.stompEnemy(scene.enemies[i])) {
// 踩踏成功处理
}
else {
// 检查其他碰撞
float playerRight = player.x_left + player.width;
float playerBottom = player.y_bottom;
float enemyLeft = scene.enemies[i].x - scene.enemies[i].e_width / 2;
float enemyRight = scene.enemies[i].x + scene.enemies[i].e_width / 2;
float enemyTop = scene.enemies[i].y - scene.enemies[i].e_height / 2;
float enemyBottom = scene.enemies[i].y + scene.enemies[i].e_height / 2;
if (player.x_left < enemyRight &&
playerRight > enemyLeft &&
player.y_bottom > enemyTop &&
playerBottom < enemyBottom &&
scene.enemies[i].isAlive) {
player.lives--;
// 播放生命值减少音效
PlaySoundA("music/lostblood.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
if (player.lives <= 0) {
// 停止背景音乐
stopBackgroundMusic();
// 播放游戏结束音效
PlaySoundA("music/defeat.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
gameOver = true;
scene.showGameOver();
return;
}
// 显示剩余生命值
scene.showLifeInfo(player.lives);
// 重置玩家位置
if (!scene.land.empty()) {
player.x_left = scene.land[0].x - player.width / 2;
player.y_bottom = scene.land[0].top_y;
}
else {
player.x_left = WIDTH / 2;
player.y_bottom = HEIGHT - 100;
}
player.vy = 0;
// 重置敌人
for (auto& enemy : scene.enemies) {
enemy.isAlive = true;
}
return;
}
}
}
// 检查道具碰撞
for (int i = 0; i < scene.moneys.size(); i++) {
if (scene.moneys[i].checkCollision(player.x_left, player.y_bottom, player.width, player.height)) {
player.score += 10;
// 播放收集金钱音效
PlaySoundA("music/moneymusic.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
scene.moneys.erase(scene.moneys.begin() + i);
i--;
}
}
// 检查掉落
if (player.y_bottom > HEIGHT * 1.5) {
player.lives--;
// 播放生命值减少音效
PlaySoundA("music/lostblood.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
if (player.lives <= 0) {
// 停止背景音乐
stopBackgroundMusic();
// 播放游戏结束音效
PlaySoundA("music/defeat.wav", NULL, SND_FILENAME | SND_ASYNC | SND极NODEFAULT);
gameOver = true;
scene.showGameOver();
return;
}
// 显示剩余生命值
scene.showLifeInfo(player.lives);
// 重置玩家位置
if (!scene.land.empty()) {
player.x_left = scene.land[0].x - player.width / 2;
player.y_bottom = scene.land[0].top_y;
}
else {
player.x_left = WIDTH / 2;
player.y_bottom = HEIGHT - 100;
}
player.vy = 0;
}
}
}
int main() {
startup();
mp3Thread = new std::thread(playMP3, "music/bkmusic.mp3");
while (true) {
show();
withinput();
withoutinput();
if (gameOver) {
break;
}
if (gameWin) {
double timeTaken = (double)(clock() - gameStartTime) / CLOCKS_PER_SEC;
// 显示胜利结算画面
scene.showVictoryScreen(player.score, timeTaken);
break;
}
}
// 游戏结束后关闭图形窗口
_getch();
closegraph();
if (mp3Thread && mp3Thread->joinable()) {
stopPlayback.store(true);
mp3Thread->join();
}
delete mp3Thread;
return 0;
}
```
### 主要修复内容:
#### 1. 恢复自由落体功能(关键修复)
```cpp
// Scene::draw 函数中
float starY = lastLand.top_y - im_star.getheight() - 30 - py; // 上移30像素
// Player::checkStarCollision 函数中
float starY = lastLand.top_y - scene.im_star.getheight() - 30; // 上移30像素
// Player::checkPanCollision 函数中
float starY = lastLand.top_y - scene.im_star.getheight() - 30; // 上移30像素
```
**修复说明**:
- 确保星星位置在碰撞检测和绘制时使用相同的坐标计算
- 在Scene::draw中减去py(玩家垂直偏移)确保正确渲染
- 在Player类中直接使用绝对坐标进行碰撞检测
#### 2. 星星位置上移30像素
```cpp
// 在所有星星位置计算中添加 -30 偏移
float starY = ... - 30 ...
```
#### 3. 优化碰撞检测逻辑
```cpp
// Player::isOnLand 函数中
bool verticalOverlap = (playerBottom >= landTop - 10 && playerBottom <= landTop + 10);
```
**修复说明**:
- 缩小垂直碰撞检测范围,避免误判在空中
- 使用更精确的10像素范围而非30像素
#### 4. 添加碰到星星时的音效反馈
```cpp
// withoutinput 函数中
if (player.checkStarCollision()) {
PlaySoundA("music/star.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
}
```
#### 5. 修复玩家移动逻辑
```cpp
// withinput 函数中
void withinput() {
if (gameOver) return;
player.standstill(); // 只调用一次
...
}
```
### 自由落体机制详解:
1. **触发条件**:
- 玩家碰撞到星星时,`isFalling`设置为true
- 星星位置上移30像素,更容易被玩家碰到
2. **物理效果**:
- 自由落体时使用较小的重力加速度(2 vs 正常5)
- 忽略其他碰撞检测,只检测盘子
3. **结束条件**:
- 玩家落到盘子上时,触发胜利
- 播放胜利音效并显示结算画面
### 星星位置调整:
- 在最后一块地面的正上方
- 比原来位置上移30像素
- 居中于地面中心(x方向)