Linking HUDLayer to GameplayLayer properly - ios

I'm trying to pass the touch location from GameplayLayer to HUDLayer to see if user presses control buttons.
HudLayer.mm
-(void) handleTouchAtLocation:(CGPoint) location {
NSLog(#"Touch passed to HUD");
}
Gameplay.mm
enum {
kHudLayer = 2;
};
+(CCScene *) scene {
CCScene *scene = [CCScene node];
HudLayer *hud = [HudLayer node];
[scene addChild:hud z:kHudLayer];
GameplayLayer *layer = [GameplayLayer node];
[scene addChild:layer];
return scene;
}
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
[self handTouchAtPoint:(location)];
}
}
-(void) handleTouchAtPoint:(CGPoint)location {
NSLog(#"Touch At Point");
HudLayer *h1 = (HudLayer *)[self.parent getChildWithTag:kHudLayer];
[h1 handleTouchAtLocation:location];
}
HudLayer.h is imported in GameplayLayer.h.
I'm getting the "Touch At Point" log but it's not going through to HUD layer for some reason..

The only explanation is that self.parent has no child with the tag kHudLayer. If you set a breakpoint in handleTouchAtPoint you'll notice h1 being nil after the getChildWithTag line did execute.

Related

Sprites and Bounding Box issues

I am making an IOs game for an assignment using cocos2D. So far I am just trying to get all of the menus done, but I am having some issues with bounding box when setting up some of my buttons.
The game starts with the cocos2D logoscreen, then transits into a splash screen for my game, then into the main menu. In the game menu I have 2 buttons: Begin, and Options. Both buttons are working perfectly fine, each makinga transicion to the correct scene.
I am currently working on the options menu scene, where I have a button to take care of volume, a butto to set game difficulty, and a button to go back to main menu. I have been trying to make the back button work, but is not triggering when touched, I placed an NSLog for debugging, and the touches are being detected, just the if statement that checks whether the sprite was touched oris never true. I am puzzled by this, since I am using the same method I used in my main menu (which works). I searched for similar issues before posting, but everything I tried, like altering the parent coordinate system and/or the sprites boundingbox.origin, haven't worked at all.
This is the code for my Options Scene:
#import "OptionsMenu.h"
#implementation OptionsMenu
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
OptionsMenu *layer = [OptionsMenu node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id) init
{
if( (self=[super init]) ) {
self.touchEnabled = YES;
self.accelerometerEnabled = YES;
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *background = [CCSprite spriteWithFile:#"Menu0.png"];
background.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:background z:0];
CCSprite *volumeBar = [CCSprite spriteWithFile:#"VolumeBar.png"];
volumeBar.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:volumeBar z:1];
CCSprite *volumeButton = [CCSprite spriteWithFile:#"VolumeButton.png"];
volumeButton.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:volumeButton z:1];
CCSprite *difficultyButton = [CCSprite spriteWithFile:#"EasyButton.png"];
difficultyButton.position = ccp(winSize.width/2, winSize.height/2-55);
[self addChild:difficultyButton z:1];
CCSprite *backButton = [CCSprite spriteWithFile:#"BackButton.png"];
backButton.position = ccp(winSize.width/12, winSize.height/8);
[self addChild:backButton z:1];
[self scheduleUpdate];
}
return self;
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//Add a new body/atlas sprite at the touched location
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
CCActionInterval *actionInterval = [CCActionInterval actionWithDuration:0.1];
if (CGRectContainsPoint([self.backButton boundingBox], location)) {
[self.backButton setTexture:[[CCTextureCache sharedTextureCache] addImage:#"BackButton.png"]];
id actionCallFunc = [CCCallFunc actionWithTarget:self selector:#selector(goToMainMenu:)];
[self.background runAction: [CCSequence actions: actionInterval, actionCallFunc, nil]];
}
else if (CGRectContainsPoint([self.difficultyButton boundingBox], location)) {
[self.volumeButton setTexture:[[CCTextureCache sharedTextureCache] addImage:#"VolumeButton.png"]];
}
}
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//Add a new body/atlas sprite at the touched location
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
NSLog(#"Touch");
//missileButton* missileButton;
location = [[CCDirector sharedDirector] convertToGL: location];
if (CGRectContainsPoint([self.backButton boundingBox], location))
{
[self.backButton setTexture:[[CCTextureCache sharedTextureCache]addImage:#"BackButtonPressed.png"]];
NSLog(#"BackButtonTouched");
}
else if (CGRectContainsPoint([self.volumeButton boundingBox], location))
[self.volumeButton setTexture:[[CCTextureCache sharedTextureCache]addImage:#"VolumeButtonPressed.png"]];
}
}
-(void) goToMainMenu:(id) sender
{
[[CCDirector sharedDirector] replaceScene: [CCTransitionSlideInR transitionWithDuration:2 scene:[MainMenu node]]];
}
#end
I would appreciate any help in solving this issue.
Your self.backButton is never defined. Change your init function to the following (I'm assuming all of your CCSprite defines are actually properties:
-(id) init
{
if( (self=[super init]) ) {
self.touchEnabled = YES;
self.accelerometerEnabled = YES;
CGSize winSize = [[CCDirector sharedDirector] winSize];
_background = [CCSprite spriteWithFile:#"Menu0.png"];
_background.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:_background z:0];
_volumeBar = [CCSprite spriteWithFile:#"VolumeBar.png"];
_volumeBar.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:_volumeBar z:1];
_volumeButton = [CCSprite spriteWithFile:#"VolumeButton.png"];
_volumeButton.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:_volumeButton z:1];
_difficultyButton = [CCSprite spriteWithFile:#"EasyButton.png"];
_difficultyButton.position = ccp(winSize.width/2, winSize.height/2-55);
[self addChild:_difficultyButton z:1];
_backButton = [CCSprite spriteWithFile:#"BackButton.png"];
_backButton.position = ccp(winSize.width/12, winSize.height/8);
[self addChild:_backButton z:1];
[self scheduleUpdate];
}
return self;
}
Another approach would be to use CCMenu and CCMenuItemSprite for all of your buttons. These classes make simple buttons for menus much easier to implement. Most/all of the touch handling is done for you and there are variants that use selectors or blocks for handling the button press.
This would save you a lot of code, and trouble.

Moving sprites in cocos2d

I have 6 sprites that I want to move around:
-(id) init
{
if( (self=[super init]) )
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite * backGround = [CCSprite spriteWithFile:#"background_ipad.png"];
backGround.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:backGround z:0];
cloth1 = [CCSprite spriteWithFile:#"clothe_1.png"];
cloth1.position = ccp(-200, -15);
[self addChild:cloth1 z:1];
cloth2 = [CCSprite spriteWithFile:#"clothe_2.png"];
cloth2.position = ccp(130, 225);
[self addChild:cloth2 z:2];
cloth3 = [CCSprite spriteWithFile:#"clothe_3.png"];
cloth3.position = ccp(365, 225);
[self addChild:cloth3 z:3];
cloth4 = [CCSprite spriteWithFile:#"clothe_4.png"];
cloth4.position = ccp(-110, -15);
[self addChild:cloth4 z:4];
cloth5 = [CCSprite spriteWithFile:#"clothe_5.png"];
cloth5.position = ccp(130, -20);
[self addChild:cloth5 z:5];
cloth6 = [CCSprite spriteWithFile:#"clothe_6.png"];
cloth6.position = ccp(365, -15);
[self addChild:cloth6 z:6];
}
return self;
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
and this method to move:
-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
//Sprite follows the finger movement.
[cloth1 setPosition:location];
}
and the thing is that, I want to add more sprites there to move the sprite. I triend adding more sprites in the //follow the finger movement but then all sprites follow the finger movement. I want to move one single sprite. For example: when touching cloth1, move cloth 1; when touching cloth2, move cloth 2; but not both at the same time.
Can someone tell me how to do this?
#interface YourClass : NSObject
{
NSMutableArray *mSpriteArray;
CCSprite *mSpriteOnHand;
}
//In implementation:
-(id) init
{
.. //your old code
..
[mSpriteArray addObject: cloth1];
[mSpriteArray addObject: cloth2];
[mSpriteArray addObject: cloth3];
[mSpriteArray addObject: cloth4];
[mSpriteArray addObject: cloth5];
[mSpriteArray addObject: cloth6];
}
-(void)onEnter
{
[super onEnter];
self.touchEnabled = YES; // self.isTouchEnabled = YES;
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
mSpriteOnHand = nil;
for(CCSprite *cloth in mSpriteArray)
{
if(CGRectContainsPoint([cloth boundingBox], location))
{
mSpriteOnHand = cloth;
break;
}
}
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
if(mSpriteOnHand)
mSpriteOnHand.position = location;
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
mSpriteOnHand = nil;
}
-(void)onExit
{
[mSpriteArray release];
mSpriteArray = nil;
[super onExit];
}
return statement always terminates function and returns control to the calling function. You wrote:
return self;
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
[[CCTouchDispatcher sharedDispatcher]... line will never get executed.
Is your class a subclass of CCLayer? If you set layer's isTouchEnabled property to YES, it will add this layer as standard (non-targeted) touch delegate.
If you must use targeted touch in your layer, you should return YES in -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event if you are claiming this touch, and NO if you aren't. Updates of a claimed touch are sent only to the delegate which claimed it.
To determine which sprite is touched: make an instance variable to store "selected" sprite, in touchBegan method check which sprite's bounding box contains touch location and store this sprite in instance variable (also, looks like you want to claim a touch only if it's touching a sprite).
[cloth1 setPosition:location];
-- your sprite will "jump" to touch position. Usually it doesn't look nice. I would get touch's locationInView: and previousLocationInView:, convert them to GL, get the difference and change sprite's position by that difference.

Cocos2d Drawing App Making Lines

I am making a very simple drawing application. I got the lines to draw using the ccTouchMoved event. I am putting all the touch moved moved points into an array and then using a for loop to draw a line between all the points. Now, I do not want to join the points when I have lifted my finger and started new line drawing. I got that part working too but now whenever I begin a new drawing the whole screen flicker.
//
// HelloWorldLayer.mm
// DrawPuppets
//
// Created by Mohammad Azam on 12/11/12.
// Copyright __MyCompanyName__ 2012. All rights reserved.
//
// Import the interfaces
#import "DrawPuppetLayer.h"
#import "AppDelegate.h"
#import "PhysicsSprite.h"
enum {
kTagParentNode = 1,
};
#pragma mark - HelloWorldLayer
#interface DrawPuppetLayer()
-(void) initPhysics;
-(void) addNewSpriteAtPosition:(CGPoint)p;
-(void) createMenu;
#end
#implementation DrawPuppetLayer
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
DrawPuppetLayer *layer = [DrawPuppetLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id) init
{
if( (self=[super init])) {
// enable events
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
index = -1;
canvas = [[NSMutableArray alloc] init];
// init physics
[self initPhysics];
[self scheduleUpdate];
}
return self;
}
-(void) draw
{
if([lineDrawing.points count] > 1)
{
for(int i = 0; i<([canvas count]) ;i++)
{
LineDrawing *drawing = (LineDrawing *) [canvas objectAtIndex:i];
for(int j=0;j<[drawing.points count] - 1;j++)
{
LinePoint *firstPoint = (LinePoint *) drawing.points[j];
LinePoint *secondPoint = (LinePoint *) drawing.points[j + 1];
CGPoint point1 = [[CCDirector sharedDirector] convertToGL:CGPointMake(firstPoint.x, firstPoint.y)];
CGPoint point2 = [[CCDirector sharedDirector] convertToGL:CGPointMake(secondPoint.x, secondPoint.y)];
ccDrawLine(point1, point2);
}
}
}
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
lineDrawing = [[LineDrawing alloc] init];
lineDrawing.points = [[NSMutableArray alloc] init];
[canvas addObject:lineDrawing];
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView: [touch view]];
LinePoint *linePoint = [[LinePoint alloc] init];
linePoint.x = point.x;
linePoint.y = point.y;
[lineDrawing.points addObject:linePoint];
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//Add a new body/atlas sprite at the touched location
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
}
}
#end
Can anyone spot my mistake?
Try visiting it all down to a texture, there's going to be a time when your points array gets too large to be drawn nicely
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// get the node space location of our touch
CGPoint location = [self getNodeSpaceTouchLocationFromUIEvent:event];
// draw with our current location and a random colour
[_canvas begin]; // our rendertexture instance
// do your drawing here
[_pen drawPenWithPosition:location andColour:_colour];
// end capturing the current pen state
[_canvas end];
}
Here's a simple example project written for iOSDevUK 2012 it uses GL_POINTS in Cocos2d v1 and is based on the approach we took when developing SketchShare

Undisplayed view crash in cocos2D

There are no errors or warning but this crashes when I try and switch to the view:
#import "Level1.h"
#import "LevelSelect.h"
#import "GameOver.h"
enum {
kTagPlayer, kTagComputer
};
#implementation Level1
+(id) scene {
CCScene *scene = [CCScene node];
Level1 *layer = [Level1 node];
[scene addChild: layer];
return scene;
}
-(id) init {
if( (self=[super init] )) {
CCSprite *player = [CCSprite spriteWithFile:#"Icon_Small_50.png"];
player.position = ccp(60, 60);
[self addChild:player z:2 tag:kTagPlayer];
CCSprite *computer = [CCSprite spriteWithFile:#"Icon_Small.png"];
computer.position = ccp(440, 160);
[self addChild:computer z:1 tag:kTagComputer];
[computer runAction:[CCMoveTo actionWithDuration:3.0
position:ccp(player.position.x, player.position.y)]];
self.isTouchEnabled = YES;
[self schedule:#selector(SpritesDidColide)
interval:.01];
}
return self;
}
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *myTouch = [touches anyObject];
CGPoint point = [myTouch locationInView:[myTouch view]];
point = [[CCDirector sharedDirector] convertToGL:point];
CCNode *player = [self getChildByTag:kTagPlayer];
[player setPosition:point];
CCNode *computer = [self getChildByTag:kTagComputer];
[computer runAction:[CCMoveTo actionWithDuration:3.0
position:ccp(player.position.x, player.position.y)]];
}
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *myTouch = [touches anyObject];
CGPoint point = [myTouch locationInView:[myTouch view]];
point = [[CCDirector sharedDirector] convertToGL:point];
CCNode *player = [self getChildByTag:kTagPlayer];
[player setPosition:point];
CCNode *computer = [self getChildByTag:kTagComputer];
[computer runAction:[CCMoveTo actionWithDuration:3.0
position:ccp(player.position.x, player.position.y)]];
}
-(void) SpritesDidCollide {
CCNode *player = [self getChildByTag:kTagPlayer];
CCNode *computer = [self getChildByTag:kTagComputer];
float xDif = computer.position.x - player.position.x;
float yDif = computer.position.y - player.position.y;
float distance = sqrt(xDif * xDif + yDif * yDif);
if (distance < 45) {
[self unschedule:#selector(SpritesDidCollide)];
[[CCDirector sharedDirector] replaceScene:[CCTransitionScene transitionWithDuration:1 scene:[GameOver node]]];
}
}
#end
And here is what displays in the Console:
* Assertion failure in -[CCTimer initWithTarget:selector:interval:], /Users/GLaDOS/Documents/Xcode 4 Projects/2DPlatformer/2DPlatformer/libs/cocos2d/CCScheduler.m:110
2012-02-17 19:13:07.460 2DPlatformer[709:707] Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt'
** First throw call stack:
(0x37f1d8bf 0x31a031e5 0x37f1d7b9 0x31e1c3b3 0x46565 0x4843f 0x3402d 0x948d3 0x32249 0x93a23 0x37f20814 0x37e7b7e1 0x2de31 0x2bdc9 0x37e77435 0x7087f 0x71019 0x727ff 0x352ef50f 0x352eef01 0x352d54ed 0x352d4d2d 0x379f2df3 0x37ef1553 0x37ef14f5 0x37ef0343 0x37e734dd 0x37e733a5 0x379f1fcd 0x35303743 0x912c7 0x2300)
terminate called throwing an exceptionkill
Current language: auto; currently objective-c
quit
Program ended with exit code: 0
I believe the schedule method will trigger a call back to a selector of the form:
-(void) SpritesDidCollide:(ccTime) dt{
// do your thing here.
}
and change the selector to
[self schedule:#selector(SpritesDidCollide:) interval:.01];

Touch location CCLayer

*Working Code Now*
OK, I thought that this would be easy to get working but it turns out to not work like I expected.
I am trying to get the Touch location from a CCLayer that can be moved or zoomed, not the location on the screen itself? here is how I thought that it would work but it crashes?
Interface
#import "cocos2d.h"
#interface TestTouch : CCLayer {
CCLayerColor *layer;
}
+(CCScene *) scene;
#end
Implementation
#import "TestTouch.h"
#implementation TestTouch
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
TestTouch *layer = [TestTouch node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
- (id)init
{
self = [super init];
if (self) {
CGSize winsize = [[CCDirector sharedDirector]winSize];
CCLayerColor *layer = [CCLayerColor layerWithColor:ccc4(255, 255, 255, 255)];
// layer.scale = 0.7f;
layer.contentSize = CGSizeMake(640, 960);
layer.position = CGPointMake(winsize.width/2, winsize.height/2);
layer.isRelativeAnchorPoint = YES;
[self addChild:layer z:0];
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:layer priority:0 swallowsTouches:YES];
}
return self;
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchStart = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
touchStart = [layer convertToNodeSpace:touchStart];
NSLog(#"Touch:%f,%f",touchStart.x, touchStart.y);
return YES;
}
#end
If I change this line to include "self":
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
It will obviously work but then I get the location relevant to the screen and not the layer, which is what I need.
You need to convert the location on the screen to the layer's "node space":
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchStart = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
// convert touch location to layer space
touchStart = [self convertToNodeSpace:touchStart];
NSLog(#"Touch:%f,%f",touchStart.x, touchStart.y);
return YES;
}

Resources