前回のChapter8ではチャットの導入を行いました。
しかし現在のチャットのログはUI上にしか表示されず、特に人が増えていった場合、どのプレイヤーがどの発言をしているかがゲーム画面上ではとてもわかりにくい状態となっています。
そのため今回はそのチャットを発言したキャラクターの頭上に一定時間表示するようにします。
チャットの頭上表示
チャットの頭上表示をするためには自分がChatのUIに入力した文字列を自分のキャラクターの頭上に表示しなければなりません。
つまりチャットの頭上表示にはChatManager→PlayerPrefab→PlayerUIという3つのオブジェクトを文字列データが流れるように変更を加える必要があります。
では以下の手順で変更を加えていきましょう。
- InRoomChat.csに以下のように一部コードを追加
- プレイヤーPrefabにアタッチしているPlayerManager.csを以下のように一部コードを追加
- PlayerUIPrefabにアタッチしているPlayerUIScript.csを以下のように一部コードを追加
- キャラクターのHPや名前を表示しているPlayerUIのPrefabにチャット用Textオブジェクトを追加し、設定
※追加したコードの行の背景には色が付いていますので参考にしてください。
InRoomChat.csの変更後ソースコード
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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
using System.Collections.Generic; using UnityEngine; using System.Collections; [RequireComponent(typeof(PhotonView))] public class InRoomChat : Photon.MonoBehaviour { #region 変数宣言 //範囲チャット実装のためのオブジェクト、変数定義 GameObject[] players; //全てのプレイヤーキャラ取得用 GameObject sender; //送信キャラ取得用 GameObject myPlayer; //自分のキャラ取得用 PlayerManager myPM; //自分のキャラのPlayerManager取得用 GUIStyle ChatStyle = new GUIStyle(); //範囲チャットStyle GUIStyleState ChatStyleState = new GUIStyleState(); GUIStyle AllChatStyle = new GUIStyle(); //全体チャットStyle GUIStyleState AllChatStyleState = new GUIStyleState(); public Rect GuiRect = new Rect(0, 0, 300, 200); //チャットUIの大きさ設定用 public bool IsVisible = true; //チャットUI表示非表示フラグ public bool AlignBottom = true; public List<string> messages = new List<string>(); //チャットログ格納用List public List<bool> chatKind = new List<bool>(); //チャットログの種類格納用(範囲チャor全チャ) public string inputLine = "";//入力文章格納用String private Vector2 scrollPos = Vector2.zero; //スクロールバー位置 #endregion #region Start関数 Updata関数 public void Start() { //myPlayerオブジェクト取得(範囲チャット発言時にpositionとmyPM使う) GetmyPlayer(); //範囲チャットの場合は白文字にし、文字がUIからあふれた場合は折り返す設定 ChatStyleState.textColor = Color.white; ChatStyle.normal = ChatStyleState; ChatStyle.wordWrap = true; //全体チャットの場合は赤文字にし、文字がUIからあふれた場合は折り返す設定 AllChatStyleState.textColor = Color.red; AllChatStyle.normal = AllChatStyleState; AllChatStyle.wordWrap = true; } public void Update() { //ChatUIの位置を調整 this.GuiRect.y = Screen.height - this.GuiRect.height; //ChatUIの大きさ調整 GuiRect.width = Screen.width / 3; GuiRect.height = Screen.height / 3; } #endregion #region OnGUI関数 public void OnGUI() { if (!this.IsVisible || !PhotonNetwork.inRoom) //表示フラグがOFFまたはphotonにつながっていないとき { //UI非表示 return; } //ChatUIの作成開始 //チャットUI生成 Begin&EndAreaでチャットUIの位置と大きさを設定 GUILayout.Window(0, GuiRect, ChatUIWindow, ""); //チャットUIウインドウを作成 //Enterを押すと if (Event.current.type == EventType.KeyDown && (Event.current.keyCode == KeyCode.KeypadEnter || Event.current.keyCode == KeyCode.Return)) { //チャット入力待ち状態にする GUI.FocusControl("ChatInput"); } } #endregion #region チャットUI生成 void ChatUIWindow(int windowID) { //FocusがチャットUIに乗ってるときにEnterを押すとチャット発言が実行される if (Event.current.type == EventType.KeyDown && (Event.current.keyCode == KeyCode.KeypadEnter || Event.current.keyCode == KeyCode.Return)) { if (!string.IsNullOrEmpty(this.inputLine)) //チャット入力欄がNullやEmptyでない場合 { //範囲チャット送信関数実行 SendChat(false); return; } } //垂直のコントロールグループ開始 GUILayout.BeginVertical(); //スクロールビュー開始位置 scrollPos = GUILayout.BeginScrollView(scrollPos); //チャットログ表示用フレキシブルスペース生成 GUILayout.FlexibleSpace(); //フレキシブルスペースにチャットログを表示 for (int i = 0; i <= messages.Count - 1; i++) { if (chatKind[i] != true) //範囲チャットであれば { GUILayout.Label(messages[i], ChatStyle); } else //全チャットであれば { GUILayout.Label(messages[i], AllChatStyle); } } //スクロールビュー終了 GUILayout.EndScrollView(); //水平のコントロールグループ開始 GUILayout.BeginHorizontal(); //入力テキストフィールド生成、Focusが乗った状態をChatInputと命名 GUI.SetNextControlName("ChatInput"); inputLine = GUILayout.TextField(inputLine, 200); //「Send」ボタンを生成かつ押したときには範囲チャット送信 if (GUILayout.Button("Send", GUILayout.ExpandWidth(false))) { //範囲チャット送信関数実行 SendChat(false); } //Allボタンを生成かつ押したときには全体チャット送信 if (GUILayout.Button("All", GUILayout.ExpandWidth(false))) { //全体チャット送信関数実行 SendChat(true); } //水平のコントロールグループ終了 GUILayout.EndHorizontal(); //垂直のコントロールグループ終了 GUILayout.EndVertical(); } #endregion #region GetmyPlayer 自キャラのオブジェクトをmyPlayerに登録 void GetmyPlayer() { //自キャラのID取得 int myPlayerID = PhotonNetwork.player.ID; //全てのプレイヤーオブジェクトを取得 players = GameObject.FindGameObjectsWithTag("Player"); //全てのプレイヤーオブジェクトから自キャラをIDで検索し、取り出す foreach (GameObject player in players) { int playerLoopId = player.GetComponent<PhotonView>().owner.ID; if (playerLoopId == myPlayerID) { //自プレイヤーオブジェクトを取得 myPlayer = player; //自キャラの頭上にチャット表示するためにPlayerManager取得 myPM = myPlayer.GetComponent<PlayerManager>(); } } return; } #endregion #region チャット送信関数 void SendChat(bool isAll) { //chatRPC this.photonView.RPC("Chat", PhotonTargets.All, myPlayer.transform.position, this.inputLine, isAll); //頭上にチャット表示するためにstring送信 myPM.setChat(this.inputLine); //送信後、入力欄を空にし、スクロール最下位置に移動 this.inputLine = ""; scrollPos.y = Mathf.Infinity; } #endregion #region ChatRPC RPC呼出側:送信者 RPC受信側:受信者 [PunRPC] public void Chat(Vector3 senderposition, string newLine, bool isAll, PhotonMessageInfo mi) { if (messages.Count >= 100) //チャットログが多くなって来たらログを削除してから受信 { messages.Clear(); //全てのチャットログを削除 chatKind.Clear(); //全てのチャットの種類情報削除 } if (!isAll) //範囲チャとして受信 { //myPlayerとsenderの距離から受信するか判断 if (Vector3.Distance(myPlayer.transform.position, senderposition) < 30) { //chat受信 ReceiveChat(newLine, isAll, mi); } } else if (isAll) //全チャとして受信 { //chat受信 ReceiveChat(newLine, isAll, mi); } //受信したときはスクロール最下位置 scrollPos.y = Mathf.Infinity; } #endregion #region チャット受信関数 void ReceiveChat(string _newLine, bool isAll, PhotonMessageInfo _mi) { //送信者の名前用変数 string senderName = "anonymous"; if (_mi.sender != null) { //送信者の名前があれば if (!string.IsNullOrEmpty(_mi.sender.NickName)) { senderName = _mi.sender.NickName; } else { senderName = "player " + _mi.sender.ID; } } //受信したチャットをログに追加 this.messages.Add(senderName + ": " + _newLine); this.chatKind.Add(isAll); return; } #endregion } |
PlayerManager.csの変更後ソースコード
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 |
using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Collections; using System; public class PlayerManager : Photon.PunBehaviour, IPunObservable { //頭上のUIのPrefab public GameObject PlayerUiPrefab; //現在のHP public int HP = 100; //Localのプレイヤーを設定 public static GameObject LocalPlayerInstance; //チャット同期用変数 public string ChatText = ""; private bool isRunning; Coroutine ChatCoroutine; //頭上UIオブジェクト GameObject _uiGo; #region プレイヤー初期設定 void Awake() { if (photonView.isMine) { PlayerManager.LocalPlayerInstance = this.gameObject; } } #endregion #region 頭上UIの生成 void Start() { if (PlayerUiPrefab != null) //PlayerUIPrefabが設定されていない時のエラー回避 { //Playerの頭上UIの生成とPlayerUIScriptでのSetTarget関数呼出 _uiGo = Instantiate(PlayerUiPrefab) as GameObject; _uiGo.SendMessage("SetTarget", this, SendMessageOptions.RequireReceiver); } else { Debug.LogWarning("<Color=Red><a>Missing</a></Color> PlayerUiPrefab reference on player Prefab.", this); } } #endregion void Update() { //このオブジェクトがLocalでなければ実行しない if (!photonView.isMine) { return; } //LocalVariablesを参照し、現在のHPを更新 HP = LocalVariables.currentHP; } #region 頭上Chatの表示 public void setChat(string inputLine) { //コルーチンが動作中であれば if (isRunning) { StopCoroutine(ChatCoroutine); //コルーチンを停止 ChatCoroutine = null; //削除 isRunning = false; } //頭上チャット用Stringに入力文字列を格納 ChatText = inputLine; //新しいコルーチンを生成 ChatCoroutine = StartCoroutine(_ChatText(6f)); } //頭上チャット表示用コルーチン IEnumerator _ChatText(float pausetime) { //コルーチン動作フラグON isRunning = true; //pausetimeの間、頭上チャットを表示し続ける(今は6秒に設定) yield return new WaitForSeconds(pausetime); //頭上チャットを非表示にする ChatText = ""; //コルーチン動作フラグOFF isRunning = false; } #endregion #region OnPhotonSerializeView同期 //プレイヤーのHP,チャットをPlayerUIScriptと同期 public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { if (stream.isWriting) //送信 { stream.SendNext(this.HP); stream.SendNext(this.ChatText); } else //受信 { this.HP = (int)stream.ReceiveNext(); this.ChatText = (string)stream.ReceiveNext(); } } #endregion } |
PlayerUIScript.csの変更後ソースコード
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 |
using UnityEngine; using UnityEngine.UI; using System.Collections; public class PlayerUIScript : MonoBehaviour { #region Public Properties //キャラの頭上に乗るように調整するためのOffset public Vector3 ScreenOffset = new Vector3(0f, 30f, 0f); //プレイヤー名前設定用Text public Text PlayerNameText; //プレイヤーのHP用Slider public Slider PlayerHPSlider; //プレイヤーのチャット用Text public Text ChatText; #endregion #region Private Properties //追従するキャラのPlayerManager情報 PlayerManager _target; float _characterControllerHeight; Transform _targetTransform; Vector3 _targetPosition; #endregion #region MonoBehaviour Messages void Awake() { //このオブジェクトはCanvasオブジェクトの子オブジェクトとして生成 this.GetComponent<Transform>().SetParent(GameObject.Find("Canvas").GetComponent<Transform>()); } void Update() { //もしPlayerがいなくなったらこのオブジェクトも削除 if (_target == null) { Destroy(this.gameObject); return; } // 現在のHPをSliderに適用 if (PlayerHPSlider != null) { PlayerHPSlider.value = _target.HP; } // 頭上チャットを表示 if (ChatText != null) { ChatText.text = _target.ChatText; } } void LateUpdate() { //targetのオブジェクトを追跡する if (_targetTransform != null) { _targetPosition = _targetTransform.position; //三次元空間上のtargetの座標を得る _targetPosition.y += _characterControllerHeight; //キャラクターの背の高さを考慮する //targetの座標から頭上UIの画面上の二次元座標を計算して移動させる this.transform.position = Camera.main.WorldToScreenPoint(_targetPosition) + ScreenOffset; } } #endregion #region PlayerUIが追跡するtargetから情報を設定する関数 public void SetTarget(PlayerManager target) { if (target == null)//targetがいなければエラーをConsoleに表示 { Debug.LogError("<Color=Red><a>Missing</a></Color> PlayMakerManager target for PlayerUI.SetTarget.", this); return; } //targetの情報をこのスクリプト内で使うのでコピー _target = target; _targetTransform = _target.GetComponent<Transform>(); //CharacterController取得 CharacterController _characterController = _target.GetComponent<CharacterController>(); //PlayerManagerの頭上UIに表示したいデータをコピー if (_characterController != null) { _characterControllerHeight = _characterController.height; } if (PlayerNameText != null) { PlayerNameText.text = _target.photonView.owner.NickName; //プレイヤー名 } if (PlayerHPSlider != null) { PlayerHPSlider.value = _target.HP; //HP } if (ChatText != null) { ChatText.text = _target.ChatText; //頭上チャットText } } #endregion } |
PlayerUIPrefabに頭上Chat表示用に子テキストオブジェクトとしてChatTextを追加
以下の画像のようにPlayerUIの子オブジェクトとしてテキストオブジェクトChatTextを生成します。
生成したChatTextの設定は以下のInspectorビューのようにしてください。
頭上チャットの文字のサイズや色やフォントはここで決めることが出来るのでお好みで設定しましょう。
今回は見やすいように白で縁取った黒太文字で表示しています。
最後に以下の画像のように生成したChatTextを親オブジェクトにアタッチされているPlayerUIScriptに設定します。
PlayerUIPrefabを保存後、チャットの頭上表示の設定は完了です。
実際に動かしてみましょう!
チャットが発言者の頭上に表示されましたね!!
これで誰が何を発言しているかが一目瞭然になりました!!
次回予告!!
次回はお待ちかねの攻撃を実装してみようと思います。
お楽しみに!!