I´m new to coding and I´ve created a game with a black button in the main class. I want to create a setting class where the user can click on a blue button to change the black button in the main class or on a red button to change the black button in the main class to red.
Here is the main class:
class Mainclass: SKScene{
var gameButton = SKSpriteNode(imageNamed: "blackDot") //current button image
}
Here is the setting class:
class Settings: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = SKColor.white
let blueButton = SKSpriteNode(imageNamed: "blueDot") //button to select
blueButton.position = CGPoint(x: self.size.width * 0.2, y: self.size.height * 0.8)
blueButton.setScale(0.53)
self.addChild(blueButton)
let redButton = SKSpriteNode(imageNamed: "redDot") //button to select
redButton.position = CGPoint(x: self.size.width * 0.4, y: self.size.height * 0.8)
redButton.setScale(0.53)
self.addChild(redButton)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let locationUser = touch.location(in: self)
if atPoint(locationUser) == blueButton {
//Change the button image in the Mainclass from black to blue if user tap on blueButton
}
if atPoint(locationUser) == redButton {
//Change the button image in the Mainclass from black to red if user tap on redButton
}
}
}
}
So we have two scenes, called GameScene.swift and SettingsScene.swift and corresponding .sks files called GameScene.sks and SettingsScene.sks. Also we have three images, called default_button, purple_button and blue_button (stored in our .xcassets folder).
Here is the SettingsScene.swift:
import SpriteKit
class SettingsScene: SKScene {
var purpleButton = SKSpriteNode.init(imageNamed: "purple_button")
var blueButton = SKSpriteNode.init(imageNamed: "blue_button")
override func didMove(to view: SKView) {
backgroundColor = .purple
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let location = touches.first?.location(in: self) {
if purpleButton.contains(location) {
UserDefaults.standard.set("purple_button", forKey: "button")
}else if blueButton.contains(location){
UserDefaults.standard.set("blue_button", forKey: "button")
}else{
if let mainScene = GameScene(fileNamed:"GameScene") {
//transition to Settings scene
self.view?.presentScene(mainScene)
}
}
}
}
}
This scene has two buttons, and when a user tap on one of them, we will remember which one is tapped by storing appropriate image name in user defaults, for example:
UserDefaults.standard.set("purple_button", forKey: "button")
Later on we will read from a persistent storage (in GameScene didMoveTo:view method) and use what is found there.
Also, user defaults are not erased when app is closed by the user, so next time the a user launches the app, he will have his settings saved and ready to use.
So after selecting one of the buttons from the Settings scene, and transitioning to the GameScene, you will your button set to whatever you have previously chosen. Here is the GameScene:
import SpriteKit
class GameScene: SKScene {
let button = SKSpriteNode.init(imageNamed: "default_button")
override func didMove(to view: SKView)
{
if let buttonImageName = UserDefaults.standard.string(forKey: "button") {
//set what is found in settings
button.texture = SKTexture.init(imageNamed: buttonImageName)
}
backgroundColor = .white
addChild(button)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let location = touches.first?.location(in: self) {
if button.contains(location) {
}else{
if let settingScene = SettingsScene(fileNamed:"SettingsScene") {
//transition to Settings scene
self.view?.presentScene(settingScene)
}
}
}
}
}
Related
Im attempting to use some of the code from a solution found on this page for creating a button Create Button in SpriteKit: Swift
class GameScene: SKScene {
let button = SKSpriteNode(imageNamed: "yourImgName")
override func didMoveToView(view: SKView) {
button.name = "btn"
button.size.height = 100
button.size.width = 100
button.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) + 50)
self.addChild(button)
//Adjust button properties (above) as needed
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene)
if let name = touchedNode.name {
if name == "btn" {
let yourNextScene = YourNextScene(fileNamed: "YourNextScene")
self.view?.presentScene(yourNextScene!)
}
}
}
}
and the current code that I have is supposed to make the player jump when the button is pressed, but nothing is currently happening when its pressed
import SwiftUI
import SpriteKit
import UIKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let button = SKSpriteNode(imageNamed: "playerOBJ")
let player = SKSpriteNode(imageNamed: "playerOBJ")
let playerRadius = player.frame.width / 2.0
player.position = CGPoint(x: 200, y: 500)
player.name = "Jimmy"
addChild(player)
player.physicsBody = SKPhysicsBody(circleOfRadius: playerRadius)
player.physicsBody?.allowsRotation = false
player.physicsBody?.friction = 0
player.physicsBody?.restitution = 0
player.zPosition = 100
// Button
button.name = "btn"
button.size.height = 100
button.size.width = 100
button.position = CGPoint(x: 100, y: 100)
self.addChild(button)
// Physics
physicsBody = SKPhysicsBody(edgeLoopFrom: frame.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)))
Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in
player.physicsBody?.applyForce(CGVector(dx: 100, dy: 1000))
}
func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "btn" {
player.physicsBody?.applyForce(CGVector(dx: 0, dy: 10000))
}
}
}
}
override func update(_ currentTime: TimeInterval) { }
}
I'm thinking that maybe this is an issue with the press not being resitered at all but I'm not fully sure
Your main problem is you put all of your code inside the didMove function. You put the touchesBegan function inside the didMove function. When the didMove function finishes, touchesBegan goes away so none of your touches are going to be handled in the game.
You also declared the button and the player as local variables inside the didMove function.
override func didMove(to view: SKView) {
let button = SKSpriteNode(imageNamed: "playerOBJ")
let player = SKSpriteNode(imageNamed: "playerOBJ")
// Rest of function omitted
}
When the didMove function finishes, the button and player go away too. You also have the same image name for the player and button.
The fix is to make the button and player variables properties of the GameScene class. Move touchesBegan out of the didMove function too.
class GameScene: SKScene {
// Declare button and player here.
var button = SKSpriteNode()
var player = SKSpriteNode()
override func didMove(to view: SKView) {
// Initialize button, player, and everything else here.
}
func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Code in touchesBegan func goes here.
}
}
I am creating a calendar app, and I tap to create new events in a UIScrollView. Think of it like the apple calendar "day view" where you can create new events and view them in a list on a scroll view. Each event is a UIView with a TextField on it, and right now everything works but I'm not sure how to make it so the events don't overlap when I create them.
Is there a way to prevent UIViews from overlapping when I create them or move them? I only am moving them up or down, so I just need to keep them from overlapping on the y axis. Right now I tap to create them, but they can overlap. Is there a way to put them on the same plane or something so they can't be added on top of each other?
It is highly recommended you post your own code for us to understand the general direction you're heading towards. Otherwise, it could a wasted effort on both of our parts.
For example, you mentioned that "right now everything works", but I have no idea how you're moving the views in a vertical manner. I went ahead and subclassed the gesture recognizer so hopefully it fits the flow of your code.
import UIKit.UIGestureRecognizerSubclass
class ViewController: UIViewController {
let size = CGSize(width: 200, height: 100)
var v1: UIView!
var v2: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.v1 = UIView(frame: .init(origin: .init(x: 100, y: 100), size: size))
self.view.addSubview(self.v1)
self.v1.backgroundColor = .orange
self.v2 = UIView(frame: .init(origin: .init(x: 100, y: 300), size: size))
self.view.addSubview(v2)
self.view.addSubview(self.v2)
self.v2.backgroundColor = .purple
let verticalGesture = VerticalGesture(target: self, action: #selector(dragged))
self.v2.addGestureRecognizer(verticalGesture)
}
#objc func dragged(_ sender: VerticalGesture) {
}
}
class VerticalGesture: UIPanGestureRecognizer {
private var touch: UITouch!
private var currentView: UIView!
private var currentSuperview: UIView!
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
guard let firstTouch = touches.first, let currentView = self.view, let currentSuperview = currentView.superview else { return }
self.touch = firstTouch
self.currentView = currentView
self.currentSuperview = currentSuperview
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
var center = self.currentView.center
let currentLocation = self.touch.location(in: self.currentSuperview)
let previousLocation = self.touch.previousLocation(in: self.currentSuperview)
let yTranslation = currentLocation.y - previousLocation.y
center.y += yTranslation
self.view!.center = center
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
for subview in self.currentSuperview.subviews {
// this goes through all the sibling views including the non-calendar-event views i.e. calendar dates, calendar frame, etc
// so make sure to sort those out
if subview != self.currentView! && self.currentView!.frame.intersects(subview.frame) {
var center = self.currentView.center
let isHigher = subview.center.y >= center.y
// isHigher >= 0 means the current view that you've selected is located higher
// than the overlapping view below
if isHigher {
center = CGPoint(x: subview.center.x, y: subview.center.y - self.currentView.bounds.height)
self.view!.center = center
} else {
center = CGPoint(x: subview.center.x, y: subview.center.y + self.currentView.bounds.height)
self.view!.center = center
}
}
}
super.touchesEnded(touches, with: event)
}
}
Depending on whether the calendar event you're dragging is on the upper side or the lower side of the overlapping view, it will move to adjacent to it. Feel free to add a transform animation to it.
PhysicsBody added only to half of a sprite
I have add a sprite to a GameScene and set a Body type in Physics Definitions to Alpha mask.
Then I want to check if I've clicked on physics body or not.
But, it shows yes only when I click on green area.
override func didMove(to view: SKView) {
let texture = SKTexture(imageNamed: "walking_walking_2")
let wallS = SKSpriteNode (texture: texture)
wallS.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
wallS.physicsBody?.isDynamic = false
self.addChild(wallS)
view.showsPhysics = true
}
override func touchesBegan (_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
touchLocation = t.location(in: self)
let touchedNode = physicsWorld.body(at: touchLocation!)
if (touchedNode?.categoryBitMask == 1)
{
print("yes")
} else {
print ("no")
}
}
}
I am currently creating a 2D game in which I have a cannon staying at the bottom of the screen that fires projectiles to some targets in the upper part of the screen. The user can rotate the cannon in order to aim for the targets. I want the projectile to be fired to the location that the canon faces. I have no idea how to do that and I need your help... Heres my code ->
import SpriteKit
class GameScene: SKScene {
var canon = SKSpriteNode()
var projectile = SKSpriteNode()
var touching = false
var locked = false
var location = CGPoint()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
projectile.position = canon.position
projectile.color = UIColor.whiteColor()
projectile.size = CGSize(width: 50, height: 50)
projectile.physicsBody?.affectedByGravity = false
projectile.physicsBody?.linearDamping = 1
projectile.physicsBody?.dynamic = true
addChild(projectile)
canon.zPosition = 2
canon = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 200))
canon.position = CGPoint(x: self.size.width/2.0, y: 10)
addChild(canon)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
touching = true
let touch = touches.first as UITouch!
location = touch.locationInView(self.view)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
rotateCanon(location.x)
projectile.zRotation = canon.zRotation
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
touching = false
}
func rotateCanon(locationx:CGFloat) {
if locationx <= frame.width/5 && touching {
print(1)
canon.runAction(SKAction.rotateByAngle(0.01, duration: 0.1))
}
if locationx > frame.width/5 && touching {
print(2)
canon.runAction(SKAction.rotateByAngle(-0.01, duration: 0.1))
}
}
}`
Thanks a lot if you can help that would be awesome :D
Ok so I'm trying to make so when Button1 is clicked, it gets removed and replaced by Button2. When Button2 gets clicked, I want it to remove button2 and replace it with button1, etc.. Here's my code I have so far (The buttons are functional so that is not the problem.)
//Sound Button1
soundButton.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height * 0.42)
soundButton.zPosition = 15
self.addChild(soundButton)
Here's the function that runs when Button1 is clicked:
func sound() {
soundButton.removeFromParent()
soundButton2.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height * 0.42)
soundButton2.zPosition = 15
self.addChild(soundButton2)
}
Here's the function that runs when Button2 is clicked:
func sound1() {
soundButton2.removeFromParent()
self.addChild(soundButton)
}
Lastly, here is my code for the buttons getting clicked in the touchesEnded function:
//Sound1 Button Pressed
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if soundButton.containsPoint(location) {
sound()
for touch: AnyObject in touches {
_ = touch.locationInNode(self)
}
}
}
//Sound1 Button Pressed
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if soundButton2.containsPoint(location) {
sound1()
for touch: AnyObject in touches {
_ = touch.locationInNode(self)
}
}
}
I'd just use the hidden property to toggle the visibility of both buttons. Set the one you want displayed to hidden = false and the one you want to hide as hidden = true
You are overcomplicating this. What you need is just to remove the button which is tapped, and add an appropriate one:
import SpriteKit
class GameScene: SKScene{
let soundButton = SKSpriteNode(color: UIColor.purpleColor(), size:CGSize(width: 200, height: 200))
let soundButton2 = SKSpriteNode(color: UIColor.orangeColor(), size:CGSize(width: 200, height: 200))
override func didMoveToView(view: SKView) {
soundButton.name = "button1"
soundButton2.name = "button2"
soundButton.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame))
soundButton.zPosition = 15
self.addChild(soundButton)
soundButton2.position = soundButton.position
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
if let location = touch?.locationInNode(self){
let node = nodeAtPoint(location)
if node.name == "button1" {
node.removeFromParent()
//Not really needed for this example, but a good habit
if soundButton2.parent == nil {
addChild(soundButton2)
}
}else if node.name == "button2"{
node.removeFromParent()
//Not really needed for this example, but a good habit
if soundButton.parent == nil {
addChild(soundButton)
}
}
}
}
}
The other way way would be to subclass a SKSpriteNode and make your Button class (Button:SKSpriteNode), set its userInteractionEnabled property to true, and implement its touchesBegan/touchesEnded methods.
Or you can use SKAButton class which has a lot of different functionalities (mimics the usefulness of UIButton).