I'm trying to create a node and animate it's zRotation property with an action, however, when attempting to run the action, the zRotation property of my node is not changed. I'm unsure why this isn't working.
import UIKit
import SpriteKit
let aim = SKSpriteNode()
print(aim.zRotation)
aim.zRotation = CGFloat(Double.pi/2)
print(aim.zRotation)
let myAction = SKAction.rotate(toAngle: CGFloat(3.14), duration: 0)
aim.run(myAction)
print(aim.zRotation)
image of code with output
An SKAction will only be evaluated when your node is in a scene that is in turn being displayed in a view:
An SKAction object is an action that is executed by a node in the
scene (SKScene). ... When the scene processes its nodes, actions
associated with those nodes are evaluated.
(From Apple's SKAction documentation)
At the moment you have neither of those things so the action remains dormant. For an example of adding nodes to a scene and displaying that scene in a playground see this from Swift Studies.
I guess you want to achieve something like this
Issues
There a a few errors in your code:
An action with duration 0 will instantly apply the change, better setting a greater value.
You are not presenting the SKView in Playground
Your SKSpriteNode has not image
Solution
In Playground select View > Assistant > Show Assistant Editor in order to open a panel on the right where we'll add the SKView.
Now add this code an wait for the red square to appear in the Assistant Editor.
import PlaygroundSupport
import SpriteKit
let sceneView = SKView(frame: CGRect(x:0 , y:0, width: 500, height: 500))
let scene = SKScene(size: CGSize(width: 500, height: 500))
scene.anchorPoint.x = 0.5
scene.anchorPoint.y = 0.5
sceneView.showsFPS = true
sceneView.presentScene(scene)
PlaygroundPage.current.liveView = sceneView
let square = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 100, height: 100))
square.fillColor = .red
scene.addChild(square)
square.run(.rotate(toAngle: 3.14, duration: 20))
Related
I am currently making an iOS game in which a ball must bounce within a frame defined by the below image:
The game relies on the below method to automatically create and assign a physics body to the frame surrounding the game.
frame.physicsBody = SKPhysicsBody(texture: frameTexture, size: frame.size)
This code, however, assigns a body that encompasses the outside of the image, rather than the interior, and thus treats the blank interior as a solid object. This, in turn, means that the ball cannot be inside the frame and is forced off the screen immediately after being spawned.
Is there any way to give a physics body an interior?
Another post here: SpriteKit SKPhysicsBody with Inner Edges aims to do something similar, but does not involve automatic wrapping around a texture (hence the new question)
You can use init(edgeLoopFrom:) initializer to achieve inner edge friction. you can use this code below:
override func didMove(to view: SKView) {
//Setup scene's physics body (setup the walls)
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
let back = SKSpriteNode(imageNamed: "1")
back.position = CGPoint(x: frame.midX, y: frame.midY)
back.size = CGSize(width: 500, height: 600)
back.zPosition = -3
let rect = CGRect(origin: CGPoint(x: -back.size.width/2, y: -back.size.height/2), size: back.size)
back.physicsBody = SKPhysicsBody(edgeLoopFrom: rect)
addChild(back)
//Add Red ball "inside" the back sprite
let red = SKShapeNode(circleOfRadius: 20)
red.fillColor = .red
red.strokeColor = .clear
red.position = back.position
red.physicsBody = SKPhysicsBody(circleOfRadius: 20)
red.physicsBody?.restitution = 1
red.physicsBody?.friction = 0
red.zPosition = 1
red.physicsBody?.affectedByGravity = false
addChild(red)
red.physicsBody?.applyImpulse(CGVector(dx: 20, dy: 15))
}
please follow this Question and have a look to the answer given below.
i am providing a screenshot of my project
Above picture illustrates what I want:
The red circle is the starting point of a UIView
I want to launch it upwards (Y position) with a change in the X position
I want to make it drop within boundaries.
What I tried:
For the launch I can make use of the UIView.animate function. For the drop I can use the UIDynamicAnimator. However there are some problems:
-In the UIView.animate I cannot 'curve' the animation, only a straight line. I can use this answer here to draw a curve line: https://stackoverflow.com/a/43813458/7715250
-Combining both functions is not working. After the UIView.animate is done, the UIView just drop straight downwards.
The code for UIDynamicAnimator:
var animator: UIDynamicAnimator!
//view did appear
let view2 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view2.center = self.view.center
view2.backgroundColor = .blue
self.view.addSubview(view2)
animator = UIDynamicAnimator(referenceView: self.view)
let gravity = UIGravityBehavior(items: [view2])
animator.addBehavior(gravity)
let collosion = UICollisionBehavior(items: [view2])
collosion.translatesReferenceBoundsIntoBoundary = true
let dynamic = UIDynamicItemBehavior(items: [view2])
dynamic.elasticity = 0.7
animator.addBehavior(collosion)
animator.addBehavior(dynamic)
That will drop the UIView with a nice bounce effect. But how to launch the UIView? How to change the X and Y position and remain the added behaviours?
I think what you need is UIPushBehavior. Add this extra behavior.
let push = UIPushBehavior(items: [view2], mode: UIPushBehaviorMode.instantaneous)
push.setAngle(CGFloat(-M_PI/2 - 0.1), magnitude: 8.0) // Adjust angle and magnitude
animator.addBehavior(push)
I would like to create a MacOS application using Swift and SceneKit from scratch [using the Cocoa application template].
Somehow my overlay SpriteKit scene isn't visible while the same steps do work on iOS.
Steps taken:
new project > cocoa application
add sceneview to main.storyboard
add outlet from sceneview to viewcontroller
Code added to viewDidLoad:
// Create scenekit-scene
// ---------------------
let scene = SCNScene()
let nodeCamera = SCNNode()
nodeCamera.camera = SCNCamera()
scene.rootNode.addChildNode(nodeCamera)
let box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)
box.firstMaterial?.diffuse.contents = NSColor.blue
let nodeBox = SCNNode(geometry: box)
nodeBox.position = SCNVector3(0,0, -2)
scene.rootNode.addChildNode(nodeBox)
// Add to view
sceneView.scene = scene
// Create spritekit-scene
// ----------------------
let spriteScene = SKScene(size: CGSize(width: sceneView.frame.width, height: sceneView.frame.height))
let nodelabel = SKLabelNode(fontNamed: "Menlo")
nodelabel.text = "SpriteKit"
nodelabel.fontColor = NSColor.black
nodelabel.position = CGPoint(x: spriteScene.size.width/2, y: spriteScene.size.height/2)
spriteScene.addChild(nodelabel)
// Add to scenekit
sceneView.overlaySKScene = spriteScene
In the iOS-simulator I see a blue cube with black "SpriteKit" in the middle. On MacOS I only see the blue cube.
[BTW: What is kind of strange: when I use the MacOS game template [scenekit] the overlaySKScene functions normal and is visible.]
So what am I missing?
While comparing the storyboards scene view properties between the cocoa-template and the game-template I noticed the view controller's view in the cocoa-template had a check-mark in view effects inspector>core animation layer.
When unchecked the OverlaySKScene became visible.
I am trying to learn Swift by creating my own game (app), but I am having a problem.
I have a SKSpriteNode which I use as the background for the game. It's an image I've made. My problem is that my background won't fill the view so I get these grey bars outside of the Node.
What is wrong in my code?
override func didMoveToView(view: SKView) {
/* Setup your scene here */
Hintergrund = SKSpriteNode(imageNamed: "PenaltyLocker_BG1")
Hintergrund.size = CGSize(width: view.frame.width, height: view.frame.height)
Hintergrund.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2)
Hintergrund.zPosition = 1
Hintergrund.name = "Hintergrund"
addChild(Hintergrund)
Now when I start the simulator for iPhone it looks like this: http://imgur.com/a/mBOWn
Change to use the size of the SKScene.
self.scene.size.width / self.scene.size.height
Hintergrund.size = CGSize(width: self.scene.size.width, height: self.scene.size.height)
Hintergrund.position = CGPoint(x: self.scene.size.width/2, y: self.scene.size.height/2)
You can also check this tutorial for more info.
This is my code I wrote in Xcode using Sprite Kit, Swift. I'm trying to make a game like ComboQuest. The game consists of a moving bar that moves left and right trying to hit objects. As I'm trying to recreate that left and right movement here is my code:
func rightSprite(){
let actionR = SKAction.moveByX(0.001, y: 0, duration: 0.01)
Sprite.runAction(SKAction.repeatActionForever(actionR))
}
func leftSprite(){
let actionR = SKAction.moveByX(-0.001, y: 0, duration: 0.01)
Sprite.runAction(SKAction.repeatActionForever(actionR))
}
These two functions are then activate in the TouchesBegan override func, but there is a small flaw. When you touch the screen to change the direction there is not a complete reaction.
How would you guys code this?
First off, make sure you have a valid instance of a SKSpriteNode. It would be helpful to see more of your code to see where you are creating the bar sprite you would like to move. For example, below I created a red rectangle sprite.
let bar = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 10))
As far as your actions, you need to call them on your instance of a sprite. I used the same bar sprite from the example above. Also, since you are setting an action to repeat forever, you will need to remove that action to get it to stop. It appears you want the bar to move left and right. You can either remove all actions as I have done, or you could name your actions, and just remove a certain one. For simplicity, I just removed all actions.
func rightSprite(){
let actionR = SKAction.moveByX(0.001, y: 0, duration: 0.01)
bar.removeAllActions()
bar.runAction(SKAction.repeatActionForever(actionR))
}
func leftSprite(){
let actionL = SKAction.moveByX(-0.001, y: 0, duration: 0.01)
bar.removeAllActions()
bar.runAction(SKAction.repeatActionForever(actionL))
}
Take a look a this :
override func didMoveToView(view: SKView) {
let sprite = SKSpriteNode(color: UIColor.purpleColor(), size: CGSize(width: 20, height:50))
var startPoint:CGPoint = CGPoint(x: 100, y: CGRectGetMidY(frame))
var endPoint:CGPoint = CGPoint(x: 300, y: CGRectGetMidY(frame))
let moveLeft = SKAction.moveTo(endPoint, duration:3)
let moveRight = SKAction.moveTo(startPoint, duration:3)
let sequence = SKAction.sequence([moveLeft,moveRight])
sprite.position = startPoint
addChild(sprite)
sprite.runAction(SKAction.repeatActionForever(sequence), withKey:"moving")
}
I assume that your scene and view are initialized and sized correctly (eg that you done something like this scene.size = skView.bounds.size ) so when you try this example, sprite will show up on screen (instead of possible off-screen because you probably loading a scene from a .sks file).
IMO this is the natural way when using SKAction to move a sprite from point A to point B continuously (like from a Combo Quest) and that is:
Create starting and ending point
Make separate actions for moving sprite left and right
Make a sequence to run those two actions one after another
Repeat that action forever
Run that action with key to give your self a way to stop an action
When you want to stop action, you should do something like this:
if(sprite.actionForKey("moving") != nil){
sprite.removeActionForKey("moving")
}