How to Create a Sliding/Scrolling Menu in Cocos2d for Android

Screenshot_2014-04-21-01-49-34

In this tutorial, we use the CCScrollView class to create a horizontal scrolling menu . A scrolling menu is  excellent for tastefully laying out the menu items in your games . Items may scroll horizontall or vertically (your call) with that smooth scrolling and snapping effect e.g the one seen in “Cut the rope” when you select the an unlocked game episode. As usual we build on concepts discussed in earlier tutorials, and it’s assumed you have reviewed part 1part 2 and part 3 of building a slider puzzle game using cocos2d for android. If you haven’t, would be great to review them before starting out on this tutorial.

Switch from .jar to cocos2d source files.

Before we proceed, we have to replace the cocos2d-android.jar with the cocos2d source files. This is because the CCScrollview class is not included in the packaged jar file and appears to have been added more recently to the cocos2d for android github  project. The cocos2d source files can be found inside the cocos2d package  you already downloaded from github . Copy them and add to your game src folder. Next, delete the cocos2d-android.jar from your project lib folder. If you don’t delete it, you will encounter compile errors as the same classes exist in the added source files and the cocos2d-android.jar file . Also remember to change your folder view to hierarchical for easy viewing in package explorer (Package Explorer > Package Presentation > Hierarchical , as highlighted in the picture below). Your eclipse folder should look like this.

folder

 

How the ScrollView Class Works

Normally scrolling is needed with the content to be displayed is larger than the available space. Same concept used in browsers 🙂 . For the cocos2d scrollview class two variables are important to implement this smooth scroll effect . First the ViewSize property specifies the size of the window (red highlight in the picture below) and ContentSize is the  size of the entire large content (green in the picture below) to be displayed . ContentSize should be greater than ViewSize for the scroll to occur. CCScrollview supports  Vertical and Horizontal scrolling .

scrolview

Pretty straightforward. Now the next important part would be to write code that detects which of the menu items we have touched . This is done by listening for touch events and determining which of the menu item areas (white boxes in the image below) it falls into. We override the ccTouchesBegan and ccTouchesEnded method of the CClayer class in getting the touch position and use this in info in conjunction with the knowing the current x position of the content window to determine which menu item was touched.

scrolmenu

Lets Write Some Code! – Create the SlidingMenu.java Class and Enable Touches

We create the sliding menu layer as usual and in our default constructor we ensure we enable touches.

public class SlidingMenuLayer extends CCLayer {
private static final int SCROLLVIEW_TAG = 300;
private static CGSize screenSize;
CCScrollView scrollView;
CCBitmapFontAtlas statusLabel ;
private CGPoint startlocation; //keep track of touch starting point
private CGPoint endlocation; ////keep track of touch ending point
float tilescale ;

float generalscalefactor = 0.0f ;

public SlidingMenuLayer () {

this.setIsTouchEnabled(true);
this.setIsKeyEnabled(true) ;
this.isTouchEnabled_ = true ;

// Add our usual background

screenSize = CCDirector.sharedDirector().winSize();
generalscalefactor = CCDirector.sharedDirector().winSize().height / 500 ;

CCSprite background = CCSprite.sprite("background.jpg");
background.setScale(screenSize.width / background.getContentSize().width);
background.setAnchorPoint(CGPoint.ccp(0f,1f)) ;
background.setPosition(CGPoint.ccp(0, screenSize.height));
addChild(background,-5);

}

public static CCScene scene()
{
CCScene scene = CCScene.node();
CCLayer layer = new SlidingMenuLayer();
scene.addChild(layer);
return scene;
}

}

Add Our ScrollView to the Current Layer

We already define our scrollView variable as a field variable in the SlidingMenu class and we add it inside our SlidingMenu default constructor as follows

scrollView = CCScrollView.view(CGSize.zero());
scrollView.bounces = true ; //the bounce effect when a user scrolls to the end
scrollView.setClipToBounds( true) ; //
scrollView.direction =1 ; // for horizontal scrolling.
addChild(scrollView,218, SCROLLVIEW_TAG);

Add Items to the ScrollView

We use a for loop to add a few menu image items (and their text titles) to our scrollView content laying them out horizontally . We also set the contentSize and viewSize variables explained above. This is also added to the default SlidingMenu() default constructor started above

CCSprite tilebox = CCSprite.sprite("picture.png");
// Create our menu ttitles
String[] menutitles = {"number puzzle", "picture puzzle", "camera puzzle" , "gallery puzzle" , "fixed menu" } ;
float newwidth = tilebox.getContentSize().width * 1.5f * generalscalefactor ;
tilescale = 1.5f * generalscalefactor ;

for (int i=0 ; i < menutitles.length ; i++){
//A meu image sprite
tilebox = CCSprite.sprite("picture.png");
tilebox.setAnchorPoint(0.5f, 0.5f);
tilebox.setScale(tilescale);
// Each one is placed with a 30 pixel space from the next ... i *30*generalscalefactor.
tilebox.setPosition(CGPoint.ccp((i*newwidth) + 30*generalscalefactor + i*30*generalscalefactor , screenSize.height/2 - ((tilebox.getContentSize().height *tilescale)/2.0f) ));
//a text title added below each menu image with same x cordinates
CCBitmapFontAtlas nlabel = CCBitmapFontAtlas.bitmapFontAtlas(menutitles[i], "bionic.fnt");
nlabel.setScale(0.8f * generalscalefactor);
nlabel.setPosition(CGPoint.ccp((i*newwidth) + 30*generalscalefactor + i*30*generalscalefactor , tilebox.getPosition().y - ((tilebox.getContentSize().height *tilescale)/2.0f) + 10*generalscalefactor));
scrollView.addChild(tilebox,1,i);
scrollView.addChild(nlabel);
}
// You need to set contentSize to enable scrolling.
// The scrollview is like a sliding window that shows portions of a long list
//View Size is the size of the window and Content size is the size of the entire long list
// For scrolling to occur, the content lenght must be greater than the viewsize
// Thus you must set both content and view size.

scrollView.setViewSize(CGSize.make(screenSize.width, screenSize.height ));
scrollView.setContentSize(CGSize.make(newwidth* (menutitles.length + 1), screenSize.height ));

Handle the Touch Events when a Menu Item is Clicked

We override the ccTouchesEnded method and detect which of the menu items has been clicked. And we launch some event when that occurs using the launchmenu() method.

@Override
public boolean ccTouchesEnded(MotionEvent event)
{
CGRect spritePos ;
endlocation = CCDirector.sharedDirector().convertToGL(CGPoint.ccp(event.getX(), event.getY()));

if (Math.abs(startlocation.x - endlocation.x) < 5){

//Obtain a reference to the scrollview control
CCScrollView tilesNode = (CCScrollView)getChildByTag(SCROLLVIEW_TAG) ;

//Get scrollview children .. its inside container variable
CCClipNode container = (CCClipNode)(tilesNode.getChildren().get(0)) ;

//Iterate through its children ... nlabel and tilebox sprite
// we divide by two here because we have two items added for each menu item
for (int i = 0 ; i < ((container.getChildren().size())/2) ; i++){

// In order to get the position of the item menu that has been touched we note the following
// We need the current position of the touch event ... which is endlocation .. check
// We need to know where it falls into the content
// Since we are scrolling horizontally we add the content current x position to our touch x position
//and construct a rectangle in that area . You can inspect these values using ccMacros.CCLOG.
CCSprite backsprite = (CCSprite)container.getChildByTag(i);
spritePos = CGRect.make(
(backsprite.getPosition().x + container.getPosition().x ) ,
backsprite.getPosition().y ,
backsprite.getContentSize().width*tilescale ,
backsprite.getContentSize().height*tilescale );
if(spritePos.contains(endlocation.x, endlocation.y)){

//Use cccMacro to log information to logcat to to LogCat
ccMacros.CCLOG("Tile " + i + " has been touched : ", "Began touched : " + i);
//Launch the appropriate menu when its toucheds
launchmenu(i);
}
}

}
return true ;
}

Launch Menu Event

Launch some new layer when a menu is clicked.

private void launchmenu( int i){
if (i == 0){
CCScene scene = GameLayer.scene(); //
CCDirector.sharedDirector().runWithScene(scene);
}else if (i == 1){
try {
PictureGameLayer.getBitmapFromAsset("benin.jpg");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
CCScene scene = PictureGameLayer.scene(); //
CCDirector.sharedDirector().runWithScene(scene);
}
else if (i == 2){
PictureGameLayer.getBitMapfromCamera();
}
else if (i == 3){
PictureGameLayer.getBitMapFromGallery();;
}
else if (i == 4){
CCScene scene = MenuLayer.scene(); //
CCDirector.sharedDirector().runWithScene(scene);
}
}

Gimme that updated code

The PuzzleGame code on github has been updated to add the menu item. You may update your clone .

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 and tagged , , , , , , . Bookmark the permalink.
  • Mahbubur Rahman Turzo

    I have a question!
    I have tested this game in several devices but it shows only the black screen in Samsung galaxy S4.
    It happens in some other Full HD devices but run smoothly low resulation devices.

    Why it shows only black screen in Full HD devices ?

    • Vykthur

      Hi Mahbub,

      Very interesting question I must say! I never noticed that before now … possibly because I haven’t really done much testing on Full HD devices. Btw .. what is the resolution of the full HD device you used ? Can you install PuzzBox Africa on your full HD device .. also designed with the same code in these tutorials and let me know if the black screen issue also occurs ?
      https://play.google.com/store/apps/details?id=com.denvycom.puzzbox

      Finally, if you find a solution, please feel free to post it here and I’ll include it in the tutorials.
      -V

      • pankaj

        Hi, with higher resolution devices like s4 and nexus 5, the screen is completely black. I don’t know the solution yet, but seems a major problem. few blogs mentioned it is due to older version of cocos2d, couldn’t figure out how to solve in your example and my game, which I try to build using your code as reference for learning.

  • Jayson Pinzon

    hi Vykthur.
    I want to call a native java class using intents that when I click one of the options display on the slidingmenu. how could I do that ? thanks for the help

  • Amyn Virani

    hey. is it possible for you to convert your above code for cocos2dx c++? it would be of great help to me and to other beginners as well. Thank you.

  • Victor Ghevondyan

    Hi, Vykthur. Great tutorial! But I have a question. :). I have applied your code, but scrollView goes too far. I mean, when I scroll, it passes the items, and shows a lot of blank space. I have tried everything, but nothing help. I will be very graceful to you for any help.

    • Vykthur

      Barev Victor.
      From what you have explained here, I imagine that the size of your scrolling content width (contentSize) is much larger than the total width of content you have put in it.

      I think the problem will be with this line …
      scrollView.setContentSize(CGSize.make(newwidth* (menutitles.length + 1), screenSize.height ));

      You should modify this line to use a better calculation of your content. Verify that you have used the right calculation there .

      scrollView.setContentSize(CGSize.make( somewidth , screenSize.height ));

      Cheers.
      V.