In my game, I want a game over label to appear when the screen is touched during the red light animation. The red light animation is on when the green light animation is off. I want the game screen to pause and the game over label to appear when the player touches the screen during the red light animation. I have this so far, but the app crashes when I try to run it.
Error Message:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent: name:'(null)' text:'Game Over! Tap to Play Again' fontName:'Helvetica' position:{1024, 768}'
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
if gameOverLabel.parent == nil
{
self.addChild(gameOverLabel)
}
println(score)
scoreLabel.text = "\(score)"
}
Try this. You were setting the gameOverLabel with a font size that was too big and frame that was outside the screen. This was why it was not visible. Also you have to stop the game when it reaches game over.
if (!self.paused)
{
if isGreenLightON
{
score += 50
}
else
{
self.paused = true
if (gameOverLabel.parent != nil)
{
gameOverLabel.removeFromParent()
}
gameOverLabel.fontName = "Helvetica"
gameOverLabel.fontColor = UIColor.blackColor()
gameOverLabel.fontSize = 24
gameOverLabel.text = "Game Over! Tap to Play Again"
gameOverLabel.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height - 50)
self.addChild(gameOverLabel)
}
}
else
{
score = 0
if (gameOverLabel.parent != nil)
{
gameOverLabel.removeFromParent()
}
self.paused = false
}
scoreLabel.text = "\(score)"
Related
I am creating an iPhone app in which the user taps structures on an image of an MRI scan to select regions of interest and calculate a score. Each screen contains a UIView with multiple UIImage views. Each UIImageView that is touched by the user is "highlighted". However, on one of the screens the structures of interest are so close together that the frame rectangles of the UIImageView overlap (3 of them). Now, tapping in some areas highlights the wrong image. Rearranging views does not help (moving to front or back) doesn't help, there is a large area of overlap. I included a screen shot of the Storyboard and corresponding code of the view controller. Can anybody think of a solution??
override func touchesBegan(_ touches: Set, with event: UIEvent?)
{
let touch = touches.first!
switch touch.view {
case PO_PV_R:
if PO_PV_R.isHighlighted == false
{
PO_PV_R.isHighlighted = true
loesScore.parieto_occipital_R_perivent = 0.5
}
else
{
PO_PV_R.isHighlighted = false
loesScore.parieto_occipital_R_perivent = 0.0
}
case PO_C_R:
if PO_C_R.isHighlighted == false
{
PO_C_R.isHighlighted = true
loesScore.parieto_occipital_R_central = 0.5
}
else
{
PO_C_R.isHighlighted = false
loesScore.parieto_occipital_R_central = 0.0
}
case PO_SC_R:
if PO_SC_R.isHighlighted == false
{
PO_SC_R.isHighlighted = true
loesScore.parieto_occipital_R_subcortical = 0.5
}
else
{
PO_SC_R.isHighlighted = false
loesScore.parieto_occipital_R_subcortical = 0.0
}
default:
print("default")
}
let score = loesData.shared.totalLoesScore()
scoreLabel.text = String(format:"%.1f", score)
}
I am trying to create a pause menu on the game layer. I'm doing this just by adding two buttons on top when the pause button is pressed so the player can still see the stage of the game
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "pause" {
let button: SKSpriteNode?
let button2: SKSpriteNode?
button = SKSpriteNode(imageNamed: "button_continue")
button?.name = "button_continue"
button?.size.height = 55
button?.size.width = 215
button?.physicsBody = SKPhysicsBody(rectangleOf: button!.size)
button?.physicsBody?.allowsRotation = false
button?.position = CGPoint(x: 0, y: 160)
button?.zPosition = 100
self.addChild(button!)
button2 = SKSpriteNode(imageNamed: "button_finish")
button2?.name = "button_finish"
button2?.size.height = 55
button2?.size.width = 215
button2?.physicsBody = SKPhysicsBody(rectangleOf: button2!.size)
button2?.physicsBody?.allowsRotation = false
button2?.position = CGPoint(x: 0, y: -50)
button2?.zPosition = 100
self.addChild(button2!)
// The two buttons are created but here I am trying to stop everything until the continue button is pressed and I don't find a way
}
}
}
I have tried sleep() or timer.invalidate() but neither of them work with if statements, and I can't use while loops because then buttons don't appear until the while loop finishes:
while atPoint(location).name != "button_cotinue" {
timer1.invalidate()
}
That is what I tried and doesn't work.
Then when the continue button is pressed I will also delete the buttons but I can code that correctly. As well the finish button sends the player to the main menu scene.
what I do is put the controls (buttons) in a layer named controlsLayer that is separate from all of the game play items such as player, enemies and obstacles. Then I put all of the game items that I want to be paused in a layer named gameLayer. I make the controlsLayer have a high zPosition like 100.
then when I click my pause button I call gameLayer.isPaused = true and when I click continue I would call the opposite. gameLayer.isPaused = false. by just pausing the gameLayer, you can still run other actions like transitions or effects on your pause buttons. If you pause the whole scene you won't be able to run any actions or effects on any items.
I am currently working at a sidescroller game, in which the jump height depends on how long the player presses the right half of the screen.
Everything works just fine, except if the user touches the screen quickly. This causes the jump to be as big as possible.
Am I doing something wrong or is this just a problem with the way SpriteKit works?
How can I solve this problem?
EDIT: Here are all the methods handling touches in my game:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
for touch in touches
{
swiped = false
let location = touch.location(in: cameraNode)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.065)
{
if self.swiped == false
{
if location.x < 0
{
self.changeColor()
}
else
{
self.jump()
}
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
{
for touch in touches {
let location = touch.location(in: cameraNode)
if location.x > 0
{
// Right
thePlayer.endJump()
}
}
}
And then there is also a gesture recognizer handling swiping left and right, with the following handlers:
#objc func swipedRight()
{
if walkstate != .walkingRight
{
walkstate = .walkingRight
}
else
{
boost(direction: 0)
}
swiped = true
}
#objc func swipedLeft()
{
if walkstate != .walkingLeft
{
walkstate = .walkingLeft
}
else
{
boost(direction: 1)
}
swiped = true
}
Hopefully this is enough to describe the problems. The code above is everything I am doing to handle touches.
Well, the problem was, that I am using a DispatchQueue command to call the jumping method after a short delay, in case the user swiped and not tapped. As a result the touchesEnded method gets called before the jump has even started and thus can not be stopped anymore.
To solve this problem I added a boolean variable which is set to true as soon as the player touches the screen and set to false whenever the users finger leaves the screen. In order to jump this variable has to be set to true and thereby the character will not jump after a quick touch anymore.
I am having trouble figuring out the solution to this and am starting to get very frustrated with it.
I have a pause button and an unpause button in my game scene to allow the player to pause the game, which is the following code
else if (node == pauseButton) {
pauseButton.removeFromParent()
addChild(unpauseButton)
addChild(restartButton)
self.runAction (SKAction.runBlock(self.pauseGame))
}
func pauseGame(){
pauseButton.hidden = true
unpauseButton.hidden = false
scene!.view!.paused = true // to pause the game
}
Problem
The problem is that when I pause the game then unpause the game my player sprite seems to move two spaces forward automatically
I also have a tap and swipe gesture that allows me to move the player up left right and down when I tap anywhere on the screen.
func tapUp(){
let amountToMove:CGFloat = levelUnitHeight
let move:SKAction = SKAction.moveByX(0, y: amountToMove, duration: 0.1)
menubutton.hidden = true
settingsButton.hidden = true
highscoreLabel.hidden = true
pauseButton.hidden = false
thePlayer.runAction(move)
}
func swipedRight(){
let amountToMove:CGFloat = levelUnitHeight
let move:SKAction = SKAction.moveByX(amountToMove, y: 0, duration: 0.1)
thePlayer.runAction(move) // links the action with the players
}
As the member above me said the player is not really moving 2 spaces.
Also you should maybe change your strategy when pausing your game, because pausing the scene.view makes it very hard to add SpriteKit elements afterwards.
I think a better way is to create a worldNode in your GameScene and add all the sprites that need to be paused to that worldNode. It basically gives you more flexibility pausing a node rather than the whole scene.
First create a world node property
let worldNode = SKNode()
and add it to the scene in ViewDidLoad
addChild(worldNode)
Than add all the sprites you need paused to the worldNode
worldNode.addChild(sprite1)
worldNode.addChild(sprite2)
...
Create a global enum for your game states
enum GameState {
case Playing
case Paused
case GameOver
static var current = GameState.Playing
}
Than make a pause and resume func in your game scene
func pause() {
GameState.current = .Paused
// show pause menu etc
}
func resume() {
GameState.current = .Playing
self.physicsWorld.speed = 1
worldNode.paused = false
}
And finally add the actual pause code to your updateMethod. This way it will not resume the game even if spriteKit itself tries to resume (e.g. reopened app, dismissed alert etc)
override func update(currentTime: CFTimeInterval) {
if GameState.current == .Paused {
self.physicsWorld.speed = 0
worldNode.paused = true
}
}
In regards to your tapGesture recogniser, in the method that gets called after a tap you can add this before the rest of the code
guard GameState.current != .Paused else { return }
....
Hope this helps
I am adding sklabelnodes and skspritenodes to the scene with these functions:
var levelnode = SKSpriteNode()
var labelLevel = SKLabelNode(fontNamed: "Courier-Bold")
func addlevels(){
var level = score + 1
if (level > 0){
sprite()
levelnode.position = CGPointMake(frame.size.width / 2.2, -frame.size.height/15.6 - frame.size.height/17.6)
levelnode.name = "1"
addChild(levelnode)
sklabel()
labelLevel.text = ("1")
labelLevel.position.x = levelnode.position.x - (levelnode.size.width/40)
labelLevel.position.y = levelnode.position.y - (levelnode.size.width/6)
labelLevel.name = "1"
addChild(labelLevel)
}
if (level > 1){
sprite()
levelnode.position = CGPointMake(frame.size.width / 1.42 - frame.size.width/100, -frame.size.height/10 - frame.size.height/17.6)
levelnode.name = "2"
addChild(levelnode)
sklabel()
labelLevel.text = ("2")
labelLevel.position.x = levelnode.position.x - (levelnode.size.width/40)
labelLevel.position.y = levelnode.position.y - (levelnode.size.width/6)
labelLevel.name = "2"
addChild(labelLevel)
}
// this goes on and on till level 25
}
func sprite(){
levelnode = SKSpriteNode(imageNamed: "levelnode")
levelnode.size.width = frame.size.width / 8
levelnode.size.height = levelnode.size.width
levelnode.zPosition = 1
}
func sklabel(){
labelLevel = SKLabelNode(fontNamed: "Courier-Bold")
labelLevel.zPosition = 2
labelLevel.fontColor = SKColor.blackColor()
labelLevel.fontSize = frame.size.height / 35
}
When I am changing the scene, the sprites and labels are removed from the willmovefromview function:
override func willMoveFromView(view: SKView) {
removeAllChildren()
}
but this is taking too slow if I compare it with other scenes where even more skspritenodes are added..
I think It has to do with the functions where I add the sprites and labels, but what is wrong about it?
EDIT:
the function that takes me back too the menu:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
errint = false
for touch: AnyObject in touches {
let location: CGPoint! = touch.locationInNode(self)
let nodeAtPoint = self.nodeAtPoint(location)
if (nodeAtPoint.name != nil) {
if nodeAtPoint.name == "menu" {
removeallAction()
removeAllChildren()
var scene1 = GameMenuScene(size: self.size)
scene1.button = self.button
scene1.button2 = self.button2
scene1.button3 = self.button3
scene1.viewController = self.viewController
let transition = SKTransition.moveInWithDirection(SKTransitionDirection.Left, duration: 0.75)
self.view?.presentScene(scene1, transition: transition)
}
}
}
}
Doesn't transitioning to another scene deallocate the current scene?
And if that scene is deallocated, then there's no need for the nodes and such to be removed...
Could you do me a favor, and transition out without removing the children and actions, then transition back in and see if it matters??
Anyway, speed is trivial if you're testing on the simulator... the simulator really only simulates opengl, the core of SpriteKit.
Please try it on your device.
BTW, I'm not sure of stuff but nobody seemed to have answered you so I tried...
EDIT:
HOLY MOTHER OF GOD WHY DIDN'T I SEE THE TIMESTAMP IT WAS ASKED TWO MONTHS AGO.
Too late now I guess... but to anybody reading this, don't answer questions at 2 am.