今回はユニティちゃんが村人と会話出来る機能を作成していきます。
今回の機能はちょっと難しいかも?しれません。
前回は村人が巡回ポイントを移動するようにしました。
ユニティちゃんのRPGを作ってみようの他の記事は
から見ることが出来ます。
村人との会話機能
村人との会話機能を作成していきます。
村人が会話する範囲を作成
まずは村人が会話をする範囲を作成していきます。
Villagerゲームオブジェクトを選択し右クリックからCreate Emptyを選択しSearchAreaという名前にします。
SearchAreaのインスペクタのAdd ComponentからPhysics→Sphere Colliderを取り付け、Is Triggerにチェックを入れ衝突をせず検知するようにします。
このコライダがユニティちゃんと会話する範囲になります。
Sphere ColliderのCenterとRadiusを設定しコライダの範囲を調整します。
村人の会話が出来る範囲は以下のようにしました。
会話範囲に入った時に実行するスクリプト
先ほど村人が会話する範囲を作成しましたが、その範囲内に入った時の処理をスクリプトで作成していきます。
Assets/RPG/Scriptsフォルダ内で右クリックからCreate→C# Scriptを選択し、名前をConversationScopeScriptとし、SearchAreaゲームオブジェクトに取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ConversationScopeScript : MonoBehaviour { void OnTriggerStay(Collider col) { if (col.tag == "Player" && col.GetComponent<UnityChanScript>().GetState() != UnityChanScript.State.Talk ) { // ユニティちゃんが近づいたら会話相手として自分のゲームオブジェクトを渡す col.GetComponent<UnityChanTalkScript>().SetConversationPartner(transform.parent.gameObject); } } void OnTriggerExit(Collider col) { if (col.tag == "Player" && col.GetComponent<UnityChanScript>().GetState() != UnityChanScript.State.Talk ) { // ユニティちゃんが遠ざかったら会話相手から外す col.GetComponent<UnityChanTalkScript>().ResetConversationPartner(transform.parent.gameObject); } } } |
OnTriggerStayで範囲内にPlayerタグを持ったコライダでそのコライダのUnityChanScriptのGetStateメソッドを呼び出し、ユニティちゃんが会話している時でなければそのコライダからUnityChanTalkScriptのSetConversationPartnerメソッドを呼び出し自身のゲームオブジェクトを渡します。
OnTriggerExitでPlayerタグを持つコライダでユニティちゃんの状態が会話状態でない時に範囲外に出たらResetConversationPartnerメソッドに自身のゲームオブジェクトを渡し会話相手から外します。
ここでPlayerタグを設定したコライダでユニティちゃんを判定しているので、UnityChanゲームオブジェクトを選択し、インスペクタのTagをPlayerにします。
ここでUnityChanScriptのGetStateメソッドを呼び出して状態の判定をしていますが、UnityChanScriptの状態を表す列挙型や状態を取得するGetStateメソッド等は後で作成していきます。
UnityChanTalkScriptも後で作成します。
現時点ではスクリプトがなかったりメソッドが作成されていなかったりでエラーが出ますが、無視してください。
会話表示用UIの作成
会話表示用のUIを作成していきます。
ヒエラルキー上で右クリックからUI→Panelを選択し、作成されたCanvasの名前をTalkUIとします。
Panelを選択した状態で右クリックからUI→Image、UI→Textを作成します。
シーンビューでPanelの四隅の矢印をShiftキーを押しながらドラッグし、会話表示部分の範囲を作成します。
上のような感じにしました。
TextはインスペクタのRect TransformのAnchor Presetsでstretch stretchにし、Panelのサイズに合わせて伸縮するようにします(選択する時にShift+Altキーを押しながら選択)。
またFont Sizeを25、Colorを白色にします。
ImageはAnchor Presetsをbottom rightにし、Panelの右下に表示されるようにします。WidthとHeightは30にします。
ImageのSource ImageにはDropdownArrowを設定します。
またTalkUIはゲーム画面のサイズに合わせて拡張したいのでインスペクタのCanvas ScaleでUI Scale ModeをScale With Screen Sizeにします。
Reference Resolutionは基準となる画面の解像度でXを1024、Yを768としておきます。
Screen Match ModeはExpandとします。
細かいUIの作成方法やこの後作成するユニティちゃんの会話機能は
の方で解説していますのでそちらを参照してみてください。
TalkUIは会話する時だけ見えるようにする為、TalkUIのインスペクタの名前の横のチェックを外し、最初は見えないようにしておきます。
会話可能アイコンUIの作成
次はユニティちゃんが村人と会話が可能である時(村人のSearchAreaのコライダの範囲内にいる時)に表示する会話を促すアイコンのUIを作成します。
ヒエラルキー上で右クリックからUI→Panelを選択し、Canvasの名前をTalkIconにします。
PanelのAnchor Presetsをmiddle centerにし、Widthを200、Heightを60にします。
またAdd ComponentからLayout→Horizontal Layout Groupを取り付けます。
設定は上のような感じにします。
ImageのSource Imageはトークアイコンの背景画像を設定し、Colorで色を設定します。
Horizontal Layout Groupは子要素を横並びに整列させる機能です。
Panelの子要素にUI→Imageを作成します。
ImageのWidthとHeightを50にします。
ImageのSource Imageには押すべきボタンのアイコンを指定します(今回は適当なアイコンを設定しています)。
Panelの子要素にUI→Textを作成します。
Textのインスペクタは下のようにWidthやHeightを設定し、テキストのサイズと色を変化させています。
TalkIconも村人と会話が可能な時だけ見えるようにする為、TalkIconのインスペクタの名前の横のチェックを外し最初は見えないようにしておきます。
UnityChanScriptに処理を追加
UnityChanScriptに会話している状態を作る為に処理を追加します。
ユニティちゃんの状態を表す列挙型Stateを作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class UnityChanScript : MonoBehaviour { public enum State { Normal, Talk } |
ユニティちゃんの状態を保持するstateフィールド、UnityChanTalkScriptを保持するunityChanTalkScriptフィールドを宣言します。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // ユニティちゃんの状態 private State state; // ユニティちゃん会話処理スクリプト private UnityChanTalkScript unityChanTalkScript; // Start is called before the first frame update void Start() { state = State.Normal; unityChanTalkScript = GetComponent<UnityChanTalkScript>(); } |
Startメソッドではユニティちゃんの状態の初期化とUnityChanTalkScriptを取得しておきます。
次にUpdateメソッド内の処理をユニティちゃんの状態に応じて変化させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | // Update is called once per frame void Update() { if (state == State.Normal) { if (characterController.isGrounded) { velocity = Vector3.zero; var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); if (input.magnitude > 0.1f) { transform.LookAt(transform.position + input.normalized); animator.SetFloat("Speed", input.magnitude); if (input.magnitude > 0.5f) { velocity += transform.forward * runSpeed; } else { velocity += transform.forward * walkSpeed; } } else { animator.SetFloat("Speed", 0f); } if(unityChanTalkScript.GetConversationPartner() != null && Input.GetButtonDown("Jump") ) { SetState(State.Talk); } } } else if(state == State.Talk) { } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } |
State.Normal状態の時は今までと同じように移動処理を行い、その中でUnityChanTalkScriptのGetConversationPartnerメソッドで会話相手がいるかどうか調べ、いる時でJumpボタンを押したら状態をState.Talk状態にします。
State.Normal状態であろうとState.Talk状態であろうと重力は働かせます。
次に状態変更と初期処理を行うSetStateメソッドを追記します。
1 2 3 4 5 6 7 8 9 10 11 12 | // 状態変更と初期設定 public void SetState(State state) { this.state = state; if(state == State.Talk) { velocity = Vector3.zero; animator.SetFloat("Speed", 0f); unityChanTalkScript.StartTalking(); } } |
引数で受け取った状態を保持し、ユニティちゃんが会話状態になった時は移動速度velocityを0にし、アニメーションパラメータのSpeedを0にしてアニメーションをIdleにし、unityChanTalkScriptのStartTalkingメソッドを呼び出して会話を始めます。
状態を取得するGetStateメソッドを追加します。
1 2 3 4 5 | public State GetState() { return state; } |
状態フィールドstateを返しているだけです。
ユニティちゃんの会話処理用スクリプトを作成
先ほどConversationScopeScriptでUnityChanTalkScriptのメソッドを呼び出していますのでそのスクリプトを作成していきます。
ユニティちゃんの会話処理スクリプトUnityChanTalkScriptをAssets/RPG/Scriptsの中に作成し、後でUnityChanゲームオブジェクトに取り付けます(現時点では他のスクリプトエラーで取り付けられない為)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | using System.Collections; using System.Collections.Generic; using System.Text.RegularExpressions; using UnityEngine; using UnityEngine.UI; public class UnityChanTalkScript : MonoBehaviour { // 会話可能な相手 private GameObject conversationPartner; // 会話可能アイコン [SerializeField] private GameObject talkIcon = null; // TalkUIゲームオブジェクト [SerializeField] private GameObject talkUI = null; // メッセージUI private Text messageText = null; // 表示するメッセージ private string allMessage = null; // 使用する分割文字列 [SerializeField] private string splitString = "<>"; // 分割したメッセージ private string[] splitMessage; // 分割したメッセージの何番目か private int messageNum; // テキストスピード [SerializeField] private float textSpeed = 0.05f; // 経過時間 private float elapsedTime = 0f; // 今見ている文字番号 private int nowTextNum = 0; // マウスクリックを促すアイコン [SerializeField] private Image clickIcon = null; // クリックアイコンの点滅秒数 [SerializeField] private float clickFlashTime = 0.2f; // 1回分のメッセージを表示したかどうか private bool isOneMessage = false; // メッセージをすべて表示したかどうか private bool isEndMessage = false; void Start() { clickIcon.enabled = false; messageText = talkUI.GetComponentInChildren<Text>(); } void Update() { // メッセージが終わっているか、メッセージがない場合はこれ以降何もしない if (isEndMessage || allMessage == null) { return; } // 1回に表示するメッセージを表示していない if (!isOneMessage) { // テキスト表示時間を経過したらメッセージを追加 if (elapsedTime >= textSpeed) { messageText.text += splitMessage[messageNum][nowTextNum]; nowTextNum++; elapsedTime = 0f; // メッセージを全部表示、または行数が最大数表示された if (nowTextNum >= splitMessage[messageNum].Length) { isOneMessage = true; } } elapsedTime += Time.deltaTime; // メッセージ表示中にマウスの左ボタンを押したら一括表示 if (Input.GetButtonDown("Jump")) { // ここまでに表示しているテキストに残りのメッセージを足す messageText.text += splitMessage[messageNum].Substring(nowTextNum); isOneMessage = true; } // 1回に表示するメッセージを表示した } else { elapsedTime += Time.deltaTime; // クリックアイコンを点滅する時間を超えた時、反転させる if (elapsedTime >= clickFlashTime) { clickIcon.enabled = !clickIcon.enabled; elapsedTime = 0f; } // マウスクリックされたら次の文字表示処理 if (Input.GetButtonDown("Jump")) { nowTextNum = 0; messageNum++; messageText.text = ""; clickIcon.enabled = false; elapsedTime = 0f; isOneMessage = false; // メッセージが全部表示されていたらゲームオブジェクト自体の削除 if (messageNum >= splitMessage.Length) { EndTalking(); } } } } private void LateUpdate() { // 会話相手がいる場合はTalkIconの位置を会話相手の頭上に表示 if (conversationPartner != null) { talkIcon.transform.Find("Panel").position = Camera.main.GetComponent<Camera>().WorldToScreenPoint(conversationPartner.transform.position + Vector3.up * 2f); } } // 会話相手を設定 public void SetConversationPartner(GameObject partnerObj) { talkIcon.SetActive(true); conversationPartner = partnerObj; } // 会話相手をリセット public void ResetConversationPartner(GameObject parterObj) { // 会話相手がいない場合は何もしない if(conversationPartner == null) { return; } // 会話相手と引数で受け取った相手が同じインスタンスIDを持つなら会話相手をなくす if(conversationPartner.GetInstanceID() == parterObj.GetInstanceID()) { talkIcon.SetActive(false); conversationPartner = null; } } // 会話相手を返す public GameObject GetConversationPartner() { return conversationPartner; } // 会話を開始する public void StartTalking() { var villagerScript = conversationPartner.GetComponent<VillagerScript>(); villagerScript.SetState(VillagerScript.State.Talk, transform); this.allMessage = villagerScript.GetConversation().GetConversationMessage(); // 分割文字列で一回に表示するメッセージを分割する splitMessage = Regex.Split(allMessage, @"\s*" + splitString + @"\s*", RegexOptions.IgnorePatternWhitespace); // 初期化処理 nowTextNum = 0; messageNum = 0; messageText.text = ""; talkUI.SetActive(true); talkIcon.SetActive(false); isOneMessage = false; isEndMessage = false; // 会話開始時の入力は一旦リセット Input.ResetInputAxes(); } // 会話を終了する void EndTalking() { isEndMessage = true; talkUI.SetActive(false); // ユニティちゃんと村人両方の状態を変更する var villagerScript = conversationPartner.GetComponent<VillagerScript>(); villagerScript.SetState(VillagerScript.State.Wait); GetComponent<UnityChanScript>().SetState(UnityChanScript.State.Normal); Input.ResetInputAxes(); } } |
conversationPartnerは会話相手を保持して置くフィールド、talkIconやTalkUIは先ほど作ったゲームオブジェクトをインスペクタで設定します。
clickIconにはTalkUIの子要素のImageを設定します。
その他フィールドやUpdateメソッドの会話を表示する処理の詳細は
上の記事で詳しく書いていますので、そちらを参照してください。
今回はInput.GetButtonDown(“Jump”)でJumpに割り当てられているボタンを押した時にメッセージの一括表示や次のメッセージの表示をするようにしています。
ボタンの割り当てはUnityメニューのEditからProject Settings→Inputで設定出来ます。
メッセージ表示機能以外の部分を見ていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | private void LateUpdate() { // 会話相手がいる場合はTalkIconの位置を会話相手の頭上に表示 if (conversationPartner != null) { talkIcon.transform.Find("Panel").position = Camera.main.GetComponent<Camera>().WorldToScreenPoint(conversationPartner.transform.position + Vector3.up * 2f); } } // 会話相手を設定 public void SetConversationPartner(GameObject partnerObj) { talkIcon.SetActive(true); conversationPartner = partnerObj; } // 会話相手をリセット public void ResetConversationPartner(GameObject parterObj) { // 会話相手がいない場合は何もしない if(conversationPartner == null) { return; } // 会話相手と引数で受け取った相手が同じインスタンスIDを持つなら会話相手をなくす if(conversationPartner.GetInstanceID() == parterObj.GetInstanceID()) { talkIcon.SetActive(false); conversationPartner = null; } } // 会話相手を返す public GameObject GetConversationPartner() { return conversationPartner; } // 会話を開始する public void StartTalking() { var villagerScript = conversationPartner.GetComponent<VillagerScript>(); villagerScript.SetState(VillagerScript.State.Talk, transform); this.allMessage = villagerScript.GetConversation().GetConversationMessage(); // 分割文字列で一回に表示するメッセージを分割する splitMessage = Regex.Split(allMessage, @"\s*" + splitString + @"\s*", RegexOptions.IgnorePatternWhitespace); // 初期化処理 nowTextNum = 0; messageNum = 0; messageText.text = ""; talkUI.SetActive(true); isOneMessage = false; isEndMessage = false; // 会話開始時の入力は一旦リセット Input.ResetInputAxes(); } // 会話を終了する void EndTalking() { isEndMessage = true; talkUI.SetActive(false); // ユニティちゃんと村人両方の状態を変更する var villagerScript = conversationPartner.GetComponent<VillagerScript>(); villagerScript.SetState(VillagerScript.State.Wait); GetComponent<UnityChanScript>().SetState(UnityChanScript.State.Normal); } |
LateUpdateメソッドはUpdateメソッドと同じように毎フレーム呼ばれますが、Updateより後に呼ばれます。
会話相手がいる場合はtalkIconの位置を会話相手の頭上に表示させています。
Camera.main.GetComponent
こうすることでTalkIconのサイズが変更せず会話相手が移動してもそのワールド位置をカメラのスクリーンの位置に変換し移動するようになります。
SetConversationPartnerメソッドはtalkIconを表示して、会話相手を設定します。
ResetConversationPartnerメソッドは引数でリセットする会話相手が渡ってきますが、会話相手をリセットするのは保持している会話相手と引数で受け取った会話相手が一致した時だけです。
会話相手が設定されていなかったり、他の村人が会話相手として設定されている時に別の村人を引数で受け取っても会話相手はリセットしません。
GetConversationPartnerメソッドは会話相手を返します。
StartTalkingメソッドはユニティちゃんが会話相手と会話する時に呼び出されるメソッドで会話するのにあたっての初期化処理をしています。
conversationPartnerには会話相手の村人のゲームオブジェクトが入っているので、GetComponetでVillagerScriptを取得出来ます。
VillagerScriptのSetStateメソッドを呼び出して村人の状態を変化させます。
同時に村人の会話内容を保持しているスクリプトをGetConversationメソッドで取得しGetConversationMessageメソッドで実際の会話内容の文字列を取得します。
ユニティちゃんが村人と会話を開始するのは村人の会話範囲にいる時にJumpに割り当てられたボタンを押した時に始めますが、その際のキー入力を会話を進める時にも使っています。
そこでInput.ResetInputAxesメソッドを呼び出して一旦リセットし、会話を進める時はもう一度Jumpボタンを押す必要があります。
会話が終了した時はEndTalkingメソッドを呼び出し、そこで村人とユニティちゃんの状態を変更しています。
UnityChanゲームオブジェクトのUnityChanTalkScriptの設定は
上のように設定しました。
VillagerScriptスクリプトに追記
村人のスクリプトVillagerScriptにも会話の処理を追記します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class VillagerScript : MonoBehaviour { public enum State { Wait, Walk, Talk } |
村人にもTalk状態を追加します。
フィールドに会話処理で使うものを追記します。
1 2 3 4 5 6 7 8 9 10 | // 会話内容保持スクリプト [SerializeField] private Conversation conversation = null; // ユニティちゃんのTransform private Transform conversationPartnerTransform; // 村人がユニティちゃんの方向に回転するスピード [SerializeField] private float rotationSpeed = 2f; |
conversationはこの後作成するVillagerAやVillagerBファイルをインスペクタで設定します。
conversationPartnerTransformはユニティちゃんのTransformが入ります。
rotationSpeedは会話する時に村人がユニティちゃんの方に向くスピードを設定します。
UpdateメソッドにState.Talk状態の時の処理を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | void Update() { // 見回り if (state == State.Walk) { // エージェントの潜在的な速さを設定 animator.SetFloat("Speed", navMeshAgent.desiredVelocity.magnitude); // 目的地に到着したかどうかの判定 if (navMeshAgent.remainingDistance < 0.1f) { SetState(State.Wait); } // 到着していたら一定時間待つ } else if (state == State.Wait) { elapsedTime += Time.deltaTime; // 待ち時間を越えたら次の目的地を設定 if (elapsedTime > waitTime) { SetState(State.Walk); } } else if(state == State.Talk) { // 村人がユニティちゃんの方向をある程度向くまで回転させる if (Vector3.Angle(transform.forward, new Vector3(conversationPartnerTransform.position.x, transform.position.y, conversationPartnerTransform.position.z) - transform.position) > 5f) { transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(new Vector3(conversationPartnerTransform.position.x, transform.position.y, conversationPartnerTransform.position.z) - transform.position), rotationSpeed * Time.deltaTime); animator.SetFloat("Speed", 1f); } else { animator.SetFloat("Speed", 0f); } } } |
1 2 3 | Vector3.Angle(transform.forward, new Vector3(conversationPartnerTransform.position.x, transform.position.y, conversationPartnerTransform.position.z) - transform.position) > 5f |
でユニティちゃんの位置から村人の位置を引いてユニティちゃんの方向を求めたものと村人の前方の角度をVector3.Angleで求め5度より大きい時は村人をユニティちゃんの方向に向かせます。
ユニティちゃんの位置でYだけ村人のYの位置を設定しているのは高さが違う時にYの位置を合わせて計算したい為です。
1 2 3 | transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(new Vector3(conversationPartnerTransform.position.x, transform.position.y, conversationPartnerTransform.position.z) - transform.position), rotationSpeed * Time.deltaTime); |
でQuaternion.Lerpで徐々に現在の角度からユニティちゃんの方向の角度へと変更します。
Quaternion.LookRotationでユニティちゃんの方向のQuaternionを求めています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // 村人の状態変更 public void SetState(State state, Transform conversationPartnerTransform = null) { this.state = state; if (state == State.Wait) { elapsedTime = 0f; animator.SetFloat("Speed", 0f); } else if(state == State.Walk) { SetNextPosition(); navMeshAgent.SetDestination(GetDestination()); navMeshAgent.isStopped = false; } else if(state == State.Talk) { navMeshAgent.isStopped = true; animator.SetFloat("Speed", 0f); this.conversationPartnerTransform = conversationPartnerTransform; } } // Conversionスクリプトを返す public Conversation GetConversation() { return conversation; } |
SetStateメソッドの第2引数にTransform型の変数を受け取るようにします。
第2引数が指定されていなければnullを設定します。
State.Walk状態に変更する時にnavMeshAgent.isStopped = false;でエージェントの移動を再開させます。
State.Talk状態の時はnavMeshAgent.isStopped = true;でエージェントの移動を止めます。
会話する時はアニメーションをやめさせるためアニメーションパラメータのSpeedを0にしています。
またフィールドのconversationPartnerTransformに引数で受け取ったユニティちゃんのTransofrmを入れて保持します。
VillagerゲームオブジェクトのVillagerScriptの設定は
上のようになりました。
村人の会話内容ファイルを作成する
村人の会話内容はスクリプト中のstringフィールドで各自保持しておくことも出来ますが、今回はScriptableObjectを使ってあらかじめアセットファイルとして会話内容を作っておき、それを入れ替えるだけにします。
ScriptableObjectやあらかじめアセットファイルを作っておく方法は
や
も参照してください。
Assets/RPG/Scriptsフォルダ内で右クリックからCreate→C# Scriptを選択し、名前をConversationとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [Serializable] [CreateAssetMenu(fileName = "Conversation", menuName = "CreateConversation")] public class Conversation : ScriptableObject { // 会話内容 [SerializeField] [Multiline(100)] private string message = null; // 会話内容を返す public string GetConversationMessage() { return message; } } |
Conversationはただ単に会話内容をmessageフィールドで保持して置くだけのスクリプトです。
CreateAssetMenuアトリビュートを取り付けたことでUnityメニューのAssetsからこのConversationファイルを作成することが出来るようになりました。
fileNameは作成されるファイル名で、menuNameはAssets以下で表示される項目の名前です。
Assets/RPGフォルダ内で右クリックしCreate→Folderで名前をDataとしその中で右クリックからCreate→Folderで名前をConversationとします。
Conversationフォルダ内で右クリックしCreate→Folderで名前をFirstVillageとします。
FirstVillageフォルダ内で右クリックからCreate→CreateConversationを選択するか、
UnityメニューのAssetsからCreate→CreateConversationを選択するとConversationファイルを作成できます。
どちらかの方法でファイルを作成するとConversationというファイルが出来るので選択した状態でF2キーを押して名前をVillagerAとし、Ctrl+Dキーをおして複製し名前をVillagerBとします。
VillagerAとVillagerBのインスペクタでそれぞれ会話内容を記述します。
上のようにあらかじめ会話内容を書いたファイルが出来ました。
会話内容は<>という文字列が登場したら一回で表示する会話が終了するようにしているので途中に<>を入れる必要があります。
ここで注意が必要なのが元のConversationスクリプトのmessageフィールドの名前を変更したりすると、せっかく作成したVillagerAやVillagerBの中身が消えてしまうので注意してください。
これが嫌な人はテキストファイル等に会話内容を保持しておいて読み出すという方法もあります。
こっちの方が安全かも・・・・(^_^;)
会話内容は別途パソコンにテキストファイルとして保持したり、紙に残しておいた方が消えた時の悲しみが少なくなるかもしれません・・・・(´Д`)
決してUnity内だけで保持するのはやめましょう。(´Д`)
これで機能が完成したので、VillagerをCtrl+Dキーを押して複製しVillagerとは少し位置を変えて配置します。
ちなみにここまでのヒエラルキーは
上のようになっています。
Unityを実行して機能を試してみましょう。
上のようになりました。
巡回ポイントが同じなので村人が群がっていますね。
むらびとがむらがっている・・・・(´Д`)
終わりに
いやぁ・・・この会話機能の記事長いですね・・・(^_^;)
この記事からいきなり難しくなってるかも・・・・(´Д`)
だが、本当の闘いはこれからなのであった・・・・・(-_-)
この作品はユニティちゃんライセンス条項の元に提供されています