linux编程拼图游戏,cocos2d-x制作拼图游戏

由于本人最近在学习cocos2d-x,一时手痒,写一个游戏练练手,也是对cocos2d-x进一步的巩固,于是敲了两天,就写了这个拼图游戏,还挺有成就感的,:-),先把我的成果展示如下:

a4c26d1e5885305701be709a3d33442f.png

由于cocos2d的跨平台性,此游戏已移植到win、android平台。

言归正传,首先准备工具:

1.一张用来分割的整张大图片;

2.退出按钮的正常和按下两张图片;

3.重新开始按钮的正常和按下两张图片;

4.背景音乐素材,:-)我在这个东西上吃过亏,本来找了一个wma格式的音乐,结果在win上没问题,但linux和android上都放不出声音,后来干脆转换成了mp3,大就大点吧,先忍了。

5.没了,下面开工吧

创建工程就不必说了,就拿cocos2d的HelloCPP工程就行,把HelloWorld类中的init中的除了

if ( !CCLayer::init() )

{

return false;

}

return true;

保留,其余全部删除,我们的代码全部写在这两部分的中间。

首先修改头文件中如下:

#ifndef __HELLOWORLD_SCENE_H__

#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

class HelloWorld : public cocos2d::CCLayer

{

public:

// Here's a difference.

Method 'init' in cocos2d-x returns bool, instead of returning 'id'

in cocos2d-iphone

virtual bool init();

// there's no 'id' in

cpp, so we recommend returning the class instance pointer

static cocos2d::CCScene*

scene();

// a selector

callback

void

menuCloseCallback(CCObject* pSender);

void

menuRefreshCallback(CCObject *pSender);

// implement the "static

node()" method manually

CREATE_FUNC(HelloWorld);

static const int rows =

4; // 定义将图片分成多少行

static const int cols =

5; // 定义将图片分成多少列

static const int

picFadeOutTime = 5; // 图片展示3秒,消失的过程2秒,共5秒

static const int

fragsFadeInTime = 1; // 切片出现所需时间1秒

static const int

shuffleFragsTime = 1; // 乱序排列时间1秒

private:

cocos2d::CCSprite *pic;

// 一整张图片

cocos2d::CCArray *frags;

// 保存切片的数组

cocos2d::CCSprite

*selectedFrag; // 手指触摸处的切片的副本

cocos2d::CCLabelTTF

*winLabel; // 显示YOU WIN!!!标签

cocos2d::CCMenuItemImage

*closeItem; // 退出按钮

cocos2d::CCMenuItemImage

*refreshItem; // 重排按钮

int fragWidth; //

切片的宽

int fragHeight; //

切片的高

cocos2d::CCSize winSize;

// 窗体的尺寸

void prepareFrags(); //

准备切片

void displayPic(); //

显示整张图片,然后消失

void displayFrags(); //

显示所有的切片

void shuffleFrags(); //

打乱切片的顺序

int

currIndexOnPos(cocos2d::CCPoint &point); // 当前点所在的位置序号

bool didGameWin(); //

判断游戏是否完成

cocos2d::CCPoint

posAtIndex(int index); // 某个位置序号的坐标

int fragIndexArray[rows

* cols]; // 切片的顺序数组,当数组中的数字是按从小到大依次排列时,则游戏结束

int selectedFragIndex;

// 拖动的切片序号,与位置的序号不是一个概念

protected:

void onEnter();

void

ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent

*pEvent);

void

ccTouchesMoved(cocos2d::CCSet *pTouches, cocos2d::CCEvent

*pEvent);

void

ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent

*pEvent);

};

首先是init的实现,首先创建一个CCSprite,显示一整张图片:

pic = CCSprite::create(pathToPicture);

要说明一点的是,因为图片与屏幕的分辨率是不一样的,直接贴到屏幕上会导致只又一部分显示出来或者有黑边的情况,本人就遇到这样的问题,一开始还搜索如何缩放CCImage来达到与屏幕适配的效果,果断google了一番,终于知道,最好的方法其实是设置屏幕的分辨率与图片一致,这样的话,对以后换图片比较方便,具体如下:

CCDirector::sharedDirector()->getOpenGLView()->setDesignResolutionSize(pic->getContentSize().width,

pic->getContentSize().height, kResolutionExactFit);

将OpenGLView的分辨率设置为跟图片一样就行,前两个分别石宽和高,第三个是设置分辨率的策略,一共有6种,在这里我直接使用kResolutionExactFit,表示精确匹配,这样图片才能够完全填满屏幕,至于其他的几种,有兴趣的可以自己去研究一番

enum ResolutionPolicy

{

kResolutionExactFit,

kResolutionNoBorder,

kResolutionShowAll,

kResolutionFixedHeight,

kResolutionFixedWidth,

kResolutionUnKnown,

};

然后就是给pic定位了:

winSize = CCDirector::sharedDirector()->getWinSize();

pic->setPosition(ccp(winSize.width / 2, winSize.height /

2));

this->addChild(pic, 0);

把图片放到屏幕中间

至于两个按钮如何创建和定位,这里就省略了,在下确实比较懒一点,当然也是为了博文不要又臭又长。

接下来就要调用prepareFrags()来准备切片了

具体如下:

void HelloWorld::prepareFrags()

{

frags = CCArray::create();

char name[10];

for(int j = 0; j < rows; ++ j) {

for(int i = 0; i < cols; ++ i) {

sprintf(name, "fragd", i + j * cols);

CCSpriteFrame *fragFrame =

CCSpriteFrame::create(picture,

CCRectMake(i * fragWidth, winSize.height - (j + 1) *

fragHeight, fragWidth, fragHeight));

CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFrame(fragFrame,

name);

CCSprite *frag =

CCSprite::createWithSpriteFrame(fragFrame);

frag->setOpacity(0);

frag->setPosition(posAtIndex(i + j * cols));

frags->addObject(frag);

this->addChild(frag, 1);

}

}

frags->retain();

}

切片的方法使用的是static CCSpriteFrame* create(const char* filename,

const CCRect&

rect);根据rect指定的区域不同,切除图片的不同部分,这里要强调的石CCSpriteFrame使用的坐标左上角为原点,x向又增长,y向下增长,这与屏幕的坐标系是不一样的,刚开始我不知道,在这个上面走了点弯路,晕死了,太坑人了,基本把坐标系的问题解决后就没什么问题了。

OK,前期工作已搞定,下面就是见证奇迹的时刻。

在显示当前场景是,cocos2d会调用onEnter虚函数,因此需要重新实现此函数。

因为刚刚学习了cocos2d的延时动作和即时动作,这里全部使用动作动作来实现调用相应的函数

void HelloWorld::onEnter()

{

CCLayer::onEnter();

setTouchEnabled(true);

CCActionInterval *delayForDisplayPic =

CCDelayTime::create(3);

CCActionInterval *delayForDisplayFrags =

CCDelayTime::create(picFadeOutTime);

CCActionInterval *delayForShuffleFrags =

CCDelayTime::create(picFadeOutTime + fragsFadeInTime);

CCActionInstant *displayPicCallback = CCCallFunc::create(this,

callfunc_selector(HelloWorld::displayPic));

CCActionInstant *displayFragsCallback =

CCCallFunc::create(this,

callfunc_selector(HelloWorld::displayFrags));

CCActionInstant *displayShuffleFragsCallback =

CCCallFunc::create(this,

callfunc_selector(HelloWorld::shuffleFrags));

this->runAction(CCSequence::create(delayForDisplayPic,

displayPicCallback, 0));

this->runAction(CCSequence::create(delayForDisplayFrags,

displayFragsCallback, 0));

this->runAction(CCSequence::create(delayForShuffleFrags,

displayShuffleFragsCallback, 0));

CocosDenshion::SimpleAudioEngine::sharedEngine()>playBackgroundMusic("Resources/bgsound.mp3",

true);

}

拿DisplayPic来说,此动作我设计的是先让整张图片显示3秒,然后图片消失,消失时间为两秒,因此首先定义一个CCActionInterval

*delayForDisplayPic = CCDelayTime::create(3);

再定义一个及时动作:

CCActionInstant *displayPicCallback =

CCCallFunc::create(this,callfunc_selector(HelloWorld::displayPic));

this->runAction(CCSequence::create(delayForDisplayPic,

displayPicCallback, 0));

的后,首先前三秒内没有任何动作,3秒钟后再调用即时动作,来调用HelloWorld::displayPic函数,基本也没什么难的。

所有动作的执行顺序是图片显示3秒,用2秒消失,然后所有的切片用1秒的时间来显示出来,最后是用1秒的时间来打乱切片的顺序,因此三个CCDelayTime的时间要算好。

最后在玩游戏的时候为了让人更双一点,可以边玩边听音乐(其实也未必好,我只是觉得好玩:-))

CocosDenshion::SimpleAudioEngine::sharedEngine()>playBackgroundMusic("Resources/bgsound.mp3",

true);

OK,这个函数就这么完成了,下面要来实现三个动作了,首先是整张图片用2秒时间消失:

void HelloWorld::displayPic()

{

CCActionInterval *fadeOut =

CCFadeOut::create(picFadeOutTime - 3);

pic->runAction(fadeOut);

}

然后是所有的切片显示出来:

void HelloWorld::displayFrags()

{

for(unsigned int i = 0; i < frags->count(); ++ i)

{

CCActionInterval *fadeIn =

CCFadeIn::create(fragsFadeInTime);

CCSprite *frag =

dynamic_cast(frags->objectAtIndex(i));

frag->runAction(fadeIn);

}

}

当所有的切片显示出来是,好像是整张图片又显示出来了,这是因为一开始创建切片的时候,所有的切片都放在了正确的位置上,没关系,因为切片显示出来后就会把它们全部大乱:

void HelloWorld::shuffleFrags()

{

shuffle(fragIndexArray, rows * cols);

for(int i = 0; i < rows * cols; ++ i) {

CCSprite *sprite =

dynamic_cast(frags->objectAtIndex(fragIndexArray[i]));

CCPoint dest = posAtIndex(i);

CCActionInterval *moveToDest =

CCMoveTo::create(shuffleFragsTime, dest);

sprite->runAction(moveToDest);

}

}

首先定义一个打乱位置数组的函数,然后每张切片根据位置信息移动到相应的位置上去。

我把shuffle定义成了一个全局函数

void shuffle(int *array, int size)

{

int p1, p2, tmp;

for(int i = 0; i < size * size; ++ i) {

p1 = rand() % size;

p2 = rand() % size;

tmp = array[p1];

array[p1] = array[p2];

array[p2] = tmp;

}

}

有必要说一下我的思路:

屏幕上的位置是按从小到大的顺序排列的,位置数组中则是乱的:

位置:  0  1  2  3  4

5  6  7

8  9

切片:  5  3  6  7  1

2  4  0

8  9

则切片数组中的第5个切片移到第0号位置上,第3个切片一道第1号位置上,第6个切片一道第二号位置上,依次类推,因此还要一个函数计算某个位置序号的所对应的坐标:

CCPoint HelloWorld::posAtIndex(int index)

{

int x = index % cols;

int y = index / cols;

x = x * fragWidth + fragWidth / 2;

y = y * fragHeight + fragHeight / 2;

return ccp(x, y);

}

屏幕上的位置编号为:

16  17  18

19  20

11  12  13

14  15

6  7  8  9  10

0  1  2  3  4  5

又一个部分完成了,下面就是到了互动环节了,:-),说了这么多,总算开始游戏了,好累啊,休息一下。。。。。。

交互的部分主要是三个虚函数的实现:

void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent

*pEvent);  //

手指按下时调用

void HelloWorld::ccTouchesMoved(cocos2d::CCSet *pTouches,

cocos2d::CCEvent *pEvent);  // 手指移动时调用

void HelloWorld::ccTouchesEnded(cocos2d::CCSet *pTouches,

cocos2d::CCEvent *pEvent);  // 手指放开时调用

首先是手指按下,此时要判断当前按下的是拿一张切片,先要得到按下的是哪一个点,需要将这个点转化为gl的坐标:

CCLayer::ccTouchesBegan(pTouches, pEvent);

CCSetIterator iter = pTouches->begin();

CCTouch *touch = dynamic_cast(*iter);

CCPoint touchLocation = touch->getLocationInView();

touchLocation =

CCDirector::sharedDirector()->convertToGL(touchLocation);

再定义一个函数,用来判断当前按下的点属于哪一个位置:

int HelloWorld::currIndexOnPos(CCPoint &point)

{

int x = point.x / fragWidth;

int y = point.y / fragHeight;

return x + y * cols;

}

然后在切片位置数组中找到当前是哪一个切片,这里使用了CCSpriteFrameCache,还记得创建切片的方法吗:

char name[10];

sprintf(name, "fragd", fragIndexArray[spriteIndex]);

selectedFragIndex = spriteIndex;

selectedFrag =

CCSprite::createWithSpriteFrameName(name);

selectedFrag->setScale(0.9);

selectedFrag->setOpacity(200);

this->addChild(selectedFrag, 2);

当手指按下一个切片是,将按下的切片复制一份,但稍微小一点,而且也不是不透明的,这个切片会跟着手指在屏幕上移动,当手指松开是,切片消失,那么下面就涉及到了移动的函数了:

void HelloWorld::ccTouchesMoved(CCSet *pTouches, CCEvent

*pEvent)

{

CCSetIterator iter = pTouches->begin();

CCTouch *touch = dynamic_cast(*iter);

CCPoint touchLocation = touch->getLocationInView();

touchLocation =

CCDirector::sharedDirector()->convertToGL(touchLocation);

if(selectedFrag) {

selectedFrag->setPosition(touchLocation);

}

}

基本不用过多的解释,一看便知。

当手指松开是,最初选择的切片需要与松开处的切片调换位置:

void HelloWorld::ccTouchesEnded(CCSet *pTouches, CCEvent

*pEvent)

{

if(selectedFrag) {

this->removeChild(selectedFrag);

selectedFrag = 0;

CCSetIterator iter = pTouches->begin();

CCTouch *touch = dynamic_cast(*iter);

CCPoint touchLocation = touch->getLocationInView();

touchLocation =

CCDirector::sharedDirector()->convertToGL(touchLocation);

int destFragIndex = currIndexOnPos(touchLocation);

if(destFragIndex >= 0) {

CCSprite *selected =

dynamic_cast(frags->objectAtIndex(fragIndexArray[selectedFragIndex]));

CCSprite *dest =

dynamic_cast(frags->objectAtIndex(fragIndexArray[destFragIndex]));

CCActionInterval *selectedMovetoDest = CCMoveTo::create(0.3,

dest->getPosition());

CCActionInterval *destMovetoSelected = CCMoveTo::create(0.3,

selected->getPosition());

selected->runAction(selectedMovetoDest);

dest->runAction(destMovetoSelected);

int tmp = fragIndexArray[selectedFragIndex];

fragIndexArray[selectedFragIndex] =

fragIndexArray[destFragIndex];

fragIndexArray[destFragIndex] = tmp;

if(didGameWin()) {

winLabel->setVisible(true);

CCBlink *blink = CCBlink::create(1, 5);

winLabel->runAction(blink);

closeItem->setVisible(true);

refreshItem->setVisible(true);

}

}

}

}

调换位置使用了又延时动作,然后将切片在位置数组中的对应关系也调换一下:

每调换一次就要检查当前是否完成了游戏,如果完成了游戏,则显示YOU WIN,并将退出按钮和重玩按钮显示出来:

bool HelloWorld::didGameWin()

{

for(int i = 0; i < cols * rows; ++ i) {

if(fragIndexArray[i] != i)

return false;

}

return true;

}

到这里,一个小小的游戏就初步完成了,还是挺有成就感的,不过玩了两把就不想再玩了,毕竟是拿来练手的,没多大意思,但成就感主要在编写游戏的思考过程中,顺便也确实感受到了cocos2d的强大。

因为我是才开始学cocos2d不久,所以这个游戏肯定还有不足指出,以后随着学习的深入,会不断的完善,不过现在我还是来炫耀一把,把他一直到手机上玩玩,过过瘾:

a4c26d1e5885305701be709a3d33442f.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值