Moving sprite "smoothly" - sprite-kit

I'm trying to move a sprite in a set direction on-screen, using the physicsBody.velocity property. However, the motion is quite "jumpy" (makes the game look like it's lagging). I assume this has something to do with the different time intervals between each call to the update-method. How could I achieve a more "smooth" motion?
Right now this is the code I have in my update-loop to make the sprite move forward:
if (_movingForward) {
CGFloat forwardVel = _ball.physicsBody.velocity.dx;
forwardVel += 20.0;
if (forwardVel > bMaxForwardVelocity) {
forwardVel = bMaxForwardVelocity;
}
_ball.physicsBody.velocity = CGVectorMake(forwardVel, _ball.physicsBody.velocity.dy);
}
As you can see, I'm trying to make the speed pick up, so it doesn't immediately reach it's terminal velocity. This is to make the motion look more natural. However, the lagging is ruining this effect greatly, even when the sprite supposedly is traveling at a set speed.
EDIT 1:
I'm trying to make a platform-style game (like Super Mario etc) where the _ball moves horizontally along the ground when the user taps the screen. The code for moving the ball to the right is included above the edit. The ground is made up of multiple tiles, each 32x32 and positioned right next to each other with no gap in-between.
As per request, here's the code for setting up the _balls physicsBody:
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.size.width/2.0];
self.physicsBody.dynamic = YES;
self.physicsBody.restitution = 0.5f;
self.physicsBody.usesPreciseCollisionDetection = YES;
self.physicsBody.allowsRotation = NO;
self.physicsBody.mass = 0.5f;
self.physicsBody.friction = 0.0f;
self.physicsBody.linearDamping = 0.0f;
self.physicsBody.categoryBitMask = CollisionCategoryBall;
And here's the code for the physicsBody of each individual tile:
tile.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:tile.size];
tile.physicsBody.dynamic = NO;
tile.physicsBody.categoryBitMask = CollisionCategoryGround;
The physicsWorld is set up like this:
self.physicsWorld.gravity = CGVectorMake(0, -4);
self.physicsWorld.contactDelegate = self;
Thanks!
EDIT 2:
This is a side-scrolling game, so the "camera" follows the ball. Here's the code for moving the background layer:
- (void)centerViewOn:(CGPoint)centerOn {
CGFloat x = Clamp(centerOn.x, (self.size.width / 2.0), (_bgLayer.layerSize.width) - (self.size.width / 2.0));
_worldNode.position = CGPointMake((int)-x, _worldNode.position.y);
}
The reason for the Clamp is so the "camera" doesn't scroll beyond the bounds of the background. This is when the ball is close to either the start or end of the level.
I'm casting the position to an int because without it, small gaps sometimes occur between the tiles. I was told this was because of some inaccuracy or something. However, even without casting the position to int, it's still a bitt "laggy". But because of the gaps between the tiles, it looks even worse.

The issue was with your scrolling code.
change this line :
_worldNode.position = CGPointMake((int)-x, _worldNode.position.y);
to
_worldNode.position = CGPointMake(-x, _worldNode.position.y);
The movement is then silky smooth.
But then you do get those seams. You should solve that in the placement of those tiles. You are likely positioning your _worldNode tiles with float values.

Related

Move SKSpriteNode with Physics

I am trying to move an SKSpriteNode with physics by setting it's velocity. I am doing this on top of another SKNode (the map) that is part of the SKScene. Positions of "cities" are stored in custom Location objects.
I know the location of the initial SKSpriteNode (the ship) and I know the desired location. From what I've read, I can move the sprite by setting it's velocity. I do so like this:
float dy = _player.desiredLocation.yCoordinate - _mapNode.pin.position.y;
float dx = _player.desiredLocation.xCoordinate - _mapNode.pin.position.x;
_mapNode.pin.physicsBody.velocity = CGVectorMake(dx, dy);
This is all inside the didSimulatePhysics function inside the SKScene. Once the user taps a Location, I find the position and set the velocity. This seems to work well the FIRST time, but the sprite moves all over the place in subsequent times. Any idea what could be going wrong here?
PS: Setting skView.showsPhysics = YES; puts the circle of the physics body way off the position of the sprite.
In the map node:
self.pin = [SKSpriteNode spriteNodeWithImageNamed:[IPGameManager sharedGameData].world.player.ship.type];
self.pin.userInteractionEnabled = NO;
self.pin.position = CGPointMake([IPGameManager sharedGameData].world.player.location.xCoordinate, [IPGameManager sharedGameData].world.player.location.yCoordinate);
self.pin.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10.0];
self.pin.physicsBody.dynamic = YES;
self.pin.physicsBody.allowsRotation = NO;
self.pin.physicsBody.friction = 0.0;
self.pin.physicsBody.linearDamping = 0.0;
[self addChild:self.pin];

Is there a way to make a soft body with Spritekit using SKSpriteNode and PhysicsBody parameters?

I have the following sprite that falls to the bottom of the screen:
// The View
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.contactDelegate = self;
// The Sprite
SKSpriteNode *cube = [[SKSpriteNode alloc] initWithImageNamed:#"cube"];
[cube setPosition:CGPointMake(160,250);
[self addChild:cube];
// The Physics
cube.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:cube.frame.size];
cube.physicsBody.dynamic = YES;
cube.physicsBody.affectedByGravity = YES;
cube.physicsBody.mass = 0.02;
Is there a way to make it so its sides are bulging when it hits the bottom border of the screen? Something that would be Jelly like or a soft body that still maintains its shape to some extent but bulges out under its own weight? I hope this makes sense....
Visit the site https://gist.github.com/kazukitanaka0611/4b9d4ac9dff0cd317b6c it have explanation and source code for soft bodies (jelly) in sprite kit
quick and easy way without math:
1 use flash to tween your box warping.
2 export the tweened frames as a sprite sheet (texture atlas)
3 animate the texture atlas on contact with an edge physics body in your scene.
your box will fall and on contact animate the separate images to give the impression its warping/bulging sides.
i used this method and it works - in other words it gives the desired effect, which in my view is what is important - your gamers don't care how you did it, as long as it looks great.

Reanimating a bouncing ball in sprite-kit

Reanimating a bouncing ball in sprite-kit
I have a question about the best way to reanimate a bouncing ball. Using Ray Wenderlich's tutorial on building a break out game as a reference I have a number of circular shape nodes which continually bounce around a gravity-less environment. When the user touches one of them, it centers on the screen, stops moving, and transforms into something else. When the user touches it again, it is supposed to transform back into the circular shape node and then continue bouncing around the screen.
I have it all working as I like except for reanimating the ball. The manner in which I stop the animation is simply to set the physicsBody to nil. Once I want to reanimate, I call the method that sets up the physicsBody for the node and I apply an impulse to it. But frustratingly, the impulse does not have the effect I think it should. Clearly, my expectations are incorrect.
Here is the code in question:
- (void)applyPhysics
{
// Set physics properties
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.frame.size.width/2];
self.physicsBody.friction = 0.0f;
self.physicsBody.restitution = 1.0f;
self.physicsBody.linearDamping = 0.0f;
self.physicsBody.allowsRotation = NO;
self.physicsBody.collisionBitMask = wallCategory;
self.physicsBody.categoryBitMask = bubbleCategory;
}
- (void)morphPicker
{
BOOL check = NO;
if (check) NSLog(#"morphPicker called.");
[self runAction:[SKAction fadeAlphaTo:0.0 duration:.5]];
if (self.flipped) {
if (check) NSLog(#"The information side is showing.");
self.fillColor = bubbleColors[self.index % bubbleColors.count];
self.alpha = .9;
// Resume physics:
[self applyPhysics];
[self.physicsBody applyImpulse:CGVectorMake(10.0f, -10.0f)];
} else {
// stop physics:
self.physicsBody = nil;
if (check) NSLog(#"The statistic side is showing.");
self.fillColor = [SKColor blackColor];
self.alpha = .9;
[self transformToPickerNode];
[self removeAllChildren];
}
self.flipped = !self.flipped;
}
So my questions are these:
How should I be reanimating the ball if applying an impulse to it is not the correct method?
Is there a more elegant way to stop the animation after centering the node than setting the physicsBody to nil? Even to my novice instincts it feels like a brutish method.
After all my trials and searching for an answer to this I firmly believe that there is no elegant way to apply a new impulse to a sprite and expect it to move in the physics world. Thus my answer must be "it cannot be done at this point." However, my solution of removing all the the animated sprites from the scene and recreating them turned out to be the best solution for a completely different reason. The nature of my physics world caused my "floating bubbles" to eventually end up in the corners of the scene where they remained unmoving. Occasionally removing and recreating them had the effect of sending them happily bouncing around the scene again :)

Position on UIImageView

Me again. I have a simple question. I have an UIImageView like the one shown below.
alt text http://img683.imageshack.us/img683/1999/volumen.png
That UIimageView is supposed to be the knob to control the volume of my iphone project. My question is, how to know the positions of bar on the UIImageView when it is rotated? Because the volume needs to be 0.5 when the little bar on the cercle is vertical.
I got a piece of code which is (in the touchMoved method):
float dx = locationT.x - imgVVolume.center.x;
float dy = locationT.y - imgVVolume.center.y;
CGFloat angleDif = 0.0f;
movedRotationAngle = atan2(dy,dx);
if (beganRotationAngle == 0.0) {
beganRotationAngle = movedRotationAngle;
initialTransform = imgVVolume.transform;
}
else {
angleDif = beganRotationAngle - movedRotationAngle;
CGAffineTransform newTrans = CGAffineTransformRotate(initialTransform, -angleDif);
imgVVolume.transform = newTrans;
}
Help please.
It depends on what input mechanism you want to use to control the rotation.
If the knob is to rotate based on a single finger touch dragging from side to side then you can create a UIPanGestureRecognizer and attach it to the knob UIImageView. The translationInView: method returns a CGPoint which is the amount of X and Y movement from the touch-down point. You can feed that into a formula like the one you post to get an angle of rotation. You'll want to keep track of delta from last position and also check for stop limits (like 0..360) to prevent over-rotation.
OTOH, if you're going to use two finger rotation then you'll want to use a UIRotationGestureRecognizer and look for the rotation value. Just feed that into a CGAffineTransformRotate and set it to the UIImageView transform. That takes care of all of the above for you. Again, you'll want to check for stop limits.

Problem with smoke bubble in cocos2d

I am having a problem. When the smokeMoveBy action starts a small smoke bubble is spotted on the screen at other place then the moving path of the smoke.
This is only happening when I am using scaleX and scaleY.
The method smokeLoop is being called at each 1 seconds in the scheduler.
Here self is a layer.
Any solutions?
My code follows,
CGPoint dummyPosition=ccp(600, 600);
ParticleSystem *smoke = [ParticleSmoke node];
ccColor4F startColor;
startColor.r = 1.f;
startColor.g = 1.f;
startColor.b = 1.f;
startColor.a = 1.f;
[smoke setStartColor:startColor];
ccColor4F endColor;
endColor.r = 0.8f;
endColor.g = 0.8f;
endColor.b = 0.8f;
endColor.a = 1.0f;
[smoke setEndColor:endColor];
[smoke setLife:0.1f];
[smoke setScaleX:0.1f];
[smoke setScaleY:0.2f];
[smoke setStartSize:30.f];
[self addChild:smoke z:2];
[smoke setPosition:dummyPosition];
-(void)smokeLoop{
id smokeMoveBy = [MoveBy actionWithDuration:durTime position:ccp(0.f, (-1.f*480))]];
id smokeSeq=[Sequence actions:[Place actionWithPosition:smokeInitPosition], smokeMoveBy, nil];
[smoke runAction:smokeSeq];
}
Not sure if this is your issue, but I had an issue with Cocos2D, scaling and moving, which I solved with moving the anchorPoint.
What I wanted to do is zoom (scale) and move a layer. Zooming would move fine if position was {0,0} and transform point was {0.5,0.5}. But then if I'd move it, it would still transform around {0.5,0.5}, which might by then be out of screen, so it would scale really weird.
Solution was to move the transform point to the middle of the screen, every time I moved the layer's position. This was not an easy formula for me to fix, as when I moved the transform point, the scale operation would have a new center point.
The formula I ended up using was the following:
layer = self.foreground;
ccpAdd(
ccpDivide(
ccpNeg(layer.position),
(CGPoint){layer.contentSize.width, layer.contentSize.height}),
(CGPoint){0.5f,0.5f}
);
Basically: Divide the inverse of the layers position (meaning, {300,200} would become {-300,-200}) by the size of the layer {480,320}, and then add {0.5,0.5} (as I want my anchor to be always center + offset)
Anyway, you may need to work out a completely different formula, but this worked for me. I had to apply this to my anchor point every time I moved the layer.
Good luck, hope this helps!