Multiple movable objects in swift - ios

I am trying to make a game that has five simple objects that the user can touch and move these have physics on them but having trouble getting them to move separately any help would be appreciated.
This is what I have so far:
import SpriteKit
class GameScene: SKScene {
let starSprite = SKSpriteNode(imageNamed: "starSprite")
let circleSprite = SKSpriteNode(imageNamed: "circleSprite")
let squareSprite = SKSpriteNode(imageNamed: "squareSprite")
let triangleSprite = SKSpriteNode(imageNamed: "triangleSprite")
let moonSprite = SKSpriteNode(imageNamed: "moonSprite")
var touchLocation = CGPoint()
override func didMove(to view: SKView) {
layoutScene()
}
func layoutScene() {
backgroundColor = UIColor(red: 44/255, green: 62/255, blue:
80/255, alpha: 1.0)
starSprite.size = CGSize(width: 350, height: 350)
starSprite.position = CGPoint(x: frame.midX, y: frame.midY)
starSprite.physicsBody = SKPhysicsBody(texture:
starSprite.texture!, size: starSprite.size)
addChild(starSprite)
circleSprite.size = CGSize(width: 350, height: 350)
circleSprite.position = CGPoint(x: 0, y: 100)
circleSprite.physicsBody = SKPhysicsBody(texture:
circleSprite.texture!, size: circleSprite.size)
addChild(circleSprite)
squareSprite.size = CGSize(width: 350, height: 350)
squareSprite.position = CGPoint(x: 0, y: 200)
squareSprite.physicsBody = SKPhysicsBody(texture:
squareSprite.texture!, size: squareSprite.size)
addChild(squareSprite)
triangleSprite.size = CGSize(width: 350, height: 350)
triangleSprite.position = CGPoint(x: 0, y: 300)
triangleSprite.physicsBody = SKPhysicsBody(texture:
triangleSprite.texture!, size: triangleSprite.size)
addChild(triangleSprite)
moonSprite.size = CGSize(width: 350, height: 350)
moonSprite.position = CGPoint(x: 0, y: 400)
moonSprite.physicsBody = SKPhysicsBody(texture:
moonSprite.texture!, size: moonSprite.size)
addChild(moonSprite)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
touchLocation = touch.location(in: self)
moonSprite.position.x = (touchLocation.x)
moonSprite.position.y = (touchLocation.y)
starSprite.position.x = (touchLocation.x)
starSprite.position.y = (touchLocation.y)
}
}
}

you should first detect which object has been selected in touchDown, then change the position in touchesMoved, and release the object in touchUp!
define a value as a temp and use that!
In addition , and you don't need to use this
moonSprite.position.x = (touchLocation.x)
moonSprite.position.y = (touchLocation.y)
it's better
moonSprite.position = touchLocation
Edited Code
//
// GameScene.swift
// aa
//
// Created by ehsan on 1/30/19.
// Copyright © 2019 ehsan. All rights reserved.
//
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var spinnyNode1 = SKSpriteNode(imageNamed: "1")
private var spinnyNode2 = SKSpriteNode(imageNamed: "2")
private var movingSprite:SKSpriteNode?
override func didMove(to view: SKView) {
addChild(spinnyNode1)
addChild(spinnyNode2)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
let location = t.location(in: self)
let node = self.atPoint(location)
if node is SKSpriteNode
{
movingSprite = node as? SKSpriteNode
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if movingSprite != nil
{
movingSprite!.position = touchLocation
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
movingSprite = nil
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}

Related

How can I select a specific circle?

I have created Circle using "draw" and can place it anywhere on view. but when I want to move a specific circle by clicking it. it selects "LAST" created circle.
how can I select a specific circle? please guide me for the same.
if anything else you need let me know.
**CircleView**
import UIKit
class CircleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
}
required init(coder aDecoder : NSCoder) {
fatalError("init(coder : ) has not been implemented")
}
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext(){
context.setLineWidth(2)
UIColor.yellow.set()
let circleCenter = CGPoint(x: frame.size.width / 2, y: frame.self.height / 2)
let circleRadius = (frame.size.width - 10) / 2
context.addArc(center: circleCenter, radius: circleRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
context.strokePath()
}
}
}
**ViewController**
import UIKit
class ViewController: UIViewController {
var circleView = CircleView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
let circleWidth = CGFloat(100)
var lastCircleCenter = CGPoint()
var currentCenter = CGPoint()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
circleView.isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
lastCircleCenter = touch.location(in: view)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let circleCenter = touch.location(in: view)
if circleCenter == lastCircleCenter{
let circleHeight = circleWidth
circleView = CircleView(frame: CGRect(x: circleCenter.x - circleWidth / 2, y: circleCenter.y - circleWidth / 2, width: 100, height: 100))
view.addSubview(circleView)
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
let location = touch.location(in: view)
circleView.center = location
}
}
how can I select a specific circle? please guide me for the same.
if anything else you need let me know.
how can I select a specific circle? please guide me for the same.
if anything else you need let me know.
If you are creating multiple circles and adding them to your view, I would suggest to keep track of the created circles in a collection. That way on each touch you can check if the coordinate matches any of the created circles. Based on that you can determine if to create a new circle or to move an existing one.
Example:
class ViewController: UIViewController {
var circleViews: [CircleView] = []
let circleWidth = CGFloat(100)
var draggedCircle: CircleView?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Do nothing if a circle is being dragged
// or if we do not have a coordinate
guard draggedCircle == nil, let point = touches.first?.location(in: view) else {
return
}
// Do not create new circle if touch is in an existing circle
// Keep the reference of the (potentially) dragged circle
if let draggedCircle = circleViews.filter({ UIBezierPath(ovalIn: $0.frame).contains(point) }).first {
self.draggedCircle = draggedCircle
return
}
// Create new circle and store in an array
let offset = circleWidth / 2
let rect = CGRect(x: point.x - offset, y: point.y - offset, width: circleWidth, height: circleWidth)
let circleView = CircleView(frame: rect)
circleViews.append(circleView)
view.addSubview(circleView)
// The newly created view can be immediately dragged
draggedCircle = circleView
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// If touches end then a circle is never dragged
draggedCircle = nil
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let draggedCircle = draggedCircle, let point = touches.first?.location(in: view) else {
return
}
draggedCircle.center = point
}
}

I want to move the image left and right only

I want to move this boat image with touches. It needs to move only left and right but not up and down. The problem is with this basic code setup, The image is moving all around.
I have the following code
import UIKit
class ViewController: UIViewController {
var boat:UIImageView!
var stone:UIImageView!
#IBOutlet weak var myView: UIView!
var location = CGPoint(x: 0, y: 0)
func start() {
boat = UIImageView(image: UIImage(named: "boat"))
boat.frame = CGRect(x: 0, y: 0, width: 60, height: 90)
boat.frame.origin.y = self.view.bounds.height - boat.frame.size.height - 10
boat.center.x = self.view.bounds.midX
self.view.addSubview(boat)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
start()
movingStone()
intersectsAt()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch : UITouch = touches.first as UITouch!
location = touch.location(in: self.view)
boat.center = location
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch : UITouch = touches.first as UITouch!
location = touch.location(in: self.view)
boat.center = location
}
func movingStone() {
stone = UIImageView(image: UIImage(named: "stones.png"))
stone.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
var stone2 = 10 + arc4random() % 20
stone.bounds = CGRect(x:10, y:10, width:40.0, height:40.0)
stone.contentMode = .center;
stone.layer.position = CGPoint(x: Int(stone2), y: 10)
stone.transform = CGAffineTransform(rotationAngle: 3.142)
self.view.insertSubview(stone, aboveSubview: myView)
UIView.animate(withDuration: 5, delay: 0, options: UIViewAnimationOptions.curveLinear, animations: { () -> Void in
self.stone.frame.origin.y = self.view.bounds.height + self.stone.frame.height + 10
}) { (success:Bool) -> Void in
self.stone.removeFromSuperview()
self.movingStone()
}
}
func intersectsAt() {
if(boat.layer.presentation()?.frame.intersects
((stone.layer.presentation()?.frame)!))! {
boat.image = UIImage(named: "wreckboat.png")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You need to update only the x coordinate of the image not the y coordinate.
Try this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch : UITouch = touches.first as UITouch!
let loc_tmp = touch.location(in: self.view)
// only use the x coordinate of the touch location
location = CGPoint(x: loc_tmp.x, y: boat.center.y)
boat.center = location
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch : UITouch = touches.first as UITouch!
let loc_tmp = touch.location(in: self.view)
// only use the x coordinate of the touch location
location = CGPoint(x: loc_tmp.x, y: boat.center.y)
boat.center = location
}

SKShpapeNode position and touch.location difference

I was learning about SKNodes in Swift and have encountered a problem. App was supposed to be very simple: touching screen and getting node to appear in the touched position. I don't know why but the rectangle keeps showing in different place on the screen but sn.position is the same as touch.location(in: view) position. What's wrong?
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var spinnyNode : SKShapeNode?
private var touchPoint : CGPoint!
override func didMove(to view: SKView) {
let size = CGSize(width: 50, height: 50)
spinnyNode = SKShapeNode(rectOf: size, cornerRadius: CGFloat(0.6)) //init
if let spinnyNode = self.spinnyNode{
spinnyNode.lineWidth = 3
let rotate = SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(M_PI), duration: 1))
let fade = SKAction.fadeOut(withDuration: 0.5)
let remove = SKAction.removeFromParent()
spinnyNode.run(rotate)
spinnyNode.run(SKAction.sequence([fade, remove]))
}
}
func touchDown(point pos : CGPoint){
if let sn = self.spinnyNode?.copy() as! SKShapeNode? {
sn.position = CGPoint(x: touchPoint.x , y: touchPoint.y)
print("touchDown x:\(touchPoint.x), y:\(touchPoint.y)")
self.addChild(sn)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard touches.count == 1 else{
return
}
if let touch = touches.first{
touchPoint = touch.location(in: view)
touchDown(point: touchPoint)
print("touchesBegan -> x: \(touchPoint.x) y: \(touchPoint.y)")
}
}
Try doing something like this
import SpriteKit
import GameplayKit
class GameScene: SKScene {
//use whichever image for your node
var ballNode = SKSpriteNode(imageNamed: "ball")
var fingerLocation = CGPoint()
override func didMove(to view: SKView) {
ballNode.size = CGSize(width: 100, height: 100)
ballNode.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.5)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if let location = touches.first?.location(in: self){
if let copy = ballNode.copy() as? SKSpriteNode {
copy.position = location
addChild(copy)
}
}
}
}
Every time I click on a random spot, the ball appears. Hope this helps.

Sprite not appearing

I have a sprite defined and it isn't appearing when I run the app. Here is my code and some images to help figure out the error:
import SpriteKit
class GameScene: SKScene {
var SingleplayerButton: SKSpriteNode! = nil
override func didMoveToView(view: SKView) {
SingleplayerButton = SKSpriteNode(imageNamed: "Unknown")
SingleplayerButton.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
SingleplayerButton.size = CGSize(width: 200, height: 50)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if SingleplayerButton.containsPoint(location) {
}
}
}
}
you forget addChild to your scene

Throwing an object with SpriteKit

I have the following code at the moment. Even though the code build is successful, i cannot seem to get it to work. I am trying to make it so when you flick the object, it moves at the velocity of your begin and end touch.
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
override func didMoveToView(view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 50))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(sprite)
}
//for touch: AnyObject in touches {
//let location = touch.locationInNode(self)
//let touchedNode = self.nodeAtPoint(location)
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
}
func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
touchPoint = location
}
func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
touching = false
}
func update(currentTime: CFTimeInterval) {
if touching {
let dt:CGFloat = 1.0/60.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
}
}
}}}
You accidentally placed touchesMoved, touchesEnded and update inside touchesBegan. Besides that your code works. A hint that there were problems was the fact you didn't need to prefix touchesMoved, touchesEnded or update with override.
In the future, I would recommend using breakpoints and print statements to check the methods you expect to execute, are in fact running. Doing that you'd see that your versions of touchesMoved, touchesEnded and update weren't being called.
Anyway, here's it corrected it and now it works perfectly:
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
override func didMoveToView(view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 50))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(sprite)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
touchPoint = location
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
touching = false
}
override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
touching = false
}
override func update(currentTime: NSTimeInterval) {
if touching {
let dt:CGFloat = 1.0/60.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
}
}
}
ABakerSmith's solutions updated for Swift 4:
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody.init(edgeLoopFrom: self.frame)
sprite = SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50))
sprite.physicsBody = SKPhysicsBody.init(rectangleOf: sprite.size)
sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(sprite)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first?.location(in: self) {
if sprite.frame.contains(touch) {
touchPoint = touch
touching = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
let location = t.location(in: self)
touchPoint = location
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func update(_ currentTime: TimeInterval) {
if touching {
let dt:CGFloat = 1.0/60.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
}
}
}
Solution updated to not have the touch/drag snap to the middle of the sprite. If you're dragging/throwing an object you're going to want that throw to come from where the user touched the object and not snap to the middle of the object. This will make it look a lot smoother
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
var xDif = CGFloat()
var yDif = CGFloat()
override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody.init(edgeLoopFrom: self.frame)
sprite = SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50))
sprite.physicsBody = SKPhysicsBody.init(rectangleOf: sprite.size)
sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(sprite)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first?.location(in: self) {
xDif = sprite.position.x - touch.x
yDif = sprite.position.y - touch.y
if sprite.frame.contains(touch) {
touchPoint = touch
touching = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
let location = t.location(in: self)
touchPoint = location
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
touching = false
}
override func update(_ currentTime: TimeInterval) {
if touching {
let dt:CGFloat = 1.0/60.0
let distance = CGVector(dx: touchPoint.x-sprite.position.x + xDif, dy: touchPoint.y-sprite.position.y + yDif)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity = velocity
}
}
}

Resources