今回は戦闘シーンへ遷移した時に味方のパーティーと遭遇した敵のパーティーのメンバーのプレハブをインスタンス化し、キャラクターを戦闘位置に配置する機能を作成していきます。
前回は戦闘シーンへ遷移する時のフェードエフェクトの作成を行いました。
ユニティちゃんのRPGを作ってみようの他の記事は
から見ることが出来ます。
戦闘シーンへ遷移した時にキャラクターを配置する
戦闘シーンに遷移した時に味方パーティーのキャラクターと遭遇した敵パーティーのキャラクターを戦闘シーンに配置する機能を作成していきます。
戦闘シーン用の味方キャラクターのプレハブを作成する
戦闘シーンで使用する敵キャラクターのプレハブは
で行っていますが、味方キャラクターの戦闘用のキャラクタープレハブは作成していませんでした。
なので、味方キャラクターの戦闘用のプレハブを作成します。
ユニティちゃんの戦闘用プレハブの作成
Villageシーンに配置しているUnityChanをCtrl+Dキーで複製し、名前をBattleUnityChanとします。
Assets/RPG/Prefabs/Charactersフォルダに新しくAllyフォルダを作成し、その中にBattleUnityChanをドラッグ&ドロップします。
BattleUnityChanをプレハブにしたのでヒエラルキー上のBattleUnityChanを削除します。
Assets/RPG/Prefabs/Characters/BattleUnityChanを選択したらインスペクタでUnityChanScript、NavMeshObstacle、SceneLoadingPosition、UnityChanTalkScript、UnityChanCommnandコンポーネントを右の歯車からRemove Componentを選択し削除します。
これは戦闘用のユニティちゃんのプレハブなのでとりあえず戦闘で使用しないと思われるコンポーネントを排除しました。
大鳥ゆうじの戦闘用プレハブの作成
次にコマンド機能のステータスコマンドやアイテムコマンドで名前だけ登場していた大鳥ゆうじの戦闘用プレハブを作成します。
ユニティちゃんのデータをダウンロードし、インポートした時と同じやり方で大鳥ゆうじをダウンロード、インポートします。
『バトルコスゆうじ』をダウンロードし、Unityにインポートします。
Assets/UnityChanTPK/Models/04_yuji/Prefabsフォルダの04_yujiをヒエラルキー上にドラッグ&ドロップします。
名前をBattleYujiと変更します。
BattleYujiを選択し、右クリックからUnpack Prefab Complitelyを選択し元のプレハブとのリンクを解除します。
BattleYujiの子要素のWepをBattleYujiのボーンの右手の子要素にドラッグします。
これで大鳥ゆうじの右手のボーンと一緒に武器が移動します。
シーンビューでWepを移動し、右手の位置に移動させます。
ここまで出来たらヒエラルキー上のBattleYujiをAssets/RPG/Prefabs/Characters/Allyフォルダにドラッグ&ドロップしてプレハブにし、ヒエラルキー上のBattleYujiは削除します。
戦闘データの作成
戦闘シーンで使用するデータの作成をしていきます。
戦闘用パーティーデータの作成
味方パーティーステータスのデータはPartyStatusで管理していましたが、戦闘シーンでは配置するキャラクターのゲームオブジェクトとそのゲームオブジェクトのステータスが一致している必要がある為、別途戦闘用のパーティーステータスデータを作成します。
Assets/RPG/Scriptsフォルダに新しくBattleフォルダを作成し、その中にBattlePartyStatusスクリプトを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [Serializable] [CreateAssetMenu(fileName = "BattlePartyStatus", menuName = "CreateBattlePartyStatus")] public class BattlePartyStatus : ScriptableObject { [SerializeField] private List<GameObject> partyMembers = null; public List<GameObject> GetAllyGameObject() { return partyMembers; } } |
Assets/RPG/Data/Status/Allyフォルダ内で右クリックからCreateBattlePartyStatusを選択します。
CreateBattlePartyStatusのインスペクタで先ほど作ったBattleUnityChanプレハブとBattleYujiプレハブを設定します。
戦闘シーンで使用する戦闘用データの作成
戦闘シーンで使用する味方パーティーと敵パーティーのゲームオブジェクトを保持するデータの作成をします。
Assets/RPG/Scripts/BattleフォルダにBattleDataスクリプトを作成します。
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 | using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [Serializable] [CreateAssetMenu(fileName = "BattleData", menuName = "CreateBattleData")] public class BattleData : ScriptableObject { // 味方パーティーデータ [SerializeField] private BattlePartyStatus battlePartyStatus; // 敵パーティーデータ private EnemyPartyStatus enemyPartyStatus; public void SetAllyPartyStatus(BattlePartyStatus partyStatus) { battlePartyStatus = partyStatus; } public BattlePartyStatus GetAllyPartyStatus() { return battlePartyStatus; } public void SetEnemyPartyStatus(EnemyPartyStatus enemyPartyStatus) { this.enemyPartyStatus = enemyPartyStatus; } public EnemyPartyStatus GetEnemyPartyStatus() { return enemyPartyStatus; } } |
BattleDataスクリプトはScriptableObjectクラスを継承して作成し、後でアセットファイルを作成します。
BattleDataで保持するのは味方パーティーのPartyStatusと敵パーティーのEnemyPartyStatusです。
それぞれに対してSetterとGetterを用意しスクリプトから設定と取得が出来るようにしています。
enemyPartyStatusは[SerializeField]アトリビュートを取り付けていないのでインスペクタで設定出来ませんが、これは敵パーティーは敵と遭遇した時にどの敵パーティーと戦うかがわからないのでスクリプトで遭遇した敵パーティーをその都度設定する必要があるからです。
味方パーティーは予め誰がいるかがわかっているのでインスペクタで設定出来るようにします。
Assets/RPG/Data/Statusフォルダに新しくBattleフォルダを作成し、その中で右クリックからCreateBattleDataを選択しBattleDataのアセットファイルを作成します。
BattleDataには味方のBattlePartyStatusを設定します。
敵パーティーグループのリストを保持するスクリプトの作成
次に敵パーティーグループのリストを保持するだけのスクリプトを作成します。
Assets/RPG/Scripts/Statusフォルダに新しくEnemyPartyStatusListスクリプトを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [Serializable] [CreateAssetMenu(fileName = "EnemyPartyStatusList", menuName = "CreateEnemyPartyStatusList")] public class EnemyPartyStatusList : ScriptableObject { [SerializeField] private List<EnemyPartyStatus> partyMembersList = null; public List<EnemyPartyStatus> GetPartyMembersList() { return partyMembersList; } } |
EnemyPartyStatusのリストを保持し、それを返すだけです。
Assets/RPG/Data/Status/Enemyフォルダ内で右クリックからCreate→CreateEnemyPartyStatusを選択します。
作成したEnemyPartyStatusに以前作成したEnemyGroup1~EnemyGropup4までを設定します。
上のように敵パーティーグループのリストが出来ました。
敵と遭遇時にどの敵と戦うかを設定する
次に敵と遭遇した時にどの敵のパーティーと戦うのか?というデータをBattleDataのenemyPartyStatusに設定する処理を戦闘シーンに遷移する前にしておく必要があります。
敵と遭遇した時の処理はEncountManagerゲームオブジェクトに取り付けたEncountManagerスクリプトで行っているので、ここでBattleDataのenemyPartyStatusにどの敵と戦うのか?の情報を設定するようにしましょう。
1 2 3 4 5 6 7 8 9 10 11 | // 戦闘データ [SerializeField] private BattleData battleData = null; // 敵パーティーリスト [SerializeField] private EnemyPartyStatusList enemyPartyStatusList = null; // ワールドマップフィールド [SerializeField] private Terrain worldMapField; |
敵と遭遇時に戦闘データを設定する為BattleDataを設定出来るようにします。
どの敵パーティーを戦闘データに設定するか?を選ぶ時に敵のパーティーのリストがないと選べませんのでenemyPartyStatusListにはインスペクタで先ほど作ったEnemyPartyStatusListアセットファイルを設定します。
今回はワールドマップのフィールドに使用しているTerrainの幅と長さを使用して遭遇する敵を選ぶのでTerrainをインスペクタで設定出来るようにします。
ユニティちゃんが今どこにいるか?を知る必要がある為、ユニティちゃんのTransformを設定出来るようにします。
インスペクタでunityChanObjectを設定しても問題なさそうですね・・・・。
敵と遭遇した時の処理に追記します。
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 | if(elapsedTime >= destinationTime) { // ワールドマップ上のユニティちゃんの位置に応じて遭遇する敵を決定する if (-worldMapField.terrainData.size.x / 2 <= unityChanObjct.position.x && unityChanObjct.position.x <= 0f && 0f <= unityChanObjct.position.z && unityChanObjct.position.z <= worldMapField.terrainData.size.z / 2 ) { battleData.SetEnemyPartyStatus(enemyPartyStatusList.GetPartyMembersList().Find(enemyPartyStatus => enemyPartyStatus.GetPartyName() == "EnemyGroup1")); } else if (0f <= unityChanObjct.position.x && unityChanObjct.position.x <= worldMapField.terrainData.size.x / 2 && 0 <= unityChanObjct.position.z && unityChanObjct.position.z <= worldMapField.terrainData.size.z / 2 ) { battleData.SetEnemyPartyStatus(enemyPartyStatusList.GetPartyMembersList().Find(enemyPartyStatus => enemyPartyStatus.GetPartyName() == "EnemyGroup2")); } else if (-worldMapField.terrainData.size.x / 2 <= unityChanObjct.position.x && unityChanObjct.position.x <= 0f && -worldMapField.terrainData.size.z / 2 <= unityChanObjct.position.z && unityChanObjct.position.z <= 0f ) { battleData.SetEnemyPartyStatus(enemyPartyStatusList.GetPartyMembersList().Find(enemyPartyStatus => enemyPartyStatus.GetPartyName() == "EnemyGroup3")); } else if (0f <= unityChanObjct.position.x && unityChanObjct.position.x <= worldMapField.terrainData.size.x / 2 && -worldMapField.terrainData.size.z / 2 <= unityChanObjct.position.z && unityChanObjct.position.z <= 0f ) { battleData.SetEnemyPartyStatus(enemyPartyStatusList.GetPartyMembersList().Find(enemyPartyStatus => enemyPartyStatus.GetPartyName() == "EnemyGroup4")); } else { battleData.SetEnemyPartyStatus(enemyPartyStatusList.GetPartyMembersList().Find(enemyPartyStatus => enemyPartyStatus.GetPartyName() == "EnemyGroup1")); } audioSource.Play(); sceneManager.GoToNextScene(SceneMovementData.SceneType.WorldMapToBattle); elapsedTime = 0f; SetDestinationTime(); } |
Terrainのsizeのx(Terrain Width)とz(Terrain Length)を使ってどの敵と遭遇したか?をBattleDataに設定しています。
わたくしの場合はワールドマップ用のTerrainはTerrain Widthを500、Terrain Lengthを500で作成していて、Terrainの位置をXを-250、Zを-250としていたので、ユニティちゃんの位置が
Xが-250~0でZが0~250の時はEnemyGroup1の敵を設定
Xが0~250でZが0~250の時はEnemyGroup2の敵を設定
Xが‐250~0でZが‐250~0の時はEnemyGroup3の敵を設定
Xが0~250でZが-250~0の時はEnemyGroup4の敵を設定
それ以外はEnemyGroup1の敵を設定
しています。
敵パーティーデータはenemyPartyStatusListのGetPartyMembersListメソッドでEnemyPartyStatusを取得しますが、その条件をListのFindメソッドの引数でラムダ式を指定し、ListのEnemyPartyListのGetPartyNameメソッドでパーティー名を取得し、そのパーティー名が該当するEnemyGroup番号と一致したものを探し、それをBattleDataスクリプトのSetEnemyPartyStatusメソッドの引数に指定しています。
これで戦闘シーン遷移前にBattleDataに遭遇した敵パーティーのデータを設定出来ました。
EncountManagerゲームオブジェクトのインスペクタのEncountManagerスクリプトの設定は以下のように設定します。
上のようになります。
戦闘シーンのフィールドを作成する
戦闘シーンのフィールドはTerrainを使って作成します。
Battleシーンを開いてください。
やり方は村の作成の時と同じです。
Terrain Widthは100、Terrain Lengthは100で作成しました。
戦闘はワールドマップで遭遇して行われるので、ワールドマップで使用したTerrainのフィールドを戦闘シーンでも配置して使っても大丈夫です。
敵と遭遇した位置によってキャラクターを配置する場所を変えたりするのもいいかもしれませんね。
戦闘シーンのTerrainのTransformでXを‐50、Zを‐50にします。
作成したTerrainの名前をBattleFieldとします。
Assetsフォルダに作られたNew Terrainの名前をBattleFieldにし、Assets/RPG/Fieldフォルダに新しくBattleフォルダを作成しその中に入れておきます。
戦闘シーンにキャラクターを配置する位置や角度を指定する
戦闘が行われた時にキャラクターを配置しますが、その位置や角度は予め設定しておくことにします。
ヒエラルキー上で右クリックからCreate Emptyを選択し、名前をBattleBasePositionとします。
BattleBasePositionをBattleFieldの戦闘が行われる位置に移動させます。
わたくしは以下のように見通しが良い場所に移動させました。
BattleBasePositionを選択した状態で右クリックからCreate Emptyを選択し、名前をAllyPos0とします。
これが味方パーティーの1番目のキャラクターを配置する位置と角度になります。
AllyPos0のインスペクタで名前の横のアイコンを変更し、シーンビューでAllyPos0の位置がわかりやすいようにします。
またAllyPos0のTransformのPositionのXとZを変更し、BattleBasePositionからの相対位置に味方パーティーのキャラクターが位置するようにします(わたくしはXを5、Zを3としました)。
さらに、RotationのYを270にして敵のパーティーと正面で向き合うようにします。
AllyPos0を選択した状態でCtrl+Dキーを押して複製し名前をAllyPos1とします。
AllyPos1のTransformのPositionのZを1とします。
同様にAllyPos2とAllyPos3を作成します。
AllyPos0を選択した状態でCtrl+Dキーを押し複製して、名前をEnemyPos0とし、TransformのPositionのXを-5、RotationのYを90とします。
AllyPosの時と同様にEnemyPos0を複製してEnemyPos1~EnemyPos3までを作成して並べます。
上のような配置場所を選択しました。
TransformのPositionやRotationはカメラの位置などによって変わってきますので注意してください。
今回は味方キャラクターと敵キャラクターは最大で4体までとしていますが、必要に応じて増やしてください。
戦闘シーンが開始したらキャラクターを配置する
戦闘シーンのフィールドとキャラクターを配置する場所が出来たので、戦闘シーンで使用するキャラクターの配置をするスクリプトを作成します。
Assets/RPG/Scripts/Battleフォルダに新しくBattleManagerスクリプトを作成します。
BattleManagerスクリプトは戦闘に関する処理を実行するスクリプトです。
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 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class BattleManager : MonoBehaviour { // 戦闘データ [SerializeField] private BattleData battleData = null; // キャラクターのベース位置 [SerializeField] private Transform battleBasePosition; // 現在戦闘に参加しているキャラクター private List<GameObject> allCharacterList = new List<GameObject>(); // Start is called before the first frame update void Start() { Transform characterTransform; List<GameObject> instances = new List<GameObject>(); GameObject ins; // 味方パーティーのプレハブをインスタンス化 for (int i = 0; i < battleData.GetAllyPartyStatus().GetAllyGameObject().Count; i++) { characterTransform = battleBasePosition.Find("AllyPos" + i).transform; ins = Instantiate<GameObject>(battleData.GetAllyPartyStatus().GetAllyGameObject()[i], characterTransform.position, characterTransform.rotation); allCharacterList.Add(ins); } // 敵パーティーのプレハブをインスタンス化 for (int i = 0; i < battleData.GetEnemyPartyStatus().GetEnemyGameObjectList().Count; i++) { characterTransform = battleBasePosition.Find("EnemyPos" + i).transform; ins = Instantiate<GameObject>(battleData.GetEnemyPartyStatus().GetEnemyGameObjectList()[i], characterTransform.position, characterTransform.rotation); allCharacterList.Add(ins); } } } |
BattleManagerスクリプトは戦闘に関する処理を行うので、BattleDataをインスペクタで設定出来るようにしておきます。
キャラクターを配置する場所の親であるBattleBasePositionもインスペクタで設定出来るようにします。
StartメソッドではBattleDataアセットファイルから味方のパーティーデータ分をfor文で繰り返しbattleBasePositionの子要素から『AllyPos番号』という名前のTransformを取得し、その位置と角度にBattlePartyStatusから取得したキャラクタープレハブをインスタンス化しています。
敵のパーティーの場合も同様に行っています。
戦闘で使用するキャラクターはallCharacterListリストに保持しておきます。
Battleシーンのヒエラルキー上で右クリックからCreate Emptyを選択し、名前をBattleManagerとしBattleManagerスクリプトを取り付けます。
インスペクタでbattleDataにBattleDataアセットファイル、battleBasePositionにヒエラルキーのBattleBasePositionを設定します。
配置されたキャラクターをカメラが映すように移動させる
戦闘シーンにキャラクターが配置されるようになりましたが、Main Cameraがキャラクターを表示していない可能性があります。
BattleシーンのMain CameraのTransformのPositionとRotationを操作して、配置したキャラクター全体が写るようにします。
カメラの位置や角度を探る方法として、Unityを実行した後にMain Cameraの位置や角度を調整し、Main CameraのTransform右の歯車からCopy Componentを選択します。
Unityの実行を停止し、Main CameraのTransform右の歯車を押し、Paste Component Valuesを選択します。
これでUnity実行時に変更したTransformの値がコピー&ペーストされます。
今回は以下のようなアングルと位置にしました。
敵がでかい・・・・・・(^_^;)
ずっとこのアングルでも構わないんですが、カメラは状況に応じて移動させるようにしますが、それはまた次回以降に行っていきます。
戦闘シーンのBGMを設定する
戦闘シーンが始まったら戦闘用のBGMを流したいと思います。
Main CameraのインスペクタのAdd ComponentからAudio→Audio Sourceを選択し取り付けます。
Audio SourceのAudio ClipにAssets/RPG Game Musicのsmall epic introを設定しました(自由に設定してください)。
ワールドマップから戦闘シーンへと移行した時にキャラクターが配置されるか確認する
ワールドマップから戦闘シーンへと遷移した時に戦闘を行う敵・味方のキャラクターが配置されるかを確認しましょう。
上のようになりました。
大鳥ゆうじだけユラユラと揺れてますが、本来は全員動かないはずです(わたくしは大鳥ゆうじだけAnimatorを設定してしまっていた為になっています)。
終わりに
戦闘シーンは徐々に作っているのでなかなか進みませんね・・・・、この後はキャラクターの素早さに応じてキャラクターが攻撃する順番を決めたり、そのキャラクターが出来るコマンドを選択したりと面倒な処理が続きます。
もう(/・ω・)/ポイっとしたくなってきました・・・・(´Д`)
この作品はユニティちゃんライセンス条項の元に提供されています