Q: Spritekit SKPhysics - ios

How can I slide paddles from left to right individually and have the paddle the character jumped on stop while the rest keep sliding? how do I make 4 paddles slowly move to left and right back and forth at the same speed. Please and thank you.
In this code I am trying to make an SKPhysics body for all of them and I don't know how.
override func didMove(to view: SKView) {
background = self.childNode(withName: "background") as! SKSpriteNode
jellyghost = self.childNode(withName: "jellyghost") as! SKSpriteNode
paddle1 = self.childNode(withName: "paddle1") as! SKSpriteNode
paddle2 = self.childNode(withName: "paddle2") as! SKSpriteNode
paddle3 = self.childNode(withName: "paddle3") as! SKSpriteNode
paddle4 = self.childNode(withName: "paddle4") as! SKSpriteNode
jellyghost.physicsBody = SKPhysicsBody()
paddle1.physicsBody = SKPhysicsBody()
paddle2.physicsBody = SKPhysicsBody()
paddle3.physicsBody = SKPhysicsBody()
paddle4.physicsBody = SKPhysicsBody()
}
I am trying to make a SKPhysics body above and I don't how to do that.
and in the code below, I am trying to have each of the 4 paddles move on their own from left to right at the same speed without them moving to the touch and things are not working out for me. right now
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
for touch in touches{
let location = touch.location(in: self)
paddle1.run(SKAction.moveTo(x: location.x,duration: 1.0))
paddle2.run(SKAction.moveTo(x: location.x, duration: 1.0))
paddle3.run(SKAction.moveTo(x: location.x, duration: 1.0))
paddle4.run(SKAction.moveTo(x: location.x, duration: 1.0))
}
}
I also want to make my character jump to the moving paddles but I don't know how I would make my character jump up by dragging the screen up and releasing it to the current location of the paddle. How would I make it not restricted to not jumping only straight up but be flexible enough to jump x and y. Thank you

Yes you can. You will need to give the player and each of the paddles a SKPhysicsBody and then use SKPhysicsContactDelegate in you scene to detect collisions between the paddle and the player.
When there is a collision between the paddle and the player you can run a func like the one in the example below to stop the paddle if you are moving your player using velocity.
func stopPaddle(paddle: SKSpriteNode) {
paddle?.physicBody.velocity(CGVector(dx: 0, dy:0)
}
Please upload your code in future, as it will be easier to help you.

Related

SpriteKit not detecting physics bodies when using alpha mask but does when using bounding circle

I am pretty new to SpriteKit so I may be missing something quite obvious.
I am attempting to create an interactive map of the US. I have loaded PNG images for each state and placed a couple of them into the main SKScene using the scene editor.
My goal is wanting to detect when each state is tapped by the user. I initially wrote some code that would find the nodes within a touch location however, this meant that the user could tap the frame and it would be counted as a valid tap. I only want to register touches that happen within the state texture and not the frame. Reading online it was suggested to use SKPhysicsBody to register when a tap takes place. So I changed my code to the following.
class GameScene: SKScene {
override func didMove(to view: SKView) {}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location: CGPoint = self.convertPoint(fromView: touch.location(in: self.view))
let body = self.physicsWorld.body(at: location)
if let state = body?.node, let name = state.name {
state.run(SKAction.run({
var sprite = self.childNode(withName: name) as! SKSpriteNode
sprite.color = UIColor.random()
sprite.colorBlendFactor = 1
}))
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
Now, if I choose the Bounding circle body type everything works as expected (shown above in the screenshot). When I click within the boudning circle it runs the SKAction otherwise it does nothing. However, when I change the body type to Alpha mask (the body type I want) it suddenly stops detecting the state. In fact, it returns the SKPhysicsBody for the MainScene entity.
Any advice on how to fix this would be greatly appreciated.
Thanks.
i can reproduce this behavior (bug?) when using the scene editor. however it goes away if you skip the sks file and initialize your sprites in code. (i acknowledge that setting locations for 50 states is more tedious this way.)
class GameScene: SKScene {
let ok = SKSpriteNode(imageNamed: "ok")
let nm = SKSpriteNode(imageNamed: "nm")
let tx = SKSpriteNode(imageNamed: "tx")
override func didMove(to view: SKView) {
tx.name = "Texas"
nm.name = "New Mexico"
ok.name = "Oklahoma"
//set up physics bodies w/ alpha mask
tx.physicsBody = SKPhysicsBody(texture: tx.texture!, size: tx.texture!.size())
tx.physicsBody?.affectedByGravity = false
nm.physicsBody = SKPhysicsBody(texture: nm.texture!, size: nm.texture!.size())
nm.physicsBody?.affectedByGravity = false
ok.physicsBody = SKPhysicsBody(texture: ok.texture!, size: ok.texture!.size())
ok.physicsBody?.affectedByGravity = false
//then position your sprites and add them as children
}
}

How to give to sprite exact touch place?

My question is
When i touch black arrow areas i still touch whole picture, but i want only touch and have action when i touch red area only.
I tried to give a name to spriteNode plane = childNode(withName: "play") but it take/touch all image frame not only alphaBody.
red and black areas
I did some searches on google but no positive results.
Many thanks.
a really simple solution could be to put hit test areas on your plane object (I've left them slightly red for the example but you would make them transparent). I created my plane class in the editor and dragged it as a reference into my scene, but you could easily just program the hit zones from scratch as well.
Then in your plane class add them to the plane and detect touches within the plane class itself. It's not exact but not that much farther off then your alpha tracing was in your picture.
A nice bonus of this method is that you can isolate this touches to areas of the plane. Some uses for that could be...
touch left/right wing to turn that direction
touch body to refuel
touch wings to change guns
here is the code I used in my Plane class
class Plane: SKSpriteNode {
private var background: SKSpriteNode!
private var wingsHitTest: SKSpriteNode!
private var bodyHitTest: SKSpriteNode!
private var smallWingsHitTest: SKSpriteNode!
init() {
super.init(texture: nil, color: .clear, size: CGSize.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
isUserInteractionEnabled = true
self.color = .clear
if let background = self.childNode(withName: "//background") as? SKSpriteNode {
self.background = background
}
if let wingsHitTest = self.childNode(withName: "//wingsHitTest") as? SKSpriteNode {
self.wingsHitTest = wingsHitTest
}
if let bodyHitTest = self.childNode(withName: "//bodyHitTest") as? SKSpriteNode {
self.bodyHitTest = bodyHitTest
}
if let smallWingsHitTest = self.childNode(withName: "//smallWingsHitTest") as? SKSpriteNode {
self.smallWingsHitTest = smallWingsHitTest
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch? {
let touchLocation = touch.location(in: self)
if wingsHitTest.contains(touchLocation) {
print("hit in the wings")
}
else if smallWingsHitTest.contains(touchLocation) {
print("hit in the back wings")
}
else if bodyHitTest.contains(touchLocation) {
print("hit in the body")
}
}
}
}
It seems like the behavior you want is not included in SpriteKit so you will have to implement it yourself.
Quoting from the link above:
You will have to find out the locationInNode(spriteNode) of the UITouch object object to get the coordinates within the sprite and then read the alpha value from the sprite Image (not trivial) or precompute bitmasks (ones and zeros) from your images and read the value at the corresponding point in the bitmask for the sprite
So you will need to load your image as UIImage or CGImage and check whether the color at the pixels where the touch occurred was transparent or not. You can find more information on how to do that in this question: How to get pixel color from SKSpriteNode or from SKTexture?

Moving Eight Nodes Across the Screen (At Random Starting Positions/Intervals)?

I’ve created 8 paddles in gamescene.sks. I know how to control them manually but in this case, I want them all to begin automatically oscillating back and forth in their respective ‘lanes’ (meaning they won’t collide with one another) once a button is pressed (I haven’t created the button because idk if that should come before or the paddles function correctly).
I’m not asking for code but I just need a general idea of the commands that I need to implement in order to move a single paddle (positioned randomly) back and forth linearly when a button is pressed. I’m sure I can figure everything else out from there.
Thanks in Advance!!!
START button pressed → 8 paddles (placed randomly in their respective lanes) begin oscillating back and forth (out of sync with one another)
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var squareRight = SKSpriteNode()
var squareLeft = SKSpriteNode()
var paddleL1 = SKSpriteNode()
var paddleL2 = SKSpriteNode()
var paddleL3 = SKSpriteNode()
var paddleL4 = SKSpriteNode()
var paddleR1 = SKSpriteNode()
var paddleR2 = SKSpriteNode()
var paddleR3 = SKSpriteNode()
var paddleR4 = SKSpriteNode()
override func didMove(to view: SKView) {
squareRight = self.childNode(withName: "squareRight") as! SKSpriteNode
squareLeft = self.childNode(withName: "squareLeft") as! SKSpriteNode
paddleL1 = self.childNode(withName: "paddleL1") as! SKSpriteNode
paddleL2 = self.childNode(withName: "paddleL2") as! SKSpriteNode
paddleL3 = self.childNode(withName: "paddleL3") as! SKSpriteNode
paddleL4 = self.childNode(withName: "paddleL4") as! SKSpriteNode
paddleR1 = self.childNode(withName: "paddleR1") as! SKSpriteNode
paddleR2 = self.childNode(withName: "paddleR2") as! SKSpriteNode
paddleR3 = self.childNode(withName: "paddleR3") as! SKSpriteNode
paddleR4 = self.childNode(withName: "paddleR4") as! SKSpriteNode
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
As a general set of guidelines, I would:
Create a PaddleDirection enum with values of stationary,
movingRight and movingLeft
Subclass SKSpriteNode to create a PaddleNode
Add a property var currentDirection = PaddleDirection.stationary to your PaddleNode.
Make sure all your paddles are PaddleNodes and not SKSpriteNodes
When the start button is pressed, enumerate through all the
PaddleNodes and set their currentDirection properties to movingRight (or left, or randomly select one or the other)
In update() enumerate over all the paddles and adjust their
position according to whether they are moving left or right.
When each paddle reaches the limit of its movement, change its
direction from left to right or vice-versa.

When the screen is touched, how do I make the game over?

I want a game over screen and the game to stop when the screen is touched during a specific animation.
let lightTexture = SKTexture(imageNamed: "green light.png")
let lightTexture2 = SKTexture(imageNamed: "red light.png")
let animateGreenLight = SKAction.sequence([SKAction.waitForDuration(2.0, withRange: 0.1), SKAction.animateWithTextures([lightTexture, lightTexture2], timePerFrame: 3)])
let changeGreenLight = SKAction.repeatActionForever(animateGreenLight)
let animateRedLight = SKAction.sequence([SKAction.waitForDuration(2.0, withRange: 0.1), SKAction.animateWithTextures([lightTexture, lightTexture2], timePerFrame: 3)])
let changeRedLight = SKAction.repeatActionForever(animateRedLight)
let greenLight = SKSpriteNode(texture: lightTexture)
greenLight.position = CGPointMake(CGRectGetMidX(self.frame), 650)
greenLight.runAction(changeGreenLight)
self.addChild(greenLight)
let redLight = SKSpriteNode(texture: lightTexture2)
redLight.position = CGPointMake(CGRectGetMidX(self.frame), 650)
redLight.runAction(changeRedLight)
self.addChild(redLight)
When the animation for the red light is on the screen, I want it to be game over. Do I have to make an if statement, and if so for what specifically?
Thank You in advance!
You can make yourself another node which has the same size and position as the red light. Additionally, that node is able to handle touch events. Then, add that node before the animation runs. This can be done with a sequence of actions, e.g.:
let addAction = SKAction.runBlock({ self.addChild(touchNode) })
let animationAction = SKAction.repeatActionForever(animateRedLight)
redLight.runAction(SKAction.sequence([ addAction, animationAction ]))
Update
Since you want the game to end when you touch anywhere, alter the code such that the block sets a variable which indicates that the animation is executed and implement touchesBegan which checks for that variable, e.g.
let addAction = SKAction.runBlock({ self.redLightAnimationRuns = true })
[...]
// In touchesBegan
if touched
{
if redLightAnimationRuns
{
endGame()
}
}
use the touchesBegan() function to call a GameOver() function when the red light is on the screen (which you can control with a variable).
So, when the red light comes on to the screen, variable redLightCurrent is set to true. in TouchesBegan(), when redLightCurrent is true, then call the gameOver() function where you can include what to do when the game is over. This will only occur when a touch has began on the screen.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
let array = Array(touches)
let touch = array[0] as UITouch
let touchLocation = touch.locationInNode(self)
if redLightCurrent {
gameOver()
}
}
This code works with the new xCode 7 and Swift 2.0

How does SKScene.anchorPoint affects physicsBody?

When I changed anchorPoint for the GameScene (from default 0,0 to 0.5,0.5) the physicsBody for the sprite disappeared from the screen debugging with kView.showsPhysics = true.
It still exists, because each ship collides with each other but it looks like position of the physicsBody was shifted (out of screen).
Why this change affects physicsBody of scene's child?
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
anchorPoint = CGPointMake(0.5, 0.5)
physicsWorld.gravity = CGVector(0, 0)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let sprite = SKSpriteNode(imageNamed:"Spaceship")
sprite.physicsBody = SKPhysicsBody(circleOfRadius: 100)
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = location
let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
sprite.runAction(SKAction.repeatActionForever(action))
self.addChild(sprite)
}
}
}
I did the same thing. I put the anchor point of the SKScene also in the middle (CGPointMake(0.5, 0.5)) and it totally destroyed the physics. You will notice this especially when adding joints to the physics scene, you will not be able to put the physics joint anchor points in the right place.
My advice, don't change the SKScene anchorpoint if using SKPhysics (costs me a lot of time and rewriting to find out).
Changing the anchor point of the scene does not change coordinate of physics world. The same goes with every node and its physics body. With your own nodes you can compensate using special SKPhysicsBody init with center: (the center in the owning node’s coordinate system).
With physics world as for now you can do nothing.

Resources