I have a global array called cannonArray that I initialized before any function. I have a touchesBegan function that appends values to the array. When I print the array from within the touchesBegan function, it prints perfectly. However, when I print the array from the didMoveToViewFunction, nothing updates and it simply prints [] (and doesn't change as it should because it is global). I also have another function called createEnemies that is on a timer and it has the same problem (prints proper appended array within the function, but not in another one). Thanks! :)
var cannonArray = [SKSpriteNode]()
var enemyArray = [SKSpriteNode]()
override func didMove(to view: SKView) {
enemyCreationTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(createEnemies), userInfo: nil, repeats: true)
print(enemyArray) //prints empty array
print(cannonArray) // prints empty array
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
cannon = SKSpriteNode(imageNamed: "cannon")
cannon.size = CGSize(width: 50, height: 50)
cannon.position = location
if frameArray[0].contains(location) || frameArray[1].contains(location) {
print("cant")
}else {
self.addChild(cannon)
self.cannonArray.append(cannon)
//if i print here it works
}
}
#objc func createEnemies() {
enemiesSpawned += 1
if enemiesSpawned > enemyNumber {
enemyCreationTimer.invalidate()
}else {
enemy = SKSpriteNode(imageNamed: "enemy")
enemy.position = CGPoint(x: spriteOne.position.x, y: CGFloat(650))
enemy.size = CGSize(width: 25, height: 25)
self.addChild(enemy)
enemy.zPosition = 0
enemy.physicsBody = SKPhysicsBody(circleOfRadius: 12.5)
enemy.physicsBody?.affectedByGravity = false
enemy.run(moveEnemies())
enemyArray.append(enemy)
//if i print here it works
}
}
Related
I want to use my swift code to place a imageview everytime the func moveRight is called. I want each new imageView to be separated by a 50 on the y axis. Right now my code compiles but nothing is changing when the function is called. But in the debug area I am seeing the count increasing.
import UIKit
class ViewController: UIViewController {
var myArray = [UIImageView]()
var bt = UIButton()
var count : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(bt)
bt.backgroundColor = UIColor.systemOrange
bt.frame = CGRect(x: view.center.x - 0, y: view.center.y , width: 50, height: 50)
bt.addTarget(self, action: #selector(moveRight), for: .touchUpInside)
}
#objc func moveRight() {
print("Yes")
myArray.forEach({
$0.backgroundColor = UIColor.systemTeal
self.view.addSubview($0)
})
var ht = 50
myArray.insert(UIImageView(), at: count)
print("Your Count is ", count)
myArray[count].frame = CGRect(x: view.center.x - 0, y: view.center.y + CGFloat(ht), width: 50, height: 50)
count += 1
ht += 50
}
}
You need to move the ht variable outside the moveRight() method.
Currently every time you run the method you recreate the variable and set it's initial value to 50, and then use that to set your location.
You need to do
class ViewController: UIViewController {
var myArray = [UIImageView]()
var bt = UIButton()
var count : Int = 0
var ht = 50 //initialise it here, then update it in the moveRight() method
//etc
and then remove the equivalent line from the method.
I would like to know how I can loop through an array and change a name after recognizer.state .ended. When I use forEach method it shows me only the last item from the array.
Array:
let persons = [
Name(name: "Theo", imageName: "theoPhoto"),
Name(name: "Matt", imageName: "mattPhoto"),
Name(name: "Tom", imageName: "tomPhoto")
]
UIPanGestureRecognizer:
#IBAction func handleCard(recognizer: UIPanGestureRecognizer) {
guard let card = recognizer.view else { return }
switch recognizer.state {
case .began:
initialCGRect = card.frame
case .changed:
handleChanged(recognizer, card)
case .ended:
handleEnded(recognizer, card)
default:
return
}
}
fileprivate func handleChanged(_ recognizer: UIPanGestureRecognizer, _ card: UIView) {
let panning = recognizer.translation(in: view)
let degrees : CGFloat = panning.x / 20
let angle = degrees * .pi / 180
let rotationTransformation = CGAffineTransform(rotationAngle: angle)
card.transform = rotationTransformation.translatedBy(x: panning.x, y: panning.y)
}
fileprivate func handleEnded(_ recognizer: UIPanGestureRecognizer, _ card: UIView) {
let transitionDirection: CGFloat = recognizer.translation(in: view).x > 0 ? 1 : -1
let shuldDismissCard = abs(recognizer.translation(in: view).x) > treshold
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
if shuldDismissCard {
card.frame = CGRect(x: 600 * transitionDirection, y: 0, width: card.frame.width, height: card.frame.height)
} else {
card.transform = CGAffineTransform.identity
}
}) { (_) in
// Complete animation, bringing card back
card.transform = CGAffineTransform.identity
}
}
forEach method:
func setupCards() {
persons.forEach { (Person) in
cardView.image = UIImage(named: Person.imageName)
cardLabel.text = Person.name
}
}
If I put it on complete animation it's looping through an array and shows me the last item.
Your setupCards() method is wrong. It will always display the last item because you're updating only a single cardView. Also don't use the Person class name in the loop closure. You should hold a CardView array, and change the data accordingly.
func setupCards() {
var i = 0
persons.forEach { (p) in
let cardView = self.cards[i]
cardView.image = UIImage(named: p.imageName)
cardView.cardLabel.text = p.name
i += 1
}
}
I am just learning Swift and I am following along with another project that I had worked on. However, I am getting this error:
Error I am getting:
Thread 1: Fatal error: Index out of range
All my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var scoreLabel : SKLabelNode?
var player : SKSpriteNode?
var track : Int = 0
var trackArray : [SKSpriteNode]? = [SKSpriteNode]()
var ballDirection = Int(arc4random_uniform(2))
var velocityArray = [Int]()
var playerVelocity : Int = GKRandomSource.sharedRandom().nextInt(upperBound: 100)
var currentScore : Int = 0 {
didSet {
self.scoreLabel?.text = "SCORE: \(self.currentScore)"
}
}
func createHUD() {
scoreLabel = self.childNode(withName: "score") as? SKLabelNode
currentScore = 0
}
//I think the problem is coming from this function, however I more-or-less copied it from a tutorial project that works so I don't know what the problem is.
func setupTracks() {
for i in {
if let track = self.childNode(withName: "\(i)") as? SKSpriteNode {
trackArray?.append(track)
print(track)
}
}
}
func createBall() {
player = SKSpriteNode(imageNamed: "ball1")
player?.physicsBody = SKPhysicsBody(circleOfRadius: player!.size.width / 2)
player?.physicsBody?.linearDamping = 0
//This line throws the error
let ballPosition = trackArray?[track].position
print(track)
player?.position = CGPoint(x: (ballPosition?.x)!, y: (ballPosition?.y)!)
player?.position.x = (ballPosition!.x)
player?.position.y = (ballPosition!.y)
self.addChild(player!)
}
override func didMove(to view: SKView) {
createHUD()
createBall()
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
I have 8 Color Sprite objects that I put in through the GameScene.sks each named 0 - 9,
I think it is looking for a value in the array that has not been yet assigned? but I have a part that (I think) is assigning that value. It also works in the other project so I am very lost.
Some of this doesn't make 100% sense to me without context, but based on what I can infer it seems a better solution for you would be to use a single dictionary to access the SKSpriteNode as well as the direction and velocity. To achieve this you could create a custom object, or just use a simple tuple.
After you initialize the dictionary and fill it with the objects you can access a single value in the dictionary to get all of the data you need for your createEnemy function. It might look something like this:
var tracks = [Int : (node: SKSpriteNode, direction: CGFloat, velocity: CGFloat)]()
func setupTrack() {
for i in 0 ... 9 {
if let track = self.childNode(withName: "\(i)") as? SKSpriteNode {
//Get direction and velocity here, if initial value is known
//Setting to 0 for now
tracks[i] = (node: track, direction: 0, velocity: 0)
}
}
}
func createEnemy(forTrack track: Int) -> SKShapeNode? {
guard let thisTrack = tracks[track] else { return nil }
let enemySprite = SKShapeNode()
enemySprite.name = "ENEMY"
enemySprite.path = CGPath(roundedRect: CGRect(x: 0, y: -10, width: 20, height: 20), cornerWidth: 10, cornerHeight: 10, transform: nil)
let enemyPosition = thisTrack.node.position
let left = thisTrack.direction
enemySprite.position.x = left ? -50 : self.size.height + 50
enemySprite.position.y = enemyPosition.y
enemySprite.physicsBody?.velocity = left ? CGVector(dx: thisTrack.velocity, dy: 0) : CGVector(dx: 0, dy: -thisTrack.velocity)
return enemySprite
}
This will prevent you from going out of bounds on your array. Note on this: Since you are looping through a hardcoded 0...9 range, but you are optionally appending items to that Array (in the case where your if let track fails) you have no guarantee that the "track" item with the name "7" corresponds to the 7th index in your array. This is another reason a Dictionary is safer. Additionally, it seems you are maintaining 3 separate arrays (tracks, directionArray, and velocityArray), which seems prone to error for the same reason I just described.
Hello I am trying to take an array I have and update a label to display each element in the array one second apart. An example would be me having the array [3,6,2] and my label would show 3 then wait a second and show 6 then wait a second and show 2. I've seen many NSTimer examples with update functions doing things like this but only with an incrementation on a number, never trying to parse an array. Can anyone help?
Update
I am calling my timer in a UIButton and am running into a problem. The timer works fine but my code in the button function under my timer runs before the timer and update function. My code below generates a random array of numbers then should display them one second apart. It is doing this correct but my print statement under my timer is running before it updates and displays the numbers in the textbook. I do not know why? Is the timer running on a different thread?
func updateCountdown() {
if(numbersIndex <= numberLimit){
self.counter.text = String(numbers[numbersIndex])
}else if(numbersIndex == numberLimit+1){
self.counter.text = ""
}else{
timer!.invalidate()
timer=nil
}
numbersIndex+=1
}
#IBAction func startButton(_ sender: AnyObject){
startB.setTitle("", for: .normal)
var highestLength = 3
//for trialNumber in 0...11{
var numberSequence = Array(repeating:11, count: highestLength)
for count in 0...highestLength-1{
var randomNumber = Int(arc4random_uniform(10))
if(count>0){
while(numberSequence.contains(randomNumber) || randomNumber-1 == numberSequence[count-1]){
randomNumber = Int(arc4random_uniform(10))
}
}
numberSequence[count] = randomNumber
}
print(numberSequence)
self.numbers = numberSequence
self.numbersIndex = 0
self.numberLimit = numberSequence.count-1
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCountdown), userInfo: nil, repeats: true)
print("this should be last")
//do other stuff too
//}
}
you can do something like this.
var counter = 0
var list: [String] = ["Eggs", "Milk"]
override func viewDidLoad() {
super.viewDidLoad()
var timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
}
func update() {
label.text = "\(list[counter])"
counter += 1
if (counter >= list.count) {
counter = 0
}
}
this is assumming the name of you Label is label
I am trying to create a grid-based map made out of 20*20 tiles, but I'm having trouble making the tiles communicate and access properties of adjacent ones.
This is the code used trying to accomplish this:
var tileArray:Array = []
class Tile:SKSpriteNode
{
var upperAdjacentTile:Tile?
override init()
{
let texture:SKTexture = SKTexture(imageNamed: "TempTile")
let size:CGSize = CGSizeMake(CGFloat(20), CGFloat(20))
super.init(texture: texture, color: nil, size: size)
}
required init(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
func setAdjacentTile(tile:Tile)
{
upperAdjacentTile = tile
}
}
for i in 0..<10
{
for j in 0..<10
{
let tile:Tile = Tile()
tile.position = CGPointMake(CGFloat(i) * 20, CGFloat(j) * 20 )
tile.name = String(i) + String(j)
//checks if there is a Tile above it (aka previous item in array)
if(i+j != 0)
{
//The position of this tile in array minus one
tile.setAdjacentTile(tileArray[(j + (i * 10)) - 1] as Tile)
}
tileArray.append(tile)
}
}
println(tileArray[10].upperAdjacentTile) //Returns the previous item in tileArray
println(tileArray[9].position) //Returns the position values of same item as above
println(tileArray[10].upperAdjacentTile.position) //However, this doesn't work
Why can't i access/change the properties of the tile referenced in "upperAdjacentTile"?
upperAdjacentTile is an Optional you have to unwrap first (since it can be nil).
// will crash if upperAdjacentTile is nil
println(tileArray[10].upperAdjacentTile!.position)
or
if let tile = tileArray[10].upperAdjacentTile {
// will only run if upperAdjacentTile is not nil
println(tile.position)
}
Edit
As rintaro suggested you also have to specify the type of the objects the array contains:
var tileArray = [Tile]()