ドラクエ風 簡単な名前入力システムを作る
ドラクエをはじめ、名前を入力するシステムはよくありますね。
今回はなるべく簡単にその仕組を作ってみたいと思います。
完成系はこのような感じです。
今どきのゲームはよくわかりませんが、レトロゲームではおなじみの名前入力システムだと思います。
必要なものを配置
では早速やっていきましょう。
枠の作成
必要な素材とUIオブジェクトを配置していきます。
Hierarchy上に2つImageを作ります。
- LetterPanel(文字を選択する枠)
- NamePanel(選択した名前を表示する枠)
それぞれを程よい位置に配置します。
名前用の枠の調整
続いてNamePanelに「なまえ:」と書いてあるテキストを配置します。
NameHeaderとします。
次に実際に文字が選択されたら表示されるテキストをNamePanelに配置します。
NameTextとします。
Widthは実際に表示させたい文字数で収まるように調整して下さい。
位置を調整するときに親要素のNamePanelにHorizontalLayoutGroupコンポーネントを追加しておくと配置が楽です。
このような感じになりました。
文字選択用枠の調整
続いて文字選択用のLetterPanelを作り込んでいきます。
まずLetterPanelにGridLyaoutGroupコンポーネントを追加します。
これは格子状に子要素を並べてくれるので便利です。
続いて子要素に1文字のTextを作ります。「あ」と表示させておきましょう。
この名前をLetterとします。
選択中のカーソルが必要ですね。
カーソルとなるような素材を用意してプロジェクトにインポートしておきましょう。
カーソルの準備ができたらLetterの子要素にImageを作成しカーソルの素材を配置します。
名前はArrowとしました。
次にArrowの位置調整が必要です。
Transformで調整しても構いませんが、LetterにHorizontalLayoutGroupを追加すると楽に配置できます。
良い位置にArrowを配置してみて下さい。
現在こんな感じになっています。
Letterの位置を調整しましょう。
LetterPanelに追加したGridLayoutGroupのPaddingの値を調整すると位置を調整できます。
今回はLeftを50、Topを50にしました。
それでは子要素のLetterを複製してどのように並ぶか見てみましょう。
試しに15個並べましたが間隔が結構開いてると思います。
今回はLetterが横に10個並んで折り返しするようにしたいです。
再びLatterPanelのGridLayoutGroupを調整します。
Cell SizeというプロパティでXとYの大きさを調整します。
今回はXを55、Yを50としました。
ゲームビューとLetterPanelのInspectorはこんな感じになっています。
このあたりはお好みで調整してみて下さい。
これで配置は完成です。続いてLetterの文字の中身を「あ」ではなく五十音にしていきたいと思います。
五十音の表示
LetterのTextコンポーネントは「あ」となっていますが、それを五十音分複製し「い」「う」「え」…と手動で作っていっても良いのですが、少し手間ですのでCSVデータから自動で五十音分のLetterを作る方法をやってみたいと思います。
LetterをPrefab化
そのためにまずLetterをPrefab化しましょう。
Prefab化したLetterの文字を空にします。そして子要素のArrowを非表示にしておきましょう。
何も表示されなくなりました。
続いてLetterというC#スクリプトを作成し、LetterのPrefabにアタッチします。
Letterスクリプトの中身はこのようにします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Letter : MonoBehaviour
{
[SerializeField] GameObject arrow;
Text letter;
void Start()
{
letter = GetComponent();
}
public void Selected(bool _selected)
{
if (_selected)
{
arrow.SetActive(true);
}
else
{
arrow.SetActive(false);
}
}
public void SetLetter(string _letter)
{
letter.text = _letter;
}
public string SelectLetter()
{
return letter.text;
}
}
SerializeFieldで子要素のArrowを取得するための変数arrow、Text型の変数letterを定義します。
Start関数で自身のTextコンポーネントを取得しletter変数に格納します。
次のSelected関数は自身の選択状態によって子要素Arrowの表示・非表示を切り替える処理をしています。
SettLetter関数は自身のTextコンポーネントの文字を設定する関数です。
最後のSelectLeterは決定されたときに文字を返す関数です。
これでLetterPrefabは完成です。
続いてLetterPanelを作っていきましょう。
LetterPanelスクリプト
新たにLetterPanelというC#スクリプトを作成し、ゲームオブジェクトLetterPanelにアタッチします。
LetterPanelスクリプトを作り込んでいきましょう。
変数の定義
まず必要となる変数を定義していきます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LetterPanel : MonoBehaviour
{
[SerializeField] Letter letter;
[SerializeField] TextAsset letterText;
[SerializeField] Text nameText;
List letters;
int maxNameLength = 8;
int currentIndex = 0;
string myName = "";
void Start()
{
}
void Update()
{
}
}
上から順番に、
- LetterPrefab用の変数
- 五十音CSV用の変数
- 名前表示用のText型変数
- 生成されたLetterを格納するList
- 名前の最大文字数
- 選択中の文字の番号用変数
- 名前用のstring型変数
となっています。
ではUnityエディターに戻ってLetterPanelのInspectorからそれぞれアタッチしていきましょう。
Letterには先程プレファブ化したLetterを、LetterTextには五十音用CSVを、NameTextにはNamePanelの子要素であるNameTextをそれぞれ割り当てます。
五十音用のCSVは検索すればいくつかのサイトで提供しくれていますが、私の作ったCSVデータを載せておきますので、よろしければコピペして使ってみて下さい。
あ,い,う,え,お,か,き,く,け,こ
さ,し,す,せ,そ,た,ち,つ,て,と
な,に,ぬ,ね,の,は,ひ,ふ,へ,ほ
ま,み,む,め,も,や,ゆ,よ
ら,り,る,れ,ろ,わ,を,ん
CSVデータ読み込み
いよいよCSVデータを読み込んでいきます。Start関数を以下のようにします。
void Start()
{
string[] letterLines = letterText.text.Split(new[] { '\n', '\r' }, System.StringSplitOptions.RemoveEmptyEntries);
foreach (string letterLine in letterLines)
{
string[] letterValues = letterLine.Split(new char[] { ',' });
foreach (string l in letterValues)
{
Debug.Log(l);
}
}
}
ここではCSVデータから一行ずつ分割し、さらにカンマ区切りで分割して一文字ずつ取り出しています。
詳しくは過去の記事「テキストデータからマップを作ってみる」をご覧ください。
実行してログがちゃんと出ているか確認してみましょう。
ちゃんとひらがなが一文字ずつログで表示されていますね。
ひらがなを画面に表示する
続いて画面上にこのひらがなを表示させてみましょう。
流れはこのようになっています。
- Letterオブジェクトの生成
- lettersに格納
- letterに文字を送る
スクリプトはこのようになります。
void Start()
{
//追加 Listを初期化
letters = new List();
string[] letterLines = letterText.text.Split(new[] { '\n', '\r' }, System.StringSplitOptions.RemoveEmptyEntries);
foreach (string letterLine in letterLines)
{
string[] letterValues = letterLine.Split(new char[] { ',' });
foreach (string l in letterValues)
{
//追加
Letter letterObj = Instantiate(letter, transform);
letters.Add(letterObj);
StartCoroutine(_setLetter(l, letterObj));
}
}
}
//追加
IEnumerator _setLetter(string _l,Letter _letterObj)
{
yield return null;
_letterObj.SetLetter(_l);
}
Start関数の最初でListを初期化しています。
続いてループ内のDebug.Logを削除し、Letterを生成、Listに格納、コルーチンで生成したLetterの文字をセットしています。
生成してすぐに letterObj.SetLetter(l); とするとNullReferenceExceptionになってしまいますのでコルーチンを挟んで処理をしています。
実行してみるとこのようになりました。
無事に五十音のひらがなが並びましたね。
次はカーソルを表示して文字を選択出来るようにしていきます。
文字の選択
ではまずLetterPanelスクリプトのUpdateでキー入力の処理を書いていきましょう。
void Update()
{
if (Input.GetKeyDown(KeyCode.RightArrow))
{
currentIndex++;
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
currentIndex--;
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
currentIndex += 10;
}
if (Input.GetKeyDown(KeyCode.UpArrow))
{
currentIndex -= 10;
}
}
キー入力
入力したキーの方向でcurrentIndexの値を操作しています。
左で+1、右で-1、下で+10、上で-10と言った感じです。
上下に関してはもっと良いやり方もありますが、わかりやすくするためにマジックナンバーにしています。
10という値に関してはLetterPanel内の折り返し文字数が10個なのでこのようにしています。
カーソルの表示
次にLetterの子要素であるArrowの表示非表示をやっていきましょう。
LeterPanelスクリプトにShowArrowという関数を新たに追加します。
そしてStart時、Updateのキー入力時でそれぞれ呼び出すように修正します。
void Start()
{
/* 略 */
ShowArrow();
}
/* 略 */
void Update()
{
if (Input.GetKeyDown(KeyCode.RightArrow))
{
currentIndex++;
ShowArrow();
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
currentIndex--;
ShowArrow();
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
currentIndex += 10;
ShowArrow();
}
if (Input.GetKeyDown(KeyCode.UpArrow))
{
currentIndex -= 10;
ShowArrow();
}
}
//追加
void ShowArrow()
{
currentIndex = Mathf.Clamp(currentIndex, 0, letters.Count - 1);
for (int i = 0; i < letters.Count; i++)
{
if (i == currentIndex)
{
letters[i].Selected(true);
}
else
{
letters[i].Selected(false);
}
}
}
ShowArrow関数内ではまず、currentIndexの値が範囲外にならないようにMathf.Clampで数値を丸めています。
関連記事:プレイヤーの移動範囲をMathf.Clampで制限する
その後、lettersの数だけループしcurrentIndexの値とiの値が一致した場合はLetterスクリプトのSelectedを呼んで表示、それ以外は非表示としています。
実際に動かしてみるとこのようになります。
カーソルが範囲内で正常に動いてくれています。
次は一文字ずつ決定したり、間違えた場合はバックスペースで一文字消したりしたいと思います。
文字の決定と削除
スクリプトを触る前にNameTextが「ああああああああ」となっていますので空っぽにしておきましょう。
文字を決定していく
今回はスペースキーを押したら一文字決定とし、nameTextに表示していきます。
LetterPanelスクリプトのUpdate関数に処理を追加します。
void Update()
{
/* 略 */
if (Input.GetKeyDown(KeyCode.Space))
{
if (myName.Length < maxNameLength)
{
myName += letters[currentIndex].SelectLetter();
}
nameText.text = myName;
}
}
スペースキー入力時の処理を書きました。
string型のmyNameの長さが最大文字数maxNameLengthの値より少なければmyNameに現在選択中のLetterのSelectLetter関数を呼び出し一文字追加しています。
その後nameTextのtextにその値を入れています。
実行するとこのような感じです。
文字数の制限もできているのが確認できました。
間違えた文字を消す
では最後にバックスペースキーで文字を消していく処理を追加します。
同じくLetterPanelスクリプトのUpdate関数内に記述します。
void Update()
{
/* 略 */
if (Input.GetKeyDown(KeyCode.Backspace))
{
if (myName.Length > 0)
{
myName = myName.Remove(myName.Length - 1);
}
nameText.text = myName;
}
}
これでmyNameの一番最後の一文字が削除されます。
一点注意ですがmyNameが何も無い場合、この処理を行おうとするとエラーになりますので、0以上の場合という条件をつけています。
それでは実行してみましょう。
文字を後ろから消していけることが確認できました。
濁点、半濁点、カタカナ、英数など色々改良出来ると思います。
関連記事
TextMeshProでリンクを作成する方法とそのバグの解消法
今回はTMPのリンクを生成するLinkタグについて紹介したいと思います。
ちょっとしたハマりポイントもあるのでそちらについても触れていきたいと思います。
UnityでCSVを活用したメッセージウィンドウの作り方
メッセージの内容を変更したい時や、新しいメッセージを追加したい時にCSVファイルを使えば、メッセージの編集や追加が簡単になり、開発がスムーズになります。
Unityで9-sliceテクニックを使った効果的なUI作成方法
UI作成時に非常に便利なテクニック9-sliceをやってみます。
とても簡単に使える技です。
【初心者向け】ButtonのOnClickをC#スクリプトで登録する
ButtonのクリックイベントをInspectorではなくC#スクリプトから登録する方法です。
初心者の方でも簡単に出来る内容です。
Unity2Dで動画を流してみる
Unity2Dで動画を操作してみたいと思います。
サイズの調整や再生、一時停止、停止などの処理も実装しています。
Unity コルーチンとカラーでレインボーカラー
派手な演出レインボーの点滅を実装します。ゲームが華やかになります。
最後までご覧頂いてありがとうございました。