Below is an Enemy node in Sprite Kit, in Swift. I have set the velocity for the node, but when it spawns there is no movement. Xcode is giving me no errors, is there something I'm doing wrong here?
class Enemy: SKSpriteNode {
init(imageNamed: String) {
let imageTexture = SKTexture(imageNamed: imageNamed)
super.init(texture: imageTexture, color: nil, size: imageTexture.size())
self.name = "Enemy"
self.physicsBody = SKPhysicsBody(rectangleOfSize: imageTexture.size())
self.physicsBody?.dynamic = false
self.physicsBody?.velocity = CGVectorMake(10.0,10.0)
}
}
From Apple's SpriteKit documentation:
A Boolean value that indicates whether the physics body is moved by the physics simulation.
Declaration
Swift
var dynamic: Bool
Objective-C
#property(nonatomic, getter=isDynamic) BOOL dynamic
Discussion
If the value is NO, the physics body ignores all forces and impulses applied to it. This property is ignored on edge-based bodies; they are automatically static.
As 0x141E says, you have to make your SKPhysicsBody dynamic, or it won't move.
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 have game that during the game your finger moves around and should avoid hitting some obstacles. I know how to use collision but I recently heard that there is a funtion called the body(at : point).
I should say that I have masked my obstacles. Because of that I can't use the contains(point) function.
I really want to use the body(at : point) function otherwise I know how to work with collisions.
Some of my code:
play_button = self.childNode(withName: "play_button") as! SKSpriteNode
for t in touches{
let fingerlocation = t.location(in: self)
if physicsworld.body(at : fingerlocation)
{
let so = level6(fileNamed:"level6")
so?.scaleMode = .aspectFill
self.view?.presentScene(so!, transition:
SKTransition.crossFade(withDuration: 1))
}
}
but it does not work.
physicsworld.body returns an SKPhysicsBody if found, so you just need to check if it exists
if let _ = physicsworld.body(at : fingerlocation)
If your goal is to make a button, then I would not do this approach.
I would recommend just subclassing SKSpriteNode and enabling the isUserInteractionEnable`
class Button : SKSpriteNode
{
override init()
{
super.init()
isUserInteractionEnabled = true
}
//then in your touchesBegan/touchesEnded code (wherever you placed it)
....
let so = level6(fileNamed:"level6")
so?.scaleMode = .aspectFill
scene.view?.presentScene(so!, transition:
SKTransition.crossFade(withDuration: 1))
....
}
I've had a similar problem, "if let body = physicsWorld.body(at: touchLocation){...}" was responding wherever I've touched the screen.
I figured out, that by default the SKScene object created its own SKPhysicsBody which was set on top of all other SKPhysicsBodies.
I solved this problem by making a new SKPhysicsBody for the scene, which didn't interfere anymore. Hope this code will help you.
inside of didMove:
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
I have a GameScene.sks and Player.sks. Player.sks stores player sprite, that consists of 3 parts (body, hand[child of body] and lantern[child of hand]). In the custom class of this scene I create playerTexture from image and set it as physics body.
let playerTexture = SKTexture(imageNamed: "player_outline")
parent!.physicsBody = SKPhysicsBody(texture: playerTexture, size: playerTexture.size())
I add player scene reference to the Game Scene. And when I move player, physics body staying behind sprite and don't move at all. If I don't use 'parent!' it will work, but physics body will have offset.
But if I create physics body from the Scene Editor, it works! What's happening?
Player.swift
class PlayerNode: SKSpriteNode, EventListenerNode {
func didMoveToScene() {
let playerTexture = SKTexture(imageNamed: "player_outline")
parent!.physicsBody = SKPhysicsBody(texture: playerTexture, size: playerTexture.size())
parent!.physicsBody!.categoryBitMask = PhysicsCategory.Player
parent!.physicsBody!.collisionBitMask = PhysicsCategory.Wall
}
}
GameScene.swift
var player: PlayerNode!
player = childNode(withName: "//player") as! PlayerNode
Player.swift
Player's body called 'player', arm called 'lanternArm' and it is a child of 'player', lantern called 'lantern' and it is a child of 'lanternArm'.
With this code:
> parent!.physicsBody = SKPhysicsBody(texture: playerTexture, size:
> playerTexture.size())
You are defining the physicsBody of player's parent, not player itself. Is that what you want to do? What is player's parent?
If I don't use 'parent!' it will work,
So you have this:
physicsBody = SKPhysicsBody(texture: playerTexture, size: playerTexture.size())
Now you are defining players physics body.
but physics body will have offset.
Check the anchorPoint of player. If it's (0, 0) that will give you an offset physicsBody.
I'm working with collision detection with SpriteKit and Swift. I have a SKScene that responds to a collision with the didBeginContact function:
func didBeginContact(contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == ColliderType.Food && contact.bodyB.categoryBitMask == ColliderType.Head) {
placeFoodInRandomGridLocation()
}
}
func placeFoodInRandomGridLocation() {
let randomX = arc4random_uniform(UInt32(myGrid.columnCount))
let randomY = arc4random_uniform(UInt32(myGrid.rowCount))
foodSpriteHolder.position = CGPoint(x: colLines[Int(randomX)], y: rowLines[Int(randomY)])
}
The problem is that I can easily adjust the position of the foodSpriteHolder before this didBeginContact function fires. It simply will not move the foodSpriteHolder when the placeFoodInRandomGridLocation is called.
It seems like a scope issue. I'm just not sure how to isolate why the position won't update. I can even make the foodSpriteHolder visibility hidden in this flow...So, I know i can access it.
For reference here is how the physics body is setup for the Food class item that is within the foodSpriteHolder:
self.physicsBody = SKPhysicsBody(rectangleOfSize: self.size, center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Food
Lastly the placeFoodInRandomGridLocation function definitely gets called...The position just won't update.
Thanks for any ideas,
Josh
The answer in this case was to remove the node when the collision is detected:
contact.bodyA.node?.removeFromParent()
Now I can create a new node in a new position. Since affectedByGravity is set to false the node wouldn't update its position.
I have a class called Item, and inside there is an instance variable called itemNode which is of type SKSpriteNode. In my GameScene class, when I create an instance of Item I create a physics body that is given to the Item's itemNode. In my collision detection system, when my character's physics body collides with the itemNode's physics body, I want to preform a function on the Item object whose node's physics body just collided. However, the collision system only returns to physics body. How do I access the Item object given only the physics body of the node?
The SKPhyicsBody class has a node property which points to the SKNode instance it is attached to.
Your collision detection code can look something like this:
func didBeginContact(contact: SKPhysicsContact) {
var item: SKSpriteNode
var character: SKSpriteNode
//Change this on the basis of the order of your categoryBitMask values
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
item = contact.bodyA.node as SKSpriteNode
character = contact.bodyB.node as SKSpriteNode
}
else
{
item = contact.bodyB.node as SKSpriteNode
character = contact.bodyA.node as SKSpriteNode
}
//Perform necessary operations on item and character
}
EDIT:
In order to get access to the Item instance that declares the node, you will have to store a variable within the node which points to the Item instance. For this, you can either subclass SKSpriteNode and include a property, or use the userData
property of SKNode
Subclassing:
//New Class
class ItemNode: SKSpriteNode {
static var itemInstance
}
//In the Item class declare the itemNode using this class
let itemNode = ItemNode()
itemNode.itemInstance = self
UserData property:
item.userData = NSMutableDictionary(object: self, forKey: "ItemInstance"))