nodriver1 2022-05-18 14:47 采纳率: 50%
浏览 39
已结题

一个程序被新加入的函数图层覆盖了怎么办

呃,鄙人想做一个迷宫小程序,但觉得单纯的迷宫没有什么意思,想在程序中加入一个弹射的小球ghost,使游戏角色碰到产生一定的效果,问题就是我写好ghost函数之后整个游戏画面会被覆盖掉,而不是共存,所以想来请教一下各位。

这是本来的程序:

img

这是加了ghost后:

img

源码:

//1.程序与处理包括加载头文件,定义常量和函数,进行初始化工作
#undef UNICODE
#undef _UNICODE
#define _CRT_SECURE_NO_WARNINGS
#include <graphics.h>
#include <stack>
#include <vector>
#include <fstream>
#include<stdio.h>//*music
#include<windows.h>
#include<mmsystem.h>
#include<time.h>
#include<conio.h>
#pragma comment(lib,"Winmm.lib")
using std::stack;            // 使用STL的栈的声明
using std::vector;            // 使用STL的数组容器封装动态大小数组的序列容器;


// 游戏信息(每一行每一列宽度为32像素)
#define WIN_WIDTH    900    // 窗口的宽度(单位:像素)
#define WIN_HEIGHT    500        // 窗口的高度(单位:像素)
// !!注:由于随机生成算法的原因,地图宽高只能为奇数https://zhuanlan.zhihu.com/p/30724817

#define GAME_WIDTH    41        // 地图的宽度(单位:块)
#define GAME_HEIGHT    31    // 地图的高度(单位:块)

#define WALL        1            // 墙壁的数字标记,用于判断
#define GROUND        0            // 地面的数字标记,用于判断
#define FILLSTATE    2            // 加油站的数字标记,用于判断
#define ENDPOS        3            // 终点的数字标记,用于判断

#define MAXVIEW        8.0            // 最大的视野
#define MINVIEW        1            // 最小的视野
#define FILLNUM        10            // 加油站的数量
#define DARKTIME    12            // 视野下降1图块所需的时间

// 全局变量列表
int        g_BlockSize;            // 块大小
int        g_GameMap[GAME_HEIGHT][GAME_WIDTH];    // 地图(宽高单位为块)
POINT    g_EndPos;                // 终点位置
POINT   g_PlayerPos;            // 玩家在地图上的位置
POINT    g_CameraPos;            // 摄像机(屏幕左上角)在地图上的位置
IMAGE    g_MapImage;                // 地图的图片(由于地图是固定的,在不改变缩放的情况下只需要绘制一次)
double    g_ViewArray;            // 视野
UINT    g_BeginTime;            // 游戏开始时的时间
UINT    g_LastFillTime;            // 上次为油灯加油的时间


// 函数列表
void initGame();                // 初始化游戏
void endGame();                    // 结束游戏
void draw();                    // 绘制函数
bool upDate();                    // 数据更新函数
void absDelay(int delay);        // 绝对延迟

bool canMove(POINT pos);        // 判断某个位置是否可以移动
void computeCameraPos();        // 计算摄像机在地图上的位置
void rePaintMap();                // 重绘地图

void drawWall(POINT pos);        // 绘制墙壁图块的函数
void drawGround(POINT pos);        // 绘制地面图块的函数
void drawFillState(POINT pos);    // 绘制油灯图块的函数
void drawEndPos(POINT pos);        // 绘制终点
void drawPlayer();                // 绘制人物的函数
void drawView();                // 绘制视野
void bgm();                     //背景音乐
void jiaobu();                  //脚步声

void welcome();
void ghost();

//滚轮控制放大缩小
//2.主函数,main执行
int main()//主函数
{
    welcome();
    initGame();
        
    bgm();
    
    while (1)
    {
        
        if (!upDate()) break;    // 更新
        
        draw();                    // 绘制
        
        absDelay(16);            // 绝对延迟 16 毫秒,控制每秒 60 帧
        
    }
    
    endGame();
    return 0;
}
//3.初始化游戏,定义开局视野,随机生成地图,这个算法是先生成了一个点 ,根据这个点去找别的点的位置该模块还引用了其他函数进行初始化游戏:标记终点,确定终点位置,计算摄像机的位置,初始化人的位置,初始化画布等,在main中被引用
void initGame()// 初始化游戏
{
    g_BlockSize = 32;            // 初始图块大小为 32 个像素,这个数越大,人物越小,需要走的路程更多
    srand(GetTickCount());        // 标准库中初始化随机数生成的函数

    // 初始化间隔室
    for (int i = 0; i < GAME_HEIGHT; i++)
    {
        for (int j = 0; j < GAME_WIDTH; j++)
        {
            if (i % 2 == 0 || j % 2 == 0)//奇数行奇数列设为墙壁    
                g_GameMap[i][j] = WALL;
            else
                g_GameMap[i][j] = GROUND;//偶数行偶数列为地面
        }
    }

    // 随机生成地图(使用深度优先遍历:先输出当前结点,在根据一定的次序去递归查找孩子)
    stack<POINT> stepStack;        // 步骤栈
    vector<POINT>  stepPoint;    // 四周的点
    POINT nowPoint;                // 当前步的所在点
    stepStack.push({ 1,1 });    // 写入初始点 (1,1) 作为起点
    nowPoint = { 1,1 };
    g_GameMap[1][1] = 0xFFFF;    // 标记这个点
    while (!stepStack.empty())    // 只要步骤栈不空就继续循环
    {
        // 得到四周的点
        POINT tempPoint;
        for (int i = -1; i <= 1; i += 2)
        {
            tempPoint = { nowPoint.x,nowPoint.y + i * 2 };    // 计算点
            // 判断坐标是否合法
            if (tempPoint.x >= 0 && tempPoint.x <= GAME_WIDTH - 1 &&
                tempPoint.y >= 0 && tempPoint.y <= GAME_HEIGHT - 1 &&
                g_GameMap[tempPoint.y][tempPoint.x] != 0xFFFF)//0xffff=-1
            {
                stepPoint.push_back(tempPoint);
            }
            tempPoint = { nowPoint.x + i * 2 ,nowPoint.y };    // 计算点
            // 判断坐标是否合法
            if (tempPoint.x >= 0 && tempPoint.x <= GAME_WIDTH - 1 &&
                tempPoint.y >= 0 && tempPoint.y <= GAME_HEIGHT - 1 &&
                g_GameMap[tempPoint.y][tempPoint.x] != 0xFFFF)
            {
                stepPoint.push_back(tempPoint);
            }
        }

        // 根据周围点的量选择操作
        if (stepPoint.empty())                // 如果周围点都被遍历过了
        {
            stepStack.pop();                // 出栈当前点
            if (!stepStack.empty())
                nowPoint = stepStack.top();    // 更新当前点
        }
        else
        {
            stepStack.push(stepPoint[rand() % stepPoint.size()]);    // 入栈当前点,rand()生成随机数
            g_GameMap[(nowPoint.y + stepStack.top().y) / 2][(nowPoint.x + stepStack.top().x) / 2] = 0;    // 打通墙壁
            nowPoint = stepStack.top();        // 更新当前点
            g_GameMap[nowPoint.y][nowPoint.x] = 0xFFFF;                // 标记当前点
        }
        stepPoint.clear();                    // 清空周围点以便下一次循环
    }
    
    // 清洗标记点
    for (int i = 0; i < GAME_HEIGHT; i++)
    {
        for (int j = 0; j < GAME_WIDTH; j++)
        {
            if (g_GameMap[i][j] == 0xFFFF)
                g_GameMap[i][j] = 0;
        }
    }

    // 随机生成加油站的位置
    for (int i = 0; i < FILLNUM; i++)
    {
        POINT fillPoint = { rand() % GAME_WIDTH,rand() % GAME_HEIGHT };
        // 保证在空地生成加油站
        while (g_GameMap[fillPoint.y][fillPoint.x] != GROUND)
            fillPoint = { rand() % GAME_WIDTH,rand() % GAME_HEIGHT };
        // 标记油灯
        g_GameMap[fillPoint.y][fillPoint.x] = FILLSTATE;
    }
    
    g_GameMap[GAME_HEIGHT - 2][GAME_WIDTH - 2] = ENDPOS;        // 标记终点
    g_EndPos = { GAME_WIDTH - 2,GAME_HEIGHT - 2 };                // 确定终点位置
    g_ViewArray = MAXVIEW;                // 初始视野是最大的
    g_BeginTime = GetTickCount();        // 开始计时
    g_LastFillTime = GetTickCount();    // 油灯加油的时间
    rePaintMap();                        // 绘制地图
    g_PlayerPos = { g_BlockSize * 3 / 2,g_BlockSize * 3 / 2 };    // 初始化人的位置
    computeCameraPos();                    // 计算摄像机的位置
    initgraph(WIN_WIDTH, WIN_HEIGHT);    // 初始化画布
    setbkmode(TRANSPARENT);                // 设置背景为透明
    BeginBatchDraw();                    // 开始缓冲绘制
    
}


//4. 该模块把绘制视野,人物,时间融合到一个draw里面,在main函数中被引用

void draw()//绘图
{
    
    // 清空设备
    cleardevice();
    // 绘制视野
    drawView();
    // 绘制人
    drawPlayer();
    // 绘制时间
    
    TCHAR timeStr[256];
    int loseTime = GetTickCount() - g_BeginTime;    // 计算流失的时间
    _stprintf_s(timeStr, _T("游戏时间:%02d:%02d"), loseTime / 1000 / 60, loseTime / 1000 % 60);
    settextcolor(RGB(140, 140, 140));
    outtextxy((WIN_WIDTH - textwidth(timeStr)) / 2, 3, timeStr);
    

    FlushBatchDraw();    // 刷新屏幕
}
//5.update意为更新,这里的函数用来计算人物移动合法性和路过油灯视野更新,以及鼠标滚轮放大缩小的重新定位
bool upDate()//计算,更新位置
{
    POINT nextPos = g_PlayerPos;        // 下一个位置

    // 计算下一个位置
    if         (GetKeyState(VK_UP) & 0x8000)    nextPos.y -= 2;//GetKeyState(VK_UP) & 0x8000)判断是否有按↑键
    
    
    if (GetKeyState(VK_DOWN) & 0x8000)    nextPos.y += 2;//GetKeyState(VK_UP) & 0x8000)判断是否有按↓键
    
    if (GetKeyState(VK_LEFT) & 0x8000)    nextPos.x -= 2;//GetKeyState(VK_UP) & 0x8000)判断是否有按←键
    
    if (GetKeyState(VK_RIGHT) & 0x8000)    nextPos.x += 2;//GetKeyState(VK_UP) & 0x8000)判断是否有按→键
    

    // 如果下一个位置不合法
    if (!canMove(nextPos))
    {
        if (canMove({ g_PlayerPos.x, nextPos.y }))        // y 轴移动合法
            nextPos = { g_PlayerPos.x, nextPos.y };
        else if (canMove({ nextPos.x, g_PlayerPos.y }))    // x 轴移动合法
            nextPos = { nextPos.x, g_PlayerPos.y };
        else                                            // 都不合法
            nextPos = g_PlayerPos;
    }

    // 如果是油灯则更新时间
    if (g_GameMap[nextPos.y / g_BlockSize][nextPos.x / g_BlockSize] == FILLSTATE)
        g_LastFillTime = GetTickCount();
    // 如果是终点则通关
    else if (g_GameMap[nextPos.y / g_BlockSize][nextPos.x / g_BlockSize] == ENDPOS)
    {
        outtextxy(WIN_WIDTH / 2 - 40, WIN_HEIGHT / 2 - 12, _T("恭喜过关!"));//字体大小
        FlushBatchDraw();
        Sleep(5000);
        return false;
    }
    g_PlayerPos = nextPos;                        // 更新位置
    computeCameraPos();                            // 计算摄像机的位置

    // 根据时间缩减视野
    static unsigned int lastTime = GetTickCount();
    int loseTime = GetTickCount() - g_LastFillTime;            // 计算流失的时间
    g_ViewArray = MAXVIEW - loseTime / 1000.0 / DARKTIME;    // 每一段时间油灯的照明力会下降一个图块
    if (g_ViewArray < MINVIEW) g_ViewArray = MINVIEW;

    // 处理鼠标消息
    MOUSEMSG mouseMsg;                            // 鼠标信息
    int lastBlockSize = g_BlockSize;            // 保存原本的大小
    while (MouseHit())
    {
        mouseMsg = GetMouseMsg();
        if (mouseMsg.uMsg = WM_MOUSEWHEEL)        // 滚轮消息
        {
            g_BlockSize += mouseMsg.wheel / 120;
        }
    }

    // 如果没有滚轮消息就退出
    if (lastBlockSize == g_BlockSize) return true;
    // 处理滚轮消息
    if (g_BlockSize >= 10 && g_BlockSize <= 50)    // 块大小没有达到极限值
    {
        // 保证缩放后的地图不会比窗口小
        if (GAME_WIDTH * g_BlockSize < WIN_WIDTH ||
            GAME_HEIGHT * g_BlockSize < WIN_HEIGHT)
            g_BlockSize = lastBlockSize;
        rePaintMap();                            // 重绘地图
        // 重新计算玩家在地图上的位置
        POINT mapPos = { g_PlayerPos.x / lastBlockSize,g_PlayerPos.y / lastBlockSize };    // 计算在地图上的位置
        g_PlayerPos.x = mapPos.x * g_BlockSize + g_BlockSize / 2;    // 计算映射后的位置
        g_PlayerPos.y = mapPos.y * g_BlockSize + g_BlockSize / 2;    // 计算映射后的位置
        computeCameraPos();                        // 重新计算摄像机位置
    }
    // 保证图块不会过大和过小
    if (g_BlockSize < 10) g_BlockSize = 10;
    if (g_BlockSize > 50) g_BlockSize = 50;

    return true;
}
//6.absdelay和帧率有关,人物移速也有关
void absDelay(int delay)// 绝对延迟指每隔指定的时间(参数:滴答值),执行一次调用vTaskDelayUntil()函数的任务。//和帧率有关
{
    static int curtime = GetTickCount();//GetTickCount是一种函数。GetTickCount返回(retrieve)从操作系统启动所经过(elapsed)的毫秒数,它的返回值是DWORD。
    static int pretime = GetTickCount();
    while (curtime - pretime < delay)
    {
        curtime = GetTickCount();
        Sleep(1);
    }
    pretime = curtime;
}
//7. //判断地图合法模块//在上面update模块中引用

bool canMove(POINT pos)//判断合法模块//在上面update模块中引用
{
    // 只要外接矩形的四个顶点不在墙壁内就必定合法
    return    g_GameMap[(pos.y - 3) / g_BlockSize][(pos.x - 3) / g_BlockSize] != WALL &&
        g_GameMap[(pos.y + 3) / g_BlockSize][(pos.x + 3) / g_BlockSize] != WALL &&
        g_GameMap[(pos.y - 3) / g_BlockSize][(pos.x + 3) / g_BlockSize] != WALL &&
        g_GameMap[(pos.y + 3) / g_BlockSize][(pos.x - 3) / g_BlockSize] != WALL;
}
//8.计算摄像机位置和防止摄像机越界在上面的bool upDate()被引用
void computeCameraPos()//判断合法模块//在上面的bool upDate()被引用
{
    // 以人物位置为中心计算摄像机的理论位置
    g_CameraPos.x = g_PlayerPos.x - WIN_WIDTH / 2;
    g_CameraPos.y = g_PlayerPos.y - WIN_HEIGHT / 2;
    
    // 防止摄像机越界
    if (g_CameraPos.x < 0)                                        g_CameraPos.x = 0;//摄像机位置的最左端
    if (g_CameraPos.y < 0)                                        g_CameraPos.y = 0;//摄像机位置的最上端
    if (g_CameraPos.x > GAME_WIDTH * g_BlockSize - WIN_WIDTH)    g_CameraPos.x = GAME_WIDTH * g_BlockSize - WIN_WIDTH;//摄像机位置的最右端
    if (g_CameraPos.y > GAME_HEIGHT * g_BlockSize - WIN_HEIGHT)    g_CameraPos.y = GAME_HEIGHT * g_BlockSize - WIN_HEIGHT;//摄像机位置的最下端
}
//9. 地图元素绘制如墙壁,加油站,终点,地面
void rePaintMap()//地图元素绘制模块
{
    g_MapImage.Resize(GAME_WIDTH * g_BlockSize, GAME_HEIGHT * g_BlockSize);    // 重置地图图片大小
    SetWorkingImage(&g_MapImage);                                // 设置地图图片为当前工作图片
    for (int i = 0; i < GAME_HEIGHT; i++)
    {
        for (int j = 0; j < GAME_WIDTH; j++)
        {
            switch (g_GameMap[i][j])
            {
            case WALL:
                drawWall({ j * g_BlockSize,i * g_BlockSize });        // 绘制墙壁
                break;
            case FILLSTATE:
                drawFillState({ j * g_BlockSize,i * g_BlockSize });    // 绘制加油站
                break;
            case GROUND:
                drawGround({ j * g_BlockSize,i * g_BlockSize });    // 绘制地面
                break;
            case ENDPOS:
                drawEndPos({ j * g_BlockSize,i * g_BlockSize });   //终点
                break;
            }
        }
    }
    SetWorkingImage();    // 复位工作图片
}
//10.如下在上面的rePaintMap()被引用


void drawWall(POINT pos)//地图绘制模块//墙壁初始化// 绘制墙壁图块的函数//在上面的rePaintMap()被引用
{
    setfillcolor(RGB(166, 210, 19));//填充颜色
    solidrectangle(pos.x, pos.y, pos.x + g_BlockSize, pos.y + g_BlockSize);//solidrectangle用于画无边框的填充矩形
}
//11.如下,在上面的rePaintMap()被引用

void drawGround(POINT pos)//地图绘制模块// 绘制地面图块的函数//在上面的rePaintMap()被引用
{
    setfillcolor(RGB(255, 255, 255));
    solidrectangle(pos.x, pos.y, pos.x + g_BlockSize, pos.y + g_BlockSize);
}
//12. 在上面的rePaintMap()被引用
void drawFillState(POINT pos)//在上面的rePaintMap()被引用
{
    drawGround(pos);

    // 绘制圆角矩形
    pos.x += g_BlockSize / 5;
    pos.y += g_BlockSize / 5;
    setfillcolor(RGB(252, 213, 11));
    solidroundrect(pos.x, pos.y, pos.x + g_BlockSize / 5 * 3, pos.y + g_BlockSize / 5 * 3, g_BlockSize / 8, g_BlockSize / 8);
}

void drawEndPos(POINT pos)//终点//在上面的rePaintMap()被引用
{
    drawGround(pos);

    // 绘制圆角矩形
    pos.x += g_BlockSize / 5;
    pos.y += g_BlockSize / 5;
    setfillcolor(RGB(87, 116, 48));
    solidroundrect(pos.x, pos.y, pos.x + g_BlockSize / 5 * 3, pos.y + g_BlockSize / 5 * 3, g_BlockSize / 8, g_BlockSize / 8);
}

void drawPlayer()//在上面的draw中引用
{
    setfillcolor(RGB(252, 213, 11));
    solidcircle(g_PlayerPos.x - g_CameraPos.x, g_PlayerPos.y - g_CameraPos.y, 3);
    
}

void drawView()//在上面的draw中引用
{
    // 锁定视野
    HRGN viewArr;
    int r = int(g_BlockSize * g_ViewArray + 0.5);    // 计算视野半径
    POINT orgin = g_PlayerPos;
    orgin.x -= g_CameraPos.x;                        // 计算在屏幕上的位置
    orgin.y -= g_CameraPos.y;                        // 计算在屏幕上的位置
    viewArr = CreateEllipticRgn(orgin.x - r, orgin.y - r, orgin.x + r, orgin.y + r);    // 创建一个圆形的区域
    setcliprgn(viewArr);                            // 锁定区域

    // 绘制地图
    putimage(0, 0, WIN_WIDTH, WIN_HEIGHT, &g_MapImage, g_CameraPos.x, g_CameraPos.y);

    // 删除区域
    DeleteObject(viewArr);
    // 消除区域
    setcliprgn(NULL);
}

void endGame()// 结束游戏
{
    EndBatchDraw();                        // 这个函数用于结束批量绘制,并执行未完成的绘制任务,即结束缓冲绘制
    closegraph();                        // 关闭画布
}
void bgm()
{
    PlaySound(TEXT("bgm.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
}
void jiaobu()
{
    PlaySound(TEXT("jiaobu.wav"), SND_SYNC, SND_FILENAME | SND_ASYNC | NULL);
}
void gotoxy(int x, int y) {
    COORD pos = { x,y };
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);// 获取标准输出设备句柄
    SetConsoleCursorPosition(hOut, pos);//两个参数分别是指定哪个窗体,具体位置
}

void welcome() {
    gotoxy(15, 6);  printf("欢迎来到  熄灯迷宫小游戏 ! \n");
    gotoxy(15, 8);  printf("  玩法介绍:                                         \n");
    gotoxy(15, 10); printf("    本游戏使用方向键操控,和传统迷宫一样需要迅速找到出口,但人物视野会逐渐降低              \n");
    gotoxy(15, 12); printf("                            路上黄色方块即为“加油站”踩到可以获得最大视野,绿色方块即为终点                                     \n");
    gotoxy(15, 20); printf("                                                                                        祝你玩得开心\n\n\n\n\n\n");
    system("pause"); system("cls");//清除屏幕
    
}
void ghost()
{
    int ball_x, ball_y;        //小球位置坐标
    int ball_vx = 1;            //小球X轴与Y轴的速度
    int ball_vy = 1;
    //随机生成小球初始位置
    srand((unsigned)time(NULL));
    ball_x = rand() % 860 + 20;
    ball_y = rand() % 460 + 20;
    initgraph(900, 500);//900+500
    
    while (1)
    {
        setcolor(YELLOW);
        setfillcolor(GREEN);
        fillcircle(ball_x, ball_y, 20);
        Sleep(2);
        FlushBatchDraw();        //将之前的绘图输出,这个函数用于执行未完成的绘图人物。
        
        setcolor(WHITE);
        setfillcolor(WHITE);
        
        fillcircle(ball_x, ball_y, 20);
        ball_x = ball_x + ball_vx;
        ball_y = ball_y + ball_vy;
        //当小球表面接触墙壁时发生碰撞反弹
        if (ball_x <= 20 || ball_x >= 900)
            ball_vx = -ball_vx;
        if (ball_y <= 20 || ball_y >= 500)
            ball_vy = -ball_vy;
        
    }
    EndBatchDraw();        //结束批量绘制,并执行未完成的绘图任务
    closegraph();
    
}






  • 写回答

1条回答 默认 最新

  • 於黾 2022-05-18 15:32
    关注

    我没有看你的代码,但我知道你问题出在哪
    你小球的函数只应该规定小球出现在哪,而不应该有任何绘制的代码
    绘制应该是根据人物的位置、视野、视野里能看到的东西,统一进行绘制,而不是每个函数绘制自己的东西

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 系统已结题 5月27日
  • 已采纳回答 5月19日
  • 创建了问题 5月18日

悬赏问题

  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作