Creating a Random Position for a Sprite in Swift - xcode

I have a circle sprite that I would like to move to a random position within the confines of the iOS device's screen. I am trying to do this in Swift, but I am still I beginner and I don't even know where to start. Does anyone know what to do to create a random position for the sprite? Thank you in advance!
-Vinny

Swift 3:
func spawnRandomPosition() {
let height = self.view!.frame.height
let width = self.view!.frame.width
let randomPosition = CGPoint(x:CGFloat(arc4random()).truncatingRemainder(dividingBy: height),
y: CGFloat(arc4random()).truncatingRemainder(dividingBy: width))
let sprite = SKSpriteNode()
sprite.position = randomPosition
}

Let's assume you are calling your code from the scene class:
func spawnAtRandomPosition() {
let height = self.view!.frame.height
let width = self.view!.frame.width
let randomPosition = CGPointMake(CGFloat(arc4random()) % height, CGFloat(arc4random()) % width)
let sprite = SKSpriteNode()
sprite.position = randomPosition
}

Swift 3:
let randomPosition = CGPoint( x:CGFloat( arc4random_uniform( UInt32( floor( width ) ) ) ),
y:CGFloat( arc4random_uniform( UInt32( floor( height ) ) ) )
)

Related

CAKeyframeAnimation combine animation in scenekit

I'm play around with the CAKeyframeAnimation in order to better understanding how this type of animation work.
I want to move my SCNnode in a square shape and at every corner rotate his eulerangle Y of 90 degrees to make it follow the orientation of the track
here my code
func animatePlaneKey(nodeToAnimate: SCNNode){
// move forward
let pos = nodeToAnimate.position
let animation = CAKeyframeAnimation(keyPath: "position")
let pos1 = SCNVector3(pos.x, pos.y, pos.z)
let pos2 = SCNVector3(pos.x + 1 , pos.y, pos.z)
let pos3 = SCNVector3(pos.x + 1 , pos.y, pos.z + 1) // 1
let pos4 = SCNVector3(pos.x - 1 , pos.y, pos.z + 1)
let pos5 = SCNVector3(pos.x, pos.y, pos.z)
let easeIn = CAMediaTimingFunction(controlPoints: 0.35, 0.0, 1.0, 1.0)
animation.values = [pos1,pos2, pos3,pos4,pos5]
animation.keyTimes = [0,0.25,0.5,0.75,1]
animation.timingFunctions = [easeIn]
animation.calculationMode = .cubic
animation.duration = 12
animation.repeatCount = 1
animation.isAdditive = false
animation.autoreverses = false
// Heading 1st
let firstTurnAnim = CAKeyframeAnimation(keyPath: "eulerAngles.y")
let heading = nodeToAnimate.eulerAngles.y
let rot0heading = heading
let rot2heading = heading - Float(deg2rad(90))
firstTurnAnim.values = [rot0heading,rot2heading]
firstTurnAnim.keyTimes = [0.2,0.3]
firstTurnAnim.duration = 3
firstTurnAnim.repeatCount = 1
firstTurnAnim.isAdditive = true
firstTurnAnim.autoreverses = false
// // Heading 2st
let secondTurnAnim = CAKeyframeAnimation(keyPath: "eulerAngles.y")
let heading2 = nodeToAnimate.eulerAngles.y
let rot1head0 = heading2
let rot1head1 = heading2 - Float(deg2rad(180))
secondTurnAnim.values = [rot1head0,rot1head1]
secondTurnAnim.keyTimes = [0.45,0.55]
secondTurnAnim.duration = 6
secondTurnAnim.repeatCount = 1
secondTurnAnim.isAdditive = true
secondTurnAnim.autoreverses = false
nodeToAnimate.addAnimation(animation, forKey: "movement")
nodeToAnimate.addAnimation(firstTurnAnim, forKey: "turn")
nodeToAnimate.addAnimation(secondTurnAnim, forKey: "turn2")
}
I'm struggling to combine the animation of the y axis at the correct time.
when I add the "turn2" the animation start to mess up everything's, the node appear already rotate in the wrong direction.
For my understanding turn2 animation should start at keyframe 0.45 and finish at 0.55, why it start immediately ?
any idea what should be the correct way to combine this animation?

How to Create fall effect in Xcode ArKit

I m trying to create an effect that dice thrown by hand when I tapped to screen or shake. in my reference code there is rotation effect so that dice faces changes randomly. Im trying to make this code while dice is rotating it must be fall. I mean it will fall from a predefined coordinate y to coordinate y 0. Any idea will be apriciated.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let touchLocation = touch.location(in: sceneView)
let results = sceneView.hitTest(touchLocation, types: .existingPlaneUsingExtent)
if let hitResult = results.first{
let diceScene = SCNScene(named: "art.scnassets/diceCollada.scn")!
if let diceNode = diceScene.rootNode.childNode(withName: "Dice", recursively: false){
diceNode.position = SCNVector3(
hitResult.worldTransform.columns.3.x,
hitResult.worldTransform.columns.3.y + diceNode.boundingSphere.radius,
hitResult.worldTransform.columns.3.z)
let randomX = Float(arc4random_uniform(4) + 1) * (Float.pi/2)
let randomZ = Float(arc4random_uniform(4) + 1) * (Float.pi/2)
diceNode.runAction(
SCNAction.rotateBy(x: CGFloat(randomX * 3),
y: 0,
z: CGFloat(randomZ * 3),
duration: 0.5)
)
sceneView.scene.rootNode.addChildNode(diceNode)
}
print(hitResult)
}
}
}

Getting Pixel Color from an Image using CGPoint in Swift 3

I am try this PixelExtractor class in Swift 3, get a error;
Cannot invoke initializer for type 'UnsafePointer' with an argument list of type '(UnsafeMutableRawPointer?)'
class PixelExtractor: NSObject {
let image: CGImage
let context: CGContextRef?
var width: Int {
get {
return CGImageGetWidth(image)
}
}
var height: Int {
get {
return CGImageGetHeight(image)
}
}
init(img: CGImage) {
image = img
context = PixelExtractor.createBitmapContext(img)
}
class func createBitmapContext(img: CGImage) -> CGContextRef {
// Get image width, height
let pixelsWide = CGImageGetWidth(img)
let pixelsHigh = CGImageGetHeight(img)
let bitmapBytesPerRow = pixelsWide * 4
let bitmapByteCount = bitmapBytesPerRow * Int(pixelsHigh)
// Use the generic RGB color space.
let colorSpace = CGColorSpaceCreateDeviceRGB()
// Allocate memory for image data. This is the destination in memory
// where any drawing to the bitmap context will be rendered.
let bitmapData = malloc(bitmapByteCount)
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue)
let size = CGSizeMake(CGFloat(pixelsWide), CGFloat(pixelsHigh))
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
// create bitmap
let context = CGBitmapContextCreate(bitmapData, pixelsWide, pixelsHigh, 8,
bitmapBytesPerRow, colorSpace, bitmapInfo.rawValue)
// draw the image onto the context
let rect = CGRect(x: 0, y: 0, width: pixelsWide, height: pixelsHigh)
CGContextDrawImage(context, rect, img)
return context!
}
func colorAt(x x: Int, y: Int)->UIColor {
assert(0<=x && x<width)
assert(0<=y && y<height)
let uncastedData = CGBitmapContextGetData(context)
let data = UnsafePointer<UInt8>(uncastedData)
let offset = 4 * (y * width + x)
let alpha: UInt8 = data[offset]
let red: UInt8 = data[offset+1]
let green: UInt8 = data[offset+2]
let blue: UInt8 = data[offset+3]
let color = UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: CGFloat(alpha)/255.0)
return color
}
}
Fix this error.
let data = UnsafePointer<UInt8>(uncastedData)
->
let data = UnsafeRawPointer(uncastedData)
Get other error; 'Type 'UnsafeRawPointer?' has no subscript members'
How to modify this error?
You can write something like this when you have an UnsafeRawPointer in your data:
let alpha = data.load(fromByteOffset: offset, as: UInt8.self)
let red = data.load(fromByteOffset: offset+1, as: UInt8.self)
let green = data.load(fromByteOffset: offset+2, as: UInt8.self)
let blue = data.load(fromByteOffset: offset+3, as: UInt8.self)
Or else, you can get UnsafeMutablePointer<UInt8> from your uncastedData (assuming it's an UnsafeMutableRawPointer):
let data = uncastedData.assumingMemoryBound(to: UInt8.self)
SWIFT 3 (updated March 2017) Xcode 8 / IOS 10
Important: note that return value corresponds to red: b, green:r and blue: r as in the data they are stored backwards
First, create the extension (you can copy&paste somewhere in your
code)
extension UIImage {
func getPixelColor(pos: CGPoint) -> UIColor {
if let pixelData = self.cgImage?.dataProvider?.data {
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let pixelInfo: Int = ((Int(self.size.width) * Int(pos.y)) + Int(pos.x)) * 4
let r = CGFloat(data[pixelInfo+0]) / CGFloat(255.0)
let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
return UIColor(red: b, green: g, blue: r, alpha: a)
} else {
//IF something is wrong I returned WHITE, but change as needed
return UIColor.white
}
}
}
Then just call it as:
let colorAtPixel : UIColor = (theView.image?.getPixelColor(pos: CGPoint(x: 2, y: 2)))!
Although the code returns de exact color, it seems that is not returning the correct one for different CGPoints.
Might it be because of the screen resolution? (x1,x2,x3)?
It would be great if someone can add some light to the mystery...
Swift-3 (IOS 10.3)
extension UIImage {
func getPixelColor(atLocation location: CGPoint, withFrameSize size: CGSize) -> UIColor {
let x: CGFloat = (self.size.width) * location.x / size.width
let y: CGFloat = (self.size.height) * location.y / size.height
let pixelPoint: CGPoint = CGPoint(x: x, y: y)
let pixelData = self.cgImage!.dataProvider!.data
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let pixelIndex: Int = ((Int(self.size.width) * Int(pixelPoint.y)) + Int(pixelPoint.x)) * 4
let r = CGFloat(data[pixelIndex]) / CGFloat(255.0)
let g = CGFloat(data[pixelIndex+1]) / CGFloat(255.0)
let b = CGFloat(data[pixelIndex+2]) / CGFloat(255.0)
let a = CGFloat(data[pixelIndex+3]) / CGFloat(255.0)
return UIColor(red: r, green: g, blue: b, alpha: a)
}
}
Usage : -
let color = yourImageView.image!.getPixelColor(atLocation: location, withFrameSize: yourImageView.frame.size)
location is a CGPoint
and size is size of your imageView
The following section is taken from some Swift 3 code I'm using to sample pixels from an image to get the predominant hue which I use to generate a background for tableView rows. The mechanics for the hue selection process don't apply to your question, so I'm just providing the relevant fragment.
let colorSpace = CGColorSpaceCreateDeviceRGB() // UIExtendedSRGBColorSpace
let newImage = image.cgImage?.copy(colorSpace: colorSpace)
let pixelData = newImage?.dataProvider!.data
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
var hueFrequency = [Int: Double]()
hueFrequency[1] = 1 // Add one entry so this serves as a default if no hues from the image pass the filters
let nStart = 1
let mStart = 1
for n in nStart...Int(image.size.width / samplingFactor) {
for m in mStart...Int(image.size.height / samplingFactor) {
let pixelInfo: Int = ((Int(image.size.width) * m * Int(samplingFactor)) + n * Int(samplingFactor)) * 4 // bytesPerPixel
let b = CGFloat(data[pixelInfo]) / CGFloat(255.0) // cgImage bitmapinfo = rawValue 8194 -> BGRA ordering
let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
let r = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
Also, note that I found the bitmapInfo value (image.cgImage!.bitmapInfo using my parameters) indicated a reordering of the RGBA sequence to BGRA, which I had to deal with in ordering the steps to pick out the data. If your colors are off, you may want to check this.

during Spritenode animation and movement appear red large x

Here is problem in which I add zoombie sprite to the scene every one second. When I add another sub animated zoombie to the zoombie node, sometimes it loads animated texture, and other times appear red large X.
func addMonster() {
let zoombieSprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(40, 60))
// Determine where to spawn the monster along the Y axis
let actualY = randRange(lower: zoombieSprite.size.height, upper: size.height - zoombieSprite.size.height)
// Position the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
zoombieSprite.position = CGPoint(x: size.width + zoombieSprite.size.width/2, y: actualY)
zoombieSprite.physicsBody = SKPhysicsBody(rectangleOfSize: zoombieSprite.size) // 1
zoombieSprite.physicsBody?.dynamic = true // 2
zoombieSprite.physicsBody?.categoryBitMask = PhysicsCategory.Monster // 3
zoombieSprite.physicsBody?.contactTestBitMask = PhysicsCategory.Projectile // 4
zoombieSprite.physicsBody?.collisionBitMask = PhysicsCategory.None // 5
addChild(zoombieSprite)
//zoombieSprite.addChild(createAnimatedZoombie())
let zoombieAnimation = SKAction.runBlock({
zoombieSprite.addChild(self.createAnimatedZoombie())
})
// Determine speed of the monster
let actualDuration = randRange(lower: 6.0, upper: 10.0)
//print("actualDuration = \(actualDuration)")
let actionMove = SKAction.moveTo(CGPoint(x: -zoombieSprite.size.width/2, y: actualY), duration: NSTimeInterval(actualDuration))
// Create the actions
let actionMoveDone = SKAction.removeFromParent()
zoombieSprite.runAction(SKAction.sequence([zoombieAnimation ,actionMove,actionMoveDone]))
}
//MARK: - ANIMATE FRAME AND MOVE ZOOMBIE
func createAnimatedZoombie () -> SKSpriteNode {
let animatedZoobieNode = SKSpriteNode(texture: spriteArray[0])
let animationFrameAction = SKAction.animateWithTextures(spriteArray, timePerFrame: 0.2)
let durationTime = SKAction.waitForDuration(0.1)
let repeatAction = SKAction.repeatActionForever(animationFrameAction)
let quenceAction = SKAction.sequence([durationTime, repeatAction])
animatedZoobieNode.runAction(quenceAction)
return animatedZoobieNode
}
Thanks very much my respectable brother Joseph Lord and Thank God i solved my problem by just dividing sprite kit atlas array count property by 2 because in this folder i had put both #2x and #3x images so when i used to get number of images from this atlas folder it used to return the number which was addition of #2x and #3x images.

Repeating a Background Image in Swift

Im using swift programming language in xcode and in spritekit. I want to get my background image to scroll forever. I have this code so far but when I run it in the simulator it goes through only one time and goes back to the grey background. How do I get it so that the background repeats forever. I already got the background to move its just that I want it to repeat forever.
class GameScene: SKScene, SKPhysicsContactDelegate {
var background = SKSpriteNode()
// Background
background = SKSpriteNode(imageNamed: "background")
background.position = CGPointMake(self.size.width/2, self.size.height/2)
addChild(background)
// Repeat Background Image Forever
moveForeverAction()
}
//loop background image infinitely
func moveForeverAction() {
let moveNode = SKAction.moveByX(0, y: background.size.height * 2.0 , duration: NSTimeInterval(0.01 * background.size.height * 2.0))
let resetPosition = SKAction.moveByX(0, y: background.size.height * 2.0, duration: 0)
let moveNodeForever = SKAction.repeatActionForever(SKAction.sequence([moveNode, resetPosition]))
background.runAction(moveNodeForever)
}
It's because you're adding the same background.
add another declaration of bacground in the loop :
for var i:CGFloat=0; i < 3; ++i {
**background = SKSpriteNode(imageNamed: "background")**
background.position = CGPointMake(background.size.width / 2, i * background.size.height )
background.runAction(moveNodeForever)
}
You've to runAction() 3 times. For example :
for var i:CGFloat=0; i < 3; ++i {
background.position = CGPointMake(background.size.width / 2, i * background.size.height )
background.runAction(moveNodeForever)
}

Resources