跨平台的pong乒乓游戏代码

《现代C++编程实战:从入门到应用》

第4章 语句

4.6 项目实战: 控制台Pong游戏

4.6.1 Pong 游戏的起源与影响

1972 年,美国雅达利(Atari)公司开发了《PONG》街机游戏,被认为是电子游戏历史的起点。这个游戏模拟了乒乓球比赛,玩家使用挡板(由两条竖线表示)击打一个小球(一个点)并试图让对方无法接住,以获取得分。

最初,雅达利的创始人诺兰·布什内尔(Nolan Bushnell)和泰德·达布尼(Ted Dabney)指派刚加入公司的工程师艾尔·康(Al Alcorn)开发这个游戏,并告诉他这是为 GE 公司开发的项目。尽管 Alcorn 没有游戏开发经验,但他仍然投入大量精力完成了原型。当这款游戏被安装到酒吧后,迅速引起轰动,人们争相体验,最终促使雅达利开始量产《PONG》,并带动了整个电子游戏行业的发展。随后,日本的 Taito 公司开发了类似的游戏《Elepong》,成为日本的第一款电子游戏。

下面是跨平台的C++代码:

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>

// 判断操作系统
#define _WIN32
#ifdef _WIN32
#include <conio.h>
#include <windows.h>
#else
#include <unistd.h>
#include <term.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#endif

using namespace std;

#ifdef _WIN32
void gotoxy(int x, int y) {
    COORD coord = { x, y };
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void hideCursor() {
    CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

#else
void gotoxy(int x, int y) {
    // 使用 ANSI 转义码进行移动
    cout << "\033[" << y << ";" << x << "H";
}

void hideCursor() {
    // 使用 ANSI 转义码隐藏光标
    cout << "\033[?25l";
}

// 非阻塞键盘输入检测
bool _kbhit() {
    struct termios oldt, newt;
    int ch;
    bool ret = false;

    tcgetattr(STDIN_FILENO, &oldt); // 获取终端原始设置
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO); // 禁用规范模式和回显
    newt.c_cc[VMIN] = 1; // 至少读取一个字符
    newt.c_cc[VTIME] = 0; // 不设置超时
    tcsetattr(STDIN_FILENO, TCSANOW, &newt); // 设置新的终端设置

    if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1) {
        std::cerr << "Error setting non-blocking mode" << std::endl;
    }

    ch = getchar();
    if (ch != EOF) {
        ret = true;
        ungetc(ch, stdin); // 将字符放回标准输入流
    }

    tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // 恢复终端设置
    return ret;
}

// 获取一个字符输入,类似于 _getch()
char _getch() {
    setTerminalToRaw(); // 设置终端为原始模式
    char ch = getchar(); // 获取一个字符输入
    restoreTerminal(); // 恢复终端设置
    return ch;
}

#endif

int main() {
    // 1. 初始化游戏中的数据
    auto WIDTH{ 90 }, HEIGHT{ 25 }; // 窗口长宽
    auto ball_x{ WIDTH / 2 }, ball_y{ HEIGHT / 2 }, ball_vec_x{ 0 }, ball_vec_y{ 0 }; // 球位置及速度
    auto paddle_w{ 3 }, paddle_h{ 8 };   // 挡板的长宽
    auto paddle1_x{ 0 }, paddle1_y{ HEIGHT / 2 - paddle_h / 2 }, paddle1_vec{ 3 }; // 挡板1位置及速度
    auto paddle2_x{ WIDTH - paddle_w },
        paddle2_y{ HEIGHT / 2 - paddle_h / 2 }, paddle2_vec{ 3 }; // 挡板2位置及速度
    auto score1{ 0 }, score2{ 0 }, score1_x{ paddle_w + 8 }, score1_y{ 2 },
        score2_x{ WIDTH - 8 - paddle_w }, score2_y{ 2 };

    srand((unsigned)time(0));  // 生成随机数种子    
    ball_vec_x = rand() % 5 + 1; // 生成一个随机整数
    ball_vec_y = rand() % 5 + 1;
    if (rand() % 2 == 1) ball_vec_x = -ball_vec_x;
    if (rand() % 2 == 1) ball_vec_y = -ball_vec_y;

    while (true) {
        // 1. 处理事件
        char key;
        if (_kbhit()) {                          // 键盘有输入
            key = _getch();                    // 得到输入的键值
            if ((key == 'w' || key == 'W') && paddle1_y > paddle1_vec)
                paddle1_y -= paddle1_vec;
            else if ((key == 's' || key == 'S') && paddle1_y + paddle1_vec + paddle_h < HEIGHT)
                paddle1_y += paddle1_vec;
            else if (key == 72 && paddle2_y > paddle2_vec)
                paddle2_y -= paddle2_vec;
            else if ((key == 80) && paddle2_y + paddle2_vec + paddle_h < HEIGHT)
                paddle2_y += paddle2_vec;
        }

        // 2. 更新数据    
        std::string s1{ std::to_string(score1) }, s2{ std::to_string(score2) };

        ball_x += ball_vec_x;
        ball_y += ball_vec_y;
        if (ball_y < 0 || ball_y >= HEIGHT)           // 和上下墙碰撞,改变垂直速度方向
            ball_vec_y = -ball_vec_y;

        if (ball_x < paddle_w && ball_y >= paddle1_y && ball_y < paddle1_y + paddle_h)
        { // 和左挡板碰撞,改变水平速度的方向
            ball_vec_x = -ball_vec_x;
            score1 += 1;
        }
        else if (ball_x > WIDTH - paddle_w && ball_y >= paddle2_y
            && ball_y < paddle2_y + paddle_h)
        { // 和右挡板碰撞,改变水平速度的方向
            ball_vec_x = -ball_vec_x;
            score2 += 1;
        }
        bool is_out{ false };              // 是否跑出沟渠的bool标志
        if (ball_x < 0) { score2 += 1; is_out = true; }
        else if (ball_x > WIDTH) { score1 += 1; is_out = true; }
        if (is_out) {                  // 跑出左右沟渠,球回到中心并以新的随机速度出发
            ball_x = WIDTH / 2; ball_y = HEIGHT / 2;
            ball_vec_x = rand() % 5 + 1;
            ball_vec_y = rand() % 5 + 1;
            if (rand() % 2 == 1) ball_vec_x = -ball_vec_x;
            if (rand() % 2 == 1) ball_vec_y = -ball_vec_y;
        }

        gotoxy(0, 0);  // 定位到(0,0),相当于清空屏幕
        hideCursor();  // 隐藏光标

        // 3. 绘制场景
        // 3.1 绘制背景
        for (auto x = 0; x <= WIDTH; x++)
            std::cout << '=';
        std::cout << '\n';

        for (auto y = 0; y <= HEIGHT; y++) {
            for (auto x = 0; x <= WIDTH; x++) {
                if (x == ball_x && y == ball_y)                     // 球的位置
                    std::cout << 'O';
                else if (y >= paddle1_y && y < paddle1_y + paddle_h
                    && x >= paddle1_x && x < paddle1_x + paddle_w) {   // 左挡板位置
                    std::cout << 'Z';
                }
                else if (y >= paddle2_y && y < paddle2_y + paddle_h
                    && x >= paddle2_x && x < paddle2_x + paddle_w) {   // 右挡板位置
                    std::cout << 'Z';
                }
                else if (y == score1_y && x == score1_x) { // 左分数位置
                    std::cout << s1;
                    x += s1.size();
                    x--;
                }
                else if (y == score2_y && x == score2_x) { // 右分数位置
                    std::cout << s2;
                    x += s2.size();
                    x--;
                }
                else if (x == 0 || x == WIDTH / 2 || x == WIDTH) // 三条竖线
                    std::cout << '|';
                else std::cout << ' ';
            }
            std::cout << '\n';
        }

        for (auto x = 0; x <= WIDTH; x++)
            std::cout << '=';
        std::cout << '\n';
    }

    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容