Swift SpriteKit Layer/Node doesn't load fast enough - ios

I created my first simple IOS game and created to Layers the gameLayer with my game and a pauseLayer with a resume Button/Node. In my gameLayer I have a button to pause the game and in my pauseLayer a Button/Node to resume. I did this like that in my function that gets points from the touchesEnded function:
var ispaused = false
func touchend(atPoint pos : CGPoint) {
if ispaused == false && pausebutton.contains(pos) {
pauseLayer.isHidden = false
view?.isPaused = true
ispaused = true
}
if ispaused == true && resumebutton.contains(pos) {
pauseLayer.isHidden = true
view?.isPaused = false
ispaused = false
}
}
Everything is working besides that I cant see my resume Button. I can click where it should be and the game resumes. But when I delete the line view?.isPaused = truethe button is displayed as it should be.
This gave me the idea that the pausing view might also pause the process of loading/displaying my resume Button. How can I avoid this problem?

Well to test out your theory of it not loading quick enough.
You can delay code execution with this:
let delayDuration:Double = 1.0 /* In seconds */
let totalDelayTime = DispatchTime.now() + delayDuration
DispatchQueue.main.asyncAfter(deadline: totalDelayTime, execute: { view?.isPaused = true })
It will wait 1 second before evaluating the view?.isPaused

Try this also, not sure if it will work.
defer { view?.isPaused = true }
The defer statement delays execution until the end of the current scope. Which would be the end of the touchEnd function

Related

SKAction.run not working until program execution paused/resumed

First time using SpriteKit - tapping a start button calls startLevel method in GameScene that has a player sprite fire two SKAction.run statements in a SKAction.sequence. The startLevel method has a completion block that triggers a message to the console and updates game state.
Tapping start displays the message, but the SKAction.run statements don't work unless I first pause program execution, and then click resume before tapping the start button. If I stop the level and tap a retry button, the same startLevel method is called, and it works fine without pausing execution.
Here's the method (player, marker, and door, are all optional objects that store their sprites; when tapping start, I verified that none of the sprites are nil, none of them are paused, and GameScene and SKView are not paused):
func startLevel(finished: () -> ()) {
guard let player = player,
let marker = marker,
let door = door else {
fatalError()
}
player.sprite.run(SKAction.sequence([
SKAction.run({ marker.pop(appear: true) }),
SKAction.run({ door.slide(open: true) }),
]))
finished()
}
Here's the GameScene code that calls startLevel:
startLevel(finished: { () -> () in
print("ALERT: startLevel finished")
levelStarted = true
})
I tried adding a run method completion block with a console message, and the message never prints. It seems that the player.sprite.run method is simply ignored (unless I pause execution first), and I don't understand why. Thanks for your help.
BTW - all the code above is outside GameScene’s update method, and the marker and door methods above are both used in didBegin() and didEnd(), and they work fine there.
In iOS 11 Apple made Spritekit Scenes and objects paused by default.
What I do to combat this is...
in my SKScene
override func didMove(to view: SKView) {
//this ensures that the scene is not paused
self.isPaused = false
}
and in each of my custom objects setup or init
self.isPaused = false

Wait until a tap gesture from user?

In my spritekit game I would like users to choose starting positions on a map by tapping and then adding the sprite to the appropriate place. I am struggling to figure out how to make the game "wait" for the user input without advancing.
My initial thought was an infinite while loop, but that seems to simply freeze the game.
if self.settingsInstance.startPositionMode == "custom" {
for player in self.settingsInstance.gamePlayers {
if player.isPlayerHuman == true {
//Allow human to choose
player.isCurrentlyActive = true
while player.hasStartAssigned == false {
~ -> Wait until a tap gesture occurs
}
print("success!")
if player.hasStartAssigned == true{
player.isCurrentlyActive = false
}
}
}
}
In my tap handling function I first check for player.isCurrentlyActive == true and then set the .hasStartAssigned to true, but so far this method isnt working.
Does anyone have some pointers for how to "wait" for user input?
Thanks!

Why is my SpriteKit update() function creating multiple NSTimers?

I'm creating a SpriteKit game that updates based on the amount of time passed. The game spawns enemies using an NSTimer and its scheduledTimerWithTimeInterval method, calling the spawnEnemy function every 2.0 seconds.
When 5 seconds have passed there should be a very brief intermission, preventing new enemies from spawning in order to show a level change animation.
When the initial 5 seconds has been reached, everything works well up until the conditional where self.nextLevelDelayTicker == 100. Once this conditional is met, the "YOLO" string is only fired once in the console. However, I'm assuming multiple instances of NSTimer are being created and stored within self.timer since a massive amount of enemies are spawned after self.resumeGame() is called to create a new scheduled timer.
Any ideas on why this is happening even though I have flags set up within my conditional to only call the self.resumeGame() function once?
func resumeGame() {
// Start game timer
// Need a way to access ib action of pause button
self.timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "spawnEnemy", userInfo: nil, repeats: true)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if gameTicker.isActive == true {
gameTicker.increment()
}
// If gameTicker is equal to 5 seconds, increase enemy amount
if gameTicker.getTimePassed() % 500 == 0 {
self.enemyAmount += 1
self.timer?.invalidate()
levelCount += 1
gameTicker.isActive = false
}
// If level has been completed and last ghost has been killed, activate next level scene
if gameTicker.isActive == false && enemyArray.count == 0 {
self.nextLevelDelayTicker.increment()
if self.nextLevelDelayTicker.getTimePassed() == 100 {
print("YOLO")
self.gameTicker.isActive = true
self.nextLevelDelayTicker.reset()
self.resumeGame()
}
}
}
Trying to follow your code.. but I think your approach here isn't great for spritekit. It's probably making things way more complicated than it needs to be.
You can keep track of time using your update method directly. It would probably be worth rewriting this part of your code. Would work better within spritekit and be less prone to bugs.
All you really need is delta time.
scene properties
// time values
var delta = NSTimeInterval(0)
var last_update_time = NSTimeInterval(0)
// some time youre trying to keep track of
var timeLimit = NSTimeInterval(5)
var timeLimitMax = NSTimeInterval(5)
your scene's update method
func update(currentTime: NSTimeInterval) {
if last_update_time == 0.0 {
delta = 0
} else {
delta = currentTime - last_update_time
}
last_update_date = currentTime
// now we can keep track of time
timeLimit -= self.delta
if timeLimit <= 0 {
// do something and reset timer
timeLimit = timeLimitMax
}
}
Now if you're going to be consistently spawning something every number of seconds then we dont even need to bother with update to do this. Just put this in your viewDidLoad
Now we're running this code every two seconds forever. The best part is this will pause and resume with your game automatically. You don't have to manage SKAction too much. spritekit does it for you :)
let spawnAction = SKAction.repeatActionForever(
SKAction.sequence([
SKAction.waitForDuration(2),
SKAction.runBlock({
[unowned self] in
self.spawnEnemy()
})
])
)
runAction(spawnAction)

Small lag/jitter when tapping the screen

So my game is almost complete... but there's this little glitch or jitter that occurs when I press and hold my finger on the screen which, now I've noticed, I can't un-notice...
It happens really fast, and only happens when a function is called to handle tap&holds (long press). This happens after 0.2seconds have passed using a timer.
I've tried breakpointing it to pin down where exactly the jitter happens in the code, but it seems I can not fine tune it enough to locate it.
My update method is typical:
override func update(currentTime: CFTimeInterval) {
//calc delta time
if lastUpdateTime > 0 {
dt = currentTime - lastUpdateTime
} else {
dt = 0
}
lastUpdateTime = currentTime
//timer for handleLongPress
if touched {
longTouchTimer += dt
}
if longTouchTimer >= 0.2 && !canLongPressNow {
canLongPressNow = true
handleLongPress()
} else {
canLongPressNow = false
}
...
//switch GameSate
//switch CharacterState
}
My function to handleLongPress is this:
func handleLongPress() {
//switch gameState
//if in gameplay gamestate
if canLongPressNow {
//rotate player
//change character state
startPlayerAnimation_Sliding()
}
touched = false
longTouchTimer = 0
}
The startPlayerAnimation_Sliding() just iterates a texture array of the playerNode.
func startPlayerAnimation_Sliding() {
var textures: Array<SKTexture> = []
for i in 0..<KNumSlideFrames{
textures.append(SKTexture(imageNamed: "slide\(i)"))
}
let playerAnimation = SKAction.animateWithTextures(textures, timePerFrame: 0.3)
player.runAction(SKAction.repeatActionForever(playerAnimation), withKey: "sliding")
}
Is there anything noticeable that may be causing this?
update
I've removed this from my update(..) method, and it seems smooth again... and I have no idea why...? Maybe because it's removing a key (explosion) that hasn't been created yet? or the fact it's removing these keys every frame... Doesn't make sense though... But I'm calling it a night, and looking at this again tomorrow. Thanks for your help so far. Have a good evening. (will update tomorrow)
//for animations
switch characterState {
case .Running:
player.removeActionForKey("exploding")
player.removeActionForKey("sliding")
break
case .Sliding:
player.removeActionForKey("running")
player.removeActionForKey("exploding")
break
case .Exploding:
player.removeActionForKey("running")
player.removeActionForKey("sliding")
break
}
Yikes, how you create textures is what is slowing you down a lot, you are creating new textures every time a touch happens, this is not needed. Instead do:
var textures: Array<SKTexture> = []
var playerAnimation : SKAction?
func loadingPhase() //however this is defined for you
{
for i in 0..<KNumSlideFrames{
textures.append(SKTexture(imageNamed: "slide\(i)"))
}
playerAnimation = SKAction.repeatActionForever(SKAction.animateWithTextures(textures, timePerFrame: 0.3))
}
func startPlayerAnimation_Sliding() {
player.runAction(playerAnimation!, withKey: "sliding")
}

How to toggle two images to pause and play a game in SWIFT, SpriteKIT

I want to toggle between two images to pause and play my game. I tried using the code below but it doesn't work. I declared the pause and play as SKSpriteNodes().
Can anyone help?
func paused(){
self.scene?.view?.paused = true
pause.removeFromParent()
}
// PLAYING FUNCTION
func playing(){
self.scene?.view?.paused = false
play.removeFromParent()
}
As someone else told you, you aren't giving enough informations for us to give you a precise answer.
Anyway, the little part of your code that I can see doesn't seem to be right to me.
First of all you probably shouldn't use two nodes, nor delete any of them.
If your goal is simply to change the icon, you should use the same node and simply change its texture property.
If you'd like an example, I can give you one.
EDIT : Here is the example
// In this example, we will call playPauseNode the SKSpriteNode you are using as a button
var isPlaying = true
func buttonTouched() {
if isPlaying { // If the game is playing
playPauseNode.texture = SKTexture(imageNamed: "theNameOfYourPlayButtonImage")
isPlaying = false
}
else { // If the game is paused
playPauseNode.texture = SKTexture(imageNamed: "theNameOfYourPauseButtonImage")
isPlaying = true
}
}
I believe it should behave as you expect it to...

Resources