Unityのゲームキャラクター操作をすべてマウス操作で行う

今回はキャラクターの移動やカメラの視点の変更や回転、キャラクターへのズームイン・ズームアウト、の操作をすべてマウス操作でするにはどうしたらいいですか?

という問い合わせを頂きましたので、その為の機能を作成していきたいと思います。

さらにカメラが壁と被った時の対策、キャラクターとの会話、敵を攻撃する機能も取りつけました。

操作方法としては、

  • キャラクターの移動は地面をマウスの左ボタンを押したらそこに移動するようにする
  • カメラの視点の変更や回転はマウスの右ボタンを押した状態でドラッグした時に回転するようにする
  • カメラがキャラクターをズームイン・ズームアウトする機能はマウスのホイールをスクロールした時に行う
  • カメラとキャラクターとの間に壁等の障害物が来た時はカメラを壁の位置に動かす
  • 他のキャラクターをマウスの左ボタンで押したら会話する
  • 敵キャラクターをマウスの左ボタンで押したら攻撃する

という感じで作成します。

マウスでの移動に関しては

Unityでマウスクリックした位置にキャラクターを移動させる方法
Unityのアクションゲームでマウスクリックした位置にキャラクターが移動する機能を作成していきます

の機能を使い、さらに移動ポイントにエフェクトを表示するようにしてみます。

この移動機能はナビゲーション機能を使用して移動するわけではないので障害物があった場合ずっと障害物にぶつかった状態で止まってしまいます。

もしそれが嫌な方はナビゲーションを使った移動に変更するといいと思います。

Unityで主人公をナビゲーション機能とマウスクリックで移動させる
Unityでキャラクターはナビゲーション機能で移動させるが、移動先の指定はマウスクリックで行えるようにします。ナビゲーション機能を使っているので障害物を避けて最適な経路で目的地に進んでくれます。
スポンサーリンク

MouseMoveスクリプトに移動ポイントを表示する処理を追加

ではMouseMoveスクリプトに変更を加えていきましょう。
スクリプトが重複するので追加する部分だけを記述していきます。

まずは変数宣言ですね。

clickPointEffectは地面をクリックした時にその位置に表示するパーティクルを設定します。

publicで宣言してインスペクタで設定出来るようにしておきます。

clickObjはパーティクルをインスタンス化したものを入れておく変数です。

キャラクターがクリックした位置に到着または別の地点をクリックした場合に消す必要がある為変数に入れておきます。

クリック時に毎回インスタンス化するのが嫌な方はあらかじめゲーム内に非表示にしたパーティクルを用意しておき、クリックされた時にその地点に移動させ表示状態に変更するというのでもいいかもしれません。

↑が新しい処理を追加した部分でキャラクターが地面に接地している時の処理です。

クリックされたら、すでにclickObj(パーティクル)があった場合はそれを削除します。

その後clickPointEffectをインスタンス化してclickObj変数に参照を入れています。

clickPointEffectはインスタンス化した際にVector3.up(上向き)からtargetPosition(クリックした地点)の面の角度(hit.normal)の間の角度を指定します。

何をやっているかというとこれでパーティクルの角度をクリックした面の角度と同じにしています。

例えば坂をクリックした場合はパーティクルの角度が坂と同じになります。

パーティクルを地面と同じ角度にしない場合はQuaternion.FromToRotationの所をQuaternion.identityに変更します。

Quaternion.identityは親の角度と同じになります。
親がない場合はゲームの世界の初期値の角度になると思われます。

あとは目的地に着いた時に移動ポイントにパーティクルが表示されていたらそのパーティクルを削除します。

こうすることで移動地点を変更した時に元の移動地点に表示されているパーティクルを削除する事が出来ます。

これでキャラクターの移動は完成です。

カメラの回転、ズームイン・ズームアウト、壁対策の機能を作成する

さて、次はカメラの回転、ズームイン・ズームアウト、壁対策の機能を作成していきます。

カメラの位置は『キャラクターの位置』に『マウスのドラッグによって回転した縦横の角度 × キャラクターからの距離』を足して求めます。
つまり

カメラ位置 = キャラクター位置 + (カメラの回転 × キャラクターからの距離)

ですね。

ちょっと解り辛いかもしれませんが、
例えば水の入ったバケツを持った人間が水が落ちないように回転させているとします。

バケツの回転は完全に縦に回転させたり多少斜めに回転させたりしています。

ここで言う人間がキャラクター、人間からみたバケツの角度がカメラの回転、人間からバケツまでの距離がキャラクターからの距離となります。
つまり

バケツ位置 = 人間の位置 + (人間からみたバケツの角度 × 人間からバケツまでの距離)

となります。
少しわかっていただけたでしょうか?(^_^;)

これでカメラの位置が決定出来るので、マウスドラッグ開始点から移動した距離によって角度を計算出来ればOKです。

また、マウスホイールのスクロールでキャラクターのズームイン・ズームアウトをするには『カメラの位置』にマウススクロール値を足せばよさそうです。

この点を踏まえてスクリプトを組んでいきます。

MouseRotateスクリプトの全文は↑のようになります。

カメラの回転が一部おかしかった為cameraPosの計算とLateUpdate関数内のtransform.positionの計算方法を変更しました。

MouseRotateスクリプトの解説

少しづつ処理を見ていきましょう。

Update関数内でまずは右ボタンを押してドラッグしている状態かどうかを判定しています。

マウスの右ボタンが押されていたらmouseMoveをtrueにします。
またドラッグ開始点を設定します。

マウスの現在の位置はInput.mousePositionでVector2の値として得られます。

右ボタンが離された時はmouseMoveにfalseを入れています。

次はマウスホイールをスクロールした時にキャラクターをズームイン・ズームアウトさせる為の処理です。

Input.GetAxis(“Mouse ScrollWheel”)でマウスをスクロールした時の値が得られるので、移動があったらInput.mouseScrollDelta.yで移動値を求めscrollValueに入れます。

scrollValueはインスペクタで設定したズームインとズームアウトの制限内の値が得られるようにMathf.Clampを使用しています。

Mathf.Clampは第1引数の値を第2引数の最小値と第3引数の最大値の間の値にする事が出来ます。

maxScrollValueとminScrollValueの位置がおかしいような気がしますが、これはキャラクターからの距離がマイナスの値となる為に

カメラがキャラクターと一番離れた時が最小値になり、一番近い時が最大値となる為に変数名と引数の位置に違和感が生じているだけで問題はありません。

マウスのドラッグ中であればカメラの回転と位置を計算する為の処理を行います。

まずはカメラの上限の回転の設定(rotateDirection)によって回転する方向を変更します。

通常であればマウスを前に押すとカメラが上方向に回転しますが、逆にしたい場合はdirectionの値を-1にしてかけて
マウスを前に押した時カメラが下方向に回転するようにしています。

回転方向の設定であるrotateDirectionはインスペクタで切り替えが出来るようにしておきます。

ゲームのユーザーがこの設定を切り替えられるようにすると便利かもしれませんね!

カメラの回転は『元のカメラの回転値』に『現在のマウス位置 – ドラッグ開始点のマウス位置』を『スクリーンのサイズ』で割って『回転のスピード』をかけたものを足します。

マウスの移動値はピクセル値が返ってくるのでスクリーンサイズで割る事にします。

そこにインスペクタで回転スピード(rotateSpeed)を設定出来るようにして、回転の速さを調整出来るようにします。

mouseRotateYはマウスの横方向の移動なので360を超えた時は0に戻るようMathf.Repeatで0~360の間になるようにします。

mouseRotateXはdirectionをかけてマウスの方向によって回転する方向を変えています。

また、Mathf.Clampによって上方向、下方向に回転出来る制限を加えています。

この制限値もインスペクタで設定出来るようにしておきます。

これでカメラの回転が計算出来たので、最後にカメラの角度とキャラクターからの距離を計算しcameraPos変数に入れておきます。

LateUpdate関数でキャラクターの位置にcameraPosの値を足した位置+マウスのスクロールをした距離にカメラの位置を動かします。

スクロールした位置はマウスの向いている方向transform.forwardにスクロール値をかけて計算します。

動かした後transform.LookAtでカメラがキャラクターを向くようにしています。

カメラが壁で遮られキャラクターが見えなくなった時の為にPhysics.Linecastを使ってキャラクターの位置からカメラの位置までレイを送り、

衝突したコライダに設定されているタグがPlayer以外の時はカメラの位置を衝突した位置に設定します。

ここでPlayer以外としているのはキャラクター自身のコライダとの接触を無視する為です。

なぜLateUpdate関数でカメラの回転・位置を変更しているかというとLateUpdateはUpdateが実行された後に実行される関数だからです。

Update関数と同じようにフレーム毎に呼び出されるけれどUpdateより後に実行されます。

位置や回転をそのままUpdateでしてもいいんですが、ものによっては同フレームで位置や回転を変更しようとしても反映されない事があります(ありました)。

なので今回はLateUpdate関数で位置等を変更するようにしました。

クリックした相手によって行動を変更する

ここまででクリックした位置にキャラクターを移動させる事は出来ましたが、クリックした相手が村人だったら会話メッセージを表示させ、敵だったら攻撃出来るようにしてみます。

会話出来るキャラクターと敵キャラクターの作成

まずは会話出来るキャラクターと敵キャラクターを作成しましょう。

村人、敵を配置する

↑の色つきのキャラクターが会話出来る人でタグにHumanを設定します。

黒っぽいEthanを敵キャラクターとしタグにEnemy2を設定します(2としてるのはわたくしの都合上・・・)。

それぞれのキャラクターにはコライダが設定されている必要がありますので必ず確認してください。

色つきのキャラクターの子要素に会話メッセージ表示UIを作ります。

会話用UIを作成

↑のように会話出来るキャラクターの子要素にCanvasを作成し、名前をTalkにします。

その子要素にPanel、Textを作成します。

CanvasのScaleを小さくする

TalkキャンバスのCameraのRencerModeをWorld Spaceにし、Scaleを調整とキャラクターの頭上に表示されるように位置を調整します。

ShootTargetRotateは他の記事で使用したスクリプトで、

↑のようにUIが常にカメラの向きを向くようにしているだけです。
これを作成しTalkに設定してください。

Textにあらかじめ表示するメッセージを書く

Textに表示するメッセージを書き、Font Sizeを変更して丁度いい大きさに調整します。

会話メッセージを表示したサンプル

会話メッセージは↑のように表示されるようになります。

TalkキャンバスはHuman(会話キャラクター本体の名前)に設定したTalkスクリプトから操作し表示のオン・オフをするので、最初はオフにしておきます。

Talkキャンバスを選択しインスペクタでチェックを外しTalkをオフにしておいてください。

会話用UIの表示をオン・オフするTalkスクリプトの作成

次にHumanに設定するTalkスクリプトを作成します。

操作キャラクターがHumanキャラクターをクリックした時にTalkスクリプトのTalk関数を呼び出すようにしますので、Talk関数ではUIをオンにし表示させています。

Update関数でキャラクターとの距離を計算し、キャラクターが離れて行った時にUIをオフにします。

操作キャラクターはpublic変数で宣言してインスペクタで設定します。

これで会話キャラクターの準備が出来ました。
敵キャラクターは攻撃されるだけなので特に設定はしません。

キャラクター移動スクリプトMouseMoveの修正

それではキャラクターの移動スクリプトMouseMoveを修正していきます。

まずは状態変数を宣言します。

この状態でキャラクターが今何をしようとしているかの判断をします。

次は新しく変数を宣言します。

クリックした相手が何だったか?によって表示するパーティクルを変更してみます。
パーティクルはインスペクタで設定出来るようにpublicで宣言しています。

また状態変数を入れておくstate、会話キャラクターに設定したTalkスクリプトを呼び出す為にそのキャラクターを入れておくhuman変数、

移動と会話、攻撃によってターゲットとの距離を変更する為にtargetDistance、到着したかどうかのarrived変数を宣言します。

Start関数には↑のように初期化処理を追加します。

次はマウスの左クリックがされた時の処理部分を修正していきます。

マウスクリックされたら状態を初期状態に変更し、arrivedをfalseにします。

Physics.Raycastで得られた情報から相手方のコライダのゲームオブジェクトに設定されているタグを取得し何をクリックしたかで処理を分けます。

クリックした位置は一律でtargetPositionに入れておきます。

タグによって状態変数stateにそれぞれの状態を入れ、会話キャラクターだった時はhumanにそのキャラクターのゲームオブジェクトを入れておきます。

targetDistanceは移動の時は距離を短くし、会話キャラクターや敵キャラクターの時はキャラクターの幅も考えて距離を設定します。

表示するエフェクトは会話用と攻撃用はターゲット位置より1m上に表示するようにしました。

次はUpdate関数内の到着判定をしていた部分を変更します。

arrivedフラグを使った処理に変更しました。

Vector2.Distance(Vector2(transform.position.x, transform.position.z), Vector2(targetPosition.x, targetPosition.z))

を使って高さを無視した距離で操作キャラクターとクリックしたターゲットの距離を求めるようにしました。

到着したらキャラクターの状態によって処理分けします。
状態がMoveの時は何もしていません。

Talkの時はhuman変数に保存されたゲームオブジェクト(Human)のTalkスクリプトを取得しTalk関数を呼び出します。

Attackの時は敵を攻撃する為にanimatorのアニメーションパラメータAttackをオンにしています。

キャラクターに設定したAnimatorControllerで攻撃の状態を作りアニメーションパラメータのAttackがオンになることで遷移するように作成している必要があります。

これでスクリプトの修正も終わりました。

MouseMove2のインスペクタの設定

↑のようにインスペクタでtalkEffect、attackEffectを設定しましょう(好きなエフェクトやゲームオブジェクトを設定してください)。

スクリプトの名前がMouse Move2となっているのはわたくしの個人的な都合上なっているだけなので気にしないでください。

マウス操作だけでキャラの移動、カメラの回転、ズームイン・ズームアウトが出来るか確認する

これでカメラの回転とズームイン・ズームアウトの機能が出来上がりました。
インスペクタで設定値を調整し、試してみましょう。

MouseRotateインスペクタの設定

↑のようにパラメータを設定しました。

操作キャラクターの子要素に空オブジェクトを作り名前をLookAtPointとしてキャラクターの頭の辺りに移動させます。
そこをPlayerPositionに設定しました。

Ethanをそのまま設定してもいいんですが、Ethanは足元が基点となっている為、マウスのスクロールをして近づけると足元に近付いてしまいます。

なので、別のポイントを作成しそこを設定しました。

BaseDistanceにはキャラクターの初期位置を設定しておきます。

またbaseDistanceの距離よりもマウススクロールで近くに寄れるようにしてしまうと不具合の原因になります。

baseDistanceとminScrollValueの設定に気を付けてください。

キャラクター操作をすべてマウス操作で行ったサンプル

↑のようにマウスの左ボタンを押した位置にパーティクルが表示されそこにキャラクターが移動しています。

また移動中に別の場所を押した場合は元のパーティクルが消え新しい移動ポイントにパーティクルが表示されるようになっています。

マウスホイールのスクロールでキャラクターのズームイン・ズームアウト、マウスの右ボタンを押したままドラッグするとカメラが回転するようになっています。

カメラとキャラクターの間に壁があったら壁の位置にカメラが移動しています。

また左ボタンを押して何らかのコライダと接触した場合にそこにキャラクターを移動するようになっている為、そこが地面でなくてもパーティクルの表示と移動が行われてしまいます。

会話キャラクターをクリックした時はキャラクターの頭上に会話用UIが表示され、敵をクリックした時は攻撃をしていますね。

他のキャラクターの頭上に表示するゲームオブジェクトに2Dのものを指定した時に回転させる処理や、一度他のキャラクターに近づいた後にさらにそのキャラクターをクリックすると
頭上のゲームオブジェクトが表示されないなど細かい問題点は残ってますね。

MouseRotateのカスタマイズ

移動場所を地面に限定したい場合は移動出来る地点のゲームオブジェクトのレイヤーをGround等の名前をつけておいてPhysic.Raycastで判定する時にGroundレイヤー
を指定しておくとこの対処が可能になります。

パーティクルはターゲットの位置の1m上に表示するようにしていますが、これだと足元をクリックしたのと頭をクリックしたのではパーティクルの位置が変わってきます。

そこら辺を考慮してパーティクルの出現位置を設定するといいかもしれませんね。

これでUnityのゲームで使用するキャラクターの移動やカメラの回転、カメラのズーム、カメラの壁対策、攻撃や会話をすべてマウス操作で行えるようになりました。

カメラの回転の所がうまくいかず苦労しましたが・・・(^_^;)
ある程度機能は出来ているのではないかと思います。

最初に記事をアップした時から文字数が2.5倍ぐらいになりましたよ・・・(^_^;)

ぜひ活用してみてください。(^^)/