五角星持续放缩、旋转移动、碰撞反弹


一、设计思路

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

二、源码

#include <GL/glew.h>//OpenGL扩展库
#include <GL/glut.h>//OpenGL工具库
#include <stdio.h>
#include <math.h>


// 定义Π
#define PI 3.14159265

// 定义漂移五角星的属性
float alpha = 0.0f; // 旋转角度
float D = 0.f; // 平移距离
float tx = 0.0f, ty = 0.0f; // 平移的坐标增量
float beita = 2*PI/10; // 平移轨迹与x轴夹角β,初始为36度
float drift_x = 210.f, drift_y = 370.f;// 圆心坐标
float drift_R = 20; // 外接圆半径R

// 定义缩放五角星的属性
float zoom_x = 310, zoom_y = 210;//圆心坐标
float zoom_R = 60;//外接圆半径
float zoom_tx = 1.f, zoom_ty = 1.f, zoom_tz = 1.f;//放缩参数

// 定义坐标结构体
struct Vertex {
	double x;
	double y;
};

// ####绘制五角星方法####

// 绘制黄色三角形函数
void drawYellowTriangle(Vertex Vertex_Fir, Vertex Vertex_Sec, Vertex Vertex_Thi) {
	// 设置填充的颜色
	glColor3f(255, 255, 0);
	// 设置绘制类型
	glBegin(GL_TRIANGLES);
	// 第一个坐标
	glVertex2f(Vertex_Fir.x, Vertex_Fir.y);
	// 第二个坐标
	glVertex2f(Vertex_Sec.x, Vertex_Sec.y);
	// 第三个坐标
	glVertex2f(Vertex_Thi.x, Vertex_Thi.y);
	glEnd();
}

// 获取外圆五角星的五顶点(参数:大圆圆心坐标以及半径)
Vertex* getExternalVertex(Vertex Center_Vertex, double R) {
	// 定义Vertex结构体数组,包括五个顶点和一个圆心
	Vertex* externalVertex = new Vertex[6];
	double alpha = 2 * PI / 5;
	double thet = PI / 10;
	double bet = 2 * PI / 10;
	// 圆心坐标
	externalVertex[0].x = Center_Vertex.x, externalVertex[0].y = Center_Vertex.y;
	// 其余五个顶点
	externalVertex[1].x = Center_Vertex.x, externalVertex[1].y = Center_Vertex.y + R;
	externalVertex[2].x = Center_Vertex.x + R * cos(thet), externalVertex[2].y = Center_Vertex.y + R * sin(thet);
	externalVertex[3].x = Center_Vertex.x + R * sin(bet), externalVertex[3].y = Center_Vertex.y - R * cos(bet);
	externalVertex[4].x = Center_Vertex.x - R * sin(bet), externalVertex[4].y = Center_Vertex.y - R * cos(bet);
	externalVertex[5].x = Center_Vertex.x - R * cos(thet), externalVertex[5].y = Center_Vertex.y + R * sin(thet);
	return externalVertex;
}

// 获取内圆五角星的五顶点(参数:小圆圆心坐标以及半径)
Vertex* getInternalVertex(Vertex Center_Vertex, double r) {
	// 定义Vertex结构体数组,包括五个顶点和一个圆心
	Vertex* internalVertex = new Vertex[6];
	double alpha = 2 * PI / 5;
	double thet = PI / 10;
	double bet = (PI - alpha) / 2;
	// 圆心坐标
	internalVertex[0].x = Center_Vertex.x, internalVertex[0].y = Center_Vertex.y;
	// 其余五个顶点
	internalVertex[1].x = Center_Vertex.x + r * cos(bet), internalVertex[1].y = Center_Vertex.y + r * sin(bet);
	internalVertex[2].x = Center_Vertex.x + r * cos(thet), internalVertex[2].y = Center_Vertex.y - r * sin(thet);//
	internalVertex[3].x = Center_Vertex.x, internalVertex[3].y = Center_Vertex.y - r;//
	internalVertex[4].x = Center_Vertex.x - r * cos(thet), internalVertex[4].y = Center_Vertex.y - r * sin(thet);
	internalVertex[5].x = Center_Vertex.x - r * cos(bet), internalVertex[5].y = Center_Vertex.y + r * sin(bet);
	return internalVertex;
}

// ####绘制红旗方法####

// 绘制红色矩阵函数
void drawRedRect(Vertex* vertex) {
	// 设置绘制类型
	glShadeModel(GL_FLAT);
	glBegin(GL_POLYGON);
	// 设置填充的颜色,默认以第一个点的颜色进行纯色填充
	glColor3f(1, 0, 0);
	// 第一个坐标
	glVertex2f(vertex[0].x, vertex[0].y);
	// 第二个坐标
	glVertex2f(vertex[1].x, vertex[1].y);
	// 第三个坐标
	glVertex2f(vertex[2].x, vertex[2].y);
	// 第四个坐标
	glVertex2f(vertex[3].x, vertex[3].y);
	glEnd();
}


// 设置背景颜色
void setBackgroundColor(void) {
	glClearColor(0.2, 0.3, 0.3, 0.5);
}



// 绘制国旗
void draw(void) {
	// 清除清缓存
	glClear(GL_COLOR_BUFFER_BIT);

	// ### 一、绘制红色矩形 ###
	Vertex* rectVertex=new Vertex[4];
	// 上边界y=410;下边界y=10;左边界x=10,有边界x=610
	rectVertex[0].x = 10, rectVertex[0].y = 410;//左上角(高400)
	rectVertex[1].x = 10, rectVertex[1].y = 10;//左下角
	rectVertex[2].x = 610, rectVertex[2].y = 10;//右下角(宽600)
	rectVertex[3].x = 610, rectVertex[3].y = 410;//右上角
	drawRedRect(rectVertex);

	// ### 二、绘制五角星 ###

	// # <1>缩放五角星 #
	// 1. 定义圆心、大小圆半径
	Vertex Center_Vertex;
	Center_Vertex.x = zoom_x, Center_Vertex.y = zoom_y;//圆心以显示窗口的左下角为原点,向右x轴正方向,向上y轴正方向
	double R = zoom_R;
	double r = 23;
	// 2. 获取内外顶点
	Vertex* externalVertex = getExternalVertex(Center_Vertex, R);
	Vertex* internalVertex = getInternalVertex(Center_Vertex, r);
	// 3.几何变换(持续缩放)
	//模型观察矩阵初始化
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glPushMatrix();//几何变换的边界起点
	glTranslatef(310, 210, 0);//移动回初始位置
	glScalef(zoom_tx, zoom_ty, zoom_tz);
	//glScalef(1, 1, zoom_tz);
	glTranslatef(-310, -210, 0);//移动回原点
	// 3. 绘制黄色五角星
	for (int i = 1; i < 6; i++) {
		drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[i]);
		if (i < 5) {
			drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[i + 1]);
		}
		else {
			drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[1]);
		}
	}
	glPopMatrix();//几何变换的边界终点(只对边界内的图形进行几何变换)

	// # <2>漂移五角星 #
	// 1. 定义圆心、大小圆半径
	Center_Vertex.x = drift_x, Center_Vertex.y = drift_y;//圆心以显示窗口的左下角为原点,向右x轴正方向,向上y轴正方向
	R = drift_R;
	r = 7.6;
	// 2. 获取内外顶点
	externalVertex = getExternalVertex(Center_Vertex, R);
	internalVertex = getInternalVertex(Center_Vertex, r);

	// 3.几何变换(旋转一定角度)
	//模型观察矩阵初始化
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glPushMatrix();//几何变换的边界起点
	glTranslatef(tx, ty, 0);//平移
	glTranslatef(210, 370, 0);//移动回初始位置
	glRotatef(alpha, 0, 0, 1);//绕z轴旋转,即xoy平面以原点为中心逆时针旋转
	glTranslatef(-210, -370, 0);//移动回原点
	

	// 4. 绘制黄色五角星
	for (int i = 1; i < 6; i++) {
		drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[i]);
		if (i < 5) {
			drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[i + 1]);
		}
		else {
			drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[1]);
		}
	}

	glPopMatrix();//几何变换的边界终点(只对边界内的图形进行几何变换)
	


	

	glutSwapBuffers();//交换缓冲(双缓冲时使用)
}

// 这里有个问题,tx和ty都是累加计算的结果,那么对于tx和ty的修改是对β角度以及累计方式的修改,对于β角度的修改已经没有问题,现在需要思考如何对累计方式进行修改
// 对于累计方式的修改有如下思路:由于累计方式只有两种累加和累减,所以我们只需要定义一个变量来控制是进行累加还是累减
// 累计方式变量,为1时累加,为-1时累减
float flag_tx = 1.f;// tx的累计变量,为1时tx为累加算法,为-1时tx为累减算法
float flag_ty = 1.f;// ty的累计变量

// 放缩五角星累计方式改变变量
float zoom_flag = 1;

void collisionDetection() {

	// 检查是否与缩放五角星发生碰撞(dist<(zoom_R+drift_R)即发生碰撞)
	float dist = sqrt(pow((zoom_x - (drift_x + tx)), 2) + pow(zoom_y - (drift_y + ty), 2));//(利用勾股定理计算两个圆心坐标的直线距离)
	// 碰撞检测......
	if (dist < (zoom_R*(zoom_tx) + drift_R)) {// 使用变化后的zoom_R,即zoom_R*(1+ zoom_tx)

		// 发生碰撞,则不可进行原计划位移,将已加坐标增量减去
		tx -= flag_tx * D*cos(beita);
		ty -= flag_ty * D*sin(beita);

		// 更新碰撞后的运动方向
		// 五角星方向(沿反方向)
		flag_tx = -1;
		flag_ty = -1;
		// 放缩方向(进行缩小)
		zoom_flag = -1;
		zoom_tx += zoom_flag * 0.001;
		zoom_ty += zoom_flag * 0.001;
	}

	// 检查是否与边界发生碰撞(如果五角星之间发生碰撞,那么五角星就不可能与边界发生碰撞)
	// 在tx,ty的基础上添加外接圆半径R的距离
	float txx = tx + flag_tx * (drift_R)*cos(beita);
	float tyy = ty + flag_ty * (drift_R)*sin(beita);
	if (((drift_x + txx) < 10) || ((drift_x + txx) > 610) || ((drift_y + tyy) < 10) || ((drift_y + tyy) > 410)) {
		// 发生碰撞,则不可进行原计划位移,将已加坐标增量减去
		tx -= flag_tx * D*cos(beita);
		ty -= flag_ty * D*sin(beita);
		// 更新碰撞后的运动方向
		if (beita < PI / 2) {
			beita = PI - beita;
			if (drift_x + txx > 610 || drift_y + tyy < 10) {
				flag_tx = 1;
				flag_ty = 1;
			}
			else if (drift_x + txx < 10 || drift_y + tyy>410) {
				flag_tx = -1;
				flag_ty = -1;
			}
		}
		else if (beita > PI / 2) {
			beita = PI - beita;
			if (drift_x + txx > 610 || drift_y + tyy > 410) {
				flag_tx = -1;
				flag_ty = -1;
			}
			else if (drift_x + txx < 10 || drift_y + tyy < 10) {
				flag_tx = +1;
				flag_ty = +1;
			}
		}
		else if (beita == 0) {
			beita = beita;
			if (drift_x + txx > 610) {
				flag_tx = -1;
				flag_ty = +1;
			}
			else if (drift_x + txx < 10) {
				flag_tx = +1;
				flag_ty = +1;
			}
		}
		else if (beita == PI / 2) {
			beita = beita;
			if (drift_y + tyy > 410) {
				flag_tx = +1;
				flag_ty = -1;
			}
			else if (drift_y + tyy < 10) {
				flag_tx = +1;
				flag_ty = +1;
			}
		}
		// 更换为碰撞后的新坐标增量
		tx += flag_tx * D*cos(beita);
		ty += flag_ty * D*sin(beita);
	}

}


void change()
{
	// #### 漂移五角星 ####
	// #### 使旋转角度持续发生变化 ####
	alpha += 0.3; // 每次旋转两度
	if (alpha > 360) alpha -= 360; //当旋转达到360度后减去360度,变为0度

	// #### 使位移的tx、ty持续发生变化 ####
	//根据每次旋转的角度0.2度和外接圆半径drift_R计算平移距离
	D = 0.2 / 360 * 2 * PI*drift_R;
	//根据平移距离D、轨迹与x轴夹角β计算坐标的tx,ty增量
	tx += flag_tx*D*cos(beita);
	ty += flag_ty*D*sin(beita);

	// #### 放缩五角星 ####
	// 放缩比例大于2倍时开始缩小
	if (zoom_tx > 1) {
		zoom_flag = -1;
	}
	// 小于0.1倍时,开始放大
	else if(zoom_tx < 0.1) {
		zoom_flag = 1;
	}
	zoom_tx += zoom_flag * 0.001;
	zoom_ty += zoom_flag * 0.001;
	
	// 碰撞检测函数
	collisionDetection();

	glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y)
{
}

void reshape(int width, int height)
{
	glViewport(0, 0, width, height);//设置视区

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, width, 0, height);//设置图形数据范围
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

int main() {
	// 窗口的坐标是电脑屏幕左上角为原点,向右为x轴正方向;向下为y轴正方向
	glutInitWindowPosition(10, 10); //定义窗口位置与大小
	glutInitWindowSize(1200, 700);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); //初始化显示模式
	glutCreateWindow("五角星乱动"); //定义窗口名称

	setBackgroundColor(); // 设置背景色

	glutDisplayFunc(draw); //图形绘制函数
	glutReshapeFunc(reshape); //窗口重绘函数
	glutKeyboardFunc(keyboard);//键盘交互
	glutIdleFunc(change);//键盘交互(实现无限循环的关键)
	glutMainLoop(); //无限循环函数 
	return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值