今回はアクションゲーム等に登場するアクションイベントの一つでバーの上をスクロールするカーソルを特定の位置で止める機能の作成をしたいと思います。
カーソルのイベント発生地点に主人公が移動した時にはキャラクターを映していたカメラをイベント部分にフォーカスし、イベントが終わったらカメラを元の位置に戻す機能も取りつけます。
今回のカーソルのイベントは二通りの方法で作成します。
です。
元々はUIを使ったカーソルイベントだけ作成していたんですが、UIを動かすのは動作が遅くなりそうなので?
普通に3Dオブジェクトを動かすカーソルイベントの方法も作りました。
3Dオブジェクトを使った方では3Dのカーソルを動かすので立体になり正確な位置で止めたつもりがうまく止まっていなかったように見えてしまう事もあります。
イベント部分のカメラの写し方を変えると良さそうですが・・・・。
お好きな方法で作成してみてください。
UIを使ったカーソルイベント
まずはUIを使ったカーソルイベントを作成していきましょう。
カーソルイベントのオブジェクトを作成
ヒエラルキー上で右クリック→Create Emptyを選択し、名前をCursorEventに変更します。
その子要素に右クリック→3D Object→Cubeを選択し名前をWallにします。
↑のようにCubeのサイズを調整し、カーソルイベントを張り付ける壁を作成します。
次にCursorEventの子要素に右クリック→UI→Sliderを選択し、カーソルイベント用のスライダーUIを作成します。
UIを他のゲームオブジェクトと同じ空間に表示させる為CanvasのRender ModeをWorld Spaceに変更します。
さきほど作成した壁の大きさと合うようにScaleを調整します。
カーソルを動かすスクリプトChangeSliderValueを作成する
次にCanvasの子要素のSliderに新しいスクリプトChangeSliderValueを作成し取りつけます。
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 | using UnityEngine; using System.Collections; using UnityEngine.UI; public class ChangeSliderValue : MonoBehaviour { // メーターがスタートしているかどうか private bool startMeter; // メーターが動いている方向true:右、false:左 private bool moveDirection; // スライダー private Slider slider; // メーターを動かすスピード [SerializeField] private float speed = 1f; // 成功とする値の最小値 [SerializeField] private float targetMinValue = 60f; // 成功とする値の最大値 [SerializeField] private float targetMaxValue = 80f; [SerializeField] private GameObject player; void Start () { moveDirection = true; // メーターを右向きに動かす slider = GetComponent<Slider>(); slider.interactable = false; // スライダーをマウスで動かせないようにする startMeter = false; } void Update () { // メーターがスタートしている場合 if(startMeter) { // 右向きに動いている時 if(moveDirection) { slider.value += speed * Time.deltaTime; // 最大値を超えたら移動方向を変える if(slider.value >= slider.maxValue) { moveDirection = !moveDirection; } // 左向きに動いている時 } else { slider.value -= speed * Time.deltaTime; // 最小値を超えたら移動方向を変える if(slider.value <= slider.minValue) { moveDirection = !moveDirection; } } } } public void OnOffMeter() { startMeter = !startMeter; // slider.value = 0f; } // イベントが成功しているかどうか public bool Judge() { return targetMinValue <= slider.value && slider.value <= targetMaxValue; } } |
Startメソッド内でスライダーのinteractableをfalseにしています。
UIのスライダーはマウスで移動させる事が出来るのが本来の使い方なのでinteractableをfalseにしないとマウスでも移動出来てしまいます。
なのでStartメソッド内でinteractableをfalseにしてマウスでは動かせないようにしておきます。
(スクリプトではなくインスペクタのinteractableのチェックを外す事でも同じです)。
Updateメソッド内ではカーソルが動いている方向で処理を分け、スライダーの値を足したり引いたりしています。
カーソルの動くスピードはインスペクタで設定出来るようにし、Time.deltaTimeをかける事でパソコンの処理速度で変化がないようにします。
OnOffMeterメソッドはカーソルの移動をするかしないかを切り替えるメソッドです。
Judgeメソッドはカーソルが指定範囲内ならtrue、指定範囲外ならfalseを返す関数です。
指定範囲はインスペクタで設定出来るようにしています。
カーソルイベントが発生したらOnOffMeterメソッドを呼び出してカーソルを動かし、イベントが終了したらOnOffMeterメソッドを呼び出しカーソルを止めるという感じになります。
↑のようにChangeValueスクリプトをSliderに取り付け値を設定します。
成功範囲を作成する
次はさきほど設定した数値の成功範囲を視覚的にわかるように成功範囲部分を見た目でわかるようにします。
Sliderの子要素に右クリック→UI→Imageを作成します。
↑のようにサイズと位置を調整し、指定した数値の範囲と合うようにします。
このImageのサイズは親要素であるSliderの幅などに合わせて調整する必要があります。
動かすカーソルのImageを変更する
次は動かすカーソルのImageを変更します。
Sliderの子要素のHandle Slide Area→Handleを選択し、Source Imageを変更します。
↑のようにSource Imageをストップウォッチに変更しました。
カーソルイベントが発生する範囲を作成する
次はカーソルイベントが発生する範囲を作成し、キャラクターがその範囲に侵入したらイベントを起動させます。
CursorEventの子要素に右クリック→Create Emptyを選択し名前をSearchAreaとします。
インスペクタのAdd ComponentからPhysics→Box Colliderを追加しサイズを調整し、Is Triggerにチェックを入れ物理的に当たらないようにします。
MeterSearchCharaスクリプトは後で作成します。
これでイベント部分の作成が終了しました。
↑が実際に作成したイベント部分のゲームオブジェクト、UI、キャラクター検知エリアになります。
これでUIを使ったカーソルイベント部分が出来たので後は3Dオブジェクトと共通の機能部分を作成します。
以下の3Dオブジェクトを使ったカーソルイベントを飛ばして共通部分の機能を作成していってください。
3Dオブジェクトを使ったカーソルイベント
3Dオブジェクトを使ったカーソルイベントを作成していきます。
ヒエラルキー上で右クリック→Create Emptyを選択し名前をCursorEvent2にします。
↑が実際に作成するカーソルイベントの3Dオブジェクトの階層になります。
イベントの壁を作成
CursorEvent2の子要素に右クリック→3D Object→Cubeを選択し名前をWallにします。
サイズを調整し壁を作成します。
カーソルが移動する部分を作成
次にカーソルが移動する部分を作成します。
ヒエラルキーで右クリック→3D Object→Cubeを選択し名前をBarとします。
↑のようにサイズを調整しIs Triggerのチェックし物理的に当たらないようにします。
またBar用にBarColorというMaterialを作成し設定します。
Assetsフォルダで右クリック→Create→Materialを選択します。
↑のようにマテリアルが作成されるので名前をBarColorとします。
Albedoの右側の色選択部分をクリックし色を変更します。
色は青色にしてA(Alpha)を100にし、RenderModeをTransparentにして透明部分を反映させるようにします。
マテリアルが出来たらマテリアルをBarにドラッグ&ドロップします。
カーソルが止まったら成功とする範囲を作成
次はBarの子要素に右クリック→3D Object→Cubeを選択し名前をHitAreaとします。
↑のようにサイズを調整し、Box ColliderのIs Triggerのチェックをいれます。
HitArea用にもマテリアルを作成し設定してください(作り方はBarの時と同じです)。
カーソルが範囲内にあるかどうか判定するCheckValueスクリプトの作成
HitAreaにはCheckValueスクリプトを新しく作成し取りつけます。
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 | using UnityEngine; using System.Collections; public class CheckValue : MonoBehaviour { // カーソルが範囲内にあるかどうか private bool judge; // イベントが成功しているかどうか public bool Judge() { return judge; } void OnTriggerStay(Collider col) { if(col.tag == "Cursor") { judge = true; } } void OnTriggerExit(Collider col) { if(col.tag == "Cursor") { judge = false; } } } |
OnTriggerStayメソッドでHitArea内に侵入してきたのがCursorタグが設定されたゲームオブジェクトだった時にjudgeをtrueにし、
OnTriggerExitメソッドで範囲外に出たのがCursorタグが設定されたゲームオブジェクトだった時にjudgeをfalseにしています。
Judgeメソッドはjudge変数の値を返すだけです。
動かすカーソルの作成
次に動かすカーソルを作成します。
Barの子要素に右クリック→3D Object→Cubeを選択し名前をCursorとします。
Box ColliderのIs Triggerにチェックを入れます。
Cursor用にもマテリアルを作成し設定してください。
CursorはRigidbodyを使って動かすのでインスペクタのAdd ComponentからPhysics→Rigidbodyを取りつけます。
RigidbodyのIs kinematicにチェックを入れておきます。
カーソルを動かすMoveCursorスクリプトの作成
Cursorを動かすスクリプトMoveCursorを作成しましょう。
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 | using UnityEngine; using System.Collections; public class MoveCursor : MonoBehaviour { private Rigidbody rigid; // メーターがスタートしているかどうか private bool startMeter; // カーソルの移動スピード [SerializeField] private float speed; void Start () { rigid = GetComponent<Rigidbody>(); startMeter = false; } void FixedUpdate () { if(startMeter) { rigid.MovePosition(new Vector3(Mathf.Sin(Time.time * speed) * 0.5f, transform.position.y, transform.position.z)); } } public void OnOffMeter() { startMeter = !startMeter; } } |
動かすスピードはインスペクタで設定出来るようにしています。
RigidbodyのMovePositionメソッドを使ってCursorを移動させています。
Mathf.Sinを使って-1から1の値が切り替わるように引数にTime.time(Unity起動からの経過時間)を設定します。
Time.timeにspeedをかけることによって移動するスピードを変化させています。
カーソルを移動させるのはX軸だけなのでY、Z軸は元の位置をそのまま設定しています。
この処理で一つ問題になるのが、カーソルイベント発生時のカーソルの位置が固定にはならないところです。
X軸の位置をMathf.Sin(Time.time)で指定している為Unityの起動からの時間によって位置が変わる為です。
どうしても固定の初期位置にしなければならない時は自前でX軸の値を変更し、指定範囲外に来たら逆向きにカーソルを移動させる処理を記述するといいと思います。
↑がイベント部分をアップにした画像です。
Barの範囲内にCursorとHitAreaが含まれるように作成しており、CursorはBarの箱の中を移動するような感じになります。
CursorゲームオブジェクトにはCursorというタグを作成し取りつけておいてください。
カーソルイベントの検知エリア
次はカーソルイベントに突入するエリアを作成します。
CursorEvent2の子要素に右クリック→Create Emptyをし名前をSearchAreaとします。
インスペクタのAdd ComponentからPhysics→Box Colliderを追加しIs Triggerにチェックを入れます。
MeterSearchCharaスクリプトは後で作成します。
↑が実際に作成した3Dオブジェクトのカーソルイベント部分になります。
後はUIカーソルイベントと共通の部分を作成していきます。
共通部分のスクリプト作成
ここからはUI、3Dオブジェクトで作成したカーソルイベントで使う共通の部分を作成していきます。
それぞれで違うスクリプトを使っている所もあるので、一部変更する必要はあります。
キャラクター操作スクリプトEventCharacterMoveの作成
主人公操作スクリプトを作成していきます。
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 | using UnityEngine; using System.Collections; using UnityEngine.UI; public class EventCharacterMove : MonoBehaviour { public enum MyStateEvent { normal, // 通常時 meterEvent, // カーソルイベント中 waitEvent // イベント後にカメラが元に戻るまでの間 }; private Animator animator; private CharacterController cCon; private Vector3 velocity; private Vector3 input; [SerializeField] private float walkSpeed = 2f; private MyStateEvent state; // イベントの時は主人公を見えないようにする部位 [SerializeField] private GameObject[] chara; // カーソルイベントスクリプト [SerializeField] private ChangeSliderValue changeSliderValue; // カーソルイベントスクリプト [SerializeField] private CheckValue checkValue; // 自身を検知しているサーチエリアスクリプト [SerializeField] private MeterSearchChara meterSearchChara; [SerializeField] private Text text; // イベント中にカメラを動かすスクリプト [SerializeField] private MoveCamera moveCamera; // MoveCursorスクリプト [SerializeField] private MoveCursor moveCursor; void Start () { animator = GetComponent<Animator>(); cCon = GetComponent<CharacterController>(); velocity = Vector3.zero; state = MyStateEvent.normal; } void Update () { // 地面に接地してる時は初期化 if(cCon.isGrounded) { velocity = Vector3.zero; // ノーマル状態 if(state == MyStateEvent.normal) { input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); // 方向キーが多少押されている if(input.magnitude > 0f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += input.normalized * walkSpeed; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0f); } // カーソルイベント中 } else if(state == MyStateEvent.meterEvent) { if(Input.GetKeyDown("space")) { // if(changeSliderValue.Judge()) { if(checkValue.Judge()) { // changeSliderValue.OnOffMeter(); moveCursor.OnOffMeter(); moveCamera.SetReMoveFlag(true); SetState(MyStateEvent.waitEvent, true); meterSearchChara.SetFinish(); meterSearchChara.SetPlaying(false); text.text = "成功"; } else { // changeSliderValue.OnOffMeter(); moveCursor.OnOffMeter(); moveCamera.SetReMoveFlag(true); SetState(MyStateEvent.waitEvent, true); meterSearchChara.SetPlaying(false); text.text = "失敗"; } } } } velocity.y += Physics.gravity.y * Time.deltaTime; cCon.Move(velocity * Time.deltaTime); } // キャラクターの状態変更関数 public void SetState(MyStateEvent state, bool param) { // 状態を変更 this.state = state; if(state == MyStateEvent.normal) { } else if(state == MyStateEvent.meterEvent) { animator.SetFloat("Speed", 0f); } else if(state == MyStateEvent.waitEvent) { } // イベント時にキャラクターが見えないようにする foreach(var part in chara) { part.SetActive(param); } } public MyStateEvent GetState() { return state; } } |
主人公が通常時の状態とカーソルイベントの状態の時で処理わけをしています。
SetStateメソッド内ではイベントの状態になった時に主人公のアニメーションを立っている状態へと遷移させ、イベントから復帰後に他のアニメーションにならないようにします。
またイベント発生時にカメラがイベント部分にフォーカスする時、キャラクターが間に入ってしまうとイベント部分が見えなくなってしまうので、
イベント時には登録したキャラクターの体をすべて見えないようにしています(インスペクタで見えなくする体を設定する)。
↑のスクリプトは3Dオブジェクトを使ったカーソルイベント時のものでUIのカーソルイベントを使う場合はコメント化している部分を使い、UIで使っている部分を削除してください。
イベント時の主人公の処理を見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // カーソルイベント中 } else if(state == MyStateEvent.meterEvent) { if(Input.GetKeyDown("space")) { // if(changeSliderValue.Judge()) { if(checkValue.Judge()) { // changeSliderValue.OnOffMeter(); moveCursor.OnOffMeter(); moveCamera.SetReMoveFlag(true); SetState(MyStateEvent.waitEvent, true); meterSearchChara.SetFinish(); meterSearchChara.SetPlaying(false); text.text = "成功"; } else { // changeSliderValue.OnOffMeter(); moveCursor.OnOffMeter(); moveCamera.SetReMoveFlag(true); SetState(MyStateEvent.waitEvent, true); meterSearchChara.SetPlaying(false); text.text = "失敗"; } } } |
イベント時にSPACEキーを押してイベントが成功したか失敗したかで処理わけをしています。
成功時にはカーソルの動きを止めてカメラのフォーカスをイベント部分からキャラクターへと変更します。
またキャラクターの状態の変更、カーソルイベントの成功の通知、カーソルイベントの解除をします。
失敗時にはカーソルの動きを止めてカメラのフォーカスをキャラクターに戻し、
主人公の状態の変更、カーソルイベントの解除をするという流れになっています。
主人公キャラクターにはPlayerというタグを作成し取りつけておいてください。
キャラクター検知スクリプトMeterSearchCharaの作成
次にキャラクターの検知とイベントの終了を管理するMeterSearchCharaスクリプトを作成し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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | using UnityEngine; using System.Collections; public class MeterSearchChara : MonoBehaviour { // イベントが終了しているかどうか? private bool isFinish; // 現在イベント中かどうか? private bool isPlaying; // カメラを動かすスクリプト private MoveCamera moveCamera; void Start () { isFinish = false; isPlaying = false; moveCamera = Camera.main.GetComponent<MoveCamera>(); } // 主人公が範囲内に入ったかどうか調べる void OnTriggerEnter(Collider col) { // 侵入者が主人公でイベントが終了していない時 if(col.tag == "Player" && !isFinish && !isPlaying) { // カメラをイベント部分にフォーカスする moveCamera.SetFollowTarget(); moveCamera.SetMoveFlag(true); // キャラクターの状態を変更する col.gameObject.GetComponent<EventCharacterMove>().SetState(EventCharacterMove.MyStateEvent.meterEvent, false); isPlaying = true; } } // カーソルイベントのオン・オフ切り替え関数 public void SetPlaying(bool param) { isPlaying = param; } // カーソルイベントが成功した時に実行する関数 public void SetFinish() { isFinish = true; } } |
SearchAreaの範囲内にキャラクターが侵入した時にカメラをイベント部分にフォーカスしたりキャラクターの状態を変更したりしています。
イベントが発生するのは侵入者がキャラクターでこのカーソルイベントがすでに動いていない、イベントを成功していないという条件になります。
Main Cameraの設定
次にカメラの設定をしていきます。
カメラがキャラクターを追従するようにFollowTargetスクリプトを設定
キャラクターが通常の動きをしている(カーソルイベント中ではない)時はMain Cameraがキャラクターを追いかけて表示するようにします。
Main Cameraのインスペクタを表示し、Add ComponentからScripts→UnityStandardAsset.Utility→Follow Targetを追加します。
Targetには主人公キャラクターを指定し、主人公を写すカメラの位置をOffsetで指定します。
イベント部分にカメラをフォーカスするスクリプトMoveCamera
次にカーソルイベントが始まる前にキャラクターを写していたカメラをカーソルイベントにフォーカスするスクリプトMoveCameraを作成します。
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 | using UnityEngine; using System.Collections; using UnityStandardAssets.Utility; public class MoveCamera : MonoBehaviour { // カメラが移動しているかどうか private bool isMove; // カメラが元の位置に戻る private bool reMoveFlag; // デフォルトのカメラポジション private Vector3 defaultCameraPosition; // イベント時のカメラの位置 [SerializeField] private Vector3 cameraPosition = new Vector3(0f, 1f, 4f); // カメラの移動スピード [SerializeField] private float cameraSpeed = 2f; private FollowTarget followTarget; // キャラクター操作スクリプト [SerializeField] private EventCharacterMove eventCM; // メーターイベントスクリプト [SerializeField] private ChangeSliderValue changeSliderValue; // カーソル移動スクリプト [SerializeField] private MoveCursor moveCursor; void Start () { defaultCameraPosition = transform.position; isMove = false; reMoveFlag = false; followTarget = GetComponent<FollowTarget>(); } void Update () { // カメラをイベント部分にフォーカスする if(isMove) { transform.position = Vector3.Lerp(transform.position, cameraPosition, cameraSpeed * Time.deltaTime); if(Vector3.Distance(transform.position, cameraPosition) < 0.1f) { SetMoveFlag(false); // changeSliderValue.OnOffMeter(); moveCursor.OnOffMeter(); } // カメラを通常の位置に戻す } else if(reMoveFlag) { transform.position = Vector3.Lerp(transform.position, defaultCameraPosition, cameraSpeed * Time.deltaTime); if(Vector3.Distance(transform.position, defaultCameraPosition) < 0.1f) { SetReMoveFlag(false); SetFollowTarget(); eventCM.SetState(EventCharacterMove.MyStateEvent.normal, true); } } } // カメラをイベント部分に移動させる時の動きのオンオフをする public void SetMoveFlag(bool param) { // カメラを動かす時に元のカメラ位置をセットする if(isMove) { defaultCameraPosition = transform.position; } isMove = param; } // カメラをイベント前の通常時の位置に動かす場合のオンオフ public void SetReMoveFlag(bool param) { reMoveFlag = param; } // 主人公をフォローするスクリプトのオンオフ public void SetFollowTarget() { followTarget.enabled = !followTarget.enabled; } } |
カメラをイベント部分にフォーカスする時と通常時のカメラに戻す処理で分けています。
カメラをイベント部分にフォーカスする時の処理では最初にSetMoveFlag関数が呼ばれ、現在のカメラの位置をdefaultCameraPositionに入れておきます。
MeterSearchCharaスクリプトでSetMoveFlagと同時にSetFollowTarget関数も呼んでいますが、SetFollowTarget関数ではキャラクターを追従するようにしているFollowTargetスクリプトを無効化してます。
これはカメラの位置を移動させる時にFollowTargetスクリプトが動作してしまうとカメラ位置が変わらない為です。
Updateメソッド内でVector3.Lerpを使ってカメラをスムースに移動させ、指定した位置に近付いたらmoveFlagをfalseにしカーソルが動くようにしています。
カメラを通常の位置に戻す場合はカメラが元の位置に戻ったらFollowTargetスクリプトを有効化し、キャラクターを動かせる状態にしています。
UI、3Dオブジェクトどちらのカーソルイベントを使うかによってスクリプトを一部変更する必要があります。
イベントが成功したかどうかを表示するテキストUI
イベントが成功したか失敗したかを表示するテキストUIを作成しましょう。
今回は面倒くさいので解りやすくする為、簡易的にしましょう。
本来であればイベントが終了した時にテキストUIのプレハブを一時的に表示するという処理にするのがいいと思います。
ヒエラルキー上で右クリック→UI→Panelを選択しパネルを作成します。
Panelの子要素に右クリック→UI→Textを選択しテキストUIを作成します。
↑のような階層が出来ました(Canvasは自動的に作成されます。作成されなかった場合はUI→Canvasを作成し、同じ階層を作ってください)。
PanelのWidthとHeightを調整しサイズを小さくします。
↑のようにイベント部分とかぶらないような位置にCanvasを移動させておきます。
EventCharacterMoveスクリプトのインスペクタのTextを指定する場所に今作成したTextをドラッグ&ドロップしておきます。
これでイベントが成功したか失敗したかの表示がテキストに表示されるようになります。
カーソルイベントを実行して確認する
それでは完成した機能をUI、3Dオブジェクトの両方で確認してみましょう。
主人公キャラクターのインスペクタを表示し、EventCharacterMoveの設定をします。
↑のようにEventCharacterMoveとEventCharacterMove2とありますが、これはわたくしの都合上ふたつのスクリプトに分かれています。
EventCharacterMoveがUIカーソルイベント、EventCharacterMove2が3Dオブジェクトカーソルイベントの設定になります。
UIを使ったカーソルイベント
まずはUIを使ったカーソルイベントの確認をします。
↑のようにキャラクターがイベント部分に近付くとカメラがイベント部分にフォーカスされ、キャラクターが見えなくなります。
イベントが失敗した時はイベントが終了し、テキストに失敗と表示され、再度イベント検知エリアに侵入するとイベントが開始されます。
イベントが成功すると2度と同じイベントが発生しないようになります。
UIのカーソルイベントの問題点としてはカーソルをストップウォッチにしたこと(カーソルが大きい)と成功したかどうかを実際は数値で判定している為、
カーソルの止めた位置によっては成功しているように見えて失敗とされてしまう可能性があるところです。
成功か失敗かの判定を数値で判定しているのでこういうことが起きてしまいますが、3Dオブジェクトと同様にコライダを使って侵入を検知した方が、
見た目と実際が乖離する事はないと思います。
UIの場合は2Dになるので、他のゲームオブジェクトが侵入したかどうかを検知するには
コライダはAdd ComponentからPhysics 2Dを選択しその中から丁度いいコライダを選んで取り付ける必要があります。
またスクリプトでは
1 2 3 4 5 6 7 | void OnTriggerStay2D(Collider col) { } void OnTriggerExit2D(Collider col) { } |
等の2D系のトリガーを使う必要があります。
ちなみに3Dと2Dでの衝突の検知は別システムなのでお互いの検知は出来ません。
3Dオブジェクトを使ったカーソルイベント
次は3Dオブジェクトを組み合わせて作ったカーソルイベントを確認します。
動作はUIのカーソルイベントと同じですね。
3Dオブジェクトなので厚みがあり若干位置合わせがしづらい感じがしますね。
またカーソルはBarとHitAreaの領域を通過するのでカーソルが若干見えづらいということも発生します。
これを解消するにはカーソルの移動する場所を少しずらすといいと思います。
↑がカーソルが移動する場所です。
↑のようにカーソルの移動する位置を少し手前に移動します。
カーソルの移動する場所が変わったのでHitAreaのコライダのCenterとSizeを変更し検知するエリアを広げます。
変更すると↑のような検知エリアになります。
実際に動かしてみると、
↑のようにカーソルが少し手前で移動するので見づらくなる事はなくなります。
ただカーソルが黒色なのでよくわかりませんな・・・・(^_^;)
カーソルイベントとカメラのフォーカス機能を作り終えて
これでカーソルイベントとカメラをイベント部分にフォーカスする機能が完成しました。
最初はUIを使った処理だけ作成していたんですが、前述した通りの処理速度の問題がありそうなので3Dオブジェクトの方も作成しました。
3Dオブジェクトのカーソルイベントを作成したことで数値で成功範囲を検知する必要がなくより簡単な処理で出来るようにもなりました。
UIの方もコライダを使った処理の方がいいかもしれませんね・・・・。
今回の機能はご依頼頂いた機能で、バーのイベント部分のみ作ろうと思ったんですが、実際に作り始めたらカメラのフォーカスの機能まで一緒に取り付けてしまって・・・・。
一つの記事に色々な機能を詰め込むから長く解り辛い記事になるんでしょうね・・・・(^_^;)
一つの記事には一つの機能を心がけたいと思います・・・・(-_-)
個別の機能に関してはピックアップしてまた別記事として作成するのもありかもしれませんな・・・・うーむ。