I am working on a project and what I need to accomplish is to generate random curl line. At this point I have already have a line which is randomly generated but it is not curl. I am spawning points at different x positions on every 0.5 seconds and connect this points with bezier. Everything works perfectly except that it is not curl. On the picture below is shown on 1) How I have it now and on 2) How I suppose to make it. Any ideas how I can do that ? I am using SpriteKit - Objective C
Basically what you need to make and randomize a A Bézier (parametric) curve are control points.
After you read that great answer and get familiar with terms, you can try this example (I assume that your scene and view size are set correctly):
GameScene.m
#import "GameScene.h"
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
}
-(NSInteger)randomNumberBetween:(NSInteger)from to:(NSInteger)to {
return (int)from + arc4random() % (to-from+1);
}
- (CGMutablePathRef)generatePath
{
CGMutablePathRef path = CGPathCreateMutable();
CGPoint p0 = CGPointMake(CGRectGetMidX(self.frame),self.scene.size.height-20.0f); //starting point just little below the upper edge of the screen
CGPathMoveToPoint(path, nil, p0.x, p0.y);
CGPoint p1 =
CGPointMake([self randomNumberBetween:150 to:300], self.scene.size.height- [self randomNumberBetween:150 to:300]);
CGPoint p2 = CGPointMake([self randomNumberBetween:150 to:300],[self randomNumberBetween:150 to:300]);
CGPoint p3 = CGPointMake(CGRectGetMidX(self.frame),0 + 20.0f); //ending point, just little above the bottom edge of the screen
CGFloat v = 0.3;
CGFloat cp1x = p1.x+v * (p1.x-p0.x);
CGFloat cp1y = p1.y+v * (p1.y-p0.y);
CGFloat cp2x = p2.x-v * (p3.x-p2.x);
CGFloat cp2y = p2.y-v * (p3.y-p2.y);
CGPathAddCurveToPoint(path,nil,cp1x,cp1y,cp2x,cp2y,p3.x,p3.y);
/*Debug - not needed*/
SKSpriteNode *sp0 = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(5.0f,5.0f)];
sp0.zPosition = 5;
sp0.position = p0;
SKLabelNode *lp0 = [SKLabelNode labelNodeWithFontNamed:#"ArialMT"];
lp0.fontColor = [SKColor whiteColor];
lp0.fontSize = 20.0f;
lp0.text = #"p0";
lp0.position = CGPointMake(0.0f,-20.0f);
[sp0 addChild:lp0];
[self addChild:sp0];
SKSpriteNode *sp1 = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(5.0f,5.0f)];
sp1.zPosition = 5;
sp1.position = p1;
SKLabelNode *lp1 = [SKLabelNode labelNodeWithFontNamed:#"ArialMT"];
lp1.fontColor = [SKColor whiteColor];
lp1.fontSize = 20.0f;
lp1.position = CGPointMake(0.0f,15.0f);
lp1.text = #"p1";
[sp1 addChild:lp1];
[self addChild:sp1];
SKSpriteNode *sp2 = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(5.0f,5.0f)];
sp2.zPosition = 5;
sp2.position = p2;
SKLabelNode *lp2 = [SKLabelNode labelNodeWithFontNamed:#"ArialMT"];
lp2.fontColor = [SKColor whiteColor];
lp2.fontSize = 20.0f;
lp2.position = CGPointMake(0.0f,15.0f);
lp2.text = #"p2";
[sp2 addChild:lp2];
[self addChild:sp2];
SKSpriteNode *sp3 = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(5.0f,5.0f)];
sp3.zPosition = 5;
sp3.position = p3;
SKLabelNode *lp3 = [SKLabelNode labelNodeWithFontNamed:#"ArialMT"];
lp3.fontColor = [SKColor whiteColor];
lp3.fontSize = 20.0f;
lp3.position = CGPointMake(0.0f,15.0f);
lp3.text = #"p3";
[sp3 addChild:lp3];
[self addChild:sp3];
SKShapeNode *p0p1 = [SKShapeNode node];
p0p1.zPosition = 2;
CGMutablePathRef path1 = CGPathCreateMutable();
CGPathMoveToPoint(path1, NULL, p0.x, p0.y);
CGPathAddLineToPoint(path1, NULL, p1.x, p1.y);
p0p1.path = path1;
[p0p1 setStrokeColor:[UIColor greenColor]];
[self addChild:p0p1];
SKShapeNode *p2p3 = [SKShapeNode node];
p2p3.zPosition = 2;
CGMutablePathRef path2 = CGPathCreateMutable();
CGPathMoveToPoint(path2, NULL, p2.x, p2.y);
CGPathAddLineToPoint(path2, NULL, p3.x, p3.y);
p2p3.path = path2;
[p2p3 setStrokeColor:[UIColor greenColor]];
[self addChild:p2p3];
return path;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self removeAllChildren];
SKShapeNode *yourline = [SKShapeNode node];
yourline.zPosition = 1;
yourline.path = [self generatePath];
[yourline setLineWidth:5];
[yourline setStrokeColor:[SKColor redColor]];
[self addChild:yourline];
}
Most of this code is used for visual debugging and its not needed in order to randomize the curve. The important part is the first part of the method generatePath. Here is the result:
Related
I am trying to get collision working in my SpriteKit game using the didBeginContact function.
My problem is that the function is just not getting called at all when the ball bounces off of the bricks. This is how I have set them both up:
static const uint32_t blockCollisionCheck = 0x1 << 0;
static const uint32_t ballCollisionCheck = 0x1 << 1;
Ball:
SKShapeNode *ball = [[SKShapeNode alloc] init];
CGMutablePathRef drawPath = CGPathCreateMutable();
CGPathAddArc(drawPath, NULL, 0, 0, _ballRadius, 0, M_PI * 2, YES);
ball.path = drawPath;
CGPathRelease(drawPath);
ball.fillColor = [SKColor greenColor];
ball.position = CGPointMake(CGRectGetMidX(self.frame), 150);
ball.name = #"ball";
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_ballRadius];
ball.physicsBody.friction = 0.0;
ball.physicsBody.restitution = 1.0;
ball.physicsBody.linearDamping = 0.0f;
ball.physicsBody.allowsRotation = NO;
ball.physicsBody.dynamic = YES;
ball.physicsBody.categoryBitMask = ballCollisionCheck;
ball.physicsBody.contactTestBitMask = blockCollisionCheck;
[self addChild:ball];
Bricks:
SKSpriteNode *block = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(_blockWidth, _blockHeight)];
block.name = #"block";
block.position = CGPointMake(x, y);
block.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:block.size];
block.physicsBody.allowsRotation = NO;
block.physicsBody.friction = 0.0;
block.physicsBody.dynamic = YES;
block.physicsBody.categoryBitMask = blockCollisionCheck;
block.physicsBody.contactTestBitMask = ballCollisionCheck;
[self addChild:block];
I can't for the life of me see what is wrong with this, as I have the category bit masks correct I think? Also both of the sprites are Dynamic, which is another problem I read it could have been.
It's not that the contents of my didBeginContact function is not working, it's just never getting there as evidenced from a lack of NSLog message and breakpoints not being reached.
Any help would be greatly appreciated.
Thank you.
If, as you say, the didBeginContact method is not being called at all, I suspect you did not add the self.physicsWorld.contactDelegate = self; into your GameScene init method.
In the following example, there are three things on the screen:
ball (a SKShapeNode)
spriteContainer (a SKSpriteNode that contains ball2, a SKShapeNode)
box (a SKSpriteNode)
Why does ball fall out of view? Does a SKShapeNode need to be inside a SKSpriteNode to have physics properly applied to it?
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKColor * warmRed = [SKColor colorWithRed:0.99 green:0.41 blue:0.25 alpha:1.0];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.backgroundColor = warmRed;
//falls out of view
SKShapeNode * ball = [[SKShapeNode alloc] init];
CGMutablePathRef ballPath = CGPathCreateMutable();
CGPathAddArc(ballPath, NULL, size.width-40, self.size.height/2, 20, 0, M_PI*2, YES);
ball.path = ballPath;
ball.lineWidth = 2;
ball.fillColor = warmRed;
ball.strokeColor = [SKColor whiteColor];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:20];
[self addChild:ball];
//lands on bottom of screen
SKShapeNode * ball2 = [[SKShapeNode alloc] init];
CGMutablePathRef ball2Path = CGPathCreateMutable();
CGPathAddArc(ball2Path, NULL, 0, 0, 20, 0, M_PI*2, YES);
ball2.path = ball2Path;
ball2.lineWidth = 2;
ball2.fillColor = warmRed;
ball2.strokeColor = [SKColor whiteColor];
CGSize spriteContainerSize = CGSizeMake(40,40);
CGPoint spriteContainerPosition = CGPointMake(size.width/2, size.height/2);
SKSpriteNode * spriteContainer = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:spriteContainerSize];
spriteContainer.position = spriteContainerPosition;
spriteContainer.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spriteContainerSize];
[spriteContainer addChild:ball2];
[self addChild:spriteContainer];
//lands on bottom of screen
CGSize boxSize = CGSizeMake(40,40);
CGPoint boxPosition = CGPointMake(boxSize.width, size.height/2);
SKSpriteNode * box = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:boxSize];
box.position = boxPosition;
box.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:boxSize];
[self addChild:box];
}
return self;
}
Screenshot:
https://dl.dropboxusercontent.com/u/164157126/example.jpg
Note that you don't set a position for the node which falls off the screen, but set a position for the other two nodes you create.
The default position of a node is 0,0. Your ball will appear at the bottom left of the scene, and since it is over the edge body you defined, will fall off immediately.
Set the ball's position appropriately so that it does not intersect the edge of the screen, and the ball will not fall off.
So I have a shipNode (which is initially facing right) with two children called _laserCannon1(and 2) that are configured by the method below:
-(void)initializeLaserCannonLocations
{
CGSize size = CGSizeMake(4.0, 4.0);
_laserCannon1 = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:size];
_laserCannon2 = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:size];
_laserCannon1.position = CGPointMake(self.size.width/2, self.size.height/2 + 15);
_laserCannon2.position = CGPointMake(self.size.width/2, self.size.height/2 - 15);
[self addChild:_laserCannon1];
[self addChild:_laserCannon2];
}
Then I am later "firing" lasers from them based off of the ship's zRotation as follows
-(void)fireLocalLaser
{
CGVector standardLaserVelocity = CGVectorMake(700*cosf(_myShip.zRotation - _myShip.laserCannon1.zRotation),
700*sinf(_myShip.zRotation - _myShip.laserCannon2.zRotation));
[self spawnLaserFrom:CGPointAdd(_myShip.laserCannon1.position, ship.position); withVelocity:standardLaserVelocity fromShip:_myShip];
[self spawnLaserFrom:CGPointAdd(_myShip.laserCannon2.position, ship.position); withVelocity:standardLaserVelocity fromShip:_myShip];
}
Now everything works fine when the ships is facing directly left or right, but for some odd reason as the ship begins to face more towards the top or bottom the shots are fired increasingly towards the middle of the ship, to the point that once the ship is facing vertically both shots are fired from the middle.position.x of the ship. Does it have to do with something from where I placed my _laserCannons?
I hope my math teacher doesn't see this!
Your problem is with the relative position of your 2 laser cannons as they rotate. I have included a picture so I don't have to go into a long winded description of what is happening.
I have included code for a kinda, sorta, duct tape solution on how you can do this if you really want to. It ain't pretty but it works. The code is good from 0 to 90 degrees so you should be able to work out the rest from there if you have your heart set on having 2 cannons. BUT I would strongly suggest your Mighty Space Empire invest in single cannon ships, as it will make your coding job a whole lot easier!
#implementation MyScene {
SKSpriteNode *ship1;
SKShapeNode *turret1;
SKShapeNode *turret2;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
[self createSpaceships];
}
return self;
}
-(void)createSpaceships {
ship1 = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(50, 50)];
ship1.position = CGPointMake(300, 150);
ship1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ship1.size];
ship1.physicsBody.dynamic = NO;
[self addChild:ship1];
SKSpriteNode *frontOfShip = [SKSpriteNode spriteNodeWithColor:[SKColor grayColor] size:CGSizeMake(2, 50)];
frontOfShip.position = CGPointMake(25, 0);
[ship1 addChild:frontOfShip];
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
// touch the left side of the screen to rotate the ship by +0.0785398 radians
// touch the right hand side of the screen to fire lasers
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
if(touchLocation.x < self.size.width/2) // left side of screen
{
SKAction *block0 = [SKAction runBlock:^{
ship1.zRotation = ship1.zRotation +0.0785398;
}];
[self runAction:block0];
} else // right side of screen
{
float turret1offsetX = 0;
float turret1offsetY = 0;
float turret2offsetX = 0;
float turret2offsetY = 0;
if((ship1.zRotation >0) && (ship1.zRotation <=0.785399))
{
turret1offsetX = (14/0.785398) * ship1.zRotation;
turret1offsetY = (23/0.785398) * ship1.zRotation;
turret2offsetX = (-20/0.785398) * ship1.zRotation;
turret2offsetY = (10/0.785398) * ship1.zRotation;
}
if((ship1.zRotation >0.785399) && (ship1.zRotation <=1.570797))
{
turret1offsetX = (14 - (9/0.785398)) * ship1.zRotation;
turret1offsetY = -4 + (27/0.785398) * ship1.zRotation;
turret2offsetX = (3 - (25/0.785398)) * ship1.zRotation;
turret2offsetY = 20 - (10/0.785398) * ship1.zRotation;
}
int x = ship1.position.x + 1000 * cos(ship1.zRotation);
int y = ship1.position.y + 1000 * sin(ship1.zRotation);
turret1 = [SKShapeNode node];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, (ship1.position.x+20)+turret1offsetX, (ship1.position.y-25)+turret1offsetY);
CGPathAddLineToPoint(pathToDraw, NULL, x, y);
turret1.path = pathToDraw;
[turret1 setStrokeColor:[UIColor redColor]];
[self addChild:turret1];
turret2 = [SKShapeNode node];
CGMutablePathRef pathToDraw2 = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw2, NULL, (ship1.position.x+20)+turret2offsetX, (ship1.position.y+25)+turret2offsetY);
CGPathAddLineToPoint(pathToDraw2, NULL, x, y);
turret2.path = pathToDraw2;
[turret2 setStrokeColor:[UIColor redColor]];
[self addChild:turret2];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[turret1 removeFromParent];
[turret2 removeFromParent];
}
#end
Yes as sangony put it, it really just has to do with your relative positioning of the _laserCannons from your shipNode. I would personally just start with a singular laserCannon for now, and get more comfortable with the math involved until you feel ready to move on to adding more.
I want to make a soccer ball by IOS SpriteKit
I was able to create a ball in the code such as the following.
However, it does not bounce at all.
To make rebound rate, How can I do this?
(UITouch *touch in touches) {
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Ball"];
sprite.position = location;
const CGFloat radius = 20.0f;
SKPhysicsBody *pbody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
sprite.physicsBody = pbody;
sprite.size = (CGSize){radius * 2, radius * 2};
sprite.physicsBody.linearDamping=0.12f;
[self addChild:sprite];
}
I have declared like this.
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:(CGRect){CGPointZero, size}];
self.physicsWorld.gravity=CGVectorMake(0, -100);
}
return self;
}
This looks like a ball to me
- (void) soccerBallExample
{
// the world bounds
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];
// the ball
SKShapeNode *ball;
ball = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
CGPathAddArc(myPath, NULL, 0,0, 30, 0, M_PI*2, YES);
ball.path = myPath;
CGPathRelease(myPath);
ball.fillColor = [SKColor blueColor];
ball.position = CGPointMake(200, 200);
[self addChild:ball];
// set the physics
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:30];
ball.physicsBody.restitution = 0.8;
}
In your example, you use a sprite so for radius use
// ball.size.width * 0.5;
I'm testing out pin joints with Sprite Kit, and I'm finding something unusual happening.
My desired setup is this: one wide, flat box, and two circles; the circles are connected via SKPhysicsPinJoints to the box, so they can act as wheels.
Here's my code. I've tried to make it as concise as possible:
- (SKNode*) createWheelWithRadius:(float)wheelRadius {
CGRect wheelRect = CGRectMake(-wheelRadius, -wheelRadius, wheelRadius*2, wheelRadius*2);
SKShapeNode* wheelNode = [[SKShapeNode alloc] init];
wheelNode.path = [UIBezierPath bezierPathWithOvalInRect:wheelRect].CGPath;
wheelNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:wheelRadius];
return wheelNode;
}
- (void) createCar {
// Create the car
SKSpriteNode* carNode = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(150, 50)];
carNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carNode.size];
carNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:carNode];
// Create the left wheel
SKNode* leftWheelNode = [self createWheelWithRadius:30];
leftWheelNode.position = CGPointMake(carNode.position.x-80, carNode.position.y);
[self addChild:leftWheelNode];
// Create the right wheel
SKNode* rightWheelNode = [self createWheelWithRadius:30];
rightWheelNode.position = CGPointMake(carNode.position.x+80, carNode.position.y);
[self addChild:rightWheelNode];
// Attach the wheels to the body
CGPoint leftWheelPosition = leftWheelNode.position;
CGPoint rightWheelPosition = rightWheelNode.position;
SKPhysicsJointPin* leftPinJoint = [SKPhysicsJointPin jointWithBodyA:carNode.physicsBody bodyB:leftWheelNode.physicsBody anchor:leftWheelPosition];
SKPhysicsJointPin* rightPinJoint = [SKPhysicsJointPin jointWithBodyA:carNode.physicsBody bodyB:rightWheelNode.physicsBody anchor:rightWheelPosition];
[self.physicsWorld addJoint:leftPinJoint];
[self.physicsWorld addJoint:rightPinJoint];
}
What I'm expecting is that the pin joints are anchored at their centre points; however, when I test this, the anchors for the joints appear to be far off.
Am I missing something really obvious?
I also had this issue and the cause is setting the physics body before setting the sprites position.
carNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carNode.size];
carNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
Change the above to
carNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
carNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carNode.size];
It should work. Thanks Smick.
SpriteKit: How to create Basic Physics Joints
Try this code, I used yours and got weird issues, so started from scratch.
- (SKShapeNode*) makeWheel
{
SKShapeNode *wheel = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
CGPathAddArc(myPath, NULL, 0,0, 16, 0, M_PI*2, YES);
wheel.path = myPath;
return wheel;
}
- (void) createCar
{
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];
// 1. car body
SKSpriteNode *carBody = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(120, 8)];
carBody.position = CGPointMake(200, 200);
carBody.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carBody.size];
[self addChild:carBody];
// 2. wheels
SKShapeNode *leftWheel = [self makeWheel];
leftWheel.position = CGPointMake(carBody.position.x - carBody.size.width / 2, carBody.position.y);
leftWheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:16];
[self addChild:leftWheel];
SKShapeNode *rightWheel = [self makeWheel];
rightWheel.position = CGPointMake(carBody.position.x + carBody.size.width / 2, carBody.position.y);
rightWheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:16];
[self addChild:rightWheel];
// 3. Join wheels to car
[self.physicsWorld addJoint:[SKPhysicsJointPin jointWithBodyA:carBody.physicsBody bodyB:leftWheel.physicsBody anchor:leftWheel.position]];
[self.physicsWorld addJoint:[SKPhysicsJointPin jointWithBodyA:carBody.physicsBody bodyB:rightWheel.physicsBody anchor:rightWheel.position]];
// 4. drive car
[carBody.physicsBody applyForce:CGVectorMake(10, 0)];
}