Skip to main content

Scores in detail

Posted by malenkov on October 7, 2009 at 2:11 AM PDT

Can you live without computer of Internet for two weeks? I definitely got crazy and forgot everything I knew about JavaFX. Nevertheless, in this blog I'll try to explain how the Score class introduced in my previous post works.

For a start, consider the Digit class, an internal auxiliary class. It is used to scroll one number position. Observe how many images do we use for this class. In order to gradually change the figures from 9 to 0 and vice versa, we added an extra image, the same as the first one. If that's the case, we need to scroll the upper position as well.

The Digit class is initialized by two variables:

  var owner: Score;
  var digit: Digit;

The owner variable is always defined and it specifies the instance of the Score class that uses the current instance of the Digit class. The digit variable is used to define the previous number position. Its value for the lowest position is null.

  def score: Integer = bind if (digit == null)
    then owner.score.intValue()
    else digit.score / owner.radix
    on replace {
      if ((score != 0) and (this == owner.digit)) {
        owner.digit = Digit {
          owner: owner
          digit: this
        }
      }
    }

The score variable contains the number, which lower position is used for rendering. For the lowest position it contains the integer part of the score variable of the Score class. For every other higher position it contains the result of dividing the score value of the previous position by the radix. If the current instance of the Digit class is the last registered, and the score value increases, then one more instance of the Digit class is created.

  def value: Integer = bind score mod owner.radix;

The value variable contains the digit of the particular radix.

  def delta: Number = bind if (digit == null) 
    then owner.score - score
    else if (owner.radix == (digit.value + 1))
      then digit.delta
      else 0;

The delta variable contains a fractional part of the score variable only if the current position of the number is the lowest or if the previous position changes its value from 9 to 0 or from 0 to 9.

  override var translateX = bind if (digit == null)
    then -owner.width
    else -owner.width + digit.translateX;

Override the translateX variable to align number positions in that way, so the higher positions were located left.

  override function create() {
    Group {
      clip: Rectangle {
        width:  bind owner.width
        height: bind owner.height
      }
      content: for (i in [0..owner.radix]) {
        ...
      }
    }
  }

The create function defines a group of images with limited visibility. To better understand the logic of this function, comment the clip variable initializing before starting.

        Group {
          content: [
            ...
          ]
          translateY: bind owner.height * (i - value - delta)
          visible: bind (i != 0) or (score != 0) or (digit == null)
        }

An offset is calculated for each image. Besides, all the highest positions that have the zero value are invisible.

            Group {
              def group = Group {
                content: node
                scaleX: bind owner.width  / node.boundsInParent.width
                scaleY: bind owner.height / node.boundsInParent.height
              }
              content: group
              translateX: bind -group.boundsInParent.minX
              translateY: bind -group.boundsInParent.minY
            }

The code above is used to normalize the location of the created node. It is especially helpful when applied to the Text nodes. First, we create a group, then we scale it to the specific size, and after that, the group is aligned to the left and upper borders.

Now consider the Score class. It is far more simple. I would like to describe some of its variables.

  public var value: Integer on replace {
    if (value < 0) {
      value = 0
    }
    else {
      timer.stop();
      timer.keyFrames = at (1s) { score => value tween EASEBOTH }
      timer.playFromStart();
    }
  }

When the value variable is changed, the timer is set and then it starts so that the value of the score variable gradually changes to new value of the value variable. Note that the score value is the rendered one.

  override function create() {
    group
  }

  def group = Group {}
  var digit = Digit {
    owner: this
  } on replace {
    insert digit into group.content
  }

The digit variable contains the higher position added to the group. When this variable is changed, a new position is added to the group.

Related Topics >>