windigo
windigo

Reputation: 35

Updating a variable in AS3

I am following an avoider game tutorial and can't seem to get a value to update in all parts of my class.

package  
{
import flash.display.Sprite;    
import flash.text.TextField
import flash.text.TextFormat


public class Score extends Sprite
{
    public var scoreDisplay:String;
    public var currentValue:int;

    public function Score() 
    {
        updateDisplay()
    }

    public function updateDisplay():void
    {

        scoreDisplay = currentValue.toString();

        var format:TextFormat = new TextFormat();
            format.size = 25;
            format.font = "Verdana"

        var myText:TextField = new TextField();
            myText.defaultTextFormat = format;
            myText.text = scoreDisplay;
            myText.background = true;
            myText.autoSize
            myText.width = 50;
            myText.height = 35;

        addChild(myText)
            myText.x = 340
            myText.y = 10
            myText.mouseEnabled = false
    }

    public function addToValue( amountToAdd:Number ):void
    {
        currentValue = currentValue + amountToAdd;
    }
}
}

Whenever I run a trace for 'currentValue' inside of 'addToValue' it is updating by increments of 10 but a trace for 'currentValue' inside of 'updateDisplay' always has a value of 0. I absolutely can not figure out why the one 'currentValue' is not updating the other.

EDIT 1:

package  
{
import flash.display.Sprite;

public class Start extends Sprite
{
    public var game:Game;
    public var gameover:GameOver;
    public var background:Sprite;


    public function Start() 
    {
        background = new Sprite
        background.graphics.beginFill(0xC0C0C0)
        background.graphics.drawRect(0, 0, 400, 300)
        background.graphics.endFill();
        addChild(background)


        var score:Score = new Score()
        addChild(score)

        game = new Game();
        game.addEventListener(AvatarEvent.DEAD, startOver);
        addChild(game);
    }

    public function startOver(avatarEvent:AvatarEvent):void
    {
        game.removeEventListener(AvatarEvent.DEAD, startOver);
        gameover = new GameOver();
        gameover.addEventListener(NavigationEvent.RESTART, requestRestart);
        removeChild(game)
        addChild(gameover);
    }

    private function requestRestart(e:NavigationEvent):void 
    {
        restartGame();
    }

    public function restartGame():void
    {
        gameover.removeEventListener(NavigationEvent.RESTART, requestRestart);
        game = new Game();
        game.addEventListener(AvatarEvent.DEAD, startOver);
        addChild(game);

        removeChild(gameover);
    }
}

}

EDIT 2

package  
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.utils.Timer;

import Score;

public class Game extends Sprite
{
    public var army:Array;
    public var gameTimer:Timer;
    public var player:Player;
    public var background:Sprite;
    private var instanceScore:Score

    public function Game() 
    {           
        army = new Array();
        var newenemy:Enemy = new Enemy(100, -15);
        army.push(newenemy);
        addChild(newenemy);

        player = new Player();
        addChild(player);
        player.x = mouseX;
        player.y = mouseY;

        gameTimer = new Timer(20);
        gameTimer.addEventListener(TimerEvent.TIMER, move);
        gameTimer.addEventListener(TimerEvent.TIMER, onTick);
        gameTimer.start();

        instanceScore = new Score();
    }

    private function move(timerEvent:TimerEvent):void
    {

        player.x = mouseX;
        player.y = mouseY;

        for each (var enemy:Enemy in army)
        {
            enemy.moveDownABit();

            if (player.hitTestObject(enemy))
            {
                gameTimer.stop();

                dispatchEvent( new AvatarEvent( AvatarEvent.DEAD));

            }
        }
    }

    public function onTick(e:Event):void
    {
        if (Math.random() < .1)
        {
            var randomX:Number = Math.random() * 400;
            var newEnemy:Enemy = new Enemy(randomX, -15);
            army.push(newEnemy);
            addChild(newEnemy);

            instanceScore.addToValue(10)

            var score:Score = new Score()
            addChild(score)

        }
    }


}

}

Upvotes: 0

Views: 837

Answers (1)

Florian Salihovic
Florian Salihovic

Reputation: 3951

In addToValue call updateDisplay, but also extract the TextFields to instance variable and add them in an init method. Values have to be set explicitly. You are expecting an implicit dataflow ... which is not given in ActionScript 3. The other way would be Events ... but that's just using methods as well.

At first, I'd create a dedicated event for notifications. This comes in handy as soon as you are more familiar with architectural ideas in ActionScript. This could be off course generalised, but for demonstration purposes of accessors (get score) and mutators (set score) perhaps a good idea.

package {

  import flash.events.Event;

  public class ScoreChangeEvent extends Event {

    public static const SCORE_CHANGED:String = 'scoreChanged';

    private var _score:int;

    public function get score():int {
      return _score;
    }

    public function ScoreChangeEvent(score:int) {
      super(SCORE_CHANGED, true, false);
      this._score = score;
    }

    override public function clone():Event {
      return new ScoreChangeEvent(_score);
    }
  }
}

Then I refactored some portions of the code, making accessible what should be accessible, extensible what should be extensible and inaccessible, what should be not accessible by the outside world.

package {

  import flash.display.Sprite;
  import flash.text.TextField;
  import flash.text.TextFormat;

  [Event(name="scoreChanged", type="ScoreChangeEvent")]

  public class ScoreDisplay extends Sprite {

    /** The text field displaying the score. */
    protected var _scoreTextField:TextField;

    /** The internal storage variable for score to display. */
    private var _score:int;

    /**
     * The score to be displayed. When the score changes,
     * a ScoreChangeEvent will be dispatched.
     *
     * @see ScoreChangeEvent#SCORE_CHANGED
     */
    public function get score():int {
      return _score;
    }

    public function set score(value:int):void {
      if (_score == value) {
        return;
      }
      _score = value;
      updateDisplayList();
      this.dispatchEvent(new ScoreChangeEvent(_score));
    }

    public function ScoreDisplay() {
      init();
    }

    /** Creation of children and initial rendering. */
    private function init():void {
      this.createChildren();
      this.updateDisplayList();
    }

    /**
     * DisplayObjects, which are one created once should be
     * created in a dedicated method.
     */
    protected function createChildren():void {

      if (!_scoreTextField) {
        const format:TextFormat = new TextFormat();
        format.size = 25;
        format.font = "Verdana";

        _scoreTextField = new TextField();
        _scoreTextField.defaultTextFormat = format;
        _scoreTextField.background = true;
        _scoreTextField.autoSize;
        _scoreTextField.width = 50;
        _scoreTextField.height = 35;
        _scoreTextField.x = 340;
        _scoreTextField.y = 10;
        _scoreTextField.mouseEnabled = false;

        addChild(_scoreTextField);
      }
    }

    /** Reset the display. */
    public function reset():void {
      this.score = 0;
    }

    /**
     * Adds a certain value to the current score.
     * @param value The calue to be added.
     */
    public function addToScore(value:int):void {
      score += value;
    }

    /**
     * Update the display when ever needed.
     * 
     * This method should never be triggered by an
     * different class, only be the instance itself
     * when a value changed.
     */
    protected function updateDisplayList() {
      this._scoreTextField.text = this.score.toString();
    }
  }
}
  • Access to the score is only possible by the get/set methods, this makes it easy to understand when a value changed by logging or the debugger.
  • _scoreTextField is protected, which means it may be modified by extending sub classes.
  • addToScore will modify the score property which in return updates the display.
  • updateDisplayListwill only be invoked when score changed. This makes it easy to understand what's going on.

My code is a cheap copy of the Flex component life cycle only to illustrate that there are certain patterns which may become pretty handy in 98% of all user interface related programming in Flash.

On the contrary, your classcould also simply be refactored to

package {

  import flash.display.Sprite;
  import flash.text.TextField;
  import flash.text.TextFormat;

  public class ScoreDisplay extends Sprite {

    protected var scoreTextField:TextField;

    private var _score:int;

    public function get score():int {
      return _score;
    }

    public function set score(value:int):void {
      if (_score == value) {
        return;
      }
      _score = value;
      scoreTextField.text = _score.toString();
    }

    public function ScoreDisplay() {
      const format:TextFormat = new TextFormat();
      format.size = 25;
      format.font = "Verdana";

      scoreTextField = new TextField();
      scoreTextField.defaultTextFormat = format;
      scoreTextField.background = true;
      scoreTextField.autoSize;
      scoreTextField.width = 50;
      scoreTextField.height = 35;
      scoreTextField.x = 340;
      scoreTextField.y = 10;
      scoreTextField.mouseEnabled = false;

      addChild(scoreTextField);
    }

    public function addToScore(value:int):void {
      score += value;
    }
  }
}

But that code is not really extensible and would require more change when the game gets more complex and what's more important, will require you to think a lot more when extending the component.

I hope that helped a bit.

Upvotes: 1

Related Questions