Swift 3.0 - Sprite Kit - Multitouch - swift3

I'm new to Swift SpriteKit, I want to make a game like a virtual joystick and two buttons(two nodes), I've enabled the multi-touch. However, whenever I move both virtual joystick and attack Spritenode, the virtual joystick of the button seems to be Jagging. How am I gonna separate the touches of virtual joystick from touches attackbutton
class GameScene: SKScene {
var defend : Bool = false
var attack : Bool = false
var stickMove : Bool = false
var stickEnd:Bool = false
var moveattack:Bool = false
var movedefend:Bool = false
var playermovement:Bool = true
let vj1 = SKSpriteNode(imageNamed: "vj1")
let vj2 = SKSpriteNode(imageNamed: "vj2")
let player = SKSpriteNode(imageNamed: "player")
let rotationSpeed :CGFloat = CGFloat(M_PI)
let rotationOffSet : CGFloat = -CGFloat(M_PI/2.0)
let attackvj = SKSpriteNode(imageNamed: "attackvj")
let defendvj = SKSpriteNode(imageNamed: "defendvj")
private var touchPosition: CGFloat = 0
private var targetZRotation: CGFloat = 0
override func didMove(to view: SKView) {
self.view?.isMultipleTouchEnabled = true
self.backgroundColor = SKColor.black
//position of joystick
vj1.zPosition = 1
vj1.xScale = 1.5
vj1.yScale = 1.5
self.addChild(vj1)
vj1.position = CGPoint(x: self.size.width*15/100, y:self.size.height*30/100)
vj2.zPosition = 1
vj2.xScale = 1.5
vj2.yScale = 1.5
self.addChild(vj2)
vj2.position = vj1.position
player.zPosition = 0
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
player.physicsBody!.affectedByGravity = false
player.position = CGPoint(x: self.size.width/2, y:self.size.height/2)
self.addChild(player)
attackvj.anchorPoint = CGPoint(x: 0.5, y:0.5)
attackvj.position = CGPoint(x: self.size.width*80/100, y:self.size.height*30/100)
attackvj.xScale = 2.0
attackvj.yScale = 2.0
self.addChild(attackvj)
defendvj.anchorPoint = CGPoint(x: 0.5, y:0.5)
defendvj.position = CGPoint(x: self.size.width*90/100, y:self.size.height*50/100)
defendvj.xScale = 2.0
defendvj.yScale = 2.0
self.addChild(defendvj)
vj1.alpha = 0.4
vj2.alpha = 0.4
attackvj.alpha = 0.4
defendvj.alpha = 0.4
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches){
let location = touch.location(in: self)
if vj2.contains(location){
stickEnd = false
stickMove = true
}
if defendvj.contains(location){
defend = true
}
if attackvj.contains(location){
attack = true
attackvj.xScale = 2.5
attackvj.yScale = 2.5
}
if(stickMove == true && attack == true){
moveattack = true
}
if(stickMove == true && defend == true){
movedefend = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches){
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
let v = CGVector(dx: location.x - vj1.position.x, dy: location.y - vj1.position.y)
print("locationsss" , location , "previouslocationsss", previousLocation)
let angle = atan2(v.dy, v.dx)
targetZRotation = angle + rotationOffSet
let length:CGFloat = vj1.frame.size.height / 2
let xDist:CGFloat = sin(angle - 1.57079633) * length
let yDist:CGFloat = cos(angle - 1.57079633) * length
if(stickMove == true){
if(vj1.frame.contains(location)){
vj2.position = location
}
else{
vj2.position = CGPoint(x: vj1.position.x - xDist, y: vj1.position.y + yDist)
}
if(attackvj.frame.contains(location)){//How am I gonna make this location in attackvj, not to influence my joystick location?
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if(stickMove == true && attack == false && defend == false){
let move:SKAction = SKAction.move(to: vj1.position, duration: 0.2)
move.timingMode = .easeOut
vj2.run(move)
stickEnd = true
stickMove = false
}
if(attack == true){
attack = false
attackvj.xScale = 2.0
attackvj.yScale = 2.0
moveattack = false
}
if(defend == true){
defend = false
movedefend = false
}
}
override func update(_ currentTime: TimeInterval) {
//rotation
if (stickEnd == false) {
var angularDisplacement = targetZRotation - player.zRotation
if angularDisplacement > CGFloat(M_PI) {
angularDisplacement = (angularDisplacement - CGFloat(M_PI)*2)
}
else if angularDisplacement < -CGFloat(M_PI) {
angularDisplacement = (angularDisplacement + CGFloat(M_PI)*2)
}
if abs(angularDisplacement) > rotationSpeed*(1.0/60.0){
let angularVelocity = angularDisplacement < 0 ? -rotationSpeed : rotationSpeed
player.physicsBody!.angularVelocity = angularVelocity
} else {
player.physicsBody!.angularVelocity = 0
player.zPosition = targetZRotation
}
}
else{
player.physicsBody!.angularVelocity = 0
}
//movement but use attack button to testing
if (attack == true)
{
player.position = CGPoint(x:player.position.x + cos(player.zRotation + 1.57079633),y:player.position.y + sin(player.zRotation + 1.57079633))
}
}

The problem you are facing is that you are mixing the contexts for your touches. This is making things more difficult and complicated than they need to be.
The easiest thing to do would be to make your virtual joystick a separate SKSpriteNode class that tracks its own touches and reports them. Same with the buttons - they track their own touches and report their state.
But if you want to continue with your current approach of having a high-level object track multiple touches, what you want to do is capture the context that each touch is associated with in touchesBegan, and then just update things on touchesMoved as necessary, canceling the touches in touchesEnded.
For instance, you want to associate a particular touch with the virtual joystick, because you don't want weirdness if they drag their finger off of it and over to the button, say. And you want to know exactly which touch is lifted off when the user lifts a finger.
Here's some sample code that should illustrate the process:
//
// This scene lets the user drag a red and a blue box
// around the scene. In the .sks file (or in the didMove
// function), add two sprites and name them "red" and "blue".
//
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var redTouch:UITouch?
private var blueTouch:UITouch?
override func didMove(to view: SKView) {
super.didMove(to: view)
isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Grab some references to the red and blue sprites.
// (They must be direct children of the scene, or the
// paths must be amended.)
guard let redBox = childNode(withName: "red") else { return }
guard let blueBox = childNode(withName: "blue") else { return }
for touch in touches {
// Get the location of the touch in SpriteKit Scene space.
let touchLocation = touch.location(in: self)
// Check to see if the user is touching one of the boxes.
if redBox.contains( touchLocation ) {
// If we already have a touch in the red box, do nothing.
// Otherwise, make this our new red touch.
redTouch = touch
} else if blueBox.contains( touchLocation ) {
// If we already have a touch in the blue box, do nothing.
// Otherwise, make this our new blue touch.
blueTouch = touch
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// We have already established which touches are active,
// and we have already tied them to the two contexts, so
// we just need to read their current location and update
// the location of the red and blue boxes for the touches
// that are active.
if let redTouch = redTouch {
guard let redBox = childNode(withName: "red") else { return }
let location = redTouch.location(in:self)
redBox.position = location
}
if let blueTouch = blueTouch {
guard let blueBox = childNode(withName: "blue") else { return }
let location = blueTouch.location(in:self)
blueBox.position = location
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// The parameter touches contains a list of ending touches,
// so we check the touches we are currently tracking to
// see if they are newly lifted. If so, we cancel them.
if let touch = redTouch {
if touches.contains( touch ) {
redTouch = nil
}
}
if let touch = blueTouch {
if touches.contains( touch ) {
blueTouch = nil
}
}
}
}
In the above code, we have separated out the touches on the red box and the blue box. We always know which touch is dragging the red box around and which touch is dragging the blue box around, if any. This is a simple example, but it's generalizable to your situation, where you'd have touches for the virtual joystick and each individual button.
Note that this approach works well for multitouch elements, too. If you have a map that you want to be zoomable, you can keep track of two touches so that you can compare them for pinch gestures. That way, if your pinch gesture accidentally strays over a button, you've already marked it as part of the pinch gesture, and know not to start triggering that button.
But again, a better approach would be to have a separate SKSpriteNode subclass that just tracks the joystick touches and reports its state to some higher-level manager class. You already know everything you need to know to do this - it's like what you have without all the extra checking to see if there are other buttons pressed. Same with the buttons. The only new part would be messaging "up the chain" to a manager, and that's pretty straightforward to deal with.

Related

Phantom Button in SpriteKit Scene

I have a simple game in which players get three rounds to achieve the highest score . The gameScene exists inside a SwiftUI View and is created like this:
var gameScene: SKScene {
let scene = NyonindoGameScene(
size: CGSize(
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height
)
)
scene.viewModel = self.viewModel
scene.scaleMode = .aspectFill
return scene
}
It is called from the body of the view (inside a GeometryReader inside a ZStack) using SpriteView(). The code was working great until I tested on a new iPhone 13, which gave me all kinds of quirky and unexpected behaviors. I won't elaborate on them now as I have fixed most, but I am still left with a "phantom" start button. It is designed to display different text depending on the round being played (viz.: "Start," "Try Again," "Last Chance") using a var that is accurately counting rounds. However, I get this at the end of the first round:
When this Frankenstein button gets tapped, the new round begins. HOWEVER, SKPhysicsContactDelegate didBegin(_:) does not get called and collisions are ignored. (In my general bafflement here, I don't know if this is a separate issue or one that will go away when I solve the overlapping button problem.)
In any case, here is the relevant code for the startButton:
func addStartButton(text: String) {
startButton.removeFromParent() // added as one of many failed remedies
let startButtonLabel = SKLabelNode(text: text)
startButtonLabel.fontName = SKFont.bold
startButtonLabel.fontSize = 40.0
startButtonLabel.fontColor = UIColor.white
startButtonLabel.position = CGPoint(x: 0, y: -12)
startButton.position = CGPoint(x:self.frame.midX, y:self.frame.midY)
startButton.zPosition = 3
startButton.addChild(startButtonLabel)
addChild(startButton)
}
The initial start button is called like this in didMove(to view: SKView):
if attempts == 0 {
addStartButton(text: "Start")
}
And the buttons for the second and third round are called inside a gameOver() function like this:
if attempts == 1 {
startButton.removeFromParent() // again, this is overkill as it gets removed before this...
let text: String = "Try Again!"
addStartButton(text: text)
}
if attempts == 2 {
startButton.removeFromParent()
let text: String = "Last Chance!"
addStartButton(text: text)
}
I originally had a switch statement instead of the two if statements, but that generated the same problem. Print statements to the console suggest that only one button is being called for each round, but the results suggest something different.
Any clues? (Apologies if I haven't provided enough code for an assessment.)
why are you removing the button? change it's label:
class TTESTGameScene: SKScene {
var allBoxes: [SKSpriteNode] = []
var startButton: SKShapeNode = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 200, height: 43), cornerRadius: 20)
override func didMove(to view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
view.allowsTransparency = true
self.backgroundColor = .clear
view.alpha = 1.0
view.isOpaque = true
view.backgroundColor = SKColor.clear.withAlphaComponent(0.0)
let nextButton = SKShapeNode(rect: CGRect(x: 0, y: view.frame.maxY - 40, width: 66, height: 33), cornerRadius: 20)
nextButton.fillColor = .yellow
nextButton.name = "nextButton"
let nextLabel = SKLabelNode(text: "")
nextLabel.fontSize = 40.0
nextLabel.fontColor = UIColor.white
nextLabel.position = CGPoint(x: 0, y: -12)
nextButton.addChild(nextLabel)
addChild(nextButton)
startButton.fillColor = .red
startButton.name = "startButton"
let startButtonLabel = SKLabelNode(text: "000")
startButtonLabel.fontSize = 30.0
startButtonLabel.fontColor = UIColor.white
startButtonLabel.horizontalAlignmentMode = .center
startButtonLabel.position = CGPoint(x: startButton.frame.size.width/2, y: 10)
startButtonLabel.name = "startButtonLabel"
startButton.position = CGPoint(x:self.frame.midX - startButton.frame.size.width/2, y:self.frame.midY)
startButton.zPosition = 3
startButton.addChild(startButtonLabel)
addChild(startButton)
}
var attempts: Int = 0
func nextLevel() {
//startButton.removeFromParent() // added as one of many failed remedies
var text = ""
if attempts == 0 {
text = "Start"
}
else if attempts == 1 {
text = "Try Again!"
}
else if attempts == 2 {
text = "Last Chance!"
}
if let label = startButton.childNode(withName: "//startButtonLabel") as? SKLabelNode {
label.text = text
attempts += 1
attempts = attempts > 2 ? 0:attempts
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self.view)
let sceneTouchPoint = self.convertPoint(fromView: location)
let touchedNode = self.atPoint(sceneTouchPoint)
print(touchedNode.name)
if touchedNode.name == "nextButton" {
nextLevel()
}
}
}
// A sample SwiftUI creating a GameScene and sizing it
// at 300x400 points
struct TTESTContentView: View {
var scene: SKScene {
let scene = TTESTGameScene()
scene.size = CGSize(width: 300, height: 400)
scene.scaleMode = .aspectFill
return scene
}
var body: some View {
SpriteView(scene: scene)
.frame(width: 300, height: 400)
//.ignoresSafeArea()
}
}
struct ContentViewTest_Previews: PreviewProvider {
static var previews: some View {
TTESTContentView()
}
}

Treating adjacent tracking areas as one contiguous area

I'm trying to present a UI of a title/less window when a mouse leaves a window's title or contentview, but not when moving from one to the other; in essence have the two tracking areas function as one (I resorted to this as I couldn't figure out how to create a single area when the title view is hidden):
override func mouseEntered(with theEvent: NSEvent) {
let hideTitle = (doc?.settings.autoHideTitle.value == true)
if theEvent.modifierFlags.contains(.shift) {
NSApp.activate(ignoringOtherApps: true)
}
switch theEvent.trackingNumber {
case closeTrackingTag:
Swift.print("close entered")
closeButton?.image = closeButtonImage
break
default:
Swift.print(String(format: "%# entered",
(theEvent.trackingNumber == titleTrackingTag
? "title" : "view")))
let lastMouseOver = mouseOver
mouseOver = true
updateTranslucency()
// view or title entered
if hideTitle && (lastMouseOver != mouseOver) {
updateTitleBar(didChange: !lastMouseOver)
}
}
}
override func mouseExited(with theEvent: NSEvent) {
let hideTitle = (doc?.settings.autoHideTitle.value == true)
switch theEvent.trackingNumber {
case closeTrackingTag:
Swift.print("close exited")
closeButton?.image = nullImage
break
default:
Swift.print(String(format: "%# exited",
(theEvent.trackingNumber == titleTrackingTag
? "title" : "view")))
let lastMouseOver = mouseOver
mouseOver = false
updateTranslucency()
if hideTitle && (lastMouseOver != mouseOver) {
updateTitleBar(didChange: lastMouseOver)
}
}
}
Additionally, there's a tracking rect on the close button to appear only when over. Anyway, from my tracer output I see the issue - mouse over window from beneath to over its title:
view entered
updateTitleBar
**view entered**
view exited
updateTitleBar
title entered
updateTitleBar
title exited
Note sure why I'm getting a second view entered event (view entered), but the movement out of the view and onto the adjacent title each triggers an updateTilebar() call which is visually stuttering - not remedied so far with animation:
fileprivate func docIconToggle() {
let docIconButton = panel.standardWindowButton(.documentIconButton)
if settings.autoHideTitle.value == false || mouseOver {
if let doc = self.document {
docIconButton?.image = (doc as! Document).displayImage
}
else
{
docIconButton?.image = NSApp.applicationIconImage
}
docIconButton?.isHidden = false
self.synchronizeWindowTitleWithDocumentName()
}
else
{
docIconButton?.isHidden = true
}
}
#objc func updateTitleBar(didChange: Bool) {
if didChange {
Swift.print("updateTitleBar")
if settings.autoHideTitle.value == true && !mouseOver {
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = 1.0
panel.animator().titleVisibility = NSWindowTitleVisibility.hidden
panel.animator().titlebarAppearsTransparent = true
panel.animator().styleMask.formUnion(.fullSizeContentView)
}, completionHandler: {
self.docIconToggle()
})
} else {
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = 1.0
panel.animator().titleVisibility = NSWindowTitleVisibility.visible
panel.animator().titlebarAppearsTransparent = false
panel.animator().styleMask.formSymmetricDifference(.fullSizeContentView)
}, completionHandler: {
self.docIconToggle()
})
}
}
}
So my question is about how to defer the actions when areas are adjacent.
They (titlebar & content view) are not siblings of each other so didn't think a hitTest() was doable but basically if I could tell if I was moving into the adjacent tracking area, I'd like it to be a no-op.
Any help with why animation isn't working would be a plus.
Not a true answer, but if you know the adjacent view's rect you can use the event's location to probe whether you'd want to ignore movements among adjacent views:
override func mouseExited(with theEvent: NSEvent) {
let hideTitle = (doc?.settings.autoHideTitle.value == true)
let location : NSPoint = theEvent.locationInWindow
switch theEvent.trackingNumber {
case closeTrackingTag:
Swift.print("close exited")
closeButton?.image = nullImage
break
default:
let vSize = self.window?.contentView?.bounds.size
// If we exit to the title bar area we're still "inside"
// and visa-versa, leaving title to content view.
if theEvent.trackingNumber == titleTrackingTag, let tSize = titleView?.bounds.size {
if location.x >= 0.0 && location.x <= (vSize?.width)! && location.y < ((vSize?.height)! + tSize.height) {
Swift.print("title -> view")
return
}
}
else
if theEvent.trackingNumber == viewTrackingTag {
if location.x >= 0.0 && location.x <= (vSize?.width)! && location.y > (vSize?.height)! {
Swift.print("view -> title")
return
}
}
mouseOver = false
updateTranslucency()
if hideTitle {
updateTitleBar(didChange: true)
}
Swift.print(String(format: "%# exited",
(theEvent.trackingNumber == titleTrackingTag
? "title" : "view")))
}
}

Why is this not saving?

I am trying to save a simple piece of information using NSUserdefaults. I am trying to save a SKSprite to have an alpha of 1. Here is how I am doing it.
First scene: Level select (sprite alpha is 0.2)
When user completes Level: (edit sprite in Level Select to equal one)
GameViewController:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = levelselectscene {
// Set the scale mode to scale to fit the window
scene.scaleMode = .fill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
Level Select:
override func didMove(to view: SKView) {
if unlockLevelTwoButton == true {
levelselectscene?.childNode(withName: "LevelTwoButton")?.alpha = 1
UserDefaults.standard.set(unlockLevelTwoButton, forKey: "LevelTwoUnlocked")
print("I got this far")
}
}
Level One:
func didBegin(_ contact: SKPhysicsContact) {
var bodyA = contact.bodyA
var bodyB = contact.bodyB
let threeStars = SKScene(fileNamed: "LevelCompleted3Star")
let fadeAction = SKAction.fadeAlpha(by: 1, duration: 0.45)
if bodyA.categoryBitMask == 1 && bodyB.categoryBitMask == 2 || bodyA.categoryBitMask == 2 && bodyB.categoryBitMask == 1{
print("TEST")
levelOneCompleted() //islevelonecompleted
unlockLevelTwoButton = true
//3 stars
threeStars?.scaleMode = .fill
self.view?.presentScene(threeStars!, transition: .fade(withDuration: 0.3))
}
3 Stars:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isLevelOneCompleted == true{
unlockLevelTwoButton = true
UserDefaults.standard.set(isLevelOneCompleted, forKey: "LevelOne")
UserDefaults.standard.synchronize()
levelselectscene?.scaleMode = .fill
levelselectscene?.childNode(withName: "levelTwoButton")?.alpha = 1
self.view?.presentScene(levelselectscene)
}
To me, it looks like the information should save. What am I doing wrong? I also have the keys set to retrieve:
if let z = UserDefaults.standard.object(forKey: "LevelTwoButton")
{
unlockLevelTwoButton = z as! Bool
}
Can't figure out why it's not saving!
Based on the code you've shown, you are saving it with one name, and retrieving it with a different name (LevelTwoUnlocked) vs (LevelTwoButton)

turning physics on and off

I'm trying to solving a problem where a sprite node can jump up through a platform but cannot jump back down. I tried using this code:
override func didMove(to view: SKView) {
if (thePlayer.position.y > stonePlatform1.position.y) == true {
stonePlatform1.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: stonePlatform.size.width * 0.9, height: stonePlatform.size.height * 0.75))
stonePlatform1.physicsBody!.isDynamic = false
stonePlatform1.physicsBody!.affectedByGravity = false
stonePlatform1.physicsBody!.categoryBitMask = BodyType.object.rawValue
stonePlatform1.physicsBody!.contactTestBitMask = BodyType.object.rawValue
stonePlatform1.physicsBody!.restitution = 0.4
}
}
The idea was to turn on the physics body of the platform on when the player is above the platform. However, the physics doesn't work at all when I use this code. In fact I tried using this code:
override func didMove(to view: SKView) {
if (thePlayer.position.y < stonePlatform1.position.y) == true {
stonePlatform1.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: stonePlatform.size.width * 0.9, height: stonePlatform.size.height * 0.75))
stonePlatform1.physicsBody!.isDynamic = false
stonePlatform1.physicsBody!.affectedByGravity = false
stonePlatform1.physicsBody!.categoryBitMask = BodyType.object.rawValue
stonePlatform1.physicsBody!.contactTestBitMask = BodyType.object.rawValue
stonePlatform1.physicsBody!.restitution = 0.4
}
}
and the physics doesn't turn on either. If the IF statement isn't there, the physics does work all of the time.
You can use the node velocity for this platforms, like this:
SpriteKit - Swift 3 code:
private var up1 : SKSpriteNode!
private var down1 : SKSpriteNode!
private var down2 : SKSpriteNode!
private var player : SKSpriteNode!
override func didMove(to view: SKView) {
up1 = self.childNode(withName: "up1") as! SKSpriteNode
down1 = self.childNode(withName: "down1") as! SKSpriteNode
down2 = self.childNode(withName: "down2") as! SKSpriteNode
player = self.childNode(withName: "player") as! SKSpriteNode
up1.physicsBody?.categoryBitMask = 0b0001 // Mask for UoPlatforms
down1.physicsBody?.categoryBitMask = 0b0010 // Mask for downPlatforms
down2.physicsBody?.categoryBitMask = 0b0010 // Same mask
}
override func update(_ currentTime: TimeInterval) {
player.physicsBody?.collisionBitMask = 0b0000 // Reset the mask
// For UP only Platform
if (player.physicsBody?.velocity.dy)! < CGFloat(0.0) {
player.physicsBody?.collisionBitMask |= 0b0001 // The pipe | operator adds the mask by binary operations
}
// For Down only platforms
if (player.physicsBody?.velocity.dy)! > CGFloat(0.0) {
player.physicsBody?.collisionBitMask |= 0b0010 // The pipe | operator adds the mask by binary operations
}
}
Source code with example here: https://github.com/Maetschl/SpriteKitExamples/tree/master/PlatformTest
The example show this:
Green platforms -> Down Only
Red platforms -> Up only
You could try just starting with the physics body as nil and then set the physics values to it after the player is above it. Also, this kind of code should be in the update function. Having it in didMove only lets it get called once.
override func update(_ currentTime: TimeInterval){
if (thePlayer.position.y < stonePlatform1.position.y) && stonePlatform1.physicsBody != nil {
stonePlatform1.physicsBody = nil
}else if (thePlayer.position.y > stonePlatform1.position.y) && stonePlatform1.physicsBody == nil{
setPhysicsOnPlatform(stonePlatform1)
}
}
func setPhysicsOnPlatform(_ platform: SKSpriteNode){
platform.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: stonePlatform.size.width * 0.9, height: stonePlatform.size.height * 0.75))
...
//the rest of your physics settings
}
You should also do some handling for the height of the player and your anchorPoint. Otherwise if your anchorPoint is (0,0) and the player is halfway through the platform, the physics will be applied and a undesirable result will occur.

draw animated circle in swift 3

Refrence : https://stackoverflow.com/a/26578895/6619234
how to erase and redraw circle on click evnet?
i tried to call addCircleView method on click event but circle is overlapping every time.
class CircleClosing: UIView {
var circleLayer: CAShapeLayer!
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
// Use UIBezierPath as an easy way to create the CGPath for the layer.
// The path should be the entire circle.
let circlePath : UIBezierPath!
circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 5)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
// Setup the CAShapeLayer with the path, colors, and line width
circleLayer = CAShapeLayer()
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeColor = UIColor.blue.cgColor
circleLayer.lineWidth = 20.0;
// Don't draw the circle initially
circleLayer.strokeEnd = 0.0
// Add the circleLayer to the view's layer's sublayers
}
override func layoutSubviews()
{
let frame = self.layer.bounds
circleLayer.frame = frame
layer.addSublayer(circleLayer)
}
required init?(coder aDecoder: NSCoder)
{ super.init(coder: aDecoder) }
func animateCircle(duration: TimeInterval) {
// We want to animate the strokeEnd property of the circleLayer
let animation = CABasicAnimation(keyPath: "strokeEnd")
// Set the animation duration appropriately
animation.duration = duration
// Animate from 0 (no circle) to 1 (full circle)
animation.fromValue = 0
animation.toValue = 1
// Do a linear animation (i.e. the speed of the animation stays the same)
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
// Set the circleLayer's strokeEnd property to 1.0 now so that it's the
// right value when the animation ends.
circleLayer.strokeEnd = 1.0
// Do the actual animation
circleLayer.add(animation, forKey: "animateCircle")
}
}
Add in your subview
func addCircleView() {
var circleView : CircleClosing!
let diceRoll = CGFloat(510) //CGFloat(Int(arc4random_uniform(7))*50)
let diceRolly = CGFloat(70)
let circleWidth = CGFloat(40)
let circleHeight = circleWidth
// Create a new CircleView
circleView = CircleClosing(frame:CGRect(x:diceRoll,y: diceRolly,width: circleWidth,height: circleHeight) )
view.addSubview(circleView)
// Animate the drawing of the circle over the course of 1 second
circleView.animateCircle(duration: 20.0)
}
Thanks in Advance
var circleView : CircleClosing!
func addCircleView() {
let diceRoll = CGFloat(510) //CGFloat(Int(arc4random_uniform(7))*50)
let diceRolly = CGFloat(70)
let circleWidth = CGFloat(40)
let circleHeight = circleWidth
//Add this line here to remove from superview
circleView.removeFromSuperview()
circleView = CircleClosing(frame:CGRect(x:diceRoll,y: diceRolly,width: circleWidth,height: circleHeight) )
view.addSubview(circleView)
// Animate the drawing of the circle over the course of 1 second
circleView.animateCircle(duration: 20.0)
}