I want to scroll SKReferenceNodes created with my sks files I put together in the interface builder. I have scrolled SKSpriteNodes before, and I am trying to use the same code, but my SKReferenceNodes seem to have no frame, and so I can't position them.
Here is my SKReferenceNode setup:
let BG_POINTS_PER_SEC = 50
var dt:NSTimeInterval = 0
let background1 = SKReferenceNode(fileNamed:"BackgroundScene1")
let background2 = SKReferenceNode(fileNamed:"BackgroundScene2")
in didMoveToView
background1.position = CGPointZero;
background2.position = CGPointMake(background1.frame.size.width, 0)
self.addChild(background1)
self.addChild(background2)
in update
moveBackground()
func moveBackground() {
let bgVelocity = CGPoint(x:-BG_POINTS_PER_SEC, y:0)
let amtToMove = bgVelocity *CGFloat(dt)
background1.position = background1.position + amtToMove
background2.position = background2.position + amtToMove
if (background1.position.x <= background1.frame.size.width) {
background1.position.x = CGPointMake(background2.position.x + background2.frame.size.width, background1.position.y)
}
if (background2.position.x <= background2.frame.size.width) {
background2.position.x = CGPointMake(background1.position.x + background1.frame.size.width, background2.position.y)
}
}
Again this works for the scrolling background using a SKSpriteNode, but the SKReferenceNode returns a 0 frame. Is this even possible to scroll SKReferenceNodes from sks files like this?
Related
How can i check if two SKSpriteNodes are near each other? like in a radius of 100. i am using the gamescene.swift and gamescene.sks.
SKSpriteNode has a position property with the (x, y).
Distance between two positions is sqrt((x1-x2)^2 + (y1-y2)^2)
So:
let dist = sqrt(pow(sk1.position.x - sk2.position.x, 2.0) + pow(sk1.position.y - sk2.position.y, 2.0))
if dist < 100 {
// they are close
}
This is center to center.
Based on #MartinR's comment, you could also
let dist = hypot(sk1.position.x - sk2.position.x, sk1.position.y - sk2.position.y)
Which does the distance function for you.
If you want to use built in SKPhysicsBody, then just set the body to a circle with radius of 100, then you can use the didBeginContact method when a contact occurs:
func setup()
{
let physicsBody1 = SKPhysicsBody(circleOfRadius:100.0)
physicsBody1.categoryBitMask = 1
physicsBody1.collisionBitMask = 0
physicsBody1.contactTestBitMask = 2
sprite1.physicsBody = physicsBody1
let physicsBody2 = SKPhysicsBody(circleOfRadius:100.0)
physicsBody2.categoryBitMask = 2
physicsBody2.collisionBitMask = 0
physicsBody2.contactTestBitMask = 1
sprite2.physicsBody = physicsBody2
}
func didBeginContact(contact:SKPhysicsContact)
{
//find some tutorials to your liking, and do your contact code here
}
I am trying to get my "word" to float across the screen; constant velocity, no impacts, no gravity, no friction. Everything works except the word slows down.
Code for creating word:
func createWordNode (word: String, atPos: CGPoint) -> SKSpriteNode {
let doneSize = CGSize(width: 50, height: 50)
let wordSprite = SKSpriteNode()
wordSprite.size = CGSize(width: doneSize.width * CGFloat(word.len()), height: doneSize.height)
wordSprite.position = atPos
wordSprite.blendMode = .replace
wordSprite.zPosition = zlvlBG + 1
let ltrs = Array(word.uppercased().characters)
for i in 0 ... ltrs.count - 1 {
let done = SKSpriteNode(imageNamed: "LetterTiles/Tile" + String(ltrs[i]) + ".png")
done.size = doneSize
done.position = CGPoint(x: doneSize.width * CGFloat(Double(i) - 1.5), y: 0)
done.blendMode = .replace
done.zPosition = zlvlBG + 1
wordSprite.addChild(done)
}
wordSprite.physicsBody?.restitution = 1
wordSprite.physicsBody?.friction = 0
wordSprite.physicsBody?.linearDamping = 0
wordSprite.physicsBody?.angularDamping = 0
wordSprite.physicsBody?.mass = 2000
wordSprite.physicsBody = SKPhysicsBody(rectangleOf: wordSprite.size)
wordSprite.physicsBody?.collisionBitMask = 0
wordSprite.physicsBody?.contactTestBitMask = 0
wordSprite.physicsBody?.categoryBitMask = categoryWords
wordSprite.physicsBody?.fieldBitMask = 0
wordSprite.physicsBody?.isDynamic = true
wordSprite.physicsBody?.affectedByGravity = false
wordSprite.physicsBody?.allowsRotation = false
var velocity = CGVector()
velocity.dx = 100
velocity.dy = 0
wordSprite.physicsBody?.velocity = velocity
wordSprite.physicsBody?.applyImpulse(velocity)
wordSprite.name = "Word:" + word
return wordSprite
}
I call function like:
addChild (createWordNode(word: "Done", atPos: CGPoint(x:-500, y:450)))
Any ideas why word slows down?
Thanks.
It looks like you are creating an object that doesn't need to be in the physics simulation. So you may want to reconsider simply dropping the physics related code and update the nodes position manually.
That being said if you wish this node to remain in the Physics simulation. You need to add the SKPhysicsBody to the SKSpriteNode before you start trying to modify properties. e.g.
wordSprite.physicsBody = SKPhysicsBody(rectangleOf: wordSprite.size)
wordSprite.physicsBody?.restitution = 1
wordSprite.physicsBody?.friction = 0
wordSprite.physicsBody?.linearDamping = 0
wordSprite.physicsBody?.angularDamping = 0
wordSprite.physicsBody?.mass = 2000
I worked with sprite a little bit. But, I am not an expert. Does it work for you to use an action instead of velocity as the following for example:
var moveRight = SKAction.moveTo(CGPointMake(400, 0), duration:2.0)
wordSprite.runAction(moveRight)
One thing to point out too. Why do you use applyImpulse. velocity should be enough. Did you try to remove it?
I've added several sprite (SKSpriteNode) in my Scene like this:
let NPuzzle_Texture = SKTexture(imageNamed: "SKTexture.png")
NPuzzle0 = SKSpriteNode(texture: NPuzzle_Texture)
NPuzzle1 = SKSpriteNode(texture: NPuzzle_Texture)
NPuzzle2 = SKSpriteNode(texture: NPuzzle_Texture)
NPuzzle0.position = CGPoint (x: 100, y:125)
NPuzzle0.position = CGPoint (x: 300, y:125)
NPuzzle0.position = CGPoint (x: 500, y:125)
background.addChild(NPuzzle0)
background.addChild(NPuzzle1)
background.addChild(NPuzzle2)
I've also added several sprites in a different texture:
let Grey_Back = SKTexture(imageNamed: "Grey_Back.png")
grey_back = SKSpriteNode(texture: Grey_back)
grey_back.position = CGPoint (x: 1024, y:125)
grey_back.alpha = 0.5
background.addChild(grey_back)
Now I add new sprites (SKSpriteNode) to precedent grey_back like this:
grey_back.addChild(new_sprite1)
grey_back.addChild(new_sprite2)
grey_back.addChild(new_sprite3)
When I try to see if positions of new_sprite intersects with NPuzzle sprites, nothing is happening. But if the new_sprite are added to the scene with:
background.addChild(new_sprite0)
background.addChild(new_sprite1)
background.addChild(new_sprite2)
it works. In fact it doesn't work if the sprites have been added with differents parents (background and grey_back). What I don't understand is grey_back is the child of background, so, it should work. Why it doesn't ?
Here is an example of the code to check if sprites intersects eachothers:
switch selectedNode.name {
case "new_sprite0":
if selectedNode.frame.intersects(NPuzzle0.frame) && (selectedNode.angle == 0) {
Thanks !
switch selectedNode.name {
let framePoint = CGPointMake(selectedNode.frame.origin.x, selectedNode.frame.origin.y)
let translatedPoint = selectedNode.parent!.convertPoint(framePoint, toNode:NPuzzle0.parent!)
let translatedFrame = CGRectMake(translatedPoint.x, translatedPoint.y, selectedNode.frame.size.width, selectedNode.frame.size.height)
if translatedFrame.intersects(NPuzzle0.frame) && (selectedNode.zRotation == 0)
{
print("ok")
}
Obviously, because frames are relative to parents, the node MUST be added to the scene. You can see I also used a forced unwrap for parent...
I am trying to implement a graph drawing view in OSX using Cocoa and Quartz framework using NSBezierPath and add/delete data points as I go.
Doing so in drawRect worked fine as the graph was updating frequently but then I encountered performance problem when I need to increase total datapoints/sampling rate.
I decided to move to drawLayer: inContext: but as the function is called at 60fps, the view isn't updating the graph when the function is call and instead update at 1fps.
What am I doing wrong here?
class CustomDrawLayer: CALayer {
convenience init(view: NSView, drawsAsynchronously : Bool = false) {
self.init()
self.bounds = view.bounds
self.anchorPoint = CGPointZero
self.opaque = false
self.frame = view.frame
self.drawsAsynchronously = drawsAsynchronously
// for multiple draws in hosting view
// self.delegate = self
}
override func actionForLayer(layer: CALayer, forKey event: String) -> CAAction? {
return nil
}}
override func drawLayer(layer: CALayer, inContext ctx: CGContext) {
if layer == self.layer {
Swift.print("axes drawing")
graphBounds.origin = self.frame.origin
graphAxes.drawAxesInRect(graphBounds, axeOrigin: plotOrigin, xPointsToShow: CGFloat(totalSecondsToDisplay), yPointsToShow: CGFloat(totalChannelsToDisplay))
}
if layer == self.board {
Swift.print(1/NSDate().timeIntervalSinceDate(fpsTimer))
fpsTimer = NSDate()
drawPointsInGraph(graphAxes, context: ctx)
}
}
func drawPointsInGraph(axes: AxesDrawer, context: CGContext)
{
color.set()
var x : CGFloat = 0
var y : CGFloat = 0
for var channel = 0; channel < Int(totalChannelsToDisplay); channel++ {
path.removeAllPoints()
var visibleIndex = (dirtyRect.origin.x - axes.position.x) / (axes.pointsPerUnit.x / samplingRate)
if visibleIndex < 2 {
visibleIndex = 2
}
for var counter = Int(visibleIndex); counter < dataStream![channel].count; counter++ {
if dataStream![channel][counter] == 0 {
if path.elementCount > 0 {
path.stroke()
}
break
}
let position = axes.position
let ppY = axes.pointsPerUnit.y
let ppX = axes.pointsPerUnit.x
let channelYLocation = CGFloat(channel)
x = position.x + CGFloat(counter-1) * (ppX / samplingRate)
y = ((channelYLocation * ppY) + position.y) + (dataStream![channel][counter-1] * (ppY))
path.moveToPoint(CGPoint(x: align(x), y: align(y)))
x = position.x + CGFloat(counter) * (ppX / samplingRate)
y = ((channelYLocation * ppY) + position.y) + (dataStream![channel][counter] * (ppY) )
path.lineToPoint(CGPoint(x: align(x), y: align(y)))
if x > (axes.position.x + axes.bounds.width) * 0.9 {
graphAxes.forwardStep = 5
dirtyRect = graphBounds
for var c = 0; c < Int(totalChannelsToDisplay); c++ {
for var i = 0; i < Int(samplingRate) * graphAxes.forwardStep; i++
{
dataStream![c][i] = 0
}
}
return
}
}
path.stroke()
}
if inLiveResize {
dirtyRect = graphBounds
} else {
dirtyRect.origin.x = x
dirtyRect.origin.y = bounds.minY
dirtyRect.size.width = 10
dirtyRect.size.height = bounds.height
}
}
It is incredibly rare that you should ever call a function at 60 Hz. In no case should you ever try to call a drawing function at 60 Hz; that never makes sense in Cocoa. If you really mean "at the screen refresh interval," see CADisplayLink, which is specifically built to allow you to draw at the screen refresh interval. This may be slower than 60 Hz. If you try to draw exactly at 60 Hz, you can get out of sync and cause beats in your animation. But this really only intended for things like real-time video. If that what you have, then this is the tool, but it doesn't really sound like it.
It's a bit difficult to understand your code. It's not clear where your 60fps comes in. But I'm assuming what you're trying to do is animate drawing the graph. If so, as Mark F notes, see CAShapeLayer. It has automatic path animations built-in, and is definitely what you want. It automatically handles timings and syncing with the screen refresh and GPU optimizations, and lots of other things that you shouldn't try to work around.
Even if CAShapeLayer isn't what you want, you should be looking at Core Animation, which is designed to work with you to animate values and redraw as necessary. It automatically will handle rendering your layer on multiple cores for instance, which will dramatically improve performance. For more on that, see Animating Custom Layer Properties.
If your path needs to be drawn that frequently, check out CAShapeLayer, where you can just change the path property. That will be hardware accelerated and much faster than drawRect or drawLayer.
I wish I had more reputation so I could attach a screenshot of what I'm working on but you can see an example in the second link down below.
Basically there is a middle button in the tab bar which brings out option buttons (apple images in this case).
These apple icons are animated to their positions using UIDynamicAnimator and UIAttachmentBehavior.
Here is the code which adds the option buttons
func showOptions() {
var numberOfItems = self.delegate.brOptionsButtonNumberOfItems(self)
//NSAssert(numberOfItems > 0 , "number of items should be more than 0")
var angle = 0.0
var radius:Int = 20 * numberOfItems
angle = (180.0 / Double(numberOfItems))
// convert to radians
angle = angle/180.0 * M_PI
for(var i = 0; i<numberOfItems; i++) {
var csCalc = Float((angle * Double(i)) + (angle/2))
var buttonX = Float(radius) * cosf(csCalc)
var buttonY = Float(radius) * sinf(csCalc)
var wut = (angle * Double(i)) + (angle/2)
var brOptionItem = self.createButtonItemAtIndex(i)
var mypoint = self.tabBar.convertPoint(self.center, fromView:self.superview)
var x = mypoint.x + CGFloat(buttonX)
var y = self.frame.origin.y - CGFloat(buttonY)
var buttonPoint = CGPointMake(x, y)
//println("Button Point of Button Item x:\(x) y: \(y)")
brOptionItem.layer.anchorPoint = self.layer.anchorPoint
brOptionItem.center = mypoint
var attachment = UIAttachmentBehavior(item:brOptionItem, attachedToAnchor:buttonPoint)
attachment.damping = self.damping
attachment.frequency = self.frequency
attachment.length = 1
// set the attachment for dragging behavior
brOptionItem.attachment = attachment
self.dynamicItem!.addItem(brOptionItem)
//if(self.delegate.respondsToSelector("willDisplayButtonItem")) { //Fix me
//self.delegate.brOptionsButton(self, willDisplayButtonItem:brOptionItem)
//}
self.tabBar.insertSubview(brOptionItem, belowSubview: self.tabBar)
self.dynamicsAnimator!.addBehavior(attachment)
self.items.addObject(brOptionItem)
}
}
func createButtonItemAtIndex(indexz:NSInteger) -> BROptionsItem {
println("Create button item at index")
var brOptionItem = BROptionsItem(initWithIndex:indexz)
brOptionItem.addTarget(self, action:"buttonItemPressed:", forControlEvents:.TouchUpInside)
brOptionItem.autoresizingMask = UIViewAutoresizing.None
var image = self.delegate.brOptionsButton(self, imageForItemAtIndex:indexz)
//if((image) != nil) {
brOptionItem.setImage(image, forState: UIControlState.Normal)
//}
/*
var buttonTitle = self.delegate.brOptionsButton(self, titleForItemAtIndex:indexz)
if(buttonTitle.utf16Count > 0) {
brOptionItem.setTitle(buttonTitle, forState:UIControlState.Normal)
}
*/
return brOptionItem;
}
In showOptions the animator is assigned an attachment behavior for a brOptionItem that was just created when createButtomItemAtIndex was called.
In createButtonItemAtIndex, the brOptionItem is created and a target was added to execute a function when the button was tapped.
The option buttons work without the animation. I can click them and the target function is executed.
However, when the animation is added and the option buttons are placed where they are, nothing happens when the buttons are tapped.
I am extremely stuck on this. I have no idea why the animation stops the tapping actions. The buttons are supposed to be draggable as well.
Here is my source code. The code I reference can be see in the BROptiosnButton file.
https://github.com/WobbleDev/BROptionsSwift
Here is the reference code I used
https://github.com/BasheerSience/BROptionsButton
Thanks!