Unityで敵を倒したらアイテムをドロップさせアイテムを取得するまでの機能

今回は敵を倒した時にその敵が持っているアイテムをその場に落とすような機能を作成し、

主人公キャラクターがアイテムに近付くと設定したモードによってアイテムを自動で取得出来るようにしたり、キーを押した時に手動で取得出来るような機能を作成していきます。

スポンサーリンク

サンプル用のキャラクター作成

今回の機能を作る前に移動と攻撃が出来るキャラクターを作成する必要があります。

移動と攻撃の機能

移動に関しては

Unityで3Dキャラクターモデルを配置し、キャラクターをCharacterControllerの機能を使って移動させるようなプログラミングをしてみます。

攻撃に関しては

Unityで主人公キャラが剣を振って敵キャラを攻撃出来るようにする機能を作成します
Unityのアクションゲームで連続攻撃が出来る機能を作成します。現在攻撃中の時に攻撃ボタンを押したら次の攻撃アニメーションが再生されるようにします

を参照してください。

連続攻撃は特に作る必要はありませんが・・・・スクリプトの条件等を変更する必要が出てきます。

キャラクターにはDropItemCharaという移動と攻撃をさせるスクリプトを取りつけます。

キャラクターの移動と攻撃に関しての説明は先ほど紹介した記事を参照してください。

攻撃時に自身の子要素であるSearchItemスクリプトを取得しその中のSelectItemメソッドを実行しています。

SearchItemスクリプトは後で作成しますが、このSearchItemスクリプトは主人公キャラの子要素にあるサーチエリアに取り付け、近くにあるアイテム情報を保持させます。

その中のSelectItemメソッドはアイテム情報の中で一番近くにあるアイテムを取得する処理になります。

武器に取り付けるスクリプト

主人公キャラクターの持っている武器にスクリプトを設定します(武器にはコライダが設定されている必要があります。

武器にはコライダを設定していますが、このコライダがEnemyタグを設定した敵のコライダに接触した時DropItemEnemyスクリプトのDamageメソッドを呼び出します。

DropItemEnemyは後で作成する敵キャラクター用のスクリプトです。

アイテムプレハブの作成

次に敵を倒した時に落とすアイテムをプレハブとして作成します。

アイテム用の3DCGがないのでCubeを作成しマテリアルを設定する事でアイテムを表現する事にします。

ヒエラルキー上で右クリック→3D Object→Cubeを選択し2つのCubeを作成します。

名前をMizuとYakusouと変更します。

TagにItemを作成し、それぞれ設定しておきます。

今回敵がドロップするアイテムはこの2種類だけ作成することにしましょう。

それぞれRigidbodyコンポーネントを取りつけ、Use Gravityにチェックを入れ重力を働かせる事にします。

作成したMizuは以下のようになります。

アイテム水のインスペクタ

Mizu用のマテリアルを作成し取りつけています(画像ではEyesとなっていますが気にしないでください・・・・・)。

マテリアルはAssetsフォルダで右クリック→Create→Materialで作成する事が出来ます。

マテリアルに関しては

アクションゲーム等によくある床が上下に動いたり、ベルトコンベアーのようにキャラクターを押し戻すような機能をUnityで作成していきたいと思います。 CharacterControllerを使ったキャラクターでもベルトコンベアーの影響を与える事が出来ます。

の辺りで解説しています。

マテリアル個別の記事も作成しないとなぁ・・・・・(-。-)ボソッ

Mizu、Yakusouのインスペクタはほぼ同じで新しく作成し取りつけたItemParamスクリプトの設定値でアイテムの種類を変えているだけです。

Mizu、YakusouにItemParamスクリプトを作成し取りつけます。

ItemParamでは自身のアイテムの種類(item)、アイテムが出現してから消えるまでの時間(deleteTime)、キャラクターが持っている検知エリアに設定するSearchItemスクリプトの参照(sItem)フィールドを宣言します。

Startメソッドで『SearchItemArea』というタグが設定されたキャラクターの検知エリアを探しそこに設定されているSearchItemスクリプトを取得します。

アイテムが出現してからdeleteTimeの時間が経過したらアイテムを削除したいのでコルーチンを使います。

DeleteItemメソッドでは指定秒数経過後SearchItemのDeleteItemメソッドを呼び出します。

SearchItemのDeleteItemメソッドではキャラクターの検知エリア内にこのアイテムが登録されていた場合にこのアイテムを削除する処理です。

その後自身のゲームオブジェクトを削除しています。

SearchItemスクリプトは後で作成します。

Mizu、YakusouのインスペクタでItemParamのアイテムの種類の変更、出現してから削除するまでの時間を設定しておきます。

アイテムゲームオブジェクトのサンプル

↑が作成したアイテムで左が薬草、右が水のアイテムになります。

これで水、薬草アイテムが出来たのでAssetsフォルダにドラッグ&ドロップしプレハブ化し、ヒエラルキー上にあるMizuとYakusouは削除します。

敵キャラクターの作成

次に敵キャラクターを作成していきます。

今回の敵キャラクターはCharacterControllerを取りつけコライダの調整をし、TagにEnemyを設定しておきます。

敵キャラクターにDropItemEnemyスクリプトを取りつけます

アイテムをドロップするだけの敵キャラクターを作りたいので移動等の機能は付けず攻撃されたら倒れアイテムをドロップするだけにしておきます。

敵キャラクターに設定するAnimator Controllerには攻撃を受けた時に倒れる状態を作成しアニメーションパラメータのDeadがトリガーされたら遷移するように作成します。

アイテムを落とす敵のAnimatorController

アニメーションの再生が終わったら敵を消したいのでDeadメソッドを用意し、AnimatorControllerに作ったDead状態に設定したアニメーションクリップにアニメーションイベントDeadを設定しDeadメソッドを呼び出すことにします。

アニメーションイベントについては

Unityで主人公が敵に近づいた時に敵が主人公を攻撃してくる機能を作成します

を参照してください。

倒れるアニメーションの最後にDeadアニメーションイベントを追加

↑のように倒れるアニメーションの最後の方にアニメーションイベントを作成しDeadメソッドが実行されるようにします。

敵のインスペクタでDropItemEnemyの設定をします。

DropItemEnemyにアイテムを設定する

↑のように敵ごとに落としたいアイテムプレハブを設定します。

これで敵の準備は終了です。

アイテムをサーチする機能の作成

作成した主人公キャラクターにアイテムを検知するエリアを作成します。

キャラクターの子要素に右クリック→Create Emptyを選択し名前をSearchItemAreaとします。

インスペクタのAdd ComponentからBox Colliderを取りつけIs Triggerにチェックを入れます。

今回取り付けるのはBox Colliderですが他のコライダでも大丈夫です。

キャラクターの子要素にアイテム検知エリアを作成

↑のようにキャラクターの子要素にアイテム検知エリアを作成します。

インスペクタでBox Colliderのサイズを調整しアイテムを検知するエリアを変更します。

アイテム検知エリアのサイズを調整

SearchItemAreaにはSearchItemという新しいスクリプトを取りつけます。

実際のアイテム検知エリア

↑が実際のアイテム検知エリアです。

自身の手が届く範囲を検知エリアにしてもいいですし、もっと大きいエリアを作成しアイテムを簡単に取得出来るようにしてもいいかもしれません。

次にアイテム情報表示UIを作成します。

ヒエラルキー上でUI→Canvasを作成し名前をGetItemCanvasとし、その子要素にUI→Textを設定します。

アイテム関連用UI

アイテム用のUIのインスペクタは以下のようになります。

アイテム関連用UIのインスペクタ

アイテム情報表示UIは最初は見えなくしたいのでGetItemCanvasのインスペクタを表示し名前の横のチェックボックスのチェックを外しておきます。

アイテム検知スクリプトSearchItemArea

SearchItemAreaに取り付けたSearchItemスクリプトは以下のようになります。

長いスクリプトなので細かく見ていきましょう。

フィールド宣言部分

まずはフィールド宣言部分です。

キャラクターのアイテム検知エリアにアイテムが入ったら自動でアイテムを取得する『オート』、アイテム取得のボタンを押したらアイテム検知エリア内にあるアイテムの中で一番近くにあるものを取得する『マニュアル』モードを作りました。

インスペクタで設定出来るようにし、モードによって処理を変えていきます。

MyItemは主人公に設定するスクリプトでアイテムを取得した時の処理を書きます。

MyItemは後で作成します。

アイテム検知エリアに入ったアイテムのゲームオブジェクトを入れておくのがitemListでコレクションを使って管理します。

『アイテムを取得する』や『○○を手に入れた』のようなテキストを表示するUIの親要素のCanvasをインスペクタで設定しこれをオン・オフする事で表示・非表示をします。

検知エリアにアイテムが入った時、出た時の処理

次に検知エリアにアイテムが入った時、アイテムが検知エリアを出た時の処理を見ていきます。

OnTriggerEnterは自身のコライダに他のコライダが侵入した時に実行されるので、タグを確認しItemだったらモードを確認し、

自動取得モードであればそのアイテムの情報を取得し、MyItemスクリプトのSetItemメソッドに引数として渡しアイテム取得処理をします。

アイテムを取得したらアイテムゲームオブジェクトを消しています。

手動取得モードであれば自身のスクリプトにあるSetItemメソッドを呼び出し、アイテムリストにそのアイテムを追加します。

手動なのでアイテムの取得を促す文字列をUIに表示します。

アイテムの侵入をOnTriggerEnterにしていますが、OnTriggerStayの方が常に確認出来るのでそちらの方がきびしくチェック出来るかもしれません。

OnTriggerExitは自身のコライダから他のコライダが出て行った時に実行されるので出ていったのがItemタグを持つものだったら、

アイテムリストにそのゲームオブジェクトが含まれているかどうかをitemList.Containsで確認し、存在したらアイテムリストから削除します。

アイテムリストにアイテムがなくなったらアイテム取得を促すUIを非表示にします。

アイテムリストの処理、手動モード時の取得アイテムの処理

SetItemメソッドでは引数に追加するアイテムゲームオブジェクトを受け取り、そのアイテムがアイテムリストに含まれていなければリストに追加します。

DeleteItemメソッドはその逆でアイテムリストにアイテムが存在していればリストから削除します。

SelectItemメソッドはキャラクターに設定したDropItemCharaスクリプトから呼び出していたメソッドで、アイテムリストの中からキャラクターに一番近いアイテムを探し、

そのアイテムを取得する為の処理です。

一番近いアイテムの初期値としてアイテムリストの0番目のTransform情報を入れておきます。

それと同時にキャラクターとアイテムとの距離も計算しておきます。

キャラクターはこのスクリプトの一番大元の位置情報なのでtransform.rootで大元に移動しそのpositionで位置を取得しています。

アイテムリスト数分繰り返し、一番近いアイテムを探します。

一番違いアイテムが見つかったらアイテム取得処理をしアイテムリストからそのアイテムを削除し最後にアイテムゲームオブジェクトを削除しています。

これでSearchItemスクリプトが出来ました。

さきほどの画像でも載っていますが、再度SearchItemスクリプトの設定を見てみましょう。

SearchItemスクリプトの設定

↑のように取得モードの設定、MyItemスクリプトの設定(MyItemスクリプトはこの後作ります)、メッセージ表示用UIを設定します。

アイテム取得スクリプトMyItemの作成

最後にアイテム取得処理をするMyItemスクリプトを作成し主人公キャラクターに取りつけます。

スクリプトは主人公キャラに取りつけなくてもアイテム管理をする空のゲームオブジェクトを作成しておきそこにスクリプトを設定しアイテム管理をするという方法でもいいかもしれません。

今回はキャラクター自身にスクリプトを設定する事にしました。

アイテム数分の配列を用意しStartメソッドで全て0に初期化しています。

SetItemメソッドはItem型の引数を受け取り

items[(int)item] += 1;

でアイテムの列挙型をint型に変換しその要素に+1をしています。

こうすることでそのアイテムの要素の数を増やす事が出来ます。

列挙型はItem.Yakusou、Item.Mizuですが、内部的には0、1という値が設定されている為int型にキャストする事でそれぞれの要素に1を足す事が出来ます。

列挙型に関しては

Unityで主人公キャラクターが敵キャラクターに近づいたら敵が主人公に近づいてくるようにしたいと思います。

に記述しているので参照してください。

アイテム取得時に『薬草を手に入れた』といった文字列を表示しますが、前の文字に追加する形を取っていきます。

そこでテキストUIにすでに文字が入っていたら改行文字を入れた後に次の文字列を入れるようにしています。

テキストUIが表示されていなければコルーチンを使ってテキストを表示し10秒経ったらテキストUIを非表示にしています。

UIの処理に関してはあんまり力を入れて作らなかったので多少変な処理になっています。

例えば最初にアイテムを取得し9秒経った時はまだUIが表示されていますがその時に別のアイテムを取ると1秒後にはUIが消えてしまいます。

本来であれば別のアイテム取得時からまた10秒経ったら消すようにしたいところです。

コルーチンで時間計測せず時間計測用の変数を用意しUpdateでTime.deltaTimeを時間用変数に足していく形であればその変数値を0に戻してやる事でうまくいきそうですね。

今回はまぁ・・・不具合ありバージョンで・・・・(;一_一)

アイテム取得時にアイテム数を確認する為Debug.Logでそれぞれのアイテム数を表示するようにしています。

主人公がアイテムに乗ってしまう

今の状態だとアイテム取得モードが手動の場合、アイテムのゲームオブジェクトが小さい為に主人公がアイテムの上に乗ってしまう事があります。

主人公がアイテムの上に乗ってしまう

↑のようにアイテムに乗っかってしまいます。

そこで主人公にはHumanというレイヤーを設定(親だけで子は変えない)し、アイテムにはItemというレイヤーを設定しお互いの当たり判定をしないようにします。

UnityのEditメニューからProject Settings→Physicsを選択します。

Physicsの設定で当たり判定の設定をする

レイヤー毎の当たり判定の設定をする

↑のように表示されるので、HumanとItemのチェックを外し当たり判定をしないようにします。

こうすることでHumanレイヤーを設定した主人公とItemレイヤーを設定した主人公があたらないようになります。

これで全ての機能が完成しました!

アイテムドロップ、取得機能の確認

機能が完成したので水、薬草それぞれをドロップする敵キャラクターを量産して配置し攻撃して確認してみます。

主人公の周りに敵を大量に置いて確認してみましょう。

主人公の周りに敵を大量におく

主人公の周りに敵を大量に配置してみました。

ゾンビの朝会ですね(‘_’)

アイテム検知エリアのXとZを3にして少し広げます。

まずは取得モードを自動(auto)にして確認してみます。

↑のようになりました。

アイテム取得の情報が多すぎてオーバーフローしてますが・・・・通常のゲームであればこんなにアイテムを取得しないので大丈夫だとは思いますが、もし多くのアイテムを一気に取得する場合は表示するテキストUIの処理も変更する必要がありますね。

次は取得モードを手動(manual)にして確認してみます。

↑のようになりました。

アイテムを検知する時にOnTriggerEnterを使用している為、アイテムが検知エリア内にある時でも『アイテム取得』の文字が表示されない事があります。

やっぱりOnTriggerStayを使った方がいいのかもしれません・・・(‘_’)

また手動でアイテムを取得する時に押すボタンは攻撃ボタンと同じ条件とタイミングの時に行っている為、アイテムを取得する際にも必ず攻撃をしてしまいます。

これに対処するには攻撃ボタンとアイテムを取得するボタンを分けたり、キャラクターの状態変数を用意しアイテム取得モードの時に攻撃ボタンを押したらアイテムを取得、

通常の状態の時は攻撃と分けるといいと思います。

ただしアイテムが検知エリアにある時にキャラクターの状態をアイテム取得モードにすると近くの敵を攻撃しようと思って攻撃ボタンを押してもアイテム取得をしようとするので、

攻撃が出来なくなってしまいます。

なかなか制御が難しいですね・・・・(-_-)

終わりに

敵を倒した時にアイテムを落としそれを取得するという流れの機能が作成出来ました。

だいぶ改善する必要はありますが基本的な部分は出来たような気がします。

MyItemのSetItemメソッドでは取得する為の処理しか書いていませんがアイテムを使った時の処理も合わせて出来るとよかったですね。

引数で増減する値を渡して処理を変えるといいかもしれませんね。

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます