I'm trying to fade in an object into my game when I first touch the screen, but I think that because it was hidden before (when game was launched), it won't fade in, but only show without any animation.
Do you have any suggestions?
This is an example code:
import SpriteKit
class GameScene: SKScene {
var myLabel = SKLabelNode()
var gameStarted = Bool()
func setupMyLabel(){
myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.text = "Hello, World!"
myLabel.fontSize = 35
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
setupMyLabel()
self.addChild(myLabel)
myLabel.hidden = true
gameStarted = false
}
func startGame(){
myLabel.hidden = false
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
if gameStarted == false{
gameStarted = true
startGame()
self.myLabel.runAction(SKAction.fadeInWithDuration(2.0))
}
else{
//do nothing
}
}
}
According to Apple's documentation on fadeInWithDuration it states that:
When the action executes, the node’s alpha property animates from its
current value to 1.0.
So you're right in thinking it's because your node is hidden when it starts. =)
One possible solution would be to instead of setting the node's hidden property to true, instead set it's alpha value to 0. Or you could even create your own method to perform that includes the runAction method that would set the alpha to 0, un-hide the node, and then call SKAction.fadeInWithDuration similar to something below (please forgive any syntax errors, this is free-hand pseudo code)...
startGame()
self.fadeIn(self.myLabel, duration: 2.0)
...
func fadeIn() {
self.myLabel.alpha = 0.0
self.myLabel.hidden = false
self.myLabel.runAction(SKAction.fadeInWithDuration(2.0))
}
Related
I am trying to implement moving to another scene when a wheel stops rotating. The code I have is shown below. I cannot figure out how to detect when the velocity has reached 0.0?
import SpriteKit
import GameplayKit
class GameplayScene: SKScene {
var player: Player?;
override func didMove(to view: SKView) {
player = self.childNode(withName: "spinner") as! Player?;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "play_button" {
spin()
}
}
}
func spin () {
let random = GKRandomDistribution(lowestValue: 20, highestValue: 90)
let r = random.nextInt()
player?.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.frame.width))
player?.physicsBody?.affectedByGravity = false
player?.physicsBody?.isDynamic = true
player?.physicsBody?.allowsRotation = true
player?.physicsBody?.angularVelocity = CGFloat(r)
player?.physicsBody?.angularDamping = 1.0
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
So from here, I would like to execute the following when the wheel has stopped spinning:
let play_scene = Questions(fileNamed: "QuestionsScene")
play_scene?.scaleMode = .aspectFill
self.view?.presentScene(play_scene!, transition: SKTransition.doorsOpenVertical(withDuration: 1))
I have now edited the class and it looks as follows:
import SpriteKit
import GameplayKit
class GameplayScene: SKScene, SKSceneDelegate {
var player: Player?
override func didMove(to view: SKView) {
self.delegate = self
player = self.childNode(withName: "spinner") as! Player?;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "play_button" {
spin()
}
}
}
func spin () {
let random = GKRandomDistribution(lowestValue: 20, highestValue: 90)
let r = random.nextInt()
player?.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.frame.width))
player?.physicsBody?.affectedByGravity = false
player?.physicsBody?.isDynamic = true
player?.physicsBody?.allowsRotation = true
player?.physicsBody?.pinned = true
player?.physicsBody?.angularVelocity = CGFloat(r)
player?.physicsBody?.angularDamping = 1.0
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func didSimulatePhysics() {
if ((player?.physicsBody?.angularVelocity)! <= CGFloat(0.01))
{
print("Got it")
}
}
}
Problem is I still receive an error on the if statement within didSimulatePhysics function. The error I receive is "Thread 1: EXC_BAD_INSTRUCTION...."
Your wheel's SKPhysicsBody has a built-in property, angularVelocity, that tells you how fast it's spinning. You're already using it when you set it to r to start the wheel spinning.
To watch angularVelocity you can use didSimulatePhysics(). It gets called once every frame, right after the physics calculations are done. That will look something like this:
func didSimulatePhysics() {
if wheelIsSpinning && angularVelocity != nil && angularVelocity! <= CGFloat(0.001) {
wheelIsSpinning = false
// wheel has stopped
// add your code here
}
}
Due to the vagaries of physics modeling, the angularVelocity might never be exactly zero. So instead we watch for it to be less than some arbitrary threshold, in this case 0.001.
You don't want it to execute every frame when the wheel isn't moving, so I added a property wheelIsSpinning to keep track. You'll need to add it as an instance property to GameplayScene, and set it to true in spin().
I'm making a game using sprite kit and I want my Character to move across the screen when you hold down the left/right move button. The problem is that he only moves when the button is tapped, not held. I have looked everywhere for a solution but nothing seems to work!
Here's my code;
class Button: SKNode
{
var defaultButton: SKSpriteNode // defualt state
var activeButton: SKSpriteNode // active state
var timer = Timer()
var action: () -> Void
//default constructor
init(defaultButtonImage: String, activeButtonImage: String, buttonAction: #escaping () -> Void )
{
//get the images for both button states
defaultButton = SKSpriteNode(imageNamed: defaultButtonImage)
activeButton = SKSpriteNode(imageNamed: activeButtonImage)
//hide it while not in use
activeButton.isHidden = true
action = buttonAction
super.init()
isUserInteractionEnabled = true
addChild(defaultButton)
addChild(activeButton)
}
//When user touches button
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
action()
//using timer to repeatedly call action, doesnt seem to work...
self.timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(getter: Button.action), userInfo: nil, repeats: true)
//swtich the image of our button
activeButton.isHidden = false
defaultButton.isHidden = true
}
code..........
In my game scene...
// *** RIGHT MOVEMENT ***
let rightMovementbutton = Button(defaultButtonImage: "arrow", activeButtonImage: "arrowActive", buttonAction:
{
let moveAction = SKAction.moveBy(x: 15, y: 0, duration: 0.1)
self.player.run(moveAction)
})
You know when the button is touched because touchesBegan is called. You then have to set a flag to indicate that the button is pressed.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first!
if leftButton.containsPoint(touch.locationInNode(self)) {
leftButtonIsPressed = true
}
if rightButton.containsPoint(touch.locationInNode(self)) {
rightButtonIsPressed = true
}
}
In update(), call your function that flag is true:
update() {
if leftButtonIsPressed == true {
moveLeft()
}
if rightButtonIsPressed == true {
moveRight()
}
}
You set the flag to false when touchesEnded is called for that button:
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first!
if leftButton.containsPoint(touch.locationInNode(self)) {
leftButtonIsPressed = false
}
if rightButton.containsPoint(touch.locationInNode(self)) {
rightButtonIsPressed = flase
}
}
Edit:
As pointed out by KoD, a cleaner way to do this (for movement buttons) is with SKAction which removes the need for the flag:
Define SKActions for moveTo x:0 and moveTo x:frame.width in
didMoveTo(View:)
In touchesBegan, run the correct SKAction on the correct object
specifying a key for the SKAction.
In touchesEnded, remove the relevant SKAction.
You'll have to do some maths to calculate how many points your object will have to move and then set a duration for the SKAction based upon this distance and a movement speed (in points per second).
Alternatively, (thanks to KnightOfDragons for this) create a SKAction.MoveBy x: which moves a small distance (based upon your desired movement speed) and with a duration of 1/60s. Repeat this action forever (SKAction.repeatForever) when the button is touched and remove the repeating SKAction when the button is released.
Explanation
I'm trying to speed up my game when touching the screen (and decreasing its speeds when touching again). I kinda made it work, but not fully.
When touching the screen, all actions are removed and ran again (so it can read the new gameSpeed). But the sprites that were already spawned, won't be applied to the new action, so it still runs by the last gameSpeed. Is there any way to make all the sprites be speeded up?
Code
This is the test code I made (you can download it here):
import SpriteKit
class GameScene: SKScene {
// MARK: - Declare
lazy var frameHeight : CGFloat = CGFloat(self.frame.height)
var ball = SKSpriteNode()
var speeded = Bool()
var gameSpeed = CGFloat(1)
// MARK: - Setup
func setupBall(){
ball = SKSpriteNode(imageNamed: "ball")
ball.position = CGPointMake(self.frame.width / 2, self.frame.height)
}
// MARK: - Action
func actionsBall(){
// Spawn ball
self.setupBall()
self.addChild(self.ball)
// Move down and remove when go off screen
let duration = (NSTimeInterval((0.003 / gameSpeed) * frameHeight))
let moveBall = SKAction.moveByX(0, y: -frameHeight, duration: duration)
let removeBall = SKAction.removeFromParent()
self.ball.runAction(SKAction.sequence([moveBall, removeBall]))
}
// MARK: - First Event
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// Start spawning, moving and removing
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.waitForDuration((0.00075 / Double(gameSpeed)) * Double(frameHeight)), SKAction.runBlock(self.actionsBall)])))
}
// MARK: - Touch
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
if speeded == false{
speeded = true
gameSpeed = 2
removeAllActions()
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.waitForDuration((0.00075 / Double(gameSpeed)) * Double(frameHeight)), SKAction.runBlock(self.actionsBall)])))
}
else if speeded == true{
speeded = false
gameSpeed = 1
removeAllActions()
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.waitForDuration((0.00075 / Double(gameSpeed)) * Double(frameHeight)), SKAction.runBlock(self.actionsBall)])))
}
}
// MARK: - Update
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Thanks in advance,
Luiz.
In the didMove method of your SKScene class, add the following:
physicsWorld.speed = CGFloat(2.0)
Most likely you simply didn't pass it as a CGFloat or it was not included in the proper method.
I'm unable to move the sprite even after I set the class on the Sprite in the GameScene. Below is my code for the GameplayScene class. I did not include my Player class because the code is really basic. (just a movePlayer bool function with a if statement that moves based on the x position)
import SpriteKit
class GameplayScene: SKScene {
var player: Player? // created new varible player for the scene, inherited the player class
var canMove = false // created new varible for letting player move; false by default
var moveLeft = false // created new varible to move player left; false by default
var center: CGFloat? // created new varible to set center of screen
override func didMoveToView(view: SKView) {
center = (self.scene?.size.width)! / (self.scene?.size.height)! //calculates center of screen
player.self?.childNodeWithName("Player") as? Player! // adds node "Player" within Player class(Player!)
}
override func update(currentTime: NSTimeInterval) {
managePlayer() //constantly checks if player can/is moving
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self) // gets location inside scene
if location.x > center { // if statement to check if user to taping screen on the right
moveLeft = false //moves player right
} else {
moveLeft = true
}
}
canMove = true
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
canMove = false //says if player is not touching screen, the player will not move
}
func managePlayer() {
if canMove {
player?.movePlayer(moveLeft) //takes the Player varible(which has the class), then grabs the move player func, then checks argument moveLeft
}
}
}
add player = self.childNode(withName: "Player") as? Player to func didMove in GameplayScene.swift
override func didMove(to view: SKView) {
print("didmove")
center = (self.scene?.size.width)! / (self.scene?.size.height)!
player = self.childNode(withName: "Player") as? Player
}
What I want to know:
I want to know that how to make buttons/labels appear and disappear. When my character collides with an object the buttons/labels will show up over the view and the game-view wont be working any more, only the buttons/labels that appeared can be interacted with.
What I have tried:
I have tried .hidden = false and .hidden = true but it didn't work but maybe I was not using it correctly.
CODE: I have delete unnecessary code!
import Foundation
import AVFoundation
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var movingGround: PPMovingGround!
var square1: PPSquare1!
var square2: PPSquare2!
var wallGen: PPWallGen!
var isStarted = false
var isGameOver = false
override func didMoveToView(view: SKView) {
addMovingGround()
addSquare1()
addWallGen()
start()
}
func addSquare1() {
square1 = PPSquare1()
square1.position = CGPointMake(70, movingGround.position.y + movingGround.frame.size.height/2 + square1.frame.size.height/2)
square1.zPosition = 1
playerNode.addChild(square1)
}
func addWallGen() {
wallGen = PPWallGen(color: UIColor.clearColor(), size: view!.frame.size)
wallGen.position = view!.center
addChild(wallGen)
}
func start() {
isStarted = true
//square2.stop()
square1.stop()
movingGround.start()
wallGen.startGenWallsEvery(1)
}
// MARK - Game Lifecycle
func gameOver() {
isGameOver = true
// everything stops
//square2.fall()
square1.fall()
wallGen.stopWalls()
diamondGen.stopDiamonds()
movingGround.stop()
square1.stop()
//square2.stop()
// create game over label
let gameOverLabel = SKLabelNode(text: "Game Over!")
gameOverLabel.fontColor = UIColor.whiteColor()
gameOverLabel.fontName = "Helvetica"
gameOverLabel.position.x = view!.center.x
gameOverLabel.position.y = view!.center.y + 80
gameOverLabel.fontSize = 22.0
addChild(gameOverLabel)
func restart() {
let newScence = GameScene(size: view!.bounds.size)
newScence.scaleMode = .AspectFill
view!.presentScene(newScence)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
if isGameOver {
restart()
} else {
square1.flip()
}
}
override func update(currentTime: CFTimeInterval) {
// MARK: - SKPhysicsContactDelegate
func didBeginContact(contact: SKPhysicsContact) {
if !isGameOver {
gameOver()
} else {
println("error, not game over!"
}
Without seeing your code, this is a little hard to determine, but I would suggest the following:
Be sure you have connected the buttons to an Outlet variable. This is critical. Without connecting them, you can use the hidden boolean, but it would not have an effect on an actual button.
Be sure you are not somehow undoing your own changes. For example, further down in the code, you might have something which is setting hidden to false even after you set it to true, and so on.
In some cases, you might want to set your outlet variable as strong instead of weak. This may retain changes that are being lost with a view switch.
You can also use "alpha" such as:
myButton.alpha = 0
as an alternate way of controlling visibility. 0 would set the alpha to none (which would make the button invisible) and 1 would set the alpha to full (which would make the button visible again.)
Right after you set hidden (or alpha) put in:
println("i hid the button!")
just to be sure the code you think you are executing really is being executed. Sometimes code we think is not working is actually not even being called.
Please provide more info and I will gladly work to get this solved for you.