AndEngine for Android Game Development Cookbook
上QQ阅读APP看书,第一时间看更新

Applying text to a layer

Text is an important part of game development as it can be used to dynamically display point systems, tutorials, descriptions, and more. AndEngine also allows us to create text styles which suit individual game types better by specifying customized Font objects. In this recipe, we're going to create a Text object, which updates itself with the current system time as well as correct its position every time the length of the string grows or shrinks. This will prepare us for the use of Text objects in cases where we need to display scores, time, and other non-specific dynamic string situations.

Getting ready…

Applying Text objects to our Scene object requires a working knowledge of AndEngine's font resources. Please perform the the recipe, Using AndEngine font resources in Chapter 1, Working with Entities, then proceed with the How to do it... section of this recipe. Refer to the class named ApplyingText in the code bundle for this recipe's activity in code.

How to do it…

When applying Text objects to our Scene object, we are required to create a Font object which will define the text's style, and create the Text object itself. See the folllowing steps for the specific actions we must take in order to properly display a Text object on our scene:

  1. The first step to creating any Text object is to prepare ourselves a Font object. The Font object will act as the resource which defines the style of the Text object. Additionally, we need to prepare the letters that we plan for the Text object to display:
        mFont = FontFactory.create(mEngine.getFontManager(),
            mEngine.getTextureManager(), 256, 256,
            Typeface.create(Typeface.DEFAULT, Typeface.NORMAL), 32f, true,
            Color.WHITE);
        mFont.load();
    
        /*
         * Prepare the mFont object for the most common characters used. This
         * will eliminate the need for the garbage collector to run when using a
         * letter/number that's never been used before
         */
          mFont.prepareLetters("Time: 1234567890".toCharArray());
    Once we've got our Font object created and ready for use, we can create the Text:
        /* Create the time Text object which will update itself as time passes */
        Text mTimeText = new Text(0, timeTextHeight, mFont, TIME_STRING_PREFIX
            + TIME_FORMAT, MAX_CHARACTER_COUNT, mEngine.getVertexBufferObjectManager()) {
    
          // Overridden methods as seen in step 3...
        };
  2. If we're dealing with final strings which may never change, only the first two steps need to be covered. However, in this recipe we will need to override the onManagedUpdate() method of the Text entity in order to make adjustments to its string over time. In this case, we're updating the time value of the string after every second passed:
        int lastSecond = 0;
        
        @Override
        protected void onManagedUpdate(float pSecondsElapsed) {
    
          Calendar c = Calendar.getInstance();
    
          /*
          * We will only obtain the second for now in order to verify
           * that it's time to update the Text's string
          */
          final int second = c.get(Calendar.SECOND);
    
          /*
           * If the last update's second value is not equal to the
          * current...
           */
          if (lastSecond != second) {
    
          /* Obtain the new hour and minute time values */
            final int hour = c.get(Calendar.HOUR);
            final int minute = c.get(Calendar.MINUTE);
    
            /* also, update the latest second value */
            lastSecond = second;
    
             /* Build a new string with the current time */
            final String timeTextSuffix = hour + ":" + minute + ":"
               + second;
    
            /* Set the Text object's string to that of the new time */
            this.setText(TIME_STRING_PREFIX + timeTextSuffix);
    
            /*
              * Since the width of the Text will change with every change
             * in second, we should realign the Text position to the
              * edge of the screen minus half the Text's width
            */
            this.setX(WIDTH - this.getWidth() * 0.5f);
          }
    
          super.onManagedUpdate(pSecondsElapsed);
        }
    Finally, we can make color adjustments to the Text and then attach it to the Scene or another Entity:
        /* Change the color of the Text to blue */
        mTimeText.setColor(0, 0, 1);
    
        /* Attach the Text object to the Scene */
        mScene.attachChild(mTimeText);

How it works…

By this point, we should already have an understanding of how to create the Font object as we had discussed it in the first chapter. If creating Font objects is not yet understood, please visit the recipe, Using AndEngine font resources in Chapter 1, Working with Entities.

In the first step, we are simply creating a basic Font object which will create a rather generic style for our Text object. Once the Font object has been created, we are preparing only the necessary characters that will be displayed throughout the life of the Text object with the mFont.prepareLetters() method. Doing so allows us to avoid garbage collector invocations within the Font object. The values used in this recipe will obviously range from 0 to 9 as we are dealing with time, as well as the individual characters that make up the string, Time:.

Once step one is completed, we can move onto step two where we create the Text object. The Text object requires that we specify its initial position on the screen in x and y coordinates, the Font object to use as a style, the initial string to display, its maximum character count, and finally the vertex buffer object manager as needed by all Entity objects. However, since we're dealing with a dynamically-updating String value for this Text object, which will require adjustments on the x axis, the parameters including the x coordinate as well as the initial string are not so important as they will be adjusted frequently during updates to the Text object. The most important parameter is the maximum character count. Failing to keep the Text object's maximum character count below that of the value specified within this parameter will result in the application receiving an ArrayIndexOutOfBoundsException exception and will likely require termination. For this reason, we are adding up the length of the largest string as seen in the following code snippet:

  private static final String TIME_STRING_PREFIX = "Time: ";
  private static final String TIME_FORMAT = "00:00:00";
  
  /* Obtain the maximum number of characters that our Text 
   * object will need to display*/
  private static final int MAX_CHARACTER_COUNT = TIME_STRING_PREFIX.length() + TIME_FORMAT.length();

In the third step, we are overriding the Text object's onManagedUpdate() method in order to apply changes to the Text object's string after every second passed. At first, we simply obtain the device's current second value, using it to compare with the second value in the previous call to the Text object's onManagedUpdate() method. This allows us to avoid updating the Text object with the system time on every single update. If the previous second that the Text object's string was updated with is not the same as the new second value, then we continue on to obtain the current minute and hour values as well via the Calendar.getInstance().get(HOUR) method and MINUTE variation. Now that we've got all of the values, we build a new string containing the updated time, and call setText(pString) on the Text object to change the string it will display on the device.

However, due to the fact that each individual character width might have a different value, we also need to make corrections in the position in order to keep the full Text object on the screen. By default, the anchor position is set to the center of an Entity object, so by calling this.setX(WIDTH - this.getWidth() * 0.5f), where this refers to the Text object, we position the entity's center-most point at the maximum screen width to the right, and then subtract half of the entity's width. This will allow the text to sit right along the edge of the screen even after its characters change the width of the Text object.

There's more...

Sometimes our games may require a little bit of formatting to the Text object's strings. In cases where we need to adjust the Text object's horizontal alignment, apply auto-wrapping to the text if its string exceeds a certain width, or a leading space to the text, we can do these with some very easy-to-use methods. The following methods can be called directly on the Text object; for example, mText.setLeading(3):

  • setAutoWrap(pAutoWrap): This method allows us to define whether or not, and if so, how a Text entity will perform auto-wrapping. The options we have for parameters include AutoWrap.NONE, AutoWrap.LETTERS, AutoWrap.WORDS, and AutoWrap.CJK. With LETTERS, line break won't wait for a whitespace before breaking to a new line while WORDS will. The CJK variant is an option which allows auto-wrapping for Chinese, Japanese, and Korean characters. This method should be used alongside setAutoWrapWidth(pWidth), where pWidth defines the maximum width of any single line within the Text object's string, causing line-breaks when necessary.
  • setHorizontalAlign(pHorizontalAlign): This method allows us to define the type of alignment the Text object's string should follow. The parameters include HorizontalAlign.LEFT, HorizontalAlign.CENTER, and HorizontalAlign.RIGHT. The result is similar to what we'd see when setting alignment within a text editor.
  • setLeading(pLeading): This method allows us to set a leading space at the beginning of the Text object's string. The parameter required is a float value, which defines the leading width of the sring.

See also

  • Using AndEngine font resources in Chapter 1, Working with Entities.
  • Overriding the onManagedUpdate method in this chapter.