我们应该很熟悉游戏里这样的画面:
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(入口,追踪函数) |
chase.cpp
#include
#include
#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随机走了一个方向,这实在是太糟糕了,甚至连最初的朴素算法都不如。
既然问题出在行走方向是随机的,那么我们是否可以引导npc更趋向于选择某个方向呢。当然这是可行的,如果我们把第一种算法的思想和第二种结合起来的话,我们可以得到比较好的结果。
在具体的编程中,我们取出一个点时,检查它邻接点,计算哪个方向是最好的(可以使用哈曼顿距离衡量)。
在这里仅作简单介绍,未实现编程。
在这里,精灵的行走并不是严格意义上的按照网格方向的,比如从坐标点(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依然能够做出非常“聪明”的选择。在检测到另一个方向可以更快的接近主角后,它甚至会选择掉头走。
#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;
};
#include"test.h"
#include
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;
}
#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;
}
}
}
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#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数据指针
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include"test.h"
#include
#include
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()
{
queueq;
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)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)= 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;
}