您的位置:首页 > 运维架构

[OpenGL]游戏中的动态追踪算法

2016-08-12 03:59 701 查看
        我们应该很熟悉游戏里这样的画面:

        1.npc在地图上漫无目的的游走

        2.玩家操控主角靠近npc

        3.npc感受到玩家的存在,开始追着玩家跑

        4.玩家操控主角离开npc一定范围,或者遇到障碍物后,npc放弃追踪玩家

        以上行为就是追踪问题。它是游戏编程人工智能的重要一部分,这和传统的人工智能领域可能存在一定的差异,因为我们的npc只要"表现得"足够智能就可以了,在追踪问题中,这种智能表现为:npc会追着玩家的方向前进。

       为了保证游戏的可玩性,我们必须保证npc可以追上玩家(可能性),同样的,我们也必须保证npc不一定追的上玩家(必然性)。

       在这里,我们主要实现了2,3两个部分。

       在以下的样例中,包括了一个头文件test.h,四个源文件:

        collision.cpp(判断碰撞检测)

        sprite.cpp(精灵类)

        texture.cpp(加载纹理)

        main.cpp(入口,追踪函数)

朴素的算法

  
         如果没有相关的算法背景,我们会如何思考这个问题呢?
         一个非常直接的想法就是,如果主角在npc右边,那么npc就往右边走,如果主角在npc左边,那么就往左边走……以此类推。
        当然,我们一次只能选择一个方向,所以我们可以做一个简单的推广:

        如果y /  x > C(C是常数),那么我们优先选择y方向。
        如果x  / y > C(C是常数),那么我们优先选择x方向。
        否则,我们随机选择一个方向。(连续调用下表现为折线走)
     
        注意到这是一个在线的算法,玩家的位置是不确定的,所以,我们总是选择当前最有可能接近玩家的一个方向。这个算法非常快,而且表现也非常好:

        (图片跳动比较快是因为截图软件帧率比较低,实际会好很多)



chase.cpp

#include<math.h>
#include<stdlib.h>
#include"test.h"
void chase(sprite* s,sprite* npc)
{
//计算x,y距离差
float _x = fabs(npc->pos_x - s->pos_x);
float _y = fabs(npc->pos_y - s->pos_y);
if (!(_x<0.1f && _y<0.1f)) {

int r;
//y比x更大,优先走y方向
if (_y / _x > 3)r = 1;
//x比y更大,优先走x方向
else if (_x / _y > 3)r = 0;
//比较接近时走随机方向(效果是折线前进)
else r = rand() % 2;

//根据目标方向选择前进方向
if (r == 0) {
if (s->pos_x > npc->pos_x) {
npc->moveRight();
}
else if (s->pos_x <= npc->pos_x) {
npc->moveLeft();
}
}
else {
if (s->pos_y > npc->pos_y) {
npc->moveBack();
}
else if (s->pos_y <= npc->pos_y) {
npc->moveFront();
}
}
}

return;
}


         当然,这个算法有着比较严重的问题。

         我们一开始考虑的场景是一片空地,但是,实际中的场景往往是有障碍物的,当障碍物出现的时候,这个算法的表现就变成了这个样子:



        当遇到了障碍物后,npc仍然会朝着那个方向前进,而没有意识到自己可以转变方向,在这一点上,我们的人工智能表现得比较“蠢”。当然,我们完全可以把它设计的这么“蠢”,但是,如果我们希望它变得“聪明”一点儿呢?

宽度优先搜索

          我们可能会想,我们可不可以在遇到障碍物或者即将遇到障碍物时,让npc意识到这一点,然后改变方向呢?如果你深究于这个细节,你会发现你很难控制npc总是找到比较好的路,因为当前方有障碍物的时候,我们究竟是该选择后方,左方还是右方呢?我们并不知道哪个才会是最好的。

         之所以出现这样的困扰,是因为我们解决问题时,考虑的方面太少了。我们总是去思考下一步应该怎样走,而没有去思考,完整的路线可以是怎样的。当我们知道了完整的路线后,下一步是什么也就迎刃而解了。

         提到路线,我们很容易想到经典的寻路算法:bfs和dfs,它们可以保证在连通的情况下找到一条从起点到终点的路,当然,我们只需要用到它的第一步。那么,在这个问题中 ,它是否适用呢?我们来回顾搜索算法的步骤,它实际上是漫无目的地向四方搜索的,而我们只使用了第一步,相当于我们是直接让npc随机走了一个方向,这实在是太糟糕了,甚至连最初的朴素算法都不如。

A*算法

       既然问题出在行走方向是随机的,那么我们是否可以引导npc更趋向于选择某个方向呢。当然这是可行的,如果我们把第一种算法的思想和第二种结合起来的话,我们可以得到比较好的结果。

       在具体的编程中,我们取出一个点时,检查它邻接点,计算哪个方向是最好的(可以使用哈曼顿距离衡量)。

       在这里仅作简单介绍,未实现编程。  

最短路径算法

        如果我们非常追求精确性,那么,就可以考虑最短路径算法了。事实上最短路径算法也是在bfs的基础上的,和A*比起来,会尽可能搜索更多的方向。
        如下图,我们可以看到,使用最短路径算法的追踪效果已经非常好了:
        
        


         在这里,精灵的行走并不是严格意义上的按照网格方向的,比如从坐标点(3,4)移动到(3,5),再移动到(5,6)。所以在这里我们在地图中抽象出了网格,然后我们计算精灵的中心的x,y坐标位置(它们实际上是浮点数)处在哪个网格中。

         对于网格里的每一格,我们把它抽象为图中的一个点,设这一格的坐标为i,j,每一行有x个网格,我们用x*i+j来标识每一个点。

         那么,这个点的四个邻接点就是x * i + j - 1   ,  x * i + j + 1  ,  ( x + 1 ) * i + j  ,  ( x - 1 ) * i + j。

        由于npc的行走的步长小于网格的单位长度,所以在最终执行的时候,会产生这样一个问题:npc的中心落在网格的 i , j 坐标,但精灵事实上分布在i , j 和i, j + 1两个网格中,算法已经给出了i , j情况下正确的道路,但是因为npc身体有一部分在i, j + 1网格中,它无法通过这部分一网格的碰撞检测。所以,依然会出现撞墙走的现象。

        当我们的算法检测到npc与墙壁发生了碰撞后,比如是和前方的墙发生了碰撞,我们可以让精灵左右走动一下,避开墙壁,然后再继续按照算法给出的方向前进。

         如果我们给地图加上更多障碍物,npc依然能够做出非常“聪明”的选择。在检测到另一个方向可以更快的接近主角后,它甚至会选择掉头走。

        


代码

        出于电脑性能的考虑,有以下两个参数可以修改,一个是sprite.cpp中的void sprite::drawSprite(),修改step可以改变精灵摆动身体的速度。另一个是main中drawScene函数中的count,可以改变npc移动的速度(一般来说,需要调小很多)。此外init中精灵的构造函数最后一个参数可以修改精灵的速度,可以尝试改变你和npc的速度,来看看追踪的效果有何差异。

test.h
#pragma once

#define GLUT_DISABLE_ATEXIT_HACK
#include "GL/GLUT.H"
class sprite;
void loadTex(int i, char *filename, GLuint* texture);//一般纹理
void loadTex(int i, char *filename, GLuint* texture, unsigned char* backgroundColor);//透明纹理
void collisionTest(sprite* s, float size, int(*map)[20], float(*place_x)[20], float(*place_y)[20], const int x, const int y);
inline bool isBarrier(int map);
class sprite
{
private:
//bool isColl;
//帧动画参数
int num;//一共多少帧
int col;//一行有多少帧

//精灵索引下标
//前、左、右、后
int index[4][3][2];

//步长
float step;

//用于计数
int count;
int count3;

//精灵贴图
GLuint texture;

//是否停止
bool isStop = true;

//快速索引绘制精灵
void drawRect(GLuint texture, int i, int j);

public:
//绘制精灵
void drawSprite();

//用于计数
int count2;

//是否碰撞
bool isColl = false;

//行走方向(枚举量)
typedef enum { left, right, front, back }direction;

//精灵位置(中心位置)
float pos_x;
float pos_y;

sprite(int _col, int _num, float x, float y, GLuint _texture, int* index, float _step);
void moveLeft();
void moveRight();
void moveFront();
void moveBack();
//行走方向
direction dir;
};


sprite.cpp
#include"test.h"
#include<stdio.h>
sprite::sprite(int _col, int _num, float x, float y, GLuint _texture, int* _index, float _step)
{
count = count2 = count3 = 0;
col = _col;
num = _num;
pos_x = x;
pos_y = y;
texture = _texture;
dir = front;
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(GLuint 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 = 800;
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;
}
}
void sprite::moveFront()
{
dir = front;
isStop = false;
pos_y -= step;
if (pos_y < -3.8f)pos_y = -3.9f;
}

void sprite::moveBack()
{
dir = back;
isStop = false;
pos_y += step;
if (pos_y > 3.8f)pos_y = 3.8f;
}

void sprite::moveLeft()
{
dir = left;
isStop = false;
pos_x -= step;
if (pos_x < -3.8f)pos_x = -3.8f;
}

void sprite::moveRight()
{
dir = right;
isStop = false;
pos_x +=step;
if (pos_x > 3.8f)pos_x = 3.8f;
}


collision.cpp
#include"test.h"

//判断是否是障碍物
inline bool isBarrier(int map)
{
//定义纹理索引编号为0的不是障碍物,该数字可以自己指定
if (map != 0) {
return true;
}
else return false;
}

//碰撞检测
bool test(int i, int j, sprite* s, int (*map)[20],float size, float (*place_x)[20], float (*place_y)[20],const int x,const int y)
{
if (s->dir == sprite::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 == sprite::right&&j <= y - 2) {
if (isBarrier(map[i][j + 1])) {
if (place_x[i][j + 1]- s->pos_x < size) {
s->pos_x = place_x[i][j + 1] - size;
return true;
}
}
}
else if (s->dir == sprite::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 == sprite::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(sprite* s, float size, int(*map)[20], float(*place_x)[20], float(*place_y)[20], const int x, const int y)
{
int i1, j1, i2, j2;
s->isColl = 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, s, map, size, place_x, place_y, x, y)) {
s->isColl = true;
}
i2 = (s->pos_y + 4.0f + size / 4) / size;
if (i2 != i1) {
if (test(i2, j1, s, map, size, place_x, place_y, x, y)) {
s->isColl = true;
}
}
i2 = (s->pos_y + 4.0f - size / 4) / size;
if (i2 != i1) {
if (test(i2, j1, s, map, size, place_x, place_y, x, y)) {
s->isColl = true;
}
}
}
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, s, map, size, place_x, place_y, x, y)) {
s->isColl = true;
}
//执行2次碰撞检测
j2 = (s->pos_x + 4.0f + size / 4) / size;
if (j2 != j1) {
if (test(i1, j2, s, map, size, place_x, place_y, x, y)) {
s->isColl = true;
}
}
j2 = (s->pos_x + 4.0f - size / 4) / size;
if (j2 != j1) {
if (test(i1, j2, s, map, size, place_x, place_y, x, y)) {
s->isColl = true;
}
}
}
}


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 <math.h>
#include<queue>
using namespace std;
#define size 0.4f
const float radius = 1.5f;
const float radius2 = 2.5f;
GLuint texture[3];
//视区
float whRatio;
int wHeight = 0;
int wWidth = 0;
bool flag = false;
//视点
float center[] = { 0, 0, 0 };
float eye[] = { 0, 0, 5 };
const int x = 20, y = 20;
//每个网格的坐标
float place_x[x][y];
float place_y[x][y];

int map[x][y] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,
0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,
0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,
0,0,0,1,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,0,0,0,1,1,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,
0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,
0,0,1,1,1,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,
0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,
0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,
0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,
0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,
0,0,1,1,1,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,
0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,
};
/*
int map[x][y] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,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,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
*/
sprite *s;
sprite *npc;

inline float distance(float x1,float y1,float x2,float y2)
{
return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}

int path[x*y];

void ShortestDist();
void move()
{
int j = (s->pos_x + 4.0f) / size;
int i = (s->pos_y + 4.0f) / size;
int k = x*i + j;
int t = 0;
while (path[k] != -1) {
t = k;
k = path[k];
}
if (t == 0) {
npc->moveFront();
}
else if (t / x < k/x) {
npc->moveFront();
}
else if (t / x > k/x) {
npc->moveBack();
}
else if (t%x < k%x) {
npc->moveLeft();
}
else if (t%x > k%x) {
npc->moveRight();
}
collisionTest(npc, size, map, place_x, place_y, x, y);
if (npc->isColl) {
if (npc->dir == sprite::left||npc->dir==sprite::right) {
int r = rand() % 2;
if (r == 0)npc->moveFront();
else npc->moveBack();
}
else if (npc->dir == sprite::front || npc->dir == sprite::back) {
int r = rand() % 2;
if (r == 0)npc->moveLeft();
else npc->moveRight();
}
}
}

void ShortestDist()
{
queue<int>q;
int i;
int dist[x*y];
memset(dist,-1,sizeof(dist));
memset(path, -1, sizeof(path));
//计算得到当前人物位置索引
int j1 = (npc->pos_x + 4.0f) / size;
int i1 = (npc->pos_y + 4.0f) / size;
int j2 = (s->pos_x + 4.0f) / size;
int i2 = (s->pos_y + 4.0f) / size;

dist[x*i1+j1] = 0;
q.push(x*i1 + j1);
int count = 0;
while (!q.empty()) {
int v = q.front();
q.pop();
if ((v + 1) % x&&dist[v + 1] == -1&& !isBarrier(map[(v + 1) / x][(v + 1) % x])) {
dist[v + 1] = dist[v] + 1;
path[v + 1] = v;
q.push(v+1);
if (v + 1 == x*i2 + j2)break;
}
if (v % x&&dist[v - 1] == -1 && !isBarrier(map[(v - 1) / x][(v - 1) % x])) {
dist[v - 1] = dist[v] + 1;
path[v - 1] = v;
q.push(v - 1);
if (v - 1 == x*i2 + j2)break;
}
if ((v + x)<x*y&&dist[v + x] == -1&&!isBarrier(map[(v + x) / x][(v + x) % x])) {
dist[v + x] = dist[v] + 1;
path[v + x] = v;
q.push(v + x);
if (v + x == x*i2 + j2)break;
}
if ((v - x) >0&&dist[v - x] == -1 && !isBarrier(map[(v - x) / x][(v - x) % x])) {
dist[v - x] = dist[v] + 1;
path[v - x] = v;
q.push(v - x);
if (v-x == x*i2 + j2)break;
}

}
move();
}

void chase()
{
float _x = fabs(npc->pos_x - s->pos_x);
float _y = fabs(npc->pos_y - s->pos_y);
if (!(_x<0.1f && _y<0.1f)) {
//计算得到当前人物位置索引
int r;
if (_y / _x > 3)r = 1;
else if (_x / _y > 3)r = 0;
else r = rand() % 2;

if (r == 0) {
if (s->pos_x > npc->pos_x) {
npc->moveRight();
}
else if (s->pos_x <= npc->pos_x) {
npc->moveLeft();
}
}
else {
if (s->pos_y > npc->pos_y) {
npc->moveBack();
}
else if (s->pos_y <= npc->pos_y) {
npc->moveFront();
}
}
collisionTest(npc, size, map, place_x, place_y, x, y);
}

return;
}
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.8f, -3.8f, 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();

static int count = 0;
count++;

glPushMatrix();
glTranslatef(s->pos_x, s->pos_y, 2);
glScalef(size, size, 1);
s->drawSprite();
glPopMatrix();

glPushMatrix();
glTranslatef(npc->pos_x, npc->pos_y, 2);
glScalef(size, size, 1);
npc->drawSprite();
glPopMatrix();

if (distance(s->pos_x,  s->pos_y, npc->pos_x, npc->pos_y)<radius) {
flag = true;
}
if (flag&&count >= 400) {
//chase();
ShortestDist();
count = 0;
}
}

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

void key(unsigned char k, int _x, int _y)
{
s->count2++;
switch (k)
{
case 'a': {//向左移动
s->moveLeft();
collisionTest(s, size, map, place_x, place_y, x, y);
break;
}
case 'd': {//向右移动
s->moveRight();
collisionTest(s, size, map, place_x, place_y, x, y);
break;
}
case 'w': {//向上移动
s->moveBack();
collisionTest(s, size, map, place_x, place_y, x, y);
break;
}
case 's': {//向下移动
s->moveFront();
collisionTest(s, size, map, place_x, place_y, x, y);
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);
unsigned char color[3] = { 255,255,255 };
glGenTextures(3, texture);
loadTex(2, "1.bmp", texture, color);
int index1[] = { 0,3,0,4,0,5,1,3,1,4,1,5,2,3,2,4,2,5,3,3,3,4,3,5 };
s = new sprite(12, 96, -2.0f, -2.5f, texture[2], index1, 0.15f);
int index2[] = { 0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2,3,0,3,1,3,2 };
npc = new sprite(12, 96, 0.0f, 0.0f, texture[2], index2, 0.10f);
loadTex(0, "3.bmp", texture, color);
loadTex(1, "4.bmp", texture, color);
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
place_x[i][j] = -3.8f + size*j;
place_y[i][j] = -3.8f + size*i;
}
}
}

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(600, 600);//设置窗口大小
int windowHandle = glutCreateWindow("Simple GLUT App");//设置窗口标题
glutDisplayFunc(redraw); //注册绘制回调函数
glutReshapeFunc(reshape);   //注册重绘回调函数
glutKeyboardFunc(key); //注册按键回调函数
glutIdleFunc(idle);//注册全局回调函数:空闲时调用

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


图片资源(存为bmp):



1.bmp



3.bmp



4.bmp
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: