山水堂
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のプログラム本体には、日本語のような全角文字の表示に対応していません。
しかしながら、ご安心ください、方法があります!
日本語に対応したフォントファイルを読み込んでおくのです。
まず、フォントファイルをインターネット上からダウンロードしましょう。
といっても、具体的にどうすればいいか迷ってしまうと思うので、おすすめのリンク先を張っておきます。
このサイトから、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で動作するようにソースを修正