I have two SKSpriteKitNodes, one in the left and one in the right. Then I detect where is the location of the touch and make the action according to its location (the left side shoots, and the right side speeds up). But the thing is, if the left is being touched, how can I handle the right-side touch at the same time?
The code I have so far:
let left = SKSpriteNode()
let right = SKSpriteNode()
override func didMoveToView(view: SKView) {
left.size = CGSize(width: frame.width / 2, height: frame.height)
right.size = CGSize(width: frame.width / 2, height: frame.height)
left.position = CGPoint(x: frame.width / 4, y: frame.height / 2)
right.position = CGPoint(x: 3 * frame.width / 4, y: frame.height / 2)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
if right.containsPoint(location){
//HANDLE THE SPEED
if speed == 0{
speed = 5
}
shouldBreak = false
}
if left.containsPoint(location){
//HANDLE THE SHOOTING
shoot.position = CGPoint(x: frame.midX, y: frame.height * 0.2)
isShooting = true
}
}
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
I am trying to create a spinning fortune wheel action via SKAction. I have a SKNode which used as wheel, this SKNode is a circle that divided to four quarters (each quarter in different color). also I set an SKAction (which is repeating for 10 counts) that spin the SKNode around fixed point (the node's center). The problem is that the action is running well but it stops suddenly and not slowing down - like a real wheel. I don't really have an idea how to set this animation, I mean to slow the spinning down before the action is stop.
Here is my code so far:
class GameScene: SKScene {
let colors = [SKColor.yellow, SKColor.red, SKColor.blue, SKColor.purple]
override func didMove(to view: SKView) {
createWheel()
let sq = CGRect(x: size.width/2, y: size.height/2, width: 300, height: 300)
let sqx = SKShapeNode(rect: sq)
sqx.lineWidth = 2
sqx.fillColor = .clear
sqx.setScale(1.0)
addChild(sqx)
}
func createWheel() {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: -200))
path.addArc(withCenter: CGPoint.zero,radius: 200,startAngle: CGFloat(0.0), endAngle: CGFloat(3.0 * Double.pi / 2),clockwise: false)
path.addLine(to: CGPoint(x: 200, y: 0))
let obstacle = obstacleByDuplicatingPath(path, clockwise: true)
obstacle.position = CGPoint(x: size.width/2, y: size.height/2)
addChild(obstacle)
let rotateAction = SKAction.rotate(byAngle: CGFloat((3.0 * CGFloat(Double.pi / 2)) - 90), duration: 0.5)
//obstacle.run(SKAction.repeatForever(rotateAction))
obstacle.run(SKAction.repeat(rotateAction, count: 10))
}
func obstacleByDuplicatingPath(_ path: UIBezierPath, clockwise: Bool) -> SKNode {
let container = SKNode()
var rotationFactor = CGFloat(Double.pi / 2)
if !clockwise {
rotationFactor *= -1
}
for i in 0...3 {
let section = SKShapeNode(path: path.cgPath)
section.fillColor = colors[i]
section.strokeColor = colors[i]
section.zRotation = rotationFactor * CGFloat(i);
let origin = CGPoint(x: 0.0, y: 0.0)
switch i {
case 0:
section.position = CGPoint(x: (origin.x + 10), y: (origin.y - 10))
case 1:
section.position = CGPoint(x: (origin.x + 10), y: (origin.y + 10))
case 2:
section.position = CGPoint(x: (origin.x - 10), y: (origin.y + 10))
case 3:
section.position = CGPoint(x: (origin.x - 10), y: (origin.y - 10))
default:
print("bolbol")
}
container.addChild(section)
}
return container
}
}
edit:
I was thinking about it and I tried to do it via SKAction, I set another action but this time I set their duration to a long one. first it run a action of duration 0.5, then of 2 and at end of 4. I looks pretty good but still not smooth as I want it to be.
here is my code:
let rotateAction = SKAction.rotate(byAngle: CGFloat(2.0 * CGFloat(M_PI)), duration: 0.5)
let rotateAction2 = SKAction.rotate(byAngle: CGFloat(2.0 * CGFloat(M_PI)), duration: 2)
let rotateAction3 = SKAction.rotate(byAngle: CGFloat(2.0 * CGFloat(M_PI)), duration: 4)
let wait = SKAction.wait(forDuration: 5)
let g1 = SKAction.repeat(rotateAction, count: 10)
let group = SKAction.group([wait, g1, rotateAction2, rotateAction3])
what do you think? there is any way to do it better??
edit 2:
Continued to #Ali Beadle answer, I tried to do it via physics body, the problem now is the when I drag finger on the screen the SKShapeNode (shape) in continue to rotate and never stops. can you detect what is wrong?
class GameScene: SKScene {
var start: CGPoint?
var end:CGPoint?
var startTime: TimeInterval?
let shape = SKShapeNode.init(rectOf: CGSize(width: 150, height: 150))
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
let sceneBody = SKPhysicsBody.init(edgeLoopFrom: self.frame)
sceneBody.friction = 0
self.physicsBody = sceneBody
shape.fillColor = SKColor.red
shape.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
shape.physicsBody = SKPhysicsBody.init(rectangleOf: CGSize(width: 50, height: 50))
shape.physicsBody?.affectedByGravity = false
shape.physicsBody?.isDynamic = true
addChild(shape)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
self.start = touch.location(in: self)
self.startTime = touch.timestamp
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
self.end = touch.location(in: self)
var dx = ((self.end?.x)! - (self.start?.x)!)
var dy = ((self.end?.y)! - (self.start?.y)!)
let magnitude:CGFloat = sqrt(dx*dx+dy*dy)
if(magnitude >= 25){
let dt:CGFloat = CGFloat(touch.timestamp - self.startTime!)
if dt > 0.1 {
let speed = magnitude / dt
dx = dx / magnitude
dy = dy / magnitude
print("dx: \(dx), dy: \(dy), speed: \(speed) ")
}
}
let touchPosition = touch.location(in: self)
if touchPosition.x < (self.frame.width / 2) {
self.shape.physicsBody?.angularVelocity = 10
self.shape.physicsBody?.applyAngularImpulse(-180)
} else {
self.shape.physicsBody?.angularVelocity = 10
self.shape.physicsBody?.applyAngularImpulse(180)
}
}}
I have created an open source prize spinning wheel in Spritekit that uses physics for realistic movement and flapper control. It also allows the user to drag the wheel to spin or generates a random spin by pushing the center of the wheel.
https://github.com/hsilived/SpinWheel
You can add realistic movement like this by using the built-in Physics simulation of SpriteKit. This will allow you to give your wheel a mass and friction and then use forces to rotate it. It will then slow down realistically.
In outline see Simulating Physics in the Apple Documentation:
To use physics in your game, you need to:
Attach physics bodies to nodes in the node tree and configure their physical properties. See SKPhysicsBody.
Define global characteristics of the scene’s physics simulation, such as gravity. See SKPhysicsWorld.
Where necessary to support your gameplay, set the velocity of physics bodies in the scene or apply forces or impulses to them. ...
The most appropriate method for your wheel is probably to make the wheel pinned to the scene and then rotate it with applyAngularImpulse.
I am using swift 2 and sprite-kit. I was wondering how I would make a choose character scene for my player to choose a character to play in the game, I have the scene set up, but I don't know how to integrate multiple characters, that can be chosen as the one to play? Any references or sample code would be appreciated.
in GameScene() i have
//plane = SKSpriteNode(imageNamed: "plane")
plane.size = CGSize(width: 80, height: 80)
plane.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 4)
plane.physicsBody = SKPhysicsBody(circleOfRadius: plane.frame.height / 2)
//plane.physicsBody = SKPhysicsBody(texture: plane.texture!, size: plane.size)
plane.physicsBody?.affectedByGravity = true
plane.physicsBody?.dynamic = true
plane.zPosition = 2
self.addChild(plane)'
in settings() i have
func mainScene(){
mainSceenButton = SKSpriteNode(imageNamed: "mainMenu")
mainSceenButton.size = CGSizeMake(200, 100)
mainSceenButton.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 + self.frame.height / 3)
self.addChild(mainSceenButton)
plane = SKSpriteNode(imageNamed: "plane")
plane.size = CGSizeMake(50, 50)
plane.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 + 100 )
self.addChild(plane)
plane1 = SKSpriteNode(imageNamed: "plane1")
plane1.size = CGSizeMake(50, 50)
plane1.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 )
self.addChild(plane1)
plane2 = SKSpriteNode(imageNamed: "plane2")
plane2.size = CGSizeMake(50, 50)
plane2.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 - 100 )
self.addChild(plane2)
}
override func didMoveToView(view: SKView) {
mainScene()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let mainMenuScene = StartScene(fileNamed: "StartScene")
for touch in touches{
let location = touch.locationInNode(self)
if mainSceenButton.containsPoint(location){
self.scene?.view?.presentScene(mainMenuScene!, transition: SKTransition.fadeWithDuration(1.0))
}
if plane1.containsPoint(location){
GameScene().plane = SKSpriteNode(imageNamed: "plane1")
} else {
GameScene().plane = SKSpriteNode(imageNamed: "plane")
}
}
}'
Really too broad of a question, but here's how I have seen this done in the past (and done myself)
Create a subclass of SKSpriteNode to make your custom player class.
When initializing change the texture of the node depending on what they select.
You could try using NSUserDefaults to save the users selection in Settings touchesBegan.
let defaults = NSUserDefaults.standardUserDefaults()
if plane.containsPoint(location) {
defaults.setObject("plane", forKey: "userPlane")
} else if plane1.containsPoint(location) {
defaults.setObject("plane1", forKey: "userPlane")
} else if plane2.containsPoint(location) {
defaults.setObject("plane2", forKey: "userPlane")
}
Then in GameScene, retrieve the selection when creating the plane.
let defaults = NSUserDefaults.standardUserDefaults()
if let userPlane = defaults.stringForKey("userPlane") {
plane = SKSpriteNode(imageNamed: userPlane)
}
In your code you are trying to set the plane on a new instance of GameScene, but that selection will be lost as soon as you exit the settings scene.
I want to make it so that the player can move with a joystick but cant go out of the circle. I made the joystick but I don't know how to do the other stuff. There is an example in the image below and also my code. Hope someone can help me, thanks.
class GameScene: SKScene {
var circuloPrincipal = SKSpriteNode(imageNamed: "circulo")
var circuloFondo = SKSpriteNode(imageNamed: "circuloFondo")
let base = SKSpriteNode(imageNamed: "circuloFondo")
let ball = SKSpriteNode(imageNamed: "circulo")
var stickActive:Bool = false
override func didMoveToView(view: SKView) {
/* Setup your scene here */
base.size = CGSize(width: 100, height: 100)
base.alpha = 0.3
base.zPosition = 2.0
base.position = CGPoint(x: frame.width / 2, y: frame.height / 2 - 310)
self.addChild(base)
ball.size = CGSize(width: 50, height: 50)
ball.color = circuloPrincipal.color
//ball.alpha = 0
ball.zPosition = 3.0
ball.position = base.position
self.addChild(ball)
circuloPrincipal.size = CGSize(width: 35, height: 35)
circuloPrincipal.position = CGPoint(x: frame.width / 2, y: frame.height / 2)
self.addChild(circuloPrincipal)
circuloPrincipal.color = UIColor(red: 0.75, green: 0.75, blue: 0.75, alpha: 1.0)
circuloPrincipal.colorBlendFactor = 1.0
circuloPrincipal.zPosition = 3.0
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if (CGRectContainsPoint(ball.frame, location)) {
stickActive = true
}else {
stickActive = false
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if (stickActive == true) {
let v = CGVector(dx: location.x - base.position.x, dy: location.y - base.position.y)
let angle = atan2(v.dy, v.dx)
let deg = angle * CGFloat(180 / M_PI)
print(deg + 180)
let lenght:CGFloat = base.frame.size.height / 2 - 20
let xDist: CGFloat = sin(angle - 1.57079633) * lenght
let yDist: CGFloat = cos(angle - 1.57079633) * lenght
ball.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
if (CGRectContainsPoint(base.frame, location)) {
ball.position = location
}else {
ball.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
}
} // termina stickActive
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if (stickActive == true) {
let move: SKAction = SKAction.moveTo(base.position, duration: 0.2)
move.timingMode = .EaseOut
ball.runAction(move)
}
}
Look into SKConstraint - you can set a constraint that a node cannot move further than a specified distance from another node. You'd set this 'other' node to the centre of the area that your player is limited to. Sprite-Kit will automatically move your node back should it go to far. As this is being in the game loop at 60fps before the node is redrawn after it's moved, you won't get any weird 'jumping' effects.
https://developer.apple.com/reference/spritekit/skconstraint
That documentation is a bit lacking compared to some of the other SK documentation. Here's and example that make a green rectangle 'follow' a yellow triangle within a specified distance:
let distanceRange = SKRange(lowerLimit: 200, upperLimit: 400)
let distanceConstraint = SKConstraint.distance(distanceRange, toNode: yellowTriangle)
greenRect.constraints = [distanceConstraint]
The constraints property of an SKNode (greenRect) is an array, so if you also want an orientation constraint (to keep greenRect facing the yellow triangle, for example), you would code something like this:
let orientRange = SKRange(lowerLimit: 0.0, upperLimit: 0.0)
let orientConstraint = SKConstraint.orientToNode(yellowTriangle, offset: orientRange)
greenRect.constraints = [orientatConstraint, distanceConstraint]
For your particular example, you might want to set the constraint as:
let distanceRange = SKRange(lowerLimit: 0, upperLimit: limitcircle.size/2)
let distanceConstraint = SKConstraint.distance(distanceRange, toPoint: limitCircle.position)
player.constraints = [distanceConstraint]
This assumes that the circle from which the player cannot move is an SKNode called limitcircle and it's anchor point is set to (0.5, 0.5) i,e, it's center. The above code would constrain the player to a distance of between 0 and 1/2 the width of the circle (i.e. it's radius) from a point at the center of the circle.
Been working on this for quite some time now. I have the Sprite so that it moves counter clockwise along the UIBezierPath as soon as the screen is touched. I also have it so it reverses direction along the UIBezierPath, but it always goes back to the beginning of the path. I want the Sprite to reverse direction, but start at the position of the sprite. Here's all of my code, any help would be greatly appreciated!
If you have any further questions feel free to ask. :)
var Circle = SKSpriteNode()
var Person = SKSpriteNode()
var Path = UIBezierPath()
var gameStarted = Bool()
var clockWise = true
override func didMoveToView(view: SKView) {
Circle = SKSpriteNode(imageNamed: "Circle")
Circle.size = CGSize(width: 300, height: 300)
Circle.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
self.addChild(Circle)
Person = SKSpriteNode(imageNamed: "Person")
Person.size = CGSize(width: 40, height: 7)
Person.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 2 + 112)
Person.zRotation = 3.14 / 2
self.addChild(Person)
Path = UIBezierPath(roundedRect: CGRect(x: self.frame.width / 2 - 113 , y: self.frame.height / 2 - 113, width: 226, height: 226), cornerRadius:226)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
let follow = SKAction.followPath(Path.CGPath, asOffset: false, orientToPath: true, speed: 100)
NSLog("\(self.frame.width / 2 - Person.position.x)")
NSLog("\(self.frame.height / 2 - Person.position.y)")
if gameStarted == false{
gameStarted = true
let Follow = SKAction.followPath(Path.CGPath, asOffset: false, orientToPath: true, speed: 100)
Person.runAction(SKAction.repeatActionForever(follow))
clockWise = false
}
else if gameStarted == true{
if clockWise == true{
clockWise = false
Person.runAction(SKAction.repeatActionForever(follow))
}
else if clockWise == false{
clockWise = true
Person.runAction(SKAction.repeatActionForever(follow).reversedAction())
//NSLog("\(Path.description)")
}
}
}