implementing Static object on a moving scene - iphone

How to implement an 'menu' that seems like stuck to it's position? Here with stuck I meant to say either scene is moving or still it remain on it's on position. Which gives feel like it's on it's place. I am implementing it by moving it with scrolling scene, it works but behave absurdly, 'sometimes'.
Here is the code how am I creating menu:
resetPosition =[CCMenuItemImage itemFromNormalImage:#"position.png"
selectedImage:#"position_over.png"
disabledImage:#"disabled.png"
target:self
selector:#selector(reset)];
resetPosition.position =ccp(400, 300);
myresetMenu = [CCMenu menuWithItems:resetPosition, nil];
myresetMenu.position = ccp(0,0);
[[self parent] addChild:myresetMenu z:10];
[resetPosition setIsEnabled:NO];
And now code for moving the scene(when user trying to scroll the scene). Also I have moved the menu with scene, so it gives the effect that the menu is still with the scene:
if([touchArray count]==1)//scroll
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
moveLocation1 = [[CCDirector sharedDirector] convertToGL:location];
float diffX = beginLocation1.x - moveLocation1.x;
float diffY = beginLocation1.y - moveLocation1.y;
//NAVIGATION TOWARDS X AND Y WhenEver and how ever you want
if (abs(diffX) > abs(diffY))
{
CCLOG(#"yScrlFlag=%d",yScrlFlag);
if(diffX > 0)
{
xScrlFlag=1;
[self.parent runAction:[CCMoveTo actionWithDuration:round(-(-3112-self.parent.position.x)/650)
position:ccp((-3112-self.position.x),self.parent.position.y)]];
[resetPosition setIsEnabled:YES];
[resetPosition runAction:[CCMoveTo actionWithDuration:round(-(-3112-self.parent.position.x)/650)
position:ccp((3112+self.position.x+400),resetPosition.position.y)]];
}
else
{
xScrlFlag=0;
[self.parent runAction:[CCMoveTo actionWithDuration:(-self.parent.position.x/650)
position:ccp(0,self.parent.position.y)]];
//[resetPosition setIsEnabled:YES];
[resetPosition runAction:[CCMoveTo actionWithDuration:(-self.parent.position.x/650)
position:ccp(400,resetPosition.position.y)]];
}
}
else
{
if(diffY < 0)
{
yScrlFlag=1;
CCLOG(#"\n nodePosition.x=%f \n nodePosition.y=%f",nodePosition.x,nodePosition.y);
[self.parent runAction:[CCMoveTo actionWithDuration:(-(-300-self.parent.position.y)/650)
position:ccp(self.parent.position.x,(-self.position.y))]];
//[self.parent runAction:[CCMoveBy actionWithDuration:(-(-300-self.parent.position.y)/650)
// position:ccp(self.parent.position.x, -diffY)]];
[resetPosition setIsEnabled:YES];
//[resetPosition runAction:[CCMoveBy actionWithDuration:round(-(-300-self.parent.position.x)/650)
// position:ccp(resetPosition.position.x, (self.parent.position.y))]];
}
else
{
yScrlFlag=0;
[self.parent runAction:[CCMoveTo actionWithDuration:(-(-300-self.parent.position.y)/650)
position:ccp(self.parent.position.x,0)]];
//[resetPosition runAction:[CCMoveTo actionWithDuration:round(-(-300-self.parent.position.x)/650)
// position:ccp(resetPosition.position.x,300)]];
}
}
}

You don't specify how you create your menus and what are you doing with them, so my answer is pretty general...
The right approach to this, for me is having a UI layer where you put all your UI stuff (menus, buttons, etc.) and that you treat differently from your other, game layers. In other words, you give your UI layer a fixed position and then don't apply any actions or any other kind of transformations to it. It should stay pretty static this way.
Cocos2d offers its own classes to manage CCMenu, CCMenuItem, and CCLabel that allow you to build you UI easily. But, if you prefer, you could also try and integrate UIKit objects. Have a look at CCUIViewWrapper, if you really need it.
EDIT:
The idea about having a UI layer is the following in more detail:
you have a root layer acting as a container of all your layers;
one of those layers is the "game layer", which is pretty much what you have now;
another is the "ui layer";
touches and any kind of transformations are only applied to the "game layer", which move within the root layer without affecting the "ui layer".
There is a nice tutorial about this in "Learn Game Development with Cocos2D" by Steffen Itterheim. This book is fantastic if you want to learn several nifty approaches to your cocos2d game. The tutorial I am talking about is available in code form on Steffen's site. You can dowload it from this page and look into chapter 5 "Scene and Layers" example, specifically starting from ScenesAndLayers04, where you will find the 'MultiLayerScene.h/m` classes.

I had a bit of trouble with this a while back. I had been trying to have UI elements that did not get lost when the camera moved. What I ended up doing was adding a CCParallaxNode as a child and setting the parallax ratio to ccp(0.0, 0.0) on everything I put on it. That way the scene can move around but my UI layer stays in place. I'm not sure if that's the best way to do it, but I'm a dirty hack kind of programmer :)

#sergio has the right idea.
With your code though. Have it in a layer all to itself and then on the touch action move it like you are already and just reset its position when you take the action to bring the menu back up again.

Related

How to implement camera flyby in Cocos2d

Let's say the width of a level in my game is three times the screen width, and my player starts at the left most edge. How should I go about implementing a camera flyby starting from the right edge at the beginning of this level to scroll through the entire world so the player knows what to expect ahead?
I came across this post here http://www.cocos2d-iphone.org/forum/topic/9568 that seems to be useful, borrowed the block of code in it and put it in my GameWorldLayer, but it didn't work. I'm essentially only seeing a black screen. What's wrong? And what would be a better way of implementing this?
-(void)visit {
CGSize screenDims = [[CCDirector sharedDirector] winSizeInPixels];
CGPoint camPos = gameCamera.position;
float camZoom = gameCamera.zoom;
[[CCDirector sharedDirector] setProjection:kCCDirectorProjectionCustom];
//now set your projection
kmGLMatrixMode(KM_GL_PROJECTION);
//save current projection state
kmGLPushMatrix();
kmGLLoadIdentity();
kmMat4 orthoMat;
kmMat4OrthographicProjection(&orthoMat,
camPos.x -screenDims.width/(2*camZoom),
camPos.x +screenDims.width/(2*camZoom),
camPos.y +screenDims.height/(2*camZoom),
camPos.y -screenDims.height/(2*camZoom),
-1000,
1000);
kmGLMatrixMode(KM_GL_MODELVIEW);
kmGLLoadIdentity();
[super visit];
//put it back
kmGLMatrixMode(KM_GL_PROJECTION);
kmGLPopMatrix();
kmGLMatrixMode(KM_GL_MODELVIEW);
kmGLLoadIdentity();
}
All that is way to complicated for what you want. Position the layer at the end of the level at the beginning and then simply move it to the beginning i.e.
[layer runAction:[CCMoveTo actionWithDuration:10 position:ccp(starty, startx)]];
Where 20 is the time you want it to take for the "flyby" and the position is the start position

Transition effects for layers

In cocos2d for iPhone, in one scene, how can I fade-out one layer and fade-in another one?
The idea is that I have one screen with:
one layer that has pagination controls (done as menu items with toggling and selectors) at the top
the rest of the screen is filled with another layer that shows the content of the current page.
Now once the user clicks any of the pagination controls, I want to fade out the content layer for the current page (but keep the pagination layer), and fade in the content layer for the next page. These are both the same layers, they pull data in from a plist based on the currentPage variable, so effectively I need to refresh the layer.
I know that for scenes, when calling replaceScene, you can specify a transition effect. And doing it that way it all works. But obviously, it will also fade-out the pagination controls, which just looks stupid. So how does it work for layers?
hmmm .... use a CCLayerColor (it implements the CCRGBAProtocol protocol) and the fade will propagate to any object within. Then do something like this:
-(void) buttonTouchedCallBack{
id out = [CCFadeTo actionWithDuration:.35 opacity:0];
id callFunc = [CCCallFunc actionWithTarget:self selector:#selector(changeContent)];
id in = [CCFadeTo actionWithDuration:.35 opacity:255];
id enableMenus = [CCCallFunc actionWithTarget:self selector:#selector(layerInView)];
_menu.isTouchEnabled=NO;
[_contentLayer stopAllActions];
[_contentLayer runAction:[CCSequence actions:out,callFunc,in,enableMenus,nil]];
}
-(void) changeContent{
// do your stuff here
}
-(void) layerInView{
_menu.isTouchEnabled=YES;
// and anything else that is appropriate
}
I think you can use runAction: to let your CCLayers do CCActions (such as CCFadeIn and CCFadeOut) so that you can achieve what you want.
You gonna need two "content layer"s to hold the current page (page A) and the next page (page B) respectively. After the fading actions are over for both content layers, you can then clean up the page A.
I wrote a little function which will use blocks to give the same fade-in, fade-out effect as a scene transition, but for an individual layer. Just pass in the layer you want to cover up, the speed of the fade out and fade in, the color to fade to, and the block you wish to execute while the layer is hidden.
-(void)fadeLayer:(CCLayer*)layer withOutDuration:(float)outTime inDuration:(float)inTime color:(ccColor3B)color withBlock: (void(^)())block
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCLayerColor *toplayer = [CCLayerColor layerWithColor:ccc4(color.r, color.g, color.b, 0) width:winSize.width height:winSize.height];
[layer addChild:toplayer z:INT_MAX];
[toplayer runAction:
[CCSequence actions:
[CCFadeIn actionWithDuration:outTime],
[CCCallBlock actionWithBlock:block],
[CCFadeOut actionWithDuration:inTime],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[node removeFromParentAndCleanup:YES];
}],
nil]];
}

How do you modify this sample code to stop Image Views from stacking?

http://developer.apple.com/library/ios/#samplecode/Touches/Introduction/Intro.html
I'm new to IOS development, and have been playing around with the IOS dev sample code, trying to learn how things work, etc... I came across this sample code (the classic version, not the gesture recognizers ver.) that does everything I'm looking for, except one thing. When the image views are dragged over another image view, they begin stacking up, and you have to double click the image to separate them.
I've spent forever trying to figure out how to make them not stack up. I think I have to store that a piece is getting being moved in an ivar, and stop it from getting the position of the other views... But I'm not sure how to go about doing it. If someone has the time to modify the sample code to not stack pieces, I'd really appreciate it! I'm dying to find out the solution after the forever I spent trying to do it!
I'll try to be a little specific with what I'm trying to do here... I've made 26 of these image views, making them each a letter of the alphabet. I have 13 image views in two rows. A-M & N-Z. Currently, if I drag the A tile down from the top row, moving it down to a "spelling area" I have to cross over the N-Z row. When I do, the "A" tile (image view) picks up any tile it hovers over. So if I drag the A straight down, it's going to pick up the N tile, because it's going to hover right over it, before getting down to the "spelling" area.
Thanks in advance for any help or direction you can give me!
Well you have this function in the sample code
// Checks to see which view, or views, the point is in and then calls a method to perform the closing animation,
// which is to return the piece to its original size, as if it is being put down by the user.
-(void)dispatchTouchEndEvent:(UIView *)theView toPosition:(CGPoint)position
{
// Check to see which view, or views, the point is in and then animate to that position.
if (CGRectContainsPoint([firstPieceView frame], position)) {
[self animateView:firstPieceView toPosition: position];
}
if (CGRectContainsPoint([secondPieceView frame], position)) {
[self animateView:secondPieceView toPosition: position];
}
if (CGRectContainsPoint([thirdPieceView frame], position)) {
[self animateView:thirdPieceView toPosition: position];
}
// If one piece obscures another, display a message so the user can move the pieces apart
if (CGPointEqualToPoint(firstPieceView.center, secondPieceView.center) ||
CGPointEqualToPoint(firstPieceView.center, thirdPieceView.center) ||
CGPointEqualToPoint(secondPieceView.center, thirdPieceView.center)) {
touchInstructionsText.text = #"Double tap the background to move the pieces apart.";
piecesOnTop = YES;
} else {
piecesOnTop = NO;
}
}
wich you can try to modify to
// Checks to see which view, or views, the point is in and then calls a method to perform the closing animation,
// which is to return the piece to its original size, as if it is being put down by the user.
-(void)dispatchTouchEndEvent:(UIView *)theView toPosition:(CGPoint)position
{
// Check to see which view, or views, the point is in and then animate to that position.
if (CGRectContainsPoint([firstPieceView frame], position)) {
[self animateView:firstPieceView toPosition: position];
}
if (CGRectContainsPoint([secondPieceView frame], position)) {
[self animateView:secondPieceView toPosition: position];
}
if (CGRectContainsPoint([thirdPieceView frame], position)) {
[self animateView:thirdPieceView toPosition: position];
}
// If one piece obscures another, display a message so the user can move the pieces apart
if (CGPointEqualToPoint(firstPieceView.center, secondPieceView.center) ||
CGPointEqualToPoint(firstPieceView.center, thirdPieceView.center) ||
CGPointEqualToPoint(secondPieceView.center, thirdPieceView.center)) {
if (firstPieceView.center.x == secondPieceView.center.x)
secondPieceView.center = CGPointMake(firstPieceView.center.x - 50, firstPieceView.center.y - 50);
if (firstPieceView.center.x == thirdPieceView.center.x)
thirdPieceView.center = CGPointMake(firstPieceView.center.x + 50, firstPieceView.center.y + 50);
if (secondPieceView.center.x == thirdPieceView.center.x)
thirdPieceView.center = CGPointMake(secondPieceView.center.x + 50, secondPieceView.center.y + 50);
touchInstructionsText.text = #"";
} else {
piecesOnTop = NO;
}
}
Let me know if it does what you want...

ApplyLinearImpulse doesnt work when called within UIPanGestureRecognizer function

I am using Box2d and Cocos2d to develop an iPhone game. I have a ball in the center of the screen with a simple boundary created using fixtures etc. What I want to do is add the functionality so that a user can "swipe" the ball to apply force and basically flick it across the screen.
I am using the following code to achieve this:
b2Vec2 force = b2Vec2(30, 30);
_body->ApplyLinearImpulse(force, _ballBodyDef->position);
If I apply a linear impulse during the init function the ball fires off in the correct direction and behaves as it should do. It doesnt do anything, however, if I put it into the handlePan function that is called when a gesture is done by the user. Here is the full code for the function: (Note that the NSLog writes out the correct information.
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGPoint velocity = [recognizer velocityInView:recognizer.view];
NSLog(#"Vel: %f, newPos: %f",velocity.x,velocity.y);
b2Vec2 force = b2Vec2(30, 30);
_body->ApplyLinearImpulse(force, _ballBodyDef->position);
}
}
The force you are applying in handlePanFrom is always b2Vec2(30, 30). It will never change with your current code, and is therefore not going to move in your direction.

Problem animating a Parallax Layer in Cocos2d-iPhone

I'm trying to implement my own parallax scrolling in Cocos2d since the built in parallax functionality isn't exactly what I'm looking for (it scales the layers, which is not what I want)
So, my own Parallax Layers overwrite the Layers setPosition method like this
- (void) setPosition:(CGPoint)point
{
[super setPosition:CGPointMake(point.x / pRatio, point.y / pRatio)];
}
pRatio is an int that reduces the amount the layer moves by, thus creating the kind of Parallax effect I'm looking for. And it works perfectly like this.
However, on occasion I need to animate a Layer to a specific position using MoveTo, so I created this method which responds to an NSNotiification
- (void) gotoStartPosition:(NSNotification *)notification
{
CGPoint startPos = CGPointFromString([notification object]);
id moveToStart = [MoveTo actionWithDuration:1 position:startPos];
[self runAction:moveToStart];
}
Which works until I set the pRatio to anything above 1. If the pRatio is higher than 1, then gotoStartPosition makes the layer jump to CGPoint(0,0) and animates into position from there.
I can't figure out at what point the Layers position is being set to 0,0, and I don't understand why it's only doing this when pRatio is higher than 1.
I suspect this is going to be something painfully simple, but I think my brain is fried. Any help, greatly appreciated.
UPDATE: I'm a doofus. What's happening is obvious. The Animation first sets the layers position to it's current position. But I've told the layer to divide any setPosition coordinates by pRatio. So it's not going to CGPoint(0,0) but something like CGPoint(0.032, 0). I've fixed it like this:
- (void) setPosition:(CGPoint)point
{
if(!animating){
[super setPosition:CGPointMake(round(point.x / pRatio), round(point.y / pRatio))];
}else{
[super setPosition:point];
}
}
- (void) gotoStartPosition:(NSNotification *)notification
{
animating = YES;
CGPoint startPos = CGPointFromString([notification object]);
CGPoint pStart = CGPointMake(startPos.x / pRatio, startPos.y / pRatio);
id moveToStart = [MoveTo actionWithDuration:1 position:pStart];
id endAnim = [CallFunc actionWithTarget:self selector:#selector(endAnimation)];
[self runAction:[Sequence actions: moveToStart, endAnim, nil]];
}
- (void) endAnimation
{
animating = NO;
}
Which isn't elegant, but it works. I'm closing this question.