Archive

Posts Tagged ‘Cocos2D-iPhone’

Labels, Menus, and Options in Cocos2d-iPhone

June 1st, 2009 beardo No comments

In my previous post I indicated that I was using the Cocoa Touch framework to do my menus and was having that interact with the Cocos2D-iPhone framework that I was doing to implement the actual game. This was a big mistake. After trying to get it all to interact correctly, I realized that I was “doing it wrong”. I ended up with a solution that added a blank UIView to the window, and then had to load up the other views. It was a mess and I still had the problem of the UIView overlay screwing up the touch events in Cocos2D-iPhone.

After decided there had to be a better way, I looked into the menu API in Cocos2D-iPhone. Turns out it is much easier, but to get good looking menu’s I’d need some more art. There are a few ways to make a menu with the API. First you can just use text and specify the menu name with a string. This is okay, but it really should look better for a game. Secondly, and the route I chose, is you can use images to display menu options. First I created some the images in Inkscape:

options_n

options_s

options disabled



The first image is the default display, second the image to display when selected (i.e. touched) and finally when the menu item is disabled. Here’s how to create a simple menu with these images:

/* Utility method to create a menu item assuming a naming convention */
- (MenuItemImage *)createMenuItem:(NSString *)prefix selector:(SEL)selector
{
	NSString *normal = [prefix stringByAppendingString:@"_n.png"];
	NSString *selected = [prefix stringByAppendingString:@"_s.png"];
	NSString *disabled = [prefix stringByAppendingString:@"_d.png"];

	MenuItemImage *result = [MenuItemImage itemFromNormalImage:normal
                                            selectedImage:selected
                                            disabledImage:disabled target:self selector:selector];

	return result;
}

// event handler...
-(void)showOptions:(id)sender
{
  [[Director  sharedDirector] pushScene:[OptionsMenuScene node]];
}

- (id) init {
    self = [super init];
    if (self != nil) {

      MenuItemImage *options = [self createMenuItem:@"options"
                                            selector:@selector(showOptions:)];
      Menu *menu = [Menu menuWithItems:optionsItem, nil];
      [menu alignItemsVertically];
      [self addChild:menu];
    }
}

It’s just that easy. I create a MenuItemImage using the three images, add it to a menu, and then add the menu to my scene. This example also takes advantage of the Menu class’s built-in layout facility. The event handler simply pushes another menu scene onto the scene stack. The documentation suggests that you avoid using the scene stack to save on memory, but I simply use it for the menus and it never gets more than 3 deep.

I also replaced the score display with a simple label:

scoreLabel_ = [Label labelWithString:@"Score: " dimensions:CGSizeMake(150,30)
                    alignment:UITextAlignmentLeft fontName:@"Helvetica"
                    fontSize:20.0];
[scoreLabel_ setRGB:0 :0 :0];
[scoreLabel_ setPosition:ccp(80, 300)];
[layer addChild:scoreLabel_];

// Then later to set the score:
[scoreLabel_ setString:[NSString stringWithFormat:@"Score: %d", score]]; 

The only thing interesting here is that I specify the width of the label, and make it left justified so that it renders how I need it to.

The last thing I did this weekend was to load and save game options. I used the built-in NSUserDefaults class to store and load the preferences. There are tons of examples out there, so I won’t bother repeating it here. I only have 7 more items on the list before I’m ready to release. Mostly it’s just some game mechanics and hooking up some more menus. I’m pumped.

Let there be multi-touch and animation

May 18th, 2009 beardo No comments

Saturday was very productive for me. I re-did the input layer to support multi-touch. Now you can play more than one note at a time on the virtual piano portion of the game. I’m not sure it’s 100% right, and there seems to be occasions where the key gets “stuck” in the down position. I’ll have to debug that before release. I also added labels to the keys for those that don’t know the notes on a piano. I actually implemented the labels with an AtlasSpriteManager and AtlasSprites. The way it works is that you compound multiple sprites in a single texture (one png file for me), and set the sprites to be different regions of the shared texture. This allows it to load the texture once, draw all the sprites, and then move on to the next texture.

The static labels aren’t where AtlasSprites really shine, the best use of them is for animations. I added a pulsing gate that “kills” the notes that make it past the player. I started with a png file containing all the animation frames:
killer

Here’s the code to make the it pulsate:

Texture2D *killerTexture =
  [[TextureMgr sharedTextureMgr] addImage:@"killer.png"];
killerManager =
  [[AtlasSpriteManager alloc] initWithTexture:killerTexture capacity:9];
AtlasSprite *ks =
  [killerManager createSpriteWithRect:CGRectMake(0, 0, 32, 134)];
[ks setPosition:ccp(wallLocation, STAFFSPACING * 2)];
AtlasAnimation *animation =
  [AtlasAnimation animationWithName:@"pulse" delay:0.1f];
for(int i = 0; i < 9;i++)
{
  [animation addFrameWithRect: CGRectMake(i*32.0, 0.0, 32, 134)];
}
for (int i = 8; i > 0; i--)
{
  [animation addFrameWithRect: CGRectMake(i*32.0, 0.0, 32, 134)];
}

[killerManager addChild:ks];
[self addChild:killerManager z:5];
id action =
  [RepeatForever actionWithAction:
    [Animate actionWithAnimation: animation]];

[ks runAction:action];

Basically, you load the texture, create a atlas sprite manager with the texture, create a sprite in that manager, and then build an animation based on the different portions of the texture. One thing to note is that creating a sprite with the AtlasSpriteManager’s createSpriteWithRect message does not add it to the manager. You still need to do that yourself. Also note that you don’t add the AtlasSprite to the layer/scene, you only add the AtlasSpriteManager which handles drawing all of the child sprites. The animation is pretty straight forward, just add the individual frames, set the delay, and you’re off. In this example I add the frames going forward, and then going backwards.

I did start out with only five frames, but it was choppy on the actual device, so I expanded it to nine frames and it’s smooth as butter.

Getting touched

May 13th, 2009 beardo 2 comments

I had an opportunity last night to work on the input component of uSightRead. The main part of the game is a single scene, with layers for the individual parts. There is a main game display layer and an input layer. The input layer consists of keys from a piano that respond to touches. Initially I had thought that each sprite would be able to handle its own touches and then simply pass a message up. This is not how it works. With cocos2d-iphone only a layer can receive touches. I discovered this in a answer to this question on Stack Overflow, which the answer goes into how to do bounds checking.

If you note the highest rated answer shows how to compute where the point touched within your layer, and then determine if the touch was inside a label. This is doing a basic rectangle point intersection. The example code uses a compound if statement, but I simply built a rectangle based on my sprite, and used the CGRectContainsPoint function found. You can read about the built-in geometry functions here. Here’s what my code looks like:

CGRect spriteRect =
  CGRectMake(self.position.x - spriteWidth / 2,
                   self.position.y - spriteHeight / 2,
                   spriteWidth,
                   spriteHeight);

if(CGRectContainsPoint(spriteRect, cLoc)
{
  NSLog(@"Sprite was touched");
  return kEventHandled;
}
else
{
  return kEventIgnored;
}

The one thing to note is the subtraction of half the width and height to compute the postion. This is because the position is actually the center of the Sprite.

Progress, and then starting over

May 8th, 2009 beardo No comments

I had been progressing just fine with the app, it had the notes being draw correctly, a staff with choice of clef symbol, everything anyone could want in a music theory app. Then I got the inkling that I was “doing it wrong”. I based the application on the UIKit model for the iPhone. I knew that OpenGL ES existed, and that it was probably better for making games, but I figures I’d get enough performance the way I was doing it. As I got into coding the game mechanics, I realized that I was making a basic game engine, and really, I’d rather have use one that already exists.

After some research I decided on Cocos2D-iPhone. So, last night I mostly scrapped the existing app and started over. A few hours later I had gotten a basic menu, and some test sprites drawing on the screen. The good news is that the rest of the game should be much easier with someone else’s framework and it should be fairly high performance as it uses OpenGL ES. The bad news is, well, I should have thought of it sooner and I’d be much farther along. Ah well, live and learn.