I am building a game with Xcode's spritekit. Its a platform game and right now it functions well with flat ground pieces. I was wondering if it was possible to ignore transparency in the png for collisions. That is to say, If i have a ground piece with a curved floor and transparency filling the troughs, can i make the player walk the curves instead of a square bounding box covering the whole thing? The only example i can find is in the Gamemaker GML language, you can do "precise" collisions such that blank space in the images do not count as part of the sprite. I can supply code if necessary but this seems like more of a conceptual question. Thanks in advance
Hey there's a easy solution for this provided in the Apple Documentation.
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
sprite.physicsBody = [SKPhysicsBody bodyWithTexture:sprite.texture size:sprite.texture.size];
This creates a physics body around the physical paths of the texture.
Simulating Physics - SpriteKit Programming Guide
It's all in how you instantiate the physicsBody of the node in question.
node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node.size];
is probably the easiest and most common way of making a physicsBody, but will also create the issue you've identified above, because, for all collision purposes, the node is a rectangle.
take a look at the documentation for SKPhysicsBody to see the other options available to you. PolygonFromPath and BodyWithBodies are probably the best suited for what you're doing.
SKPhysicsBody is the right move, just wanted to note that it does make sense to have a separate (simplified) mask-image for SKPhysicsBody to improve your performance, simplified from color and geometry standpoint, and here's the code:
let birdMask: UInt32 = 0x1 << 0
let pipeMask: UInt32 = 0x1 << 1
//...
pipeImage = SKSpriteNode(imageNamed: "realImage")
//... size and position
let maskTexture = SKSpriteNode(imageNamed: mask)
maskTexture.size = pipeImage!.size // size of texture w/ real imageNamed
pipeImage!.physicsBody?.usesPreciseCollisionDetection = true
pipeImage!.physicsBody = SKPhysicsBody(texture: maskTexture.texture!, size: size)
pipeImage!.physicsBody?.affectedByGravity = false // disable falling down...
pipeImage!.physicsBody?.allowsRotation = false
pipeImage!.physicsBody?.isDynamic = true
pipeImage!.physicsBody?.friction = 0
pipeImage!.physicsBody?.categoryBitMask = pipeMask
pipeImage!.physicsBody?.collisionBitMask = birdMask | pipeMask
pipeImage!.physicsBody?.contactTestBitMask = birdMask | pipeMask
and more detailed example/guide.
Related
I am making a game with sprites that move and obstacles to learn SpriteKit. I want the sprites that move to collide with the obstacles and bounce off of them but I want the obstacles to stay fixed. How do I do this? I have tried the following with no success:
Setting obstacle.physicsBody?.isDynamic = true. This made the sprites go through the obstacle.
Fixing the movement and rotation of the object with SKConstraint. When I do this they just go through each other.
Setting the mass of the body to be really high as follows obstacle.physicsBody?.mass = CGPoint.maxFiniteMagnitude but this freezes the game. When I set it really high it doesn't seem to do anything.
Setting obstacle?.physicsBody.velocity = CGVector(dx: 0, dy: 0) when the objects collide. I know that the contact.bodyA and contact.bodyB are passed by value and not reference so I loop through an array with the obstacles and set the velocity this way. The obstacles are still pushed by the other sprites.
Update:
- Setting obstacle.physicsBody?.collisionBitMask = PhysicsCategory.none so the sprite collides with the obstacle but not the other way around.
The object is setup as follows, with fish being the other sprite:
obstacle.position = location
obstacle.physicsBody = SKPhysicsBody(rectangleOf: obstacle.size)
obstacle.physicsBody?.isDynamic = true
obstacle.physicsBody?.categoryBitMask = PhysicsCategory.obstacle
obstacle.physicsBody?.contactTestBitMask = PhysicsCategory.fish
obstacle.physicsBody?.collisionBitMask = PhysicsCategory.fish
obstacle.physicsBody?.usesPreciseCollisionDetection = true
self.obstacles.append(obstacle)
super.addChild(obstacle)
Please let me know if there is something I am doing wrong / misunderstanding. Thanks.
Found the answer here. One of the objects needs to have .physicsBody?.isDynamic = true. In this case I set the obstacle to false so it is stationary.
I currently have a scene where a cube is dropped onto a floor in scenekit. I animated the cube to move on the x axis across the floor, when it does though it sinks into the floor a bit and then repositions to be on top of the floor.
Here's just some of the code:
// create floor
SCNFloor *mainFloor = [SCNFloor floor];
SCNNode *floorNode = [SCNNode nodeWithGeometry:mainFloor];
floorNode.position = SCNVector3Make(0.0, -4.0, 0.0);
[myScene.rootNode addChildNode:floorNode];
SCNPhysicsShape *ground = [SCNPhysicsShape shapeWithGeometry:mainFloor options:nil];
SCNPhysicsBodyType kin = SCNPhysicsBodyTypeKinematic;
SCNPhysicsBody *solid = [SCNPhysicsBody bodyWithType:kin shape:ground];
floorNode.physicsBody = solid;
myScene.physicsWorld.gravity = SCNVector3Make(0.0, -9.8, 0.0);
Can someone help me find out why the cube sinks? If you can't figure out why, maybe you could provide example code of a working floor that a character can walk on? (in objective c hopefully, but i can translate from swift if need be)
I think that the category bit mask of your floor and the collision bitmask of your cube should be the same
you can do so (in swift) like this:
floorNode.physicsBody.categoryBitMask = 3
cubeNode.physicsBody.collisionBitMask = 3
Using ProBlaster's advice, I used [SCNPhysicsBody applyForce] instead and that solved the problem.
I'm currently trying to put a test game together in Sprite Kit. I have some code that creates a patch of ground using a pair of PNG images with dimensions 200 * 572.
let textures = [SKTexture(imageNamed: "GrassAni1"), SKTexture(imageNamed: "GrassAni2")]
for (var i = 0; i < 10; i += 1) {
let chunk = SKSpriteNode(texture: textures[0])
chunk.position = CGPointMake(CGFloat(200 * i), 0)
let animate = SKAction.animateWithTextures(textures, timePerFrame: 1)
chunk.runAction(SKAction.repeatActionForever(animate))
//boilerplate physics body codeā¦
self.addChild(hero)
}
Enter my problem. Here is an up-close of my sprite in the Xcode file viewer:
And here's what it looks like when the game is running:
The in-game sprite appears to be anti-aliased and as a result looks fuzzy. How do I prevent this and make the game look as sharp as the original images?
You will need to change the filteringMode on your SKTexture objects to SKTextureFilteringMode.Nearest.
Then as long as your node is positioned on pixel boundaries, it should be drawn as expected.
I'm making a platform and want to stop the character (a ball) from going through the ground.
Can I write an if statement similar to if (Ball.center.y == 463) Ball.center.y = 463;?
463 is the y position that the ground is in the game.
While that approach is certainly possible, typically you want to abstract away the details (such as the 463 y-position of the ground) so that your code is more robust. For example, if you changed the y-position of the ground, you would have to change the 463 value everywhere you use it!
But fundamentally, yes you would use an if statement somewhat like what you provided. One thing to note is that if the ground is at 463, your ball will be half-way through the ground (since you are looking at the y-position of the centre of the ball.
Moreso, you want a check that is not so absolute... what if the position of the ball somehow becomes lower than the ground? Say 462? What should the behavior be now?
Without getting into the physics and design of your program, you would at the very least want to change your statement to something like:
int ball_lower_bound = Ball.center.y - Ball.height/2;
int ground_bound = 463;
if (ball_lower_bound < ground_bound) {
ball_lower_bound = ground_bound;
}
Since you are using sprite kit you should use physics body to handle that.
This is in swift:
variables of your class:
let balCategory:UInt32 = 1 << 0
let groundCategory:UInt32 = 1 << 1
Then when you define your ball sprite:
ball.physicsBody.categoryBitMask = ballCategory
ball.physicsBody.collisionBitMask = groundCategory
ball.physicsBody.contactTestBitMask = groundCategory
Then when you define your ground sprite (hopefully you have a image for your ground, if not make a very thin transparent or have something just below the screens edge):
ground.physicsBody.categoryBitMask = groundCategory
ground.physicsBody.collisionBitMask = ballCategory
ground.physicsBody.contactTestBitMask = ballCategory
Hopefully you can translate it to objective C if you are using that.
I am using the following code to put a floor in my SpriteKit based game:
var floorNode = SKSpriteNode()
var floorBody = SKPhysicsBody(edgeFromPoint: CGPointMake(self.frame.minX, self.frame.minY), toPoint: CGPointMake(self.frame.maxX, self.frame.minY))
floorNode.physicsBody = floorBody
addChild(floorNode)
This places a floor at the bottom of the screen as expected. However as I add more things to the scene, one end of the floor eventually sinks down and everything in the scene that is resting on it slides off into the abyss.
I am at a complete loss here since Apple's documentation says that "an edge-based body does not have mass or volume, and is unaffected by forces or impulses in the system. Edge-based bodies are used to represent volume-less boundaries or hollow spaces in your physics simulation."
Anyone else seen this kind of behavior?
The easiest way to define floor for your scene, without adding any nodes to it:
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
where self is your SKScene.
If you don't want ceiling on the scene, define physicsBody as this:
[SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
self.frame.size.height*5)];