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.
Related
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()
}
}
I try to make an endless background through the nodes, but the background has not become infinite and is interrupted, the third background is not yet shown. After the first show, the number of nodes in the scene grows, how can this be fixed?
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var bgNode: SKNode!
var overlay: SKNode!
var overlayWidth: CGFloat!
//var viewSize: CGSize!
var levelPositionX: CGFloat = 0.0
//var speed: CGFloat = 5.5
override func didMove(to view: SKView) {
setupNode()
//viewSize = CGSize(width: frame.size.width, height:
frame.size.height )
}
func setupNode() {
let worldNode = childNode(withName: "World")!
bgNode = worldNode.childNode(withName: "Background")!
overlay = bgNode.childNode(withName: "Overlay")!.copy() as!
SKNode
overlayWidth = overlay.calculateAccumulatedFrame().width
}
func createBackgroundOverlay() {
let backgroundOverlay = overlay.copy() as! SKNode
backgroundOverlay.position = CGPoint(x: 0.0, y: 0.0)
bgNode.addChild(backgroundOverlay)
levelPositionX += overlayWidth
}
func update() {
bgNode.position.x -= 5
if bgNode.position.x <= -self.frame.size.width {
bgNode.position.x = self.frame.size.width * 2
createBackgroundOverlay()
}
}
override func update(_ currentTime: TimeInterval) {
update()
}
In my endless runner game, I have implemented an endless background and a ground(or floor) much similar to your app. Below I shall discuss the steps i have used in my game.
Step 1: In your GameScene.swift file add these variables.
var backgroundSpeed: CGFloat = 80.0 // speed may vary as you like
var deltaTime: TimeInterval = 0
var lastUpdateTimeInterval: TimeInterval = 0
Step 2: In GameScene file, make setUpBackgrouds method as follows
func setUpBackgrounds() {
//add background
for i in 0..<3 {
// add backgrounds, my images were namely, bg-0.png, bg-1.png, bg-2.png
let background = SKSpriteNode(imageNamed: "bg-\(i).png")
background.anchorPoint = CGPoint.zero
background.position = CGPoint(x: CGFloat(i) * size.width, y: 0.0)
background.size = self.size
background.zPosition = -5
background.name = "Background"
self.addChild(background)
}
for i in 0..<3 {
// I have used one ground image, you can use 3
let ground = SKSpriteNode(imageNamed: "Screen.png")
ground.anchorPoint = CGPoint(x: 0, y: 0)
ground.size = CGSize(width: self.size.width, height: ground.size.height)
ground.position = CGPoint(x: CGFloat(i) * size.width, y: 0)
ground.zPosition = 1
ground.name = "ground"
self.addChild(ground)
}
}
Step 3: Now we have to capture timeIntervals from update method
override func update(_ currentTime: TimeInterval) {
if lastUpdateTimeInterval == 0 {
lastUpdateTimeInterval = currentTime
}
deltaTime = currentTime - lastUpdateTimeInterval
lastUpdateTimeInterval = currentTime
}
Step 4: Here comes the most important part, moving our backgrounds and groungFloor by enumerating child nodes. Add these two methods in GameScene.swift file.
func updateBackground() {
self.enumerateChildNodes(withName: "Background") { (node, stop) in
if let back = node as? SKSpriteNode {
let move = CGPoint(x: -self.backgroundSpeed * CGFloat(self.deltaTime), y: 0)
back.position += move
if back.position.x < -back.size.width {
back.position += CGPoint(x: back.size.width * CGFloat(3), y: 0)
}
}
}
}
func updateGroundMovement() {
self.enumerateChildNodes(withName: "ground") { (node, stop) in
if let back = node as? SKSpriteNode {
let move = CGPoint(x: -self.backgroundSpeed * CGFloat(self.deltaTime), y: 0)
back.position += move
if back.position.x < -back.size.width {
back.position += CGPoint(x: back.size.width * CGFloat(3), y: 0)
}
}
}
}
Step 5: At this point you should get this error:"Binary operator '+=' cannot be applied to two 'CGPoint' operands" in updateBackground and updateGroundMovement methods.
Now we need to implement operator overloading to resolve this problem. Create a new Swift File and name it Extensions.swift and then implement as follows:
// Extensions.swift
import CoreGraphics
import SpriteKit
public func + (left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
public func += (left: inout CGPoint, right: CGPoint) {
left = left + right
}
Step 6: call setUpBackgrounds method in didMove(toView:)
override func didMove(to view: SKView) {
setUpBackgrounds()
}
Step 7: Finally call the updateBackground and updateGroundMovement methods in update(_ currentTime) method. updated code is given below:
override func update(_ currentTime: TimeInterval) {
if lastUpdateTimeInterval == 0 {
lastUpdateTimeInterval = currentTime
}
deltaTime = currentTime - lastUpdateTimeInterval
lastUpdateTimeInterval = currentTime
//MARK:- Last step:- add these methods here
updateBackground()
updateGroundMovement()
}
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.
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)
i have a Problem with the method didBeginContact. I have a ball and a wall.
The collision of both objects works fine. If both objects collide, they change their position but the method didBeginContact is not called.
// constants.swift
...
let ballCategory: UInt32 = 0x1 << 0
let wallCategory: UInt32 = 0x1 << 1
...
// Physics balls (ball.swift)
...
init() {
let size = CGSize(width: 32, height: 44)
loadPhysicsBodyWithSize(size: size)
}
func loadPhysicsBodyWithSize(size: CGSize){
physicsBody = SKPhysicsBody(rectangleOf: size)
physicsBody?.categoryBitMask = ballCategory
physicsBody?.contactTestBitMask = wallCategory
physicsBody?.affectedByGravity = false
}
...
// Physics wall (wall.swift)
...
init() {
let size = CGSize(width: 32, height: 44)
loadPhysicsBodyWithSize(size: size)
}
func loadPhysicsBodyWithSize(size: CGSize){
physicsBody = SKPhysicsBody(rectangleOf: size)
physicsBody?.categoryBitMask = wallCategory
physicsBody?.affectedByGravity = false
}
...
// GameScene (gameScene.swift)
class GameScene: SKScene, SKPhysicsContactDelegate {
...
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
}
func didBeginContact(contact: SKPhysicsContact){
print("didBeginContact called")
}
}
Has anybody any idea? Thanks for help!
I solved this by changing
func didBeginContact(contact: SKPhysicsContact)
to
func didBegin(_ contact: SKPhysicsContact)