深圳大学计算机游戏开发实验1 二维游戏场景绘制

目录

前言

一、实验目的与要求

二、实验内容与方法

三、实验步骤与过程

1. 按照“英雄快跑实验指导”文件指引,尝试运行本次实验游戏

2. 修改窗口大小和游戏显示名称

3. 将学号添加到游戏背景中

4. 查找和修改游戏的BUG

Bug1:改变窗口大小后,窗口中人物动画的位置和实际位置有偏差。

Bug2:有的砖块未添加碰撞检测,角色不能站在砖块上。

Bug3:下图所示的树的高度太高,角色无法跳到其上面。使用Tiled打开地图文件,将树改成合适的高度,这样角色就能跳上树顶了。

Bug4:在显示Gameover字样后,地图还会滚动,过一段时间,会显示Success字样,且与Gameover字样重合。

5. 优化游戏

四、实验结论或心得体会


前言

这门课的所有实验都挺简单的,但是我估摸着混了个A+,不写白不写,本次实验可能会有我没有发现的错误或者仍需完善的内容,但整体上是没有问题的,得分也不低。

一、实验目的与要求

1.熟悉cocos2d-x开发环境。

2.了解cocos2d-x中二维游戏场景绘制方法。

3.掌握瓦片地图编辑器使用方法。

二、实验内容与方法

1.完成基本实验

按照“英雄快跑实验指导”文件指引,成功运行本次实验游戏。

2.修改游戏显示名称 

通过修改游戏代码,使自己的学号替换原“MyGame”字样出现在标题栏左上角。

3.修改游戏地图 

修改原有地图,将你的学号添加在背景中。

4.完成Bug修改 

通过修改游戏代码的方式修复BUG,如:游戏结束响应Bug、树枝检测Bug。

5. 游戏优化

自行设计并优化游戏。

三、实验步骤与过程

1. 按照“英雄快跑实验指导”文件指引,尝试运行本次实验游戏

(1)进入D:\Cocos2d-x\cocos2d-x-3.17.2\tools\cocos2d-console\bin目录,在该目录打开Windows PowerShell,输入cocos new MyGame -p com.学号.edu -l cpp -d Hero_Running命令创建项目。

(2)将“英雄快跑”源代码和Resources中的文件,复制到本项目的Classes和Resources目录中,并将HelloWorldScene.cpp文件和HelloWorldScene.h文件删除,同时更新VS中的资源管理器列表。

(3)修改AppDelegate.cpp的内容,引用头文件MapScene.h,将创建场景的方法改为MapScene类中的createScene方法。

#include "MapScene.h"
  // create a scene. it's an autorelease object
    auto scene = MapScene::createScene();

    // run
    director->runWithScene(scene);

(4)尝试编译并运行程序,如下图所示,成功运行游戏。

2. 修改窗口大小和游戏显示名称

(1)在AppDelegate.cpp中,将变量designResolutionSize改为cocos2d::Size(960, 640),这样就能将窗口大小改为960x640。

static cocos2d::Size designResolutionSize = cocos2d::Size(960, 640);

(2)使用GLViewImpl类的createWithRect函数创建游戏窗口,将第一个参数改为学号,这样就能将游戏显示名称改为学号。

if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
        glview = GLViewImpl::createWithRect("学号", cocos2d::Rect(0, 0, designResolutionSize.width, designResolutionSize.height));
#else
        glview = GLViewImpl::create("学号");
#endif
        director->setOpenGLView(glview);
    }
3. 将学号添加到游戏背景中

(1)打开Resources目录中的地图文件MoveAndControl.tmx,在Tiled中编辑地图,在地图中添加我的学号。

(2)如果使用别的图集,需要添加一个新的图层,位于上方的图层会覆盖下方的图层,不然程序会出现错误,这是因为不同图集会出现相同的砖块类型。

4. 查找和修改游戏的BUG
Bug1:改变窗口大小后,窗口中人物动画的位置和实际位置有偏差。

(1)尝试用多种窗口尺寸进行测试,在1080x720尺寸下发现人物在碰到尖刺前就掉出地图,在360x640尺寸下发现人物能够站在尖刺上。尝试打印人物位置的砖块坐标,发现人物掉出地图的坐标是正确的,所以是人物动画的位置出现问题。

(2)在Config.h文件中,将变量PLAYER_WIDTH的值改为200,这个变量是MapSence.cpp中创建精灵帧函数中调用的参数,该参数决定使用的纹理块矩形的宽度,初始的窗口大小为480x360,PLAYER_WIDTH为100,而我将窗口大小改为960x640,将PLAYER_WIDTH改为200比较合适。

static int PLAYER_WIDTH = 200;		// player img width

// 玩家跑动动画
	Vector<SpriteFrame* >frameVector;
	for(int i=0;i<4;i++)
	{
		auto spriteFrame = SpriteFrame::create(PLAYER_IMG_PATH[i], Rect(0,0,PLAYER_WIDTH,PLAYER_HEIGHT));
		frameVector.pushBack(spriteFrame);
	}

(3) 最后将玩家的初始位置设为(160,40),让玩家出现在窗口中合适的位置。

// 添加玩家
	addPlayer(Vec2(160,40));
Bug2:有的砖块未添加碰撞检测,角色不能站在砖块上。

(1)角色应该能站在树顶和树枝上,而在游戏中,角色如果碰到下图所示的树顶和树叶,将会掉落。

  

(2)在Tiled选中地图中的图块,如下图所示,选中了树枝的图块,对应的砖块类型是148,因为Tiled是以0开始编号,而cocos2d是以1开始编号的,所以在cocos2d中,该图块的编号应该是149。

 (3)在MapScene.cpp中,先获取角色脚的位置在地图中坐标,然后通过该坐标获取对应的砖块坐标,再通过砖块坐标获取其对应的砖块编号playerTiledID,当playerTiledID不为可站立的砖块时,角色下落。树叶图块的编号为99,100和101,树枝图块的编号为149和133,在该if语句中添加对应条件,角色就能站立在这些图块上。

// 不跳动时遇到非地面图块自动下落
	if((int)(player_map_y / map->getTileSize().width) >= 0)
	{
		int playerTiledID = map->getLayer(MAP_BG_LAYER_NAME)->getTileGIDAt(Vec2((int)(player_map_x/map->getTileSize().width),(int)(map->getMapSize().height-1-player_map_y/map->getTileSize().height)));
		if ((m_isJump == false)&&(m_jumpDir == Dir::STOP)&& (playerTiledID != 8) && (playerTiledID != 7) 
			&& (playerTiledID != 151) && (playerTiledID != 170) && (playerTiledID != 171) && (playerTiledID != 172)
			&& (playerTiledID != 99) && (playerTiledID != 100) && (playerTiledID != 101) 
			&& (playerTiledID != 149) && (playerTiledID != 133)) {
				m_isJump = true;
				m_jumpDir = Dir::DOWN;
				m_jumpSpeed = 2;
		}
		//log("%f %d",(player_map_x / map->getTileSize().width), playerTiledID);
	}

(4) 在MapScene.cpp中,有角色跳跃操作的代码,跳跃分为两个阶段,向上跳和向下落,角色向下落时,遇到可以站立的图块,就应当停止下落。在下落过程中要获取角色脚下的地图块的编号,树叶图块的编号为99,100和101,树枝图块的编号为149和133,在if语句中,添加对应的条件,让角色遇到树叶和树枝时停止下落。

// 获取玩家脚下的地图块的编号
				if (player_map_y/map->getTileSize().width >= 0) {

					int tiledID = map->getLayer(MAP_BG_LAYER_NAME)->getTileGIDAt(Vec2((int)(player_map_x/map->getTileSize().width),(int)(map->getMapSize().height - 1 - player_map_y/map->getTileSize().height)));
					if (tiledID == 8 || tiledID == 7 || tiledID == 151 || tiledID == 170 || tiledID == 171 || tiledID == 172 
						|| tiledID == 99 || tiledID == 100 || tiledID == 101 || tiledID == 149 || tiledID == 133) {
						check = true; 
						player->setPositionY((int)(player_screen_y + player->getContentSize().height/2 - 12));
						m_jumpSpeed = PLAYER_JUMP_SPEED;
						m_jumpDir = Dir::STOP;
						m_isJump = false;
						break;
					}
				}
Bug3:下图所示的树的高度太高,角色无法跳到其上面。使用Tiled打开地图文件,将树改成合适的高度,这样角色就能跳上树顶了。

    

Bug4:在显示Gameover字样后,地图还会滚动,过一段时间,会显示Success字样,且与Gameover字样重合。

(1)在MapScene.cpp中,变量player_screen_y是角色的脚部在屏幕中的y位置,当变量小于-80时,代表角色掉出屏幕,此时调用gameOver函数显示Gameover字样。此时让变量m_gameOver变量为true,这个标志用来记录游戏是否结束。调用unscheduleUpdate函数停止计时器,让地图停止滚动,然后用removeChildByTag删除角色的结点。

if (player_screen_y <= -80) {
					gameOver();  // 角色死亡
					m_gameOver = true;
					this->unscheduleUpdate();
					this->removeChildByTag(PLAYER_TAG);
					return;
				}

(2)当地图静止时,角色位置开始移动,当角色位置超过窗口时,显示Success字样。添加一个判断条件,当m_gameOver为false才显示Success字样,m_gameOver为true表示角色已经掉出屏幕,m_gameOver为false表示游戏尚未结束,这样就不会让Success与Gameover字样同时显示了。

// 地图静止后移动角色
		player->setPositionX(player->getPositionX() + m_runSpeed);
		if(player->getPositionX() > visibleWidth && m_gameOver == false)
		{
			player->setPositionX(visibleWidth );
			this->unscheduleUpdate();
			this->removeChildByTag(PLAYER_TAG);
			success(); 
			return;
		}
5. 优化游戏

(1)每次游戏结束后重新启动游戏都很麻烦,要关闭窗口重新运行,我尝试添加一个“重新开始”的按钮,点击后重启游戏。

   在MapScene.h文件中声明restart函数,该函数用来添加一个按钮以重新启动游戏。

	//重新开始游戏
	void restart();

   在restart函数中,使用Button类的Create函数创建一个按钮,按钮使用的图片如下图所示,将按钮的位置设置在Success或者Gameover字样的下方,然后为按钮设置触摸事件,当按下按钮时,导演类调用replaceScene函数启动一个新的场景,即重启游戏。

void MapScene::restart()
{
	//创建按钮
	auto buttonReplay = Button::create("btn-play-normal.png","btn-play-selected.png");
	Size visibleSize = Director::getInstance()->getVisibleSize();
	//设置位置
	buttonReplay->setPosition(Vec2(Vec2(visibleSize.width / 2, visibleSize.height / 2-50)));
	//添加监听
	buttonReplay->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type)
		{
			Director::getInstance()->replaceScene(MapScene::createScene());
			m_gameOver = false;
		});
	this->addChild(buttonReplay, 12);
}

最后在Success函数和gameOver函数中调用restart函数,这样就能在游戏结束时显示重启游戏的按钮了。

void MapScene::success()
{
	auto tips = Label::createWithBMFont(TIPS_FNT_PATH,GAME_SUCCESS);
	Size visibleSize = Director::getInstance()->getVisibleSize();
	tips->setPosition(Vec2(visibleSize.width/2, visibleSize.height/2));
	this->addChild(tips,10);
	m_gameOver = true;
	restart();
}

 游戏中的效果如下图所示,按下play按钮,游戏将会重新开始。

(2)尝试添加暂停/继续游戏的功能。

在MapScene.h文件中声明pause1函数,该函数用来添加一个暂停/继续按钮。

	//暂停
	void pause1();

 在pause1函数中,创建两个按钮,分别表示暂停和继续,按钮的图片如下图所示。

将两个按钮的位置都设置在左上角,然后调用setVisible函数将“继续”按钮设为不可见。

void MapScene::pause1()
{
	Button* buttonPause = Button::create("pause.png", "pause.png");
	Button* buttonContinue= Button::create("continue.png", "continuee.png");
	buttonPause->setScale(0.5);
	buttonContinue->setScale(0.5);
	Size visibleSize = Director::getInstance()->getVisibleSize();
	buttonContinue->setPosition(Vec2(Vec2(30, visibleSize.height - 30)));
	buttonPause->setPosition(Vec2(Vec2(30, visibleSize.height  - 30)));
	//隐藏
	buttonContinue->setVisible(false);
	//buttonPause->setVisible(false);
	this->addChild(buttonPause, 13);
	this->addChild(buttonContinue, 13);

 为“暂停”按钮添加触摸事件,当按下按钮时,先要判断m_gameOver变量是否为false,因为游戏结束时不能使用暂停按钮。

调用pause函数暂停游戏,调用setVisible函数将“暂停”按钮设置为不可见,将“继续”按钮设置为可见,然后使用Label类的createWithBMFont函数创建“Pause”文字,让它显示在屏幕中间,调用setTag函数将该Label的Tag设为15。

//暂停事件
	buttonPause->addTouchEventListener([buttonContinue, buttonPause,this](Ref* sender, Widget::TouchEventType type)
		{
			if (!m_gameOver)
			{
					Director::getInstance()->pause();
					buttonContinue->setVisible(true);
					buttonPause->setVisible(false);
					//添加pause文字
					auto labelPause = Label::createWithBMFont(TIPS_FNT_PATH, "Pause");
					Size visibleSize = Director::getInstance()->getVisibleSize();
					labelPause->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));
					labelPause->setTag(15);
					this->addChild(labelPause, 10);
			}
			
			
		});

 为“继续”按钮添加触摸事件,当按下按钮时,调用setVisible函数将“暂停”按钮设置为可见,将“继续”按钮设置为不可见,通过removeChildByTag函数删除Tag为15的结点,即删除“Pause”文字,然后调用resume函数继续游戏。

//继续事件
	buttonContinue->addTouchEventListener([buttonContinue, buttonPause,this](Ref* sender, Widget::TouchEventType type)
		{
				//删除pause文字
					this->removeChildByTag(15);
					buttonContinue->setVisible(false);
					buttonPause->setVisible(true);
					Director::getInstance()->resume();

		});

}

游戏中的效果如下图所示,按下暂停按钮后游戏暂停,按下继续按钮后游戏继续。

(3)添加游戏难度设置的功能

每一帧都让地图位置向左移动3,当地图静止时,让人物x位置每一帧都加3,这是本游戏滚动地图和让角色移动的方式,因此只要改变这个移动的值,就能改变地图滚动和人物移动的速度。

在MapScene.h文件中声明m_runSpeed变量表示地图滚动和人物移动的速度,声明setting函数用来改变地图滚动和人物移动的速度。

	int m_runSpeed=3;//滚动速度
	//设置
	void setting();

 在MapScene.cpp的update函数中,获取地图的x位置,然后让该位置每帧减m_runSpeed,当地图的最右端显示在窗口右端时,让地图静止,将角色的x位置每帧加m_runSpeed。

	// 滚动背景地图
	int mapWidth = map->getMapSize().width*map->getTileSize().width;
	int visibleWidth = Director::getInstance()->getWinSize().width;

		this->getChildByTag(MAP_TAG)->setPositionX(this->getChildByTag(MAP_TAG)->getPositionX() - m_runSpeed);

	if(map->getPositionX() < -(mapWidth-visibleWidth))  
	{
		map->setPositionX(-(mapWidth-visibleWidth));
		// 地图静止后移动角色
		player->setPositionX(player->getPositionX() + m_runSpeed);
		if(player->getPositionX() > visibleWidth && m_gameOver == false)
		{
			player->setPositionX(visibleWidth );
			this->unscheduleUpdate();
			this->removeChildByTag(PLAYER_TAG);
			success(); 
			return;
		}
	}

  在setting函数中,使用Label类的createWithBMFont函数创建“Difficuty:Easy”文字,然后将其位置设置在右上角。

void MapScene::setting()
{
	auto setting_title=Label::createWithBMFont(TIPS_FNT_PATH, "Difficulty: Easy");
	Size visibleSize = Director::getInstance()->getVisibleSize();
	setting_title->setPosition(Vec2(visibleSize.width-140, visibleSize.height -30));
	setting_title->setScale(0.7);
	int &runSpeed= m_runSpeed;

 创建监听器,监听鼠标事件,使用getCursorX和getCursorY函数获取鼠标点击的位置,如果该位置在创建的文字的附近时,改变游戏难度,如果游戏难度为“Easy”,就将难度改为“Normal”,并将滚动速度m_runSpeed改为6,如果游戏难度为“Normal”,就将难度改为“Hard”,并将滚动速度m_runSpeed设为9,如果游戏难度为“Hard”,就将难度改为“Easy”。

//添加监听器
	auto listener1 = EventListenerMouse::create();

	listener1->onMouseDown = [setting_title, &runSpeed]( Event* event)
	{
		EventMouse *e=(EventMouse*)event;
		if (e->getCursorX() >= setting_title->getPositionX() - 100 && e->getCursorX() <= setting_title->getPositionX() + 100
			&& e->getCursorY() >= setting_title->getPositionY() - 30 && e->getCursorY() <= setting_title->getPositionY() + 30)
		{
				if (setting_title->getString() == "Difficulty: Easy")
				{
					setting_title->setString("Difficulty: Normal");
					runSpeed = 6;
				}
				else if (setting_title->getString() == "Difficulty: Normal")
				{
					setting_title->setString("Difficulty: Hard");
					runSpeed = 9;
				}
				else {
					setting_title->setString("Difficulty: Easy");
					runSpeed = 3;
				}
				return true;
		}
		
	};

	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, setting_title);
	this->addChild(setting_title, 11);
}

 因为跳跃操作也添加了触摸事件,调整难度时点击屏幕会让角色跳一下,所以在触摸事件的函数中,先判断触摸的位置是否在难度设置字样附近,如果是角色就不进行跳跃,否则角色跳跃。

bool MapScene::onTouchBegan(Touch *touch, Event *unused_event)
{
	Size visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 setting_title = Vec2(visibleSize.width - 140, visibleSize.height - 30);
	if (touch->getLocation().x >= setting_title.x - 100 && touch->getLocation().x <= setting_title.x + 100
		&& touch->getLocation().y >= setting_title.y - 30 && touch->getLocation().y <= setting_title.y + 30)
	{

	}
	else {
		if (!this->m_isJump) { 
				m_isJump = true;
				m_jumpDir = Dir::UP; 
			}
			return true;
	}
	
}

 游戏中的效果如下图所示,点击难度会更改难度,地图滚动速度和人物移动速度会改变。

  

四、实验结论或心得体会

在本次实验中,我成功运行了“英雄快跑”游戏,查找和修复了游戏的4个Bug,并添加了一些优化功能,包括重启游戏功能,难度调整功能和暂停/继续游戏功能。通过本次实验,我学习了“英雄快跑”游戏的代码内容,熟悉了cocos2d-x开发环境,了解了cocos2d-x中二维游戏场景绘制方法,以及熟练掌握了瓦片地图编辑器使用方法。

    我初步接触了cocos2d的游戏代码,cocos2d有许多封装好的函数,使用起来非常方便,但这也表示要学习的知识也非常多。我在代码中使用了触摸事件和鼠标事件,复习了相应的知识,并且学习了许多新知识,如按钮,文字等,这让我以后学习cocos2d变得简单许多。

    我在本次实验遇到了诸多困难,最难处理是人物动画效果和实际位置出现偏差这个Bug,我在网上查询了许多动画帧的资料,才修复了这个Bug。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值