Unityを使った3Dゲームの作り方(かめくめ)Unityを使った3Dゲームの作り方(かめくめ)ゲーム開発エンジンUnityを使った3Dゲームの制作に参考になる情報を公開しています。0今回は、崖から崖へといった高い所から高い所へとジャンプでは渡れない場所があった時、ロープに捕まって渡れるような機能を作成してみたいと思います。ゴッドオブウォーやゴッドオブウォー2やゴッドオブウォー3にあるような鎖を掴んで飛ぶようなやつですね。それ以外のゲームが思いつきませんでした・・・・(^_^;)キャラクターにジャンプ機能を搭載していれば穴がある場所も飛ぶことは出来ますが、ジャンプでは到達出来ない部分に行く為に上からロープや鎖のようなものがぶら下がっており、それを使ってブランコの要領?で飛び越えられるような機能です。↑のような感じでロープを掴んで移動するようなアクションゲームに使えると思います。スポンサーリンクロープを掴んで崖を渡る機能の作成それではロープを掴んで崖を渡る機能を作成していきましょう。崖とロープを含んだステージの作成まずは、崖とロープのゲームオブジェクトを配置していきます。サンプルなので崖はCubeで作成し、ロープはCylinderで作成します。Emptyで空のゲームオブジェクトを作成し、名前をStageとします。機能が完成したら位置を変更しても問題はありません。Stageの子要素にCubeで崖を作成します。↑のようにStageの子要素にCubeで作成します。↑が作成した崖のサンプルです。崖はただの土台として作成しているだけなので、今回の機能とはあまり関係がありません。崖以外の地面はPlaneで作成し、ワールド空間に作成します。キャラクターはStandardAssetsのEthanを配置し、AnimatorControllerの設定をしておきます。キャラクター操作スクリプトは、後で作りますのでおいておきます。次にロープを作成します。Triggerにチェックを入れ、物理的に当たらないようにしておきます。Emptyで空のゲームオブジェクトを作成し、名前をRopeBaseとします。RopeBaseの位置を調整しRopeの上に来るようにします。RopeBaseはRopeを回転させる時のBaseの位置になるもので、後でRopeをRopeBaseの子要素にします。RopeBaseを選択した状態で位置をRopeの上になるように調整します。調整が終了したらRopeBaseをドラッグしStageの上でドロップします。するとRopeとの親子関係が解除されるので、次はRopeをドラッグしRopeBaseの上でドロップします。これでRopeBaseが親、Ropeが子になりました。こんな面倒くさい事をしたかというと、RopeBaseの位置をRopeの上に合わせる為です。0.1)に変更します。ここまできたらRopeBaseの位置を調整し、崖と崖の間に移動させます。崖やロープにマテリアルを設定し、見た目を変えてみてもいいですね。これで崖とロープが完成しました。ロープを動かすロープが完成したので、ロープを左右に動かすようにしましょう。RopeBaseの前方は↑のように青軸の向きとなっています。ロープを左右に振るにはこの青軸を中心に回転させればいい事になります。新しくMoveRopeスクリプトを作成し、RopeBaseに取り付けます。limitAngleの角度の間でスムーズに補間する角度を計算しangleに代入します。ロープの角度が限界角度の絶対値との差が1度より下になったら向きを反転させています。ベースのキャラクター操作スクリプトロープに捕まり移動するキャラクターを作る前に、とりあえず移動とジャンプが出来ないといけません。そこでベースとなるスクリプトを作成します。Time.deltaTime);}}スクリプトの詳細についてはUnityでキャラクターの移動をプログラミングしてみるUnityで3Dキャラクターモデルを配置し、キャラクターをCharacterControllerの機能を使って移動させるようなプログラミングをしてみます。02:49やUnityでキャラクターがジャンプ出来るようにするUnityのゲームでキャラクターがジャンプ出来るようにする機能を作成します。20:09を参照してください。今回はこのスクリプトに処理を追加していきます。どれから作成しましょうか・・・・(-_-)キャラクターをロープに張りつかせるキャラクターがロープにジャンプした時に、キャラクターがロープと一緒に動くようにしなければいけません。そこでロープにキャラクターを検知するエリアを作成し、キャラクターが侵入した時にロープに張りつかせるようにしましょう。ロープに張りつかせるというのは難しい感じがしますが、実は簡単です。キャラクターをロープの子要素に配置してしまえば、ロープと一緒に動くキャラクターになります。Colliderを選択し取りつけます。Triggerにチェックを入れ物理的に当たらないようにします。ColliderのCenterとSizeを調整します。RopeBaseには、CatchTheRopeというキャラクターが侵入した事を検知するスクリプトを作成し取りつけます。sta;}}OnTriggerEnterメソッドでキャラクターが侵入してきた事を検知し、自身が保持しているキャラクターの状態がState.catchRopeでなければ、キャラクターの親要素をこのスクリプトを設置しているRopeBaseにします。その後、キャラクター操作スクリプトのSetStateメソッドを呼び出し、キャラクターの状態を変更しています。SetStateはまだ定義していないのでこれから定義します。処理の順番の関係で、キャラクターをロープの子要素にしてからSetStateでキャラクターの状態を変更する必要があります。キャラクターがロープを掴んだ時は、キャラクターの移動や重力は働かせたくないので、キャラクター操作スクリプトを変更します。以下のスクリプトは追加部分だけ記述しています。true;}}}キャラクターの状態を表すStateを宣言し、Startメソッド内でState.normalを設定します。Updateメソッドでは、キャラクターの状態がState.normalの時だけ実行するように変更します。SetStateメソッドでは、キャラクターの状態を変更し、AnimatorControllerのアニメーションパラメータの設定や移動値の初期化等を行っています。これでキャラクターがロープの検知エリアに侵入したら、キャラクターがロープの子要素になりキーボードでの移動やジャンプ、重力が働かなくなります。それでは実行してみましょう。↑のように、キャラクターがロープと接触するとロープの子要素となり一緒に動いています。ヒエラルキーを確認するとRopeBaseの子要素にキャラクターがいますね。キャラクターの位置と角度を合わせる先ほどキャラクターをロープの子要素にする事が出来ましたが、キャラクターの角度や位置がロープに合っていません。キャラクターの到達点をRopeBaseの子要素に作成し、ロープを掴んだ状態の時は徐々に移動させるようにします。キャラクターの角度はY座標だけを回転させロープの角度と合わせます。RopeBaseの子要素に到達点であるArrivalPointを作ります。RopeBaseの子要素に空オブジェクトを作成し、名前をArrivalPointとします。↑のように子要素にArrivalPointが作成されました。ArrivalPointを移動させキャラクターをロープの子要素にした時に、キャラクターの足元が来る位置にArrivalPointを合わせます。次にJumpToRopeCharaスクリプトに追加していきます。();}ロープを掴んでいる状態の時で、キャラクター位置が到達点に達していない時は、キャラクターの位置を少しづつ到達点に移動させます。speedToRopeはインスペクタで設定出来るようにし、到達点までのスピードを設定します。到達点に達したらmoveFlagをfalseにし、到達点移動処理の実行を止めます。次にCatchTheRopeスクリプトを修正します。arrivalPoint.localPosition;}CatchTheRopeスクリプトでは、ArrivalPointをインスペクタで設定出来るようにし、そのローカル位置を返すメソッドを定義します。これで処理の修正が終わりました。CatchTheRopeスクリプトのarrivalPointにArrivalPointゲームオブジェクトを設定し、JumpToRopeCharaスクリプトのspeedToRopeに1を設定し確認してみます。speedToRopeに1を設定した理由は到達点に至る過程を確認する為です。本来であれば、もう少し数値を大きくし到達点に早く移動出来るようにします。それでは実行してみましょう。試しに横向きでジャンプしてみました。ロープに接触すると、段々と到達点(ArrivalPoint)に移動していくのがわかると思います。これで、キャラクターの位置と角度をロープと合わせる事が出来ました。手をロープに合わせるここまでで、キャラクターをロープに合わせて動かす事が出来ました。キャラクターは棒立ちのままなのでキャラクターがロープを掴むようにしてみます。これにはIKを使って手を所定の位置に設定するようにします。IKを使うと本来のアニメーションとは別に手の位置や角度、肘の方向を所定の位置に固定する事が出来ます。IKに関してはUnityのIKを使って物を運ぶアニメーションUnityのIKを使ってキャラクターが物を運んでいるようなアニメーションを作成していきます。20:33等を参考にしてください。RopeBaseの子要素にIKで使う右手、左手、右ひじ、左ひじ交互に見て(謎)の位置や角度を表す空のゲームオブジェクトを作ります。親はSphereで作成し、MeshRendererのチェックを外します。親のゲームオブジェクトも空のゲームオブジェクトで作成してもいいんですが、位置等を確認する時にSphereの方が便利かと思ってそうしました。親の名前をIKとし、その子要素に右手の位置や角度を設定するRightHand、左手のLeftHand、右ひじの方向を表すRightElbow、左ひじの方向を表すLeftElbowの空のゲームオブジェクトを作成します。位置や角度は、スクリプトでIKを有効にしてから調整しますのでおいておいてください。Passにチェックを入れます。これでAnimatorを設定しているゲームオブジェクトのスクリプトで、OnAnimatorIKやOnAnimatorMove等のメソッドが呼ばれるようになります。IKはこれらのメソッドの中で設定します。CatchTheRopeスクリプトにIK関連の処理を追記します(追記処理だけ載せています)。leftElbow;}親のIKや右手、左手のゲームオブジェクトのTransformをインスペクタで設定出来るようにしておきます。大元のIKゲームオブジェクトは、キャラクターがロープを掴んだ時の向きによって回転させるので、初期位置をStartメソッドで入れています。キャラクターがロープを掴んだ時に、IKのローカル角度にキャラクターの右手方向のローカル方向の角度をかけた値を再度IKのローカル角度に代入しています。ここら辺は難しいので、とりあえず元のIKの角度をキャラクターの向きに合わせて回転させる処理だと思ってください。次にキャラクター操作スクリプトJumpToRopeCharaに処理を追記します。().rotation);}}IK用のウエイト変数を用意し、ロープを掴んでいる間は0→1へとウエイトを足していきます。OnAnimatorIKメソッド内でウエイトを設定し、右手や左手の位置や角度をRightHandやLeftHandの位置や角度に設定しています。ウエイトはどれだけその位置や角度にするかの値で、0だとIKが働かず、1だと完全にその位置や角度になります。徐々にIKを働かせる事によって、手の位置を少しづつRightHandやLeftHandに近づけているわけです。徐々に手の位置を動かさなくてもいい場合は、ウエイトを1にするだけです。それでは実行してみましょう。↑のようにキャラクターの方向に合わせてIKゲームオブジェクトが回転し、手の位置が合うようになっています。途中ジャンプしてもロープに接触出来てないですが・・・まぁ気にしないでください・・・・。Triggerにチェックを入れるのを忘れていただけです・・・・(;一_一)先にすでにRightHandやLeftHandの位置や角度を調整した状態を紹介しましたが、通常であれば手の位置や角度がおかしなところを向いているはずです。IK用のゲームオブジェクトの位置や角度を調整します。右側の崖から左向きに飛んだ時に、右手の位置や角度、左手の位置や角度が合うように調整してください。調整の仕方はUnityを実行し、キャラクターを右側のがけから左にむかせロープに接触させます。Componentをします。Valuesを選択し値をペーストします。面倒ではありますがRightHand、LeftHand、RightElbow、LeftElbowを一つ一つ実行、停止してベストな位置と角度を探します。これでロープを掴む処理が完成しました。ロープを離した時の処理を追加するここまででロープを掴んで手の位置や角度を合わせる事まで出来ました。キャラクターがロープを掴んだまま離す事が出来ません。そこで、Jumpキーを押した時にロープを離すようにしたいと思います。ロープを離した時にキャラクターの状態をState.normalにするだけでは、ロープの振りの力をキャラクターの移動値に反映出来ません。そこで、ロープを離してから地面に触れるまではロープを離した状態(State.releaseRope)にし、ロープが動いている方向にキャラクターを動かすことにします。左から右へロープが移動している場合は左から右へ移動値を加えます。右から左へロープが移動している場合は左へ移動値を加えます。JampToRopeCharaスクリプトに追記します。preRotation;}}ロープを掴んでいる状態の時にJumpボタンを押したら、キャラクターの状態をState.releaseRopeにします。キャラクターの状態がState.releaseRopeの時は、キャラクターの角度をロープを掴む前の状態に徐々に変化させます。MoveRopeのdirectionによって分岐させ、移動値を加える方向を算出してます。キャラクターに移動値を加える方向はロープを離した時の方向なのでそれをxDirectionに保持しておきます。移動値には重力値も加えて下方向に移動させています。地面に接地したらキャラクターの状態をState.normalにし、通常の移動やジャンプが出来るようにしています。SetStateメソッドでは、キャラクターがロープを離した時に親子関係を解除したり、ロープを離した時のアニメーションの遷移をさせたりしています。キャラクターがノーマル状態になったら、強制でキャラクターの角度をロープを掴む前の角度に設定しています。これで機能が出来ました。JumpToRopeCharaのインスペクタのspeedToRopeの値を5にして実行してみます。↑のようにキャラクターがロープを離した時に、ロープの向かっている方向に移動値を加える事が出来ました。ロープが停止している状態から動かすロープが停止している時にキャラクターがロープに触れたら接触した方向へ動き出すようにしてみます。キャラクターがロープを離した後は、ロープが自然に停止するようにします。ロープを動かすMoveRopeスクリプトの改造それではまずMoveRopeを改造していきましょう。moveFlag;}defaultLimitAngleはロープの限界の角度のデフォルト値で、limitAngleは現在のロープの限界角度です。キャラクターがロープを離した時はこのlimitAngleの値を段々小さくすることでロープの動きを弱めていきます。その弱めていくスピードがundoSpeedになります。moveFlagがtrueの時はキャラクターがロープを掴んでいる時でロープを動かします。ロープを離した時はlimitAngleの値を徐々に小さくし、ロープの動きを弱めます。SetDirectionメソッドはキャラクターが飛び乗った方向を指定する時に使用します。SetMoveFlagはmoveFlagのオンオフをするメソッドでそれ用の設定変更を行います。directionをロープを離した時にも使用すると後で困りますので・・・・別のフィールドに保持しておきます。次にCatchTheRopeに追記します。(true);}}キャラクターの状態がロープを掴んでいる時やロープを離した後地面に接地していないときは何もしないようにします。それ以外でロープに接触したらMoveRopeスクリプトのSetMoveFlagの引数にtrueを渡してロープが動き出すようにします。次にロープにキャラクターが接触した向きによってロープを動かす方法を決めるので、検知エリアを左右に作成しそこに侵入した時にMoveRopeスクリプトのSetDirectionメソッドを呼び出し方向を設定することにします。Triggerにチェックを入れて検知範囲とします。RightAreaとLeftAreaにはRopeAreaスクリプトを取り付けます。(-1);}}}}Playerタグを設定したキャラクターが検知範囲に入ったら、そのゲームオブジェクトの名前でロープを動かす方向の設定をしています。実際の検知エリアの領域は↑のような感じに作成しました。検知エリアを作るのではなくキャラクターがロープのエリアのどこに接触したかで判断し動かす向きを設定する事も出来そうですが、難しいので今回は検知エリアで方向を決めてしまいます。これで機能が完成しました。実行して確認しましょう。↑のようになりました。ロープをHingeJointとRigidbodyを使って動かす2019/07/17に追記した内容になります。ここまででキャラクターがロープに飛んで掴み離すという機能は出来ましたが、ロープを動かすのにスクリプトで細かく制御していて分かり辛くなっています。そこでロープにはHingeJointとRigidbodyを取り付け、キャラクターがロープと接触した時にロープに力を加えて動かすだけでロープ自体の動きをスクリプトで動かさないようにしてみます。ロープに上る階段とロープを1セットにして名前をJointRopePrefabとし、Assetsエリア内にドラッグ&ドロップをします。子要素にあるどちらの階段から侵入したか検知しているRightAreaとLeftAreaは使わないので削除します(上の画像では削除済みです)。RopeBaseに設定していたMoveRopeとCatchTheRopeスクリプトを削除します。RopeBaseのTagに新しくRopeを作成し設定しておきます。Jointを取り付けると一緒にRigidbodyも取り付けられます。Gravityにチェックを入れて重力を働かせ、Dragを0.1にして空気抵抗を加えます。Limitにチェックを入れMinに―90、Maxに90を設定しロープが回転しすぎないようにします。RopeBaseに新しくRopeScriptを作成し取り付けます。swingPower);}}}}RopeScriptはCatchTheRopeスクリプトに記述していた内容を一部移しました。インスペクタで設定していた手の位置等はStartメソッドで階層下から設定することにします。oldAngleはUpdateメソッドが呼ばれる度にロープのRotationのZの値を入れます。AlignDirectionOfIKメソッドではキャラクターがロープに飛び乗った時にIKの角度をキャラクターの向きに合わせて変更する為の処理です。Swingメソッドはキャラクターがロープに捕まっている時にブランコを漕ぐのと同じようにロープに力を加えた時に呼び出す処理です。引数ではキャラクターの前方方向と漕ぐ力を受け取ります。角度の計算ではロープの下向きが0でX軸の正の方向が0~90、負の方向が270~360になるので、-90から90の間に補正しています。ロープに力を加える時はキャラクターの前方方向とロープの前方方向(この記事ではX軸の方に動く)からVector3.Dotで内積を求め0より大きければキャラクターの横より前方、0より小さければキャラクターの横より後方になります。キャラクターの前方方向とロープの前方からキャラクターがどっちからロープに捕まっているかを計算しています。その向きに合わせてロープを漕げる角度の範囲を指定しロープに力を加えています。文章では分かり辛いので画像で説明します。内積が0より小さい時は下のようにキャラクターが左を向いていて、赤い四角で囲まれている部分で上から下にロープが回転している時にロープを漕げます。ロープが上から下へと回転している時に前回のロープのZの角度と今のロープの角度を調べています。次に主人公の操作スクリプトとロープに接触した時のスクリプトを新しく作ります。主人公操作スクリプトはJumpToRopeChara2でロープに接触した時のスクリプトはCatchTheRope2という名前にします(名前はちゃんとした名前を付けてください)。主人公にこの二つのスクリプトを取り付けます。JumpToRopeChara2スクリプトはJumpToRopeCharaスクリプトとほとんど同じです。swingPower;}}キャラクターの状態がロープを掴んでいる時にEnterキー(return)を押したらRopeScriptのSwingメソッドでロープを漕ぎます。swingPower);}SetStateメソッドでキャラクターがロープを掴んだ状態に変更する時にCharacterControllerを無効化し、ロープを離した時に有効にする処理を記述しています。CharacterControllerのコライダが邪魔してロープに影響を与えてしまう為です。preRotation;}}SetRopeDataでRopeScriptを保持します。GetSwingPowerメソッドでロープを漕ぐ力を返します。swingPower;}次にCatchTheRope2スクリプトを見ていきます。jumpToRope.GetSwingPower());}}}}OnControllerColliderHitはCharacterControllerで移動中に他のコライダと接触した時に呼ばれます(動いていない時は呼ばれないので別途それ用の処理が必要かもしれません)。キャラクターがRopeという名前のタグを設定されたコライダと接触したらキャラクターの親をロープにします。キャラクター操作スクリプトのSetStateで状態と接触したロープのRopeScriptを引数として渡します。RopeScriptと同じゲームオブジェクトに設定されているRigidbodyにAddForceメソッドで力を加えます。これで機能が完成しました。JointとRigidbodyを付けるだけで振り子運動をさせることが出来ますし、ロープ自体をスクリプトを使って動かすのではなくキャラクターが接触した時とロープを漕いだ時だけロープのRigidbodyに力を加えるだけなので結構楽ですね。ロープを漕ぐ時にアニメーションも変化させてアニメーションに合わせてロープに力を加えるとより良くなりそうですね。終わりに今回の機能は、前々から作ろう作ろうと思っていた機能です。簡単にこの機能を作れると思っていたんですが、結構大変でした・・・・。コメントをキャンセルメールアドレスが公開されることはありません。コメントデータの処理方法の詳細はこちらをご覧ください。サイト内検索が出来ますプロフィール管理人のかめくめちゃんです。(-_-)メールはこちらにお願いします(記事への質問はコメント欄にお願いいたします)。kamekumechan@gmail.comいずれはSIRENやバイオハザードみたいなアクションゲームを作りたいと思っています。ゲームシナリオが書けないので面白いゲームはなかなか作れません・・・・。(^_^;)名前の由来はカメが好きだからです。Д`)シクシク...お知らせ2020/01/18個人的にUnity2019.2.18f1をインストールしました。2020/01/01あけましておめでとうございます。(^^)/ユニティちゃんのRPGを作ってみようのPartyStatusスクリプトでpartyMembersのListの型がCharacterStatusとなっていたのでAllyStatusに変更しました。変更したというか、元々AllyStatusに書き換える前のスクリプトを掲載していたので修正しました。ユニティちゃんのRPGを作ってみよう10ーコマンド画面の作成とステータスの表示ーユニティちゃんのRPGでコマンド画面の作成とステータスコマンドを作成し、選択したキャラクターのステータスを表示するようにしていきます。20:00GetPartyStatusメソッドの戻り値もそれに伴って変更しています。2018/08/07コメント欄へのわたくしの返信は停止しております。わたくしに出来る事は既に記事にしてあるので、ブログ右上の検索窓を活用してください。(._.)またブログの最初の方の機能を積み上げていく記事に関しての疑問点は当ブログ記事のスクリプトの説明と寄せられた質問への回答Unityを使った3Dゲームの作り方(かめくめ)の機能積み上げ形式の記事のスクリプトの説明と寄せられた記事の機能についての質問への回答を掲載しています。21:41も参照してみてください。コメントの返信はありませんが、回答は記事に反映されている可能性があります。当ブログはハイスピードプランです。WordPressの編集画面の動作が早くなった。Unityを使った3Dゲームの作り方(かめくめ).。