I am using the following code to make the camera follow my character as he falls down the screen:
[self runAction:[CCFollow actionWithTarget:charSprite worldBoundary:[levelLoader gameWorldSize]/*CGRectMake(0,0,320,3000)*/]];
What happens is this: the code runs as you would expect, and follows the character as he falls from the top of the screen. However: when he reaches the middle of the screen the camera stops following him.
I have tried both the code above as well as replacing the [levelLoader gameWorldSize] with the commented out CGRectMake(0,0,320,3000) - both yield the same results. As a last resort I tried to use some code I found on www.raywenderlich.com that has worked for me before (see below) (it worked on the x-axis in another game I wrote), but the same problem occurred.
-(void)setViewpointCenter:(CGPoint) position
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
CGRect worldRect = [levelLoader gameWorldSize];
int x = MAX(position.x, worldRect.origin.x + winSize.width / 2);
int y = MAX(position.y, worldRect.origin.y + winSize.height / 2);
x = MIN(x, (worldRect.origin.x + worldRect.size.width) - winSize.width / 2);
y = MIN(y, (worldRect.origin.y + worldRect.size.height) - winSize.height/2);
CGPoint actualPosition = ccp(x, y);
CGPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
CGPoint viewPoint = ccpSub(centerOfView, actualPosition);
self.position = viewPoint;
}
My world is created in Portrait mode using LevelHelper.
Any help would be greatly appreciated!
CCFollow follows the node within the world boundary, minus half the screen width/height. If your sprite starts falling at y = 320 then CCFollow will follow it until y = 160.
You can either position the sprite to your world boundary's top (y = 3000) or make the world boundary negative (y = -3000). Either way, when the sprite reaches the world boundary, scrolling will stop half a screen width/height before the world boundary.
Related
I am working with the flappy bird demo trying different things just to get to "know each other".
Going through the demo, I've managed to change the direction of the game to vertical scroll moving upwards.
Having reversed the CGFloat to negative values makes my obstacles move upward but once they are out of bounds they do not re-spawn.
If I change the values for a downward scroll they re-spawn as per the update method.
Can someone explain to me what I'm doing wrong with the x to y conversion? Why is the bottom recognized and the top of my screen not?
Thanks in advance
#import "MainScene.h"
static const CGFloat scrollSpeed = -280.f; //upwards
static const CGFloat firstObstaclePosition = -568.f;
static const CGFloat distanceBetweenObstacles = 80;
#implementation MainScene {
CCSprite *_hero;
CCPhysicsNode *_physicsNode;
NSMutableArray *_obstacles;
}
- (void)spawnNewObstacle {
CCNode *previousObstacle = [_obstacles lastObject];
CGFloat previousObstacleYPosition = previousObstacle.position.y;
if (!previousObstacle) {
// this is the first obstacle
previousObstacleYPosition = firstObstaclePosition;
}
CCNode *obstacle = [CCBReader load:#"Obstacle"];
obstacle.position = ccp(0, previousObstacleYPosition + distanceBetweenObstacles);
[_physicsNode addChild:obstacle];
[_obstacles addObject:obstacle];
}
- (void)update:(CCTime)delta {
_hero.position = ccp(_hero.position.x, _hero.position.y + delta * scrollSpeed);//move on Y axis
_physicsNode.position = ccp(_physicsNode.position.x, _physicsNode.position.y - (scrollSpeed *delta));//scroll in Y axis
//spawn more
NSMutableArray *offScreenObstacles = nil;
for (CCNode *obstacle in _obstacles) {
CGPoint obstacleWorldPosition = [_physicsNode convertToWorldSpace:obstacle.position];
CGPoint obstacleScreenPosition = [self convertToNodeSpace:obstacleWorldPosition];
if (obstacleScreenPosition.y < -obstacle.contentSize.height) {
if (!offScreenObstacles) {
offScreenObstacles = [NSMutableArray array];
}
[offScreenObstacles addObject:obstacle];
}
}
for (CCNode *obstacleToRemove in offScreenObstacles) {
[obstacleToRemove removeFromParent];
[_obstacles removeObject:obstacleToRemove];
// for each removed obstacle, add a new one
[self spawnNewObstacle];
}
}
- (void)didLoadFromCCB {
self.userInteractionEnabled = TRUE;
_obstacles = [NSMutableArray array];
[self spawnNewObstacle];
[self spawnNewObstacle];
[self spawnNewObstacle];
}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
}
#end
I've attached the _physicsNode screenshot from SB.
It looks like your obstacles will be spawning fine if they are a short, constant height, and the distance between them value is large enough. It may be better to incorporate the height of the obstacles to get a more meaningful value of the distance variable. Just a thought.
The line -
obstacle.position = ccp(0, previousObstacleYPosition + distanceBetweenObstacles);
Could be -
obstacle.position = ccp(0, previousObstacleYPosition + distanceBetweenObstacles + previousObstacle.contentSize.height);
As for the problem of the vertical scrolling working downwards and not upwards I believe it is due to this line:
if (obstacleScreenPosition.y < -obstacle.contentSize.height) {
Since this line is responsible for determining when an obstacle is off the screen it has an effect on the spawning of the next obstacle. It makes sense why this line works for downwards scrolling but needs to be changed for upwards scrolling.
Try:
if (obstacleScreenPosition.y > (_physicsNode.contentSize.height + obstacle.contentSize.height)) {
You may or may not need the size of the obstacle depending on where it is anchored.
I hope this works, Good luck.
I need to have the Cocos2d camera follow a sprite (attached to a Box2D body) that the user is touching on the screen. As the user is dragging the player around, I need it to be able to go to other parts of the world. This has to be through touch, and not automatic scrolling.
I tried several approaches based on tutorials but nothing seem to address this issue. For example the solution offered here Move CCCamera with the ccTouchesMoved method? (cocos2d,iphone) by #Michael Fredrickson has the entire layer move, but when it moves, the sprites / bodies on the screen have unmatched coordinations and when I test to see if they're touched, the if(fixture->TestPoint(locationWorld)) fails.
I also looked at the tutorials here http://www.learn-cocos2d.com/2012/12/ways-scrolling-cocos2d-explained/ but this also isn't what I'm looking for.
Any help would be greatly appreciated.
EDIT:
I'm accepting Liolik's answer below because it put me on the right track. The last piece of the puzzle, though, is to make the value received from the getPoint method an instance variable, and deduce it from locationWorld which I'm doing the TestPoint against. Like this:
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
b2Vec2 diff = b2Vec2(difference.x, difference.y);
for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext()) {
b2Fixture* f = b->GetFixtureList();
while(f != NULL) {
if(f->TestPoint(locationWorld-diff)) {
b2MouseJointDef def;
def.bodyA = _groundBody;
def.bodyB = b;
def.target = locationWorld-diff;
def.maxForce = 9999999.0f * b->GetMass();
_mouseJoint = (b2MouseJoint*)_world->CreateJoint(&def);
b->SetAwake(true);
}
f = f->GetNext();
}
}
in update function :
CGPoint direction = [self getPoint:myBody->GetPosition()];
[self setPosition:direction];
- (CGPoint)getPoint:(b2Vec2)vec
{
CGSize screen = [[CCDirector sharedDirector] winSize];
float x = vec.x * PTM_RATIO;
float y = vec.y * PTM_RATIO;
x = MAX(x, screen.width/2);
y = MAX(y, screen.height/2);
float _x = area.width - (screen.width/2);
float _y = area.height - (screen.height/2);
x = MIN(x, _x);
y = MIN(y, _y);
CGPoint goodPoint = ccp(x,y);
CGPoint centerOfScreen = ccp(screen.width/2, screen.height/2);
CGPoint difference = ccpSub(centerOfScreen, goodPoint);
return difference;
}
So if i understand correctly, when the sprite is inside of the middle of the screen, the background is stationary and the sprite follows your finger, but when you scroll toward the edge, the camera starts to pan?
I had something roughly similar in my game Star Digger where there's a ship in the middle of the screen on its own layer that has to fly around the world, and had the same problem when the ship fired bullets into the main world layer.
heres what I did:
float thresholdMinX = winSize*1/3;
float thresholdMaxX = winSize*2/3;
if(touch.x > thresholdMaxX) //scrolling right
{
self.x += touch.x - thresholdMaxX;
}
else if(touchX < thresholdMinX)
{
self.x += thresholdMinX - touchX;
}
else
{
sprite.position = touch;
}
CGPoint spritePointInWorld = ccp(sprite.x - self.x, sprite.y - self.y);
then every time you calculate collisions, you need to recompute the sprites "actual" position in the world, which is its screen position minus the worlds offset, instead of the sprites screen position.
As I am newbie to cocoa2d I am struggling alot to rotate the physics or dynamic body along an arc path.
The way I tried is as follows:
#define COS_ANIMATOR(position, timeCount, speed, waveMagnitude) ((cosf(timeCount * speed) * waveMagnitude) + position)
#define SIN_ANIMATOR(position, timeCount, speed, waveMagnitude) ((sinf(timeCount * speed) * waveMagnitude) + position)
CCSpriteBatchNode *pipe_parent = [CCSpriteBatchNode batchNodeWithFile:#"pipe.png" capacity:100];
CCTexture2D *pipeSpriteTexture_ = [pipe_parent texture];
PhysicsSprite *pipeSprite = [PhysicsSprite spriteWithTexture:pipeSpriteTexture_ rect:CGRectMake(0 ,0 ,55,122)];
//pipe = [CCSprite spriteWithFile:#"pipe.png"
// rect:CGRectMake(0, 0, 55, 122)];
[self addChild:pipeSprite];
// pipe.position = ccp(s.width/2 , 420.0);
b2BodyDef myBodyDef;
myBodyDef.type = b2_staticBody; //this will be a dynamic body
myBodyDef.position.Set(((s.width/2) - 90)/PTM_RATIO, 420.0/PTM_RATIO); //set the starting position
myBodyDef.angle = 0; //set the starting angle
b2Body* staticBody = world->CreateBody(&myBodyDef);
b2PolygonShape boxShape;
boxShape.SetAsBox(1,1);
b2FixtureDef boxFixtureDef;
boxFixtureDef.shape = &boxShape;
boxFixtureDef.density = 1;
boxFixtureDef.userData = pipeSprite;
boxFixtureDef.filter.groupIndex = -1;
staticBody->CreateFixture(&boxFixtureDef);
[pipeSprite setPhysicsBody:staticBody];
-(void) draw
{
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
const CGPoint newSpritePosition = ccp(COS_ANIMATOR(150, mTimeCounter, 0.05,50), SIN_ANIMATOR(400, mTimeCounter, -0.05, 50));
pipeSprite.position = newSpritePosition;
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
on following this approach my sprite rotating in circular motion instead of rotating in an arc path.
Please give your ideas or suggestions.
Thanks all
I'm not entirely sure what it is you are looking to accomplish when you talk about rotating in an arc path. I only see you setting a position, not a rotation, so are you just wanting to set a position, or a rotation, or both? Your position code looks like you are trying to achieve a circular (or elliptical) path because you are using the sine and cosine in the x,y position.
If you're looking to move a sprite along a sine curve, I did that today and it took a bit of trial and error. I had some variables for the amplitude and period, and from there I traced out a nice sine curve movement in the sprite's update: method.
CGPoint initialPosition; // set this to the sprite's initial position
float amplitude; // in points
float period; // in points
float y, x = initialPosition.x;
-(void) update:(ccTime)dt
{
x += dt * 100; // speed of movement across the screen. Picked by trial and error.
y = initalPosition.y + amplitude * sinf((x - initialPosition.x)/period);
sprite.position = ccp(x,y);
sprite.rotation = cosf((x - initialPosition.x)/period); // optional if you want to rotate along the path as well
}
Don't know if this is anything you are looking for but it might give you some ideas.
I have been trying to implement a standard pinch/zoom on a CCLayer in cocos2d (using the Kobold2D gesture recognisers) but with only partial success.
Below is my code which does make pinch/zoom work, so long as the pinch point doesn't move. However if I zoom in over one point on the layer and then lift off and move my fingers to zoom in further over another point, there is an instantaneous jump of the layer. It jumps to where the layer would have been if I'd been zooming in over the second point from the start, instead of simply zooming smoothly from where it was.
Can you see what I'm doing wrong or have I missed an existing simple pinch/zoom algorithm that does this job for CCLayers?
NB: I've left the default (YES) value for ignoreAnchorInPosition. Also, at the start self.scalePrePinch = 1.0f
-(void) update:(ccTime)delta
{
KKInput* input = [KKInput sharedInput];
if (input.gesturePinchBegan) {
CGSize scr = [[CCDirector sharedDirector] screenSize];
CGPoint pinchLocation = [self convertToNodeSpace:input.gesturePinchLocation];
CGPoint anchor = ccp(pinchLocation.x/scr.width, pinchLocation.y/scr.height);
CGFloat newScale = input.gesturePinchScale * self.scalePrePinch;
self.anchorPoint = ccp(self.anchorPoint.x + self.scale / newScale * (anchor.x - self.anchorPoint.x),
self.anchorPoint.y + self.scale / newScale * (anchor.y - self.anchorPoint.y));
self.scale = newScale;
}
else
self.scalePrePinch = self.scale;
}
Hi I have finally made a working joystick in cocos2d. I am able to rotate a sprite to the exact angle that the joystick thumb, or cap, is 'pointing'. However, I am unable to move the sprite in that same direction. Is there an easy way to move the sprite with the way I have the rotating code set up? Also is there a way to keep it moving if your thumb is still pressed, but not moving the joystick?. PS this code is all within the TouchesMoved method. PPS. the cap is the thumb, the pad is the joystick background, and the Sprite2 is the sprite that I want to move. (95, 95) is the center of the pad sprite.
if(capSprite.position.x>=padSprite.position.x){
id a3 = [CCFlipX actionWithFlipX:NO];
[sprite2 runAction:a3];
}
if(capSprite.position.x<=padSprite.position.x){
id a4 = [CCFlipX actionWithFlipX:YES];
[sprite2 runAction:a4];
}
CGPoint pos1 = ccp(95, 95);
CGPoint pos2 = ccp(capSprite.position.x, capSprite.position.y);
int offX = pos2.x-pos1.x;
int offY = pos2.y-pos1.y;
float angleRadians = atanf((float)offY/(float)offX);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float theAngle = -1 * angleDegrees;
sprite2.rotation = theAngle;
I'm not familiar with cocos2d but I had a quick look at the documentation and this sample might be of use to you:
if keys[key.UP]:
self.target.acceleration = (200 * rotation_x, 200 * rotation_y)
I had written a long explanation answering your second question but I believe this "self.target.acceleration" solves that too. You can read more at the cocos2d API documentation.
What I generally do is get the angle, convert it to a CGPoint with ccpForAngle(float) and then multiply the CGPoint by a value:
float angle = whatever;
CGPoint anglePoint = ccpForAngle(angle);
// You will need to play with the mult value
angle = ccpMult(angle, 2.5);
// This also works with box2D or probably Chipmunk.
sprite.position = angle;