I am trying to reset the ball after it hit the goal to the middle of the screen, but it seems I can no longer set it's position anymore after i declare it's position during declaration.
This is how i define my ball:
-(void)setUpBall {
self.ball = [SKSpriteNode spriteNodeWithImageNamed:#"Ball"];
self.ball.name = bName;
self.ball.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
self.ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.ball.frame.size.width/2];
self.ball.physicsBody.restitution = 1.0;
self.ball.physicsBody.angularDamping = 0.0;
self.ball.physicsBody.linearDamping = 0.0;
self.ball.physicsBody.friction = 0.0;
self.ball.physicsBody.dynamic = true;
self.ball.physicsBody.allowsRotation = false;
self.ball.physicsBody.usesPreciseCollisionDetection = true;
self.ball.physicsBody.categoryBitMask = bCategory;
self.ball.physicsBody.collisionBitMask = pCategory | borderCategory;
self.ball.physicsBody.contactTestBitMask = borderCategory;
[self addChild:self.ball];
[self.ball.physicsBody applyImpulse:CGVectorMake(15, 10)];
}
This is my code to reset the ball to middle of the screen
-(void)reset {
NSLog(#"reset method ran");
[self.ball setPosition: CGPointMake(self.size.width/2, self.size.height/2)];
}
It seems that the reset method is definitely ran since there was output from the NSLog(#"reset method ran"); but the ball always continue moving like the setPosition never work at all
Doing things like setting position might not work in Sprite Kit if it's a result of a call from testing collisions or other things. You may need to add a BOOL property to your ball to flag it if you need to reset it, and then call the reset when you are updating all your sprites.
I have an asteroids type game where I was trying to replace one large asteroid with 3 smaller rocks when the collision detection against a player's missile fired – it did not work because the position was being set during the collision check part of the Sprite Kit update loop. When I added the new rocks during the standard part of the update loop, it worked fine.
Yopu could simply set up the ball again.
-(void)reset
{
NSLog(#"reset method ran");
[self.ball removeFromParent];
[self setUpBall];
}
Could you not replace your reset method with:
-(void)reset {
NSLog(#"reset method ran");
self.ball.position = CGPointMake(self.size.width/2, self.size.height/2)];
}
I tend to use this and not the setPosition method to relocate a sprite.
Related
I have a player character and a ground tile. On both physics bodies, I have restitution set to 0.
To jump, I directly set the velocity.dy of the player. When the player comes back to the ground due to gravity, for the most part it works, or seems to.
Problems occur when I repeatedly jump. If I jump right as the player lands, there's some bounce and the height the player reaches on the next bounce doesn't always match the initial bounce.
I've tried various ways to force the velocity.dy to 0 when the user lands, but nothing fixes the weird jumping issue. How can I properly and smoothly have a consistent physics jump?
Honestly, I am not sure what you are trying to accomplish. Normally we shouldn't mess with velocities of objects. In a typical Spritekit game, we must treat it like a "real world" situation and generally apply force or impulse on the object.
I suspect you are trying to make a Mario-like game. All you have to do is apply a big enough gravity to the physicsworld and apply impulse on the sprite to jump (on touchesBegan delegate).
Just now I went ahead and made a simple mario jump scenario in Spritekit. And this is what I ended up with by setting gravity -30 for the y-component and impulse y=100 on the mario sprite. (Frame rate is bad. Looks better on simulator/device)
PhysicsWorld setup:
[self.physicsWorld setGravity:CGVectorMake(0, -30)];
self.physicsWorld.contactDelegate = self;
Mario and platform sprite setup code:
SKSpriteNode *platform = [SKSpriteNode spriteNodeWithImageNamed:#"platform"];
platform.position = CGPointMake(view.frame.size.width/2.0,0);
platform.name = #"platform";
platform.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:platform.frame.size];
platform.physicsBody.categoryBitMask = platformCategory;
platform.physicsBody.dynamic = NO;
platform.physicsBody.usesPreciseCollisionDetection = YES;
platform.physicsBody.affectedByGravity = NO;
[self addChild:platform];
SKSpriteNode *mario = [SKSpriteNode spriteNodeWithImageNamed:#"mario"];
mario.position = CGPointMake(view.frame.size.width/2.0, 400);
mario.name = #"mario";
mario.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mario.frame.size];
mario.physicsBody.categoryBitMask = marioCategory;
mario.physicsBody.dynamic = YES;
mario.physicsBody.usesPreciseCollisionDetection = YES;
mario.physicsBody.contactTestBitMask = platformCategory;
mario.physicsBody.affectedByGravity = YES;
[self addChild:mario];
touchesBegan:
SKSpriteNode *mario = (SKSpriteNode*)[self childNodeWithName:#"mario"];
[mario.physicsBody applyImpulse:CGVectorMake(0, 100)];
I develop an iOS game using SpriteKit (such a helpful framework to quickly make a game). I add texture and configure a physical body for a main character as image
The green rectangle is the frame of the physical body. I'm using the following code to create it
#interface MainCharacter : SKSpriteNode
#end
#implementation MainCharacter
+ (instancetype)mainCharacterAtPosition:(CGPoint)pos {
MainCharacter* mainChar = [[MainCharacter alloc] initWithTexture:[SKTexture textureWithImageNamed:#"stand_up"]];
mainChar.position = pos;
mainChar.xScale = 0.5f;
mainChar.yScale = 0.5f;
return mainChar;
}
- (instancetype)initWithTexture:(SKTexture *)texture {
if (self = [super initWithTexture:texture]) {
self.name = kCharacterName;
self.anchorPoint = CGPointMake(0.5f, 0.0f);
[self standup];
CGSize spriteSize = self.size;
CGPoint center = CGPointMake(spriteSize.width*(self.anchorPoint.x-0.5f), spriteSize.height*(0.5f-self.anchorPoint.y));
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spriteSize center:center];
self.physicsBody.dynamic = NO;
self.physicsBody.categoryBitMask = kCharacterCategory;
self.physicsBody.contactTestBitMask = 0x0;
self.physicsBody.collisionBitMask = 0x0;
}
return self;
}
- (void)standup {
SKAction* standupAction = [SKAction setTexture:self.standupTexture resize:YES];
[self runAction:standupAction];
}
- (void)standdown {
SKAction* standownAction = [SKAction setTexture:self.standdownTexture resize:YES];
[self runAction:standownAction completion:^{
}];
[self performSelector:#selector(standup) withObject:nil afterDelay:1.0f];
}
MainCharacter is a class that inherits from SKSPriteNode, just an convienient class to manage a main character. Stand Up is a first state of the character. I have another state, temporarily called stand down (demonstrate as following image)
I add a swipe down gesture to make character stand down.
The green rectangle also the physical body but it's too large for the character. I want to make a physical body frame as the red rectangle.
Can anyone help me how to make the physical body smaller when my character stand down and enlarge the physical body after it stands up
You can destroy the current physics body self.physicsBody = nil; and then simply create a new one with the new size requirements.
I solve this problem by using 2 nodes for 2 states (as a suggestion): stand up state and stand down state. I named it
standupNode and standdownNode
First, add the standupNode to the game scene. If swipe donw gesture recognize, I remove the standupNode from game scene and add the standdownNode instead. On contrary, removing the standdownNode from the game scene then add the standupNode if character stands up
I'm programming a little Game. For this I need some Walls. Therefor I have used:
Wall[w] = [[SKShapeNode alloc] init];
Wall[w].path = WallPos[w];
Wall[w].lineWidth = 4;
Wall[w].strokeColor = [UIColor whiteColor];
Wall[w].zPosition = 3;
[self addChild: Wall[w]];
Wall is an Array of SKShapeNodes and is set in #interface, so I can use it in every method. WallPos contains CGMutablePathRefs.
In TouchesBegan and TouchesMoved I'm calling a method which should check if you have touched one of the walls.
I have also some SKShapeNodes which are Rectangles, and to check if they are touched, I have used
if ([SomeShape containsPoint: Position] {
//Do some stuff
}
But with a line it's not working. Sometimes I'm touching on the line and nothing happens. Then I've seen this: Detecting Touch on SKShapeNode that is a Line and I have tried to do it on that way:
for (int i = 0; i < NrWalls; i++) {
if (CGRectContainsPoint(Wall[i].frame, Position)) {
[self GameOver];
}
}
But now every Point I touch sets a "Game Over" to me!!
Has anyone an Idea, how could I check if the line is touched?
Thanks for your help!
DXXL
Not sure why you want to use SKShapeNodes for walls and rectangles. To answer your question, you can attach a physics body to your shape node and use the contact methods to check for possible contacts. However, assigning a physics body to a shape node could be a tricky undertaking due to the anchor points and getting a desired alignment.
Seeing that you are really only drawing rectangles for your walls, I suggest you use a SKSpriteNode with a physics body instead. Something like this:
SKSpriteNode *myNode = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(5, 100)];
myNode.position = CGPointMake(100, 100);
myNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:myNode.size];
myNode.physicsBody.dynamic = NO;
myNode.physicsBody.categoryBitMask = CategoryWall;
[self addObject:myNode];
If you need, you can read up on SKPhysicsBody here.
I'm having a little trouble implementing a background change when the player hits a certain score.
I'm using the if(self.score>=10) line to tell my game to change the background but it doesn't seem to be working. I have no errors with this line but no results either.
What I have:
#implementation Scene{
SKScrollingNode * floor;
SKScrollingNode * back;
SKLabelNode * scoreLabel;
}
- (void) createBackground
{
back = [SKScrollingNode scrollingNodeWithImageNamed:#"back" inContainerWidth:WIDTH(self)];
if(self.score>=10){
back = [SKScrollingNode scrollingNodeWithImageNamed:#"image2" inContainerWidth:WIDTH(self)];
[back setAnchorPoint:CGPointZero];
[back setPhysicsBody:[SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]];
back.physicsBody.categoryBitMask = backBitMask;
back.physicsBody.contactTestBitMask = birdBitMask;
[self addChild:back];
}
there are certain points you have to keep in your mind i think your both background contains physics body
1)may be and the time of transition they first background colliding with second background and they preventing each other to swap
change your collisionBitMask for both background ->prevent them for colliding
for eg
bg.physicsBody.categoryBitMask=bg1;
bg.physicsBody.collisionBitMask=0;
bg.physicsBody.contactTestBitMask=bg2;
bg.physicsBody.categoryBitMask=bg2;
bg.physicsBody.collisionBitMask=0;
bg.physicsBody.contactTestBitMask=bg1;
or use skaction to swap backgrounds
The way i would do this would be to create a brand new SKScene entirely with a new background.
Or if you want them to scroll to the next background. I would create two backgrounds.
Ex.
//this syntax is wrong (dont remember)
//initializing
SKSpriteNode *bg1 = [SKSpritenode spritenodefromImage:#"bg1"];
SKSpriteNode *bg1 = [SKSpritenode spritenodefromImage:#"bg1"];
bg1.position = CGPointMake(self.size.width/2, self.size.height/2);
bg2.position = CGPointMake(self.size.width/2 + bg1.size.width, self.size.height/2);
//////////////////////////////
//then when you want to scroll them
-(void)scrollBackgrounds
{
bg1.position = CGPointMake(bg1.position.x - 5, self.size.height/2);
bg2.position = CGPointMake(bg2.position.x -5 , self.size.height/2);
}
I am trying to create a game where a character runs forever to the right (the game is landscape). On the ground there are spikes that the character can jump over. Currently, I am creating a new (and somewhat random) set of spikes in almost a checkpoint-like style where once the character reaches a certain distance, the next set of randomly organized spikes are created and the checkpoint distance gets pushed back and so on. Along with the spikes, I have a separate but very similar checkpoint-like system that is used to create the tiles that make up the ground.
This is my code for that portion, 'endlessX' and 'endlessGroundX' are the checkpoint value:
- (void) didSimulatePhysics {
if (player.position.x > endlessX) {
int random = player.position.x + self.frame.size.width;
[self createSpike:random];
endlessX += self.frame.size.width/2.2 + arc4random_uniform(30);
}
if (player.position.x + self.frame.size.width > endlessGroundX) {
[self createGround:endlessGroundX];
endlessGroundX += tile1.frame.size.width;
}
[self centerOnNode: player];
}
The parameter of the createSpike and createGround method is just the 'x' value for the SKSpriteNodes.
I am currently having it as the character itself is the one moving and the spikes and tiles are stationary. This is how I am creating the character:
-(void) createPlayer {
player = [SKSpriteNode spriteNodeWithImageNamed:#"base"];
player.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
player.name = #"player";
player.zPosition = 60;
player.xScale = 0.8;
player.yScale = 0.8;
player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:player.frame.size.height/2];
player.physicsBody.mass = 1;
player.physicsBody.linearDamping = 0.0;
player.physicsBody.angularDamping = 0.0;
player.physicsBody.friction = 0.0;
player.physicsBody.restitution = 0.0;
player.physicsBody.allowsRotation = NO;
player.physicsBody.dynamic = YES;
player.physicsBody.velocity = CGVectorMake(400, 0);
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.collisionBitMask = wallCategory;
player.physicsBody.contactTestBitMask = wallCategory | spikeCategory;
[myWorld addChild:player];
}
With that, the character will never lose any of its kinetic energy to friction or any other force like that. Then, I am using the 'center on node' method that apple used in their adventure game so that the character will always remain in the same x-position on the screen:
- (void) centerOnNode: (SKSpriteNode *) node {
CGPoint cameraPositionInScene = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(self.frame.size.width/5 + node.parent.position.x - cameraPositionInScene.x, node.parent.position.y);
}
I am calling this method in 'didSimulatePhysics.'
When I run this for some time, the programs gets slower and slower. I am guessing that that is due to the fact that I am never removing these nodes and they are always being added. However, to fix this problem, I tried doing something like this:
-(void)update:(CFTimeInterval)currentTime {
[self enumerateChildNodesWithName:#"*" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x + 50 < player.position.x) {
[node removeFromParent];
}
}];
}
(the +50 would be just to make sure that the node is off the screen before removing it)
However, when I did this, instead of removing the specific node that satisfies the 'if' statement, the program removes all of the sprite nodes. Is there a different method or something that I am missing to fix this? Or are there any other simple ways to remove the specific nodes?
Lacking quite a few details, like how you are animating the spikes for instance, makes it a bit hard to be too specific. Nevertheless, from what you are sharing I guess you might be looking for something a little like this:
SKAction *moveSpikeAction = [SKAction moveToX:-50 duration:5];
SKAction *removeSpikeAction = [SKAction removeFromParent];
SKAction *spikeSequence = [SKAction sequence:#[moveSpikeAction, removeSpikeAction]];
[yourSpikeSpriteNode runAction:spikeSequence];
The idea simply being that when the spike has animated to the off screen position you use the removeFromParent action to clear it.