Present Scene with Sprite Kit in Swift - swift

So when i start my game the you can play until you die, then the main menu Scene is presented and if you touch on the screen in the main menu you should load the GameScene again but i get an Error, i use this code to present the GameScene:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let revealGameScene = SKTransition.fade(withDuration: 0.5)
let goToGameScene = GameScene(size: self.size)
goToGameScene.scaleMode = SKSceneScaleMode.aspectFill
self.view?.presentScene(goToGameScene, transition:revealGameScene)
}
and the Same to present the main menu when you die which works perfectly fine but for some reason it doesn't work when i try to present the GameScene from the main menu

I think that there has to be an error outside of what you're showing us. I can create a very basic project that starts on a MenuScene (directed there by the GameViewController), when you click anywhere on the MenuScene it takes you to the GameScene. In the GameScene if you click a green box it takes you back to the MenuScene. And round and round you go.
Take a look at the code and see if anything different from yours
class GameViewController: UIViewController {
var menuScene: MenuScene!
var skView: SKView!
override func viewDidLoad() {
super.viewDidLoad()
// Create and configure the scene.
skView = self.view as! SKView
menuScene = MenuScene(size: skView.bounds.size)
menuScene.scaleMode = .aspectFill
skView.ignoresSiblingOrder = true
skView.showsFPS = true
skView.showsNodeCount = true
// Present the scene.
skView.presentScene(menuScene)
}
}
class MenuScene: SKScene {
override init(size: CGSize) {
super.init(size: size)
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let background = SKSpriteNode(color: .red, size: self.size)
background.position = CGPoint(x: 0, y: 0)
addChild(background)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let revealGameScene = SKTransition.fade(withDuration: 0.5)
let goToGameScene = GameScene(size: self.size)
goToGameScene.scaleMode = SKSceneScaleMode.aspectFill
self.view?.presentScene(goToGameScene, transition:revealGameScene)
}
}
class GameScene: SKScene {
private var box = SKSpriteNode()
override func didMove(to view: SKView) {
box = SKSpriteNode(color: .green, size: CGSize(width: 200, height: 200))
box.position = CGPoint.zero
addChild(box)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let touchLocation = touch.location(in: self)
if box.frame.contains(touchLocation) {
let revealGameScene = SKTransition.fade(withDuration: 0.5)
let goToMenuScene = MenuScene(size: self.size)
goToMenuScene.scaleMode = SKSceneScaleMode.aspectFill
self.view?.presentScene(goToMenuScene, transition:revealGameScene)
}
}
}
}

Related

UITextField not showing when added as subview in SpriteKit

I'm pretty new to Swift and I'm having some trouble displaying UITextField in SpriteKit. Init() in SceneMenu will not display the UITextField, while using function showTextField() with touchesBegan() works. How can I display UITextField without the user clicking the button Show TextField?
GameViewController.swift:
class GameViewController: UIViewController {
override func viewDidLoad() {
let scene = SceneMenu(size: view.frame.size)
scene.scaleMode = .aspectFill
scene.backgroundColor = .white
let view = view as! SKView
view.presentScene(scene)
}
}
SceneMenu.swift:
class SceneMenu: SKScene {
override init(size: CGSize) {
super.init(size: size)
let btnAlert = SKLabelNode(text: "Show TextField")
btnAlert.name = "btn_text"
btnAlert.fontSize = 20
btnAlert.fontColor = SKColor.blue
btnAlert.fontName = "Avenir"
btnAlert.position = CGPoint(x: size.width / 2, y: size.height / 2)
addChild(btnAlert)
let textFieldFrame = CGRect(origin: CGPoint(x: 100, y: 300), size: CGSize(width: 200, height: 30))
let textField = UITextField(frame: textFieldFrame)
textField.backgroundColor = SKColor.blue
textField.placeholder = "Type name"
view?.addSubview(textField)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showTextField() {
let textFieldFrame = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 30))
let textField = UITextField(frame: textFieldFrame)
textField.backgroundColor = SKColor.blue
textField.placeholder = "Type name"
view?.addSubview(textField)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let action = atPoint(location)
if action.name == "btn_text" {
showTextField()
}
}
}
}
SKScene has a method that lets you setup the scene when you move to it. So, instead of using the custom class's initializer, override and use SKScene's didMove(to view: SKView) method. An example:
override func didMove(to view: SKView) {
showTextField()
}

How can I update scene size of SpriteKit with GeometryReader in SwifUI

I am looking to solve my issue using GeometryReader for given new size to SKScene, this down code can keep tracking the children but when I rotate the device in makes some wired issue like this photos:
struct ContentView: View {
var body: some View {
GeometryReader { proxy in
SpriteView(scene: scene(proxy.size))
}
.ignoresSafeArea()
}
}
class GameScene: SKScene {
override func didMove(to view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let box = SKSpriteNode(color: UIColor.red, size: CGSize(width: 20, height: 20))
box.position = location
box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 20, height: 20))
addChild(box)
}
}
}
let sceneGame: GameScene = GameScene()
let scene: (CGSize) -> SKScene = { sizeValue in
sceneGame.size = sizeValue
sceneGame.scaleMode = .fill
return sceneGame
}

How to make part of a view controller SKScene and use both on the same screen?

I try to make a view controller, where i.e top half of the screen is SKScene, and bottom half of the screen is normal scene, I mean with buttons etc. Is this possible? I created a Single View application, and I am adding some code snippets below, which is the code from viewcontroller.swift file. But it doesn't create boxes to the tapped locations and also it doesn't move as expected a free fall motion when tapped. Maybe is there an issue about that I created a Single view application rather than choosing Game application when first creating the project? Maybe Metal enable/disable needed? I am not professional, therefore just guesses.
import UIKit
import SwiftUI
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
let box = SKSpriteNode(color: .red, size: CGSize(width: 0.04, height: 0.04))
box.physicsBody = SKPhysicsBody(rectangleOf: box.frame.size)
box.position = location
addChild(box)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = .aspectFill
scene.backgroundColor = .clear
let transition = SKTransition.fade(withDuration: 1)
view.presentScene(scene, transition: transition)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.allowsTransparency = true
view.backgroundColor = UIColor.clear
}
}
}

Draggable UIView does not move smoothly and is only draggable for a little distance (Video included)

The video: https://www.youtube.com/watch?v=8_xCTJAld9Y&feature=youtu.be
. Here you can see my issue, and I do not know what exactly is wrong. I cant move it like I want.
I have a draggable UIView, which has a custom class:
import UIKit
class UIViewDraggable: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.isUserInteractionEnabled = true
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first;
let location = touch?.location(in: self.superview);
if(location != nil)
{
self.frame.origin = CGPoint(x: location!.x-self.frame.size.width/2, y: location!.y-self.frame.size.height/2);
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
I create the UIView with a button tap:
#IBAction func buttonAddSubviewTextTapped(_ sender: UIButton) {
let label = UIView()
label.frame = CGRect(x: 0, y: 0, width: 200, height: 21)
label.center = CGPoint(x: 160, y: 285)
label.backgroundColor = .red
self.viewImagePreview.addSubview(label)
self.viewImagePreview.bringSubviewToFront(label)
}
How can I implement a fully smoothly draggable UIView?
Final Solution to make button created view draggable within its parentview:
#IBAction func buttonAddSubviewTextTapped(_ sender: UIButton) {
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
let label = UIView()
label.frame = CGRect(x: 0, y: 0, width: 200, height: 21)
label.center = CGPoint(x: 160, y: 285)
label.backgroundColor = .red
label.isUserInteractionEnabled = true
label.addGestureRecognizer(panGesture)
self.viewImagePreview.addSubview(label)
self.viewImagePreview.bringSubviewToFront(label)
}
// Function to drag a view
#objc func draggedView(_ sender: UIPanGestureRecognizer) {
guard let createdView = sender.view else {return}
createdView.bringSubviewToFront(viewImagePreview)
if sender.state == .began || sender.state == .changed {
let point = sender.location(in: self.viewImagePreview)
if let parentView = self.viewImagePreview {
let superBounds = CGRect(x: parentView.bounds.origin.x, y: parentView.bounds.origin.y, width: parentView.bounds.size.width - 2, height: parentView.bounds.size.height - 2)
if (superBounds.contains(point)) {
let translation = sender.translation(in: parentView)
createdView.center = CGPoint(x: createdView.center.x + translation.x, y: createdView.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: parentView)
}
}
}
}
Add a panGesture to your View and then based on the touch location, updated your view.center
let pan = UIPanGestureRecognizer(target: self, action: #selector(panView))
self.addGestureRecognizer(pan)
then use it as:
#objc func panView(pan: UIPanGestureRecognizer) {
let location = pan.location(in: self.superView) // get pan location
switch pan.state {
case .began:
print("began")
case .changed:
self.center = location
// updated your frames here
default:
print("nothing")
}
}
Also for your case,
try replacing
self.frame.origin = CGPoint(x: location!.x-self.frame.size.width/2, y: location!.y-self.frame.size.height/2);
to
self.center = location

Creating Player Class Results in Error "Missing argument for parameter 'coder' in call"

I created the player class with:
class Player : SKSpriteNode, GameSprite {
var initialSize = CGSize(width:50, height:50 )
var textureAtlas: SKTextureAtlas = SKTextureAtlas(named: "Dino")
func onTap()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
I then tried to add an instance of the player class in GameScene.swift with:
class GameScene: SKScene {
override func didMove(to view: SKView) {
self.anchorPoint = .zero
self.backgroundColor = UIColor(red: 0.95, green: 0.1, blue: 0.25, alpha: 1.0)
let player = Player()
player.size = CGSize(width: 50, height: 50)
player.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
self.addChild(player)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let location = touch.location(in: self)
let nodeTouched = atPoint(location)
if let gameSprite = nodeTouched as? GameSprite {
gameSprite.onTap()
}
}
}
}
But an error occurred saying:
Missing argument for parameter 'coder' in call
In the line that reads let player = Player()
Thanks!