I'm trying to build a board game whereby the board is built of sprites and you can look over them birds eye view style with an SKCamera node. I've gotten the pan, zoom and rotate down but they don't feel natural as wherever I put my fingers on the screen it will rotate about the centre of the camera node.
To remedy this, I was going to create a SKNode and place this in-between where the fingers are as an anchor for the camera's rotation and zooming.
However I can't get the location of the centre point of the fingers to line up with the right coords in the gamescene.
I've created some red boxes to debug and find my problem and looks as so:
Where-ever I place my fingers in the entire game scene to rotate it, the centre where it thinks it should be is always in that red blob.
The SKView is placed on the storyboard as a SpriteKit view and is bound to the left and right edges of the screen with top and bottom padding:
Inside of viewcontroller:
#IBOutlet weak var Gameview: SKView!
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene(size:Gameview.frame.size)
Gameview.presentScene(scene)
}
Inside of GameScene.swift, let a = sender.location(in: self.view) seems to be where the problem lies.
class GameScene: SKScene, UIGestureRecognizerDelegate {
var newCamera: SKCameraNode!
let rotateRec = UIRotationGestureRecognizer()
let zoomRec = UIPinchGestureRecognizer()
//let panRec = UIPanGestureRecognizer()
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer)
-> Bool {return true}
override func didMove(to view: SKView) {
rotateRec.addTarget(self, action: #selector(GameScene.rotatedView (_:) ))
rotateRec.delegate = self
self.view!.addGestureRecognizer(rotateRec)
zoomRec.addTarget(self, action: #selector(GameScene.zoomedView (_:) ))
zoomRec.delegate = self
self.view!.addGestureRecognizer(zoomRec)
//panRec.addTarget(self, action: #selector(GameScene.pannedView (_:) ))
//self.view!.addGestureRecognizer(panRec)
newCamera = SKCameraNode()
newCamera.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
self.addChild(newCamera)
self.camera = newCamera
//drawBoard()
//I removed this function as it had nothing to do with the question
}
#objc func rotatedView(_ sender:UIRotationGestureRecognizer) {
print(sender.location(in: self.view))
let square = SKSpriteNode(color: #colorLiteral(red: 0.7450980544, green: 0.1568627506, blue: 0.07450980693, alpha: 1), size: CGSize(width: 10, height: 10))
let a = sender.location(in: self.view)
let b = -a.x
let c = -a.y
let d = CGPoint(x: b, y: c)
square.position = d
self.addChild(square)
newCamera.zRotation += sender.rotation
sender.rotation = 0
if sender.state == .ended {
let rotateAmount = abs(Double(newCamera.zRotation) * Double(180) / Double.pi)
let remainder = abs(rotateAmount.truncatingRemainder(dividingBy: 90))
//print(rotateAmount)
//print(remainder)
if remainder < 10 {
newCamera.zRotation = CGFloat((rotateAmount-remainder)*Double.pi/180)
} else if remainder > 80 {
newCamera.zRotation = CGFloat((rotateAmount-remainder+90)*Double.pi/180)
}
}
}
#objc func zoomedView(_ sender:UIPinchGestureRecognizer) {
let pinch = SKAction.scale(by: 1/sender.scale, duration: 0.0)
newCamera.run(pinch)
sender.scale = 1.0
}
//#objc func pannedView(_ sender:UIPanGestureRecognizer) {
// sender.translation(in: )
//}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
let deltaY = location.y - previousLocation.y
let deltax = location.x - previousLocation.x
newCamera!.position.y -= deltaY
newCamera!.position.x -= deltax
}
}
}
I did remove the function drawboard() as it was redundant in context to the question.
You needed to convert the coords from the sender.location to the coords in the skview using the functions shown in the page:
https://developer.apple.com/documentation/spritekit/skscene
--> https://developer.apple.com/documentation/spritekit/skscene/1520395-convertpoint
in the rotatedview() function, putting the code:
let bluesquare = SKSpriteNode(color: #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1), size: CGSize(width: 10, height: 10))
let a = sender.location(in: self.view)
bluesquare.position = self.convertPoint(fromView: a)
self.addChild(bluesquare)
...will add blue sprites where the centre of your two fingers are.
Related
I'm working in SpriteKit and I need to create a 'playable area', an area that is same size as the device screen so that I can stop my player from moving off-screen.
I'm using the following line of code:
var playableRect: CGRect = UIScreen.main.bounds
But the resulting rectangle is about a quarter of the device screen, with a corner of that rectangle at what looks to be the center of the screen. And the device orientation doesn't change that.
I've tried everything I can think of. Nothing is working.
How do I create a rectangle that's the same size as the device screen?
Here's my full coding, after changing to include Gene's suggestion. I incorporated Gene's suggestion by revising playableRect inside the didMove method. But the result is unchanged with that coding.
import SpriteKit
import GameplayKit
import CoreMotion
#objcMembers
class GameSceneUsingTilt: SKScene {
let player = SKSpriteNode(imageNamed: "player-motorbike")
let motionManager = CMMotionManager()
var playableRect = UIScreen.main.bounds
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "road")
background.zPosition = -1
addChild(background)
if let particles = SKEmitterNode(fileNamed: "Mud") {
let farRightPt = frame.maxX // start the emitter at the far right x-point of the view
particles.advanceSimulationTime(10)
particles.position.x = farRightPt
addChild(particles)
}
let nearLeftPt = frame.minX * 3 / 4 // start the player at a quarter of the way to the far left x-point of the view
player.position.x = nearLeftPt
player.zPosition = 1
addChild(player)
motionManager.startAccelerometerUpdates()
// coding below shows outline of playableRect
let bounds = UIScreen.main.bounds
let scale = UIScreen.main.scale
// let size = CGSize(width: bounds.size.width * scale, height: bounds.size.height * scale)
playableRect = CGRect(x: 0, y: 0, width: bounds.size.width * scale, height: bounds.size.height * scale)
drawPlayableRect(rect: playableRect)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
func boundsCheck() {
let playableRect = CGRect(x: frame.minX, y: frame.minY, width: frame.width, height: frame.height)
let bottomLeft = CGPoint(x: playableRect.minX, y: playableRect.minY)
let topRight = CGPoint(x: playableRect.maxX, y: playableRect.maxY)
if player.position.x <= bottomLeft.x {
player.position.x = bottomLeft.x
}
if player.position.x >= topRight.x {
player.position.x = topRight.x
}
if player.position.y <= bottomLeft.y {
player.position.y = bottomLeft.y
}
if player.position.y >= topRight.y {
player.position.y = topRight.y
}
}
override func update(_ currentTime: TimeInterval) {
if let accelerometerData = motionManager.accelerometerData {
// note that since the game will be running in landscape mode, up and down movement is controlled by the x axis and right to left movement is controlled by the y-axis...so this is the inverse of what you'd normally think.
let changeX = CGFloat(accelerometerData.acceleration.y) * 4
let changeY = CGFloat(accelerometerData.acceleration.x) * 4
// you have to subtract from x position and add to the y position because device is rotated so the axises aren't what you would normally expect.
player.position.x -= changeX
player.position.y += changeY
// check to make sure position isn't outside payable area:
boundsCheck()
}
}
// function below shows outline of playableRect
func drawPlayableRect(rect: CGRect) {
let shape = SKShapeNode()
let path = CGMutablePath()
path.addRect(rect)
shape.path = path
shape.strokeColor = .red
shape.lineWidth = 4.0
addChild(shape)
}
}
Use: UIScreen.main.nativeBounds
So here is my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ship1 = [2,1]
var ship2 = [1,2]
let jonahSpriteNode = SKSpriteNode(imageNamed: "jonah_spaceship")
let georgeSpriteNode = SKSpriteNode(imageNamed: "george_spaceship")
override func didMove(to view: SKView) {
//var jonahFrames = [SKTexture]()
jonahSpriteNode.position = CGPoint(x: 30, y: frame.midY)
jonahSpriteNode.size = CGSize(width: 100.0, height: 100.0)
addChild(jonahSpriteNode)
georgeSpriteNode.position = CGPoint(x: 628, y: frame.midY)
georgeSpriteNode.size = CGSize(width: 100.0, height: 100.0)
addChild(georgeSpriteNode)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
var touchLocation = touch.location(in: self)
var angle1 = atan2(jonahSpriteNode.position.y - touchLocation.y , jonahSpriteNode.position.x - touchLocation.x)
var angle = angle1 - CGFloat(Double.pi / 1)
makeCircularRange(to: jonahSpriteNode)
let rotate = SKAction.rotate(toAngle: angle, duration: 1.0)
let move = SKAction.move(to: CGPoint(x: touchLocation.x, y: touchLocation.y), duration: 2.5)
let sequence = SKAction.sequence([rotate, move])
jonahSpriteNode.run(sequence)
}
}
func makeCircularRange(to node: SKNode) {
let range = SKRange(lowerLimit: 0, upperLimit: 400)
let constraint = SKConstraint.distance(range, to: .zero)
node.constraints = [constraint]
}
}
I wanted to display the SKRange by showing upperLimit in a certain color. I tried making nodes around the limit and then coloring the nodes but it just showed a bunch of errors. If you have any ideas please answer.
Something like this:
The sprite node will be in the center and the circle will show where it can move.
You can make a circular shape using range.upperLimit as a radius an add to scene.
func drawCircularRangeBy(range: SKRange) {
let radius = range.upperLimit
let node = SKShapeNode(circleOfRadius: radius)
node.strokeColor = .white
addChild(node)
}
Take a look of this example: https://github.com/Maetschl/SpriteKitExamples/blob/master/CircularRange/CircularRange/GameScene.swift
If you really need dots instead of a line please see this answer: Drawing dashed line in Sprite Kit using SKShapeNode
I am building a spritekit game and have just started the project. I have a circle on the screen which starts in the center, and when i drag my finger from the circle outward, it will show a dotted line/bezierpath connected to the ball which will help the user see where it is aiming the ball. When the user lifts their finger, the ball will shoot in the opposite direction of the aim line. (Think a game like soccer stars or pool). The issue is that the maneuver works the first time when everything starts in the middle: I drag my finger and the ball shoots in opposite direction then stops. But when I try it again, the position of the aiming line says it is the same as the ball (It should be), but then it shows up like an inch away from the ball on the screen. I feel like this may be an issue that the scene(s) behind the objects may not be the same size? But I'm confused because I think I'm only using one scene.
GameViewController viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
scene.size = view.bounds.size
//scene.anchorPoint = CGPoint(x: 0.0, y: 0.0)
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
GameScene Code (Doubt you need all of it but whatever):
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKShapeNode(circleOfRadius: 35)
var touchingBall = false
var aimLine = SKShapeNode()
var startAimPoint = CGPoint()
var endAimPoint = CGPoint()
let damping:CGFloat = 0.94
override func didMove(to view: SKView) {
ball.fillColor = SKColor.orange
ball.name = "ball"
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
var physicsBody = SKPhysicsBody(circleOfRadius: 35)
ball.physicsBody = physicsBody
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.friction = 10.0
ball.position = CGPoint(x: frame.midX, y: frame.midY)
self.addChild(ball)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOUCHES BEGAN.")
for touch in touches {
print("TB: \(touchingBall)")
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
if node.name == "ball" {
// touched inside node
if ball.physicsBody!.angularVelocity <= 0.0{
touchingBall = true
startAimPoint = ball.position
print(touchingBall)
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOCUHES MOVED.")
for touch in touches {
let location = touch.location(in: self)
if touchingBall{
endAimPoint = location
assignAimLine(start: startAimPoint, end: endAimPoint)
print("Moving touched ball")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches ended. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x - startAimPoint.x) * 3, dy: -(endAimPoint.y - startAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches cancelled. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x - startAimPoint.x) * 3, dy: -(endAimPoint.y - startAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
print(ball.physicsBody!.velocity)
let dx2 = ball.physicsBody!.velocity.dx * damping
let dy2 = ball.physicsBody!.velocity.dy * damping
ball.physicsBody!.velocity = CGVector(dx: dx2, dy: dy2)
}
func assignAimLine(start: CGPoint, end: CGPoint){
aimLine.removeFromParent()
var bezierPath = UIBezierPath()
bezierPath.move(to: start)
bezierPath.addLine(to: shortenedEnd(startPoint: start, endPoint: end))
var pattern : [CGFloat] = [10.0, 10.0]
let dashed = SKShapeNode(path: bezierPath.cgPath.copy(dashingWithPhase: 2, lengths: pattern))
aimLine = dashed
aimLine.position = ball.position
aimLine.zPosition = 0
self.addChild(aimLine)
}
func hypotenuse(bp: UIBezierPath) -> Double{
var a2 = bp.cgPath.boundingBox.height * bp.cgPath.boundingBox.height
var b2 = bp.cgPath.boundingBox.width * bp.cgPath.boundingBox.width
return Double(sqrt(a2 + b2))
}
func hypotenuse(startP: CGPoint, endP: CGPoint) -> Double{
var bezierPath = UIBezierPath()
bezierPath.move(to: startP)
bezierPath.addLine(to: endP)
return hypotenuse(bp: bezierPath)
}
func shortenedEnd(startPoint: CGPoint, endPoint: CGPoint) -> CGPoint{
var endTemp = endPoint
//while hypotenuse(startP: startPoint, endP: endTemp) > 150{
endTemp = CGPoint(x: endTemp.x / 1.01, y: endTemp.y / 1.01)
//}
return endTemp
}
func addTestPoint(loc: CGPoint, color: UIColor){
var temp = SKShapeNode(circleOfRadius: 45)
temp.fillColor = color
temp.position = loc
self.addChild(temp)
}
}
I tried printing the frame size for the scene and it says 400 something x 700 something (I am testing on iPhone 6 Plus), and it says the UIScreen is same size so i don't know what issue is. Overall, I just need the aiming line to be on the center of the circle more than just the first time I try the maneuver. Thanks.
Like I mentioned in the comments, your problem was how you were laying out your paths. The code below makes the path relative to the ball instead of absolute to the scene. I also fixed the issue with creating new shapes every time.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKShapeNode(circleOfRadius: 35)
var touchingBall = false
var aimLine = SKShapeNode()
var endAimPoint = CGPoint()
override func didMove(to view: SKView) {
ball.fillColor = SKColor.orange
ball.name = "ball"
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
ball.position = CGPoint(x: frame.midX, y: frame.midY)
let physicsBody = SKPhysicsBody(circleOfRadius: 35)
physicsBody.affectedByGravity = false
physicsBody.friction = 10.0
physicsBody.linearDamping = 0.94
ball.physicsBody = physicsBody
self.addChild(ball)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOUCHES BEGAN.")
for touch in touches {
print("TB: \(touchingBall)")
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
if node.name == "ball" {
// touched inside node
if ball.physicsBody!.angularVelocity <= 0.0{
touchingBall = true
aimLine.path = nil
self.addChild(aimLine)
print(touchingBall)
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOCUHES MOVED.")
for touch in touches {
let location = touch.location(in: self)
if touchingBall{
endAimPoint = self.convert(location, to: ball)
assignAimLine(end: endAimPoint)
print("Moving touched ball")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches ended. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x) * 3, dy: -(endAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches cancelled. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x) * 3, dy: -(endAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
print(ball.physicsBody!.velocity)
//let dx2 = ball.physicsBody!.velocity.dx * damping
//let dy2 = ball.physicsBody!.velocity.dy * damping
//ball.physicsBody!.velocity = CGVector(dx: dx2, dy: dy2)
}
func assignAimLine(end: CGPoint){
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint.zero)
bezierPath.addLine(to: end)
let pattern : [CGFloat] = [10.0, 10.0]
aimLine.position = ball.position
aimLine.path = bezierPath.cgPath.copy(dashingWithPhase: 2, lengths: pattern)
aimLine.zPosition = 0
}
func addTestPoint(loc: CGPoint, color: UIColor){
var temp = SKShapeNode(circleOfRadius: 45)
temp.fillColor = color
temp.position = loc
self.addChild(temp)
}
}
In SpriteKit I need to rotate a sprite along an axis (e.g. the one that passes through the center of the sprite) just like a wheel to be spinned by the user.
I tried to use applyTorque function (to apply a force that is only angular and not linear), but I cannot handle the different forces caused by different movements on the screen (longer the touch on the screen, stronger the force to apply).
Can someone help me to understand how to deal with this problem?
Here is an answer that spins a ball according to how fast you swipe left / right:
class GameScene: SKScene {
let speedLabel = SKLabelNode(text: "Speed: 0")
let wheel = SKShapeNode(circleOfRadius: 25)
var recognizer: UIPanGestureRecognizer!
func pan(recognizer: UIPanGestureRecognizer) {
let velocity = recognizer.velocity(in: view!).x
// Play with this value until if feels right to you.
let adjustor = CGFloat(60)
let speed = velocity / adjustor
wheel.physicsBody!.angularVelocity = -speed
}
// Scene setup:
override func didMove(to view: SKView) {
removeAllChildren()
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
wheel.fillColor = .blue
wheel.physicsBody = SKPhysicsBody(circleOfRadius: 25)
wheel.physicsBody!.affectedByGravity = false
let wheelDot = SKSpriteNode(color: .gray, size: CGSize(width: 5, height:5))
wheel.addChild(wheelDot)
wheelDot.position.y += 20
wheel.setScale(3)
speedLabel.setScale(3)
speedLabel.position.y = (frame.maxY - speedLabel.frame.size.height / 2) - 45
recognizer = UIPanGestureRecognizer(target: self, action: #selector(pan))
view.addGestureRecognizer(recognizer)
addChild(wheel)
addChild(speedLabel)
}
override func didSimulatePhysics() {
speedLabel.text = "Speed: \(abs(Int(wheel.physicsBody!.angularVelocity)))"
}
}
Here is a basic example of spinning a wheel clockwise or counterclockwise depending on if you press on left / right side of screen. Hold to increase speed:
class GameScene : SKScene {
enum Direction { case left, right }
var directionToMove: Direction?
let wheel = SKShapeNode(circleOfRadius: 25)
let speedLabel = SKLabelNode(text: "Speed: 0")
override func didMove(to view: SKView) {
// Scene setup:
anchorPoint = CGPoint(x: 0.5, y: 0.5)
removeAllChildren()
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
wheel.fillColor = .blue
wheel.physicsBody = SKPhysicsBody(circleOfRadius: 25)
wheel.physicsBody!.affectedByGravity = false
let wheelDot = SKSpriteNode(color: .gray, size: CGSize(width: 5, height:5))
wheel.addChild(wheelDot)
wheelDot.position.y += 20
wheel.setScale(3)
speedLabel.setScale(3)
speedLabel.position.y = (frame.maxY - speedLabel.frame.size.height / 2) - 45
addChild(wheel)
addChild(speedLabel)
}
// Change this to touchesBegan for iOS:
override func mouseDown(with event: NSEvent) {
// Change this to touches.first!.location(in: self) for iOS.
let location = event.location(in: self)
// Determine if touch left or right side:
if location.x > 0 {
directionToMove = .right
}
else if location.x < 0 {
directionToMove = .left
}
}
override func mouseUp(with event: NSEvent) {
// Stop applying gas:
directionToMove = nil
print("lol")
}
override func update(_ currentTime: TimeInterval) {
// This is how much speed we gain each frame:
let torque = CGFloat(0.01)
guard let direction = directionToMove else { return }
// Apply torque in the proper direction
switch direction {
case .left:
wheel.physicsBody!.applyTorque(torque)
case .right:
wheel.physicsBody!.applyTorque(-torque)
}
}
override func didSimulatePhysics() {
// Speedometer:
speedLabel.text = "Speed: \(abs(Int(wheel.physicsBody!.angularVelocity)))"
}
}
I am creating a game where there is a square shape and every time the player taps on the square, it goes to GameOver scene. All I want to do is when the square shape is tapped, it will be allotted different position of the screen to be tapped on the squares.
Here is my code:
let touch:UITouch = touches.first!
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name
{
//The first ball that shows up
if name == "startball"
{
print("Touched", terminator: "")
addBall(ballSize)
self.addChild(score)
}
else if name == "shape"{
scoreCount += 1
addBall(ballSize)
audioPlayer.play()
}
}
else {
let scene = GameOver(size: self.size)
scene.setMyScore(0)
let skView = self.view! as SKView
skView.ignoresSiblingOrder = true
scene.scaleMode = .resizeFill
scene.size = skView.bounds.size
scene.setMessage("You Lost!")
scene.setEndGameMode(va)
gameTimer.invalidate()
shownTimer.invalidate()
print(" timers invalidated ", terminator: "")
ran = true
skView.presentScene(scene, transition: SKTransition.crossFade(withDuration: 0.25))
}
if firstTouch {
shownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(GameScene.decTimer), userInfo: nil, repeats: true)
gameTimer = Timer.scheduledTimer(timeInterval: TIME_INCREMENT, target:self, selector: Selector("endGame"), userInfo: nil, repeats: false)
firstTouch = false
}
if touchCount > 5 {
for touch: AnyObject in touches {
let skView = self.view! as SKView
skView.ignoresSiblingOrder = true
var scene: Congratz!
scene = Congratz(size: skView.bounds.size)
scene.scaleMode = .aspectFill
skView.presentScene(scene, transition: SKTransition.doorsOpenHorizontal(withDuration: 1.0))
}
}
touchCount += 1
}
override func update(_ currentTime: TimeInterval) {
super.update(currentTime)
}
func endGame(){
shownTimer.invalidate()
gameTimer.invalidate()
let scene = GameOver(size: self.size)
scene.setMyScore(scoreCount)
if let skView = self.view {
skView.ignoresSiblingOrder = false
scene.scaleMode = .resizeFill
scene.size = skView.bounds.size
scene.setMessage("Times up")
skView.presentScene(scene, transition: SKTransition.crossFade(withDuration: 0.25))
}
}
override func addBall(_ size: Int) {
// Add the ball
let currentBall = SKShapeNode(circleOfRadius: CGFloat(size))
let viewMidX = view!.bounds.midX
let viewMidY = view!.bounds.midY
currentBall.fillColor = pickColor()
currentBall.position = randomBallPosition()
if scoreCount != 0{
if scoreCount == 1{
self.addChild(score)
self.addChild(timeLeftLabel)
self.childNode(withName: "welcome")?.removeFromParent()
}
self.childNode(withName: "ball")?.run(getSmaller)
self.childNode(withName: "ball")?.removeFromParent()
}
currentBall.name = "ball"
self.addChild(currentBall)
}
func addSquare(_ size: Int) {
// Add the square
let shape = SKShapeNode(rectOf: CGSize(width:CGFloat(size), height:CGFloat(size)))
shape.path = UIBezierPath(roundedRect: CGRect(x: 64, y: 64, width: 160, height: 160), cornerRadius: 50).cgPath
shape.fillColor = pickColor()
shape.position = randomBallPosition()
shape.name = "shape"
self.addChild(shape)
}
func randomBallPosition() -> CGPoint {
let xPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxX)! + 1)))
let yPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxY)! + 1)))
return CGPoint(x: xPosition, y: yPosition)
}
What I would suggest would be to make something like an enum for the different shapes, so you can keep track of what shape you're using.
enum GameShape: Int {
case circle = 0
case square = 1
}
Then create a GameShape property at the top of your GameScene:
var currentShape: GameShape = .circle
Then you could create some sort of updateShape method, which you could call in your touchesBegan method instead of just addBall
func updateShape(shapeSize: CGSize) {
switch currentShape {
case .circle:
addCircle(shapeSize)
case .square:
addSquare(shapeSize)
default:
break
}
// However you want to setup the condition for changing shape
if (condition) {
currentShape = .square
}
}
func addBall(_ size: CGSize) {
// Add the ball
}
func addSquare(_ size: CGSize) {
// Add the square
}
Now in your touchesBegan method, instead of calling addBall(size, you could call updateShape:
override func touchesBegan(_ touches: Set<UITouch>!, with event: UIEvent?) {
// Setup your code for detecting position for shape origin
updateShape(shapeSize)
}
EDIT - Your code is a mess. You really should take the time to make sure it's properly formatted when you submit it, otherwise it's really hard to help you. Indentation helps to see where a closure begins and ends. From what I can tell, it looks like you have two or more functions nested within your addBall method. This is not good. I tried my best to clean it up for you. You'll still need to write the code to make the shape a square, but I've lead you in the right direction to start to make that happen:
func addBall(_ size: CGSize) {
// Add the ball
let currentBall = SKShapeNode(circleOfRadius: CGFloat(size))
let viewMidX = view!.bounds.midX
let viewMidY = view!.bounds.midY
currentBall.fillColor = pickColor()
shape.path = UIBezierPath(roundedRect: CGRect(x: 64, y: 64, width: 160, height: 160), cornerRadius: 50).cgPath
shape.fillColor = pickColor()
currentBall.position = randomBallPosition()
shape.position = randomBallPosition()
self.addChild(shape)
if scoreCount != 0{
if scoreCount == 1{
self.addChild(score)
self.addChild(timeLeftLabel)
self.childNode(withName: "welcome")?.removeFromParent()
}
self.childNode(withName: "ball")?.run(getSmaller)
self.childNode(withName: "ball")?.removeFromParent()
}
currentBall.name = "ball"
shape.name = "ball"
self.addChild(currentBall)
}
func addSquare(_ size: CGSize) {
// Add the square
}
func randomBallPosition() -> CGPoint {
let xPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxX)! + 1)))
let yPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxY)! + 1)))
return CGPoint(x: xPosition, y: yPosition)
}
Now the above code assumes you have some property named shape because from what I could tell you never explicitly instantiated that, but you begin manipulating it.