A012.サウンドノベル基本

文章をサウンドノベルのように1文字ずつ描画してゆくプログラムです。

http://dxlib.o.oo7.jp/dxprogram.html#N6

DXライブラリを参考にしました!

 

-- conf.lua

function love.conf(t)
	t.window.width = 640
	t.window.height = 480
end

640x480で設計していますので、コンフィグファイルを設定しておきましょう。

 

日本語のプログラムを作るには、ひと工夫必要です。

LOVEのプログラム本体には、日本語のような全角文字の表示に対応していません。

しかしながら、ご安心ください、方法があります!

日本語に対応したフォントファイルを読み込んでおくのです。

まず、フォントファイルをインターネット上からダウンロードしましょう。

といっても、具体的にどうすればいいか迷ってしまうと思うので、おすすめのリンク先を張っておきます。

 

独立行政法人情報処理推進機構 IPAexフォント 

 

このサイトから、IPAexfont00301.zip(9.21MB)をクリックすることでzipファイルをダウンロードできます。

zipファイルを解凍して展開したフォルダには「ipaexm.ttf」と「ipaexg.ttf」というファイルがあります。

これが、フォントファイルです。

明朝とゴシックの違いなので、今回はゴシックの「ipaexg.ttf」を使ってみましょう。

このファイルを開発している「main.lua」のあるフォルダにコピーしてください。

これで、準備が整いました!

(フォントを含めて配布する場合は、ライセンス条項を順守してください)

 

実際にソースを記述していきましょう。

 

-- main.lua

-- 文字サイズ
local	MOJI_SIZE = 32
local	MOJI_SPEED = 0.05

local	messages = {
	" ゲームプログラムとは、いやプログラムとは" ,
	"ある事柄を実現するプログラムの方法を説明されても理解できないことがある。B" ,
	"@ なぜならそのプログラム技法も何かの基本的な技法の組み合わせで出来ているからだ。B",
	"@ これはその他の学問も基本がわからないと応用が利かないということと同じ現象で、",
	"別に特に珍しいことでもない。B" ,
	"C しかしゲームプログラムとなると覚えなくてはならない基礎が沢山あり、" ,
	"さらにある程度クオリティの高いソフトを作ろうとすると色々なプログラム技法を",
	"習得しなくてはならない。B" ,
	"@ しかもある程度レベルが高くなると自分で技法を編み出すか、技術レベルの高い",
	"プログラマーに聞くなどするしか方法がなく大変厄介である。B",
	"というかそのせいでゲームプログラムの敷居は高くなっているといえる。BE",
}

-- 基本情報
local	font
local	mx, my
local	message = ""
local	sentence = ""
local	moji_dt = 0
local	button_flag = false
local	button_message = "--ボタンを押してね!--"
local	keystate = {}


function love.load()
	-- ファイルからフォントを作成
	font = love.graphics.newFont("ipaexg.ttf", MOJI_SIZE)
	
	-- フォントをセット
	love.graphics.setFont(font)
	
	-- 初期位置をセット
	mx = 1
	my = 1
end


function love.keypressed(key, irepeat)
	keystate.key = true
	
	if key == "escape" then
		love.event.quit()
	end
end


function love.keyreleased(key)
	keystate.key = false
end


function get_char(
	-- 指定位置からキャラクターを取得
	text,
	pos
	)
	local	len = nil
	local	check_byte = function(
		-- バイト領域が正しいかどうかを判定
		text,
		pos,
		first,
		last
		)
		local	byte = string.byte(text:sub(pos, pos))
		if byte then
			return byte >= first and byte <= last
		end
		return nil
	end
	if check_byte(text, pos, 0x00, 0x7f) then
		len = 1
	elseif check_byte(text, pos, 0xc2, 0xdf) then
		if check_byte(text, pos + 1, 0x80, 0xbf) then
			len = 2
		end
	elseif check_byte(text, pos, 0xe0, 0xef) then
		local	bl = true
		for i = 1, 2 do
			bl = check_byte(text, pos + i, 0x80, 0xbf)
			if bl == false then break end
		end
		if bl == true then len = 3 end
	elseif check_byte(text, pos, 0xf0, 0xf7) then
		local	bl = true
		for i = 1, 3 do
			bl = check_byte(text, pos + i, 0x80, 0xbf)
			if bl == false then break end
		end
		if bl == true then len = 4 end
	elseif check_byte(text, pos, 0xf8, 0xfb) then
		local	bl = true
		for i = 1, 4 do
			bl = check_byte(text, pos + i, 0x80, 0xbf)
			if bl == false then break end
		end
		if bl == true then len = 5 end
	elseif check_byte(text, pos, 0xfc, 0xfd) then
		local	bl = true
		for i = 1, 5 do
			bl = check_byte(text, pos + i, 0x80, 0xbf)
			if bl == false then break end
		end
		if bl == true then len = 6 end
	end
	if len then
		local	char = text:sub(pos, pos + len - 1)
		return char, len
	end
	
	return nil
end



function love.update(dt)
	
	
	if button_flag == true then
		for key, value in pairs(keystate) do
			if value == true then
				button_flag = false
			end
		end
	end
	
	if button_flag == false and moji_dt >= MOJI_SPEED then
		-- stringを解析してゆく
		
		-- 1文字取得
		local	char, len = get_char(messages[my], mx)
		if char == '@' then
			-- 改行文字
			
			sentence = ""
			message = message .. "\n"
			
			
		elseif char == 'B' then
			-- ボタン押し待ち文字
			
			button_flag = true
			
		elseif char == 'E' then
			-- 終了文字
			
			love.event.quit()
			
		elseif char == 'C' then
			-- クリア文字
			
			sentence = ""
			message = ""
			
		else
			-- 使用フォントの横サイズを把握して、入りきらなければ、メッセージを改行
			sentence = sentence .. char
			if font:getWidth(sentence) > love.graphics.getWidth() then
				-- オーバーしたので、メッセージを折り返す処理
				sentence = char
				message = message .. "\n"
			end
			
			-- 連結!
			message = message .. char
		end
		
		-- 位置を進める
		mx = mx + len
		
		-- 文字列がこれ以上読めない場合
		if mx > string.len(messages[my]) then
			my = my + 1
			mx = 1
			
			-- 読み終わったら終了
			if messages[my] == nil then
				love.event.quit()
			end
		end
		
		moji_dt = 0
	end
	
	moji_dt = moji_dt + dt
end



function love.draw()
	
	-- メッセージを描画
	love.graphics.printf(message, 0, 0, love.graphics.getWidth())
	
	-- ボタンを押す必要のときだけ、要求メッセージを描画
	if button_flag then
		local	y = love.graphics.getHeight() - font:getHeight(button_message)
		
		love.graphics.printf(button_message, 0, y, love.graphics.getWidth())
	end
end

日本語を表示するには、更に、工夫が必要です!

「メモ帳」で開いたときの手順を例に説明します。

「main.lua」を開いている状態で……

ファイル(F)(タグ)→名前を付けて保存→

と開くと、「名前を付けて保存」ウィンドウが開きます。

ウィンドウの下部分に「文字コード(E)」の項目がありますね!

この項目は通常であれば「ANSI」になっているので、逆三角を押し「UTF-8」を選択、保存してください。

 

これで「UTF-8」の文字コードのファイルになりました!

やり方を覚えている方もいるかもしれません。

以前「conf.lua」のタイトルを表示する個所で説明した内容です!

 

では、さっそくプログラムを実行してみてください。

文字がうまく表示されていけば成功です、お疲れさまでした!

 

以下、補足です。

get_charは、UTF-8の文字を解析し、該当する一文字と、何バイト分あるかをチェックしています。

Shift-JISでは、半角英数字は1バイト、日本語の文字は2バイトと決まっています。

ところが、UTF-8は、一つの文字を表すのに、1バイトから6バイトまであるのです。

文字が何バイトなのかを判別し、キャラクターとバイト数を取得しているというのがポイントです。

小難しいことをやっているようですが、範囲内にあるかどうかをチェックしているだけです。

UTF-8の詳細は、ウィキペディアをご覧ください。

 

http://ja.wikipedia.org/wiki/UTF-8

 


[2016.08.18] 変更:0.10.1で動作するようにソースを修正