Siv3Dでグラデーション文字を作る

はじめに

この記事は Siv3D AdventCalendar 2017 13 日目の記事です。
前日の記事はmoonmileさんのLattePanda と Siv3D(仮)です。

みなさんはコンピューターゲームでのグラデーション文字を見たことはありますか?

影付き文字ではありません。文字そのものがグラデーションしてるものです。
RPGツクール2000東方projectシリーズ*1などを始め様々なゲームで見ることが出来ます。

f:id:movementi:20171203230053j:plain
RPGツクール2000
f:id:movementi:20171203230054j:plain
東方萃夢想(青字が自キャラ赤字が敵のセリフと色分けされている)
f:id:movementi:20171204232927j:plain
Siv3Dでこんなものが作れます*2
グラデーション文字は目がチカチカしない読みやすさを保ちつつ誰のセリフか視覚的に分かりやすいです。重要なセリフの色分けに使うのも効果的ですね。なにより単色のテキストよりオシャレ。

自分もグラデ文字使いたい!!でもどうやって作ればいいかわかんない!?
そんなあなたもこの記事を読んで今日から君もグラデーション文字デビューだ!!!

環境

Siv3DのバージョンはAugust 2016 v2版を使います。 Siv3Dの基本的な使い方については説明を省略しますので公式の豊富なリファレンスを読んで学んでいただけると幸いです。

github.com

目次

完成図

コード全文

# include <Siv3D.hpp>
void Main()
{
    const Font font(30); //Font作成

    const String string(L"Siv3Dで\nグラデーション文字を作る\n"); //テキスト作成

    Image image(font.region(string).size);//Image作成

    font.overwrite(image, string, { 0,0 }, Palette::White); //ベース文字を書き込む

    for (int i = 0; i <= font.region(string).size.y / font.height; i++) //グラデーション書き込み
    {
        Image(Palette::Blue, L"Image/gradation.jpg") //グラデーション画像を透過画像に
            .scaled(font.region(string).size.x, font.height) //透過画像のサイズ調整
            .write(image, Point(0, font.height*i)); //透過画像を書き込む
    }

    const Texture texture = Texture(image);  //ImageからTextureを作成

    while (System::Update())
    {
        texture.draw(30, 80); //Textureを表示
    }
}

実行結果

f:id:movementi:20171204221526j:plain

Imageフォルダを作ってgradation.jpgさえ入れておけばコピペするだけで動きます。 こんな短いコードでグラデーション文字が実装出来ます。すごいですねSiv3D。
どういうコードになっているか今から一行一行解説していきます

解説

フォントと文章の用意

Font font(30);
String string(L"Siv3D グラデーション文字");

好きなフォントと表示したい文章を用意します。フォントのサイズは30ぐらいにしときましょう。

Image型インスタンスの作成

 Image image(font.region(string).size);

Image型でグラデーションを掛ける処理を行うので表示する文章の大きさに合わせたImageを用意します。
テキストを描く · Siv3D/Reference-JP Wiki · GitHub

font.region()はテキストが表示される領域(のサイズ)を調べる関数です詳しくは上のリンクから確認してください。

Imageに文章を書き込む

 font.overwrite(image, string, { 0,0 }, Palette::White);

先程用意したImageに文章を書き込みます。overwriteではなくwriteにしてしまうと文字が見えなくなってしまうので気をつけましょう。*3
Palette::Whiteの部分が文字のベースの色になります。

文字とグラデーション用画像を合成

 for (int i = 0; i <= font.region(string).size.y / font.height; i++)
    {
        Image(Palette::Blue, L"Image/gradation.jpg")
            .scaled(font.region(string).size.x, font.height)
            .write(image, Point(0, font.height*i));
    }

今回の記事の肝となる部分です。

透過情報を持つ画像を作成する

Image(Palette::Blue, L"Image/gradation.jpg")

for文は飛ばしてここから。これはSiv3Dの公式サンプルにも乗ってる機能です。こちらから確認してみてください

画像編集 · Siv3D/Reference-JP Wiki · GitHub

// 第 2 引数の画像の r 成分を、新しく作る画像の a 成分とする

「rとかaとか何言ってるのか分かんねぇ…」自分も悩んだので解説します。 rとはRGBのR成分。つまり赤色成分です。256段階で大きいほど赤色が強くなります。 aとはalpha、透明度成分です。256段階の0なら完全に透明で255なら完全に不透明になります。

これをふまえるとこの関数は『第二引数の画像の赤い部分を赤い分だけ不透明にして第一引数の色で塗りつぶす』関数と説明できます。 ちなみにRGBのGとBはなにも使いません。この先では特に記載が無い場合この2つの成分は0とします。

ここの第一引数の色を自由に設定することで1つのgradation.jpgでありとあらゆる色のグラデーションが作れます。

気になるgradation.jpgはこちら。

f:id:movementi:20171204221523j:plain
gradation.jpg(圧縮されてぼやけてます)
赤い分だけ不透明な色に、黒い分だけ透明になると考えてもらって結構です。

グラデーション画像の調達

グラデーション画像を用意する方法はお絵かきソフトで作る、Siv3Dで作る等ありますが簡単に作れるサイトを1つご紹介します。 www.lancork.net

f:id:movementi:20171204232928j:plain
使い方サンプル
使用時のコツをいくつか

  • 横サイズは1で大丈夫です。自分は管理のしやすさとサイズの差が微々たるものである点を考慮して横に長いものを使っています。
  • 縦サイズは60あれば十分ですフォントサイズを100にしてもちっとも荒れません。
  • この記事で使われているグラデーション画像は開始色#000000終了色#a40000です。好みに合わせて調整してみてください。
  • GB成分である後ろ4桁は0000のままで構いません

グラデーション画像のサイズを文字に合わせる

 .scaled(font.region(string).size.x, font.height)

グラデーション用画像の大きさを変えます。横幅はテキスト表示領域いっぱい、縦幅はフォントの高さを指定します。ちなみにフォントのサイズを30にするとフォントの高さは60になります。引き伸ばしたら汚くなると思われる方も居るかもしれませんが、グラデーション用画像がある程度大きければ縦に引き伸ばしてもきれいに写りますよ。横は言わずもがな。

f:id:movementi:20171204221524p:plain
引き伸ばした画像のイメージ

文字と合成

f:id:movementi:20171204221525j:plain
合成のイメージ

.write(image, Point(0, font.height*i));

いよいよ合成です。文字がない部分は完全に透明なので合成した後は文字にだけ色がかかります。writeではなくoverwriteにしてしまうと一面のグラデーション画像で文字が見えなくなってしまうので気をつけましょう。上で青い透過画像にしたので文字は青のグラデーションがかかります。

for文について

 for (int i = 0; i <= font.region(string).size.y / font.height; i++)
    {
       //略
       .write(image, Point(0, font.height*i));
    }

このfor文について解説します。これは文章が複数行になった際、一行一行にグラデーションをかけるためのfor文です。
font.region(string).size.y / font.heightは文章全体の高さ÷一行の高さになるのでこの文章が何行有るか計算できます。
Point(0, font.height*i(添字))で行数に合わせてグラデーション画像を合成する位置を調整します。

f:id:movementi:20171204232926j:plain
一行一行ではなく文章全体にグラデーションがかかった例

TextureにImageからコピーして表示

 const Texture texture = Texture(image);   

    while (System::Update())
    {
        texture.draw(30, 80);
    }

後はテクスチャにした文章を表示するだけです。この先は関数化したりクラス化したりして自作のアプリで使いやすく改造してみてください。

さいごに

「Siv3Dでグラデーション文字を作る」いかがだったでしょうか。長い記事にお付き合いいただきありがとうございます。お疲れ様でした。

おまけ

実はSiv3Dにはグラデーション図形を描くというグラデーション文字作成にドンピシャで使えそうな機能があります。

siv3d.hateblo.jp

しかし残念なことにこの機能、Textureを描くことにしか使えずImageに描きこむことができないのです。(writeやoverwriteといった関数が使えない) もし使えたら画像を用意することなくグラデーション文字が作れるんですけどねぇ。

Twitterの話によるとバージョンアップでImageへの書き込み機能が追加されるかもしれませんね。楽しみに待っています。 ほんとに終わり。

*1:東方紅魔郷からダブルスポイラーまで

*2:「Siv3Dでグラデーション文字を作る」の部分のフォントはロゴたいぷゴシック、「こんなものがつくれる!!」は手描き

*3:透明なImageとブレンドして文字も透明になる