Alpha mask/texture physics body fatal error on launch - swift - sprite-kit

So, i am creating a game and I'm trying to code physics boundaries for the hero so he doesn't just fall through the floor on launch lol, So i implemented my scene and I'm trying to create an alpha mask texture physics body on my "desertForeground" and i don't get any errors, however it keeps crashing and i get this error and the debugging doesn't tell me much, it just basically says invalid instruction but doesn't tell me why is wrong. Can anybody help me out? I'm pretty new to coding physics bodies in swift
import SpriteKit
enum BodyType:UInt32 {
case hero = 01
case bullet1 = 010
case enemy1 = 0100
case enemy2 = 01000
case enemy3 = 010000
case desertForegroundCase = 02
}
class GameScene: SKScene, SKPhysicsContactDelegate {
let desertBackground = SKSpriteNode (imageNamed: "RZ-
DesertBackground.png")
let desertForeground = SKSpriteNode (imageNamed: "RZ-
DesertForeground.png")
let desertgully = SKSpriteNode (imageNamed: "RZ-DesertGully.png")
override func didMoveToView(view: SKView) {
CreateScene()
}
func CreateScene (){
desertForeground.position = CGPoint(x: 570 , y: 102)
desertForeground.size = CGSize (width: 1136, height: 200)
desertForeground.zPosition = 1
let desertForegroundTexture = SKTexture()
let desertForegroundBody = SKPhysicsBody(texture:
desertForegroundTexture, size: CGSize(width: 1136, height: 200))
desertForeground.texture = desertForegroundTexture
desertForegroundBody.dynamic = true
desertForegroundBody.affectedByGravity = false
desertForegroundBody.allowsRotation = false
desertForegroundBody.categoryBitMask =
BodyType.desertForegroundCase.rawValue
desertForegroundBody.contactTestBitMask = BodyType.enemy1.rawValue
| BodyType.enemy2.rawValue | BodyType.enemy3.rawValue |
BodyType.soldierL.rawValue | BodyType.soldierT.rawValue
desertForeground.physicsBody = desertForegroundBody
}
}

The problem is how you populate desertForegroundTexture.
Replace this
let desertForegroundTexture = SKTexture()
with this
let desertForegroundTexture = SKTexture(imageNamed: "YOUR_IMAGE_NAME")

Your issue come when you try to create a physicBody from an empty texture:
/**
Creates a body from the alpha values in the supplied texture.
#param texture the texture to be interpreted
#param size of the generated physics body
*/
#available(iOS 8.0, *)
public /*not inherited*/ init(texture: SKTexture, size: CGSize)
In fact, for example this kind of thing don't happened if your try to create a SKSpriteNode like this:
let desertForegroundTexture = SKTexture()
desertForeground = SKSpriteNode(texture: desertForegroundTexture, size:CGSize(width: 1136, height: 200))

Related

Render text with SKRenderer

I initialized SKRenderer in my project to use it alongside of metal. Although I'm not sure that I did it correctly, I need to know how I can render a simple 2D text with it?
For example, here is how we can draw a text with SKLabelNode, do we have anything like it for SKRenderer?
thank you so much
let winner = SKLabelNode(fontNamed: "Chalkduster")
winner.text = "You Win!"
winner.fontSize = 65
winner.fontColor = SKColor.red
winner.position = CGPoint(x: 100, y: 100)
view.addChild(winner)
The first thing you need to do is initialize the scene.
var renderer: SKRenderer
var skScene: SKScene
in your init function:
renderer = SKRenderer(device: device)
// iphone 11 portrait
skScene = SKScene(size: CGSize(width: 1125, height: 2436))
renderer.scene = skScene
let winner = SKLabelNode(fontNamed: "Chalkduster")
winner.text = "You Win!"
winner.fontSize = 65
winner.fontColor = SKColor.red
winner.position = CGPoint(x: 1125 / 2, y: 2436 / 2)
skScene.addChild(winner)
and finally in your render function:
renderer.render(withViewport: viewport, renderCommandEncoder: renderEncoder, renderPassDescriptor: renderPassDescriptor, commandQueue: commandQueue)
renderEncoder.endEncoding()
but keep in mind ui layer (SKRenderer) should be rendered in the last pass.

Swift 3 Physicsbody Collisions doesn't work

This is my class for the Player
My Player doesn't collide with the Gamefield they just go through each other.
I dont want them to be on top of each other.
I tried to google the solution but for me it seems that I did mostly all right.
Please Help:
class Player: SKShapeNode {
static public let length: CGFloat = 50
static public let rect = CGRect(x: -length/2, y: -length/2, width: length, height: length)
var life = 100
override init() {
super.init()
self.fillColor = SKColor.blue
self.strokeColor = SKColor.black
self.position = CGPoint(x: 50, y: 50)
self.physicsBody = SKPhysicsBody(edgeLoopFrom: Player.rect)
self.physicsBody?.isDynamic = true
self.physicsBody?.allowsRotation = false
self.physicsBody?.categoryBitMask = PhysicsCategory.Robot
self.physicsBody?.contactTestBitMask = PhysicsCategory.Projectile
self.physicsBody?.collisionBitMask = PhysicsCategory.Gamefield
self.physicsBody?.usesPreciseCollisionDetection = true
}; required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")}
func moveBy(vect: CGVector) {
self.position.x += vect.dx
self.position.y += vect.dy
//print(vect.dx, vect.dy)
}
}
This is my Gamescene Class
struct PhysicsCategory {
static let None : UInt32 = UInt32.min
static let All : UInt32 = UInt32.max
static let Robot : UInt32 = 0b0001 // 1
static let Gamefield : UInt32 = 0b0010 // 2
static let Monster : UInt32 = 0b0011 // 3
static let Projectile : UInt32 = 0b0100 // 4
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var player = Player(rect: Player.rect)
var controlL: Control
var controlR: Control
var cam = SKCameraNode()
override init(size: CGSize) {
controlL = Control(posX: 0, size: size, direction: 1)
controlR = Control(posX: size.width, size: size, direction: -1)
super.init(size: size)
}; required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")}
override func didMove(to view: SKView) {
self.backgroundColor = SKColor.white
physicsWorld.gravity = CGVector.zero
physicsWorld.contactDelegate = self
cam.xScale = 1
cam.yScale = 1
self.camera = cam
player.addChild(cam)
let gameFieldRect = CGRect(x: 0, y: 0, width: 1000, height: 600)
let gameField = SKShapeNode(rect: gameFieldRect)
gameField.fillColor = SKColor.clear
gameField.strokeColor = SKColor.black
gameField.position = CGPoint(x: 0, y: 0)
gameField.physicsBody = SKPhysicsBody(edgeLoopFrom: gameFieldRect)
gameField.physicsBody?.isDynamic = true
gameField.physicsBody?.allowsRotation = false
gameField.physicsBody?.categoryBitMask = PhysicsCategory.Gamefield
gameField.physicsBody?.contactTestBitMask = PhysicsCategory.None
gameField.physicsBody?.collisionBitMask = PhysicsCategory.Robot
self.addChild(gameField)
self.addChild(player)
cam.addChild(controlL.add())
cam.addChild(controlR.add())
I hope someone sees the mistake. It took me quite long allready.
You need to change your robot to a different physics body type (rectangleOf)?:
SpriteKit supports two kinds of physics bodies, volume-based bodies and edge-based bodies. When you create a physics body, its kind, size, and shape are determined by the constructor method you call. 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. In contrast, volume-based bodies are used to represent objects with mass and volume.
Also, are you using impulses or forces to move your robot? Not .move(to:) or .position =? move and position will break your physics world (it will go through it in some scenarios)
Also, it looks like you need to change your category masks to a proper series (for didBegin(contact:))
it should be, 1, 2 , 4, 8, 16, so on... that is how you get unique contact hits.. Right now you could get a hit of "4" from 0+4 or 1+3... thus, not unique collisions.
Your collisionBitMask looks good though.. They should be bumping into each other.

How do I create a collision with SpriteKit with an already created SKSpriteNode?

I want the pacman to restart from its original position when it collides with blinky, that is moving.
How can I make them collide considering I have already declared them?
You move the pacman, but blinky moves alone. I want it to work like the pacman game.
public class PacmanScene: SKScene {
let playerSpeed: CGFloat = 40.0
var pacman: SKSpriteNode?
var playerTextures: [SKTexture] = []
var lastTouch: CGPoint? = nil
var blinky: SKSpriteNode?
var clyde: SKSpriteNode?
var inky: SKSpriteNode?
var pinky: SKSpriteNode?
override public init(size: CGSize) {
let pacmanTexture = SKTexture(imageNamed: "pacman01.png")
pacman = SKSpriteNode(texture: pacmanTexture)
pacman?.name = "pacman"
pacman?.position = CGPoint(x:30, y:30)
pacman?.zPosition = 1.0
pacman?.physicsBody = SKPhysicsBody(texture: pacmanTexture, size: CGSize(width: (pacman?.size.width)!, height: (pacman?.size.height)!))
pacman?.physicsBody?.allowsRotation = true
pacman?.physicsBody?.affectedByGravity = false
pacman?.physicsBody?.mass = 2
let blinkyTexture = SKTexture(imageNamed: "blinky.png")
blinky = SKSpriteNode(texture: blinkyTexture)
blinky?.name = "blinky"
blinky?.position = CGPoint(x: 15, y: 60)
blinky?.zPosition = 1.0
blinky?.physicsBody = SKPhysicsBody(texture: pacmanTexture, size: CGSize(width: (blinky?.size.width)!, height: (blinky?.size.height)!))
blinky?.physicsBody?.allowsRotation = false
blinky?.physicsBody?.affectedByGravity = false
blinky?.physicsBody?.mass = 1000
super.init(size: size)
addChild(pacman!)
addChild(blinky!)
override public func didMove(to view: SKView) {
let bmoveUp = SKAction.moveBy(x: 0, y: 450, duration: 4.0)
let bmoveRight = SKAction.moveBy(x:20, y:0, duration: 1.0)
let bmoveDown = SKAction.moveBy(x:0, y: -450, duration: 4.0)
let bmoveLeft = SKAction.moveBy(x:-20, y:0, duration: 1.0)
let bsequence = SKAction.sequence([bmoveUp, bmoveRight, bmoveDown, bmoveLeft])
let bendlessAction = SKAction.repeatForever(bsequence)
blinky?.run(bendlessAction)
}
If iv got this right you want your "blinky" to follow your "pacman" to do this you would have to work out the position of the pacman then add an SKAction to your blinky to move to that position.
Try something like this
//Speed blinky moves
let blinkySpeed = 100
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
updateBlinky()
}
func updateBlinky() {
//Set the point that blinky moves to
let point = CGPoint(x: pacman.position.x, y: pacman.position.y)
//Get the distance its got to travel
let distance = distanceBetweenPoints(first: pacman.position, second: blinky.position)
//Get the time is got to take from the speed and distance
let time = distance / blinkySpeed
//Create and run the action
let action = SKAction.move(to: point, duration: TimeInterval(time))
blinky.run(action)
}
//work out the distance between the sprites
func distanceBetweenPoints(first: CGPoint, second: CGPoint) -> Int {
return Int(hypot(second.x - first.x, second.y - first.y))
}
The end result would be something like this
Edit:
Okay I think from your question you already have "blinky" moving you just want to detect collisions.
First you need to add the SKPhysicsContactDelegate to your class
public class PacmanScene: SKScene, SKPhysicsContactDelegate {
Then you need to add a category bit mask to both the sprites then handle the collisions in didBegin(_ contact: SKPhysicsContact) method.
What I would do
//Create Physics category struct
struct PhysicsCategory {
static let pacman : UInt32 = 0x1 << 1
static var blinky : UInt32 = 0x1 << 2
}
Then where you are setting up pacman and blinky set the category bitmask
//Set the category bit mask for pacman
pacman?.physicsBody?.categoryBitMask = PhysicsCategory.pacman
//Set what categories you want to test contact
pacman?.physicsBody?.contactTestBitMask = PhysicsCategory.blinky
//Set what categories you want to collide with each other
pacman?.physicsBody?.collisionBitMask = PhysicsCategory.blinky
//Set the category bit mask for blinky
blinky?.physicsBody?.categoryBitMask = PhysicsCategory.blinky
//Set what categories you want to test contact
blinky?.physicsBody?.contactTestBitMask = PhysicsCategory.pacman
//Set what categories you want to collide with each other
blinky?.physicsBody?.collisionBitMask = PhysicsCategory.pacman
Then you would need to implement the didBegin(_ contact: SKPhysicsContact) method to handle the collisions
func didBegin(_ contact: SKPhysicsContact) {
//Work out which contact was pacman
let other = contact.bodyA.categoryBitMask == PhysicsCategory.pacman ? contact.bodyB : contact.bodyA
//Test what it hit
switch other.categoryBitMask {
case PhysicsCategory.blinky:
print("pacman hit blinky")
//Move pacman
pacman?.position = CGPoint(x:30, y:30)
default:
break
}
}
Hope this helps

Detecting collision on SpriteKit from two objects

I am having some trouble detecting the collision of two objects when i shoot from my cannon. The goal is to shoot a bullet from it and remove as soon as it colides with the obstacle. It seems there is no collision going on at all in the game, since i cannot receive the "Collision" message i set. Take a look at the following code:
class GameScene: SKScene, SKPhysicsContactDelegate {
enum BodyType: UInt32 {
case bullet = 1
case line = 2
case breaker = 4
}
i've enumerated the components so i can set them in the category Bitmask. After that, i did a setup from shooting when a touch begins.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let bullet = SKSpriteNode(imageNamed: "bullet.png")
bullet.position = self.shooterTriangle.position
var actionRotate = SKAction.rotateToAngle(CGFloat(2 * -M_PI), duration: NSTimeInterval(0.1))
let actionMove = SKAction.moveTo(CGPoint(x: 500, y: size.height * 0.5), duration: NSTimeInterval(0.5))
let removeBullet = SKAction.removeFromParent()
bullet.runAction(SKAction.sequence([actionRotate]))
bullet.runAction(SKAction.sequence([actionMove, removeBullet]))
addChild(bullet)
// Bullet Physics Start ****
bullet.physicsBody? = SKPhysicsBody(circleOfRadius: bullet.size.width/2.0)
bullet.physicsBody?.dynamic = true
bullet.physicsBody?.affectedByGravity = false
bullet.physicsBody?.allowsRotation = false
bullet.physicsBody?.categoryBitMask = BodyType.bullet.rawValue
bullet.physicsBody?.contactTestBitMask = BodyType.line.rawValue
bullet.physicsBody?.collisionBitMask = BodyType.line.rawValue
}
}
Then i`ve add the line i want the bullet to colide with in a loadGameComponents function...
func loadGameComponents() {
//Load line....
let line = SKSpriteNode(imageNamed: "Line_1.png")
line.position = CGPoint(x: size.width * 0.95, y: size.height * (2.0))
addChild(line)
var actualSpeedDuration = 2.2
let lineActionMove = SKAction.moveTo(CGPoint(x: size.width * 0.95, y: size.height * 0.6), duration: NSTimeInterval(actualSpeedDuration))
line.runAction(SKAction.sequence([lineActionMove]))
// Line Physics starts! *****
line.physicsBody? = SKPhysicsBody(rectangleOfSize: CGSize(width: line.size.width, height: line.size.height))
line.physicsBody?.dynamic = false
line.physicsBody?.affectedByGravity = false
line.physicsBody?.allowsRotation = false
line.physicsBody?.categoryBitMask = BodyType.line.rawValue
line.physicsBody?.contactTestBitMask = BodyType.bullet.rawValue
line.physicsBody?.collisionBitMask = 0
Finally, i've defined the contactDelegate = self in my didLoad function and included the didBeginContact method
func didBeginContact(contact: SKPhysicsContact) {
println("Contact!!!")
}
I am not receiving the message at all, but i am not sure what is going wrong!
Thanks for the patience! :)
Looks like the main problem is that i used to place the optional indicator (?) when declaring the physics body of an sprite. correction should be made in this lines...
line.physicsBody? = SKPhysicsBody(rectangleOfSize: CGSize(width: line.size.width, height: line.size.height))
bullet.physicsBody? = SKPhysicsBody(circleOfRadius: bullet.size.width/2.0)
Removing them solved my problem :)
I also included the view.showPhysics = true in my didMoveToView method so it is easy to see the boundaries between the sprites.

Ball collision and SKSpriteNode(s) collision not detected?

I can’t find any help or solution for my problem. I have 4 SKSpriteNodes named: bottomGoalGreen, topGoalGreen, bottomGoalBlue, and topGoalBlue. I also have a ball that is a SKSpriteNode named ball. My first question/problem is when I have my ball collide with, for example, topGoalGreen or bottomGoalGreen, I want the topGoalGreen to be removed as well as bottomGoalGreen and then topGoalBlue and bottomGoalBlue to appear and vice versa. My other problem is with my ball and the collision. I have two SKAction.moveToY so the ball can move up and down the screen. I was wondering if the SKActions could be the culprit to why the collision will not happen. I hope I improved my question. If not, I will try again to clarify.
import Foundation
import SpriteKit
import UIKit
struct PhysicsCatagory {
static let bottomGoalGreen : UInt32 = 1
static let topGoalGreen : UInt32 = 2
static let bottomGoalBlue : UInt32 = 4
static let topGoalBlue : UInt32 = 8
static let ball : UInt32 = 16
}
class GamePlayScene: SKScene, SKPhysicsContactDelegate {
var topGoalGreen = SKSpriteNode(imageNamed: "green goal (top).png")
var bottomGoalGreen = SKSpriteNode(imageNamed: "green goal (bottom).png")
var topGoalBlue = SKSpriteNode(imageNamed: "blue goal (top).png")
var bottomGoalBlue = SKSpriteNode(imageNamed: "blue goal (bottom).png")
var ball = SKSpriteNode(imageNamed: "green ball.png")
override func didMoveToView(view: SKView) {
//setup scene
physicsWorld.gravity = CGVector.zeroVector
physicsWorld.gravity = CGVectorMake(0, 0)
physicsWorld.contactDelegate = self
self.scene?.backgroundColor = UIColor.blackColor()
self.scene?.size = CGSize(width: 640, height: 1136)
//Top goal green code
topGoalGreen.position = CGPoint (x: self.size.width * 0.5, y: self.size.width * 1.52)
topGoalGreen.physicsBody = SKPhysicsBody(rectangleOfSize: topGoalGreen.size)
topGoalGreen.size = CGSize (width: 300, height: 309)
topGoalGreen.physicsBody?.dynamic = false
topGoalGreen.physicsBody?.categoryBitMask = PhysicsCatagory.topGoalGreen
topGoalGreen.physicsBody?.collisionBitMask = 0
topGoalGreen.physicsBody?.contactTestBitMask = PhysicsCatagory.ball
self.addChild(topGoalGreen)
//Bottom goal code
bottomGoalGreen.position = CGPoint (x: self.size.width * 0.5, y: self.size.width * 0.252)
bottomGoalGreen.size = CGSize (width: 300, height: 309)
bottomGoalGreen.physicsBody?.dynamic = false
bottomGoalGreen.physicsBody?.categoryBitMask = PhysicsCatagory.bottomGoalGreen
bottomGoalGreen.physicsBody?.contactTestBitMask = PhysicsCatagory.ball
self.addChild(bottomGoalGreen)
//Ball code
ball.position = CGPoint (x: self.size.width * 0.5, y: self.size.width * 0.9)
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.size.width / 2)
ball.size = CGSize (width: 80, height: 82)
ball.physicsBody?.dynamic = true
ball.physicsBody?.categoryBitMask = PhysicsCatagory.ball
ball.physicsBody?.collisionBitMask = 0
ball.physicsBody?.contactTestBitMask = PhysicsCatagory.topGoalGreen
ball.physicsBody?.categoryBitMask = PhysicsCatagory.bottomGoalGreen
ball.physicsBody?.collisionBitMask = PhysicsCatagory.bottomGoalGreen
ball.physicsBody?.contactTestBitMask = PhysicsCatagory.bottomGoalGreen
let moveBallUp = SKAction.moveToY(1040, duration: 2)
let moveBallDown = SKAction.moveToY(90, duration: 2)
let moveUpAndDown = SKAction.sequence([moveBallUp, moveBallDown])
let moveUpAndDownForever = SKAction.repeatActionForever(moveUpAndDown)
ball.runAction(moveUpAndDownForever)
self.addChild(ball)
}
func didBeginContact(contact: SKPhysicsContact) {
var firstBody : SKPhysicsBody = contact.bodyA
var secondBody : SKPhysicsBody = contact.bodyB
if (((firstBody.categoryBitMask == PhysicsCatagory.topGoalGreen) && (secondBody.categoryBitMask == PhysicsCatagory.ball)) ||
((firstBody.categoryBitMask == PhysicsCatagory.ball) && (secondBody.categoryBitMask == PhysicsCatagory.topGoalGreen))){
CollisionWithBall(firstBody.node as! SKSpriteNode, ball: secondBody.node as! SKSpriteNode)
NSLog("Collision!")
}
}
func CollisionWithBall(topGoalGreen : SKSpriteNode, ball : SKSpriteNode) {
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
I figured out what I was missing. I was missing a SKPhysicsBody for both goals and ball. Thanks to the other commenters for trying to help me.
//Top goal green code
topGoalGreen.position = CGPoint (x: self.size.width * 0.5, y: self.size.width * 1.67)
topGoalGreen.size = CGSize (width: 400, height: 80)
topGoalGreen.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize (width: 10, height: 10))
topGoalGreen.physicsBody?.dynamic = false
topGoalGreen.physicsBody?.categoryBitMask = PhysicsCatagory.topGoalGreen
topGoalGreen.physicsBody?.contactTestBitMask = PhysicsCatagory.ball
self.addChild(topGoalGreen)
In order to register contact you should set contact delegate.
physicsWorld.contactDelegate = self
Otherwise , you can't use methods like didBeginContact or didEndContact. Also you have to set category, contact and collision bitmasks properly in order to make everything to work. So, next thing would be to set category and contact bit masks properly.
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
//Contact will be registered when ball makes a contact with top or bottom goal.
ball.physicsBody?.contactTestBitMask = PhysicsCategory.topGoal | PhysicsCategory.bottomGoal
You have to follow this principle as well for top and bottom goal nodes.
Check out this great answer by rickster in order to understand how things works when using physics engine in SpriteKit.