实验材料

实验任务

(1) 进一步掌握数组的定义与使用;进一步掌握函数的定义和函数调用方法;
(2) 学习和掌握结构体的定义和使用方法。
(3) 进一步掌握 C 语言的编程方法;学习动画程序的基本设计思想和方法。
(4) 编译并运行你的程序。调试正确后将原程序工程文件目录压缩后提交到 Blackboard。其中压缩文件名称的前两个字母为你的姓与名的拼音的首字母。
(5) 提交正式的实验报告

设计思想

在计算机中如何生成动画?

所谓动画,实际上是按照一定的时间间隔显示的图像,在这些图像的每一帧之间都有一些不同。在计算机中,每一帧图像是以内存中的一个二维数组的形式存储的。数组中的每一个元素的值代表图像中的一个像素值。由于在 VC6 的集成开发环境的控制台窗口中可以显示 25 行 80 列的字符,因此该窗口一帧图像的大小最大为 2580 个像素。在本次试验中,动画中图像的大小规定为 24 行 79 列。因此,可以定义一个 2480 的二维数组,该数组的最后 1 列存储字符串结束标志”\0”,以便可以使用字符串函数的形式显示二维数组中的每一行字符。

要想使一个图像序列在连续显示时看起来像动画,每一帧图像在屏幕上的停留时间要基本与人眼的视觉暂留时间相适应。因此在显示每一帧图像以后,还要继续适当延时,然后再进行下一帧图像的显示。因此你的模拟程序中需要有一个延时函数,以控制每一帧图像的显示时间。

要想让动画连续不断地进行,还需要设计一个不限定循环次数的循环。

如何在一个二维数组中绘制一幅图像?

首先,需要对数组元素进行初始化。初始化的实质是将背景图像重新写入到二维数组中。
然后,将要绘制的图形以像素点的形式写入对应的二维数组元素中,二维数组中的每个元素对应于一个像素点。

如何显示二维数组中的图像?

图像显示的实质,就是将二维字符数组中存储的每个字符输出到屏幕上。在本次实验的程序中,实际就是输出到控制台窗口中。由于图像以字符阵列的形式存储在二维数组中,因此,可以用一个字符串输出的循环实现。

在本次实验的程序中,为了加快字符数组的显示过程,在二维数组的每一行的最后一个元素中,可以写入字符串结束标志:”\n”,然后用字符串输出函数显示二维数组的每一行字符。

如何让一个弹球运动?

  1. 定义描述一个弹球的结构体 BALL,一种可能的形式如下:
struct BALL{char body[2];    //两个不同的字符,分别代表两个不同颜色的球int sel;       //当前球的颜色。0表示第一种颜色,1表示第二种颜色int wX;    //在二维数组中,球在x方向的实际显示位置(整数) int wY;  //在二维数组中,球在y方向的实际显示位置(整数) double X;    //球在x方向的精确位置(实数)double Y; //球在y方向的精确位置(实数) double dX;   //球在x方向的速度(实数) double dY; //球在y方向的速度(实数)
};

其中,结构体中的每一个成员的说明如上所示。

  1. 对弹球 BALL 结构体的每一个元素进行初始化
    为了使模拟程序看起来更自然,我们可以用随机数对其进行初始化:

随机生成0、1最为当前弹球的颜色值 sel;
随机生成 1-22 之间的随机数,最为当前弹球的行坐标位置 wX,X;
随机生成 1-77 之间的随机数,最为当前弹球的列坐标位置 wY,Y;
每个弹球的速度大小都是1,但速度的方向θ是一个0-359之间的随机数,表示角度。这样它的
X、Y方向的速度分量分别为:

 dX = cos(πθ/180); dY = sin(πθ/180);
  1. 弹球根据自己的速度,移动一步
    弹球运动的实质是改变弹球当前的位置。由于弹球在X、Y方向的速度分量dX、dY都为 < 1 的值,因此弹球一步运动后的精确位置是两个实数分量:
 X = X + dX;Y = Y + dY;

但是,弹球在二维数组图像中的显示位置是二维数组的行、列两个下标,只能是整数值。因此, 需要对弹球当前的精确实数位置进行四舍五入取整,得到实际显示的数组行、列位置wX、wY。可以用下面的方法实现四舍五入取整:

 wX = (int)( X + 0.5);wY = (int)( Y + 0.5);

如何检测弹球撞到了墙壁?如何弹回来?

假设,弹球当前的位置是(X,Y),弹球运动一步以后的位置是:

 X = X + dX;Y = Y + dY;

假设表示图像的二维字符数组有24行,则若 X<0,则说明弹球撞到了上面的墙壁;X>23,则说明弹球撞到了下面的墙壁。
检测到弹球撞墙壁后,弹球应该被弹回。也就是说弹球的速度分量需要改变方向,并且被弹回到上次的位置。具体可用下面数学模型实现:

 dX = - dX;X = X + dX;

对弹球在左、右方向(即 Y方向)的撞墙检测,以及被弹回的原理同上。

如何检测两个弹球相撞?

首先,根据两个弹球的当前位置(X1,Y1)、(X2,Y2),计算它们之间的距离:

 dist = sqrt(pow(X1 - X2, 2) + pow(Y1 – Y2, 2));

然后,若 dist < 1,则可判定两个弹球相撞。

如何让弹球的速度方向改变 90 度?

若弹球当前的速度矢量为(dX1,dY1),则方向改变90度后的速度矢量(dX2,dY2)为:

 dX2 = dY1 dY2 = dX1

实验源代码

由于是C语言程序设计课程,老师不允许使用c++的封装方法,也不允许调用图形库。因此代码写得艰难。其中一些条条框框我认为不妥,例如碰撞后90°拐弯,明显与常识不符。

有基于此,我并没有严格按照实验要求完成,而是做了部分调整。用每秒钟40帧的刷新频率,尝试完成了此实验。

实验中设计了球与球的完全弹性碰撞、实现了球与边界的碰撞,并且统计了与下边界的次数(实验中有要求)。源代码和注释如下:

//此间彼方流浪,分不清决绝和迷惘
//2020.6.19
//曹弈轩 2019282129#include<stdio.h>
#include<math.h>
#include<Windows.h>
#include<time.h>
#include<stdlib.h>//界面的长和宽
#define HIGN 10
#define WIDTH 40//暂定球与球之间的距离≤1时视为碰撞
#define REACH 1   #define PI 3.14159//圆周率
#define NUM 10  //球的最大数量
int COUNT = 0;struct BALL {char body;//单个字符,表示球在dos控制台应有的形态int sel; //当前球的颜色。0表示第一种颜色,1表示第二种颜色int wX; //在二维数组中,球在x方向的实际显示位置(整数)int wY; //在二维数组中,球在y方向的实际显示位置(整数)double X; //球在x方向的精确位置(实数)double Y; //球在y方向的精确位置(实数)double dX; //球在x方向的速度(实数)double dY; //球在y方向的速度(实数)
};void Manage(struct BALL*, int);//每一个周期进行的一次处理
void print_pos(struct BALL*, int);//一组球的输出函数
void swap(double*, double*);//double类型的交换函数
void color(const unsigned short);//设定颜色的函数int main() {srand(time(NULL));printf("请输入球的个数:");int num;//球的个数scanf("%d", &num);if (num > NUM)num = NUM;struct BALL* ball = (struct BALL*)malloc(sizeof(struct BALL) * num);for (int i = 0; i < num; i++) {(ball + i)->sel = rand() % 15 + 1;    //颜色(ball + i)->X = rand() % WIDTH + 1;   //x精确坐标(ball + i)->Y = rand() % HIGN + 1; //y精确坐标//此判断看似多余,其实是为了防止有些时候,球被“撞”出边界,//以至于常年平行于边界低速运动,按正常的四舍五入无法显示出来if ((ball + i)->X < 1)                          //边界情况(ball + i)->wX = 1;else if ((ball + i)->X >WIDTH)                 //边界情况(ball + i)->wX = WIDTH;else (ball + i)->wX = (int)((ball + i)->X+0.5); //四舍五入if ((ball + i)->Y < 1)                         //边界情况(ball + i)->wY = 1;                          else if ((ball + i)->Y > HIGN)                   //边界情况(ball + i)->wY = HIGN;else(ball + i)->wY = (int)((ball + i)->Y+0.5);   //四舍五入(ball + i)->body = 'o';//球是圆的,所以直接全部设为小写字母o//速度的初始化,大小为一个单位,方向随机生成double xita = rand() % 360;(ball + i)->dX = cos(PI * xita / 180);(ball + i)->dY = sin(PI * xita / 180);}while (TRUE){//  system("CLS");//清屏,但我不用此法。下为更优方法,来自周宇航大佬。/**************************************************************/HANDLE hOut;COORD pos={0,0};hOut = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(hOut,pos);//重设打印起点CONSOLE_CURSOR_INFO cci;GetConsoleCursorInfo(hOut, &cci);cci.bVisible = FALSE;SetConsoleCursorInfo(hOut, &cci);//隐藏光标/**************************************************************/print_pos(ball, num);Manage(ball, num);printf("落地次数:%d", COUNT);Sleep(25);//休眠25毫秒}free(ball);          //其实这条是多余的return 1;         //这个程序不可能会有正常的返回值0,所以如果返回,则一定是非0的
}//显示操作台和某球的实际位置
void print_pos(struct BALL* p, int num) {//上边界for (int i = 0; i < WIDTH + 2; i++)putchar('*');putchar('\n');//中间部分for (int i = 1; i <= HIGN; i++) {putchar('|');for (int j = 1; j <= WIDTH; j++) {short flag = 1;for (int k = 0; k < num; k++) {//这个循环的目的是,看一看是否在该位置已有一个(或多个)球//如果有一个球,马上break;//如果多个球,在第一个球就已经break,了。这一瞬间两球重影(肉眼无法察觉。)//这样做看似不美观不简洁,但是不这样做,可能导致右边界被“撞出”。if ((p + k)->wX == j && (p + k)->wY == i) {color((p + k)->sel);putchar((p + k)->body);color(7);flag = 0;break;}}if (flag)putchar(' ');}putchar('|');putchar('\n');}//下边界for (int i = 0; i < WIDTH + 2; i++)putchar('*');putchar('\n');}void Manage(struct BALL* p, int num) {//这里简便起见,直接将球设为质点,采用对心碰撞。//考虑球与球之间的相撞。不妨假设球的质量是一样的,无能量损失,动量守恒,即速度交换。for (int i = 1; i < num; i++) for(int j=0;j<num-i;j++)if (pow((p + i)->X - (p + j)->X, 2) + pow((p + i)->Y - (p + j)->Y, 2) <= pow(REACH,2)){swap(&(p + i)->dX, &(p + j)->dX);swap(&(p + i)->dY, &(p + j)->dY);}//以下采用指针的方式,以便处理多个球for (int i = 0; i < num; i++){//考虑左右碰壁的情况if ((p + i)->X <= 1 || (p + i)->X >= WIDTH) {(p + i)->dX = -(p + i)->dX;}//考虑上方碰壁的情况if ((p + i)->Y <= 1) {(p + i)->dY = -(p + i)->dY;}//考虑下方碰壁的情况if ((p + i)->Y >= HIGN) {(p + i)->dY = -(p + i)->dY;putchar('\7');//发出声音COUNT++;//记录落地次数}//球的位置在此发生改变了,改变量为速度乘以一个时间单位 (p + i)->X += (p + i)->dX;(p + i)->Y += (p + i)->dY;//球的显示位置随实际位置相应改变if ((p + i)->X < 1)(p + i)->wX = 1;else if ((p + i)->X > WIDTH)(p + i)->wX = WIDTH;else(p + i)->wX = (int)((p + i)->X + 0.5);if ((p + i)->Y < 1)(p + i)->wY = 1;else if ((p + i)->Y > HIGN)(p + i)->wY = HIGN;else(p + i)->wY = (int)((p + i)->Y + 0.5);}
}void swap(double* x, double* y) {double temp = *x;*x = *y;*y = temp;
}void color(const unsigned short color1)
{       /*仅限改变0-15的颜色;如果在0-15,那么实现对应的颜色。因为如果超过15,则默认白色。*/if (color1 >= 0 && color1 <= 15)SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color1);/*如果不在0-15的范围颜色,那么改为默认的颜色白色;*/elseSetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);/*颜色对应值:0=黑色                  8=灰色  1=蓝色                9=淡蓝色                  2=绿色               10=淡绿色           0xa          3=湖蓝色               11=淡浅绿色      0xb 4=红色                 12=淡红色           0xc  5=紫色                13=淡紫色           0xd          6=黄色                14=淡黄色           0xe          7=白色                15=亮白色           0xf也可以把这些值设置成常量。*/
}

一部分说明

由于不让调第三库,所以不可能做出非常好的动画效果。另一方面,在二维平面上球与球之间的碰撞是非常复杂的。哪怕是完全弹性碰撞,在能量守恒、动量守恒的前提下,考虑碰撞位置、冲量大小和方向的不同,可能出现无穷多解。

因此,我全部质点化处理,把球的碰撞直接处理为速度交换或不妥当的。
程序运行的效果如下:

囿于当时的有限水平和悲伤心情,不足之处,敬请谅解。

深圳大学电信学院《C程序设计》期末大作业:《在二维封闭房间中的弹球模拟程序》相关推荐

  1. java期末大作业设计_java程序设计-期末大作业报告模板.doc

    云南大学软件学院报告 Java programming – final Report School of Software, Yunnan University 个人成绩 序号学号姓名成绩12345 ...

  2. linux程序设计教程期末考试,LINUX程序设计期末大作业

    b) 很多公共服务场所都有取号机,用来给用户进行编号,如医院,银行等.试编写一个socket客户机/服务器程序,用来模拟取号机.实现的功能如下: (1).客户机连接服务器 (2).服务器收到客户机连接 ...

  3. 《Python数据分析与挖掘》实战项目 - Python程序设计(期末大作业、课程设计、毕业设计)2012-2021近十年考研英语一真题词汇词频统计与可视化(附代码)

    <Python数据分析与挖掘> - 2012-2021近十年考研英语一真题词汇词频统计与可视化 声明 本文仅在CSDN发布,其他均为盗版.请支持正版! 正版链接: https://blog ...

  4. 游戏物品管理系统 - 程序设计期末大作业

    游戏物品管理系统 github: https://github.com/okfanger/xiaofang-adv @email: lovefyj616@foxmail.com 本管理系统可以存储同一 ...

  5. 深圳大学电信C语言期末大作业 弹球模拟

    深圳大学电信C语言期末大作业 弹球模拟 实验16-17:综合实验 4-在二维封闭房间中的弹球模拟程序 实验任务: (1) 进一步掌握数组的定义与使用:进一步掌握函数的定义和函数调用方法: (2) 学习 ...

  6. python小游戏_课程设计_期末大作业——小游戏合集(含源代码)

    设计小游戏盒子 本文代码链接 点击直接下载 https://download.csdn.net/download/Wps1919/87103302?spm=1001.2014.3001.5501 如将 ...

  7. HTML5期末大作业:商城网页设计——仿京东商城网页端模板(8页面) HTML+CSS+JavaScript...

    HTML5期末大作业:商城网页设计--仿京东商城网页端模板(8页面) HTML+CSS+JavaScript 商城网页HTML代码 学生网页课程设计期末作业下载 商城大学生网页设计制作成 临近期末, ...

  8. web前端期末大作业:文化网站设计——中国风文化html源码(6个页面) HTML+CSS+JavaScript...

    web前端期末大作业:文化网站设计--中国风文化html源码(6个页面) HTML+CSS+JavaScript 期末作业HTML代码 临近期末, 你还在为HTML网页设计结课作业,老师的作业要求感到 ...

  9. HTML5期末大作业:旅游网站设计——随行旅游网 HTML+CSS+JavaScript

    web课程设计网页规划与设计:旅游网站设计--绿色的随行旅游网站页面模板html源码 HTML+CSS+JavaScript 期末作业HTML代码 学生网页课程设计期末作业下载 动漫大学生网页设计制作 ...

  10. HTML5期末大作业:关于旅游网站设计——汉中印象景点 HTML+CSS+JavaScript

    HTML5期末大作业:关于旅游网站设计--汉中印象景点介绍HTML+CSS+JavaScript 期末作业HTML代码 学生网页课程设计期末作业下载 大学生网页设计制作成 临近期末, 你还在为HTML ...

最新文章

  1. 电脑显示没有被指定在上运行_win10系统运行程序提示“dll没有被指定在windows上运行”的办法...
  2. EOS与以太坊有哪些区别?
  3. 十条不错的编程观点(转载)
  4. 深入浅出mfc随笔——MFc程序的生死因果
  5. eclipse安装快速打开项目所在位置的插件
  6. android多行文本框hint居中,在安卓等移动浏览器中placeholder中的文字不垂直居中问题...
  7. dokcer 运行和进入容器
  8. kotlin 判断数字_Kotlin程序检查给定数字是正数,负数还是零
  9. 计算机辅助园林设计常用软件,计算机辅助园林设计应用探讨.doc
  10. 线程编程(thread programming)介绍
  11. [置顶] Android系统移植与调试之-------build.prop文件详细赏析
  12. ld.so.conf.d配置文件
  13. mysql 合并_MySQL——合并查询结果
  14. word转pdf转换器免费版是一款专业将word文档转换成pdf文件的软件,完美支持在线word转换成pdf,可将office word文档doc、docx、wps格式转换成PDF格式。word转pd
  15. (七)数字后端之形式验证
  16. matlab如何编newton-raphson,Matlab中的Newton-Raphson方法
  17. C语言 switch 企业奖金,企业发放的奖金根据利润提成 switch 或 if 语句编程 C++源代码...
  18. 正则验证车牌号码(包括新能源车牌)
  19. 自学计算机等级可以在哪学,计算机二级怎么自学
  20. 一级域名和二级域名的差异

热门文章

  1. 双高教育建设与混合制校企合作
  2. 机器人焊钳选型_焊接机器人选型资料
  3. 360扫描出来html木马,你的电脑真的做好防护了吗?使用360安全卫士木马查杀一键扫描就知道...
  4. UDID 和 UUID 的问题
  5. latex如何插入图片格式
  6. 荣耀10手机计算机科学计算器,荣耀赵明回应手机计算器10%问题:国外计算小费使用...
  7. 视频网关是什么,视频接入网关技术作用
  8. 出售主题HTML代码,房地产HTML主题
  9. Blender插件安装不显示问题
  10. 史上最强窃密软件来袭,手机或成泄密工具