二维游戏中的经典布局方式莫过于网格布局。在网格布局的基础上,还可以实现基于网格的碰撞检测,算法实现简单,且效果较好。碰撞检测的精度主要取决于网格的大小。
(图片素材来源:rpg maker)
网格与图层
所谓网格布局,就是用一个二维数组来存储地图每个位置存放的图片索引,用此数组来编辑地图,然后绘制。比如在这里,我们用-1表示不绘制,0表示浅色草地,1表示深色草地,2表示土块,3表示小路,4表示水面,5表示森林……而在绘制函数中,直接使用二维循环,指定偏移量,就能完成地图的快速绘制。
除了这些基本的地图,我们还有一些比较大的场景物件,比如房子、箱子、树木、桶等等,它们无法绘制在一个小地图块中,而是分布在多个地图块中。在这里,我们并不需要把图片分割成块,只需要直接绘制就可以了,但是我们需要清楚,这张图片占据了哪些网格,我们要在地图索引中把这些块单独标记出来。
比如说,我们可以用-1(不绘制)进行标记,如果图片是含透明通道的,那么我们也可以采用原来的地图图片作为背景,但是一定要用一个新的索引编号,比如在这个代码中,使用12作为浅色草地的索引编号,代表这个网格已经绘制了物体。因为我们需要通过这个编号来告诉碰撞检测的函数,这个网格有障碍物,是不能走过去的。
我们同时还需要实现物体的遮挡关系效果,比如如上图,我们希望人走在房子前面时,人遮挡住房子;而人走到房子后面时,房子遮挡住人。在这里我们可以引入图层的概念,当然,OpenGL中是没有图层这一说的,所以我们实际上是通过z轴坐标来管理。
我们把地图背景放置在z = 1的位置,把人物放置在z = 2的位置。在实现遮挡关系的时候,如果希望人挡住物体,物体的z值就设为1,反之设为3。
同时,我们也注意到,就在上图,这种遮挡关系不是一定的,也就是说,我们有时希望A遮挡B,有时又希望B遮挡住A,这时候只要用一个小小的技巧就能解决这个问题,我们根据人物的位置来判断这个时候谁应该遮挡谁,加一个条件判断语句,来绘制不同位置的物体即可。
总而言之,我们在OpenGL的三维框架下实现二维场景是一件非常轻松愉快的事情,因为我们可以利用多出来的一维,发挥自己的想象力,实现很多障眼法,然而,二维的纸片人永远不知道你动了什么手脚,它看到的仅仅是你搭建出来世界的一个投影而已。
碰撞检测
现在,我们可以来考虑碰撞检测了。我们记录了当前精灵的位置以及每个网格的坐标。在网格布局下,碰撞检测可以非常高效,因为我们根本不需要遍历所有的网格来检查,我们也不用像四叉树一样还需要通过四分搜索来找到可能发生碰撞的物体,根据当前方向和精灵位置,我们可以在常数时间内计算出精灵所在网格。当然,这个常数的大小和精灵有关,在这里,我们的精灵大小只占一个网格,所以问题变得更简单,我们的精灵最多只可能与两个网格发生碰撞,我们首先判断可能发生碰撞的网格是不是我们定义的障碍物,如果是,我们再判断它们在当前方向上的距离是否小于网格长度,如果小于,那么就认为发生碰撞,直接弹回即可。
具体代码是这样的,非常清晰:
//...
i = (s->pos_x + 4.0f) / size;
j = (s->pos_y + 4.0f) / size;
//...
bool test(int i,int j)
{
if (s->dir == s->left&&j>=1) {
if (isBarrier(map[i][j - 1])){ //遇到障碍物
if (s->pos_x - place_x[i][j-1] < size) { //产生碰撞
s->pos_x = place_x[i][j-1]+ size;
return true;
}
}
}
else if (s->dir == s->right&&j <= y-2) {
if (isBarrier(map[i][j + 1])){
if (s->pos_x - place_x[i][j+1] >- size) {
s->pos_x = place_x[i][j+1] - size;
return true;
}
}
}
else if (s->dir == s->front&&i >= 1) {
if (isBarrier(map[i-1][j])) {
if (s->pos_y - place_y[i-1][j] < size) {
s->pos_y = place_y[i-1][j] + size;
return true;
}
}
}
else if (s->dir == s->back&&i <=x-2) {
if (isBarrier(map[i+1][j])) {
if (s->pos_y - place_y[i+1][j] >- size) {
s->pos_y = place_y[i+1][j] - size;
return true;
}
}
}
return false;
}
当然,这个是实际的运算。但是我们也发现,上图的精灵实际上与房子碰撞了——人的头部和房子已经有重叠部分了,但是对于人的视觉而言,这是正常的,因为精灵在房子之前,它们有前后关系,但是我们的算法并没有这么智能,它不知道所谓的前后关系,那么,我们的算法是否需要改进呢?
其实也不用,我们只要把房子的位置稍微下移一点就可以了!房子在视觉上下移了,但是网格却还在原来的位置,我们检测的是和网格的碰撞,却不是和房子的碰撞。在图中也就是检测与白线的碰撞。
test.h
#pragma once
#define GLUT_DISABLE_ATEXIT_HACK
#include "GL/GLUT.H"
void loadTex(int i, char *filename, GLuint* texture);//一般纹理
void loadTex(int i, char *filename, GLuint* texture, unsigned char* backgroundColor);//透明纹理
sprite.h
#pragma once
class sprite
{
public:
//精灵位置(中心位置)
float pos_x;
float pos_y;
//帧动画参数
int num = 96;//一共多少帧
int col = 12;//一行有多少帧
//精灵索引下标
//前、左、右、后
int index[4][3][2];
//步长
float step;
//用于计数
int count;
int count2;
int count3;
//精灵贴图
unsigned int texture;
//行走方向(枚举量)
typedef enum { left, right, front, back }direction;
//是否停止
bool isStop = true;
//行走方向
direction dir = front;
sprite(int _col, int _num, float x, float y, unsigned int _texture, int* index, float _step);
//快速索引绘制精灵
void drawRect(unsigned int texture, int i, int j);
//绘制精灵
void drawSprite();
};
sprite.cpp
#include"sprite.h"
#define GLUT_DISABLE_ATEXIT_HACK
#include "GL/GLUT.H"
sprite::sprite(int _col, int _num, float x, float y, unsigned int _texture, int* _index, float _step)
{
count = count2 = count3 = 0;
col = _col;
num = _num;
pos_x = x;
pos_y = y;
texture = _texture;
int cnt = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 2; k++) {
index[i][j][k] = _index[cnt++];
}
}
}
step = _step;
}
void sprite::drawRect(unsigned int texture, int i, int j)
{
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 x = 1.0 / col, y = 1.0 / (num / col);
const GLfloat point[4][2] = { { x1,y1 },{ x2,y1 },{ x2,y2 },{ x1,y2 } };
const GLfloat dir[4][2] = { { j*x,1 - (i + 1)*y },{ (j + 1)*x,1 - (i + 1)*y },{ (j + 1)*x ,1 - i*y },{ j*x,1 - i*y } };
glBegin(GL_QUADS);
for (int k = 0; k < 4; k++) {
glTexCoord2fv(dir[k]);
glVertex2fv(point[k]);
}
glEnd();
glDisable(GL_TEXTURE_2D);
}
void sprite::drawSprite()
{
const int step = 50;
count++;
if (isStop) {
if (dir == front) {
drawRect(texture, index[0][1][0], index[0][1][1]);
}
else if (dir == back) {
drawRect(texture, index[3][1][0], index[3][1][1]);
}
else if (dir == left) {
drawRect(texture, index[1][1][0], index[1][1][1]);
}
else if (dir == right) {
drawRect(texture, index[2][1][0], index[2][1][1]);
}
}
else if (dir == front) {
if (count <= step) {
drawRect(texture, index[0][0][0], index[0][0][1]);
}
else if (count > step&&count <= step * 2) {
drawRect(texture, index[0][1][0], index[0][1][1]);
}
else if (count > step * 2 && count <= step * 3) {
drawRect(texture, index[0][2][0], index[0][2][1]);
}
}
else if (dir == back) {
if (count <= step) {
drawRect(texture, index[3][0][0], index[3][0][1]);
}
else if (count > step && count <= step * 2) {
drawRect(texture, index[3][1][0], index[3][1][1]);
}
else if (count > step * 2 && count <= step * 3) {
drawRect(texture, index[3][2][0], index[3][2][1]);
}
}
else if (dir == left) {
if (count <= step) {
drawRect(texture, index[1][0][0], index[1][0][1]);
}
else if (count > step && count <= step * 2) {
drawRect(texture, index[1][1][0], index[1][1][1]);
}
else if (count > step * 2 && count <= step * 3) {
drawRect(texture, index[1][2][0], index[1][2][1]);
}
}
else if (dir == right) {
if (count <= step) {
drawRect(texture, index[2][0][0], index[2][0][1]);
}
else if (count > step && count <= step * 2) {
drawRect(texture, index[2][1][0], index[2][1][1]);
}
else if (count > step * 2 && count <= step * 3) {
drawRect(texture, index[2][2][0], index[2][2][1]);
}
}
if (count%step == 0) {
if (count2 == count3) {
if (dir == front) {
drawRect(texture, index[0][1][0], index[0][1][1]);
}
else if (dir == back) {
drawRect(texture, index[3][1][0], index[3][1][1]);
}
else if (dir == left) {
drawRect(texture, index[1][1][0], index[1][1][1]);
}
else if (dir == right) {
drawRect(texture, index[2][1][0], index[2][1][1]);
}
isStop = true;
}
count3 = count2;
}
if (count == step * 3) {
count = 0;
}
}
texture.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>
#include"test.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(char *filename, BITMAPINFOHEADER *bitmapInfoHeader, unsigned char* backgroundColor)
{
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;
//添加alpha通道
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] >= backgroundColor[0]
&& bitmapData[count + 1] >= backgroundColor[1]
&& bitmapData[count + 2] >= backgroundColor[2]) {
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(int i, char *filename, GLuint* texture, unsigned char* backgroundColor)
{
BITMAPINFOHEADER bitmapInfoHeader; // bitmap信息头
unsigned char* bitmapData; // 纹理数据
bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader, backgroundColor);
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_RGBA, //我们希望该纹理有红、绿、蓝、alpha数据
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"test.h"
#include"sprite.h"
#define size 0.2f
//视区
float whRatio;
int wHeight = 0;
int wWidth = 0;
//网格个数
const int x = 40;
const int y = 40;
GLuint texture[23];
//视窗大小
float size_x = 4.0f;
float size_y = 4.0f;
//视点
float center[] = { 0, 0, 0 };
float eye[] = { 0, 0, 5 };
//每个网格的坐标
float place_x[x][y];
float place_y[x][y];
//精灵
sprite *s;
int map[x][y] = { 0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,
0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,8,
0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,1,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,5,5,5,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,0,3,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,5,5,5,5,0,3,0,0,12,12,12,0,0,0,0,5,-1,-1,-1,-1,-1,-1,5,0,
0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,5,5,5,5,0,3,0,0,0,12,0,0,12,12,0,0,-1,-1,-1,-1,-1,-1,5,5,
3,3,3,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,5,5,0,3,0,0,0,0,0,12,12,12,12,0,-1,-1,-1,-1,-1,-1,5,5,
0,0,3,0,0,0,0,1,4,4,4,4,4,1,1,0,0,0,0,0,0,3,0,0,0,0,0,12,12,12,12,0,-1,-1,-1,-1,-1,-1,5,5,
0,0,3,0,0,0,0,1,4,4,4,4,4,1,1,0,0,0,0,0,0,3,0,0,0,0,0,12,12,12,12,0,-1,-1,-1,-1,-1,-1,0,0,
0,0,3,0,0,0,0,1,4,4,4,4,4,4,1,0,0,0,0,0,0,3,0,0,0,0,0,0,12,12,12,12,12,12,12,12,12,12,12,0,
0,0,3,3,3,0,0,1,4,4,4,4,4,4,1,0,0,0,0,0,0,3,0,0,0,0,0,0,0,12,12,12,12,12,0,12,12,12,12,0,
0,0,0,0,3,0,0,1,4,4,4,4,4,4,1,0,0,0,3,3,3,3,3,3,0,0,0,0,0,12,12,12,12,12,0,12,12,12,0,0,
0,0,0,0,3,0,0,1,7,4,4,4,4,4,1,0,0,0,3,0,0,0,0,3,0,0,0,0,0,0,12,12,0,0,0,0,0,0,0,0,
0,0,0,0,3,0,0,1,7,4,4,4,4,1,0,0,0,0,3,0,0,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,2,2,0,3,0,0,0,0,1,1,1,1,0,0,0,0,0,3,0,0,-1,-1,-1,-1,-1,-1,0,0,0,12,0,0,0,3,3,3,3,3,3,
2,2,2,0,3,0,0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,-1,-1,-1,-1,-1,-1,0,12,12,12,0,0,0,3,0,0,0,0,0,
2,2,2,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,3,0,0,0,0,0,
0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,4,4,
0,0,0,0,0,0,0,0,0,0,1,1,3,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,4,4,
0,0,0,0,0,0,1,1,1,1,1,1,3,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,3,0,0,0,0,0,3,0,0,4,4,4,
0,0,0,0,0,1,1,1,1,1,1,1,3,1,0,0,0,1,5,5,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,0,0,3,0,0,4,4,4,
0,0,0,0,1,1,1,1,1,1,1,1,3,1,1,5,5,5,5,5,0,0,12,12,0,-1,-1,-1,-1,-1,-1,-1,0,0,3,0,0,4,4,4,
0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,5,5,5,5,5,0,12,12,12,12,-1,-1,-1,-1,-1,-1,-1,0,0,3,0,4,4,4,4,
0,0,0,0,0,0,0,0,0,0,1,1,3,0,0,5,5,5,5,5,0,12,12,12,12,-1,-1,-1,-1,-1,-1,-1,0,0,3,0,4,4,4,4,
0,0,0,0,0,0,0,3,3,3,3,3,3,0,0,5,5,5,5,5,5,12,12,12,12,-1,-1,-1,-1,-1,-1,-1,0,0,3,0,4,4,4,4,
0,0,0,0,0,0,0,3,1,1,1,1,3,1,1,0,5,5,5,5,5,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,0,0,3,0,4,4,4,4,
0,0,0,0,0,-1,-1,-1,-1,-1,-1,0,3,1,1,0,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,4,4,4,4,
0,0,0,12,12,-1,-1,-1,-1,-1,-1,0,3,1,1,0,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,4,4,4,
0,0,12,12,12,-1,-1,-1,-1,-1,-1,0,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,4,4,4,
0,0,12,12,12,-1,-1,-1,-1,-1,-1,0,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,4,4,4,
0,0,0,0,0,0,0,0,0,0,0,0,3,0,1,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,4,4,
0,0,0,0,0,0,0,0,0,0,0,0,3,0,1,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,
0,0,0,0,0,0,0,0,0,0,0,0,3,1,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,12,
7,7,7,7,7,7,7,7,7,7,7,7,3,0,0,0,0,0,0,0,0,0,3,0,0,-1,-1,-1,0,0,0,0,0,0,0,12,12,12,12,12,
7,7,7,7,7,7,7,7,7,7,7,7,3,0,0,0,0,0,0,0,0,0,3,0,0,-1,-1,-1,0,0,0,0,0,0,0,0,12,12,12,12,
7,7,7,7,7,7,7,7,7,7,7,7,3,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,
7,7,7,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,12,0,0,
7,7,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
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);
}
void drawScene()
{
//绘制地图块
glPushMatrix();
glTranslatef(-3.9f, -3.9f, 0.0f);
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
glPushMatrix();
glScalef(size, size, 1);
if(map[i][j]!=-1)drawRect(texture[map[i][j]]);
glPopMatrix();
glTranslatef(size, 0, 0);
}
glTranslatef(-y*size, size, 0);
}
glPopMatrix();
//绘制房子
glPushMatrix();
glTranslatef(3.0f, -2.5f, 1.0f);
glScalef(size*6, size*5, 1);
drawRect(texture[10]);
glPopMatrix();
//绘制树
glPushMatrix();
glTranslatef(1.8f, -2.3f, 1.0f);
glScalef(size * 4, size * 5, 1);
drawRect(texture[11]);
glPopMatrix();
glPushMatrix();
glTranslatef(2.3f, -1.7f, 1.0f);
glScalef(size * 4, size * 5, 1);
drawRect(texture[14]);
glPopMatrix();
glPushMatrix();
glTranslatef(3.3f, -1.9f, 1.0f);
glScalef(size * 4, size * 5, 1);
drawRect(texture[14]);
glPopMatrix();
//绘制井
glPushMatrix();
if (s->pos_y > -3.8f&&s->pos_x<1.7f) {
glTranslatef(1.4f, -3.5f, 3.0f);
}
else{
glTranslatef(1.4f, -3.5f, 1.0f);
}
glScalef(size * 2.6, size * 3.5, 1);
drawRect(texture[15]);
glPopMatrix();
//绘制房子
glPushMatrix();
if(s->pos_y>-0.8f&&s->pos_x<1.4f)glTranslatef(0.85f, -0.8f,2.0f);
else glTranslatef(0.85f, -0.8f, 1.0f);
glScalef(size * 6.6, size * 5, 1);
drawRect(texture[16]);
glPopMatrix();
//桶
glPushMatrix();
glTranslatef(1.9f, -0.8f, 1.0f);
glScalef(size * 3, size*2 , 1);
drawRect(texture[17]);
glPopMatrix();
//灌木
glPushMatrix();
glTranslatef(3.5f, 2.9f, 1.0f);
glScalef(size * 2, size * 2, 1);
drawRect(texture[18]);
glPopMatrix();
glPushMatrix();
glTranslatef(3.2f, 3.0f, 1.0f);
glScalef(size * 2, size * 2, 1);
drawRect(texture[18]);
glPopMatrix();
glPushMatrix();
glTranslatef(3.8f, 3.1f, 1.0f);
glScalef(size * 2, size * 2, 1);
drawRect(texture[18]);
glPopMatrix();
glPushMatrix();
glTranslatef(3.3f, 3.3f, 1.0f);
glScalef(size * 2, size * 2, 1);
drawRect(texture[18]);
glPopMatrix();
glPushMatrix();
glTranslatef(3.7f, 3.4f, 1.0f);
glScalef(size * 2, size * 2, 1);
drawRect(texture[18]);
glPopMatrix();
glPushMatrix();
glTranslatef(3.5f, 3.5f, 1.0f);
glScalef(size * 2, size * 2, 1);
drawRect(texture[18]);
glPopMatrix();
glPushMatrix();
glTranslatef(1.1f, -2.9f, 1.0f);
glScalef(size * 3, size * 2, 1);
drawRect(texture[13]);
glPopMatrix();
//绘制箱子
glPushMatrix();
if (s->pos_y > 3.2f&&s->pos_x<1.6f)glTranslatef(1.4f, 3.2f, 3.0f);
else glTranslatef(1.4f, 3.2f, 1.0f);
glScalef(size * 4, size * 3, 1);
drawRect(texture[19]);
glPopMatrix();
//绘制房子
glPushMatrix();
glTranslatef(1.8f, 1.0f, 1.0f);
glScalef(size * 8, size * 6, 1);
drawRect(texture[20]);
glPopMatrix();
//绘制树
glPushMatrix();
glTranslatef(0.6f, 1.0f, 1.0f);
glScalef(size * 3.5, size * 4.6, 1);
drawRect(texture[14]);
glPopMatrix();
//绘制房子
glPushMatrix();
if (s->pos_y > 2.0f&&s->pos_x < -1.85f) {
glTranslatef(-2.4f, 2.0f, 3.0f);
}
else glTranslatef(-2.4f, 2.0f, 1.0f);
glScalef(size * 6, size * 5, 1);
drawRect(texture[10]);
glPopMatrix();
//灌木
glPushMatrix();
glTranslatef(-3.2f, 1.9f, 1.0f);
glScalef(size * 2, size * 2, 1);
drawRect(texture[18]);
glPopMatrix();
glPushMatrix();
glTranslatef(-3.5f, 2.1f, 1.0f);
glScalef(size * 2, size * 2, 1);
drawRect(texture[18]);
glPopMatrix();
glPushMatrix();
glTranslatef(-3.3f, 2.2f, 1.0f);
glScalef(size * 2, size * 2, 1);
drawRect(texture[18]);
glPopMatrix();
//精灵
glPushMatrix();
glTranslatef(s->pos_x, s->pos_y, 2);
glScalef(size, size, 1);
s->drawSprite();
glPopMatrix();
}
void updateView(int height, int width)
{
glViewport(0, 0, width*2*3/4, height*2);
glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影
glLoadIdentity(); //初始化矩阵为单位矩阵
whRatio = (GLfloat)width / (GLfloat)height; //设置显示比例
glOrtho(-4, 4, -4, 4, -100, 100); //正投影
glMatrixMode(GL_MODELVIEW); //设置矩阵模式为模型
}
//判断是否是障碍物
inline bool isBarrier(int map)
{
//定义纹理索引编号为0,1,3的不是障碍物,该数字可以自己指定
if (map != 1 && map != 0 && map != 3) {
return true;
}
else return false;
}
//碰撞检测
bool test(int i,int j)
{
if (s->dir == s->left&&j>=1) {
if (isBarrier(map[i][j - 1])){ //遇到障碍物
if (s->pos_x - place_x[i][j-1] < size) { //产生碰撞
s->pos_x = place_x[i][j-1]+ size;
return true;
}
}
}
else if (s->dir == s->right&&j <= y-2) {
if (isBarrier(map[i][j + 1])){
if (s->pos_x - place_x[i][j+1] >- size) {
s->pos_x = place_x[i][j+1] - size;
return true;
}
}
}
else if (s->dir == s->front&&i >= 1) {
if (isBarrier(map[i-1][j])) {
if (s->pos_y - place_y[i-1][j] < size) {
s->pos_y = place_y[i-1][j] + size;
return true;
}
}
}
else if (s->dir == s->back&&i <=x-2) {
if (isBarrier(map[i+1][j])) {
if (s->pos_y - place_y[i+1][j] >- size) {
s->pos_y = place_y[i+1][j] - size;
return true;
}
}
}
return false;
}
//碰撞检测入口与地图卷动
void collisionTest()
{
int i1, j1, i2, j2;
bool flag = false;
if (s->dir == s->right || s->dir == s->left) {
//计算得到当前人物位置索引
j1 = (s->pos_x + 4.0f) / size;
i1 = (s->pos_y + 4.0f) / size;
//执行2次碰撞检测
if (test(i1, j1)) {
flag = true;
}
i2 = (s->pos_y + 4.0f + size / 4) / size;
if (i2 != i1) {
if (test(i2, j1)) {
flag = true;
}
}
i2 = (s->pos_y + 4.0f - size / 4) / size;
if (i2 != i1) {
if (test(i2, j1)) {
flag = true;
}
}
//地图卷动
if (s->dir == s->right&&j1 > x / 2 - 5 && j1<x&&!flag) {
eye[0] += s->step;
center[0] += s->step;
if (eye[0]>4.0f * 2 / 3) {
eye[0] = 4.0f * 2 / 3;
center[0] = 4.0f * 2 / 3;
}
}
else if (s->dir == s->left&&j1 > 0 && j1<x / 2 + 5 && !flag) {
eye[0] -= s->step;
center[0] -= s->step;
if (eye[0]<0) {
eye[0] = 0;
center[0] = 0;
}
}
}
flag = false;
if (s->dir == s->front || s->dir == s->back) {
//计算得到当前人物位置索引
j1 = (s->pos_x + 4.0f) / size;
i1 = (s->pos_y + 4.0f) / size;
if (test(i1, j1)) {
flag = true;
}
//执行2次碰撞检测
j2 = (s->pos_x + 4.0f + size / 4) / size;
if (j2 != j1) {
if (test(i1, j2)) {
flag = true;
}
}
j2 = (s->pos_x + 4.0f - size / 4) / size;
if (j2 != j1) {
if (test(i1, j2)) {
flag = true;
}
}
//地图卷动
if (s->dir == s->back&&i1 > y / 2 - 5 && i1<y && !flag) {
eye[1] += s->step;
center[1] += s->step;
if (eye[1]>4.0f) {
eye[1] = 4.0f;
center[1] = 4.0f;
}
}
else if (s->dir == s->front&&i1 > 0 && i1<y / 2 + 5 && !flag) {
eye[1] -= s->step;
center[1] -= s->step;
if (eye[1]<0) {
eye[1] = 0;
center[1] = 0;
}
}
}
}
void key(unsigned char k, int _x, int _y)
{
s->count2++;
switch (k)
{
case 'a': {//向左移动
s->dir = sprite::left;
s->isStop = false;
s->pos_x -= s->step;
if (s->pos_x < -size_x)s->pos_x = -size_x;
collisionTest();
break;
}
case 'd': {//向右移动
s->dir = sprite::right;
s->isStop = false;
s->pos_x += s->step;
if (s->pos_x > size_x)s->pos_x = size_x;
collisionTest();
break;
}
case 'w': {//向上移动
s->dir = sprite::back;
s->isStop = false;
s->pos_y += s->step;
if (s->pos_y > size_y)s->pos_y = size_y;
collisionTest();
break;
}
case 's': {//向下移动
s->dir = sprite::front;
s->isStop = false;
s->pos_y -= s->step;
if (s->pos_y < -size_y)s->pos_y = -size_y;
collisionTest();
break;
}
}
updateView(wHeight, wWidth); //更新视角
}
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);
glAlphaFunc(GL_GREATER, 0.5);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
int index[] = { 0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2,3,0,3,1,3,2 };
unsigned char color[] = {170,170,170 };
unsigned char color2[] = {230,230,230 };
glGenTextures(23, texture);
loadTex(0, "9.bmp", texture);
loadTex(1, "10.bmp", texture);
loadTex(2, "11.bmp", texture);
loadTex(3, "12.bmp", texture);
loadTex(4, "13.bmp", texture);
loadTex(5, "14.bmp", texture);
loadTex(6, "15.bmp", texture);
loadTex(7, "16.bmp", texture);
loadTex(8, "17.bmp", texture);
loadTex(9, "1.bmp", texture,color2);
loadTex(10, "30.bmp", texture);
loadTex(11, "23.bmp", texture,color);
loadTex(13, "24.bmp", texture, color);
loadTex(14, "25.bmp", texture, color);
loadTex(15, "26.bmp", texture, color2);
loadTex(16, "27.bmp", texture);
loadTex(17, "28.bmp", texture, color);
loadTex(18, "31.bmp", texture, color);
loadTex(19, "32.bmp", texture, color);
loadTex(20, "33.bmp", texture);
texture[12] = texture[0];
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
place_x[i][j] = -3.9f + size*j;
place_y[i][j] = -3.9f + size*i;
}
}
s = new sprite(12, 96, -3.5f, -3.5f, texture[9], index, 0.06f);
}
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);
drawScene();//绘制场景
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;
}
图片素材:
1.bmp
9.bmp
10.bmp
11.bmp
12.bmp
13.bmp
14.bmp
15.bmp
16.bmp
17.bmp
23.bmp
24.bmp
25.bmp
26.bmp
28.bmp
30.bmp
31.bmp
32.bmp
33.bmp