Xcode6 Swift. arc4random using array with textures - arrays

I have a spriteNode which has a default texture of a black circle and I have placed it in the center of the screen. I also have an array which contains 4 textures. What I want to do is when I click on the screen the black circle in the center randomly picks a SKTexture from the array and changes into set texture. I was thinking a long the lines of the code in the didBeginTouches but I'm stuck on how to truly execute this idea. Thanks for any help. :)
var array = [SKTexture(imageNamed: "GreenBall"), SKTexture(imageNamed: "RedBall"), SKTexture(imageNamed: "YellowBall"), SKTexture(imageNamed: "BlueBall")]
override func didMoveToView(view: SKView) {
var choiceBallImg = SKTexture(imageNamed: "BlackBall")
choiceBall = SKSpriteNode(texture: choiceBallImg)
choiceBall.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2)
self.addChild(choiceBall)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
choiceBall.texture = SKTexture(imageNamed: arc4random(array))
//error: Cannot assign a value of type 'SKTexture!' to a value of type 'SKTexture?'
}

Almost there, change your touchesBegan to this:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
choiceBall.texture = array[randomIndex]
}
First line generates a random number from 0 to the size of your array-1 using the arc4random_uniform function. We also need to convert the size of your array to an Unsigned Integer because Swift is very strict (and rightfully so). We then cast it back to an Integer and use it to access the textures that you already created in your array.

Related

Getting rotation of ARAnchor from didAdd anchors

So I am using session(_ session: ARSession, didAdd anchors: [ARAnchor] to get all detected planes (I cannot use renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) Because I am using RealityKit instead of ARKit).
So my question is, How can I get the rotation of the detected anchor from this method? I want to create a plane Entity that has exactly the same position, size, orientation & rotation of the detected anchor. So far Im using the extent for the size, center for position but I can't figure out how to get the rotation. Because right now Its detected correctly (Right position and size)but with wrong rotation that the real plane.
You can get the position and orientation of the ARPlaneAnchor from its transform property, then use that to render the bounding plane in RealityKit.
extension float4x4 {
/// Returns the translation components of the matrix
func toTranslation() -> SIMD3<Float> {
return [self[3,0], self[3,1], self[3,2]]
}
/// Returns a quaternion representing the
/// rotation component of the matrix
func toQuaternion() -> simd_quatf {
return simd_quatf(self)
}
}
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
guard let planeAnchor = anchors[0] as? ARPlaneAnchor else {
return
}
// NOTE: When the ARPlaneAnchor is first created its transform
// contains the position and the plane.center == [0,0,0]. On updates
// the plane.center will change as the extents of the plane change.
let position = planeAnchor.transform.toTranslation()
let orientation = planeAnchor.transform.toQuaternion()
// The center is a position before the orientation is taken in to
// account, so we need to rotate it to get the true position before
// we add it to the anchors position
let rotatedCenter = orientation.act(planeAnchor.center)
// You have a ModelEntity that you created earlier
// e.g. modelEntity
// Assuming you added the entity to an anchor that is just
// fixed at 0,0,0 in the world, or you created a custom entity
// with HasAnchor set to 0,0,0
modelEntity.transform.translation = position + rotatedCenter
modelEntity.transform.rotation = orientation
// Doesn't seem to be a way to update meshes in RealityKit so
// just create a new plane mesh for the updated dimensions
modelEntity.model?.mesh = MeshResource.generatePlane(
width: planeAnchor.extent.x,
depth: planeAnchor.extent.z
)
}

Can move only the last object placed in Scene

Hy! I can move only the last virtual object placed in scene. When i add another object, the previous object added remains static and i cannot do any action on it. Can you give me please give me a solution to move, scale, rotate what object i want when I touch it?
Seeing as you haven't posted any code, its hard to provide an exact answer.
However, in order to keep track of the current SCNNode you wish to use, when you load a model etc you could store it as a variable e.g:
var currentNode: SCNNode!
You could also use the name property of the node when you are loading your model to keep a more specific reference to it.
Here is an example of doing this:
/// Example Of Loading An SCNScene & Setting It As The Current Node
func loadModel(){
let modelPath = "Character.scn"
//1. Get The Reference To Our SCNScene & Get The Model Root Node
guard let model = SCNScene(named: modelPath),
let modelObject = model.rootNode.childNode(withName: "Root", recursively: false) else { return }
//2. Scale It
modelObject.scale = SCNVector3(0.4, 0.4, 0.4)
//3. Add It To The Scene & Position It 1.5m Away From The Camera
augmentedRealityView.scene.rootNode.addChildNode(modelObject)
modelObject.position = SCNVector3(0, 0, -1.5)
//4. Set It As The Current Node & Assign A Name
currentNode = modelObject
currentNode.name = "Character"
}
You can also make use of an SCNHitTest which:
Looks for SCNGeometry objects along the ray you specify. For each
intersection between the ray and and a geometry, SceneKit creates a
hit-test result to provide information about both the SCNNode object
containing the geometry and the location of the intersection on the
geometry’s surface.
This can be used to differentiate between which node(s) have been hit e.g:
/// Detects A Tap On An SCNNode
///
/// - Parameter gesture: UITapGestureRecognizer
#objc func detectTap(_ gesture: UITapGestureRecognizer){
//1. Get The Current Touch Point
let currentTouchPoint = gesture.location(in: self.augmentedRealityView)
//2a. Perform An SCNHitTest To Detect If An SCNNode Has Been Touched
guard let nodeHitTest = self.augmentedRealityView.hitTest(currentTouchPoint, options: nil).first else { return }
if nodeHitTest.node == currentNode{
print("The Current Node Has Been Touched")
}
//Or To See Which Node Has Been Touched
if nodeHitTest.node.name == "The Model"{
print("The Model Has Been Touched")
}
}
Another way you can handle this is to add a UITapGestureRecognizer to your view, and enable interaction based on the result e.g. setting the currentNode that way e.g:
/// Detects A Tap On An SCNNode & Sets It As The Current Node
///
/// - Parameter gesture: UITapGestureRecognizer
#objc func detectTap(_ gesture: UITapGestureRecognizer){
//1. Get The Current Touch Point
let currentTouchPoint = gesture.location(in: self.augmentedRealityView)
//2a. Perform An SCNHitTest To Detect If An SCNNode Has Been Touched
guard let nodeHitTest = self.augmentedRealityView.hitTest(currentTouchPoint, options: nil).first else { return }
//2b. Set It As The Current Node
currentNode = nodeHitTest.node
}
Then you can apply logic to ensure that only the currentNode moves etc:
/// Moves An SCNNode
///
/// - Parameter gesture: UIPanGestureRecognizer
#objc func moveNode(_ gesture: UIPanGestureRecognizer) {
//1. Get The Current Touch Point
let currentTouchPoint = gesture.location(in: self.augmentedRealityView)
//2. If The Gesture State Has Begun Perform A Hit Test To Get The SCNNode At The Touch Location
if gesture.state == .began{
//2a. Perform An SCNHitTest To Detect If An SCNNode Has Been Touched
guard let nodeHitTest = self.augmentedRealityView.hitTest(currentTouchPoint, options: nil).first else { return }
//2b. Get The SCNNode Result
if nodeHitTest.node == currentNode{
//3a. If The Gesture State Has Changed Then Perform An ARSCNHitTest To Detect Any Existing Planes
if gesture.state == .changed{
//3b. Get The Next Feature Point Etc
guard let hitTest = self.augmentedRealityView.hitTest(currentTouchPoint, types: .existingPlane).first else { return }
//3c. Convert To World Coordinates
let worldTransform = hitTest.worldTransform
//3d. Set The New Position
let newPosition = SCNVector3(worldTransform.columns.3.x, worldTransform.columns.3.y, worldTransform.columns.3.z)
//3e. Apply To The Node
currentNode.simdPosition = float3(newPosition.x, newPosition.y, newPosition.z)
}
}
}
}
This should be more than enough to point you in the right direction...

How to compare a random UIColor to colors in an array of colors in swift?

I have a table view that every time I create a new row, a different color is randomly created to change it`s background. using this function:
func getRandomColor() -> UIColor{
let red:CGFloat = CGFloat(drand48())
let green:CGFloat = CGFloat(drand48())
let blue:CGFloat = CGFloat(drand48())
return UIColor(red:red, green: green, blue: blue, alpha: 1.0)
}
I store the color in an array, doing this I want to avoid equals colors in my table view.
The array is populated when the table view is created in table view cellForRow, like this:
colors.append(category.color as! UIColor)
category is my object.
The problem is when I close the app and starts it again. The memory continuos and start to randomly create the same colors. So I'm trying to compare colors to keep generating colors until it is a new color. Using this function:
func validateColor(color: UIColor) -> Bool {
let primeiraCor = colors.first! as UIColor
if primeiraCor == color {
return false
} else {
return true
}
}
colors is my array of color, and color the color I want to compare. But every time the function is called it's return true.
What can I do?
it can be done easier:
func validateColor(color: UIColor) -> Bool {
return !colors.contains(color)
}
or if you want to add some threshold for equality, then you could do something like this:
The extension is taken from Extract RGB Values From UIColor
extension UIColor {
var components: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)? {
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
return getRed(&r, green: &g, blue: &b, alpha: &a) ? (r,g,b,a) : nil
}
}
func validateColor(color: UIColor) -> Bool {
let maxDistance: CGFloat = 0.3
guard let colorComponents = color.components else {
return true
}
return !colors.contains { c in
guard let components = c.components else {
return false
}
let distance = abs(colorComponents.red - components.red) + abs(colorComponents.green - components.green) + abs(colorComponents.blue - components.blue)
return distance < maxDistance
}
}
Set the threshold in maxDistance variable or move it to a static member of your class for better readability.
firstly, you'll always get the same colours on each run because drand48() generates the same sequence of numbers unless you seed it. See this answer on how to do that, but here is the code, using the current time for randomise the seed:
let time = UInt32(NSDate().timeIntervalSinceReferenceDate)
srand48(Int(time))
let number = drand48()
Secondly - you are comparing a totally random colour (effectively a number between 0 and nearly 17,000,000) and wondering why it never seems to match the first colour in your colour array? I think you should be amazed if it ever matched :-)

Use SKSpriteNode(s) Multiple Times From An Array

The app I'm working on has multiple nodes which are added repeatedly to the game. What I have been doing is creating new SKSpriteNodes using an array of texture, and once the node has been added, I assign that node with a specific physics body. But what I would like to do is instead of a having an array of textures, have an array of SKSpriteNodes with the physics body pre-assigned.
I believe I have figured out how to do this, the problem I have is when I try to add that SKSpriteNode more then once to the scene.
For example, below is I'm been playing around with, which works, but if I add another line of addChild(arraySegment_Type1[0] as SKSpriteNode), I get an error and the app crashes.
override func didMoveToView(view: SKView) {
anchorPoint = CGPointMake(0.5, 0.5)
var arraySegment_Type1:[SKSpriteNode] = []
var sprite = SKSpriteNode(imageNamed: "myimage")
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(sprite.size.width, sprite.size.height))
sprite.physicsBody?.dynamic = false
sprite.name = "mysprite"
arraySegment_Type1.append(sprite)
addChild(arraySegment_Type1[0] as SKSpriteNode)
}
Does anybody know how I would be able to load SKSpriteNodes multiple times from an array?
What your code does at the moment is the following:
Creates an empty array of the type [SKSpriteNode]
Creates a single SKSpriteNode instance
Ads a physicsBody to the sprite
Ads this single spriteNode to an array
Picks out the first object in the array (index 0) and ads it to the parentNode.
Your app crashes because you are trying to add the same SKSpriteNode instance to the scene several times over.
Here is a reworked version of your code that actually ads several sprites to the scene, it technically works but does not make a lot of sense just yet:
override func didMoveToView(view: SKView) {
// your ARRAY is not an array-segment it is a collection of sprites
var sprites:[SKSpriteNode] = []
let numberOfSprites = 16 // or any other number you'd like
// Creates 16 instances of a sprite an add them to the sprites array
for __ in 0..<numberOfSprites {
var sprite = SKSpriteNode(imageNamed: "myimage")
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.physicsBody?.dynamic = false
sprite.name = "mysprite" // not sure what your plans are here...
sprites.append(sprite)
}
// picks out each sprite in the sprites array
for sprite in sprites {
addChild(sprite)
}
}
Now as I said this doesn't make all that much sense. As you've probably noticed the sprites all appear on top of each other for one thing. More importantly, doing this all in the didMoveToView means it makes very little sense going through the whole array-dance, you could simply do the following instead:
override func didMoveToView(view: SKView) {
let numberOfSprites = 16 // or any other number you'd like
for __ in 0..<numberOfSprites {
var sprite = SKSpriteNode(imageNamed: "myimage")
// skipping the physicsBody stuff for now as it is not part of the question
addChild(sprite)
}
}
An improvement obviously, but all sprites are still covering each other, and didMoveToView might not be the best place to handle all this anyway, so perhaps something like this to illustrate the point:
override func didMoveToView(view: SKView) {
let sprites = spritesCollection(count: 16)
for sprite in sprites {
addChild(sprite)
}
}
private func spritesCollection(#count: Int) -> [SKSpriteNode] {
var sprites = [SKSpriteNode]()
for __ in 0..<count {
var sprite = SKSpriteNode(imageNamed: "myimage")
// skipping the physicsBody stuff for now as it is not part of the question
// giving the sprites a random position
let x = CGFloat(arc4random() % UInt32(size.width))
let y = CGFloat(arc4random() % UInt32(size.height))
sprite.position = CGPointMake(x, y)
sprites.append(sprite)
}
return sprites
}
Now, long-wound as this is it is still just a starting point towards a working and more sensible code-structure...

Remove Array of UIButtons in Swift

I'm looking for a way to remove an array of buttons created in Swift.
My project is a game of anagrams where a word is broken down into its letters, shuffled, and then shown in buttons. That works very well with the code below.
The only thing is that I'd like to remove the buttons before showing a new word, so they don't overlap (makes for bad UI). So my quest is this:
Check if the characterArray that holds the letters is empty, or nil
If nil, then load new word
If a word is already there, then remove the buttons (the word) before adding the new buttons (word)
#IBAction func shuffleString() {
txtFileArray = fileContent!.componentsSeparatedByString("\n")
// 1. Shuffle the plist with the words to form anagrams from
currentQuestion = Int(arc4random_uniform(UInt32(txtFileArray.count)))
anagramString = txtFileArray.objectAtIndex(currentQuestion) as String
var staticY: CGFloat = self.view.bounds.size.height / 9 * 1; // Static X for all buttons.
var staticWidth: CGFloat = 46; // Static Width for all Buttons.
var staticHeight: CGFloat = 46; // Static Height for all buttons.
var staticPadding: CGFloat = 10; // Padding to add between each button.
var characters: String? = anagramString
// 2. Initialize the buttons for the letters
var button = UIButton()
// 3. Add the word to the characterArray
var characterArray = Array(anagramString)
/* HERE I NEED CONDITIONAL STATEMENT AS DESCRIBED IN MY QUESTION: */
/* CHECK IF CHARACTERARRAY IS EMPTY, IF NOT REMOVE THE CURRENT BUTTONS */
/* BEFORE ADDING NEW ONES */
for (index, value) in enumerate(characterArray) {
button.removeFromSuperview() // THE BUTTONS ARE NOT REMOVED
println("Clear Tiles")
println("index: \(index) and value: \(value)")
}
// 4. Add the word from the characterArray to corresponding amount of buttons
for (index, value) in enumerate(characterArray) /* randomIndex */ {
button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button = UIButton(frame:CGRectMake(0, 0, 44, 44))
button.frame.origin.x = (CGFloat(index) * 45.0) + 100.0
button.setTitleColor(UIColor.blackColor(), forState: .Normal)
button.setBackgroundImage(UIImage(named:"Tile.png"), forState: .Normal)
button.setTitle("\(value)", forState: .Normal)
buttonSeriesArray.append(button)
self.view.addSubview(button)
// println("index: \(index) and value: \(value)")
}
println(characterArray)
}
Your code is simply broken. First you create a button and then you remove it from superview, but it is not assigned to any. And you do that in a loop.

Resources