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
Related
so I want to randomly create falling blocks. The blocks can be GRects or Sprites, I just have no clue how to go about it.
I need to both randomly choose a time to call the createBlock function. Then I need it to create a block at the top of the screen that falls to the bottom off the screen. The end result should be an endless flow of blocks falling off the screen. I'm less concerned with the random interval part and more concerned with getting as many blocks as I need to fall.
Thanks in advance!
Well, assuming you have a basic understanding of how cocos2d operates (layers, scenes, children, etc), this is how you might go about it.
CGSize winSize = [[CCDirector shareDirector] winSize];
CCSprite *block = [CCSprite spriteWithFile:#"block.png"];
// Generate a random x position
CGFloat x = arc4random() % winSize.width;
// Position the block at a random x, just above the top of the screen
block.position = ccp(x, winSize.height + block.contentSize.height/2);
// Tell the block to fall down to the bottom of the screen over 2 seconds
CCMoveTo *fall = [CCMoveTo actionWithDuration:2 position:ccp(block.position.x, 0 - block.contentSize.height/2)];
[block runAction:fall];
If you wanted this to occur in a constant stream of blocks, you can setup a callback function to call your "createBlock" function every so often. It's not very efficient to constantly recreate blocks, but it's more simple than maintaining a list of off-screen blocks and moving them up to the top to fall again when necessary.
CCSequence *rainBlocks = [CCSequence actions: [CCCallBlock actionWithBlock:^{
[self createBlock];
}],
[CCDelayTime actionWithDuration:3],
nil]];
[self runAction:[CCRepeatForever actionWithAction:rainBlocks]];
I'm not 100% on the syntax there, but that's the general idea.
I'm having a bit of a problem solving this trivially-looking feature. In my game, at some point in a level, I stop the main game loop and want to zoom to the player sprite. The player sprite is added to a layer that contains this loop and run the code to zoom to this player.
It looks like this :
-(void) stopGameAndZoomToPlayer {
[gamePlayLoop_ invalidate];
zoomToPlayerLoop_ = [NSTimer scheduledTimerWithTimeInterval:1.0f/60.0f target:self selector:#selector(tickZoomToPlayerLoop:) userInfo:nil repeats:YES];
}
The tick method, is for now implemented like this :
-(void) tickZoomToPlayerLoop:(NSTimer*)timer {
if (self.scale > 4) {
[zoomToPlayerLoop_ invalidate];
}
[self setScale:self.scale+0.001];
CGSize winSize = [[CCDirector sharedDirector]winSize];
CGPoint targetPosition = playerSprite_.position;
float wantedX = ((winSize.width/2-targetPosition.x)/self.scale);
float wantedY = ((winSize.height/2-targetPosition.y)/self.scale);
CGPoint maximumCenter = ccp(wantedX,wantedY);
[self setPosition:maximumCenter];
}
Seems trivial right ? Yet, there's something I must be missing because the zoom, while slow, is offset vis-a-vis the player sprite. The player sprite, at the end of the tickZoomToPlayerLoop timer, appears in the top right corner of the screen ; it is not in the center (but not even centered of the top right corner of the viewport).
The way I see it, this code put the center of the main layer to the position of the player sprite. Then it scales. Actually the position shouldn't be in the zoom loop but I put it here because I'll need to handle the boundaries of the level layer, because as for now the user can see the black void of non playable zone, outside the level. Yet, even if the center of the main layer at the beginning of the loop seems to be the player sprite, when scaling, one can see it is not.
Does the scale is targeted to the center of a CCNode ? Is it offseted in any way ? Does the anchor point plays a part in the way the scaling is done ?
Some more infos :
the anchor point of the main layer is (0.5,0.5) - the default one
the position of the main layer is, at first before the timer, (0,0) - simply added when creating the scene
Scaling to my knowledge is effected by anchor points.
If all you need to do is zoom in way not use CCCamera?
[myLayer.camera setEyeX:0 eyeY:0 eyeZ:zoomValue];
You can change the eyeZ to zoom in and out.
I've been looking around for a while, and I have not been able to find an answer to this for some reason. It seems simple enough, but maybe I just can't find the right function in the library.
I have a scene with a layer that contains a bunch of CCNodes with each one CCSprite in them.
During the application, I move around the position of the main layer, so that I "pan" around a camera in a way. (i.e. I translate the entire layer so that the viewport changes).
Now I want to determine the absolute position of a CCNode in screen coordinates. The position property return the position relative to the parent node, but I really would like this transformed to its actual position on screen.
Also, as an added bonus, it would be awesome if I could express this position as coordinate system where 0,0 maps to the upper left of the screen, and 1,1 maps to the lower right of the screen. (So I stay compatible with all devices)
Edit: Note that the solution should work for any hierarchy of CCNodes preferably.
Every CCNode, and descendants thereof, has a method named convertToWorldSpace:(CGPoint)p
This returns the coordinates relative to your scene.
When you have this coordinate, flip your Y-axis, as you want 0,0 to be in the top left.
CCNode * myNode = [[CCNode alloc] init];
myNode.position = CGPointMake(150.0f, 100.0f);
CCSprite * mySprite = [[CCSprite alloc] init]; // CCSprite is a descendant of CCNode
[myNode addChild: mySprite];
mySprite.position = CGPointMake(-50.0f, 50.0f);
// we now have a node with a sprite in in. On this sprite (well, maybe
// it you should attack the node to a scene) you can call convertToWorldSpace:
CGPoint worldCoord = [mySprite convertToWorldSpace: mySprite.position];
// to convert it to Y0 = top we should take the Y of the screen, and subtract the worldCoord Y
worldCoord = CGPointMake(worldCoord.x, ((CGSize)[[CCDirector sharedDirector] displaySizeInPixels]).height - worldCoord.y);
// This is dry coded so may contain an error here or there.
// Let me know if this doesn't work.
[mySprite release];
[myNode release];
I have implemented a drag on a sprite object as follows..
-(BOOL)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
CGPoint location = [[Director sharedDirector] convertCoordinate: [touch locationInView:touch.view]];
[diskSprite setPosition:ccp(location.x , location.y )];
return kEventHandled;
}
but this dragging is not smooth.....
when i drag fast with my thumb the object left from the path.
Thanks
Probably a little bit late but I was searching for a similar thing.
I found this great Tutorial which explained everything:
http://www.raywenderlich.com/2343/how-to-drag-and-drop-sprites-with-cocos2d
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
CGPoint translation = ccpSub(touchLocation, oldTouchLocation);
CGPoint newPos = ccpAdd(mySpriteToMove.position, translation);
mySpriteToMove.position = newPos;
}
I had this same issue with my game. Dragging operations appeared jerky. I believe the reason is that touch events aren't generated fast enough to give a smooth appearance.
To solve the problem I smoothed the motion out by running an action on the sprite toward the desired location, instead of setting the position immediately.
I'm not exactly sure what you mean by "the object left from the path". I suppose what you mean is that if you drag your finger over the screen in an arc or circle, that the sprite will "jump" from point to point, instead of follow your finger precisely. Is this correct?
If you want your sprite to follow an exact path, you will have to create a path and then set the sprite to follow it. What you do now is simply set the sprite's position to the touch position, but a "dragged" touch will not create an event for every pixel it touches.
It is fairly easy to create a path for touches received, and code samples can be found here and there. However, if the sprite's speed (in pixels per frame) is too high, you will always see it "jump", even if you use a smooth path.
Example:
You can animate a sprite over a circular path. If you animate this to complete the path in 1 second, you will likely see smooth animation. But if it runs at a high speed, like a full circle in 4 frames, you will just see your sprite at 4 places, not in a smooth circle.
If you wish to 'correct' that, you will need to look into blending, or determine what the maximum speed is for acceptable motion, and slow your sprite down when it's too fast.
I hope that answers your question. If it's not clear, feel free to edit your question, or add a comment to my answer.
look here, what I suggest you to try in such case:
-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
if (_binCaptured) {
CGPoint location = [self convertTouchToNodeSpace:touch];
[_sprite stopAllActions];
id move = [CCEaseIn actionWithAction:[CCMoveTo actionWithDuration:0.1 position:ccp(location.x, _sprite.position.y)]];
[_sprite runAction:move];
}
}
And it really work smoothly.
I enjoyed this easy way.
On iPhone, how to implement rotating image around the center point using finger touch ?
Just like wheel, if you put finger on the iPhone screen , then move suddenly, then the image becoming rotating around center point just like the wheel, after a while, it becomes more and more slow , finally stop.
Who can help to give some pieces of codes (Object-C) or some suggest ?
I was working with a "spin the bottle"-app yesterday. On the window I have a ImageView with an bottle that's suppose to response to touches and rotate the way the user swipes his finger. I struggled to get my ImageView to rotate during the touch-events (TouchesBegan, Touchesoved, TouchesEnd). I used this code in TouchesMoved to find out the angle in witch to rotate the image.
public override void TouchesMoved (NSSet touches, UIEvent evt)
{
PointF pt = (touches.AnyObject as UITouch).LocationInView(this);
float x = pt.X - this.Center.X;
float y = pt.Y - this.Center.Y;
double ang = Math.Atan2(x,y);
// yada yada, rotate image using this.Transform
}
THIS IS IMPORTANT! When the ImageView rotates, even the x & y-coordinates changes. So touching the same area all the time would give me different values in the pt and prePt-points. After some thinking, googeling and reading I came up with an simple solution to the problem. The "SuperView"-property of the ImageView.
PointF pt = (touches.AnyObject as UITouch).LocationInView(this.SuperView);
Having that small change in place made it alot easier, no i can use the UITouch-metohs LocationInView and PreviousLocationInView and get the right x & y coordinates. Her is parts of my code.
float deltaAngle;
public override void TouchesMoved (NSSet touches, UIEvent evt)
{
PointF pt = (touches.AnyObject as UITouch).LocationInView(this.Superview);
float x = pt.X - this.Center.X;
float y = pt.Y - this.Center.Y;
float ang = float.Parse(Math.Atan2(dx,dy).ToString());
//do the rotation
if (deltaAngle == 0.0) {
deltaAngle = ang;
}
else
{
float angleDif = deltaAngle - ang;
this.Transform = CGAffineTransform.MakeRotation(angleDif);
}
}
Hope that helped someone from spending hours on how to figure out how to freaking rotate a bottle! :)
I would use the affine transformations - yuou can assign a transformation to any layer or UI element using the transform property.
You can create a rotation transform using CGAffineTransform CGAffineTransformMakeRotation( CGFloat angle) which will return a transformation that rotates an element. The default rotation should be around the centerpoint.
Be aware, the rotation is limited to 360 degrees, so if you want to rotate something more than that (say through 720 degrees) - you have to break the rotation into several sequences.
You may find this SO article useful as well.
The transform property of a view or layer can be used to rotate the image displayed within. As far as the spinning part goes, you just track the location and movement of touches in your view with touchesBegan, touchesMoved, and touchesEnded.
Use the distance and time between the touches updates to calculate a speed, and use that to set a rotational velocity. Once you start the image spinning, update the position periodically (with an NSTimer, maybe), and reduce the rotational velocity by some constant.