Step by Step Guide on How to build your first Slider Puzzle game in Cocos2d for Android – Part 3

Screenshot_2014-03-15-00-14-41

In Part 1 of this tutorial series, you learned about creating a new Cocos2D for android project, adding sprites, scaling and positioning sprites. In Part 2, you learned more about generating and positioning the tiles for the slider puzzle, and catching/interpreting user’s touch response. In this final part, we will look at final code to check if the player has successfully solved the puzzle .

Checking for a Solved State

Basically, each time the player slides a tile, we inspect the game to check if the arrangement is correct. Our handleWin method is listed as a callback in the code that animates each slide from tutorial Part 2. If not correctly positioned , we proceed with  allowing more slides , else we show a message indicating success. Our method WinCallback is fired if the tiles are in a correctly solved position.

public void handleWin(Object sender){
if(checkCorrect()){
//gameover = true ;
SoundEngine.sharedEngine().playEffect(appcontext, R.raw.tileclick);
WinCallback(sender);

}

Again here, we leverage our knowledge of the physical position of each tile on the screen in ascertainig a correctly solved game. On the first row, the square in the topmost left, we expect tile 1 to be there, and next to it tile 2 and 3. In the second row, we expect tile 4, 5 and 6. Finally on our last row we expect tile 6 and 7 followed by the empty tile space. This is our winning or correct condition. We use a loop to go through each row and each column and if we have a condition where the expected tile is not found at a position , bingo .. we return false. Else the player solved the puzzle.


//This method checks if the puzzle has been correctly solved.
	public boolean checkCorrect(){
		CCNode tileNode = (CCNode) getChildByTag(TILE_NODE_TAG);
		int nodeindex = 1 ;
		boolean result = false;

		for (float j = toppoint ; j > toppoint - (TILE_SQUARE_SIZE * NUM_ROWS); j-= TILE_SQUARE_SIZE){
			for (float i = topleft ; i < (topleft - 5) + (TILE_SQUARE_SIZE * NUM_COLUMNS); i+= TILE_SQUARE_SIZE){
				if(tileNode.getChildByTag(nodeindex).getPosition().x == i && tileNode.getChildByTag(nodeindex).getPosition().y == j ){
					result = true ;
				}else{
					return false ;
				}
				nodeindex++ ;
				if(nodeindex == (NUM_ROWS * NUM_COLUMNS)){
					return result ;
				}
			}

			//rowindex++ ;
		}

		return result ;

Sleek “Pop Up” / Transparent Overlay for Notifications

Next, we provide some notification to the user who has won. This is done using the WinCallback method. I have included some cool animations to make the game little sleek. First a transparent overlay is added to the screen. Its basically a CCColorLayer with stuff some color transparency.

unschedule("updateTimeLabel"); // stop the timer

		//Create a dark semi-transparent layer called pauseOverlay and add over our scene
		CCColorLayer pauseOverlay = CCColorLayer.node(ccColor4B.ccc4(25, 25, 25, 255));
		pauseOverlay.setOpacity(200);
		pauseOverlay.setIsTouchEnabled(true);
		addChild(pauseOverlay,200,PAUSE_OVERLAY_TAG);

		//Show the number of moves in a label called movesLabel
		CCBitmapFontAtlas gamemoves = CCBitmapFontAtlas.bitmapFontAtlas ( CCFormatter.format("%02d", moves ) + " Moves", "bionic.fnt");
		gamemoves.setAnchorPoint(CGPoint.ccp(0,1));
		gamemoves.setScale(generalscalefactor);
		gamemoves.setAnchorPoint(0.5f,1f);

		//Annimate the moves label a little .. scale it
		gamemoves.setPosition(CGPoint.ccp(screenSize.width / 2.0f , screenSize.height/2.0f ));
		pauseOverlay.addChild(gamemoves,300 );
		gamemoves.runAction(CCSequence.actions(
				CCDelayTime.action(0.5f),
				CCScaleBy.action(0.2f, 2.0f)
				));

		//Some instruction for the user to procee
		CCBitmapFontAtlas instructionFontAtlas = CCBitmapFontAtlas.bitmapFontAtlas( "TAP Back button below to continue!" , "bionic.fnt");
		instructionFontAtlas.setPosition(gamemoves.getPosition().x, -instructionFontAtlas.getContentSize().height*0.6f*generalscalefactor) ;
		instructionFontAtlas.setScale(0.6f*generalscalefactor) ;
		pauseOverlay.addChild(instructionFontAtlas,301);

		//animate the instruction label a little ... moveTo
		instructionFontAtlas.runAction(CCSequence.actions(
				CCDelayTime.action(0.5f),
				CCMoveTo.action(0.5f, CGPoint.make(gamemoves.getPosition().x, gamemoves.getPosition().y - 10*generalscalefactor - gamemoves.getContentSize().height *generalscalefactor * 2.0f))
				));

		//Show amount of time in a lable
		CCBitmapFontAtlas gametime = CCBitmapFontAtlas.bitmapFontAtlas (CCFormatter.format("%02d:%02d", (int)(thetime /60) , (int)thetime % 60 ), "bionic.fnt");
		gametime.setAnchorPoint(CGPoint.ccp(0,1));
		gametime.setScale(generalscalefactor);
		gametime.setAnchorPoint(0.5f,1f);
		gametime.setPosition(CGPoint.ccp(screenSize.width / 2.0f , gamemoves.getPosition().y + gamemoves.getContentSize().height*generalscalefactor/2.0f + 10 ));
		pauseOverlay.addChild(gametime,301);

		//reset time to zero

		CCBitmapFontAtlas label = CCBitmapFontAtlas.bitmapFontAtlas("BACK", "bionic.fnt");
		CCMenuItemLabel item5 = CCMenuItemLabel.item(label, this, "backCallback");

		CCMenu resumemenu = CCMenu.menu(item5);
		resumemenu.setPosition(CGPoint.make(label.getContentSize().width, label.getContentSize().width));
		pauseOverlay.addChild(resumemenu,800) ;

Our back button is linked to a final callback method where we remove the overlay and reset the game by reloading the game scene.


schedule("updateTimeLabel"); //restart timer
		moves = 0 ; //Reset moves
		thetime = 0 ; //Reset time

		//Remove the pause layer and reload the scene
		CCColorLayer pauselayer = (CCColorLayer) getChildByTag(PAUSE_OVERLAY_TAG) ;
		pauselayer.runAction(CCMoveTo.action(0.2f, CGPoint.make(screenSize.width / 2.0f, screenSize.height + pauselayer.getContentSize().height*generalscalefactor)));

		pauselayer.removeAllChildren(true);
		pauselayer.removeSelf() ;
		CCDirector.sharedDirector().replaceScene(GameLayer.scene());

Next Steps

There are a still few things you can do to improve the puzzle game

  • Create more rows and columns in your puzzle ?
  • Enable automatic generation of tiles and write code to generate a sequence of solvable tiles.
  • Convert this into a letter puzzle ?
  • Raise the bar and push this up into a picture puzzle ?
  • Manage your audio better . Give users a change to enable or disable sound within the game . Play some background music to give context to you game experience. Link the sound volume levels to the hardware sound buttons on your android device.
  • Implement achievements, a leader-board and connect the app with gaming sdks  such as scooreloop ?

To get more inspiration, check out gidigames, and puzzbox Africa which contains a similar puzzle with a few extra bells and whistles.

Gimme that Code!

Following the tutorial step by step might have been a little daunting, with some errors popping up. You can download the full code (bug free) we have written through a github repo that can be found here . Good luck and Godspeed in your Cocos2D for android journey!

Updates :
New posts on
Handling Sound in Cocos2D
How to build a game menu in Cocos2D
How to build a Slider Puzzle in Cocos2D
  Gidigames has been made open source

About Vykthur

Mobile and Web App Developer and Researcher. Passionate about learning, teaching, and recently - writing.
This entry was posted in Android Tutorials, Cocos2d, Cocos2D for android, Developer Tips, Programming, Tutorials and tagged , , , , . Bookmark the permalink.
  • Turk

    Well maybe its not the place because not about this puzzle game …But i have a problem;
    I just want to add background to 480×800 device..Now when background.setPosition(CGPoint.make((s.width / 3)+80, s.height / 2)); is added i see that pixel quality lowered too much..My image is png 4381×801 which will run at backgorund repeatedly.However i dont know what cocos2d do my image is not just oriented according to screen but somehow lost quality.Why its happening and how can i prevent such a thing?
    Thanks for your answer..

    • Vykthur

      Hi Turk,

      The above is more like a scaling issue. To me, it looks like the image is being applied some kind of scaling factor (greater than 1) which magnifies it and causes the blurry effect.

      To fix this, I would suggest you inspect your scale factor code if you have any. For example,

      background.setScale(1.5f);

      where background is your sprite containing the background image and 1.5f is your scale factor. You can then change this scale factor to 1f or use a more appropiate scale calculation that evaluates to 1 (e..g screesize.width/300 , screesize.width/300 ) .

      Experiment with this a little .. and let me know.

      You can also paste your scaling code and your test device screen size specs .

      V.

  • Vlad

    Hi. Awesome tutorials. Really helpful and well written. Can you give me a few guidelines on turning this into a picture puzzle? I am thinking of a camera and gallery integration. Cheers

    • Vykthur

      Hi Vlad,

      Great question.
      To make this into a picture puzzle .. you could break down you picture into 9 pieces and assign each to a tile. Thats fairly inefficient.

      The better way would be to get an image and assign portions of it to each tile. To break down the steps …

      1.) Create a bitmap from the image of choice

      Bitmap mybitmap = getBitmapFromAsset(mainActivity.pictureresource);

      2.) Create a 2D texture form the image and initialize it using the created bitmap.

      CCTexture2D metexture = new CCTexture2D();
      metexture.initWithImage(mybitmap);

      3.) Next we create a new sprite from this texture which is a subset of the entire image and assign that to the tile. Essentially we break the picture into smaller bits (spriteframes) and assign them to the tile. If you have followed the tutorial thus far, you’ll be familiar with basic math used to calculate the size of each tile and the associated for loops used to position them on screen. We draw on that here again and use these for loops to break a given image into bits.

      We then create a sprite frame like this.

      CCSpriteFrame myframe = CCSpriteFrame.frame(metexture, CGRect.make(i*theblock, j*theblock, theblock, theblock), CGPoint.make(0, 0));

      where i is a a horizontal loop index and j is a vertical loop index

      4.) Assign each sprite frame as the tile background.

      tile = CCSprite.sprite(myframe);

      This might sound a little confusing, but these are the basic steps to implement a picture puzzle.

      I will write up a more detailed tutorial as soon as I can make up some time.

      Hopefully, this has been helpful in the interim.
      Vykthur.

    • Vykthur

      Hello Vlad,

      Like I mentioned earlier, I’v written a more detailed tutorial on how you can improve this code and integrate camera and gallery.

      You can find the post here …
      http://denvycom.com/blog/step-by-step-slider-picture-puzzle-game-using-cocos2d-for-android/

      Hope you find it helpful

  • Matt

    Hi Vykthur,

    I’ve got a bit of time on my hands so I’ve returned to your tutorials. Looking at your menu example, would there be a way of firing a similar method from the onResume method in the main activity? (A lot of games show a pause menu when you resume a game following, for example, a phone call).

    To test, I tried adding the follow line into the CCLayer but it never seems to run:

    @Override
    public void resumeSchedulerAndActions() {
    super.resumeSchedulerAndActions();
    System.out.println(“Game Resumed!”);
    }

    Any ideas?
    Thanks,
    Matt

    • Matt

      Wrote this too soon, just figured it out. My director.runWithScene(scene); wasn’t in onStart() (it was in onCreate() ).

      • Vykthur

        Awesome .. I think you are getting a hang of the “unconventional” anatomy of a cocos2d game. Everything regarding app state must be done on the MainActivity.java … and some variables used to keep state.
        Complex … but thats whats possible right now ..

        -V.

  • rence

    Vykthur I want to have a game result that similar to you that if I win the game, a picture pops up and a description of the image will appear also .. can I have those code ? 😀 cheers

  • Jayson Pinzon

    Hello again 😀 I want get the image from the puzzle itself and if it calls the wincall method I want to show the same image in thepuzzle. thanks for the reply

  • Gaurav

    Awesome tutorial !