cocos2dx实例开发之2D横版跑酷

cocos2dx实例开发之2D横版跑酷,第1张

概述从网上下了一点素材资源,外加自己ps一点资源,然后东拼西凑写了一个横版跑酷的小游戏 ps:csdn好不爽,无法传大点的gif,所以只好录了个短的gif,而且压缩之后凑合能看 预览 步骤 1 工程结构 开发环境 win8.1 vs2013 cocos2dx 3.2 代码目录 游戏组成结构 主要有以下几个场景 预加载场景,用于loading等待 主菜单场景 游戏主场景(背景、移动地图、角色、拾取物)

从网上下了一点素材资源,外加自己ps一点资源,然后东拼西凑写了一个横版跑酷的小游戏

ps:csdn好不爽,无法传大点的gif,所以只好录了个短的gif,而且压缩之后凑合能看

预览



步骤 1 工程结构 开发环境 win8.1 vs2013 cocos2dx 3.2 代码目录

游戏组成结构


主要有以下几个场景

预加载场景,用于loading等待 主菜单场景 游戏主场景(背景、移动地图、角色、拾取物) 选项界面 关于界面
2 预加载场景 主要是用于图片资源,音频资源的异步预加载,然后在回调里面跳转到主菜单场景,并且添加了进度条提示
voID LoadingScene::loadingCallBack(Texture2D *texture){	loadednum++;	//此处的预加载帧动画指示用于实验性质	switch(loadednum)	{	case 1:		//预加载帧缓存纹理		SpriteFrameCache::getInstance()->addSpriteFramesWithfile("boy.pList",texture);		loadProgress->setPercentage((float)loadednum/totalNum*100);		break;	case 2:		//预加载帧缓存纹理		SpriteFrameCache::getInstance()->addSpriteFramesWithfile("girl.pList",texture);		loadProgress->setPercentage((float)loadednum/totalNum*100);		break;	default:		break;	}	if(loadednum==totalNum)	{		//预加载帧动画		auto boyAnimation=Animation::create();		boyAnimation->setDelayPerUnit(0.1f);		for(int i=1;i<=12;i++)		{			char str[100]={0};			sprintf(str,"boy%d.png",i);			boyAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByname(str));		}					AnimationCache::getInstance()->addAnimation(boyAnimation,"boyAnimation");		//预加载帧动画		auto girlAnimation=Animation::create();		girlAnimation->setDelayPerUnit(0.2f);		for(int i=1;i<=8;i++)			girlAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByname("girl"+std::to_string(i)+".png"));		AnimationCache::getInstance()->addAnimation(girlAnimation,"girlAnimation");		////预加载音乐和音效		SimpleAudioEngine::getInstance()->preloadBackgroundMusic("spring_music.wav");		SimpleAudioEngine::getInstance()->preloadBackgroundMusic("winter_music.mp3");		SimpleAudioEngine::getInstance()->preloadEffect("jump.wav");		SimpleAudioEngine::getInstance()->preloadEffect("point.mp3");		SimpleAudioEngine::getInstance()->preloadEffect("gameover.wav");				//加载完毕跳转到游戏场景		auto mainMenu=MainMenu::createScene();		TransitionScene *Transition=TransitionFade::create(1.0f,mainMenu);		Director::getInstance()->replaceScene(Transition);	}	}

3 主菜单场景 添加一个菜单和对应的回调函数,分别有开始游戏、选项、关于等菜单项
//添加菜单	auto newGameItem=MenuItemImage::create("newgameA.png","newgameB.png",CC_CALLBACK_1(MainMenu::menuStartCallback,this));	newGameItem->setposition(Point(visibleOrigin.x+visibleSize.wIDth/2,visibleOrigin.y+visibleSize.height/2));		auto optionItem=MenuItemImage::create("option_btn.png","option_btn.png",CC_CALLBACK_1(MainMenu::menuOptionCallback,this));	optionItem->setposition(Point(visibleOrigin.x+visibleSize.wIDth/2,visibleOrigin.y+visibleSize.height/2-60));		auto aboutItem=MenuItemImage::create("aboutA.png","aboutB.png",CC_CALLBACK_1(MainMenu::menuAboutCallback,this));	aboutItem->setposition(Point(visibleOrigin.x+visibleSize.wIDth/2,visibleOrigin.y+visibleSize.height/2-120));	auto menu=Menu::create(newGameItem,optionItem,aboutItem,NulL);	menu->setposition(Point::ZERO);	this->addChild(menu,1);

4 游戏主场景 common头文件 这个头文件里面存储了一些定义的全局变量,比如关卡、男女角色选取等
//游戏关卡枚举enum Level{	SPRING,WINTER};//游戏角色枚举enum PlayerType{	BOY,GIRL};//游戏角色状态enum PlayerState{	RUN,SLIDE,JUMP};//地图元素枚举enum BlockType{	LAND,//砖块	NPC,//怪物	STAR,//星星	TOol,//工具	NONE   //空};//场景间隔基本单位定为80,分辨率800*480,则排满是10*6个格子,便于作碰撞检测const float BLOCK_SIZE=80.0f; const float PICKUP_SIZE=40.0f;const float PLAYER_RADIUS=50.0f;const float GraviTY=-1500.0f;const float PLAYER_SPEED=700.0f;
为了便于计算,将屏幕设计分辨率定位800*480,一个砖块或怪物的大小是80*80,一个拾取物的大小是40*40,这样计算坐标时只需要左整数倍数的偏移就可以了。 游戏里面设置了两种关卡,分别对应了春天和冬天的地图,春天的怪物是绿水灵,冬天的怪物是蓝蘑菇,(玩过冒险岛的都知道),同时还可以选择男、女两种角色。 都是用全局变量进行控制。

游戏角色player类
class Player:public Node{public:	virtual bool init() overrIDe;	CREATE_FUNC(Player);public:	voID run(); //主角奔跑	voID jump(); //主角跳跃	voID slIDe(); //主角滑行	PlayerState playerState; //角色状态private:	Sprite *playerSprite; //奔跑的角色精灵	Sprite *playerSpriteslIDeJump; //滑行和起跳的角色精灵	Animate *playerAnim;	Texture2D *jumpTexture;	Texture2D *slIDeTexture;	};
游戏角色分男女,具有奔跑、跳跃、滑行三种状态 角色还有奔跑动画,同时还要为角色绑定刚体,需要受物理引擎作用
playerSprite=Sprite::create(playerTexturename); //此处必须初始化一张角色纹理,否则后面无法切换纹理		jumpTexture=Sprite::create(playerJumpTexurename)->getTexture(); //创建跳跃纹理	slIDeTexture=Sprite::create(playerSlIDeTexturename)->getTexture(); //创建滑行纹理	playerAnim=Animate::create(playerAnimation);	this->addChild(playerSprite);	auto playerBody=PhysicsBody::createBox(playerSprite->getContentSize()); //这里要用包围盒,如果用圆形的话会导致滚动	playerBody->setDynamic(true);	playerBody->setContactTestBitmask(1);	playerBody->setGravityEnable(true);	playerBody->getShape(0)->setRestitution(0.0f); //设置刚体回d力	this->setPhysicsBody(playerBody);

游戏移动地图类
主要是地图中的砖块和怪物等元素的无限滚屏移动 首先,在初始化里面,手动设置初始地图,放置砖块、怪物、拾取物等等
//手动搭建地图,没有加入即时计算~	//1层	for(int i=0;i<10;i++)	{		if(i!=3&&i!=4&&i!=7&&i!=8)		{			//添加land			auto block=Sprite::create(block_file);			block->setposition(BLOCK_SIZE/2+i*BLOCK_SIZE,BLOCK_SIZE/2+1*BLOCK_SIZE);			this->addChild(block);			block->setTag(LAND); //设置tag			auto blockBody=PhysicsBody::createBox(block->getContentSize());			blockBody->setDynamic(false);			blockBody->setContactTestBitmask(1);			blockBody->getShape(0)->setRestitution(0);			block->setPhysicsBody(blockBody);		}	}	//2层	for(int i=0;i<10;i++)	{		if(i==2||i==5||i==6)		{			//添加怪物			auto npc=Sprite::create(npc_file);			npc->setTag(NPC);			npc->setposition(BLOCK_SIZE/2+i*BLOCK_SIZE,BLOCK_SIZE/2+2*BLOCK_SIZE);			auto npcBody=PhysicsBody::createBox(npc->getContentSize());			npcBody->setDynamic(false);			npcBody->setContactTestBitmask(1);			npcBody->getShape(0)->setRestitution(0);			npc->setPhysicsBody(npcBody);			this->addChild(npc);		}		if(i==3)		{			//添加land			auto block=Sprite::create(block_file);			block->setposition(BLOCK_SIZE/2+i*BLOCK_SIZE,BLOCK_SIZE/2+1*BLOCK_SIZE);			this->addChild(block);			block->setTag(LAND); //设置tag			auto blockBody=PhysicsBody::createBox(block->getContentSize());			blockBody->setDynamic(false);			blockBody->setContactTestBitmask(1);			blockBody->getShape(0)->setRestitution(0);			block->setPhysicsBody(blockBody);		}	}	//3层	for(int i=0;i<10;i++)	{		if(i!=0&&i!=3&&i!=4)		{			//添加星星			auto star1=Sprite::create(star_file);			star1->setTag(STAR);			star1->setposition(PICKUP_SIZE/2+i*BLOCK_SIZE,BLOCK_SIZE/2+3*BLOCK_SIZE);			auto starBody1=PhysicsBody::createBox(star1->getContentSize());			starBody1->setDynamic(false);			starBody1->setContactTestBitmask(1);			starBody1->getShape(0)->setRestitution(0.0f);			star1->setPhysicsBody(starBody1);			this->addChild(star1);			auto star2=Sprite::create(star_file);			star2->setTag(STAR);			star2->setposition(PICKUP_SIZE/2*3+i*BLOCK_SIZE,BLOCK_SIZE/2+3*BLOCK_SIZE);			auto starBody2=PhysicsBody::createBox(star2->getContentSize());			starBody2->setDynamic(false);			starBody2->setContactTestBitmask(1);			starBody2->getShape(0)->setRestitution(0.0f);			star2->setPhysicsBody(starBody2);			this->addChild(star2);		}	}	//4层	for(int i=0;i<10;i++)	{		if(i==3||i==4)		{			//添加land			auto block=Sprite::create(block_file);			block->setposition(BLOCK_SIZE/2+i*BLOCK_SIZE,BLOCK_SIZE/2+4*BLOCK_SIZE);			this->addChild(block);			block->setTag(LAND); //设置tag			auto blockBody=PhysicsBody::createBox(block->getContentSize());			blockBody->setDynamic(false);			blockBody->setContactTestBitmask(1);			blockBody->getShape(0)->setRestitution(0);			block->setPhysicsBody(blockBody);		}		if(i==8)		{			auto star1=Sprite::create(star_file);			star1->setTag(STAR);			star1->setposition(PICKUP_SIZE/2+i*BLOCK_SIZE,BLOCK_SIZE/2+3*BLOCK_SIZE);			auto starBody2=PhysicsBody::createBox(star2->getContentSize());			starBody2->setDynamic(false);			starBody2->setContactTestBitmask(1);			starBody2->getShape(0)->setRestitution(0.0f);			star2->setPhysicsBody(starBody2);			this->addChild(star2);		}		if(i==6)		{			//添加道具			auto tool=Sprite::create(tool_file);			tool->setTag(TOol);			tool->setposition(PICKUP_SIZE/2+i*BLOCK_SIZE,BLOCK_SIZE/2+3*BLOCK_SIZE);			auto toolBody=PhysicsBody::createBox(tool->getContentSize());			toolBody->setDynamic(false);			toolBody->setContactTestBitmask(1);			toolBody->getShape(0)->setRestitution(0.0f);			tool->setPhysicsBody(toolBody);			this->addChild(tool);		}	}
每个物体都绑定了刚体,便于在物理世界做碰撞检测 然后需要启动无限滚屏
//启动调度器,地图滚屏	this->schedule(schedule_selector(GameMap::mapUpdate),0.01f);
目前所采用的策略是党某个node消失在左侧是,重新设置其坐标到有边缘从右往左移动,不过,更好的方法是动态的随机生成地图结点,但是算法较复杂。
voID GameMap::mapUpdate(float dt){	for(auto &node:this->getChildren())	{		node->setpositionX(node->getpositionX()-3.0f);		if(node->getpositionX()<=-node->getContentSize().wIDth/2)			node->setpositionX(node->getpositionX()+BLOCK_SIZE/2+10*BLOCK_SIZE);	}}

游戏主场景类 这里有游戏最重要的逻辑代码。
一开始需要初始化物理引擎
auto *scene=Scene::createWithPhysics();	scene->getPhysicsWorld()->setGravity(Vec2(0,GraviTY));

添加背景滚屏,这是底层背景,移动速度比地图移动速度慢,这样有层次感,有景深感觉,让画面更加动感
backGround1=Sprite::create(backGroundfile);	backGround1->setAnchorPoint(Point::ZERO);	backGround1->setposition(Point::ZERO);	this->addChild(backGround1,0);	backGround2=Sprite::create(backGroundfile);	backGround2->setAnchorPoint(Point::ZERO);	backGround2->setposition(Point::ZERO);	this->addChild(backGround2,0);
voID GameScene::backGroundUpdate(float dt){	backGround1->setpositionX(backGround1->getpositionX()-1.0f);	backGround2->setpositionX(backGround1->getpositionX()+backGround1->getContentSize().wIDth);	if(backGround2->getpositionX()<=0.0f)		backGround1->setpositionX(0.0f);}

添加角色
//添加player	player=Player::create();	player->setposition(Point(visibleOrigin.x+2*BLOCK_SIZE,visibleOrigin.y+4*BLOCK_SIZE));	this->addChild(player,1);

添加地图
//设置地图,默认锚点在左下角	gameMap=GameMap::create();	gameMap->setposition(visibleOrigin.x,visibleOrigin.y);	this->addChild(gameMap,1);

添加两个控制按钮
//添加滑行和跳跃按钮的事件	score=0; //初始化分数	slIDeBtn=Sprite::create("slIDebutton.png");	auto slIDeBtnTexture1=Sprite::create("slIDebutton.png")->getTexture();	auto slIDeBtnTexture2=Sprite::create("slIDebuttonPress.png")->getTexture();	slIDeBtnTextures.pushBack(slIDeBtnTexture1);	slIDeBtnTextures.pushBack(slIDeBtnTexture2);	slIDeBtn->setScale(0.5);	slIDeBtn->setposition(Point(visibleOrigin.x+100,visibleOrigin.y+50));	this->addChild(slIDeBtn,2);	jumpBtn=Sprite::create("jumpbutton.png");	auto jumpBtnTexture1=Sprite::create("jumpbutton.png")->getTexture();	auto jumpBtnTexture2=Sprite::create("jumpbuttonPress.png")->getTexture();	jumpBtnTextures.pushBack(jumpBtnTexture1);	jumpBtnTextures.pushBack(jumpBtnTexture2);	jumpBtn->setScale(0.5);	jumpBtn->setposition(Point(visibleOrigin.x+visibleSize.wIDth-100,visibleOrigin.y+50));	this->addChild(jumpBtn,2);

触摸检测,用于控制按钮 滑行时动态更换纹理 跳跃时给角色一个向上的速度,暂停动画,更换纹理 要注意的是,一开始只能二段跳,接了道具之后可以三段跳
bool GameScene::ontouchBegan(touch *touch,Event *event){	auto touchPoint=touch->getLocation();	//检测是否触摸在按钮区域	if(slIDeBtn->getBoundingBox().containsPoint(touchPoint))	{		slIDeBtn->setTexture(slIDeBtnTextures.at(1));		player->slIDe();	}	if(jumpTimes<jumpTotal&&jumpBtn->getBoundingBox().containsPoint(touchPoint))	{		if(isSound)			SimpleAudioEngine::getInstance()->playEffect("jump.wav"); //播放跳跃音效		jumpBtn->setTexture(jumpBtnTextures.at(1));		player->jump();		jumpTimes++;	}			return true;}voID GameScene::ontouchended(touch *touch,Event *event){	auto touchPoint=touch->getLocation();	//判断是释放时是否在按钮区域	if(slIDeBtn->getBoundingBox().containsPoint(touchPoint))	{		slIDeBtn->setTexture(slIDeBtnTextures.at(0));		player->run();	}	if(jumpBtn->getBoundingBox().containsPoint(touchPoint))	{		jumpBtn->setTexture(jumpBtnTextures.at(0));	}}

碰撞检测
bool GameScene::onContactBegin(const PhysicsContact &contact){	jumpTimes=0; //落回地面就将已跳跃次数清零	//获得被碰撞物体,getShapeA  getShapeB要尝试一下	auto target=contact.getShapeA()->getbody()->getNode();	if(target->getTag()==STAR)	{		//碰到星星就涨分,星星消失		gameMap->moveNode(target);		addscore(100); //拾取星星得100	}	else if(target->getTag()==NPC&&target->getpositionY()+target->getContentSize().height/2<player->getpositionY()) //此处要用else if,只有当角色在怪物头上才能踩中	{		gameMap->moveNode(target);		addscore(150); //踩怪得150	}	else if(target->getTag()==NPC&&target->getpositionY()+target->getContentSize().height/2>=player->getpositionY()) //如果角色正面遇到怪物就挂了		gameOver();	else if(target->getTag()==TOol)	{		jumpTotal=3;		auto toolicon=Sprite::create("accelerate_state.png");		toolicon->setposition(Point(visibleOrigin.x+180,visibleOrigin.y+50));		this->addChild(toolicon,2);		target->removeFromParent(); //道具只出现一次,从parent里面删除		addscore(300); //道具300	}	//落回地面恢复跑步状态	if(player->playerState==JUMP)		player->run();	return true;}

碰撞检测的逻辑主要是 判断碰撞的是砖块则继续奔跑 判断碰撞的是怪物,并且是踩在投上,则消除怪物,得分 判断碰撞的是拾取物,则消除拾取物,得分,如果是道具的话则获得三段跳的能力 判断碰撞是正面遇到怪物,则游戏结束
游戏结束的逻辑 正面遇到怪物 落到屏幕底下
voID GameScene::gameOver(){	//游戏结束停止所有的调度器	gameMap->unscheduleAllSelectors();	this->unscheduleAllSelectors();	//播放游戏结束声音	if(isSound)		SimpleAudioEngine::getInstance()->playEffect("gameover.wav");	//游戏结束出现菜单	visibleSize=Director::getInstance()->getVisibleSize();	visibleOrigin=Director::getInstance()->getVisibleOrigin();		auto gameOverPanel=Node::create();	auto overLabel=Sprite::create("gameover.png");	overLabel->setposition(visibleOrigin.x+visibleSize.wIDth/2,visibleOrigin.y+visibleSize.height/2+100);	gameOverPanel->addChild(overLabel);	auto backItem=MenuItemImage::create("back_to_menu.png","back_to_menu_press.png",[](Object *sender)	{		//用lambda表达式作为菜单函数回调		auto mainMenu=MainMenu::createScene();		TransitionScene *Transition=TransitionFade::create(1.0f,mainMenu);		Director::getInstance()->replaceScene(Transition);	});	auto backMenu=Menu::createWithItem(backItem);	backMenu->setposition(visibleOrigin.x+visibleSize.wIDth/2,visibleOrigin.y+visibleSize.height/2-50);	gameOverPanel->addChild(backMenu);	gameOverPanel->setpositionY(visibleOrigin.y+visibleSize.height);	this->addChild(gameOverPanel,3);	//滑入gameover logo,注意node的锚点在左下角	gameOverPanel->runAction(Moveto::create(0.5f,Vec2(visibleOrigin.x,visibleOrigin.y)));}

其他逻辑 都放到update这个函数里面进行
角色被阻挡后赶到原来的位置
//当角色被挡道之后跟上原来的位置	float step=2.0f;	if(player->getpositionX()<2*BLOCK_SIZE)		player->setpositionX(player->getpositionX()+step);	if(player->getpositionX()>2*BLOCK_SIZE)		player->setpositionX(player->getpositionX()-step);

加分
voID GameScene::addscore(float number){	if(isSound)		SimpleAudioEngine::getInstance()->playEffect("point.mp3"); //播放得分音效	score+=number;	scoreLabel->setString(String::createWithFormat("score: %d",score)->_string);}

5 设置场景 设置游戏关卡、游戏角色和声音,这里懒得加xml配置文件存储。
//春天	auto springItem=MenuItemImage::create("spring_icon.png","spring_icon_press.png",[](Object *sender)	{		//修改关卡		level=SPRING;		textLevel->setString("level: Spring");	});		springItem->setposition(visibleOrigin.x+visibleSize.wIDth/4+35,visibleOrigin.y+visibleSize.height/4*3);	//冬天	auto winterItem=MenuItemImage::create("winter_icon.png","winter_icon_press.png",[](Object *sender)	{		//修改关卡		level=WINTER;		textLevel->setString("level: Winter");	});	winterItem->setposition(visibleOrigin.x+visibleSize.wIDth/4*3-35,visibleOrigin.y+visibleSize.height/4*3);	//男生	auto boyItem=MenuItemImage::create("boy1.png","boy_jump.png",[](Object *sender)	{		//修改角色		playerType=BOY;		textPlayer->setString("player: Boy");	});	boyItem->setposition(visibleOrigin.x+visibleSize.wIDth/2,visibleOrigin.y+visibleSize.height/2-100);	//女生	auto girlitem=MenuItemImage::create("girl1.png","girl_jump.png",[](Object *sender)	{		//修改角色		playerType=GIRL;		textPlayer->setString("player: Girl");	});	girlitem->setposition(visibleOrigin.x+visibleSize.wIDth/2+200,visibleOrigin.y+visibleSize.height/2-100);	//声音开关	auto soundItem=MenuItemImage::create("sound_on.png","sound_off.png",[](Object *sender)	{		//修改声音		isSound=!isSound;		textSound->setString(isSound?"sound: On":"sound: Off");	});	soundItem->setposition(visibleOrigin.x+soundItem->getContentSize().wIDth/2,visibleOrigin.y+soundItem->getContentSize().height/2);	auto menu=Menu::create(backItem,springItem,winterItem,boyItem,girlitem,soundItem,NulL);	menu->setposition(Point::ZERO);	this->addChild(menu);
通过全局变量与其他场景交互

6 关于场景
//添加动态背景	auto backGround=Sprite::create("about.jpg");	backGround->setposition(visibleOrigin.x+visibleSize.wIDth/2,visibleOrigin.y+visibleSize.height/2);	auto repeatanim=MoveBy::create(3.0f,Vec2(0,50));	backGround->runAction(RepeatForever::create(Sequence::create(repeatanim,repeatanim->reverse(),NulL)));	this->addChild(backGround,0);	//添加关于告示	auto aboutLabel=Sprite::create("aboutLabel.png");	aboutLabel->setposition(visibleOrigin.x+visibleSize.wIDth/2,visibleOrigin.y+visibleSize.height/2);	this->addChild(aboutLabel,1);	auto text=LabelBMFont::create("author:tashaxing\nE-mail:tashaxing@163.com\n\nWish you have fun!","bitmapFontChinese.fnt");	text->setposition(visibleOrigin.x+visibleSize.wIDth/2,visibleOrigin.y+visibleSize.height/2);	this->addChild(text,2);
一些简介

效果图















整个demo也做了蛮久的,很多图片和图标,配色都是我自己ps的,算是策划、美术和程序一人承担了。
源码下载 csdn: 跑酷游戏 github: parkour 总结

以上是内存溢出为你收集整理的cocos2dx实例开发之2D横版跑酷全部内容,希望文章能够帮你解决cocos2dx实例开发之2D横版跑酷所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1070801.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-26
下一篇2022-05-26

发表评论

登录后才能评论

评论列表(0条)

    保存