You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ncurses中实现游戏窗口与暂停窗口的切换?

NCurses自定义窗口实现游戏暂停功能的问题

我用C语言开发游戏,未使用stdscr,而是自定义了名为basewin的窗口。需求如下:

  • 按下P键时,打开pausewin窗口并暂停basewin中的游戏
  • 按下R键时,关闭pausewin并恢复游戏,且basewin需保持暂停前的状态

我原本认为用窗口实现该需求有以下优势:

  • 代码更简洁
  • 窗口是内存空间指针,可轻松恢复basewin
  • 能作为通用游戏暂停方案,避免退出pausewin后使用printw出现问题

但该思路未成功实现,我编写了简化版测试代码(如下),但在标记位置出现问题。尝试过用tempwin但仅生效一次,也试过overwrite,似乎并非正确解决方法。

#include <ncurses.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <stdlib.h> 
    
int main() {
    /* INIT NCURSES */ 
    initscr(); 
    curs_set(0); 
    noecho();
    
    /* INIT PAUSE WIN */
    WINDOW *pausewin = newwin(LINES/3, COLS/3,LINES/3, COLS/3);
    keypad(pausewin,TRUE);
    int cMax_pause, lMax_pause; getmaxyx(pausewin,lMax_pause, cMax_pause);
    box(pausewin,0,0);
    mvwprintw(pausewin,1,cMax_pause/2 -5,"PAUSE WIN");
    mvwprintw(pausewin,lMax_pause/2, cMax_pause/3-4, "[R]ESUME");
    mvwprintw(pausewin,lMax_pause/2, 2*cMax_pause/3-3, "[Q]UIT");
    
    /* INIT BASE WIN */
    WINDOW *basewin = newwin(LINES, COLS/2, 0, COLS/4);
    keypad(basewin,TRUE);
    int lMax, cMax; getmaxyx(basewin, lMax, cMax);
    box(basewin,0,0);
    mvwprintw(basewin,0,2, "[P]AUSE");
    mvwprintw(basewin,1,cMax/2 -4,"BASE WIN");
    
    /* INIT VAR */
    bool loop = true;
    bool pause = false;
    int ch,chp;
    
    /* INIT VARS PLAYER */
    int pline = lMax/2;
    int pcol = cMax/2;
    char pch[2] ; pch[0] = 'M';
    
    /* AFFICHAGE BASE WIN : PRE-LOOP */
    wrefresh(basewin);
    
    /* "GAME" LOOP */
    while(loop){
        
        ch = wgetch(basewin);
        switch(ch){
            case 'p': /* On met le jeu en pause */
            case 'P':
                pause = true;
                break;
            case KEY_LEFT: /* Sinon deplacement */
                pcol -= 1;
                break;
            case KEY_RIGHT:
                pcol += 1;
                break;
            case KEY_UP:
                pline -= 1;
                break;
            case KEY_DOWN:
                pline += 1;
                break;
            default:
                break;
        }
    
    if (pause){
        wrefresh(pausewin);  // HERE IS THE PLACE I HAVE MY PROBLEM
        while(pause){
            chp = wgetch(pausewin);
            switch (chp){
            case 'q':
            case 'Q':
                pause = false;
                loop = false;
                break;
            case 'r':
            case 'R':
                pause = false;
                wrefresh(basewin); // HERE TOO
                break;
            default:
                break;
            }
        }
        
    }
    
    else{
        mvwprintw(basewin,pline,pcol,pch);
    }
    
    wrefresh(basewin);
    }
    
    
    delwin(pausewin);
    delwin(basewin);
    endwin();
    return EXIT_SUCCESS;
}

问题根源与解决方法

核心问题

当前代码的问题在于:

  1. 打开pausewin时,没有保存basewin的当前画面状态,导致pausewin覆盖basewin区域后,恢复时无法还原之前的游戏画面
  2. 恢复时直接调用wrefresh(basewin),但ncurses的窗口刷新机制不会自动处理被其他窗口覆盖的内容,需要手动还原状态

修正方案

使用copywin创建临时窗口保存basewin的状态,暂停时显示pausewin,恢复时用临时窗口的内容覆盖basewin后再刷新。

修改后的完整代码:

#include <ncurses.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <stdbool.h> // 补充bool类型头文件
    
int main() {
    /* INIT NCURSES */ 
    initscr(); 
    curs_set(0); 
    noecho();
    cbreak(); // 添加cbreak模式,确保按键立即响应
    
    /* INIT PAUSE WIN */
    WINDOW *pausewin = newwin(LINES/3, COLS/3, LINES/3, COLS/3);
    keypad(pausewin, TRUE);
    int cMax_pause, lMax_pause; 
    getmaxyx(pausewin, lMax_pause, cMax_pause);
    box(pausewin, 0, 0);
    mvwprintw(pausewin, 1, cMax_pause/2 - 5, "PAUSE WIN");
    mvwprintw(pausewin, lMax_pause/2, cMax_pause/3 - 4, "[R]ESUME");
    mvwprintw(pausewin, lMax_pause/2, 2*cMax_pause/3 - 3, "[Q]UIT");
    wrefresh(pausewin); // 预先绘制暂停窗口,避免第一次显示时闪烁
    wclear(pausewin); // 清空暂停窗口的缓冲区,避免干扰后续显示
    
    /* INIT BASE WIN */
    WINDOW *basewin = newwin(LINES, COLS/2, 0, COLS/4);
    keypad(basewin, TRUE);
    int lMax, cMax; 
    getmaxyx(basewin, lMax, cMax);
    box(basewin, 0, 0);
    mvwprintw(basewin, 0, 2, "[P]AUSE");
    mvwprintw(basewin, 1, cMax/2 - 4, "BASE WIN");
    
    /* 用于保存basewin状态的临时窗口 */
    WINDOW *tempwin = newwin(LINES, COLS/2, 0, COLS/4);
    
    /* INIT VAR */
    bool loop = true;
    bool pause = false;
    int ch, chp;
    
    /* INIT VARS PLAYER */
    int pline = lMax/2;
    int pcol = cMax/2;
    char pch[2] = {'M', '\0'}; // 修正字符串初始化
    
    /* AFFICHAGE BASE WIN : PRE-LOOP */
    mvwprintw(basewin, pline, pcol, pch);
    wrefresh(basewin);
    
    /* "GAME" LOOP */
    while(loop){
        
        ch = wgetch(basewin);
        switch(ch){
            case 'p': 
            case 'P':
                pause = true;
                // 保存basewin当前状态到tempwin
                copywin(basewin, tempwin, 0, 0, 0, 0, lMax-1, cMax-1, FALSE);
                // 显示暂停窗口
                box(pausewin, 0, 0);
                mvwprintw(pausewin, 1, cMax_pause/2 - 5, "PAUSE WIN");
                mvwprintw(pausewin, lMax_pause/2, cMax_pause/3 - 4, "[R]ESUME");
                mvwprintw(pausewin, lMax_pause/2, 2*cMax_pause/3 - 3, "[Q]UIT");
                wrefresh(pausewin);
                break;
            case KEY_LEFT: 
                pcol = (pcol > 1) ? pcol - 1 : 1; // 防止越界
                break;
            case KEY_RIGHT:
                pcol = (pcol < cMax-2) ? pcol + 1 : cMax-2; // 防止越界
                break;
            case KEY_UP:
                pline = (pline > 1) ? pline - 1 : 1; // 防止越界
                break;
            case KEY_DOWN:
                pline = (pline < lMax-2) ? pline + 1 : lMax-2; // 防止越界
                break;
            default:
                break;
        }
    
        if (pause){
            while(pause){
                chp = wgetch(pausewin);
                switch (chp){
                case 'q':
                case 'Q':
                    pause = false;
                    loop = false;
                    break;
                case 'r':
                case 'R':
                    pause = false;
                    // 用tempwin的内容还原basewin
                    copywin(tempwin, basewin, 0, 0, 0, 0, lMax-1, cMax-1, FALSE);
                    wrefresh(basewin);
                    break;
                default:
                    break;
                }
            }
        } else {
            wclear(basewin); // 清空窗口重绘,避免残留旧的玩家位置
            box(basewin, 0, 0);
            mvwprintw(basewin, 0, 2, "[P]AUSE");
            mvwprintw(basewin, 1, cMax/2 - 4, "BASE WIN");
            mvwprintw(basewin, pline, pcol, pch);
            wrefresh(basewin);
        }
    }
    
    delwin(tempwin);
    delwin(pausewin);
    delwin(basewin);
    endwin();
    return EXIT_SUCCESS;
}

关键修改点说明

  1. 添加stdbool.h头文件,修复bool类型未定义问题
  2. 创建tempwin临时窗口,用于保存basewin的状态
  3. 按下P键时,用copywin将basewin的当前内容复制到tempwin,然后显示pausewin
  4. 按下R键时,用copywin将tempwin的内容复制回basewin,再刷新恢复画面
  5. 优化玩家移动的边界判断,防止越界
  6. 游戏循环中重绘basewin的边框和固定文本,避免移动时丢失界面元素

内容的提问来源于stack exchange,提问作者rm-star

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 15:10:53