见缝插针小游戏 (基于OPENGL)|计算机图形学

见缝插针小游戏 (基于OPENGL)|计算机图形学

1.前言

最近开始学习图形学,试着用glut做了一个小游戏,也是之前见得蛮多的小游戏叫做见缝插针,基本上功能都实现了,可能还有些不足,之后有机会再改进,废话不多说,先上图

在这里插入图片描述

在这里插入图片描述

2.玩法

每次点击鼠标左键可以插入一针,每次插入完的针都会一直旋转,过程中要保证每插入一针,针与针不要碰撞,最后尽可能多的“见缝插针
(听起来是不是很简单呢,确实也不难)

接下来就是代码部分,这里我直接把源码贴出来,还有一些里面的注意事项,以免你们拿到手不能直接运行

3.代码

#include<GL/glut.h>
#include<stdio.h>
#include <windows.h> 
#include<math.h>

#define MAX_CHAR  128									// ASCII字符总共只有0到127,一共128种字符 
#define PI        acos(-1.0)							// cos (pi)=-1 求一个反余弦就得到PI
#define SPEED     (PI/360)								// 针的旋转速度
#define NEEDLE_L  180									// 针的长度

void Init();
void Reshape(int w, int h);
void myDisplay();
void myIdle(void);
void MouseHit(int button, int state, int x, int y);
void DrawString(const char* str);
void DrawFrame(int number);
void Move(int number, int x, int y);
void Rotate(double* R, int num);
bool Pin(double* R, int num);

double radian[25] = { 0 };								//储存针的弧度
int Needle_N = 0;										//针的个数
int n = 40;												//多边形边数,n越大,越趋近圆
int win;												//窗口返回值

int main(int argc,char*argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(480, 640);
	win=glutCreateWindow("Game Demo 左键点击开始"); //基于圆的碰撞检测做出来的游戏
	

	Init();
	glutDisplayFunc(myDisplay);
	glutIdleFunc(myIdle);
	glutReshapeFunc(Reshape);
	glutMouseFunc(MouseHit);
	glutMainLoop();
	return 0;
}

//初始化
void Init()
{
	glClearColor(189.0/255.0, 188.0/255.0, 187.0/ 255.0,0.0);
}

//调整窗口函数
void Reshape(int w, int h)
{
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();//初始化变换矩阵
	gluOrtho2D(0, (double)w, 0, (double)h);//左下角(0,0),右上角(640,480)
}

//绘制函数
void myDisplay()
{
	glClear(GL_COLOR_BUFFER_BIT);
	DrawFrame(1);
	Move(Needle_N, 240, 80);						
	Move(Needle_N + 1, 240, 40);
	Move(Needle_N + 2, 240, 0);
	Rotate(radian, Needle_N);
	glutSwapBuffers();
}

//动画函数
void myIdle(void)
{
	myDisplay();
	Sleep(10);//控制旋转快慢
}

//鼠标响应
void MouseHit(int button, int state, int x, int y)
{	

	if (button== GLUT_LEFT_BUTTON&&state==GLUT_DOWN)//controlMouseHit==true
	{
		if (Pin(radian, Needle_N))
		{
			Needle_N++;
		}
		else
		{
			glutIdleFunc(NULL);
			HWND hwnd = GetActiveWindow();
			if (MessageBox(hwnd, "游戏结束。\n重来一局吗?", "询问", MB_YESNO | MB_ICONQUESTION) == IDYES)
			{
				Needle_N = 0;
				glutIdleFunc(myIdle);//重启动画
			}
			else
			{
				glutDestroyWindow(win);//摧毁窗口,但是loop其实仍在继续
			}
				
		}
	}
	
}

//显示文本函数
void DrawString(const char* str) 
{
	static int isFirstCall = 1;
	static GLuint lists;

	if (isFirstCall) { // 如果是第一次调用,执行初始化 
						// 为每一个ASCII字符产生一个显示列表 
		isFirstCall = 0;

		// 申请MAX_CHAR个连续的显示列表编号 
		lists = glGenLists(MAX_CHAR);

		// 把每个字符的绘制命令都装到对应的显示列表中 
		wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists);
	}
	// 调用每个字符对应的显示列表,绘制每个字符 
	for (; *str != '\0'; ++str)
		glCallList(lists + *str);
}

//图案绘制
void DrawFrame(int number)
{
	char str[25];
	int i;
	_itoa_s(number, str, 10);
	glColor3f(70 / 255.0, 70 / 255.0, 69 / 255.0);
	// 绘制多边形,n足够大就会变成圆形 //也可以换成八对称画法
	glBegin(GL_POLYGON);
	for (i = 0; i < n; ++i)
		glVertex2f(240 + 50 * cos(2 * PI / n * i), 400 + 50 * sin(2 * PI / n * i));
	glEnd();

	glColor3f(1.0, 1.0, 1.0);
	glRasterPos2f(240,400);
	DrawString(str);
}

//
void Move(int number, int x, int y)
{
	char str[25];
	int i;
	_itoa_s(number, str, 10);
	glColor3f(70 / 255.0, 70 / 255.0, 69 / 255.0);
	glBegin(GL_POLYGON);
	for (i = 0; i < n; ++i)
		glVertex2f(x + 20 * cos(2 * PI / n * i), y + 20 * sin(2 * PI / n * i));
	glEnd();
	
	glColor3f(1.0, 1.0, 1.0);
	glRasterPos2f(x,y);
	DrawString(str);
}

//旋转函数
void Rotate(double* R, int num)
{
	int X_NEEDLE;
	int Y_NEEDLE;
	
	for (int i = 0; i < num; i++)
	{
		R[i] = R[i] + SPEED;
		//循环一圈后。-2PI
		if (R[i] > 2 * PI)
		{
			R[i] = R[i] - 2 * PI;
		}
		X_NEEDLE = int(NEEDLE_L * cos(R[i]) + 240);
		Y_NEEDLE = int(400-NEEDLE_L * sin(R[i]));

		glColor3f(70 / 255.0, 70 / 255.0, 69 / 255.0);
		glLineWidth(3);
		glEnable(GL_LINE_STIPPLE);
		glBegin(GL_LINES);
		glVertex2s(X_NEEDLE, Y_NEEDLE);
		glVertex2s(240, 400);
		glEnd();

		Move(i, X_NEEDLE, Y_NEEDLE);
		DrawFrame(1);
	}
}

//碰撞检测
bool Pin(double* R, int num)
{
	int X_NEEDLE;
	int Y_NEEDLE;

	bool T = true;
	R[num] = PI / 2;
	X_NEEDLE = int(NEEDLE_L * cos(R[num]) + 240);
	Y_NEEDLE = int(400-NEEDLE_L * sin(R[num]));

	glColor3f(70 / 255.0, 70 / 255.0, 69 / 255.0);
	glLineWidth(3);
	glEnable(GL_LINE_STIPPLE);
	glBegin(GL_LINES);
	glVertex2s(X_NEEDLE, Y_NEEDLE);
	glVertex2s(240, 350);
	glEnd();
	Move(num, X_NEEDLE, Y_NEEDLE);
	glutSwapBuffers();
	for (int i = 0; i < num; i++)
	{
		if (fabs(R[num] - R[i]) < (PI / 15))
		{
			T = false;
			break;								// 不需要再次进行比较了,循环跳出
		}
	}
	return T;									// 如果失败返回false;
}

注意事项

1、我这是在vs下写的代码,由于OPENGL他文字显示没有直接的api我就写了一个,里面用到了itoa函数(这里我用这个函数把数字转换字符串)
_itoa_s(number, str, 10);这是vs的要求itoa写法,虽然我可以在预处理器加宏定义可以不按照他这个来写,但是我偷懒2333
所以如果你用的不是vs或者vs没有这样的语法要求,你可以把_itoa_s改成itoa

2、如果你发现点击后旋转速度太快,可以在myIdle函数里更改sleep,以及更改宏定义SPEED

3一定要先配置好glut!!!! 不然你不能opengl画图

4 结尾

我这次没有细讲每个函数有什么用,不过应该标注的比较清楚了,然后就是如果有什么问题,欢迎大家提问,我都会尽力回答的,因为我也是刚接触opengl不久 谢谢大家看我的博客

  • 6
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
作者对游戏的说明: 首先,您应当以一种批判的眼光来看待本程序。这个游戏是我制作 的第一部RPG游戏,无任何经验可谈,完全按照自己对游戏的理解进 行设计的。当我参照了《圣剑英雄2》的源码之后,才体会到专业游 戏引擎的博大精深。 该程序的内核大约有2000余行,能够处理人物的行走、对话、战斗, 等等。由于该程序的结构并不适于这种规模的程序,故不推荐您详 细研究该程序。所附地图编辑器的源程序我已经添加了详细的注释, 其程序结构也比较合理,可以作为初学VC的例子。 该程序在VC的程序向导所生成的SDI框架的基础上修改而成。它没有 使用任何关于VC底层的东西。程序的绝大部分都是在CgameView类中 制作的,只有修改窗口特征的一段代码在CMainFrm类中。其他的类 统统没有用到。另外添加的一个类是CEnemy类。 整个游戏的故事情节分成8段,分别由Para1.h ~ Para8.h八个文件 实现。由于程序仅仅能够被动的处理各种各样的消息,所以情节的 实现也只能根据系统的一些参数来判断当前应当做什么。在程序中 使用了冗长的if……else if……结构来实现这种判断。 当然,在我的记录本上,详细的记录了每个事件的判断条件。这种 笨拙的设计当然是不可取的。成都金点所作《圣剑英雄II》采用了 剧本解读的方式,这才是正统的做法。但这也需要更多的编程经验 和熟练的code功夫。 下面列举的是程序编制过程中总结出来的经验和教训。 第一,对话方式应该采用《圣剑英雄II》的剧本方式。 现在的方式把一个段落中所有的对话都混在一个文件中,然后给每 句话一个号码相对应。这样做虽然降低了引擎的难度,却导致剧情的 编写极其繁琐。 第二,运动和显示应当完全分开。 现在的程序中,运动和显示是完全同步的。即:在定时器中调用所有 敌人的运动函数,然后将主角的动画向前推一帧,接着绘制地图,调 用所有敌人的显示函数、重绘主角。这样的好处是不会掉帧,但带来 的问题是,如果要提高敌人的运动速度,那么帧数也跟着上去了。所 以当DEMO版反馈说速度太慢的时候,我修改起来非常困难。而这个问 题到最后也仅仅是将4步一格该成了2步一格。 第三,VC中数组存在上限。如果用“int aaa[1000000000]”定义一个 数组,编译器肯定不会给分配那么大的内存空间。而在这个程序中, 地图矩阵、NPC矩阵都超过了VC中数组的上限。但这一点知道的太晚了。 在1.0版本中已经发现地图最右端缺少了几行,但不知道是什么原因 造成的。(地图编辑器中未出现此问题,因为地图编辑器是用“序列 化”的方式存盘读盘的。)解决这个问题的方法是用“new”来分配 内存空间。 第四,由于不知道应该如何使用“new”和“delete”,几乎所有的DC 都使用了全局变量。这是完全没有必要的。程序运行期大约会耗用20 多M的内存空间,相当于一个大型游戏所使用的内存空间了。 另外,在游戏的剧情、美工方面也有许多问题,总之一个词“业余”。 我就不总结了。下一部作品,我将争取在程序上有一个质的飞跃。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值