今回はキャラクターの移動やカメラの視点の変更や回転、キャラクターへのズームイン・ズームアウト、の操作をすべてマウス操作でするにはどうしたらいいですか?
という問い合わせを頂きましたので、その為の機能を作成していきたいと思います。
さらにカメラが壁と被った時の対策、キャラクターとの会話、敵を攻撃する機能も取りつけました。
操作方法としては、
- キャラクターの移動は地面をマウスの左ボタンを押したらそこに移動するようにする
- カメラの視点の変更や回転はマウスの右ボタンを押した状態でドラッグした時に回転するようにする
- カメラがキャラクターをズームイン・ズームアウトする機能はマウスのホイールをスクロールした時に行う
- カメラとキャラクターとの間に壁等の障害物が来た時はカメラを壁の位置に動かす
- 他のキャラクターをマウスの左ボタンで押したら会話する
- 敵キャラクターをマウスの左ボタンで押したら攻撃する
という感じで作成します。
マウスでの移動に関しては
の機能を使い、さらに移動ポイントにエフェクトを表示するようにしてみます。
この移動機能はナビゲーション機能を使用して移動するわけではないので障害物があった場合ずっと障害物にぶつかった状態で止まってしまいます。
もしそれが嫌な方はナビゲーションを使った移動に変更するといいと思います。
MouseMoveスクリプトに移動ポイントを表示する処理を追加
ではMouseMoveスクリプトに変更を加えたスクリプトを作成していきます。
名前はMouseCharacterに変更しました。
スクリプトが重複するので追加する部分だけを記述していきます。
1 2 3 4 5 6 7 | // クリックした位置に表示するパーティクルのプレハブ [SerializeField] private GameObject clickPointEffectPrefab; // 表示しているクリックパーティクル private GameObject clickPointEffectInstance; |
まずはフィールド宣言ですね。
clickPointEffectPrefabは地面をクリックした時にその位置に表示するパーティクルゲームオブジェクトのプレハブを設定します。
インスペクタで設定出来るようにしておきます。
clickPointEffectInstanceはパーティクルをインスタンス化したものを入れておくフィールドです。
キャラクターがクリックした位置に到着または別の地点をクリックした場合に消す必要がある為、フィールドに入れておきます。
クリック時に毎回インスタンス化するのが嫌な方はあらかじめゲーム内に非表示にしたパーティクルを用意しておき、クリックされた時にその地点に移動させ表示状態に変更するというのでもいいかもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void Update () { if (characterController.isGrounded) { velocity = Vector3.zero; // マウスクリックまたはmouseDownModeがOffの時マウスの位置を移動する位置にする if (Input.GetButton ("Fire1") || !isMouseDownMode) { Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition); RaycastHit hit; if (Physics.Raycast (ray, out hit, rayRange)) { targetPosition = hit.point; if (clickPointEffectInstance) { Destroy (clickPointEffectInstance); } clickPointEffectInstance = Instantiate<GameObject>(clickPointEffectPrefab, targetPosition, Quaternion.FromToRotation(Vector3.up, hit.normal)); } } } } |
↑が新しい処理を追加した部分でキャラクターが地面に接地している時の処理です。
クリックされたら、すでにclickPointEffectInstanceがあった場合はそれを削除します。
その後clickPointEffectPrefabをインスタンス化してclickPointEffectInstanceフィールドに参照を入れています。
clickPointEffectPrefabはインスタンス化した際にVector3.up(上向き)からtargetPosition(クリックした地点)の面の角度(hit.normal)の間の角度を指定します。
何をやっているかというとこれでパーティクルの角度をクリックした面の角度と同じにしています。
例えば坂をクリックした場合はパーティクルの角度が坂と同じになります。
パーティクルを地面と同じ角度にしない場合はQuaternion.FromToRotationメソッドを呼び出している所をQuaternion.identityに変更します。
Quaternion.identityは親の角度と同じになります。
親がない場合はゲームの世界の初期値の角度になると思われます。
1 2 3 4 5 6 7 8 9 | // 目的地に近付いたら走るアニメーションをやめる } else { animator.SetFloat ("Speed", 0f); if (clickPointEffectInstance) { Destroy (clickPointEffectInstance); } } |
あとは目的地に着いた時に移動ポイントにパーティクルが表示されていたらそのパーティクルを削除します。
こうすることで移動地点を変更した時に元の移動地点に表示されているパーティクルを削除する事が出来ます。
これでキャラクターの移動は完成です。
カメラの回転、ズームイン・ズームアウト、壁対策の機能を作成する
さて、次はカメラの回転、ズームイン・ズームアウト、壁対策の機能を作成していきます。
カメラの位置は『キャラクターの位置』に『マウスのドラッグによって回転した縦横の角度 × キャラクターからの距離』を足して求めます。
つまり
カメラ位置 = キャラクター位置 + (カメラの回転 × キャラクターからの距離)
ですね。
ちょっと解り辛いかもしれませんが、
例えば水の入ったバケツを持った人間が水が落ちないように回転させているとします。
バケツの回転は完全に縦に回転させたり多少斜めに回転させたりしています。
ここで言う人間がキャラクター、人間からみたバケツの角度がカメラの回転、人間からバケツまでの距離がキャラクターからの距離となります。
つまり
バケツ位置 = 人間の位置 + (人間からみたバケツの角度 × 人間からバケツまでの距離)
となります。
少しわかっていただけたでしょうか?(^_^;)
これでカメラの位置が決定出来るので、マウスドラッグ開始点から移動した距離によって角度を計算出来ればOKです。
また、マウスホイールのスクロールでキャラクターのズームイン・ズームアウトをするには『カメラの位置』にマウススクロール値を足せばよさそうです。
この点を踏まえてMouseRotateスクリプトを作成します。
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 System.Collections; public class MouseRotateScript : MonoBehaviour { // プレイヤー位置 [SerializeField] private Transform player; // カメラの回転と位置 private Vector3 cameraPos; // マウスを横にドラッグした時 private float mouseRotateX; // マウスを縦にドラッグした時 private float mouseRotateY; // カメラの回転スピード [SerializeField] private float cameraRotateSpeed = 360f; // 下へ回転する限界角度 [SerializeField] private float minRotateX = -30f; // 上へ回転する限界角度 [SerializeField] private float maxRotateX = 10f; // マウスの右ボタンを押してドラッグしているかどうか private bool mouseMove; // マウスのドラッグ開始点 private Vector2 mouseStartPosition = Vector2.zero; // カメラの初期位置 [SerializeField] private Vector3 baseDistance = new Vector3(0f, 1f, 2f); // キャラクターとの一番近い距離(baseDistanceからの距離) [SerializeField] private float minScrollValue = 0f; // キャラクターとの一番遠い距離(baseDistanceからの距離) [SerializeField] private float maxScrollValue = 5f; // スクロールのスピード [SerializeField] private float scrollSpeed = 10f; // マウスのスクロール値 private float scrollValue = 0f; // マウスを前にドラッグした時カメラを上に回転する場合false、手前にドラッグしカメラを上に回転する場合true [SerializeField] private bool rotateDirection = false; // Use this for initialization void Start () { baseDistance = transform.position; } // Update is called once per frame void Update () { // マウスの右ボタンドラッグ開始 if(Input.GetButtonDown("Fire2")) { mouseMove = true; mouseStartPosition = Input.mousePosition; // ドラッグの終了 } else if(Input.GetButtonUp("Fire2")) { mouseMove = false; } // マウスのホイールをスクロールした時 if(!Mathf.Approximately(Input.GetAxis("Mouse ScrollWheel"), 0f)) { scrollValue += Input.mouseScrollDelta.y * scrollSpeed * Time.deltaTime; // スクロールできる距離を制限する。 scrollValue = Mathf.Clamp(scrollValue, -maxScrollValue, -minScrollValue); } // マウスをドラッグ中である if(mouseMove) { Debug.Log ("ドラッグ中"); // カメラの上下の回転を反転する為の処理 var direction = 1; if(rotateDirection) { direction = -1; } // カメラの回転を計算 mouseRotateY += (Input.mousePosition.x - mouseStartPosition.x) / Screen.width * cameraRotateSpeed * Time.deltaTime; mouseRotateY = Mathf.Repeat(mouseRotateY, 360f); mouseRotateX += direction * (Input.mousePosition.y - mouseStartPosition.y) / Screen.height * cameraRotateSpeed * Time.deltaTime; mouseRotateX = Mathf.Clamp(mouseRotateX, minRotateX, maxRotateX); } // カメラの回転と位置を決める cameraPos = Quaternion.Euler(mouseRotateX, mouseRotateY, 0f) * baseDistance; } void LateUpdate() { // カメラの位置をキャラクターの位置に計算した回転と距離を足した位置に移動させる transform.position = player.position + cameraPos + transform.forward * scrollValue; // カメラがキャラクターの方を向くようにする transform.LookAt(player.position); RaycastHit hit; // カメラが壁と被ったら壁の位置に移動させる if(Physics.Linecast(player.position, transform.position, out hit)) { if(hit.collider.tag != "Player") { transform.position = hit.point; } } } } |
MouseRotateスクリプトの全文は↑のようになります。
MouseRotateスクリプトの解説
少しづつ処理を見ていきましょう。
1 2 3 4 5 6 7 8 9 10 | // マウスの右ボタンドラッグ開始 if(Input.GetButtonDown("Fire2")) { mouseMove = true; mouseStartPosition = Input.mousePosition; // ドラッグの終了 } else if(Input.GetButtonUp("Fire2")) { mouseMove = false; } |
Updateメソッド内でまずは右ボタンを押してドラッグしている状態かどうかを判定しています。
マウスの右ボタンが押されていたらmouseMoveをtrueにします。
またドラッグ開始点を設定します。
マウスの現在の位置はInput.mousePositionでVector2の値として得られます。
右ボタンが離された時はmouseMoveにfalseを入れています。
1 2 3 4 5 6 7 8 | // マウスのホイールをスクロールした時 if(!Mathf.Approximately(Input.GetAxis("Mouse ScrollWheel"), 0f)) { scrollValue += Input.mouseScrollDelta.y * scrollSpeed * Time.deltaTime; // スクロールできる距離を制限する。 scrollValue = Mathf.Clamp(scrollValue, -maxScrollValue, -minScrollValue); } |
次はマウスホイールをスクロールした時にキャラクターをズームイン・ズームアウトさせる為の処理です。
Input.GetAxis(“Mouse ScrollWheel”)でマウスをスクロールした時の値が得られるので、移動があったらInput.mouseScrollDelta.yで移動値を求めscrollValueに入れます。
scrollValueはインスペクタで設定したズームインとズームアウトの制限内の値が得られるようにMathf.Clampを使用しています。
Mathf.Clampは第1引数の値を第2引数の最小値と第3引数の最大値の間の値にする事が出来ます。
maxScrollValueとminScrollValueの位置がおかしいような気がしますが、これはキャラクターからの距離がマイナスの値となる為に
カメラがキャラクターと一番離れた時が最小値になり、一番近い時が最大値となる為に変数名と引数の位置に違和感が生じているだけで問題はありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // マウスをドラッグ中である if(mouseMove) { Debug.Log ("ドラッグ中"); // カメラの上下の回転を反転する為の処理 var direction = 1; if(rotateDirection) { direction = -1; } // カメラの回転を計算 mouseRotateY += (Input.mousePosition.x - mouseStartPosition.x) / Screen.width * cameraRotateSpeed * Time.deltaTime; mouseRotateY = Mathf.Repeat(mouseRotateY, 360f); mouseRotateX += direction * (Input.mousePosition.y - mouseStartPosition.y) / Screen.height * cameraRotateSpeed * Time.deltaTime; mouseRotateX = Mathf.Clamp(mouseRotateX, minRotateX, maxRotateX); } // カメラの回転と位置を決める cameraPos = Quaternion.Euler(mouseRotateX, mouseRotateY, 0f) * baseDistance; |
マウスのドラッグ中であればカメラの回転と位置を計算する為の処理を行います。
まずはカメラの上限の回転の設定(rotateDirection)によって回転する方向を変更します。
通常であればマウスを前に押すとカメラが上方向に回転しますが、逆にしたい場合はdirectionの値を-1にしてかけて
マウスを前に押した時カメラが下方向に回転するようにしています。
回転方向の設定であるrotateDirectionはインスペクタで切り替えが出来るようにしておきます。
ゲームのユーザーがこの設定を切り替えられるようにすると便利かもしれませんね!
カメラの回転は『元のカメラの回転値』に『現在のマウス位置 – ドラッグ開始点のマウス位置』を『スクリーンのサイズ』で割って『回転のスピード』をかけたものを足します。
マウスの移動値はピクセル値が返ってくるのでスクリーンサイズで割る事にします。
そこにインスペクタで回転スピード(rotateSpeed)を設定出来るようにして、回転の速さを調整出来るようにします。
mouseRotateYはマウスの横方向の移動なので360を超えた時は0に戻るようMathf.Repeatで0~360の間になるようにします。
mouseRotateXはdirectionをかけてマウスの方向によって回転する方向を変えています。
また、Mathf.Clampによって上方向、下方向に回転出来る制限を加えています。
この制限値もインスペクタで設定出来るようにしておきます。
これでカメラの回転が計算出来たので、最後にカメラの角度とキャラクターからの距離を計算しcameraPos変数に入れておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void LateUpdate() { // カメラの位置をキャラクターの位置に計算した回転と距離を足した位置に移動させる transform.position = player.position + cameraPos + transform.forward * scrollValue; // カメラがキャラクターの方を向くようにする transform.LookAt(player.position); RaycastHit hit; // カメラが壁と被ったら壁の位置に移動させる if(Physics.Linecast(player.position, transform.position, out hit)) { if(hit.collider.tag != "Player") { transform.position = hit.point; } } } |
LateUpdateメソッドでキャラクターの位置にcameraPosの値を足した位置+マウスのスクロールをした距離にカメラの位置を動かします。
スクロールした位置はマウスの向いている方向transform.forwardにスクロール値をかけて計算します。
動かした後transform.LookAtでカメラがキャラクターを向くようにしています。
カメラが壁で遮られキャラクターが見えなくなった時の為にPhysics.Linecastを使ってキャラクターの位置からカメラの位置までレイを送り、
衝突したコライダに設定されているタグがPlayer以外の時はカメラの位置を衝突した位置に設定します。
ここでPlayer以外としているのはキャラクター自身のコライダとの接触を無視する為です。
なぜLateUpdateメソッドでカメラの回転・位置を変更しているかというとLateUpdateはUpdateが実行された後に実行されるメソッドだからです。
Updateメソッドと同じようにフレーム毎に呼び出されるけれどUpdateより後に実行されます。
位置や回転をそのままUpdateでしてもいいんですが、ものによっては同フレームで位置や回転を変更しようとしても反映されない事があります(ありました)。
なので今回はLateUpdateメソッドで位置等を変更するようにしました。
クリックした相手によって行動を変更する
ここまででクリックした位置にキャラクターを移動させる事は出来ましたが、クリックした相手が村人だったら会話メッセージを表示させ、敵だったら攻撃出来るようにしてみます。
会話出来るキャラクターと敵キャラクターの作成
まずは会話出来るキャラクターと敵キャラクターを作成しましょう。
↑の色つきのキャラクターが会話出来る人でタグにHumanを設定します。
黒っぽいEthanを敵キャラクターとしタグにEnemy2を設定します(2としてるのはわたくしの都合上・・・)。
それぞれのキャラクターにはコライダが設定されている必要がありますので必ず確認してください。
色つきのキャラクターの子要素に会話メッセージ表示UIを作ります。
↑のように会話出来るキャラクターの子要素にCanvasを作成し、名前をTalkにします。
その子要素にPanel、Textを作成します。
TalkキャンバスのCameraのRencerModeをWorld Spaceにし、Scaleを調整とキャラクターの頭上に表示されるように位置を調整します。
RotateTalkUIスクリプトは
1 2 3 4 5 6 7 8 9 10 11 | using UnityEngine; using System.Collections; public class RotateTalkUI : MonoBehaviour { void Update () { transform.rotation = Camera.main.transform.rotation; } } |
↑のようにUIが常にカメラの向きを向くようにしているだけです。
これを作成しTalkに設定してください。
Textに表示するメッセージを書き、Font Sizeを変更して丁度いい大きさに調整します。
会話メッセージは↑のように表示されるようになります。
TalkキャンバスはHuman(会話キャラクター本体の名前)に設定したTalkスクリプトから操作し表示のオン・オフをするので、最初はオフにしておきます。
Talkキャンバスを選択しインスペクタでチェックを外しTalkをオフにしておいてください。
会話用UIの表示をオン・オフするTalkスクリプトの作成
次にHumanに設定するTalkScriptスクリプトを作成します。
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 | using UnityEngine; using System.Collections; public class TalkScript : MonoBehaviour { // キャラクターが近くにいるかどうか private bool flag = false; // 会話文表示用UI private GameObject talkUI; // キャラクターの位置情報 [SerializeField] private Transform player; void Start() { talkUI = transform.Find ("Talk").gameObject; } // メッセージ表示用UIを表示する public void Talk() { talkUI.SetActive(true); flag = true; } void Update() { // 会話フラグがオンの時だけ if(flag) { if(Vector3.Distance(transform.position, player.position) > 1.5f) { talkUI.SetActive(false); flag = false; } } } } |
操作キャラクターがHumanキャラクターをクリックした時にTalkスクリプトのTalkメソッドを呼び出すようにしますので、TalkメソッドではUIをオンにし表示させています。
Updateメソッドでキャラクターとの距離を計算し、キャラクターが離れて行った時にUIをオフにします。
操作キャラクターはインスペクタで設定します。
これで会話キャラクターの準備が出来ました。
敵キャラクターは攻撃されるだけなので特に設定はしません。
キャラクター移動スクリプトMouseCharacterの修正
それではキャラクターの移動スクリプトMouseCharacterを修正していきます。
まずは状態変数を宣言します。
1 2 3 4 5 6 7 8 | public enum CharaState { Idle, Move, Talk, Attack }; |
この状態でキャラクターが今何をしようとしているかの判断をします。
次は新しくフィールドを宣言します。
1 2 3 4 5 6 7 8 9 10 11 | [SerializeField] private GameObject talkEffectPrefab; [SerializeField] private GameObject attackEffectPrefab; // クリックしたキャラクターを入れる private GameObject human; // クリックしたターゲットとの距離 private float targetDistance; private CharaState state; |
クリックした相手が何だったか?によって表示するパーティクルを変更してみます。
パーティクルはインスペクタで設定出来るようにします。
また状態を入れておくstate、会話キャラクターに設定したTalkスクリプトを呼び出す為にそのキャラクターを入れておくhuman、
移動と会話、攻撃によってターゲットとの距離を変更する為にtargetDistance、到着したかどうかのarrivedを宣言します。
1 2 3 | state = CharaState.Idle; |
Startメソッドには↑のように初期化処理を追加します。
次はマウスの左クリックがされた時の処理部分を修正していきます。
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 | // マウスクリックまたはmouseDownModeがOffの時マウスの位置を移動する位置にする if (Input.GetButton ("Fire1") || !mouseDownMode) { Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition); RaycastHit hit; if (Physics.Raycast (ray, out hit, rayRange)) { targetPosition = hit.point; if (clickPointEffectInstance) { Destroy (clickPointEffectInstance); } // クリックした相手がHumanタグを設定されたキャラクターだった if(hit.collider.tag == "Human") { state = CharaState.Talk; human = hit.collider.gameObject; targetDistance = 1f; clickPointEffectInstance = Instantiate<GameObject>(talkEffectPrefab, targetPosition + Vector3.up, Quaternion.identity); // クリックした相手がEnemyタグを設定されたキャラクターだった } else if(hit.collider.tag == "Enemy") { state = CharaState.Attack; targetDistance = 1f; clickPointEffectInstance = Instantiate<GameObject>(attackEffectPrefab, targetPosition + Vector3.up, Quaternion.identity); // それ以外の時は移動(本当は地面にGround等のタグを設定した方がよい) } else { state = CharaState.Move; targetDistance = 0.1f; clickPointEffectInstance = Instantiate<GameObject>(clickPointEffectPrefab, targetPosition, Quaternion.FromToRotation(Vector3.up, hit.normal)); } } } |
マウスクリックされたら状態を初期状態に変更し、arrivedをfalseにします。
Physics.Raycastで得られた情報から相手方のコライダのゲームオブジェクトに設定されているタグを取得し何をクリックしたかで処理を分けます。
クリックした位置は一律でtargetPositionに入れておきます。
タグによって状態stateにそれぞれの状態を入れ、会話キャラクターだった時はhumanにそのキャラクターのゲームオブジェクトを入れておきます。
targetDistanceは移動の時は距離を短くし、会話キャラクターや敵キャラクターの時はキャラクターの幅も考えて距離を設定します。
表示するエフェクトは会話用と攻撃用はターゲット位置より1m上に表示するようにしました。
次は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 37 38 39 40 | // Idle状態でない時にターゲットとの距離を計算 if(state != CharaState.Idle) { // 移動の目的地と0.1mより距離がある時は速度を計算 if(Vector2.Distance(new Vector2(transform.position.x, transform.position.z), new Vector2(targetPosition.x, targetPosition.z)) > targetDistance) { var moveDirection = (targetPosition - transform.position).normalized; velocity = new Vector3(moveDirection.x * moveSpeed, velocity.y, moveDirection.z * moveSpeed); // スムースモードの時は徐々にキャラクターの向きを変更する if(smoothRotateMode) { transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(new Vector3(moveDirection.x, 0f, moveDirection.z)), smoothRotateSpeed * Time.deltaTime); // スムースモードでなければ一気に目的地の方向を向かせる } else { transform.LookAt(transform.position + new Vector3(moveDirection.x, 0f, moveDirection.z)); } // アニメーションパラメータの設定 animator.SetFloat("Speed", 1f); // 目的地に近付いたら走るアニメーションをやめる } else { animator.SetFloat("Speed", 0f); if(clickPointEffectInstance) { Destroy(clickPointEffectInstance); } // 状態が移動状態 if(state == CharaState.Move) { // 状態が話す } else if(state == CharaState.Talk) { human.GetComponent<TalkScript>().Talk(); // 状態が攻撃 } else if(state == CharaState.Attack) { // 攻撃前に敵の方向を向かせる。Y座標は自分のY座標にしないと変な方向を向いてしまう transform.LookAt(new Vector3(targetPosition.x, transform.position.y, targetPosition.z)); animator.SetTrigger("Attack"); } // 状態をリセットする state = CharaState.Idle; } } |
少しづつ見ていきます。
1 2 3 | Vector2.Distance(new Vector2(transform.position.x, transform.position.z), new Vector2(targetPosition.x, targetPosition.z)) |
を使って高さを無視した距離で操作キャラクターとクリックしたターゲットの距離を求めるようにしました。
到着したらキャラクターの状態によって処理分けします。
状態がMoveの時は何もしていません。
Talkの時はhuman変数に保存されたゲームオブジェクト(Human)のTalkScriptスクリプトを取得しTalkメソッドを呼び出します。
Attackの時は敵を攻撃する為にanimatorControllerのアニメーションパラメータAttackをオンにしています。
キャラクターに設定したAnimatorControllerで攻撃の状態を作りアニメーションパラメータのAttackがオンになることで遷移するように作成している必要があります。
これでスクリプトの修正も終わりました。
↑のようにインスペクタでtalkEffectPrefab、attackEffectPrefabを設定しましょう(好きなエフェクトやゲームオブジェクトを設定してください)。
マウス操作だけでキャラの移動、カメラの回転、ズームイン・ズームアウトが出来るか確認する
これでカメラの回転とズームイン・ズームアウトの機能が出来上がりました。
インスペクタで設定値を調整し、試してみましょう。
↑のようにパラメータを設定しました。
操作キャラクターの子要素に空オブジェクトを作り名前をLookAtPointとしてキャラクターの頭の辺りに移動させます。
そこをPlayerPositionに設定しました。
Ethanをそのまま設定してもいいんですが、Ethanは足元が基点となっている為、マウスのスクロールをして近づけると足元に近付いてしまいます。
なので、別のポイントを作成しそこを設定しました。
BaseDistanceにはキャラクターの初期位置を設定しておきます。
またbaseDistanceの距離よりもマウススクロールで近くに寄れるようにしてしまうと不具合の原因になります。
baseDistanceとminScrollValueの設定に気を付けてください。
↑のようにマウスの左ボタンを押した位置にパーティクルが表示されそこにキャラクターが移動しています。
また移動中に別の場所を押した場合は元のパーティクルが消え新しい移動ポイントにパーティクルが表示されるようになっています。
マウスホイールのスクロールでキャラクターのズームイン・ズームアウト、マウスの右ボタンを押したままドラッグするとカメラが回転するようになっています。
カメラとキャラクターの間に壁があったら壁の位置にカメラが移動しています。
また左ボタンを押して何らかのコライダと接触した場合にそこにキャラクターを移動するようになっている為、そこが地面でなくてもパーティクルの表示と移動が行われてしまいます。
会話キャラクターをクリックした時はキャラクターの頭上に会話用UIが表示され、敵をクリックした時は攻撃をしていますね。
他のキャラクターの頭上に表示するゲームオブジェクトに2Dのものを指定した時に回転させる処理や、一度他のキャラクターに近づいた後にさらにそのキャラクターをクリックすると
頭上のゲームオブジェクトが表示されないなど細かい問題点は残ってますね。
MouseRotateのカスタマイズ
移動場所を地面に限定したい場合は移動出来る地点のゲームオブジェクトのレイヤーをGround等の名前をつけておいてPhysic.Raycastで判定する時にGroundレイヤー
を指定しておくとこの対処が可能になります。
パーティクルはターゲットの位置の1m上に表示するようにしていますが、これだと足元をクリックしたのと頭をクリックしたのではパーティクルの位置が変わってきます。
そこら辺を考慮してパーティクルの出現位置を設定するといいかもしれませんね。
これでUnityのゲームで使用するキャラクターの移動やカメラの回転、カメラのズーム、カメラの壁対策、攻撃や会話をすべてマウス操作で行えるようになりました。
カメラの回転の所がうまくいかず苦労しましたが・・・(^_^;)
ある程度機能は出来ているのではないかと思います。
最初に記事をアップした時から文字数が2.5倍ぐらいになりましたよ・・・(^_^;)
ぜひ活用してみてください。(^^)/