I am trying to create a impenetrable border at the edge of the game scene frame.
In Swift I have attempted to re-write this as:
override func didMoveToView(view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: CGRect(x: 0,y: 100,width: self.frame.size.width,height: self.frame.size.height-200))
}
Instead of giving me a clear boundary at the edge of frame it is providing a weak border, which my player (with no specified mass) can pass over.
I'm also not sure why I am having to subtract and add to the y axis to get the correct border but this is less of a concern. Any help would be much appreciated.
The key is to move your sprites by applying a force/impulse or by setting the velocity property so that they respond correctly to
collisions with other physics bodies (such as an edge loop)
forces such as linear/angular damping, friction, and force fields.
You should first define a bitmask category to represent the physics body of your game scene. I typically use the 32-bit binary value 00000000000000000000000000000001, which I define as a constant:
let SCENE_EDGE_CATEGORY: UInt32 = 0x1
With that constant defined, you can now define the scene's physics body in a very readable way:
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody!.categoryBitMask = SCENE_EDGE_CATEGORY
For any sprite that you want to be constrained by the edge of the scene, set its physics body's collision bitmask to the scene's category bitmask. You're telling Sprite Kit that you want the physics engine to handle collisions between that sprite and the scene's edge:
someSprite.physicsBody!.collisionBitMask = SCENE_EDGE_CATEGORY
Hope this helps!
Try this:
override init(size: CGSize)
{
super.init(size: size)
physicsBody = SKPhysicsBody(edgeLoopFromRect: CGRectMake(0, 0, size.width, size.height))
anchorPoint = CGPointMake(0.0, 0.0)
}
works fine for me.
This will definitely help you to define the edge of the screen in Swift.
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
Related
I'm currently working on a small iOS game. In its current iteration, 20 targets spawn and move across the screen space-invaders style, and you control a little ship to shoot and destroy them. The code for my targets, the player ship's bullets, and a simple collision detection function I've written in the interim are as follows:
class Red_Target: SKSpriteNode{
var game_scene: GameScene!
private var ship_texture: SKTexture!
convenience init(scale: CGFloat, game_world: GameScene){
self.init(texture: SKTexture(imageNamed: "Proto Target"))
self.ship_texture = SKTexture(imageNamed: "Proto Target")
self.setScale(scale)
game_scene = game_world
game_scene.addChild(self)
self.position = CGPoint(x: game_scene.view!.bounds.width/10, y: 9 * game_scene.view!.bounds.height/10)
//self.physicsBody = SKPhysicsBody(texture: ship_texture, size: self.size)
self.physicsBody = SKPhysicsBody(circleOfRadius: 13)
self.physicsBody!.affectedByGravity = false
self.physicsBody!.collisionBitMask = 0x0
self.physicsBody!.categoryBitMask = CollisionType.Enemy.rawValue
self.physicsBody!.contactTestBitMask = CollisionType.Player_Bullet.rawValue
}
func move() {
self.run(space_invaders(scene: game_scene))
}
}
class PC_Bullet: SKSpriteNode{
convenience init(scale: CGFloat){
self.init(imageNamed: "Goodbullet")
self.setScale(scale)
self.physicsBody = SKPhysicsBody(circleOfRadius: 3)
self.physicsBody!.affectedByGravity = false
self.physicsBody!.categoryBitMask = CollisionType.Player_Bullet.rawValue
self.physicsBody!.collisionBitMask = 0x0
self.physicsBody!.contactTestBitMask = CollisionType.Enemy.rawValue
}
}
func didBegin(_ contact: SKPhysicsContact) {
contact.bodyA.node!.removeFromParent()
contact.bodyB.node!.removeFromParent()
}
}
This code, in its current iteration, works just fine. However, if the line defining the target's physicsbody as its texture is uncommented and the line defining physicsbody as circleOfRadius is removed, the game will consistently crash after the 5th target is destroyed, claiming that didBegin unwraps a nil value. Why does this only happen from physics bodies with textures? Is there any way I could change the code for this to work? I would love to be able to use the physics body from texture function later on, when working with more irregular shapes.
You are pulling a classic nooby mistake. You are removing your bodies too early. If you browse Stackoverflow you will find a plethera of ways to solve it.
The basic idea is do not remove your sprites until the end of the physics phase because your 1 sprite could have multiple contact points to handle. So come up with a way to flag sprites that need to be deleted, and remove them during the didSimulatePhysics function.
I'm trying to have a rain particles which are affected by wind aka physicsWorld gravity.
I can see that the gravity does has an affect on my SKSpriteNodes but I can't achieve the same affect on an SKEmitterNode.
I'm just wondering if it's possible.
Here's what I've been trying...
override func didMove(to view: SKView) {
if let rainParticles = SKEmitterNode(fileNamed: "Rain.sks") {
rainParticles.position = CGPoint(x: size.width/2, y: size.height)
rainParticles.name = "rainParticle"
rainParticles.targetNode = scene
rainParticles.particlePositionRange =
CGVector(dx: frame.size.width, dy: frame.size.height)
rainParticles.zPosition = -1
// I don't think this is right
rainParticles.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
rainParticles.physicsBody?.affectedByGravity = true
addChild(rainParticles)
}
physicsWorld.contactDelegate = self
// gravity is pushing to the right here
physicsWorld.gravity = CGVector(dx: 20, dy: 0)
physicsWorld.speed = 0.85
}
Yes I have added SKPhysicsContactDelegate.
Obviously I want to ignore collisions so I haven't set collisionBitMask, also I don't want to have rain bouncing off anything with contactTestBitMask. I don't believe I need to set a categoryBitMask.
Particles are not represented by objects in SpriteKit. This means you cannot perform node-related tasks on particles, nor can you associate physics bodies with particles to make them interact with other content. Although there is no visible class representing particles added by the emitter node, you can think of a particle as having properties like any other object.
This is straight from SKEmitterNode documentation. Particles won't get any gravity acceleration from the physicsWorld of the scene.
Also rainParticles.physicsBody refers to the SKEmitterNode physicsBody, not its particles.
If you simply want the particles to simulate the current physicsWorld's gravity:
rainParticles.xAcceleration = self.physicsWorld.gravity.dx
rainParticles.yAcceleration = self.physicsWorld.gravity.dy
I'm making my first game with SpriteKit and for the enemy aliens (SKSpriteNode subclass) physics bodies I have been using:
self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width/2)
this has worked so far as most of the aliens are circle-shaped. However, if I were to have ellipse or square aliens what would be the best method of designing their physics bodies? I tried this for square shape:
let squarePath = UIBezierPath(rect: CGRect(x: -64, y: -64, width: 128, height: 128))
self.physicsBody = SKPhysicsBody(polygonFromPath: squarePath.CGPath)
but I don't know if this is the most efficient, and also don't know how to exactly make ellipses. Any suggestions would be great!
Let SpriteKit draw the shape for you!
If you have a sprite and you need to create a PhysicsBody for it just let SpriteKit do it for you. SpriteKit can analyze the texture, find the borders (using the alpha/transparent channel) and finally create a physics body matching the image.
Let's say I add this sprite to my scene
let croc = SKSpriteNode(imageNamed: "croc_walk01")
croc.position = CGPoint(x: frame.midX, y: frame.midY)
addChild(croc)
Automatically creating a PhysicsBody shape
Now I simply add this line
croc.physicsBody = SKPhysicsBody(texture: croc.texture!, size: croc.texture!.size())
And SpriteKit automatically creates the PhysicsBody for me.
Can you see the line line around the sprite? That's it. Let me hide the sprite so you can see it better
croc.hidden = true
Using paths is the only way to create complex physics bodys, however, you can create rectangles with SKPhysicsBody(rectangleOf: CGSize(width: 100, height: 100))
Edit:
Use this to help create efficient paths from images: http://insyncapp.net/SKPhysicsBodyPathGenerator.html
Note: this will only generate the paths for the image, you will need to clean it up your self by deleting paths and connecting the points.
I want to create my player on top of the background. Somehow this doesn't work. The node counter goes up by one but it isn't visible. I do add the player after I add the background so it should be on top. The code I use to create the player:
var player = SKSpriteNode()
func addPlayer(gameScene: GameScene, xPos: CGFloat, yPos: CGFloat){
player = SKSpriteNode(imageNamed: "player")
player.size = CGSize(width: fieldsWidth, height: fieldsWidth)
player.position = CGPoint(x: xPos, y: yPos)
gameScene.addChild(player)
}
And I access the function from another class like this:
person().addPlayer(self, xPos: fieldsWidth/2, yPos: fieldsWidth/2)
Hopefully you can help me.
If you are sure that you have added node at desired position, which is probably the situation here because node count is incremented, you can set player's and background's zPosition to make player above the background:
var player = SKSpriteNode()
//1. somewhere in your code set background's zPosition to 1 before you add it to the scene
func addPlayer(gameScene: GameScene, xPos: CGFloat, yPos: CGFloat){
player = SKSpriteNode(imageNamed: "player")
//2. Set player's zPosition to be higher than background's zPosition
player.zPosition = 2
player.size = CGSize(width: fieldsWidth, height: fieldsWidth)
player.position = CGPoint(x: xPos, y: yPos)
gameScene.addChild(player)
}
About ignoresSiblingOrder...I would recommend you to leave that to true because it can help a lot when it comes to performance . The only "thing" about this kind of optimization is that you have to explicitly set zPosition for nodes at same zPosition which can overlap, but you don't want to let them overlap in random order (which ignoresSiblingsOrder does when set to true), but rather to overlap in determined order (controlled by you). This is from docs:
When this property is set to YES, the position of the nodes in the
tree is ignored when determining the rendering order. The rendering
order of nodes at the same z position is arbitrary and may change
every time a new frame is rendered.
So, just set zPosition explicitly and you will be fine.
In the View Controller of your scene, set skView.ignoresSiblingOrder = false.
I'm working in a side-scolling game and I need to know what nodes are in an area to implement something like "line of sight". Right now I'm trying using enumerateBodyiesInRect() however it's detecting bodies that are 20px or more from the evaluated rect and I cannot figure out why it's so imprecise.
This is what I'm trying now:
import SpriteKit
import CoreMotion
class GameScene: SKScene, SKPhysicsContactDelegate
{
var player = SKShapeNode()
var world = SKShapeNode()
var rShape = SKShapeNode()
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
self.scaleMode = SKSceneScaleMode.AspectFit
self.size = view.bounds.size
// Add world
world = SKShapeNode(rectOfSize: view.bounds.size)
world.physicsBody = SKPhysicsBody(edgeLoopFromPath: world.path)
world.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2) // Move camera
self.addChild(world)
// Add player
player = SKShapeNode(rectOfSize: CGSize(width: 25, height: 25))
player.physicsBody = SKPhysicsBody(rectangleOfSize: player.frame.size)
player.physicsBody.dynamic = false
player.strokeColor = SKColor.blueColor()
player.fillColor = SKColor.blueColor()
player.position = CGPointMake(90, -50)
world.addChild(player)
}
override func update(currentTime: CFTimeInterval) {
// Define rect position and size (area that will be evaluated for bodies)
var r : CGRect = CGRect(x: 200, y: 200, width: 25, height: 25)
// Show rect for debug
rShape.removeFromParent()
rShape = SKShapeNode(rect: r)
rShape.strokeColor = SKColor.redColor()
self.addChild(rShape)
// Evaluate rect
rShape.fillColor = SKColor.clearColor()
self.physicsWorld.enumerateBodiesInRect(r) {
(body: SKPhysicsBody!, stop: UnsafePointer<ObjCBool>) in
self.rShape.fillColor = SKColor.redColor() // Paint the area blue if it detects a node
}
}
}
This code should show the evaluated rect and ray on the screen (for debugging purposes) and paint them red if they contact the player node. However you can see in the screenshot how it turns red when the player is 25px or more away from it, it's like if the drawing is a little bit off, or smaller than the actual area being evaluated. You can copy paste it to a project to duplicate the problem.
Could this be because this is just beta or am I doing something wrong?
You are creating a physical world where there is a specific rectangle that has 'special properties' - this is the rectangle that you use in enumerateBodiesInRect(). Why not create an invisible, inert physical body with the required rectangular dimension and then use SKPhysicsBody to check for collisions and/or contacts? You could then use allContactedBodies() or some delegate callbacks to learn what other bodies are inside your special rectangle.
Think of it like a 'tractor beam' or a 'warp rectangle'.
I believe you want SKPhysicsWorld's enumerateBodyiesInRect() instance method, which will iterate over all nodes in a given rectangle. If you're looking to get at the physics world through your scene, usage could look like this:
self.physicsWorld.enumerateBodiesInRect(CGRect(x: 0, y: 0, width: 50, height: 50)) {(body: SKPhysicsBody!, stop: UnsafePointer<ObjCBool>) in
// enumerates all nodes in given frame
}
I've experimented quite a bit with enumerateBodiesInRect now, and I've found it to be incredibly inaccurate. It seems to not have any of the claimed functionality, and instead produces random results. I honestly cannot even determine any pattern from its products.
enumerateBodiesAlongRay seems better, but still very buggy. The problem with that function seems to be the conversion between Screen and PhysicsWorld coordinates. I would avoid that one, as well.
I think your solution should simply be to use the existing contact detection system. All of your desired functionality can be written in the didBeginContact() and didEndContact() functions. This has the added benefit of allowing you to specify distinct functionality for both entering and leaving the area. You can also add particle effects, animations, and similar, as well as intentionally ignoring specific types of nodes.
The only thing to ensure success with this method is to clarify that the contact area has a unique category, that the contactTestBitMask contains all desired nodes and the collisionBitMask is set to 0.
The enumerateBodiesInRect method of SKPhysicsWorld expects the rect parameter to be in scene coordinates. This is important. If you have a scene hierarchy of nodes, you need to convert the rect you calculate from a reference node to the scene coordinates.
I faced a lot of issues with this method returning bodies that were off by values like 30px to the left etc. and finally realized the issue was because of the rect parameter not defined in scene coordinate space.
In my case, I had a worldNode inside my scene, and all objects were created in the worldNode. My camera was moving the worldNode about, and applying scaling to it for zooming out and in.
In order to use enumerateBodiesInRect correctly, I had to do something as follows:
// get your world rect based on game logic
let worldRect = getWorldRect()
// calculate the scene rect
let sceneRectOrigin = scene.convertPoint(worldRect.origin, fromNode:scene.worldNode)
let worldScale = scene.worldNode.xScale // assert this is not 0
// now to get the scene rect relative to the world rect, in scene coordinates
let sceneRect = CGRectMake( sceneRectOrigin.x, sceneRectOrigin.y, worldRect.width / worldScale, worldRect.height / worldScale)
world.physicsWorld.enumerateBodiesInRect(sceneRect) {
// your code here
}
Hope this helps.
I am not sure if this is a good practice. Correct me if not. But I am using
let shapeNode = SKShapeNode()
shapeNode.intersects(playerNode)
I checked selected nodes with simple loop if they intersect the player. Additionally I created SKShapeNodes which are drawn in front of nodes representing view sight of other actors in the game. They are moved along those actors.
There is only nodesAtPoint: method.
To achieve what you want you'd better to store all enemies in an array and have an int variable, something like nextEnemyIndex. This approach lets you to easily return the next enemy node, it's much more efficient than trying to find a node on the scene.
yes problem may occur because of your player's image, for example try to use 10px smaller body size:
player.physicsBody = SKPhysicsBody(rectangleOfSize: CGRectMake(self.frame.origin.x, self.frame.origin.y, self.size.width-10, self.size.height-10)));