工場裏のアーカイブス

素人によるiPhoneアプリ開発の学習記 あと機械学習とかM5Stackとか

M5Stackで日本語文章をスクロール(2)

※本記事は前回の続きとなります。前回はフォントおよび文字をまとめたデータファイルを作成し、それをmicro SDカードから読み込ませることで、M5Stackで日本語文章を表示可能とする方法についてメモしました。

f:id:fleron:20191214235145j:plain
 
本記事では、M5Stackのライブラリの一つであるTFT_eSPIのスプライト機能を用いて、文章を画面上でスクロールする方法についてメモしたいと思います(こちらの公式サンプルを大いに参考にしております)。
 

スプライトとは何か?

そもそもスプライトとは何であるのか、端的な説明は中々難しく、私自身もスプライトについて正しく理解出来ているのかは正直自信がありません。不正確な点もあるかもしれませんが、私の理解に基づいて例え話で説明してみます。

例えばM5Stackの画面に眼と口の絵を表示して、画面を顔のように見せるプログラムを作りたいとします。このときM5.Lcd系の描画関数(drawLineとかdrawRectとか)を用いる標準的な方法は、大きな紙(画面)に直接マジックで目と口の絵を描き込むようなものです。一方スプライト機能は、あらかじめ眼だけ、口だけの絵を描いた小さな紙を用意しておき、それらを大きな紙の上に配置して顔にするようなものです。この小さな紙にあたるのがスプライトです。

f:id:fleron:20191216232325p:plain

ここでは小さな紙と例えましたが、実際はスプライトの大きさは自由に設定可能であり、画面よりサイズの大きなスプライトを用意することも可能です。スプライトにもM5.Lcd系と同様の描画用関数が存在し、同じように図形や文字列などを描き込むことが出来ます。

ここで、この顔の右眼だけをウインクさせたいとします。大きな紙に直接マジックで書き込むM5.Lcd系の方法では、新しく大きな紙を用意してまた両眼と口を全て描き直さないといけません。左眼と口には何の変化も無いにもかかわらずです。一方でスプライト機能では、右眼の小さな紙だけを新しくすれば良いので効率的です。

さらに、大きな紙とは画面そのものであるので、そこに直接描き込む過程も外から丸見えとなってしまいます。単純な顔の表示だけならまだしも、より複雑な絵をアニメーションさせたいような場合は、絵を描く→紙を新しくする(画面をクリアする)→新しい絵を描く、という過程の繰り返しが画面のちらつきとなって表れてしまいます。
一方スプライトは、それに対して色々を描き込んでも、画面に配置するまでは外に見えることはありません。いわば裏方で描画を行うことが可能であり、画面には裏で描画完了済みのスプライトのみを表示することで、アニメーションのちらつきを抑制することが可能です。

拙い説明でしたが、スプライト機能とはこのような物となります。
 

スプライト機能を用いた文字列のスクロール

TFT_eSPIのスプライト機能には、setScrollRect関数・Scroll関数という簡単にスクロールを実現可能な、非常に便利な仕組みが備わっています。

setScrollRect関数ではスプライト上に、指定したサイズ・色でスクロール用の短形を描画することが出来ます。そして短形上に描画した図形や文字列は、Scroll関数を呼び出すだけで、x軸・y軸方向に指定した量だけスクロールさせることが出来ます。ベルトコンベアの上に載せた物が運ばれるようなイメージです。

これらを用いて、日本語の文字列を一方向にスクロールさせる簡単なサンプルスケッチを作成してみました(※前回記事で作成したフォントのデータファイルを使用します)。

#include <M5Stack.h>
#include <M5Stack.h>

//スプライトの初期化。
TFT_eSprite esp = TFT_eSprite(&M5.Lcd);

int tcount = 0;

void setup() {
  M5.begin();
  M5.Lcd.fillScreen(TFT_WHITE);

  //スプライトで使用する色のビット数を設定(1 or 8 or 16)
  esp.setColorDepth(8);

  //スプライトの実体を生成。ただしこの段階では、まだ画面上には
  //表示されない。後にpushSprite関数で画面上に配置する。
  esp.createSprite(280, 200);

  //M5.Lcd系と同名、同機能の描画関数が多く揃っており(例外もあり)、
  //スプライト上に様々な描画を行うことが可能。前回記事で作成した
  //フォントのデータも同様に読み込んで表示可能。
  esp.fillSprite(TFT_DARKGREY);
  String fileName = "MotoyaLMaru-20”; //ファイル名は適宜書き換え
  esp.loadFont(fileName, SD);
  esp.setTextColor(TFT_WHITE, TFT_BLACK);
  
  //ここでの座標は画面全体ではなく、スプライト内部のローカル座標を表す
  //スプライトの左上から(5, 5)の位置に文字を描画
  esp.setCursor(5, 5);
  esp.print("スプライト");

  //スクロール用の短形を作成。この短形上に描画した図形や文字列は
  //scroll関数によってスクロールさせることが可能。
  esp.setScrollRect(20, 90, 240, 20, TFT_BLACK);
}

void loop() {
  //画面上にスプライトが配置されて、表示されるようになる。
  //指定座標がスプライト左上の頂点位置となる。
  esp.pushSprite(20, 20);

  delay(10);

  //スクロール用短形上に描画した図形や文字列を、(x, y)方向に
  //指定した量だけスクロールさせる。
  esp.scroll(2, 0);

  //スクロール用短形の上に載るように、文字列を描画する。
  //スクロールした文字列は、やがて短形の端に消えて行くので
  //一定周期で新たな文字列を描画する。
  tcount--;
  if(tcount <= 0){
    tcount = 50;
    esp.setTextColor(TFT_YELLOW, TFT_BLACK);
    esp.setCursor(20, 90);
    esp.print("スクロール");
  }
}

こちらを実行すると、以下のようにスプライトが灰色の領域として表示され、その中央には黒色の領域(ScrollRect)が表示されます。そしてその上を「スクロール」という文字列が左から右へ次々と流れて行きます。

f:id:fleron:20191220223226p:plain
f:id:fleron:20191220223159p:plain
 

ところで本スケッチでは、ScrollRectの上に文字列がきちんと乗るように調整しています。しかし以下のように、文字列がScrollRectからはみ出したら何が起きるでしょうか?

    //カーソル位置を書き換えて、文字がScrollRectからはみ出すようにする
    esp.setCursor(20, 80);  //(20, 90)から書き換え
    esp.print("スクロール");

実際にやってみると、以下のようにScrollRectに乗った部分のみがスクロールして、はみ出した部分は動かずに取り残されます。

f:id:fleron:20191220225307p:plain

以上のようにスプライト機能を用いることで、M5Stackで日本語文章をスクロールさせるという目的を、比較的簡単に達成することが出来ました。スプライト機能については、まだまだ色々と面白い使い方が出来そうですので、今後も色々調べて勉強してみたいと思います。