[OpenGL] 小游戏 - 太空对战

写的比较粗糙的一个小游戏,算是对游戏编程流程的一个入门认识。

OpenGL中,如何加载纹理,如何绘制透明纹理,如何显示文字,如何制作简单动画(画面刷新机制),如何使用键盘和鼠标回调函数,这些我们已经很熟悉了。当然,这些都是最基本的要求。

那么,如何实现游戏基本逻辑?一般而言,用oop的思想能够很好地完成这一点,我们给每类对象维护一些状态量和方法,每个对象可以通过下标索引访问到这些状态量,我们把代码分成两部分 —— 第一部分,当达到一个触发条件时,我们改变状态量的值;第二部分,根据状态量,我们绘制不同形态的物体。

这样下来,整个流程就清晰很多了,我们不再需要写大量的条件判断语句,在条件触发后手动改变绘制形态,而是把改变记录下来,然后置之不理;帧刷新时绘制物体的函数读到了这些变化,然后自动做出调整。如果我们想加入新的状态量,我们只需要加入新的判断语句,并维护状态量即可,而不用改动之前的代码,也就是说不同过程相互之间是独立的。

一.飞机的状态量

在这里,我暂时仍然使用了C风格的变量来维护这些状态量。

我们注意到飞机产生的位置看起来很随机,它们的位置也是比较乱的,那么我们该如何管理这些飞机呢?

使用bool isShip[6][3]这个二维数组,我们可以很方便地控制这些飞机,其中6代表飞机会在6种不同的高度产生,3是每个高度最多的飞机数,一开始,我们只设置一个为true,其余都为false。

每隔一定帧数,我们产生飞机,我们首先随机产生一个高度h,在这个高度下,我们寻找isShip[h][0], isShip[h][1], isShip[h][2] 中还没有被占用的变量(即为false),得到新产生飞机的下标索引,如果都被占用了,那么就不产生飞机。飞机产生后占用这个变量(设为true),消失后又把位置让出(设为false)。

产生的同时,我们还要初始化一些其它的状态量,比如飞机的形状shipType, 飞机从左边还是从右边产生isLeft[6][3], 飞机的初始坐标move_x[6][3],move_y[6],飞船的子弹与飞机的碰撞次数。

这些量都是随机生成的,唯一要注意的是从左边还是从右边产生这一状态量,如果不加处理可能会发生左右碰撞现象,而这是我们不希望的,所以我们在扫描到这一行存在某个方向的飞机时,我们让新的飞机和它方向一样。如果没有检测到,才随机产生。

而飞机的坐标,我们只要在一定时间内不断给它加上或者减去一定值,到了边缘或者被击中后的时候把isShip设为false即可。

当然,这个飞机还会发射子弹,所以我们还需要一些量来维护子弹的状态,同样的,我们需要一个状态量isAttack[6][3][3]来维护子弹是否存在。这个量相比飞机多了一个维度,因为我们希望一个飞机至多可以发射3个子弹。

那么相应的,我们维护子弹的坐标attack_x[6][3][3],attack_y[6][3][3], 子弹是否击中飞isSuccess[6][3][3]

二.白线的控制

我们注意到这个游戏中飞船会不断地发射白线,看起来非常连贯,而且白线还会随着飞船的移动而改变位置。那么,这样的效果究竟是如何实现的呢?我们需要多少变量来保存白线的状态呢?

看似连续不断的白线,实际上是三帧画面的重复绘制!

在这个场景中,一共显示25根白线,而一共有三种状态量,所以我们用二维数组pos_y[25][3]来管理它们的y坐标,当然,x坐标方向不存在这样的移动动画,用一维数组维护即可。

解决了线条的动画效果后,我们来考虑如何实现线条随飞船移动而移动。

飞船可以左右移动,那么便有一个变量来维护它的x坐标,每次白线移动的时候,我们使用推移的方法,把后面白线的x坐标转移给前一根白线,然后最后的白线(离飞船最近的)用飞船的x坐标取代,这样,就产生了白线随飞船位置而产生的效果。

game.h

#pragma once
#define GLUT_DISABLE_ATEXIT_HACK
#include "GL/GLUT.H"
void loadTex(int i, char *filename, GLuint* texture);
void loadTex_alpha(int i, char *filename, GLuint* texture);

textrue.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>
#include"game.h"
#define BITMAP_ID 0x4D42   

//读纹理图片
static unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{

	FILE *filePtr;    // 文件指针
	BITMAPFILEHEADER bitmapFileHeader;    // bitmap文件头
	unsigned char    *bitmapImage;        // bitmap图像数据
	int    imageIdx = 0;        // 图像位置索引
	unsigned char    tempRGB;    // 交换变量    

								 // 以“二进制+读”模式打开文件filename
	filePtr = fopen(filename, "rb");
	if (filePtr == NULL) {
		printf("file not open\n");
		return NULL;
	}
	// 读入bitmap文件图
	fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
	// 验证是否为bitmap文件
	if (bitmapFileHeader.bfType != BITMAP_ID) {
		fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n");
		return NULL;
	}
	// 读入bitmap信息头
	fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
	// 将文件指针移至bitmap数据
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
	// 为装载图像数据创建足够的内存
	bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage];
	// 验证内存是否创建成功
	if (!bitmapImage) {
		fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
		return NULL;
	}

	// 读入bitmap图像数据
	fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);
	// 确认读入成功
	if (bitmapImage == NULL) {
		fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
		return NULL;
	}
	//由于bitmap中保存的格式是BGR,下面交换R和B的值,得到RGB格式
	for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) {
		tempRGB = bitmapImage[imageIdx];
		bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
		bitmapImage[imageIdx + 2] = tempRGB;
	}
	// 关闭bitmap图像文件
	fclose(filePtr);
	return bitmapImage;
}

//读纹理图片
static unsigned char *LoadBitmapFile_alpha(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{

	FILE *filePtr;    // 文件指针
	BITMAPFILEHEADER bitmapFileHeader;    // bitmap文件头
	unsigned char    *bitmapImage;        // bitmap图像数据
	int    imageIdx = 0;        // 图像位置索引    

								 // 以“二进制+读”模式打开文件filename
	filePtr = fopen(filename, "rb");
	if (filePtr == NULL) {
		printf("file not open\n");
		return NULL;
	}
	// 读入bitmap文件图
	fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
	// 验证是否为bitmap文件
	if (bitmapFileHeader.bfType != BITMAP_ID) {
		fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n");
		return NULL;
	}
	// 读入bitmap信息头
	fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);

	// 将文件指针移至bitmap数据
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
	// 为装载图像数据创建足够的内存
	bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage];
	// 验证内存是否创建成功
	if (!bitmapImage) {
		fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
		return NULL;
	}

	// 读入bitmap图像数据
	fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);
	// 确认读入成功
	if (bitmapImage == NULL) {
		fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
		return NULL;
	}
	unsigned char*   bitmapData;   // 纹理数据 

	bitmapData = new unsigned char[bitmapInfoHeader->biSizeImage / 3 * 4];

	int count = 0;
	//由于bitmap中保存的格式是BGR,下面交换R和B的值,得到RGB格式
	for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) {
		bitmapData[count] = bitmapImage[imageIdx + 2];
		bitmapData[count + 1] = bitmapImage[imageIdx + 1];
		bitmapData[count + 2] = bitmapImage[imageIdx];
		if (bitmapData[count] == 255 && bitmapData[count + 1] == 255 && bitmapData[count + 2] == 255) {
			bitmapData[count + 3] = 0;
		}
		else bitmapData[count + 3] = 255;
		count += 4;
	}

	// 关闭bitmap图像文件
	fclose(filePtr);
	return bitmapData;
}

//加载纹理的函数
void loadTex(int i, char *filename, GLuint* texture)
{

	BITMAPINFOHEADER bitmapInfoHeader;                                 // bitmap信息头
	unsigned char*   bitmapData;                                       // 纹理数据    

	bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader);

	glBindTexture(GL_TEXTURE_2D, texture[i]);
	// 指定当前纹理的放大/缩小过滤方式
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	glTexImage2D(GL_TEXTURE_2D,
		0,         //mipmap层次(通常为,表示最上层)
		GL_RGB,    //我们希望该纹理有红、绿、蓝数据
		bitmapInfoHeader.biWidth, //纹理宽带,必须是n,若有边框+2
		bitmapInfoHeader.biHeight, //纹理高度,必须是n,若有边框+2
		0, //边框(0=无边框, 1=有边框)
		GL_RGB,    //bitmap数据的格式
		GL_UNSIGNED_BYTE, //每个颜色数据的类型
		bitmapData);    //bitmap数据指针    

}

//加载纹理的函数
void loadTex_alpha(int i, char *filename, GLuint* texture)
{

	BITMAPINFOHEADER bitmapInfoHeader;                                 // bitmap信息头
	unsigned char*   bitmapData;                                       // 纹理数据    

	bitmapData = LoadBitmapFile_alpha(filename, &bitmapInfoHeader);

	glBindTexture(GL_TEXTURE_2D, texture[i]);
	// 指定当前纹理的放大/缩小过滤方式
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glTexImage2D(GL_TEXTURE_2D,
		0,         //mipmap层次(通常为,表示最上层)
		GL_RGBA,    //我们希望该纹理有红、绿、蓝数据
		bitmapInfoHeader.biWidth, //纹理宽带,必须是n,若有边框+2
		bitmapInfoHeader.biHeight, //纹理高度,必须是n,若有边框+2
		0, //边框(0=无边框, 1=有边框)
		GL_RGBA,    //bitmap数据的格式
		GL_UNSIGNED_BYTE, //每个颜色数据的类型
		bitmapData);    //bitmap数据指针    

}

main.cpp

#define _CRT_SECURE_NO_WARNINGS  

#include <stdio.h>
#include <string.h>
#include<time.h>
#include <stdlib.h>
#include <windows.h>
#include"game.h"  

GLuint texture[5];

//视区
float whRatio;
int wHeight = 0;
int wWidth = 0;

//控制飞机生成(最好能改成类)

float move_x[6][3];//移动飞机的x坐标
float move_y[6] = { 2.5f,2.0f,1.5f,1.0f,0.0f,-0.5f };//移动飞机的y坐标
bool isShip[6][3];//该位置是否存在飞机
int collisionTimes[6][3] = { 0 };//碰撞次数
bool isAttack[6][3][3];//该飞机是否发子弹
float attack_x[6][3][3];//子弹的x坐标
float attack_y[6][3][3];//子弹的y坐标
bool isSuccess[6][3][3];//是否击中飞船
bool isLeft[6][3];//飞机是否从左边产生
int shipType[6][3];//飞机的形状

//白线位置
float pos[25] = { 0.0f };//25条白线的x坐标
float pos_y[25][3];//25条白线的y坐标(3种状态)
float pos_x = 0.0f; //飞船的x坐标
int status = 0;

//计时
int count = 0;
int count2 = 0;
int count3 = 0;

//视点
float center[] = { 0, 0, 0 };
float eye[] = { 0, 0, 5 };

//分数
int score = 0;

//生命值
int life = 300;

void drawRect(GLuint texture)
{
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texture);  //选择纹理texture[status]   

	const GLfloat x1 = -0.5, x2 = 0.5;
	const GLfloat y1 = -0.5, y2 = 0.5;
	const GLfloat point[4][2] = { { x1,y1 },{ x2,y1 },{ x2,y2 },{ x1,y2 } };
	int dir[4][2] = { { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } };
	glBegin(GL_QUADS);

	for (int i = 0; i < 4; i++) {
		glTexCoord2iv(dir[i]);
		glVertex2fv(point[i]);
	}
	glEnd();

	glDisable(GL_TEXTURE_2D);
}

inline bool collisionTest(float y1, float y2, float y3, float x1, float x2, float x3)
{
	if (x3 > x1 && x3 < x2 && y1 < y3 && y3 < y2) return true;
	else return false;
}

inline bool collisionTest(float y1, float y2, float y3, float y4, float x1, float x2, float x3)
{
	if (x3 > x1 && x3 < x2 && (y1<y3 && y1 > y4 || y2 < y3 && y2 > y4)) return true;
	else return false;
}

//绘制飞船发出的白线
void drawLine()
{
	glTranslatef(0.0, 2.9f, 0.0f);
	for (int i = 0; i < 25; i++) {
		glBegin(GL_LINES);
		glVertex3f(pos[i], -0.05f, 1.0f);
		glVertex3f(pos[i], 0.05f, 1.0f);
		glEnd();
		glTranslatef(0.0, -0.2f, 0.0f);
	}
}

//控制从右边产生的飞机的移动
void shipMoveRight(int i,int j)
{
	glPushMatrix();
	glTranslatef(move_x[i][j], move_y[i], 1.0f);
	glScalef(0.6, 0.5, 1);
	move_x[i][j] -= 0.0005f;
	if (move_x[i][j] < -3.5f) {
		isShip[i][j] = false;
		collisionTimes[i][j] = 0;
	}
	if (collisionTimes[i][j] >= 5) {
		glColor3f(1, 0, 0);
	}
	drawRect(texture[shipType[i][j]]);
	glColor3f(1, 1, 1);
	glPopMatrix();

}

//控制从左边产生的飞机的移动
void shipMoveLeft(int i, int j)
{
	glPushMatrix();
	glTranslatef(move_x[i][j], move_y[i], 1.0f);
	glScalef(0.6, 0.5, 1);
	move_x[i][j] += 0.0005f;
	if (move_x[i][j] > 3.5f) {
		isShip[i][j] = false;
		collisionTimes[i][j] = 0;
	}
	if (collisionTimes[i][j] >= 5) {
		glColor3f(1, 0, 0);
	}
	drawRect(texture[shipType[i][j]]);
	glColor3f(1, 1, 1);
	glPopMatrix();

}

void printData()
{
	static int frame = 0, time, timebase = 0;
	static char buffer[256]; //字符串缓冲区  

	frame++;
	int life2 = 10;
	int score2 = 10;
	time = glutGet(GLUT_ELAPSED_TIME);
	//返回两次调用glutGet(GLUT_ELAPSED_TIME)的时间间隔,单位为毫秒
	if (time - timebase > 1000) { //时间间隔差大于1000ms时
		life2 = life;
		score2 = score;
		sprintf(buffer, "Score : %d   Life : %d",
			score2, life2); //写入buffer中
		timebase = time; //上一次的时间间隔
		frame = 0;
	}

	char *c;
	glDisable(GL_DEPTH_TEST);     // 禁止深度测试
	glMatrixMode(GL_PROJECTION);  // 选择投影矩阵
	glPushMatrix();               // 保存原矩阵
	glLoadIdentity();             // 装入单位矩阵
	glOrtho(0, 480, 0, 480, -1, 1);    // 位置正投影
	glMatrixMode(GL_MODELVIEW);   // 选择Modelview矩阵
	glPushMatrix();               // 保存原矩阵
	glLoadIdentity();             // 装入单位矩阵
	glRasterPos2f(10, 10);
	for (c = buffer; *c != '\0'; c++) {
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *c); //绘制字符
	}
	glMatrixMode(GL_PROJECTION);  // 选择投影矩阵
	glPopMatrix();                // 重置为原保存矩阵
	glMatrixMode(GL_MODELVIEW);   // 选择Modelview矩阵
	glPopMatrix();                // 重置为原保存矩阵
	glEnable(GL_DEPTH_TEST);      // 开启深度测试  

	//弹出对话框提示游戏结束
	if (life2 == 0) {
		char result[30];
		char str[30];
		strcpy(result, "游戏结束,您的得分是:");
		_itoa(score2, str, 10);
		strcat(result, str);
		MessageBox(NULL, TEXT(result), TEXT("注意"), MB_ICONINFORMATION);
		exit(0);
	}
}

void drawScene()
{

	//绘制星空背景
	glPushMatrix();
	glScalef(8, 6, 1);
	drawRect(texture[0]);
	glPopMatrix();

	//绘制白线
	glPushMatrix();
	glColor3f(1.0f, 1.0f, 1.0f);
	if (count >= 50 && count < 100) {
		glTranslatef(0.0f, 0.066f, 0.0f);
		status = 1;
	}
	else if (count >= 100 && count < 150) {
		glTranslatef(0.0f, 0.132f, 0.0f);
		status = 2;
	}
	else status = 0;
	drawLine();
	glPopMatrix();

	//设置深度缓存为只读
	glDepthMask(GL_FALSE);

	//绘制移动的飞机

	for (int i = 0; i < 6; i++) {
		for (int j = 0; j < 3; j++) {
			if (isShip[i][j]) {
				//判断飞机从哪个方向产生
				if(!isLeft[i][j])shipMoveRight(i, j);
				else shipMoveLeft(i, j);
				if (isShip[i][j] == false)continue;
				int t = status;
				for (int k = 0; k < 25; k++) {
					//判断白线和飞机是否发生碰撞
					if (collisionTest(pos_y[k][t] - 0.05f, pos_y[k][t] + 0.05f, move_y[j] - 0.3f, move_x[i][j] - 0.1f, move_x[i][j] + 0.1f, pos[k])) {
						collisionTimes[i][j]++;
						if (shipType[i][j] == 3 && collisionTimes[i][j]>15) {
							isShip[i][j] = false;
							score += 10;
							printData();
							collisionTimes[i][j] = 0;
						}
						else if (shipType[i][j] == 1 && collisionTimes[i][j]>100) {
							isShip[i][j] = false;
							score += 15;
							printData();
							collisionTimes[i][j] = 0;
						}
					}
				}
			}
			for (int k = 0; k < 3; k++) {
				//绘制飞机的子弹
				if (isAttack[i][j][k]) {
					glPushMatrix();
					glColor3f(1, 0, 0);
					glTranslatef(attack_x[i][j][k], attack_y[i][j][k], 0.0f);
					glBegin(GL_LINES);
					glVertex3f(0.0f, -0.03f, 1.0f);
					glVertex3f(0.0f, 0.03f, 1.0f);
					glEnd();
					glPopMatrix();
					glColor3f(1, 1, 1);
					//判断子弹是否击中飞船
					if (!isSuccess[i][j][k] && collisionTest(attack_y[i][j][k] - 0.03f, attack_y[i][j][k] + 0.03f, -1.95f,-2.65f, pos_x - 0.38f, pos_x + 0.38f, attack_x[i][j][k])) {
						isSuccess[i][j][k] = true;
						life -= 10;
						printData();

					}
					//移动子弹位置
					if (count % 50 == 0) {
						attack_y[i][j][k] -= 0.05f;
					}
					//子弹消失
					if (attack_y[i][j][k] < -2.5f) {
						isAttack[i][j][k] = false;
						isSuccess[i][j][k] = false;
					}
				}
			}
		}
	}

	//绘制飞船
	glPushMatrix();
	glTranslatef(pos_x, -2.3f, 1.0f);
	glScalef(0.8, 0.7, 0);
	drawRect(texture[2]);
	glPopMatrix();

	//取消设置深度缓存为只读
	glDepthMask(GL_TRUE);

	//控制随机生成飞机
	if (count >= 150) {
		count = 0;
		for (int i = 1; i < 25; i++) {
			pos[i - 1] = pos[i];
		}
		pos[24] = pos_x;
		count2++;

		if (count2 == 5 || count2 == 10) {
			for (int i = 0; i < 6; i++) {
				for (int j = 0; j < 3; j++) {
					if (isShip[i][j]) {
						for (int k = 0; k < 3; k++) {
							//控制飞机开始发射子弹
							if (isAttack[i][j][k] == false) {
								isAttack[i][j][k] = true;
								attack_x[i][j][k] = move_x[i][j];
								attack_y[i][j][k] = move_y[i];
								break;
							}
						}
					}
				}
			}
		}
		if (count2 == 10) {
			count2 = 0;
			int num = rand() % 6;
			for (int i = 0; i < 3; i++) {
				//找到可以生成飞机的位置
				if (isShip[num][i] == false) {
					bool flag = false;
					//保证飞机从左右发射时不相撞,在同一行已经有飞机的时候,和已有飞机方向相同
					for (int j = 0; j < 3; j++) {
						if (isShip[num][j] == true) {
							flag = true;
							isLeft[num][i] = isLeft[num][j];
							break;
						}
					}
					//同一行不存在飞机的时候,随机生成飞机产生方向
					if (!flag) {
						isLeft[num][i] = rand() % 2;

					}
					isShip[num][i] = true;
					//随机生成飞机种类
					int random = rand() % 3;
					if (random == 0) {
						shipType[num][i] = 1;
					}
					else shipType[num][i] = 3;
					//初始化操作
					collisionTimes[num][i] = 0;
					if(isLeft[num][i])move_x[num][i] = -3.0f;
					else move_x[num][i] = 3.0f;
					break;
				}
			}
		}
	}
	else count++;
}

void updateView(int height, int width)
{
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影
	glLoadIdentity();   //初始化矩阵为单位矩阵
	whRatio = (GLfloat)width / (GLfloat)height;  //设置显示比例
	glOrtho(-3, 3, -3, 3, -100, 100); //正投影
	glMatrixMode(GL_MODELVIEW);  //设置矩阵模式为模型
}

void reshape(int width, int height)
{
	if (height == 0)      //如果高度为0
	{
		height = 1;   //让高度为1(避免出现分母为0的现象)
	}

	wHeight = height;
	wWidth = width;

	updateView(wHeight, wWidth); //更新视角
}

void idle()
{
	glutPostRedisplay();
}

void init()
{
	srand(unsigned(time(NULL)));
	glEnable(GL_DEPTH_TEST);//开启深度测试
	glEnable(GL_ALPHA_TEST);//开启alpha测试
	glAlphaFunc(GL_GREATER, 0.1);
	glEnable(GL_LIGHTING);  //开启光照模式   

	glClearColor(1.0f, 1.0f, 1.0f,1.0f);
	memset(pos, -10.0f, sizeof(pos));
	glGenTextures(4, texture);
	loadTex(0, "1.bmp", texture);
	loadTex_alpha(1, "ship3.bmp", texture);
	loadTex_alpha(2, "ship.bmp", texture);
	loadTex_alpha(3, "ship2.bmp", texture);

	pos_y[0][0] = 2.9f;
	pos_y[0][1] = 2.9f + 0.66f;
	pos_y[0][2] = 2.9f + 0.132f;

	for (int i = 0; i < 3; i++) {
		for (int j = 1; j < 25; j++) {
			pos_y[j][i] = pos_y[j-1][i]-0.2f;
		}
	}

	memset(isShip, false, sizeof(isShip));
	memset(isAttack, false, sizeof(isAttack));

	isShip[0][0] = true;
	shipType[0][0] = 3;
	move_x[0][0] = 3.0f;

}

void key(unsigned char k, int x, int y)
{
	switch (k)
	{
	case 'a': {
		pos_x -= 0.05f;
		if (pos_x < -2.90f)pos_x = -2.90f;
		break;
	}
	case 'd':{
		pos_x += 0.05f;
		if (pos_x > 2.90f)pos_x = 2.90f;
		break;
	}
	}

	updateView(wHeight, wWidth); //更新视角
}

void redraw()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓存
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();   //初始化矩阵为单位矩阵
	gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], 0, 1, 0);                // 场景(0,0,0)的视点中心 (0,5,50),Y轴向上
	glPolygonMode(GL_FRONT, GL_FILL);

	glFrontFace(GL_CCW);
	glEnable(GL_CULL_FACE);
	// 启用光照计算
	glEnable(GL_LIGHTING);
	// 指定环境光强度(RGBA)
	GLfloat ambientLight[] = { 2.0f, 2.0f, 2.0f, 1.0f };

	// 设置光照模型,将ambientLight所指定的RGBA强度值应用到环境光
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
	// 启用颜色追踪
	glEnable(GL_COLOR_MATERIAL);
	// 设置多边形正面的环境光和散射光材料属性,追踪glColor
	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

	drawScene();//绘制场景
	printData();
	glutSwapBuffers();//交换缓冲区
}

int main(int argc, char *argv[])
{
	glutInit(&argc, argv);//对glut的初始化
	glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
	//初始化显示模式:RGB颜色模型,深度测试,双缓冲
	glutInitWindowSize(800, 600);//设置窗口大小
	int windowHandle = glutCreateWindow("Simple GLUT App");//设置窗口标题
	glutDisplayFunc(redraw); //注册绘制回调函数
	glutReshapeFunc(reshape);   //注册重绘回调函数
	glutKeyboardFunc(key); //注册按键回调函数
	glutIdleFunc(idle);//注册全局回调函数:空闲时调用       

	init();

	glutMainLoop();  // glut事件处理循环
	return 0;
}

图片资源

ship.bmp

ship2.bmp

ship3.bmp

时间: 2024-12-28 02:13:26

[OpenGL] 小游戏 - 太空对战的相关文章

小游戏●两人对战

利用结构体编写的两人对战小游戏,代码及简要分析如下 1 public struct Fighter 2 { 3 public string name; 4 public int blood; 5 public int attack; 6 public int defense; 7 public int hit; 8 public int hide; 9 } 10 11 12 static void Main(string[] arge) 13 { 14 15 //定义一组对手 16 Fighte

介绍一款Android小游戏--交互式人机对战五子棋

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6589025 学习Android系统开发之余,编写了一个小游戏--交互式人机对战五子棋,自娱自乐.之所以称之为交互式人机对战五子棋,一是因为在进入人机对战模式这前,你可以任意设置好开局,同时,在对战过程中,你可以看到机器的思考过程,还可以标识出每一个落子点的优劣势:二是因为可以为机器增加游戏经验,使得机器越来越聪明.希望喜欢五子棋的同学能够喜欢,

借助实时数据推送快速制作在线对战五子棋小游戏丨实战

1 项目概述 游戏开发,尤其是微信小游戏开发,是最近几年比较热门的话题. 本次「云开发」公开课,将通过实战「在线对战五子棋」,一步步带领大家,在不借助后端的情况下,利用「小程序 ? 云开发」,独立完成一款微信小游戏的开发与上线. 2 任务目标 根据项目初始框架,阅读教程的同时,逐步完成棋盘绘制.音乐播放.玩家对战.输赢判定等功能,最终实现一个可以快乐玩耍的在线对战五子棋. 在这个过程中,会了解到 Serverless 的一些概念,并且实际应用它们,比如:云数据库.云存储.云函数.增值能力.除了这

机房小游戏

目录 网页小游戏 slay.one 名字大作战 一些乱七八糟的小游戏 丛林大作战 C++小游戏 猜数字: 冒险小游戏: 俄罗斯方块: 买彩票 三国杀 扫雷 众所周知,在机房里是不能玩游戏的,但某些人仍然胆大包天,想在老师们的火眼金睛下偷偷地"爽"一把,这里我就用我以往的经验给大家推荐一些小游戏. @(qwq) 网页小游戏 这些是作者收录的一些有趣的网页小游戏,可以用来打发无聊的信息学生活. slay.one 这款游戏,许多OIer都非常地熟悉,是一款2D的枪战小游戏,正版在steam上

结对项目之小游戏编程(斗地主)

一.题目简介    本次的项目是编写一个斗地主的小游戏,实现语言:java:主要完成了GUI设计.计时线程.算法.本次项目的主要目的是对算法的学习.算法分析在心得里面. 技术难点:1.图片的移动    2.计时线程的设定   3.对牌的分割,必须考虑到优先拆分方案,将权值低的拆分方案舍去. 4.在删除的时候遇到问题了,删除不了. 二.结对分工及过程 本次结对项目的成员有两个,张国伟:负责对GUI界面的设计,完成功能:洗牌功能,发牌功能,打牌功能的图片的位移处理,基本打牌的桌面等等. 我主要负责对

Delphi的几个跨平台小游戏例子。

Embarcadero开源了几个FireMonkey的小游戏,支持Windows, Android,Ios, MacOS等. 源码地址: https://github.com/EmbarcaderoPublic/DelphiArcadeGames 游戏介绍: http://community.embarcadero.com/blogs?view=entry&id=8927 1.外星人入侵,射击游戏 2.火星火箭 3.Delta火箭飞船 4.太空碎石

分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”

这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业不是百度来的,我只是百度了一些示例代码的意思,怎么用!算了,越解释万一越黑呢!哈哈O(∩_∩)O哈哈~) ----------------------------------------------------------------分界线------------------------------

2048小游戏

需求分析:NABCD N(Need,需求) 生活在21世纪的我们已经进入电子时代,比如平板.笔记本.手机等等.而随着时代的进步,生活学习压力也在不断加大,而越来越多的人都会热衷于玩手机,当你感到无聊的时候,可以上上网,看看新闻,累了之后可以玩玩小游戏缓解一下,所以我们设计了这款2048的小游戏,既开发智力,又缓解疲劳. A(Approach,做法) 我们的小游戏是基于安卓平台的,而且对于其中的功能我们有良好的界面设计.可以和别人进行联机对战:在玩的过程中,游戏的流畅度是非常好的,我们还有种多种背

【C语言探索之旅】 第一部分第八课:第一个C语言小游戏

? 内容简介 1.课程大纲 2.第一部分第八课:第一个C语言小游戏 3.第一部分第九课预告: 函数 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写三个游戏. C语言编程基础知识 什么是编程? 工欲善其事,必先利其器 你的第一个程序 变量的世界 运算那点事 条件表达式 循环语句 实战:第一个C语言小游戏 函数 练习题 习作:完善第一个C语言小游戏 C语言高级技术 模块化编程 进击的指针,C语言王牌 数组 字符串 预处理 创建你自己的变量类型 文