C++フォントを扱う方法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
またかし
記事: 4
登録日時: 4年前

C++フォントを扱う方法

#1

投稿記事 by またかし » 4年前

初めまして。またかしと申します。
最近、C++とDXライブラリでゲーム制作を始めました。
何度も質問させていただく事になるかと思います。よろしくお願いします。

現在、文章を表示するプログラムを作っています。
実装のイメージは下記のような文章を下図のような形で表示するものです。

あいうえお\s[25]あいうえお\s
\s[25]\c[255,0,0]あいうえお\s\c

画像

実装方法は、まずテキストファイルをすべて読み込んで
読み込んだ文字を一文字一文字チェックし、「\」があったら
対応するコマンド(上記でいうと s , c)に応じてDXライブラリの関数CreateFontToHandleで
新しくフォントを作成する。

上図でいうと、
・通常の「あいうえお」
・サイズが25の「あいうえお」
・サイズが25で色が255,0,0の「あいうえお」
の3種類フォントを作成する形になります。

ここからが疑問なんですが、いちいち新しくフォントを作成し、フォントの仕様の数だけ
フォントを用意するのは果たして効率的なのでしょうか。

疑問を解消する為に、現在はそもそもフォントとはどういうもので、どういう仕組みでPC上で動いているのかを勉強しています。
しかし、なかなか自分の求める情報が手に入らず迷っています。

上記のような文章を表示する仕組みを効率的に実装するための何かヒントをいただけないでしょうか。

ちょっと質問が曖昧で申し訳ないですが、ご検討いただけると幸いです。

何卒よろしくお願いします。

アバター
あたっしゅ
記事: 663
登録日時: 13年前
住所: 東京23区
連絡を取る:

Re: C++フォントを扱う方法

#2

投稿記事 by あたっしゅ » 4年前

とりあえず、即レス。
固定文字なら、画像で持てばいいだろう。
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたッしュ、[MrAtassyu] 手提鞄屋魚有店
http://ameblo.jp/mratassyu/
Pixiv: 666303
Windows, Mac, Linux, Haiku, Raspbery Pi, Jetson Nano, 電子ブロック 持ち。

またかし
記事: 4
登録日時: 4年前

Re: C++フォントを扱う方法

#3

投稿記事 by またかし » 4年前

早々の回答をありがとうございます。
すみません。用途を記載し忘れていました。
RPGを製作しており、キャラクターの会話文に使用します。
その為、ひらがなから漢字まで対応できる必要があり、使用する文字もあらかじめ決まっていない為
フォントを利用する必要があります。

littlestream
記事: 48
登録日時: 7年前

Re: C++フォントを扱う方法

#4

投稿記事 by littlestream » 4年前

自分はRPGはほとんど作った事が無いですが、フォントと画像の組み合わせのためには
やはり文字コードShift-JISコードやASCIIコードを学んだ方を良いのだろうと思いますが、アクアプラスさん
のToHeart(トゥハート)なんかは文字列の画像を確かあいうえおかきくけこ~わをん~亜阿亞みたいに
文字列をあいうえお順で1つのファイルにまとめて利用していたという記事を見た事があります。

かずま

Re: C++フォントを扱う方法

#5

投稿記事 by かずま » 4年前

DxLib でフォントのサイズを変更したければ、
CreateFontToHandle で、サイズの異なるフォントを用意しておき、
DrawFormatStringToHandle で、文字列を表示すればよいでしょう。

ちょっと作ってみました。

コード:

#include "DxLib.h"
#include <map>
using namespace std;

char *message[3][2] = {
	{
		"あいうえお\\s[24]あいうえお\\s",
		"\\s[24]\\c[255,0,0]あいうえお\\s\\c"
	}, {
		"\\s[36]かきくけこ\\s\\c[0,0,255]ABCDEFG\\c",
		"\\c[0,128,128]\\s[24]さしすせそ\\s\\c"
	}, {
		"1234567\\s[24]89\\s",
		"abcdefg\\c[0,255,0]hijklmnopqr\\c[128,0,128]stuvwxyz\\c"
	}
};

int isLeading(char c) { return (c ^ 0x20) - 0xa1 < 60u; }

int createFont(int size)
{
	return CreateFontToHandle("MS ゴシック", size, 2,
			DX_FONTTYPE_ANTIALIASING);
}

class Text {
	int fh;  // font handle
	int width;
	int color;
	map<int, int> fonts;

public:
	Text() : color(0), width(8) {
		fonts[16] = createFont(16);
		fonts[24] = createFont(24);
		fonts[36] = createFont(36);
		fh = fonts[16];
	}

	void flush(int x, int y, int h, const char *s0, const char *s) {
		if (s > s0) {
			y += h / 2 - width;
			DrawFormatStringToHandle(x, y, color, fh, "%.*s", s - s0, s0);
		}
	}

	void setFont(int size) {
		if (size == 0) size = 16;
		auto it = fonts.find(size);
		if (it != fonts.end()) {
			fh = it->second;
			width = size / 2;
		}
	}

	void drawString(int x, int y, int h, const char *s) {
		const char *s0 = s;
		int xpos0 = x, xpos1 = x;
		while (*s) {
			if (*s == '\\') {
				int n = 0, size, r, g, b;
				if (s[1] == 's') {
					if (sscanf(s+2, "[%d]%n", &size, &n) != 1)
						size = 16;
					flush(xpos0, y, h, s0, s);
					s0 = s += n + 2, xpos0 = xpos1;
					setFont(size);
				}
				else if (s[1] == 'c') {
					if (sscanf(s+2, "[%d,%d,%d]%n", &r, &g, &b, &n) != 3) 
						r = g = b = 0;
					flush(xpos0, y, h, s0, s);
					s0 = s += n + 2, xpos0 = xpos1;
					color = GetColor(r, g, b);
				}
				else
					s++, xpos1 += width;
			}
			else {
				if (isLeading(*s)) s++, xpos1 += width;
				s++, xpos1 += width;
			}
		}
		flush(xpos0, y, h, s0, s);
	}
};

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
	SetBackgroundColor(255, 255, 255);
	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen(DX_SCREEN_BACK);

	Text text;
	int count = 0, id = 0, x = 50, y = 30, height = 36;

	while (!ScreenFlip() && !ProcessMessage() && !ClearDrawScreen()) {
		if (++count == 60) {
			count = 0;
			if (++id == 3) id = 0;
		}
		for (int i = 0; i < 2; i++)
			text.drawString(x, y + height * i, height, message[id][i]);
	}
	DxLib_End();
	return 0;
} 
フォントのサイズは偶数でなければいけません。
プロポーショナルフォントも使えません。
文字コートは Shift-JIS であることを仮定しています。

分からないことは質問してください。

またかし
記事: 4
登録日時: 4年前

Re: C++フォントを扱う方法

#6

投稿記事 by またかし » 4年前

>littlestreamさん。
 ご返信ありがとうございます。
 文章が多いゲームでそのような手法がとられているという事は、あたっしゅさんの内容と同様に文字を画像として使用するのが効率的そうですね。

>かずまさん。
 CreateFontToHandleを使用する方法は思いついたんですが、そこで疑問がでました。
 FontHandleをいくつも作る事は効率なのでしょうか? という疑問です。
 RPGを製作しており長い文章が必要になる可能性があります。その際は、FontHandleを何十個を作ることになる場合もありえます。 メモリー消費や動作速度の観点からこの手法以外に良い方法がないのかと考えています。

ご返信ありがとうございました。

アバター
あたっしゅ
記事: 663
登録日時: 13年前
住所: 東京23区
連絡を取る:

Re: C++フォントを扱う方法

#7

投稿記事 by あたっしゅ » 4年前

「効率」とかは、実際に「長い文章」の表示で、メモリー消費や動作速度が問題になってから考えろ。
まず、「長い文章」を作れ。

64 bit 時代に、文章表示で、メモリー消費や動作速度が問題になるか。ここやほかの web ページ、ちゃんと読めてるんだろ。

そもそも、フォントたくさん使っても、そのフォント、Game する側が持っているのか ?
フォントがないから「文字列の画像を確かあいうえおかきくけこ~わをん~亜阿亞みたいに文字列をあいうえお順で1つのファイルにまとめて利用」するんだろ。
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたッしュ、[MrAtassyu] 手提鞄屋魚有店
http://ameblo.jp/mratassyu/
Pixiv: 666303
Windows, Mac, Linux, Haiku, Raspbery Pi, Jetson Nano, 電子ブロック 持ち。

dic
記事: 655
登録日時: 13年前
住所: 宮崎県
連絡を取る:

Re: C++フォントを扱う方法

#8

投稿記事 by dic » 4年前

想定する環境を書いてください。

またかし
記事: 4
登録日時: 4年前

Re: C++フォントを扱う方法

#9

投稿記事 by またかし » 4年前

>あたっしゅさん
 dicさん

ご返信ありがとうございます。
あたっしゅさんのおっしゃるように、まずは自分なりのやり方をやってみて不都合があってから別案を考える事にします。

皆さま、アドバイスありがとうございました。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: C++フォントを扱う方法

#10

投稿記事 by ISLe » 4年前

DXライブラリでは、同時に作成できるハンドルの最大数が固定なので、足りなくなるときは足りなくなりますよね。
あと、CreateFontToHandleで作成したフォントハンドルごとに描画キャッシュ用のテクスチャが作成されるので、ビデオメモリの容量が不足することも懸念されます。

フォントを描画するときにビットマップレベルで拡大縮小できる仕組みがDXライブラリにあれば、ある程度大きめに作っておいたフォントをいろんなサイズに使い回すことができるのですが、残念ながらありません。

低スぺ環境しかないユーザーを切り捨てるのは簡単ですが、せっかくならできる限り多くのユーザーに楽しんでもらいたいと個人的には思いますし、少し工夫すればプレイできるユーザーに、開発者が楽するためにプレイしてもらうことができないとなれば、個人的には心が痛みます。

わたしが、ずっと以前にPCゲーム開発者が集うメーリングリストに入っていたとき、GetGlyphOutlineというWin32 APIでフォント一文字ずつビットマップを取得して、描画に用いる方法を得ました。
ひらがなカタカナなどの頻出文字はあらかじめ作っておいて、そうでない文字は必要になる都度つくり、あまり使われない文字から削除するキャッシュ機構を用意して、消費する容量の上限を一定に保つことができます。
#DXライブラリも内部では同様のことをしている。
自前でキャッシュを用意すれば、描画時にサイズを変えて描画することができるので、描画に必要なサイズすべてを用意する必要はなくなり、メモリ消費の軽減を図ることができます。

とは言え、Windows7のサポート期間終了も近いので、DirectWriteに移行してしまうのも一考の余地あり。
低スぺを切り捨てるというのと、サポートの切れたOSに対応し続けるというのは違いますからね。

返信

“C言語何でも質問掲示板” へ戻る