C语言实现扔香蕉的大猩猩游戏(附带源码)

C语言实现扔香蕉的大猩猩游戏 —— 项目介绍与实现详解

一、项目背景与简介

“扔香蕉的大猩猩”游戏最早出现在 QBasic 的经典游戏《Gorillas》中,两个大猩猩分别位于高楼之间,玩家通过调整投掷角度和力度,使香蕉按抛物线轨迹飞行,争取击中对方大猩猩。该游戏不仅考验玩家对物理知识(抛物线运动)的理解,同时也是编程入门中的经典案例。

本项目旨在使用 C 语言实现一个简化版的“扔香蕉的大猩猩”游戏,通过命令行进行交互,利用物理公式模拟香蕉的运动轨迹,并判断是否命中对手。项目不仅适合初学者熟悉 C 语言的基本语法、流程控制、函数调用以及数学计算,同时也展示了如何构建一个简单的游戏循环和图形(文本)显示效果。

该项目整体分为以下几部分:

  • 游戏显示与交互: 利用简单的文本方式显示两个大猩猩在屏幕(模拟二维坐标)上的位置,同时提示玩家输入投掷角度和速度。
  • 物理模拟: 根据物理公式计算抛体运动轨迹(即香蕉的运动轨迹),用时间步长进行离散化模拟,并在每一步检查是否碰撞目标。
  • 碰撞检测: 当香蕉在飞行过程中,其位置与目标大猩猩的坐标距离在一定范围内(定义为“命中半径”)时,判定为命中目标,游戏结束。
  • 回合制对战: 游戏采用双人对战的方式,轮流输入参数进行投掷,直至有一方击中对手为止。

通过这个项目,读者可以了解如何将数学模型与编程实现相结合,从而开发一个简单但完整的游戏应用。

二、项目实现思路

在实现“扔香蕉的大猩猩”游戏的过程中,主要需要解决以下几个问题:

  1. 坐标系与显示问题:
    游戏采用二维坐标系,水平坐标用 x 表示,竖直坐标用 y 表示。为了简化,我们将屏幕(或游戏区域)定义为固定大小的矩阵,例如宽 80 个单位、高 25 个单位,并在矩阵中以不同字符标记两个大猩猩的位置。

  2. 物理模型的建立:
    香蕉的运动遵循经典的抛物线运动规律,运动公式为:

    • 水平位移:x = x0 + v * cos(θ) * t
    • 垂直位移:y = y0 + v * sin(θ) * t - 0.5 * g * t²

    其中 x0, y0 为起始坐标,v 为初始速度,θ 为投掷角度(需转换为弧度计算),g 为重力加速度(此处取 9.8 m/s²),t 为时间。采用固定时间步长(如 0.1 秒)不断计算香蕉在每个时刻的坐标,模拟其飞行过程。

  3. 碰撞检测机制:
    当香蕉飞行过程中,我们计算香蕉与目标大猩猩之间的距离。设定一个“命中半径”(例如 2 个单位),当距离小于等于此半径时,就认为香蕉击中了目标大猩猩。

  4. 游戏循环与交互:
    游戏整体采用回合制,每个回合由当前玩家输入投掷角度和初始速度。程序根据输入参数计算香蕉轨迹并显示相关飞行信息。如果未命中目标,则切换玩家进入下一回合,直到有一方成功命中对手为止。

  5. 跨平台显示(清屏效果):
    为了获得较好的显示效果,在每个回合开始时调用清屏函数(在 Windows 上调用 cls,在 Linux/Unix 上调用 clear)刷新命令行界面,显示最新的游戏状态。

通过以上思路,本项目将以模块化的设计实现各个功能模块。接下来给出完整代码,并在代码中加入详细注释,便于理解每一部分的具体实现。

三、项目完整代码

下面是整个项目的 C 语言代码,所有代码集中在一个文件中,包含详细注释:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

// 定义重力加速度常量
#define GRAVITY 9.8   // 单位:m/s²

// 定义模拟屏幕的尺寸(水平和垂直单位数,可根据需要调整)
#define SCREEN_WIDTH 80
#define SCREEN_HEIGHT 25

// 定义模拟时间步长(每步时间间隔)
#define TIME_STEP 0.1

// 定义一个结构体来表示大猩猩的位置,包含水平坐标 x 和垂直坐标 y
typedef struct {
    double x;
    double y;
} Gorilla;

// 定义一个常量表示碰撞检测的半径,当香蕉与目标距离小于等于此值时判定为命中
#define HIT_RADIUS 2.0

// ----------------------- 辅助函数区 -------------------------

/*
 * 函数名称:clearScreen
 * 函数作用:清空屏幕,用于刷新显示效果
 * 实现原理:根据操作系统调用不同的命令
 */
void clearScreen() {
    #ifdef _WIN32
        system("cls");    // Windows 系统下使用 cls 命令清屏
    #else
        system("clear");  // Unix/Linux 系统下使用 clear 命令清屏
    #endif
}

/*
 * 函数名称:checkCollision
 * 函数作用:检测香蕉是否与目标大猩猩发生碰撞
 * 参数说明:
 *      bx, by:香蕉当前的 x, y 坐标
 *      target:目标大猩猩的坐标(结构体 Gorilla)
 * 返回值:
 *      1 表示碰撞成功(击中目标),0 表示未碰撞
 * 实现原理:
 *      计算香蕉与目标大猩猩之间的欧几里得距离,并与预定义的 HIT_RADIUS 比较
 */
int checkCollision(double bx, double by, Gorilla target) {
    // 计算水平和垂直距离差
    double dx = bx - target.x;
    double dy = by - target.y;
    // 计算两点之间的直线距离
    double distance = sqrt(dx * dx + dy * dy);
    if(distance <= HIT_RADIUS)
        return 1; // 距离小于等于命中半径,判定为碰撞成功
    else
        return 0; // 否则未碰撞
}

/*
 * 函数名称:simulateBanana
 * 函数作用:模拟香蕉的飞行轨迹,并判断是否击中目标大猩猩
 * 参数说明:
 *      initX, initY:香蕉发射的初始位置(通常在发射大猩猩的上方)
 *      angleDeg:投掷角度(单位:度)
 *      velocity:投掷初始速度
 *      target:目标大猩猩的坐标,用于碰撞检测
 * 返回值:
 *      1 表示模拟过程中击中目标,0 表示未击中目标
 * 实现原理:
 *      将角度转换为弧度后,计算水平和垂直方向的速度分量,使用时间步长离散化计算香蕉在各时刻的位置,
 *      每步打印当前香蕉的位置,并检测是否与目标大猩猩发生碰撞。如果香蕉落地或飞出屏幕,则退出模拟。
 */
int simulateBanana(double initX, double initY, double angleDeg, double velocity, Gorilla target) {
    // 将角度转换为弧度(C语言数学函数使用弧度)
    double angleRad = angleDeg * M_PI / 180.0;
    // 计算初始速度在水平和垂直方向上的分量
    double vx = velocity * cos(angleRad);
    double vy = velocity * sin(angleRad);
    
    // 初始化香蕉当前位置和模拟时间
    double x = initX;
    double y = initY;
    double t = 0;
    
    // 模拟香蕉的飞行过程:只要香蕉在空中且未飞出水平范围,就持续模拟
    while (y >= 0 && x >= 0 && x <= SCREEN_WIDTH) {
        // 根据抛物线运动公式计算当前时间 t 对应的香蕉位置
        x = initX + vx * t;
        y = initY + vy * t - 0.5 * GRAVITY * t * t;
        
        // 输出当前时间和香蕉的坐标,便于观察飞行轨迹
        printf("时间: %.2f s, 香蕉位置: (%.2f, %.2f)\n", t, x, y);
        
        // 调用碰撞检测函数,检查当前香蕉位置是否击中目标大猩猩
        if (checkCollision(x, y, target)) {
            printf("击中目标!\n");
            return 1; // 返回 1 表示命中目标,结束模拟
        }
        
        // 增加时间步长,进入下一步计算
        t += TIME_STEP;
    }
    // 如果循环结束,表示香蕉已经落地或飞出预设屏幕范围,未击中目标
    printf("未击中目标,香蕉落地或飞出屏幕.\n");
    return 0;
}

/*
 * 函数名称:displayGorillas
 * 函数作用:在文本模式下显示两个大猩猩在屏幕上的位置
 * 参数说明:
 *      g1, g2:两个大猩猩的位置结构体
 * 实现原理:
 *      构建一个二维字符数组模拟屏幕,先将数组初始化为空格,
 *      然后将大猩猩所在位置标记为不同的字符(如 '1' 和 '2'),最后打印整个屏幕内容。
 */
void displayGorillas(Gorilla g1, Gorilla g2) {
    char screen[SCREEN_HEIGHT][SCREEN_WIDTH];
    
    // 初始化屏幕数组,每个字符都为空格
    for (int i = 0; i < SCREEN_HEIGHT; i++) {
        for (int j = 0; j < SCREEN_WIDTH; j++) {
            screen[i][j] = ' ';
        }
    }
    
    // 将大猩猩的位置映射到屏幕坐标(注意:屏幕 y 坐标从上到下增加)
    int g1x = (int)g1.x;
    int g1y = SCREEN_HEIGHT - 1 - (int)g1.y; // 将数学坐标系转换为屏幕坐标系
    int g2x = (int)g2.x;
    int g2y = SCREEN_HEIGHT - 1 - (int)g2.y;
    
    // 检查映射后的坐标是否在屏幕范围内,确保不会越界
    if(g1x >= 0 && g1x < SCREEN_WIDTH && g1y >= 0 && g1y < SCREEN_HEIGHT)
        screen[g1y][g1x] = '1'; // 用字符 '1' 表示大猩猩1
    if(g2x >= 0 && g2x < SCREEN_WIDTH && g2y >= 0 && g2y < SCREEN_HEIGHT)
        screen[g2y][g2x] = '2'; // 用字符 '2' 表示大猩猩2
    
    // 打印整个屏幕,展示大猩猩在场景中的位置
    for (int i = 0; i < SCREEN_HEIGHT; i++) {
        for (int j = 0; j < SCREEN_WIDTH; j++) {
            printf("%c", screen[i][j]);
        }
        printf("\n");
    }
}

// ----------------------- 主函数区 -------------------------

/*
 * 函数名称:main
 * 函数作用:游戏的入口,组织整个游戏流程,包括初始化、玩家交互、投掷模拟及游戏结束判定
 */
int main() {
    // 初始化随机数种子,用于后续若有随机元素(例如大猩猩位置随机生成)使用
    srand((unsigned)time(NULL));
    
    // 初始化两个大猩猩的位置,采用固定坐标,也可以改为随机分布
    Gorilla gorilla1, gorilla2;
    gorilla1.x = 10;             // 大猩猩1放置在屏幕左侧,x 坐标设为 10
    gorilla1.y = 0;              // y 坐标 0 表示地面高度
    gorilla2.x = SCREEN_WIDTH - 10; // 大猩猩2放置在屏幕右侧
    gorilla2.y = 0;
    
    // currentPlayer 用于记录当前回合投掷香蕉的玩家,1 表示大猩猩1发射,2 表示大猩猩2发射
    int currentPlayer = 1;
    // hit 变量标记是否有玩家成功击中对方,初始设为 0(未击中)
    int hit = 0;
    
    // 游戏主循环:只要没有一方命中对手,就不断进行回合交替
    while (!hit) {
        // 清屏刷新,确保每回合显示最新场景
        clearScreen();
        // 显示当前场景中两个大猩猩的位置
        displayGorillas(gorilla1, gorilla2);
        
        double angle, velocity;
        // 根据当前玩家显示提示信息
        if (currentPlayer == 1) {
            printf("玩家1投掷香蕉 (目标:玩家2大猩猩).\n");
        } else {
            printf("玩家2投掷香蕉 (目标:玩家1大猩猩).\n");
        }
        
        // 获取玩家输入的投掷角度(单位:度)和初始速度
        printf("请输入投掷角度(度数):");
        scanf("%lf", &angle);
        printf("请输入投掷初始速度:");
        scanf("%lf", &velocity);
        
        // 根据当前玩家确定香蕉的发射初始位置和目标大猩猩
        double initX, initY;
        Gorilla target;
        if (currentPlayer == 1) {
            initX = gorilla1.x;
            initY = gorilla1.y + 1; // 使香蕉从大猩猩头部稍上方发射
            target = gorilla2;
        } else {
            initX = gorilla2.x;
            initY = gorilla2.y + 1;
            target = gorilla1;
            // 注意:如果需要对玩家2的投掷角度进行反转处理,可以在此处加入调整,本例中要求玩家自行输入合适的角度
        }
        
        // 调用 simulateBanana 函数,模拟香蕉的飞行轨迹,并检测是否命中目标
        hit = simulateBanana(initX, initY, angle, velocity, target);
        
        // 如果本回合未命中目标,则提示玩家按回车继续,并切换到下一玩家
        if (!hit) {
            printf("按回车键继续下一回合...\n");
            getchar(); // 获取换行符,防止缓冲区干扰
            getchar(); // 等待用户按下回车
            // 切换玩家:如果当前玩家是 1,则下回合为 2;反之亦然
            currentPlayer = (currentPlayer == 1) ? 2 : 1;
        }
    }
    
    // 游戏结束后提示胜利信息
    printf("游戏结束!\n");
    return 0;
}

四、代码解读

下面对代码中各个模块进行详细解读,帮助读者更好地理解程序实现原理。

1. 头文件和常量定义

  • 头文件部分:
    程序中包含了 <stdio.h><stdlib.h><math.h><time.h> 等头文件。其中:

    • <stdio.h> 用于标准输入输出;
    • <stdlib.h> 用于系统命令(例如清屏命令)和随机数函数;
    • <math.h> 提供数学函数,如 cos()sin()sqrt() 和常量 M_PI
    • <time.h> 用于获取当前时间,以初始化随机数种子。
  • 常量定义:

    • GRAVITY 定义为 9.8,模拟真实重力加速度;
    • SCREEN_WIDTHSCREEN_HEIGHT 定义了模拟屏幕的尺寸,便于在命令行中以字符矩阵显示大猩猩的位置;
    • TIME_STEP 为 0.1,用于离散化模拟时间;
    • HIT_RADIUS 为 2.0,当香蕉与大猩猩之间距离小于等于此值时,判定为命中。

2. 结构体 Gorilla

该结构体包含两个 double 类型的成员 xy,分别表示大猩猩在水平与垂直方向上的坐标。使用结构体可以方便地传递和管理位置数据。

3. 辅助函数

  • clearScreen() 函数:
    根据当前操作系统调用 system("cls")system("clear") 实现清屏效果,使得每回合显示时界面干净、整洁。

  • checkCollision() 函数:
    通过计算香蕉当前位置 (bx, by) 与目标大猩猩位置之间的欧几里得距离,再与预定的 HIT_RADIUS 比较,判断是否发生碰撞。该函数简单而高效,适用于本项目中离散化的碰撞检测。

  • simulateBanana() 函数:
    这是本项目的核心函数,负责模拟香蕉的飞行轨迹:

    • 首先将玩家输入的角度转换为弧度,计算出初始速度在水平(vx)和垂直(vy)方向的分量;
    • 以时间 t 为变量,从 0 开始,每次增加固定的时间步长 TIME_STEP,按照物理公式计算出香蕉的新坐标;
    • 每一步调用 checkCollision() 检测是否击中目标,如果是,则打印“击中目标”的信息并返回 1;
    • 当香蕉落地(y < 0)或飞出水平范围时,结束模拟并返回 0。
  • displayGorillas() 函数:
    该函数创建一个二维字符数组,代表屏幕,将所有元素初始化为空格,然后将两个大猩猩分别标记为字符 '1''2'。注意在将数学坐标转换为屏幕坐标时,需要将 y 坐标反转(因为屏幕行号从上到下递增)。最后打印整个数组,达到简单图形化显示的目的。

4. 主函数 main

  • 初始化部分:
    利用 srand(time(NULL)) 初始化随机数种子,为后续可能的随机因素(例如随机位置扩展)做好准备。接着固定了两个大猩猩的位置,分别设置在屏幕的左侧和右侧。

  • 游戏主循环:
    采用 while (!hit) 循环进行回合制对战:

    • 每个回合开始时调用 clearScreen() 清屏,并使用 displayGorillas() 显示当前场景;
    • 根据当前玩家提示投掷香蕉的目标,并要求输入投掷角度和初始速度;
    • 根据玩家不同,确定香蕉发射的初始位置(通常在大猩猩上方一点),并设定目标大猩猩;
    • 调用 simulateBanana() 模拟香蕉飞行,若返回 1 则代表命中目标,游戏退出循环;
    • 如果未命中,则提示玩家按回车继续,并切换玩家进入下一回合。
  • 游戏结束:
    当某一回合检测到命中目标后,循环退出,程序打印“游戏结束”的提示,结束整个游戏。

5. 用户交互

程序通过标准输入输出与玩家进行交互,输入角度和速度,并在控制台上打印香蕉在飞行过程中每个时间步的位置信息,使玩家可以直观地看到抛物线轨迹以及是否命中目标。虽然本示例为文字界面,但通过合理的设计,同样能引导玩家体验物理模拟和游戏对战的乐趣。

五、项目总结

通过本项目,我们实现了一个基于 C 语言的简单“扔香蕉的大猩猩”游戏。整个项目的核心在于利用经典的抛物线运动公式来模拟物体运动,并通过离散时间步长实现近似连续运动的效果。同时,通过简单的碰撞检测算法,实现了游戏中目标检测的功能。下面总结几个关键点:

  1. 物理建模的重要性:
    本项目中利用了物理公式(水平和垂直运动分离的原理)来计算香蕉的运动轨迹,展示了数学模型在实际编程实现中的应用。通过离散化时间步长,模拟出近似连续的运动状态,这种方法在许多游戏和仿真中都有应用。

  2. 模块化编程思路:
    整个项目将清屏、碰撞检测、物体运动模拟、屏幕显示等功能分解为独立的函数。这样不仅使代码逻辑清晰、便于维护,也方便后续对各模块进行扩展,例如增加更多的游戏元素或改进碰撞检测算法。

  3. 用户交互与反馈:
    通过文本模式展示飞行轨迹和实时提示,程序为玩家提供了反馈信息。虽然界面较为简单,但通过不断刷新屏幕和回合制提示,仍然能够营造出较强的代入感。未来可以考虑引入图形界面库,实现更直观的显示效果。

  4. 扩展性与改进方向:
    本项目为一个基础版本的游戏,后续可以考虑以下扩展:

    • 图形化显示: 使用 SDL、OpenGL 或 ncurses 库实现图形界面,增强视觉效果。
    • 增加障碍物: 模拟城市建筑等障碍物,使游戏策略性更强,玩家需要考虑建筑物对香蕉飞行轨迹的影响。
    • 改进碰撞检测: 可以采用更精细的碰撞检测算法,考虑大猩猩的实际形状而非简单的圆形区域。
    • 多人对战及 AI: 除了双人对战,还可以添加 AI 对手,实现单人游戏模式。
  5. 项目收获:
    通过本项目的编写,不仅复习了 C 语言的基本语法(变量、流程控制、函数调用等),还锻炼了如何将数学公式应用于实际编程中的能力。同时,学习了如何设计简单的游戏逻辑和用户交互流程,对今后更复杂的游戏开发打下了良好基础。

总的来说,本项目是一个从理论到实践、从简单到复杂逐步实现的示例。它展示了如何利用 C 语言将经典的物理模型转化为一个可玩的小游戏,既有趣又具备实用价值。

六、参考资料

  1. 《C 程序设计语言》 —— K&R 经典著作,为本项目提供了 C 语言基础知识支持。
  2. 《游戏编程入门》 —— 介绍如何利用基本的编程技术实现简单的游戏逻辑。
  3. 物理学教材 —— 关于抛物线运动和力学基本原理的参考资料。
  4. 网络上关于 QBasic “Gorillas” 游戏的介绍及相关实现案例,提供了项目灵感和实现思路。

结语

本篇博客文章详细介绍了如何利用 C 语言实现“扔香蕉的大猩猩”游戏,从项目背景、实现思路,到完整代码及逐步解读,再到项目总结,涵盖了项目开发中的各个重要环节。希望本文能够帮助各位读者更好地理解物理模拟在编程中的应用,同时激发大家利用 C 语言开发更多有趣项目的热情。如果在实现过程中遇到任何问题,欢迎在评论区交流讨论,共同进步!

以上就是完整的 C 语言扔香蕉大猩猩游戏项目介绍,文章内容详尽、代码注释充分,后续可以根据需求进一步拓展。希望大家喜欢并有所收获!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值