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.
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 a sprite that the user is able to move side to side by pressing on the left or right side of the screen. If you hold down on either side, the player sprite will leave the screen. I want to stop that from happening using physic bodies, but I can't seem to make it work.
To start, here are my categories.
//Categories for physics bodies
let sceneCategory:UInt32 = 0x1 << 0 // Equal to 1
let playerCategory:UInt32 = 0x1 << 1 // Equal to 2
Here is where I set the physics body of the scene itself.
self.physicsWorld.contactDelegate = self
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
self.physicsBody?.categoryBitMask = sceneCategory
self.physicsBody?.contactTestBitMask = playerCategory
self.physicsBody?.collisionBitMask = 0
self.physicsBody?.isDynamic = false
This is how I have the physics set for the player itself. I'm trying to set the physics body around the car itself. Is the way I have it setup the same as setting an alpha mask physics body?
let texture = SKTexture(imageNamed: "PorscheBlue")
player.physicsBody = SKPhysicsBody(texture: texture, size: player.size)
player.physicsBody?.isDynamic = false
player.physicsBody?.categoryBitMask = playerCategory
player.physicsBody?.contactTestBitMask = sceneCategory
player.physicsBody?.collisionBitMask = 0
Then, in the didBegin method, I just wanted it to print showing that the method was called. This isn't happening though for some reason.
func didBegin(_ contact: SKPhysicsContact)
{
print("called")
}
Why isn't the didBegin method being called? Do I have the physics set up properly? How can I make it so the player isn't allowed to leave the screen when moving?
Thank you
EDIT: So when having the physics boundaries visible, it looks like the boundaries for the scene are only being drawn on the top and bottom. I can't see any lines being drawn on the sides. That may be the issue, but I can't get it to show on the sides.
I resolved my issue by changing the dynamic value. I had both set to false, which causes the didBegin function to not be called. At least one of the nodes needs to be dynamic.
I'm trying to add a hitbox to the bottom of my player to only detect platforms. After a lot of trial and error I figured out to have an additional physicsbody attached as a child in a fixed position to another physicsbody I need to use an SKPhysicsJoint. I did this and it seems to have an unforeseen consequence. when I add the hitbox to the player in this way, it alters the players collision / contact bit masks slightly.
In my game you tap the screen to jump and it works seamlessly with no lag like so:
Player jumping
But when I add this hitbox with the joint I have to hold the screen with my finger and eventually the player jumps, but most of the time the input is ignored. Almost as if there's some sort of severe lag when reading the input.In this gif I'm tapping the screen constantly trying to make my player jump and a lot of the inputs are being ignored or blocked:
Player hitbox not behaving
This function setups up the player in my gamescene.swift and adds the hitbox to the player via a fixed SKPhysicsJoint to the scene:
func playerSetup(){
//setups player
addChild(player)
player.addChild(playerPlatformHitbox)
let myCGPoint = player.position // sets joint position
let myJoint = SKPhysicsJointFixed.joint(withBodyA: playerPlatformHitbox.physicsBody!, bodyB: player.physicsBody!, anchor: myCGPoint)
scene?.physicsWorld.add(myJoint)
}
And this is the hitbox class I made to only detect platforms for the player. you'll see it should ignore everything expect platforms from the bit masks. For some reason it's not allowing the player to accept contact with it's bit masks even though this hitbox and player arent touching:
import Foundation
import SpriteKit
class PlayerPlatformHitbox: SKSpriteNode{
init() {
super.init(texture: nil, color: SKColor.blue, size: CGSize(width: playerTexture.size().width, height: 10))
physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.size.width, height: self.size.height))
position = CGPoint(x:0, y: (-playerTexture.size().height * 0.6))
physicsBody?.categoryBitMask = CollisionTypes.jumpHitBox.rawValue
physicsBody?.contactTestBitMask = CollisionTypes.platform.rawValue
physicsBody?.collisionBitMask = CollisionTypes.platform.rawValue
physicsBody?.restitution = 0.0
physicsBody?.friction = 0.0
zPosition = 20
physicsBody?.linearDamping = 0.0
physicsBody?.angularDamping = 0.0
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)}
}
And this is my player class
class Player: SKSpriteNode {
init() {
super.init(texture: nil, color: SKColor.clear, size: playerTexture.size())
//starts accelerameter
motionManager = CMMotionManager()
motionManager.startAccelerometerUpdates()
physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: playerTexture.size().width,
height: playerTexture.size().height))
physicsBody?.categoryBitMask = CollisionTypes.player.rawValue
physicsBody?.contactTestBitMask = CollisionTypes.memoryModule.rawValue | CollisionTypes.spikes.rawValue | CollisionTypes.finish.rawValue | CollisionTypes.enemy.rawValue | CollisionTypes.ground.rawValue
physicsBody?.collisionBitMask = CollisionTypes.ground.rawValue | CollisionTypes.sceneEdge.rawValue
physicsBody?.affectedByGravity = true
physicsBody?.restitution = 0.0
physicsBody?.friction = 0.3
physicsBody?.isDynamic = true
//physicsBody?.friction = 0.0
physicsBody?.allowsRotation = false
setScale(0.65)
zPosition = 1
physicsBody?.linearDamping = 0.0
physicsBody?.angularDamping = 0.0
animateWalk()
}
The goal of all this is to use the hitbox to detect platforms instead of the player in this function I have in my update method:
//jump through platform check
if let body = player.physicsBody {
let dy = body.velocity.dy
if dy > 0{
// Prevent collisions if the hero is jumping
body.collisionBitMask &= ~CollisionTypes.platform.rawValue
body.contactTestBitMask &= ~CollisionTypes.platform.rawValue
}
else {
// Allow collisions if the hero is falling
body.collisionBitMask |= CollisionTypes.platform.rawValue
body.contactTestBitMask |= CollisionTypes.platform.rawValue
}
}
and the only reason I need to do this is because when the player is falling, if he falls and hits the side of the platform he will stop and slide down it because collisions are back on and the players physicsbody is a square.
If i can use the platform detection hitbox to detect the platforms instead of the players bulky hitbox it will get rid of this issue.
Thanks for any and all advice.
EDIT: this is the childs behavior with a physics body and no joint:
Odd behavior
I found the solution.
It seems there's some bugs with fixed joints and the answer was to use a "pin" joint instead. This StackO question goes into detail about a rotation bug with fixed joints. On a whim I decided to change my fixed joint to a pin and now the child behaves as you would expect, positionally speaking anyway.
So it appears fixed joints have a few bugs that need to be worked out.
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.
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.