I am currently creating the main menu of a mobile game, and it works fine however To make it look nicer I would like to add an effect when I touch one of my SKSpriteNodes which is an image of a button. I would like it to slightly move down then back up before transitioning to the game, im having some difficulty. Currently when the button is pressed it changed scenes to the game scene.
class MainMenuScene: SKScene{
let startGame = SKSpriteNode(imageNamed: "StartButton")
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "Menu")
background.size = self.size
background.position = CGPoint(x: self.size.width/2, y:self.size.height/2)
background.zPosition = 0
self.addChild(background)
startGame.position = CGPoint(x: self.size.width/2, y: self.size.height*0.6)
startGame.zPosition = 1
startGame.name = "startButton"
self.addChild(startGame)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
if nodeITapped.name == "startButton" {
let sceneToMoveTo = GameScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
}
}
}
You could make your logic in the function touchesEnded and then you call a SKAction in your button, passing the moveToScene logic as completion.
Something like:
var shouldMoveToNewScene: Bool = False
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
shouldMoveToNewScene = nodeITapped.name == "startButton" ? True : False
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard shouldMoveToNewScene, touces.count == 1 else { return }
startButton.run(myAnimation, completion: {
moveToScene()
})
}
private func moveToScene() {
//do your transition scene logic here
{
I haven't tried it yet, but hopefully you could get the idea.
If you're not familiar with SKAction you should consider follow this guide.
Related
I want my swift code to draw a straight horiziontal line. Right now it selects a point and the user extends the line anchored in the first point. I just want the user to be able to draw the line left or right. I tried alternating bezier.addLine(to: CGPoint(x:startTouch!.x, y:startTouch!.y)) but that does not seem to have any effect.
import UIKit
class ViewController2: UIViewController {
#IBOutlet weak var drawingPlace: UIImageView!
var startTouch : CGPoint?
var secondTouch : CGPoint?
var currentContext : CGContext?
var prevImage : UIImage?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemOrange
drawingPlace.backgroundColor = .gray
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
startTouch = touch?.location(in: drawingPlace)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
secondTouch = touch.location(in: drawingPlace)
if(self.currentContext == nil){
UIGraphicsBeginImageContext(drawingPlace.frame.size)
self.currentContext = UIGraphicsGetCurrentContext()
}else{
self.currentContext?.clear(CGRect(x: 0, y: 0, width: drawingPlace.frame.width, height: drawingPlace.frame.height))
}
self.prevImage?.draw(in: self.drawingPlace.bounds)
let bezier = UIBezierPath()
bezier.move(to: startTouch!)
bezier.addLine(to: secondTouch!)
bezier.addLine(to: CGPoint(x:startTouch!.x, y:startTouch!.y))
bezier.close()
UIColor.blue.set()
self.currentContext?.setLineWidth(4)
self.currentContext?.addPath(bezier.cgPath)
self.currentContext?.strokePath()
let img2 = self.currentContext?.makeImage()
drawingPlace.image = UIImage.init(cgImage: img2!)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.currentContext = nil
self.prevImage = self.drawingPlace.image
}
}
If you want to draw a horizontal line, create a CGPoint whose x is the location of the touch and whose y is that of the starting point. That will result in a horizontal line.
That having been said, here are a few other of observations:
If you call UIGraphicsBeginImageContext, you must call UIGraphicsEndImageContext. You should do this within touchesMoved, not trying to hang on to the context beyond this call.
If you were to do this, we would generally use UIGraphicsImageRenderer nowadays.
Personally, I wouldn't try rendering an image for every touch. That is a pretty expensive operation. I would just add a CAShapeLayer and then update the path for that layer. Let CAShapeLayer take care of the rendering of the path.
I'm not quite sure why you are iterating through the array of touches. I would just grab one and use that.
I might suggest using predictive touches to minimize perceived lagginess.
The startTouch is actually a CGPoint, not a UITouch, so I might call it startPoint instead.
If you want to make a snapshot image, I'd do that in touchesEnded, not in touchesMoved.
For example:
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var startPoint: CGPoint?
let shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = 4
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.blue.cgColor
return shapeLayer
}()
override func viewDidLoad() {
super.viewDidLoad()
imageView.backgroundColor = .systemOrange
imageView.layer.addSublayer(shapeLayer)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
startPoint = touch?.location(in: imageView)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard var touch = touches.first else { return }
if let predicted = event?.predictedTouches(for: touch)?.last {
touch = predicted
}
updatePath(in: imageView, to: touch)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
updatePath(in: imageView, to: touch)
let image = UIGraphicsImageRenderer(bounds: imageView.bounds).image { _ in
imageView.drawHierarchy(in: imageView.bounds, afterScreenUpdates: true)
}
shapeLayer.path = nil
imageView.image = image
}
}
private extension ViewController {
func updatePath(in view: UIView, to touch: UITouch) {
let point = touch.location(in: view)
guard view.bounds.contains(point) else { return }
let bezier = UIBezierPath()
bezier.move(to: startPoint!)
bezier.addLine(to: CGPoint(x: point.x, y: startPoint!.y))
shapeLayer.path = bezier.cgPath
}
}
This just renders the current line as a CAShapeLayer, but when the stroke is done, it creates a snapshot of the image view (permanently capturing the stroke in an image), removes the shape layer’s path, and updates the image view’s image.
But hopefully this answers the question on how to make the line horizontal.
I am trying to make a game where you can click a ball using SpriteKit. At the moment, I can click anywhere on the screen and the ball moves up.
I want it to only move or bounce when I click the ball. What am I doing wrong?
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKSpriteNode()
var grass = SKSpriteNode()
override func didMove(to view: SKView) {
ball = (self.childNode(withName: "ball") as? SKSpriteNode)!
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 1
self.physicsBody = border
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
func touchDown(atPoint pos: CGPoint) {
jump()
}
func touchUp(atPoint pos: CGPoint) {
ball.texture = SKTexture(imageNamed: "football-161132_640")
}
func jump() {
ball.texture = SKTexture(imageNamed: "football-161132_640")
ball.physicsBody?.applyImpulse(CGVector(dx: Int.random(in: -5...5), dy: 80))
}
}
Multiple touches:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if ball.contains(location) {
jump()
}
}
}
Single touch:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
if ball.contains(location) {
jump()
}
}
}
I'm making an app where you controll the ship's movement with a joystick, and fires a bullet with a shoot button. These two functions works great separately but if i tap the shooting button while i move the ship with the joystick, the joystick becomes uncontrolable. So what i need is that the joystick functions normally when i tap anywhere on the screen.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
let player=SKSpriteNode(imageNamed: "prez")
let joystickback=SKSpriteNode(imageNamed: "0")
let joystickbutton=SKSpriteNode(imageNamed: "1")
let shootbutton=SKSpriteNode(imageNamed: "1")
var joystickinuse=false
var velocityX : CGFloat=0.0
var velocityY : CGFloat=0.0
override func didMove(to view: SKView) {
let background=SKSpriteNode(imageNamed: "back")
background.size=self.size
background.position=CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition=0
self.addChild(background)
//joystick back
self.joystickback.position=CGPoint(x: (self.view?.frame.width)!*0.1, y: (self.view?.frame.height)!/5)
self.joystickback.zPosition=1
//jostick butt
self.joystickbutton.position=CGPoint(x: (self.view?.frame.width)!*0.1, y: (self.view?.frame.height)!/5)
self.joystickbutton.zPosition=2
//shootingbutton
self.shootbutton.position=CGPoint(x: (self.view?.frame.width)!*0.9, y: (self.view?.frame.height)!/5)
self.shootbutton.zPosition=2
self.shootbutton.setScale(0.4)
self.ship.position=CGPoint(x: (self.view?.frame.width)!/2, y: (self.view?.frame.height)!/2)
self.ship.setScale(0.2)
self.ship.zPosition=3
self.joystickback.setScale(0.5)
self.joystickbutton.setScale(0.5)
self.addChild(shootbutton)
self.addChild(ship)
self.addChild(joystickback)
self.addChild(joystickbutton)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches{
let location=t.location(in: self)
if (joystickbutton.frame.contains(location)){
joystickinuse=true
}
}
}
func touchMoved(touch:UITouch){
let location=touch.location(in: self)
if (joystickinuse){
let vecktor=CGVector(dx: location.x-joystickback.position.x, dy: location.y-joystickback.position.y)
let angle=atan2(vecktor.dy, vecktor.dx)
let DistanceFromCenter=CGFloat(joystickback.frame.size.height/2)
let DistanceX=CGFloat(sin(angle-CGFloat(M_PI)/2)*DistanceFromCenter)
let DistanceY=CGFloat(cos(angle-CGFloat(M_PI)/2)*DistanceFromCenter)
if(joystickback.frame.contains(location)){
joystickbutton.position=location
}
else{
joystickbutton.position=CGPoint(x: joystickback.position.x-DistanceX, y: joystickback.position.y+DistanceY)
}
velocityX=(joystickbutton.position.x-joystickback.position.x)/10
velocityY=(joystickbutton.position.y-joystickback.position.y)/10
}
}
func firebullet(){
let bullet=SKSpriteNode(imageNamed: "rocket")
bullet.position=urhajo.position
bullet.setScale(0.2)
bullet.zPosition=1
self.addChild(bullet)
let movebullet=SKAction.moveTo(x: self.size.width+bullet.size.width, duration: 1)
let deletebullet=SKAction.removeFromParent()
let bulletsequence=SKAction.sequence([movebullet,deletebullet])
bullet.run(bulletsequence)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches{
self.touchMoved(touch: t)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if(joystickinuse){
movementOver()
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if(joystickinuse){
movementOver()
}
}
func movementOver(){
let moveBack=SKAction.move(to: CGPoint(x: joystickback.position.x,y: joystickback.position.y), duration: TimeInterval(floatLiteral:0.1))
moveBack.timingMode = .linear
joystickbutton.run(moveBack)
joystickinuse=false
velocityY=0
velocityX=0
}
override func update(_ currentTime: TimeInterval) {
if(ship.position.x>=(self.view?.frame.width)!+urhajo.frame.width/2){
}
else{ self.ship.position.x+=velocityX
}
self.ship.position.y+=velocityY
}
}
try setting this is your Scene code
func didMove(to view: SKView) {
self.view!.isMultipleTouchEnabled = true
}
I´m making a game with Swift.
I need to add nodes where I press the screen, I know this is not from other planet but I can't do it.
Highly simplified example could look like this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
let node = SKNode()
node.position = location
addChild(node)
}
Here is how you can add an remove labels by tapping:
class ViewController: UIViewController{
private var labels = [UILabel]()
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
for label in labels {
if label.frame.contains(touch.location(in: view)) {
label.removeFromSuperview()
return
}
}
let label = UILabel()
view.addSubview(label)
labels.append(label)
label.text = "touch"
label.sizeToFit()
label.center = touch.location(in: view)
}
}
I'm super new to coding (like I started yesterday new). I'm trying to make the same SpriteNode appear multiple times that follow the same methods. For example, the game starts off with one Node that reacts to touch and after you reach 5 points another appears, after 15 another and so on... I have been able to spawn a spriteCopy at 5 points; however I cannot get it to respond to touch like the original node. I'm sure it's something super simple I just don't have the experience. Thanks.
Here is my code so far:
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
var score: Int = 0
let scoreTextNode = SKLabelNode(fontNamed: "Helvetica Neue UltraLight")
override func didMoveToView(view: SKView) {
sprite = SKSpriteNode(imageNamed: "PongBall")
sprite.position = CGPoint(x: 500,y: 500)
sprite.size = CGSize(width: 75, height: 75)
sprite.physicsBody = SKPhysicsBody(circleOfRadius: 37.5)
self.addChild(sprite)
scoreTextNode.text = "\(score)"
scoreTextNode.fontSize = 70
scoreTextNode.fontColor = SKColor.blackColor()
scoreTextNode.position = CGPoint (x: 680.734, y: 700.941)
self.addChild(scoreTextNode)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch!
let location = touch.locationInNode(self)
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
if touchPoint == location {
self.score += 1
self.scoreTextNode.text = "\(score)"
}
let spriteCopy = sprite.copy() as! SKSpriteNode
if score == 5 {
self.addChild(spriteCopy)
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch!
let location = touch.locationInNode(self)
touchPoint = location
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
touching = false
}
override func update(currentTime: CFTimeInterval) {
if touching {
let dt:CGFloat = 1.0/30.0
let distance = CGVector(dx: touchPoint.x-sprite.position.x, dy: touchPoint.y-sprite.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity=velocity
}
}
}
Your problem is you have multiple sprites appearing on your screen, but you do not do anything with them. To keep things real simple for you, take this approach.
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
var score: Int = 0
let scoreTextNode = SKLabelNode(fontNamed: "Helvetica Neue UltraLight")
var spriteList = [SKSpriteNode]() //Add this line
override func didMoveToView(view: SKView) {
sprite = SKSpriteNode(imageNamed: "PongBall")
sprite.position = CGPoint(x: 500,y: 500)
sprite.size = CGSize(width: 75, height: 75)
sprite.physicsBody = SKPhysicsBody(circleOfRadius: 37.5)
self.addChild(sprite)
spriteList.append(sprite) //Add This line
scoreTextNode.text = "\(score)"
scoreTextNode.fontSize = 70
scoreTextNode.fontColor = SKColor.blackColor()
scoreTextNode.position = CGPoint (x: 680.734, y: 700.941)
self.addChild(scoreTextNode)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch!
let location = touch.locationInNode(self)
for sprite in spriteList{ //Add This line
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
if touchPoint == location {
self.score += 1
self.scoreTextNode.text = "\(score)"
}
let spriteCopy = sprite.copy() as! SKSpriteNode
if score == 5 {
self.addChild(spriteCopy)
spriteList.append(spriteCopy) //Add this line
}
} //Add this line
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch!
let location = touch.locationInNode(self)
touchPoint = location
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
touching = false
}
override func update(currentTime: CFTimeInterval) {
if touching {
for sprite in spriteList{ //Add this line
let dt:CGFloat = 1.0/30.0
let distance = CGVector(dx: touchPoint.x-sprite.position.x, dy: touchPoint.y-sprite.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity=velocity
} //Add this line
}
}
}
The basic concept is you maintain it in an array. This should be a good starting point for a beginner. After you learn how to override classes, you can learn that the touch code can be done inside of the sprite instead of inside of the scene. This would actually be my preferred method.