I am trying to do a registration scene inside a game on iOS. what I have so far is this:
Since I couldn't manage to put UITextField inside the Scene that I have, I tried to put SKNodes that activate the keyboard on touch. It would be very useful if someone could help me to put UITextFields inside my game scene (because it would be easier to get a registration scene, and with less code, and also with native support for this).
My code so far is this:
let nameForm:SKSpriteNode = SKSpriteNode(imageNamed: "RegistrationScene_TEST_textviews")
let usernameForm:SKSpriteNode = SKSpriteNode(imageNamed: "RegistrationScene_TEST_textviews")
let passwordForm:SKSpriteNode = SKSpriteNode(imageNamed: "RegistrationScene_TEST_textviews")
let passwordForm2:SKSpriteNode = SKSpriteNode(imageNamed: "RegistrationScene_TEST_textviews")
And when someone touch any of these nodes it should be managed with a method like this:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject()! as UITouch
let location = touch.locationInView(self.view)
for touch:AnyObject in touches{
if CGRectContainsPoint(nameForm.frame, touch.locationInNode(self)){
//Function to display the keyboard
//Function that saves the keystroke
//Function to display a nameLabel with the saved keystroke
}
//same for the other Forms
//methods for the buttons (DONE)
}
}
If there is any other way to achieve a text input into the game (to save game statistics or something useful for the gameplay) please let me know, any help would be very appreciated
EDIT:
According to Cristian Woerz, i made the following and this solved my problem.
let txtField:UITextField!
txtField = UITextField(frame: CGRect(x: 0, y: 0, width: 200, height: 30));
self.view?.addSubview(txtField!)
Normally, you shouldn't use UIKit-elements in a SpriteKit-game. But For this problem, you can use a UITextfield and add it to your view:
var txtField: UITextField = UITextField(frame: CGRect(x: 0, y: 0, width: 200, height: 30));
self.view.addSubview(txtField)
You could also make your UITextField semi transparent by setting the background-color alpha value to less then the default 1:
txtField.backgroundColor = UIColor(red: 125/255, green: 125/250, blue: 125/250, alpha: 0.5)
Related
I'd like for a rectangle move to move around the screen randomly when the user holds his/her finger on it, and stop moving if the user's finger moves off of it. In other words, so long as the user is able to keep up with it, it will keep moving. How should I go about doing this?
let rect = CGRect(x: 157, y: 398, width: 100, height: 100) // create rect
let view = UIView(frame: rect) // create view for rect
view.backgroundColor = .red // color rect
self.view.addSubview(view) // display rect
Simply you can use touch Methods to move your view
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch
let location = touch.location(in: self.view) //Gives you CGPoints
YourView.frame = CGRect(origin: location, size: Your size here>
}
That might help!
As the question states, I am attempting to call on a UITextField inside of SpriteKit to accept a user input for a name. I've looked at many posts on here but none of them seem to apply to the issue I am facing. The first thing I am doing is declaring the UITextField as a class variable, so I can remove it from view in a separate function later by doing the following:
let nameEntry = UITextField(frame: CGRect(origin: CGPoint(x: 800, y: 875), size: CGSize(width: 600, height: 200)))
then I go on to add the UITextField by stating:
self.view?.addSubview(nameEntry)
However, the text box does not show up in my scene. I've looked at it in hierarchy view and it is simply not there. I am fairly inexperienced at using SceneKit so I'm curious to see what I'm doing wrong. Thanks in advance for anyone who attempts to help!
for a better look at how I am going about this:
class Tutorial : SKScene{
let nameEntry = UITextField(frame: CGRect(origin: CGPoint(x: 800, y: 875), size: CGSize(width: 600, height: 200)))
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
//insert many lines of code here
self.view?.addSubview(nameEntry)
}
}
EDIT: Here's all of the code leading up to this point.
class Tutorial : SKScene{
var chatBoxInt : Int = 0
let chatBoxLabel : SKLabelNode = SKLabelNode(text: "")
let arrow = SKSpriteNode(imageNamed: "Arrow")
let HappinessIcon = SKSpriteNode(imageNamed: "Happiness Icon")
let IntelligenceIcon = SKSpriteNode(imageNamed: "IntelligenceIcon")
let HealthIcon = SKSpriteNode(imageNamed: "HealthIcon")
let chatBox = SKSpriteNode(imageNamed: "ChatBox")
let maleButtonBackground = SKSpriteNode(imageNamed: "ButtonBackground")
let femaleButtonBackground = SKSpriteNode(imageNamed: "ButtonBackground")
let selectedGenderButtonBackground = SKSpriteNode(imageNamed: "ButtonBackgroundSelected")
let femaleLabel = SKLabelNode(text: "Female")
let maleLabel = SKLabelNode(text: "Male")
let genderLabel = SKLabelNode(text: "Gender:")
let nameLabel = SKLabelNode(text: "Name:")
let nameEntry = UITextField(frame: CGRect(origin: CGPoint(x: 800, y: 875), size: CGSize(width: 600, height: 200)))
enum genders {
case Male
case Female
}
var genderSelected : genders = .Male
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "Background")
background.size = CGSize(width: self.size.width, height: self.size.height)
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
scene?.addChild(background)
Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(beginTutorial), userInfo: nil, repeats: false)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch : AnyObject in touches{
let pointOfTouch = touch.location(in: self)
if(maleButtonBackground.contains(pointOfTouch) || femaleButtonBackground.contains(pointOfTouch)){
if(maleButtonBackground.contains(pointOfTouch)){
swapGenderButton(ImageToSwap: maleButtonBackground)
genderSelected = .Male
}
if(femaleButtonBackground.contains(pointOfTouch)){
swapGenderButton(ImageToSwap: femaleButtonBackground)
genderSelected = .Female
}
}else{
chatBoxInt += 1
let fadeInAnimation = SKAction.fadeIn(withDuration: 1)
let fadeOutAnimation = SKAction.fadeOut(withDuration: 1)
let removeAction = SKAction.removeFromParent()
let fadeOutSequence = SKAction.sequence([fadeOutAnimation, removeAction])
switch chatBoxInt {
case 1:
ChangeText(LabelText: "Best Life is the place for you to make the life you've always dreamed of!")
case 2:
ChangeText(LabelText: "In Best Life you can be who you want to be, do what you want to do... as long as you don't die.")
case 3:
ChangeText(LabelText: "The goal of Best Life is simple, make the best life possible for yourself before your time runs out.")
case 4:
ChangeText(LabelText: "Let's go over some of the basics of best life:")
case 5:
ChangeText(LabelText: "When you get into your new life, you'll have a HUD at the bottom of your screen at all times. This HUD will act as your guide as you go throughout Best Life. Let's get familiar with the icons and what they mean.")
case 6:
ChangeText(LabelText: "This is your happiness indicator, it will show you how much you are enjoying your life, try and keep this high at all times... too little happiness can result in your alter ego committing suicide.")
HappinessIcon.position = CGPoint(x: self.size.width/2, y: (self.size.height/2) - 100)
HappinessIcon.size = CGSize(width: 200, height: 200)
HappinessIcon.zPosition = 2
HappinessIcon.alpha = 0
self.addChild(HappinessIcon)
HappinessIcon.run(fadeInAnimation)
case 7:
HappinessIcon.run(fadeOutSequence)
ChangeText(LabelText: "This is your intelligence indicator... it shows... well.... intelligence... what else would it do...? Too little of this and you may not get good jobs; leading to lower income.")
IntelligenceIcon.position = CGPoint(x: self.size.width/2, y: (self.size.height/2) - 100)
IntelligenceIcon.size = CGSize(width: 200, height: 200)
IntelligenceIcon.zPosition = 2
IntelligenceIcon.alpha = 0
self.addChild(IntelligenceIcon)
IntelligenceIcon.run(fadeInAnimation)
case 8:
IntelligenceIcon.run(fadeOutSequence)
ChangeText(LabelText: "This is your health icon. It shows your current overall health level. If this gets too low it can contribute to life threatning illnesses which can ultimately lead to your demise.")
HealthIcon.position = CGPoint(x: self.size.width/2, y: (self.size.height/2) - 100)
HealthIcon.size = CGSize(width: 200, height: 200)
HealthIcon.zPosition = 2
HealthIcon.alpha = 0
self.addChild(HealthIcon)
HealthIcon.run(fadeInAnimation)
case 9:
HealthIcon.run(fadeOutSequence)
ChangeText(LabelText: "The last thing that you need to know is that one day in your world will pass one year in your alter ego's life. Make sure you are making the most out of every day to make the best life possible for your alter ego!")
case 10:
ChangeText(LabelText: "Alright, that pretty much sums it up. The world is yours for the taking, go seize it! Have fun in your new Best Life!")
case 11:
ChangeText(LabelText: "First, we'll need to we'll need to set up your new alter ego. Some of these options can only be accessed by purchasing them, but since this is your first go around I'll cover this one for you.")
chatBoxLabel.run(SKAction.moveTo(y: 1750, duration: 1))
let changeSize = SKAction.scale(to: CGSize(width: 800, height: 1600), duration: 1)
chatBox.run(changeSize)
arrow.run(fadeOutSequence)
genderLabel.position = CGPoint(x: 500, y: 1300)
genderLabel.fontColor = .black
genderLabel.zPosition = 2
genderLabel.fontSize = 45
genderLabel.alpha = 0
self.addChild(genderLabel)
genderLabel.run(fadeInAnimation)
maleButtonBackground.position = CGPoint(x: 575, y: 1175)
maleButtonBackground.zPosition = 2
maleButtonBackground.setScale(0.3)
maleButtonBackground.alpha = 0
self.addChild(maleButtonBackground)
maleButtonBackground.run(fadeInAnimation)
femaleButtonBackground.position = CGPoint(x: 955, y: 1175)
femaleButtonBackground.zPosition = 2
femaleButtonBackground.setScale(0.3)
femaleButtonBackground.alpha = 0
self.addChild(femaleButtonBackground)
femaleButtonBackground.run(fadeInAnimation)
maleLabel.fontSize = 45
maleLabel.fontColor = .white
maleLabel.zPosition = 4
maleLabel.alpha = 0
maleLabel.position = CGPoint(x: 575, y: 1162)
self.addChild(maleLabel)
maleLabel.run(fadeInAnimation)
femaleLabel.fontSize = 45
femaleLabel.fontColor = .white
femaleLabel.zPosition = 4
femaleLabel.alpha = 0
femaleLabel.position = CGPoint(x: 955, y: 1162)
self.addChild(femaleLabel)
femaleLabel.run(fadeInAnimation)
nameLabel.position = CGPoint(x: 500, y: 1000)
nameLabel.fontSize = 45
nameLabel.fontColor = .black
nameLabel.alpha = 0
nameLabel.zPosition = 3
self.addChild(nameLabel)
nameLabel.run(fadeInAnimation)
nameEntry.backgroundColor = .white
self.view?.addSubview(nameEntry)
default:
return
}
}
}
}
SpriteKit's SKSKScene exists inside of UIKit's SKView. ie your whole Spritekit scene is basically the backing layer for a single view in your app. If you want to do things in the UIKit layer it makes more sense to do them at the level of your view controller and delegate the responsibility back. So, for instance if you want a textField you can set it up in interface builder and make it hidden, then show it only when your scene requests it.
If you don't want to do it this way, you can still reach up into your view as you are doing and add subviews from your SKScene (its bad OOP to reach up into and alter your parent, but nothing in UIKit prevents you from doing it). Most likely the issue you are having is that the coordinate systems in SpriteKit and UIKit are different and they are actually upside down. So you cannot just use your SKScene coordinates for UIViews. You need to convert. SKView has a family of methods that will do the math for you: SKView.convert(:to:) SkView.convert(:from:). Note that that only gets you as far as your SKView. You then need to convert to your viewController.view coordinates using UIKit's convert methods.
I think its far less confusing if you leave the UIKit stuff in the UIKit layer and just have the viewController and scene pass the relevant information back and forth.
EDIT: here is an example
import PlaygroundSupport
import SpriteKit
class GameScene: SKScene {
private lazy var textField: UITextField = {
let textField = UITextField()
textField.frame.size = CGSize(width: 100, height: 30)
textField.backgroundColor = .cyan
return textField
}()
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let view = view,
let point = touches.first?.location(in: self) else {
return
}
if textField.superview != nil {
textField.removeFromSuperview()
}
textField.center = view.convert(point, from: self)
view.addSubview(textField)
textField.becomeFirstResponder()
}
}
let sceneView = SKView(frame: CGRect(x:0 , y:0, width: 640, height: 480))
if let scene = GameScene(fileNamed: "GameScene") {
scene.scaleMode = .aspectFill
sceneView.presentScene(scene)
}
PlaygroundSupport.PlaygroundPage.current.liveView = sceneView
This is my code I wrote in Xcode using Sprite Kit, Swift. I'm trying to make a game like ComboQuest. The game consists of a moving bar that moves left and right trying to hit objects. As I'm trying to recreate that left and right movement here is my code:
func rightSprite(){
let actionR = SKAction.moveByX(0.001, y: 0, duration: 0.01)
Sprite.runAction(SKAction.repeatActionForever(actionR))
}
func leftSprite(){
let actionR = SKAction.moveByX(-0.001, y: 0, duration: 0.01)
Sprite.runAction(SKAction.repeatActionForever(actionR))
}
These two functions are then activate in the TouchesBegan override func, but there is a small flaw. When you touch the screen to change the direction there is not a complete reaction.
How would you guys code this?
First off, make sure you have a valid instance of a SKSpriteNode. It would be helpful to see more of your code to see where you are creating the bar sprite you would like to move. For example, below I created a red rectangle sprite.
let bar = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 10))
As far as your actions, you need to call them on your instance of a sprite. I used the same bar sprite from the example above. Also, since you are setting an action to repeat forever, you will need to remove that action to get it to stop. It appears you want the bar to move left and right. You can either remove all actions as I have done, or you could name your actions, and just remove a certain one. For simplicity, I just removed all actions.
func rightSprite(){
let actionR = SKAction.moveByX(0.001, y: 0, duration: 0.01)
bar.removeAllActions()
bar.runAction(SKAction.repeatActionForever(actionR))
}
func leftSprite(){
let actionL = SKAction.moveByX(-0.001, y: 0, duration: 0.01)
bar.removeAllActions()
bar.runAction(SKAction.repeatActionForever(actionL))
}
Take a look a this :
override func didMoveToView(view: SKView) {
let sprite = SKSpriteNode(color: UIColor.purpleColor(), size: CGSize(width: 20, height:50))
var startPoint:CGPoint = CGPoint(x: 100, y: CGRectGetMidY(frame))
var endPoint:CGPoint = CGPoint(x: 300, y: CGRectGetMidY(frame))
let moveLeft = SKAction.moveTo(endPoint, duration:3)
let moveRight = SKAction.moveTo(startPoint, duration:3)
let sequence = SKAction.sequence([moveLeft,moveRight])
sprite.position = startPoint
addChild(sprite)
sprite.runAction(SKAction.repeatActionForever(sequence), withKey:"moving")
}
I assume that your scene and view are initialized and sized correctly (eg that you done something like this scene.size = skView.bounds.size ) so when you try this example, sprite will show up on screen (instead of possible off-screen because you probably loading a scene from a .sks file).
IMO this is the natural way when using SKAction to move a sprite from point A to point B continuously (like from a Combo Quest) and that is:
Create starting and ending point
Make separate actions for moving sprite left and right
Make a sequence to run those two actions one after another
Repeat that action forever
Run that action with key to give your self a way to stop an action
When you want to stop action, you should do something like this:
if(sprite.actionForKey("moving") != nil){
sprite.removeActionForKey("moving")
}
I want to invert the color from the part of the HelloLable which is outside of the boxSprite. I know that it is possible with SKEffectNode but I don't know how.
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let box = SKSpriteNode()
box.position = CGPoint(x: size.width/2-100, y: size.height/2)
box.color = UIColor.blackColor()
box.size = CGSize(width: 200, height: 200)
addChild(box)
let HelloLable = SKLabelNode()
HelloLable.text = "Hello"
HelloLable.position = CGPoint(x: size.width/2, y: size.height/2)
HelloLable.fontColor = UIColor.whiteColor()
HelloLable.fontSize = 50
addChild(HelloLable)
}
If I understand you correctly, you'd like to make something like this:
I don't think there is a standard solution to do this (maybe you could try blending modes or shaders).
Maybe the easiest solution could be to make 2 labels, one white in the box, and one black under the box, with similar properties, and clip the white one with the box.
I have just encountered a really odd bug when testing my app.
import Foundation
import SpriteKit
import AVFoundation
class EndScene : SKScene {
var Highscore : Int!
var ScoreLabel : UILabel!
var HighscoreLabel : UILabel!
var AudioPlayer = AVAudioPlayer()
override func didMoveToView(view: SKView) {
scene?.backgroundColor = UIColor.grayColor()
var EndGameMusic = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("endSong", ofType: "wav")!)
AudioPlayer = AVAudioPlayer(contentsOfURL: EndGameMusic, error: nil)
AudioPlayer.prepareToPlay()
AudioPlayer.play()
var scoreDefault = NSUserDefaults.standardUserDefaults()
var Score = scoreDefault.valueForKey("Score") as! NSInteger
var HighscoreDefault = NSUserDefaults.standardUserDefaults()
Highscore = HighscoreDefault.valueForKey("Highscore") as! NSInteger
NSLog("\(Highscore)")
ScoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 30))
ScoreLabel.center = CGPoint(x: view.frame.size.width, y: view.frame.size.width/4)
ScoreLabel.text = "Last Game:\(Score)"
self.view?.addSubview(ScoreLabel)
HighscoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 30))
HighscoreLabel.center = CGPoint(x: view.frame.size.width, y: view.frame.size.width/2)
HighscoreLabel.text = "Highscore: \(Highscore)"
self.view?.addSubview(HighscoreLabel)
let RestartBtn = SKSpriteNode(imageNamed: "RestartBtn")
RestartBtn.position = CGPointMake(size.width/2, size.height/2)
RestartBtn.name = "Restart"
addChild(RestartBtn)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
if(touchedNode.name == "Restart"){
Restart()
}
}
}
func Restart(){
self.view?.presentScene(InGame(), transition: SKTransition.crossFadeWithDuration(0.3))
HighscoreLabel.removeFromSuperview()
ScoreLabel.removeFromSuperview()
}
}
what happens is that when this scene is called, the two labels, highscore and score appear, but my background goes a yellow color and my image i'm using as a button, isn't present, yet if i touch anywhere on the screen (regardless of bounds, I've tried) it will activate the button and exit.
if i remove this code:
let RestartBtn = SKSpriteNode(imageNamed: "RestartBtn")
RestartBtn.position = CGPointMake(size.width/2, size.height/2)
RestartBtn.name = "Restart"
addChild(RestartBtn)
Then the bug doesn't occur and the background sets normally, so i know there is an issue with this code, and assumed that for some reason it was zoomed in on my image (cant post without 10 reputation...)
However, after all attempts at resizing and fixing this issue, nothing actually helped, but i still cant help thinking that this has a relatively simple solution, its just that this code was used in my previous scenes without any issue.
Any Suggestions?
EDIT: i am certain that its taken the color of my image and used that as the background, and made the entire background the restart button, i just tried it with another image styled differently.
EDIT 2: image size = 200x80, the image i used in my first edit i scaled down to 40x80
This is not a bug, so basically the way SKSpriteNode's work is they take on the size of your image file since you did not explicitly set the size anywhere else. From the code you have, I'm guessing your image is over 1000 by 1000 pixels, so it is taking up the entire screen. The iPhone 6's size for example, is something like 667 by 375 pixels within xCode so in a sense your restart button is actually larger than the screen. To fix this, set the xScale and yScale values low and see what works(makes the size of the sprite smaller)
RestartBtn.xScale = 0.01
RestartBtn.yScale = 0.01
That would make the button 100x smaller(or 1/100 the previous size), just play around with the values until you get a size you like.
Edit: So I had this issue before as well in spriteKit. For some reasons the size of the screen gets messed up unless you set its size where you declare the scene.
(What you called your screen).size = skView.bounds.size
For example if you did like
let skView = self.view as SKView!
myScene = GameScene()
myScene.size = skView.bounds.size
skView.presentScene(myScene)