深圳大学计算机游戏开发实验5 游戏中的人工智能

2024-01-30 14:30

本文主要是介绍深圳大学计算机游戏开发实验5 游戏中的人工智能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、实验目的与要求

二、实验内容与方法

1.完成游戏编译

2.完成修改内容一 

3.完成修改内容二 

4.完成修改内容三 

5.完成修改内容四 

6.完成Bug修改

7.添加配乐、音效

8. 游戏优化升级

三、实验步骤与过程

1. 完成游戏编译并修改游戏名称和窗口大小。

2. 添加DANGEROUS区域。

3. 增加按键监听,当按下R键时,游戏重新开始。

4. 添加地图层次,将迷宫地图设置为两层。

5. 修改游戏中出现的Bug。

Bug1:点击smile所处的位置会显示“No Way!”,按照正常逻辑,宝箱出现的位置与smile相同应该成功找到宝箱才对。

Bug2:点击笑脸出现“No Way” 和“Found Treasure”文字的叠加。这是因为代码中只是简单地向场景中添加Label,而显示新的Label时,原来的Label不会被清除。

Bug3:smile精灵和box精灵的碰撞矩阵太大,相隔一个图块的情况下,程序将其判定为碰撞。

Bug4:当距离危险区域只剩一个图块时,点击进入危险区域,不会显示危险区域提示。

Bug5:下方的“Click Map To Put Treasure”文字无法完全显示。

6. 为游戏添加配乐和音效。

 7. 优化游戏。

优化1:增加迷宫地图层数至4层和增加返回上一层的楼梯。

优化2:显示当前smile位于哪一层地图。

优化3:显示smile的得分,每次找到一个宝箱加一分。

四、实验结论或心得体会


一、实验目的与要求

1. 理解A*寻路算法原理。

2. 进一步熟悉地图编辑器的使用。

3. 实现游戏中的人工智能。

二、实验内容与方法

1.完成游戏编译

成功编译并运行教材P200“游戏AI实例-迷宫寻宝”。

2.完成修改内容一 

修改游戏代码,实现修改内容一,即修改窗口大小。

3.完成修改内容二 

修改游戏代码,实现修改内容二,即增加DANGEROUS区域。

4.完成修改内容三 

修改游戏代码,实现修改内容三,即增加按键监听。

5.完成修改内容四 

修改游戏代码,实现修改内容四,即增加地图层次。

6.完成Bug修改

通过更改游戏代码的方式修复BUG,如:点击笑脸出现“No Way” 和“Found Treasure”的叠加。

7.添加配乐、音效

为游戏添加配乐、音效。

8. 游戏优化升级

自行发挥想象力,优化游戏功能。

三、实验步骤与过程

1. 完成游戏编译并修改游戏名称和窗口大小。

(1)创建游戏项目,详细步骤见我的实验1的文章。

(2)根据地图文件中显示的地图的宽度、高度、图块宽度和高度信息,计算出地图的宽为32 x 32=1024,高为20 x 32=640。如下图所示,aStar.h文件中定义的3个变量也证明了这一点。

 

(3) 在AppDelegate.cpp中,将游戏窗口大小改为1024x640。

static cocos2d::Size designResolutionSize = cocos2d::Size(1024, 700);

2. 添加DANGEROUS区域。

(1)使用Tiled打开地图文件,在地图中添加危险区域的图块,四个图块的编号分别是251、252、275和276。

(2)在aStar.h中定义两个新的变量DANGEROUS和NOT_DANGEROUS,表示方块是否为危险区域,然后在mapNode结构体中添加一个新的成员变量isdangerous,用于记录当前节点是否为危险区域。

#define DANGEROUS 7   //方格是否为危险区域
#define NOT_DANGEROUS 8//定义节点结构:
struct mapNode
{int status;           //节点的状态标志int isdangerous;       //节点是否为危险区域int xCoordinate;	   //节点的横、纵坐标int yCoordinate;int fValue;		   //节点的f值,g值,h值int gValue;int hValue;mapNode* parent;    //节点的父节点指针
};

 (3)在MazeScene类的initMap函数中,遍历地图每一个图块时,先通过getTileGIDAt函数获取当前的图块编号,如果图块编号是251、252、275或276时,表示当前图块为危险区域,将当前图块的结构体mapNode的isdangerous的值设为DANGEROUS,否则设为NOT_DANGEROUS。

//依次扫描地图数组每一个单元for (int i = 0; i< MAP_WIDTH; i++){for (int j = 0; j< MAP_HEIGHT; j++){int temp_isdangerous = NOT_DANGEROUS;//危险区域if (m_mapLayer->getTileGIDAt(Vec2(i, j)) == 251 || m_mapLayer->getTileGIDAt(Vec2(i, j)) == 252|| m_mapLayer->getTileGIDAt(Vec2(i, j)) == 275 || m_mapLayer->getTileGIDAt(Vec2(i, j)) == 276|| m_mapLayer->getTileGIDAt(Vec2(i, j)) == 169 || m_mapLayer->getTileGIDAt(Vec2(i, j)) == 170|| m_mapLayer->getTileGIDAt(Vec2(i, j)) == 185 || m_mapLayer->getTileGIDAt(Vec2(i, j)) == 186){temp_isdangerous = DANGEROUS;//CCLOG("dangreous\n");}//若当前位置为墙体瓦片设置为不可通过if (m_mapLayer->getTileGIDAt(Vec2(i, j)) == NOT_ACCESS_TILE|| m_mapLayer->getTileGIDAt(Vec2(i, j)) == NOT_ACCESS_TILE1){mapNode temp={NOT_ACCESS,temp_isdangerous, i, j, 0, 0, 0, nullptr };m_map[i][j] = temp;}//否则设置为可以通过else{mapNode temp={ ACCESS,temp_isdangerous, i, j, 0, 0, 0, nullptr };m_map[i][j] = temp;}}}

(4)在MazeScene类的移动路径moveOnPath函数中,定义一个变量dangerous_num记录危险区域在当前路径上的编号,当路径中出现危险区域时,立即记录该编号,否则为默认值-2,由于此时路径是从终点向起点遍历的,所以一定能够获取到smile第一次遇到危险区域时的编号。

//危险区域在当前路径上的编号int dangerous_num = -2;while (tempNode != nullptr){//判断是1否存在危险区域if (tempNode->isdangerous == DANGEROUS){dangerous_num = loopNum;}path[loopNum].x = tempNode->xCoordinate;path[loopNum].y = tempNode->yCoordinate;loopNum++;tempNode = tempNode->parent;}

在遍历路径的结构体数组时,将结构体的编号和dangerous_num进行比较,如果相等表示存在危险区域,显示危险区的提示文字,并直接退出遍历。

 (5)运行程序,当smile移动路线上有危险区域时,显示“Dangerous! You die!”文字,路径被危险区域阻挡,无法移动至新的区域。

3. 增加按键监听,当按下R键时,游戏重新开始。

(1)在MazeScene类的init函数中调用EventListenerKeyboard类的create函数定义变量listener,使用onKeyReleased函数为监听器添加键盘事件,当按下R按键时,调用replaceScene函数重新加载场景,最后调用addEventListenerWithSceneGraphPriority函数设置键盘监听优先级。

//增加R按键监听auto listener = EventListenerKeyboard::create();listener->onKeyReleased=[&](EventKeyboard::KeyCode code, Event* e){switch (code){case EventKeyboard::KeyCode::KEY_R:Director::getInstance()->replaceScene(this->createScene());break;default:break;}};Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);

4. 添加地图层次,将迷宫地图设置为两层。

(1)使用Tiled设计一个1024x640的新地图,由于图块元素太多会让代码写起来更复杂,因此我让不可通行的区域用同一种图块,这样代码写起来比较简洁。

接着我新建一个图层覆盖在其上面,这个图层仅仅起到让地图更加美观的作用。

最后的地图效果如下图所示,图中白色的草为危险区域,绿色、黄色图块和桥都是可通行区域,水域和墙面都是不可通行区域。

(2)在MazeScene类中添加两个新的成员变量,一个用于记录当前游戏进入第几层的迷宫,另一个变量用于判断smile是否位于楼梯。

//位于第几层迷宫int map_layer = 0;//是否位于楼梯bool is_starts_up = false;

(3)修改MazeScene类的init函数,当map_layer等于0时,加载第一层的地图,当map_layer等于1时,加载第二层的地图。

(4)修改MazeScene类的initMap函数,在危险区域和不可通过区域的判断条件中添加第二层的地图对应的图块编号。

//危险区域if (m_mapLayer->getTileGIDAt(Vec2(i, j)) == 251 || m_mapLayer->getTileGIDAt(Vec2(i, j)) == 252|| m_mapLayer->getTileGIDAt(Vec2(i, j)) == 275 || m_mapLayer->getTileGIDAt(Vec2(i, j)) == 276|| m_mapLayer->getTileGIDAt(Vec2(i, j)) == 169 || m_mapLayer->getTileGIDAt(Vec2(i, j)) == 170|| m_mapLayer->getTileGIDAt(Vec2(i, j)) == 185 || m_mapLayer->getTileGIDAt(Vec2(i, j)) == 186){temp_isdangerous = DANGEROUS;//CCLOG("dangreous\n");}//若当前位置为墙体瓦片设置为不可通过if (m_mapLayer->getTileGIDAt(Vec2(i, j)) == NOT_ACCESS_TILE|| m_mapLayer->getTileGIDAt(Vec2(i, j)) == NOT_ACCESS_TILE1){mapNode temp={NOT_ACCESS,temp_isdangerous, i, j, 0, 0, 0, nullptr };m_map[i][j] = temp;}

 (5)当点击的图块为楼梯时,应该不显示宝箱。在MazeScene类的onTouchBegan中,要为点击的地图单元添加宝箱精灵,通过getTileGIDAt函数获取点击的图块的编号,如果其编号为4即楼梯,就通过setVisible函数将宝箱精灵设为不可见,并将is_starts变量设为true。

//在当前地图单元添加宝箱精灵auto box = this->getChildByTag(BOX_TAG);box->setPosition(0.5 * UNIT + x * UNIT, m_visibleSize.height - 0.5 * UNIT - y * UNIT);box->setVisible(true);
//终点为楼梯if (m_mapLayer->getTileGIDAt(Vec2(x, y)) == 4){box->setVisible(false);is_starts_down = true;}

(6)在MazeScene类的update函数中,通过intersectsRect函数判断smile是否到达宝箱的位置,如果到达宝箱的位置,通过is_starts的值判断目的地是否为楼梯,如果为楼梯,则根据map_layer的值判断进入下一层还是回到第一层,最后调用removeAllChildren函数将本类的所有子节点移除,并调用init函数重新初始化场景,这样就能加载新的地图。

5. 修改游戏中出现的Bug。

Bug1:点击smile所处的位置会显示“No Way!”,按照正常逻辑,宝箱出现的位置与smile相同应该成功找到宝箱才对。

(1)在MazeScene类的aStar函数中,通过判断OPEN表是否为空来判断寻路失败与否,但是当点击的位置正好就是smile所处的位置时,OPEN表也是空的,因此,此时要判断起点m_origin和终点destination的x,y坐标是否相同,如果相同,就不显示“No Way”文字。

	//循环检验8个方向的相邻节点while (checkNeighboringNodes(map, open, open->openNode, destination)){//从OPEN表中选取节点插入CLOSED表insertNodeToClosedList(close, open);//若OPEN表为空,表明寻路失败if (open == nullptr){//点击自身不显示no wayif (m_origin->xCoordinate != destination->xCoordinate&& m_origin->yCoordinate != destination->yCoordinate)
Bug2:点击笑脸出现“No Way” 和“Found Treasure”文字的叠加。这是因为代码中只是简单地向场景中添加Label,而显示新的Label时,原来的Label不会被清除。

(1)在MazeScene类定义3个变量,分别表示3种情况下要显示的Label的TAG。

#define TIP_TAG 5
#define WAY_TAG 6
#define DANGEROUS_TAG 7

(2)在MazeScene类的init函数中,添加3种情况下要显示的文字,并且调用setVisible函数将Label设为不可见。

//预置找到宝箱的提示,将其设置为不可见auto successTip = Label::createWithBMFont("fonts/futura-48.fnt", "Found Treasure!");successTip->setPosition(m_visibleSize.width / 2, m_visibleSize.height / 2);this->addChild(successTip, 2,TIP_TAG);successTip->setVisible(false);//预置屏幕中央显示寻路失败的提示auto failedTip = Label::createWithBMFont("fonts/futura-48.fnt", "No Way!");failedTip->setPosition(m_visibleSize.width / 2, m_visibleSize.height / 2);this->addChild(failedTip, 2, WAY_TAG);failedTip->setVisible(false);//预置屏幕中央显示危险区的提示auto dangerousTip = Label::createWithBMFont("fonts/futura-48.fnt", "Dangerous! You die!");dangerousTip->setPosition(m_visibleSize.width / 2, m_visibleSize.height / 2);this->addChild(dangerousTip, 2, DANGEROUS_TAG);dangerousTip->setVisible(false);

(3)在要显示文字的时候,通过getChildByTag获取3个Label的实例,然后通过setVisible将要显示的Label设为可见,其他两个Label设为不可见。

//显示寻路成功的标签提示auto tip1 = this->getChildByTag(TIP_TAG);auto tip2 = this->getChildByTag(WAY_TAG);auto tip3 = this->getChildByTag(DANGEROUS_TAG);tip1->setVisible(true);tip2->setVisible(false);tip3->setVisible(false);
Bug3:smile精灵和box精灵的碰撞矩阵太大,相隔一个图块的情况下,程序将其判定为碰撞。

(1)这实际上是因为MazeScene类的update中判断smile精灵是否移动到box精灵位置的条件中使用了getBoundingBox获取的矩形,这个区域太大了,不符合要求。

 (2)其实smile和box精灵的位置是根据地图图块像素和地图宽高计算出来的,因为是用的同样的计算方法,所以它们在每个相同图块中对应的实际位置也是相同的,只需要用getPosition函数获取其实际坐标进行比较即可。

//如果笑脸精灵达到宝箱位置if ( smile->getPosition().equals( box->getPosition() ) )
Bug4:当距离危险区域只剩一个图块时,点击进入危险区域,不会显示危险区域提示。

(1)这实际上是因为在MazeScene类的moveOnPath函数中,结构体的是从loopNum-2开始扫描的,即直接扫描其下一个图块,而我的判断条件是j-1,当只差一个时,loopNum值为2,此时不满足危险区域条件,这是我代码设计的漏洞。

(2)如下图所示,让结构体的扫描从loopNum-1开始,这样smile移动的第一个位置变成其当前所处的位置,这样即使相差一个图块也能让j-1的判断条件生效了。

	//从结构体数组尾部开始扫描for (int j = loopNum - 1; j >= 0; j--)
Bug5:下方的“Click Map To Put Treasure”文字无法完全显示。

(1)将窗口高度改为700,这样就能让文字完全显示了。

(2)尝试运行程序,成功让文字完全显示。

6. 为游戏添加配乐和音效。

(1)将背景音乐和音效要使用的文件添加到Resources文件夹中。

(2)由于要让背景音乐在切换地图时不重新播放,要在MazeScene类的createScene方法中加载背景音乐并且循环播放,预加载3个音效。

//bgmSimpleAudioEngine::getInstance()->preloadBackgroundMusic("boss1_bgm.wav");SimpleAudioEngine::getInstance()->playBackgroundMusic("boss1_bgm.wav", true);//音效SimpleAudioEngine::getInstance()->preloadEffect("found.wav");SimpleAudioEngine::getInstance()->preloadEffect("press.wav");SimpleAudioEngine::getInstance()->preloadEffect("wrong.wav");

 (3)接着添加开启和关闭背景音乐的按钮,创建关闭和开启音乐的Button的实例,将两个按钮的位置设置在右下角,然后调用setVisible函数将开启音乐按钮设置为不可见。

//按钮Button* button_v = Button::create("Chapter11/v_btn.png","Chapter11/v_btn.png");Button* button_nv = Button::create("Chapter11/nv_btn.png","Chapter11/nv_btn.png");button_v->setScale(0.5);button_nv->setScale(0.5);button_v->setPosition(Vec2(Vec2(visibleSize.width - 20, 20)));button_nv->setPosition(Vec2(Vec2(visibleSize.width - 20, 20)));//隐藏button_nv->setVisible(false);scene->addChild(button_v, 113);scene->addChild(button_nv, 113);

(4)添加触摸事件,当按下关闭音乐按钮时,调用setVisible函数将关闭音乐按钮设置为不可见,开启音乐按钮设置为可见,接着调用SimpleAudioEngine类的stopBackgroundMusic函数关闭背景音乐。

接着为开启按钮添加触摸事件,按下按钮时,将关闭音乐按钮设置为可见,开启音乐按钮设置为不可见,接着调用SimpleAudioEngine类的playBackgroundMusic函数播放背景音乐。

//关闭bgmbutton_v->addTouchEventListener([button_v, button_nv](Ref* sender, Widget::TouchEventType type){switch (type){case cocos2d::ui::Widget::TouchEventType::BEGAN:break;case cocos2d::ui::Widget::TouchEventType::MOVED:break;case cocos2d::ui::Widget::TouchEventType::ENDED:button_nv->setVisible(true);button_v->setVisible(false);SimpleAudioEngine::getInstance()->stopBackgroundMusic();SimpleAudioEngine::getInstance()->stopAllEffects();bgm = false;break;}});//开启bgmbutton_nv->addTouchEventListener([button_v, button_nv](Ref* sender, Widget::TouchEventType type){switch (type){case cocos2d::ui::Widget::TouchEventType::BEGAN:break;case cocos2d::ui::Widget::TouchEventType::MOVED:break;case cocos2d::ui::Widget::TouchEventType::ENDED:button_v->setVisible(true);button_nv->setVisible(false);SimpleAudioEngine::getInstance()->playBackgroundMusic("2D MUSIC LOOP.wav", true);bgm = true;break;}});

(5)在按钮的触摸事件中直接使用stopAllEffects函数无法关闭音效,因为音效不是一直播放的,只播放几秒钟的时间,因此我在aStar类中添加一个静态变量控制音效的开关,当关闭按钮的触摸事件触发时,其值为false,当开启按钮的触摸事件触发时,其值为true。

static bool bgm;

(6)添加“no way”和“dangerous”时的音效,在MazeScene类的aStar函数中会通过判断OPEN表是否为空来判断寻路是否成功,如果寻路失败则使用stopAllEffects函数停止正在播放的音效,然后根据静态bgm的值判断是否通过playEffect函数播放音效。

//音效SimpleAudioEngine::getInstance()->stopAllEffects();if (bgm){SimpleAudioEngine::getInstance()->playEffect("wrong.wav", false);}

在MazeScene类的moveOnPath函数中,判断路径中存在危险区域时,也要以同样的规则播放音效。

//判断是否存在危险区域if (j - 1 == dangerous_num){//音效SimpleAudioEngine::getInstance()->stopAllEffects();if (bgm){SimpleAudioEngine::getInstance()->playEffect("wrong.wav", false);}

(7)添加寻宝成功的音效,因为寻宝成功的文字是在smile到达宝箱位置时才显示,所以音效也要在此时播放。先在MazeScene类中添加一个成员变量is_found,用来判断是否播放寻宝成功的音效。

	//音效bool is_found = false;

MazeScene类的aStar函数寻路成功时,就要将is_found的值改为true。

在MazeScene类的update函数中,当smile到达宝箱位置时,要在bgm和is_found的值都为true的情况下才播放寻宝成功的音效,并将is_found的值重新改为false。

(8)添加鼠标点击的音效,在MazeScene类的onTouchBegan函数中根据bgm的值判断是否播放音效即可。

 7. 优化游戏。

优化1:增加迷宫地图层数至4层和增加返回上一层的楼梯。

(1)在Tiled创建两个新的地图superMaze3.tmx和superMaze4.tmx,并用如下图所示的图块分别表示“返回上一层”和“进入下一层”,并且第一层地图只有向下的楼梯,第四层地图只有向上的楼梯。

 

 (2)在MazeScene类的init函数中,根据map_layer的值判断加载哪一层的地图,并且每个地图的主图层都是“layer1”。

//载入地图,将放置到适当位置TMXTiledMap* tileMap;switch (map_layer){case 0:tileMap = TMXTiledMap::create("Chapter11/superMaze1.tmx");break;case 1:tileMap = TMXTiledMap::create("Chapter11/superMaze2.tmx");break;case 2:tileMap = TMXTiledMap::create("Chapter11/superMaze3.tmx");break;case 3:tileMap = TMXTiledMap::create("Chapter11/superMaze4.tmx");break;default:break;}tileMap->setPosition(0, m_visibleSize.height - MAP_HEIGHT * UNIT);this->addChild(tileMap, 1);//获取地图中的主图层m_mapLayer = tileMap->getLayer("layer1");//初始化地图数组initMap();

(3)在MazeScene类添加成员变量is_starts_up和is_starts_down,用于判断smile是否位于楼梯图块。

	//是否位于楼梯bool is_starts_up = false;bool is_starts_down = false;

(4)当点击的图块为楼梯时,应该不显示宝箱。在MazeScene类的onTouchBegan中,通过getTileGIDAt函数获取点击的图块的编号,如果其编号为4即向下楼梯,就通过setVisible函数将宝箱精灵设为不可见,并将is_starts_down变量设为true,如果编号为61,将is_starts_up变量设为true。

//终点为楼梯if (m_mapLayer->getTileGIDAt(Vec2(x, y)) == 4){box->setVisible(false);is_starts_down = true;//不得分goal--;}else if (m_mapLayer->getTileGIDAt(Vec2(x, y)) == 61){box->setVisible(false);is_starts_up = true;//不得分goal--;bgm = true;}

(5)在MazeScene类的update函数中,通过intersectsRect函数判断smile是否到达宝箱的位置,如果到达宝箱的位置,通过is_starts_down和is_starts_dp的值判断目的地是否为楼梯,如果为向下楼梯,则进入下一层,map_layer值加一,如果为向上的楼梯,则返回上一层,map_layer值减一,最后调用removeAllChildren函数将本类的所有子节点移除,并调用init函数重新初始化场景,这样就能加载新的地图。

//判断是否到达楼梯if (is_starts_down){map_layer++;//移除所有结点,重新初始化this->removeAllChildren();this->init();is_starts_down = false;}else if (is_starts_up){map_layer--;//移除所有结点,重新初始化this->removeAllChildren();this->init();is_starts_up = false;}
优化2:显示当前smile位于哪一层地图。

(1)在MazeScene类的init函数中添创建一个Label的实例,用于显示地图层数,将map_layer+1的值显示在游戏左下角。

//显示地图层数和得分auto layerTip = Label::createWithBMFont("fonts/futura-48.fnt", "Map Layer: "+Value(map_layer+1).asString());layerTip->setPosition(130, 0.5 * (m_visibleSize.height - MAP_HEIGHT * UNIT));layerTip->setScale(0.8);this->addChild(layerTip, 1);
优化3:显示smile的得分,每次找到一个宝箱加一分。

(1)在MazeScene类中添加2个新的成员变量,一个用于记录当前的分数,一个用于显示当前的分数。

	//得分int goal = 0;Label* goalTip;

(2)在MazeScene类的init函数中添创建一个Label的实例,用于显示当前得分,将goal的值显示在游戏右下角。

 goalTip = Label::createWithBMFont("fonts/futura-48.fnt", "Goal: " + Value(goal).asString());goalTip->setPosition(m_visibleSize.width-150, 0.5 * (m_visibleSize.height - MAP_HEIGHT * UNIT));goalTip->setScale(0.8);this->addChild(goalTip, 1);

因为得分会变化,所以要在MazeScene类的update函数中将新的goal的值赋予Label。

void MazeScene::update(float delta)
{auto smile = this->getChildByTag(SMILE_TAG);auto box = this->getChildByTag(BOX_TAG);//更新得分goalTip->setString("Goal: " + Value(goal).asString());

(3)在MazeScene类的aStar函数中,更新goal的值。当寻路成功时,goal值加一。

(4)当点击楼梯或者危险区域时,根据我设计的代码逻辑,也会将goal值加一,这显然错误的,因此,我在判断是否为楼梯和是否为危险区域的代码中添加一行“goal--”,这样就能让goal的值保持不变了。

四、实验结论或心得体会

在本次实验中,我成功运行了“迷宫寻宝”游戏,增加了DANGEROUS区域、1层新的迷宫地图和游戏配乐与音效,设计了进入下一层地图和返回上一层地图的机制,修改了5个bug。在这些基础上,我还优化了游戏,将地图层数增加到了4,添加了显示地图层数和得分的功能。通过本次实验,我熟练掌握了添加游戏配乐与音效的方法,学习了使用多个图层绘制地图的技巧。

本次实验比较简单,读懂代码之后就非常容易上手了,游戏的Bug比较少,修改起来非常简单。实验最复杂的部分是地图的绘制,如果想要设计比较美观的地图需要花费较长时间,设计地图可以使用多个图层,第一个图层可以作为代码逻辑判断的依据,在这一层规定哪些图块是可通过的,哪些是不可通过的,其他图层会覆盖第一层,因此这些图层的功能仅仅是美化地图,这样达到代码简洁,地图美观的效果,这个地图设计的技巧是我本实验最大的收获。

这篇关于深圳大学计算机游戏开发实验5 游戏中的人工智能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/660536

相关文章

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

基于Python开发批量提取Excel图片的小工具

《基于Python开发批量提取Excel图片的小工具》这篇文章主要为大家详细介绍了如何使用Python中的openpyxl库开发一个小工具,可以实现批量提取Excel图片,有需要的小伙伴可以参考一下... 目前有一个需求,就是批量读取当前目录下所有文件夹里的Excel文件,去获取出Excel文件中的图片,并

基于Python开发PDF转PNG的可视化工具

《基于Python开发PDF转PNG的可视化工具》在数字文档处理领域,PDF到图像格式的转换是常见需求,本文介绍如何利用Python的PyMuPDF库和Tkinter框架开发一个带图形界面的PDF转P... 目录一、引言二、功能特性三、技术架构1. 技术栈组成2. 系统架构javascript设计3.效果图

基于Python开发PDF转Doc格式小程序

《基于Python开发PDF转Doc格式小程序》这篇文章主要为大家详细介绍了如何基于Python开发PDF转Doc格式小程序,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用python实现PDF转Doc格式小程序以下是一个使用Python实现PDF转DOC格式的GUI程序,采用T

使用Python开发一个图像标注与OCR识别工具

《使用Python开发一个图像标注与OCR识别工具》:本文主要介绍一个使用Python开发的工具,允许用户在图像上进行矩形标注,使用OCR对标注区域进行文本识别,并将结果保存为Excel文件,感兴... 目录项目简介1. 图像加载与显示2. 矩形标注3. OCR识别4. 标注的保存与加载5. 裁剪与重置图像

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安