Статьи

Разработка игр для Android — Использование растровых шрифтов

Android — это ОС, работающая на многих типах устройств с разными размерами экрана. Из-за этого довольно сложно решить проблемы со шрифтами, касающиеся как размера, так и внешнего вида на этих платформах.
Чтобы использовать согласованные шрифты на разных устройствах, я использовал растровые шрифты. То есть каждый символ представлен растровым изображением.

Например, мы можем создать изображение для каждой буквы в алфавите и цифре, и всякий раз, когда мы хотим отобразить текст, мы рисуем изображения символов из строки в заданной позиции.
Но ждать! Не загружается ли изображение для каждого персонажа излишним? Да, это. Мы можем использовать утилиты Android для управления растровыми изображениями в наших интересах.

Все, что нам нужно, это растровое изображение, содержащее все символы, которые мы хотим использовать, и разделить его на отдельные растровые изображения. Чтобы сделать это простым способом, мы будем использовать моноширинный шрифт. Моноширинный шрифт — это шрифт, буквы и символы которого занимают одинаковое горизонтальное пространство. Другими словами, символы имеют одинаковую ширину.

Простая карта персонажей может выглядеть следующим образом.

Обратите внимание, что фон прозрачен.
Приведенная выше карта символов организована в виде сетки. Ширина символа составляет 8 пикселей. Высота 12 пикселей. Обратите внимание, что вторая строка начинается с 15 пикселей, поэтому между строками есть промежуток. Это просто выбор, а также то, как Photoshop нарушил границы, и я не стал их менять.

На изображении выше показано начало первого ряда и то, как символы организованы в ячейки.
После этого его легко нарезать. Простая итерация сделает свое дело. Для простоты я создал карту только из английского алфавита, цифр и нескольких знаков препинания. Вы можете расширить его, как хотите. Используйте моноширинный шрифт в Photoshop, Gimp или любом другом редакторе изображений, который вам нравится, и создайте свой собственный лист.
Вот множество растровых шрифтов на выбор.

Как мы можем использовать это? Моя идея отобразить текст на экране — создать метод drawString, который принимает текст для отображения в качестве параметра вместе с позицией, в которой мы хотим его отобразить.

Что-то вроде этого сделает это:

1
void drawString(Canvas canvas, String text, int x, int y)

Я также передаю объект canvas , на котором я хочу нарисовать текст. Это просто для простоты. В случае рендерера OpenGL нам придется использовать рекламные щиты (квадраты с текстурами). Но для нарезки изображений и отображения шрифтов давайте придерживаться этого подхода.

Создайте простой проект Android, который использует простой 2D-холст. Мы будем опираться на это.
Я реализовал SurfaceView для хранения холста и назвал его DrawingPanel .
В его конструкторе я просто регистрирую его, чтобы получать события при касании поверхности и загружать ресурсы. Ресурсы на самом деле просто изображения глифов / символов.

Загрузите следующий файл изображения и перетащите его в папку ресурсов проектов eclipse по адресу : / res / drawable-mdpi для ADP, чтобы сгенерировать идентификатор для ресурса.

Создайте класс DrawingPanel .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DrawingPanel extends SurfaceView implements SurfaceHolder.Callback {
 
    private Canvas canvas;      // the canvas to draw on
    private Glyphs glyphs;      // the glyphs
 
    public DrawingPanel(Context context) {
        super(context);
        // adding the panel to handle events
        getHolder().addCallback(this);
 
        // initialise resources
        loadResources();
 
        // making the Panel focusable so it can handle events
        setFocusable(true);
    }
 
    /** Loads the images of the glyphs */
    private void loadResources() {
        this.glyphs = new Glyphs(BitmapFactory.decodeResource(getResources(), R.drawable.glyphs_green));
        Log.d(TAG, "Green glyphs loaded");
    }

Я опустил другие методы, которые должны быть реализованы, так как они просто заглушки.

Переменная canvas используется для рисования текста и получается при каждом событии касания. Вы увидите позже в методе onTouchEvent .
Наиболее интересным классом является класс Glyphs, который содержит ассоциацию символов с изображениями. Переменная glyphs создается в методе loadResources () . Он вызывает конструктор класса Glyphs с ранее скопированным изображением листа персонажа.

Проверьте класс Glyphs :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package net.obviam.fonts;
 
import java.util.HashMap;
import java.util.Map;
 
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.Log;
 
/**
 * @author impaler
 *
 */
public class Glyphs {
 
    private static final String TAG = Glyphs.class.getSimpleName();
    private Bitmap bitmap;  // bitmap containing the character map/sheet
 
    // Map to associate a bitmap to each character
    private Map<Character, Bitmap> glyphs = new HashMap<Character, Bitmap>(62);
 
    private int width;  // width in pixels of one character
    private int height; // height in pixels of one character
 
    // the characters in the English alphabet
    private char[] charactersL = new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
            'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y', 'z' };
    private char[] charactersU = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G',
            'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
            'U', 'V', 'W', 'X', 'Y', 'Z' };
    private char[] numbers = new char[] { '1', '2', '3', '4', '5', '6', '7',
            '8', '9', '0' };
 
    public Glyphs(Bitmap bitmap) {
        super();
        this.bitmap = bitmap;
        this.width = 8;
        this.height = 12;
        // Cutting up the glyphs
        // Starting with the first row - lower cases
        for (int i = 0; i < 26; i++) {
            glyphs.put(charactersL[i], Bitmap.createBitmap(bitmap,
                    0 + (i * width), 0, width, height));
        }
        Log.d(TAG, "Lowercases initialised");
 
        // Continuing with the second row - upper cases
        // Note that the row starts at 15px - hardcoded
        for (int i = 0; i < 26; i++) {
            glyphs.put(charactersU[i], Bitmap.createBitmap(bitmap,
                    0 + (i * width), 15, width, height));
        }
        // row 3 for numbers
        Log.d(TAG, "Uppercases initialised");
        for (int i = 0; i < 10; i++) {
            glyphs.put(numbers[i], Bitmap.createBitmap(bitmap,
                    0 + (i * width), 30, width, height));
        }
        Log.d(TAG, "Numbers initialised");
 
        // TODO - 4th row for punctuation
    }
 
    public Bitmap getBitmap() {
        return bitmap;
    }
 
    /**
     * Draws the string onto the canvas at <code>x</code> and <code>y</code>
     * @param text
     */
    public void drawString(Canvas canvas, String text, int x, int y) {
        if (canvas == null) {
            Log.d(TAG, "Canvas is null");
        }
        for (int i = 0; i < text.length(); i++) {
            Character ch = text.charAt(i);
            if (glyphs.get(ch) != null) {
                canvas.drawBitmap(glyphs.get(ch), x + (i * width), y, null);
            }
        }
    }
}

Линия

1
private Map<Character, Bitmap> glyphs = new HashMap<Character, Bitmap>(62);

создает карту, которая связывает растровое изображение с каждым символом.
Чтобы загрузить это нам нужны растровые изображения для каждого символа. Я создал 3 массива для персонажей, для которых у меня есть растровые изображения.
символы L [] содержат строчные буквы английского алфавита, символы U [] буквы верхнего регистра и цифры [] содержат цифры. В качестве упражнения добавьте массив знаков препинания, так как этот отсутствует.

Обратите внимание, что порядок символов такой же, как и в таблице символов.
Для удобства я создал массив для каждой строки из изображения. Чтобы связать изображения с символами, я буду проходить через них и вырезать соответствующее изображение из листа на основе индекса. Имея фиксированную ширину для персонажей, все это просто. Внимательно изучите и поймите конструктор. Это делает нарезку и ассоциацию.

Метод drawString (Canvas canvas, String text, int x, int y) выполняет рисование. Он занимает позицию для рисования, просматривает переданный текст и постепенно рисует каждый символ. Горизонтальное смещение легко рассчитать, поскольку каждый символ имеет одинаковую ширину.

Вот и все. Чтобы попробовать это, добавьте следующий метод в DrawingPanel :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public boolean onTouchEvent(MotionEvent event) {
    // draw text at touch
    try {
        canvas = getHolder().lockCanvas();
        synchronized (getHolder()) {
            if (event.getAction() == MotionEvent.ACTION_DOWN
                    || event.getAction() == MotionEvent.ACTION_MOVE) {
                // clear the screen
                canvas.drawColor(Color.BLACK);
                // draw glyphs
                glyphs.drawString(canvas, "Drawing string at "
                        + (int) event.getX() + " " + (int) event.getY(),
                        (int) event.getX(), (int) event.getY());
            }
            if (event.getAction() == MotionEvent.ACTION_UP) {
                canvas.drawColor(Color.BLACK);
                glyphs.drawString(canvas, "Drawn string at "
                        + (int) event.getX() + " " + (int) event.getY(),
                        (int) event.getX(), (int) event.getY());
            }
        }
    } finally {
        if (canvas != null) {
            getHolder().unlockCanvasAndPost(canvas);
        }
    }
    // event was handled
    return true;
}

Каждый раз, когда мы прикасаемся к экрану, мы пытаемся ухватить холст, чтобы иметь возможность рисовать на нем. В случае касания или перетаскивания мы просто очищаем холст и рисуем на нем строку.
Когда мы перестали касаться экрана, мы показываем немного другой текст в последней позиции.
Наконец мы выпускаем холст. Убедитесь, что вы возвращаете true, чтобы указать, что событие было обработано.

Вот и все. Последнее, что нужно сделать, это установить DrawingPanel в качестве нашего представления. Это сделано в деятельности, созданной с проектом. Я также отключил заголовок.

01
02
03
04
05
06
07
08
09
10
public class PrintingActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // turn off title
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(new DrawingPanel(this));
    }
}

Полученное приложение должно выглядеть так:

Для этого с OpenGL следуйте тому же принципу и используйте растровые изображения для текстур. Я опубликую версию этой статьи в OpenGL, так что следите за обновлениями.

Загрузите код здесь (obviam.bitmapfont.tar.gz) .

Справка: Использование растровых шрифтов в Android от нашего партнера по JCG Тамаса Яно из блога « Против зерна ».

Не забудьте проверить нашу новую Android игру ArkDroid (скриншоты ниже) . Ваш отзыв будет более чем полезным!
Статьи по Теме: