1,项目描述
功能1:使用图形化的方式描述地球围绕着太阳转动,月球围绕着地球转动
功能2:在转动的过程中当用户按下1,2,3,4,5,6,7时它可以变换出7种不同的颜色,当用户按下8时它可以变换从1-7的颜色依次变换当用户再次按下8键时停止变换颜色
功能3:当用户按下上键时,地球会围绕太阳反转,当再次按下上键时地球会恢复到正转
功能4:当用户按下空格键的时候,所有动画暂停,当再次按下空格键的时候所有动画继续进行。
2,解决思路
其实纵观整个项目需求,在不考虑真实的星体运算下,它的实现原理就像是时钟转动的实现原理是一致的
对于地球围绕着太阳进行转动来说,可以将太阳作为中心点,地球围绕着这个中心点进行转动,对于月球围绕着地球进行转动来说,可以将地球作为一个中心点,月球围绕着地球进行转动,这样就实现了基本的地球,太阳和月球的三星环绕
需要考虑的是整个项目是在转动中获取按键信息做出相应的画面更新,所以按键更新画面应该是放在一个非阻塞的函数下进行,也就是使用_kbhit()来实现,将这个条件放在运行中的死循环之中,可以随时通过按键信息更新画面基本元素
当考虑到8键,空格键和上键时,它们三个键位就相当于开关,按下就开,再按就关,我可以使用开关算法完成这样的操作。
3,关键代码
开发使用IDE:Visual Studio2019
注意:
easyX库不是C++的自带库需要下载和安装,很简单
easyX库是基于C++的,所以文件的后缀名需要改为.cpp才能正常运行
首先为了方便程序的可维护性,所以最开始,使用了枚举定义了地球,月球和太阳的半径。
typedef enum RADIES {//星球半径
sunradies = 150,//太阳
earthradies = 30,//地球
moonradies = 5,//月亮
}radies;
然后为了能实现色彩的变化,也可以先定义一个颜色的枚举,颜色是基于easyX库中的。
typedef enum COLOR {//颜色
black = 0,//黑色
blue = 0xAA0000,//蓝色
green = 0x00AA00,//绿色
cyan = 0xAAAA00,//青色
red = 0x0000AA,//红色
magenta = 0xAA00AA,//紫色
brown = 0x0055AA,//棕色
lightgray = 0xAAAAAA,//浅灰
yellow = 0x55FFFF,//黄色
}color;
3.1,实现基本的地球,太阳和月球三星环绕关键代码
可以看作是三个中心点之间的不断的运算关系,最核心的是通过不断更新中心坐标的位置实现,对于太阳来说,它的中心坐标是位于窗口的中心,并且对于太阳来说它是不动的,对于地球来说,它的中心坐标是根据以太阳为中心基点,围绕着太阳进行转动的,对于月球来说,亦然,地球和月球都是行星,它们的运动是依靠着坐标点的位置变化实现,所以可以定义一个行星结构体,包含行星的半径和行星中心x和y坐标
typedef struct Plant {//行星
int radies;//行星半径
int center_x;//行星中心X坐标
int center_y;//行星中心Y坐标
}plant;
在程序中先创建行星,也就是为行星赋予基本的中心点和半径作初始化,然后,通过绘制行星,传入行星实时更新的坐标点在死循环中更新图像。
//通过传入结构体指针首先给指针所指向的内存地址中的相关属性赋初值
void creatplant(plant* p, int x, int y, int radies) {
p->center_x = x;//行星中心的初始x坐标
p->center_y = y;//行星中心的初始y坐标
p->radies = radies;//行星的半径
}
通过传入中心行星和环绕行星,使用指针来访问内存空间,通过修改内存中的数值实现实时改变终点坐标
void drawplant(plant* p1, plant* p2, double angle, int a, color c, radies r) {
p1->center_x = p2->center_x + (WIDTH / a) * sin(angle);//以p2为中心,实时改变角度改变内存的x值
p1->center_y = p2->center_y - (WIDTH / a) * cos(angle);//以p2为中心,实时改变角度改变内存的y值
setfillcolor(c);
solidcircle(p1->center_x, p1->center_y, p1->radies);//根据改变的值绘制图形
}
直接绘制太阳,因为太阳就是一个静止的状态:
//因为太阳它是固定的就是一个静态图片
void drawsun(color c, int x, int y, radies r) {
setfillcolor(c);
solidcircle(x, y, r);
}
接下来就可在主方法中进行绘制的实验了
int main(){
plant s, e, m;
color c1=red;
creatplant(&s, WIDTH / 2, HEIGHT / 2, sunradies);//创建太阳属性
creatplant(&e, WIDTH / 8, HEIGHT / 8, earthradies);//创建地球属性
creatplant(&m, WIDTH / 10, HEIGHT / 10, moonradies);//创建月球属性
while (1) {
drawsun(c1, s.center_x, s.center_y, sunradies);//画太阳
BeginBatchDraw();在内存中绘画,避免频闪
drawplant(&e, &s, eangle, 3, blue, earthradies);//传入地球,更新角度并且绘制
drawplant(&m, &e, mangle, 13, yellow, moonradies);//传入月球,更新角度并且绘制
eangle = eangle + 2 * PI / 60;//地球的角度实时变化
mangle = mangle + 2 * PI / 30;//月球的角度实时变化,分的分数应该更小如果等于60一起转动
Sleep(50); //便于看出轨迹的运动
FlushBatchDraw();//将画出的内容从内存调出
clearcircle(e.center_x, e.center_y, earthradies);//清除
clearcircle(m.center_x, m.center_y, moonradies);//清除,再画下一次的更新
}
return 0;
}
实验截图:
3.2,开关算法思想关键代码
该代码用于主方法中控制整个程序的流程,其关键部分如下,56,32,72是键值,也就是在键盘中它都有对应的值
int i = 1;//作为闪烁颜色的开关
int m1 = 1;//作为暂停的开关
int n1 = 1;//作为逆时针转动的开关
while (key != 27) {
if (_kbhit()) {//使用的非阻塞方法,也就是它不会等待事件的发生而阻塞是一直接收事件
switch (key = _getch()) {
case 56:i = i * -1; break;//当按下8键时,i=-1<0,进行颜色变换,当再按下8键时,i>0,不变
case 32:m1 = m1 * -1; break;//按下空格时<0,再按下时>0
case 72:n1 = n1 * -1; break;//按下上时<0,再按下时>0
}
}
if (i < 0) {//进行颜色的变换
color c[8] = { black,blue,red,blue,yellow,cyan,magenta,blue };
srand((unsigned)time(NULL));
int n = rand() % 8;
Sleep(1);
drawsun(c[n], s.center_x, s.center_y, sunradies);
}
if (m1 > 0) {//只有当m1>0时才会改变角度,当m1<0时不再改变
if (n1 > 0) {//当n1>0时才会正转,<0时走反转
eangle = eangle + 2 * PI / 60;
mangle = mangle + 2 * PI / 30;
}
else {
eangle = eangle - 2 * PI / 60;
mangle = mangle - 2 * PI / 30;
}
}
}
4,项目运行截图
5,具体代码实现
#include<graphics.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#define WIDTH 800//窗口的宽
#define HEIGHT 800//窗口的高
#define PI 3.14159//PI
typedef enum RADIES {//星球半径
sunradies = 150,//太阳
earthradies = 30,//地球
moonradies = 5,//月亮
}radies;
typedef enum COLOR {//颜色
black = 0,//黑色
blue = 0xAA0000,//蓝色
green = 0x00AA00,//绿色
cyan = 0xAAAA00,//青色
red = 0x0000AA,//红色
magenta = 0xAA00AA,//紫色
brown = 0x0055AA,//棕色
lightgray = 0xAAAAAA,//浅灰
yellow = 0x55FFFF,//黄色
}color;
typedef struct Plant {//行星
int radies;//行星半径
int center_x;//行星中心X坐标
int center_y;//行星中心Y坐标
}plant;
void drawsun(color c, int x, int y, radies r);//绘制太阳
void creatplant(plant* p, int x, int y, int radies);//创建星球
void drawplant(plant* p1, plant* p2, double angle, int a, color c, radies r);//绘制星球
int main()
{
plant s, e, m;
creatplant(&s, WIDTH / 2, HEIGHT / 2, sunradies);//创建太阳属性
creatplant(&e, WIDTH / 8, HEIGHT / 8, earthradies);//创建地球属性
creatplant(&m, WIDTH / 10, HEIGHT / 10, moonradies);//创建月球属性
color c1 = red;
initgraph(WIDTH, HEIGHT);
double eangle = 0;
double mangle = 0;
char key = 0;
int i = 1;//作为闪烁颜色的开关
int m1 = 1;//作为暂停的开关
int n1 = 1;//作为逆时针转动的开关
while (key != 27) {
if (_kbhit()) {
switch (key = _getch()) {
case 49:c1 = blue; break;
case 50:c1 = yellow; break;
case 52:c1 = brown; break;
case 53:c1 = magenta; break;
case 54:c1 = red; break;
case 55:c1 = lightgray; break;
case 56:i = i * -1; break;
case 32:m1 = m1 * -1; break;
case 27:break;
case 72:n1 = n1 * -1; break;
}
drawsun(c1, s.center_x, s.center_y, sunradies);
}
drawsun(c1, s.center_x, s.center_y, sunradies);
if (i < 0) {
color c[8] = { black,blue,red,blue,yellow,cyan,magenta,blue };
srand((unsigned)time(NULL));
int n = rand() % 8;
Sleep(1);
drawsun(c[n], s.center_x, s.center_y, sunradies);
}
BeginBatchDraw();
drawplant(&e, &s, eangle, 3, blue, earthradies);
drawplant(&m, &e, mangle, 13, yellow, moonradies);
if (m1 > 0) {
if (n1 > 0) {
eangle = eangle + 2 * PI / 60;
mangle = mangle + 2 * PI / 30;
}
else {
eangle = eangle - 2 * PI / 60;
mangle = mangle - 2 * PI / 30;
}
}
Sleep(50);
FlushBatchDraw();
clearcircle(e.center_x, e.center_y, earthradies);
clearcircle(m.center_x, m.center_y, moonradies);
}
EndBatchDraw();
_getch();
closegraph();
return 0;
}
void drawsun(color c, int x, int y, radies r) {
setfillcolor(c);
solidcircle(x, y, r);
}
void creatplant(plant* p, int x, int y, int radies) {
p->center_x = x;
p->center_y = y;
p->radies = radies;
}
void drawplant(plant* p1, plant* p2, double angle, int a, color c, radies r) {
p1->center_x = p2->center_x + (WIDTH / a) * sin(angle);
p1->center_y = p2->center_y - (WIDTH / a) * cos(angle);
setfillcolor(c);
solidcircle(p1->center_x, p1->center_y, p1->radies);
}
/*
地球要围绕着太阳转动
月球要围绕着地球转动
按下1-7的按键时,太阳会变色
按下8的时候太阳会依次的变色
当按下空格键的时候,太阳会停止转动
当按下上键的时候地球和月亮会逆向的转动,再按下上键的时候,地球和月亮会顺时针转动
*/