Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
191 views
in Technique[技术] by (71.8m points)

ios - Swift Array Append Overwriting Other Array Values

I'm new to Swift, but I'm not new to coding. I thought I would try my hand at making a little game. It's something I like to do when learning.

Here's what I'm doing. I start of by calling a method that initialized my "object templates"

internal func initializeObjectTemplates(){
    objectTemplates.append(GameObject(passed_x: 0,
        passed_y: 0,
        passed_max: 25,
        passed_min: 25,
        passed_points: 100,
        passed_img: "BeerGlass1",
        passedLengthOfTimeOnScreen: 5,
        passedBottomChance: 1,
        passedTopChance: 50,
        passedId: 0))
    objectTemplates.append(GameObject(passed_x: 0,
        passed_y: 0,
        passed_max: 25,
        passed_min: 25,
        passed_points: 300,
        passed_img: "BeerGlass2",
        passedLengthOfTimeOnScreen: 2,
        passedBottomChance: 51,
        passedTopChance: 100,
        passedId: 0))

}

I have a timer that runs a func named "update" every second. The "update" func randomly selects one of the two templates, adds some other information to the template object, does some other logic, and appends the object to an array I have. Everything up to the append seems to be working. I have added various breakpoints and the GameObject object seems to be getting populated correctly.

When I append the object it is overwriting other items in the objects array. I can't figure it out. I've googled it as much as possible, and can't really seem to find anything that fits my issue. Here's the rest of my code related to this.

This is the top of the ViewController and the viewDidLoad

internal var step = 0
internal var objects = [GameObject]()

@IBOutlet weak var points: UILabel!

internal var objectTemplates = [GameObject]()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    view.backgroundColor = UIColor.blackColor()

    initializeObjectTemplates()

    let timer = NSTimer(timeInterval: 1, target: self, selector: #selector(ViewController.update), userInfo: nil, repeats: true)
    NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
    //var i = 1
    //while i <= 100{
    //    update()
    //    i += 1
    //}
}

Update Func - the commented out/hard coded append works, but obviously not what I want

internal func update(){
    step += 1

    //removeExpiredButtons()

    objects.append(populateObjectTemplate(selectObjectTemplate(), step: step))

    //objects.append(GameObject(passed_x: 0,
    //    passed_y: 0,
    //    passed_max: 25,
    //    passed_min: 25,
    //    passed_points: 300,
    //    passed_img: "BeerGlass2",
    //    passedLengthOfTimeOnScreen: 2,
    //    passedBottomChance: 51,
    //    passedTopChance: 100,
    //    passedId: step))

    print("There are (objects.count) items and the first item has an id of (objects[0].id)")
}

These are the methods that update calls

selectObjectTemplate

func selectObjectTemplate() -> GameObject {
    let rand = Random.within(1...100)
    return objectTemplates.filter(){
        let isAbove = $0.bottomChance <= rand
        let isBelow = $0.topChance >= rand

        return isAbove && isBelow
        }[0]
}

populateObjectTemplate

func populateObjectTemplate(obj: GameObject, step: Int) -> GameObject {
    let widthBoundary = Float(self.view.frame.width - 20)
    let heightBoundary = Float(self.view.frame.height - 20)

    let placex = CGFloat(Random.within(20.0...widthBoundary))
    obj.x = placex

    let placey = CGFloat(Random.within(50.0...heightBoundary))
    obj.y = placey

    obj.expiration = obj.lengthOfTimeOnScreen + step
    obj.id = step

    obj.button = populateObjectButton(obj)

    return obj
}

populateObjectButton

func populateObjectButton(obj: GameObject) -> UIButton {
    let button = UIButton(type: UIButtonType.Custom)
    let image = UIImage(named: obj.img)

    button.setBackgroundImage(image, forState: UIControlState.Normal)
    button.frame = CGRectMake(obj.x, obj.y, obj.minSize, obj.minSize)
    button.layer.cornerRadius = 0.5 * button.bounds.size.width
    button.layer.masksToBounds = true
    button.addTarget(self, action: #selector(ViewController.objectTapped(_:)), forControlEvents: .TouchUpInside)
    button.tag = obj.id

    self.view.addSubview(button)

    return button
}

Sorry for such a lengthy post. I just wanted to include as much information as possible. I don't usually post to places like this, because I try as hard as possible to find the solution myself. The swift file is also on Git.

https://github.com/JoeBrewing/TapGlassMasterChallenge/blob/master/Glass%20Tap%20Master%20Challenge/ViewController.swift

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Your objectTemplates array stores references to two GameObject instances that were created in initializeObjectTemplates. When your timer fires you select one of these 'template' objects, modify it and add it to the array. However, the array is merely storing references to the objects you add to it, and you only have two objects, even though you are adding those objects to the array multiple times.

When you call populateObjectTemplate you modify one of the two object templates, which means that all other entries in the array that refer to that particular template will reflect those modifications. You need populateObjectTemplate to return a new GameObject, using the information in the template;

func populateObjectTemplate(obj: GameObject, step: Int) -> GameObject {

    let newGameObject=GameObject(passed_x: 0,
    passed_y: obj.passed_y,
    passed_max: obj.passed_max,
    passed_min: obj.passed_min,
    passed_points: obj.passed_points,
    passed_img: obj.passed_img,
    passedLengthOfTimeOnScreen: obj.passedLengthOfTimeOnScreen,
    passedBottomChance: obj.passedBottomChance,
    passedTopChance: obj.passedTopChance,
    passedId: obj.passedId)

    let widthBoundary = Float(self.view.frame.width - 20)
    let heightBoundary = Float(self.view.frame.height - 20)

    let placex = CGFloat(Random.within(20.0...widthBoundary))
    newGameObject.x = placex

    let placey = CGFloat(Random.within(50.0...heightBoundary))
    newGameObject.y = placey

    newGameObject.expiration = obj.lengthOfTimeOnScreen + step
    newGameObject.id = step

    newGameObject.button = populateObjectButton(obj)

    return newGameObject
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...