今回はUnityで敵を倒した時にその敵が持っているアイテムをその場に落とすような機能を作成し、
主人公キャラクターがアイテムに近付くと設定したモードによってアイテムを自動で取得出来るようにしたり、キーを押した時に手動で取得出来るような機能を作成していきます。
サンプル用のキャラクター作成
今回の機能を作る前に移動と攻撃が出来るキャラクターを作成する必要があります。
移動と攻撃の機能
移動に関しては
攻撃に関しては
を参照してください。
連続攻撃は特に作る必要はありませんが・・・・スクリプトの条件等を変更する必要が出てきます。
キャラクターにはDropItemCharaという移動と攻撃をさせるスクリプトを取りつけます。
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 | using UnityEngine; using System.Collections; public class DropItemChara : MonoBehaviour { private Animator animator; private CharacterController characterController; private Vector3 velocity; void Start () { animator = GetComponent<Animator>(); characterController = GetComponent<CharacterController>(); velocity = Vector3.zero; } void Update () { // キャラクターコライダが接地、またはレイが地面に到達している場合 if(characterController.isGrounded) { // 地面に接地してる時は初期化 if(characterController.isGrounded) { velocity = Vector3.zero; // レイを飛ばして接地確認の場合は重力だけは働かせておく、前後左右は初期化 } else { velocity = new Vector3(0, velocity.y, 0); } var input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); // 方向キーが多少押されている if(input.magnitude > 0.1f && !animator.GetCurrentAnimatorStateInfo(0).IsName("Attack1") && !animator.GetCurrentAnimatorStateInfo(0).IsName("Attack2") && !animator.GetCurrentAnimatorStateInfo(0).IsName("Attack3") ) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += transform.forward * 2; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0); } // 攻撃 if(Input.GetButtonDown("Fire1") && !animator.IsInTransition(0) // 遷移途中でない ) { animator.SetBool ("Attack", true); // 手動でアイテムを取得する場合は攻撃出来る条件と同じ条件で取得処理を呼び出す transform.GetComponentInChildren <SearchItem>().SelectItem (); } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } } |
キャラクターの移動と攻撃に関しての説明は先ほど紹介した記事を参照してください。
攻撃時に自身の子要素であるSearchItemスクリプトを取得しその中のSelectItemメソッドを実行しています。
SearchItemスクリプトは後で作成しますが、このSearchItemスクリプトは主人公キャラの子要素にあるサーチエリアに取り付け、近くにあるアイテム情報を保持させます。
その中のSelectItemメソッドはアイテム情報の中で一番近くにあるアイテムを取得する処理になります。
武器に取り付けるスクリプト
主人公キャラクターの持っている武器にスクリプトを設定します(武器にはコライダが設定されている必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 | using UnityEngine; using System.Collections; public class DropItemMyAttack : MonoBehaviour { void OnTriggerEnter(Collider col) { if(col.tag == "Enemy") { col.transform.GetComponent<DropItemEnemy>().TakeDamage(); } } } |
武器にはコライダを設定していますが、このコライダがEnemyタグを設定した敵のコライダに接触した時DropItemEnemyスクリプトのTakeDamageメソッドを呼び出します。
DropItemEnemyは後で作成する敵キャラクター用のスクリプトです。
アイテムプレハブの作成
次に敵を倒した時に落とすアイテムをプレハブとして作成します。
アイテム用の3DCGがないのでCubeを作成しマテリアルを設定する事でアイテムを表現する事にします。
ヒエラルキー上で右クリック→3D Object→Cubeを選択し2つのCubeを作成します。
名前をMizuとYakusouと変更します。
TagにItemを作成し、それぞれ設定しておきます。
今回敵がドロップするアイテムはこの2種類だけ作成することにしましょう。
それぞれRigidbodyコンポーネントを取りつけ、Use Gravityにチェックを入れ重力を働かせる事にします。
作成したMizuは以下のようになります。
Mizu用のマテリアルを作成し取りつけています(画像ではEyesとなっていますが気にしないでください・・・・・)。
マテリアルはAssetsフォルダで右クリック→Create→Materialで作成する事が出来ます。
マテリアルに関しては
の辺りで解説しています。
マテリアル個別の記事も作成しないとなぁ・・・・・(-。-)ボソッ
Mizu、Yakusouのインスペクタはほぼ同じで新しく作成し取りつけたItemParamスクリプトの設定値でアイテムの種類を変えているだけです。
Mizu、YakusouにItemScriptスクリプトを作成し取りつけます。
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 | using UnityEngine; using System.Collections; // アイテムの名前の列挙型 public enum Item { Yakusou, Mizu } public class ItemScript : MonoBehaviour { public Item item; // どのアイテムか public float deleteTime; // アイテムが消えるまでの時間 private SearchItem searchItem; void Start() { searchItem = GameObject.FindWithTag ("SearchItemArea").GetComponent <SearchItem> (); // アイテムが登場したら消す処理をスタート StartCoroutine (DeleteItem ()); } public Item GetItem() { return item; } // 指定時間が経過したらアイテムを削除 IEnumerator DeleteItem() { yield return new WaitForSeconds(deleteTime); searchItem.DeleteItem(this.gameObject); Destroy(this.gameObject); } } |
ItemScriptでは自身のアイテムの種類(item)、アイテムが出現してから消えるまでの時間(deleteTime)、キャラクターが持っている検知エリアに設定するSearchItemスクリプトの参照(searchItem)フィールドを宣言します。
Startメソッドで『SearchItemArea』というタグが設定されたキャラクターの検知エリアを探しそこに設定されているSearchItemスクリプトを取得します。
アイテムが出現してからdeleteTimeの時間が経過したらアイテムを削除したいのでコルーチンを使います。
DeleteItemメソッドでは指定秒数経過後SearchItemのDeleteItemメソッドを呼び出します。
SearchItemのDeleteItemメソッドではキャラクターの検知エリア内にこのアイテムが登録されていた場合にこのアイテムを削除する処理です。
その後自身のゲームオブジェクトを削除しています。
SearchItemスクリプトは後で作成します。
Mizu、YakusouのインスペクタでItemScriptのアイテムの種類の変更、出現してから削除するまでの時間を設定しておきます。
↑が作成したアイテムで左が薬草、右が水のアイテムになります。
これで水、薬草アイテムが出来たのでAssetsフォルダにドラッグ&ドロップしプレハブ化し、ヒエラルキー上にあるMizuとYakusouは削除します。
敵キャラクターの作成
次に敵キャラクターを作成していきます。
今回の敵キャラクターはCharacterControllerを取りつけコライダの調整をし、TagにEnemyを設定しておきます。
敵キャラクターにDropItemEnemyスクリプトを取りつけます
アイテムをドロップするだけの敵キャラクターを作りたいので移動等の機能は付けず攻撃されたら倒れアイテムをドロップするだけにしておきます。
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 | using UnityEngine; using System.Collections; public class DropItemEnemy : MonoBehaviour { // 落とすアイテムゲームオブジェクト public GameObject dropItemObj; private Animator animator; // 死んだかどうか private bool isDead; private void Start() { animator = GetComponent<Animator>(); } // ダメージを受けた時の処理 public void TakeDamage() { // 死んでいなければアイテムを落とす if (!isDead) { isDead = true; // 設定したアイテムを敵の1m上から落とす Instantiate<GameObject>(dropItemObj, transform.position + Vector3.up, Quaternion.identity); // 敵のDeadアニメーションを再生 animator.SetTrigger ("Dead"); } } // アニメーションイベントDeadを受け取ったらゲームオブジェクトを消す void Dead() { Destroy (this.gameObject); } } |
敵キャラクターに設定するAnimator Controllerには攻撃を受けた時に倒れる状態を作成しアニメーションパラメータのDeadがトリガーされたら遷移するように作成します。
アニメーションの再生が終わったら敵を消したいのでDeadメソッドを用意し、AnimatorControllerに作ったDead状態に設定したアニメーションクリップにアニメーションイベントDeadを設定しDeadメソッドを呼び出すことにします。
アニメーションイベントについては
を参照してください。
↑のように倒れるアニメーションの最後の方にアニメーションイベントを作成しDeadメソッドが実行されるようにします。
今回使用したDead用のアニメーションはAsset Storeで『Mecanim Example Scenes』で検索して出てくるUnityさんが作成したメカニムのサンプルのAnimationsフォルダにあるDyingアニメーションを使用しました。
『Mecanim Example Scenes』アセットはメカニムのサンプルで使用している他のアセットもたくさんあるので全部をインポートしない方が良いです。
敵のインスペクタでDropItemEnemyの設定をします。
↑のように敵ごとに落としたいアイテムプレハブを設定します。
これで敵の準備は終了です。
アイテムをサーチする機能の作成
作成した主人公キャラクターにアイテムを検知するエリアを作成します。
キャラクターの子要素に右クリック→Create Emptyを選択し名前をSearchItemAreaとします。
インスペクタのAdd ComponentからBox Colliderを取りつけIs Triggerにチェックを入れます。
今回取り付けるのはBox Colliderですが他のコライダでも大丈夫です。
↑のようにキャラクターの子要素にアイテム検知エリアを作成します。
インスペクタでBox Colliderのサイズを調整しアイテムを検知するエリアを変更します。
SearchItemAreaにはSearchItemという新しいスクリプトを取りつけます。
↑が実際のアイテム検知エリアです。
自身の手が届く範囲を検知エリアにしてもいいですし、もっと大きいエリアを作成しアイテムを簡単に取得出来るようにしてもいいかもしれません。
次にアイテム情報表示UIを作成します。
ヒエラルキー上でUI→Canvasを作成し名前をGetItemCanvasとし、その子要素にUI→Textを設定します。
アイテム用のUIのインスペクタは以下のようになります。
アイテム情報表示UIは最初は見えなくしたいのでGetItemCanvasのインスペクタを表示し名前の横のチェックボックスのチェックを外しておきます。
アイテム検知スクリプトSearchItem
SearchItemAreaに取り付けたSearchItemスクリプトは以下のようになります。
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 | using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; public class SearchItem : MonoBehaviour { // アイテムを取る時のモード public enum GetMode { Auto, Manual } // 自動でアイテムを取得するか手動で取得するか public GetMode getItemMode; // アイテム管理スクリプト private MyItem myItem; // 近くにあるアイテムのリスト [SerializeField] private List<GameObject> itemList; // メッセージ表示用キャンバス public GameObject itemMessageCanvas; private void Start() { myItem = GetComponentInParent<MyItem>(); } // アイテムがサーチエリア内に入ったら(場合によってはOnTriggerStayを使った方がいいかも?) void OnTriggerEnter(Collider col) { if (col.tag == "Item") { // 自動取得モード if (getItemMode == GetMode.Auto) { myItem.SetItem (col.transform.GetComponent<ItemScript>().GetItem()); Destroy(col.gameObject); // 手動取得モード } else { SetItem (col.gameObject); itemMessageCanvas.GetComponentInChildren <Text>().text = "アイテムを取る"; itemMessageCanvas.SetActive (true); } } } // アイテムがサーチエリア外に出たら void OnTriggerExit(Collider col) { if (col.tag == "Item") { // アイテムリストに存在していたらアイテムリストから削除 if (itemList.Contains (col.gameObject)) { itemList.Remove (col.gameObject); // アイテムが範囲内になくなったら主人公の状態変更 if (itemList.Count <= 0) { itemMessageCanvas.SetActive (false); } } } } // アイテムリストにアイテムを追加する処理 void SetItem(GameObject addItem) { // すでに同じアイテムがあるかどうか if (!itemList.Contains (addItem)) { itemList.Add (addItem); } } // アイテムリストからアイテムを削除する処理 public void DeleteItem(GameObject deleteItem) { if(itemList.Contains(deleteItem)) { itemList.Remove (deleteItem); } } // 手動モードの場合一番近いアイテムを探し取得する public void SelectItem() { if (getItemMode == GetMode.Manual) { // 一つでも検知エリア内にアイテムがあれば if (itemList.Count >= 1) { // アイテムリストの先頭にあるアイテムを初期値にする Transform near = itemList[0].transform; float distance = Vector3.Distance (transform.root.position, near.position); // 一番近いアイテムを探す foreach (var item in itemList) { if (Vector3.Distance (transform.root.position, item.transform.position) < distance) { near = item.transform; distance = Vector3.Distance (transform.root.position, item.transform.position); } } // 一番近いアイテムを取得、アイテムリストから削除、アイテムゲームオブジェクトの削除 itemMessageCanvas.GetComponentInChildren <Text> ().text = ""; itemMessageCanvas.SetActive (false); myItem.SetItem (near.GetComponent <ItemScript> ().GetItem ()); DeleteItem (near.gameObject); Destroy (near.gameObject); } } } } |
長いスクリプトなので細かく見ていきましょう。
フィールド宣言部分
まずはフィールド宣言部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class SearchItem : MonoBehaviour { // アイテムを取る時のモード public enum GetMode { Auto, Manual } // 自動でアイテムを取得するか手動で取得するか public GetMode getItemMode; // アイテム管理スクリプト private MyItem myItem; // 近くにあるアイテムのリスト [SerializeField] private List<GameObject> itemList; // メッセージ表示用キャンバス public GameObject itemMessageCanvas; |
キャラクターのアイテム検知エリアにアイテムが入ったら自動でアイテムを取得する『オート』、アイテム取得のボタンを押したらアイテム検知エリア内にあるアイテムの中で一番近くにあるものを取得する『マニュアル』モードを作りました。
インスペクタで設定出来るようにし、モードによって処理を変えていきます。
MyItemは主人公に設定するスクリプトでアイテムを取得した時の処理を書きます。
MyItemは後で作成します。
アイテム検知エリアに入ったアイテムのゲームオブジェクトを入れておくのがitemListでコレクションを使って管理します。
『アイテムを取得する』や『○○を手に入れた』のようなテキストを表示するUIの親要素のCanvasをインスペクタで設定しこれをオン・オフする事で表示・非表示をします。
検知エリアにアイテムが入った時、出た時の処理
次に検知エリアにアイテムが入った時、アイテムが検知エリアを出た時の処理を見ていきます。
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 | // アイテムがサーチエリア内に入ったら(場合によってはOnTriggerStayを使った方がいいかも?) void OnTriggerEnter(Collider col) { if (col.tag == "Item") { // 自動取得モード if (getItemMode == GetMode.Auto) { myItem.SetItem (col.transform.GetComponent<ItemScript>().GetItem()); Destroy(col.gameObject); // 手動取得モード } else { SetItem (col.gameObject); itemMessageCanvas.GetComponentInChildren <Text>().text = "アイテムを取る"; itemMessageCanvas.SetActive (true); } } } // アイテムがサーチエリア外に出たら void OnTriggerExit(Collider col) { if (col.tag == "Item") { // アイテムリストに存在していたらアイテムリストから削除 if (itemList.Contains (col.gameObject)) { itemList.Remove (col.gameObject); // アイテムが範囲内になくなったら主人公の状態変更 if (itemList.Count <= 0) { itemMessageCanvas.SetActive (false); } } } } |
OnTriggerEnterは自身のコライダに他のコライダが侵入した時に実行されるので、タグを確認しItemだったらモードを確認し、
自動取得モードであればそのアイテムの情報を取得し、MyItemスクリプトのSetItemメソッドに引数として渡しアイテム取得処理をします。
アイテムを取得したらアイテムゲームオブジェクトを消しています。
手動取得モードであれば自身のスクリプトにあるSetItemメソッドを呼び出し、アイテムリストにそのアイテムを追加します。
手動なのでアイテムの取得を促す文字列をUIに表示します。
アイテムの侵入をOnTriggerEnterにしていますが、OnTriggerStayの方が常に確認出来るのでそちらの方がきびしくチェック出来るかもしれません。
OnTriggerExitは自身のコライダから他のコライダが出て行った時に実行されるので出ていったのがItemタグを持つものだったら、
アイテムリストにそのゲームオブジェクトが含まれているかどうかをitemList.Containsで確認し、存在したらアイテムリストから削除します。
アイテムリストにアイテムがなくなったらアイテム取得を促す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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // アイテムリストにアイテムを追加する処理 void SetItem(GameObject addItem) { // すでに同じアイテムがあるかどうか if (!itemList.Contains (addItem)) { itemList.Add (addItem); } } // アイテムリストからアイテムを削除する処理 public void DeleteItem(GameObject deleteItem) { if(itemList.Contains(deleteItem)) { itemList.Remove (deleteItem); } } // 手動モードの場合一番近いアイテムを探し取得する public void SelectItem() { if (getItemMode == GetMode.Manual) { // 一つでも検知エリア内にアイテムがあれば if (itemList.Count >= 1) { // アイテムリストの先頭にあるアイテムを初期値にする Transform near = itemList[0].transform; float distance = Vector3.Distance (transform.root.position, near.position); // 一番近いアイテムを探す foreach (var item in itemList) { if (Vector3.Distance (transform.root.position, item.transform.position) < distance) { near = item.transform; distance = Vector3.Distance (transform.root.position, item.transform.position); } } // 一番近いアイテムを取得、アイテムリストから削除、アイテムゲームオブジェクトの削除 itemMessageCanvas.GetComponentInChildren <Text> ().text = ""; itemMessageCanvas.SetActive (false); myItem.SetItem (near.GetComponent <ItemScript> ().GetItem ()); DeleteItem (near.gameObject); Destroy (near.gameObject); } } } |
SetItemメソッドでは引数に追加するアイテムゲームオブジェクトを受け取り、そのアイテムがアイテムリストに含まれていなければリストに追加します。
DeleteItemメソッドはその逆でアイテムリストにアイテムが存在していればリストから削除します。
SelectItemメソッドはキャラクターに設定したDropItemCharaスクリプトから呼び出していたメソッドで、アイテムリストの中からキャラクターに一番近いアイテムを探し、
そのアイテムを取得する為の処理です。
一番近いアイテムの初期値としてアイテムリストの0番目のTransform情報を入れておきます。
それと同時にキャラクターとアイテムとの距離も計算しておきます。
キャラクターはこのスクリプトの一番大元の位置情報なのでtransform.rootで大元に移動しそのpositionで位置を取得しています。
アイテムリスト数分繰り返し、一番近いアイテムを探します。
一番違いアイテムが見つかったらアイテム取得処理をしアイテムリストからそのアイテムを削除し最後にアイテムゲームオブジェクトを削除しています。
これでSearchItemスクリプトが出来ました。
さきほどの画像でも載っていますが、再度SearchItemスクリプトの設定を見てみましょう。
↑のように取得モードの設定、MyItemスクリプトの設定(MyItemスクリプトはこの後作ります)、メッセージ表示用UIを設定します。
アイテム取得スクリプトMyItemの作成
最後にアイテム取得処理をするMyItemスクリプトを作成し主人公キャラクターに取りつけます。
スクリプトは主人公キャラに取りつけなくてもアイテム管理をする空のゲームオブジェクトを作成しておきそこにスクリプトを設定しアイテム管理をするという方法でもいいかもしれません。
今回はキャラクター自身にスクリプトを設定する事にしました。
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 MyItem : MonoBehaviour { public int [] items = new int[2]; // 持っているアイテム数分の配列を用意 public GameObject canvas; // アイテムを取得した事を表示するテキストキャンバス void Start () { // アイテム数初期化処理 for (int i = 0; i < items.Length; i++) { items [i] = 0; } } // アイテムを取得する処理 public void SetItem(Item item) { // アイテム数を1つ足す items[(int)item] += 1; // アイテム名を入れる変数を宣言 string itemName = ""; // アイテムの種類でアイテム名を設定 if(item == Item.Mizu) { itemName = "水"; } else if(item == Item.Yakusou) { itemName = "薬草"; } // テキストUIを取得 Text preText = canvas.GetComponentInChildren <Text> (); // 表示する文字列を宣言 string putText = ""; // 空文字でなければ改行文字を入れておく if (preText.text != "") { putText = "\n"; } // 表示する文字列に取得したアイテム情報を追加 putText += itemName + "を手に入れた"; preText.text += putText; // アイテム取得表示用UIが表示されていなければUIを表示 if (!canvas.activeSelf) { StartCoroutine (ShowText ()); } // 確認の為、それぞれのアイテム数をコンソールに表示 Debug.Log("持っている水:" + GetItem(Item.Mizu) + " 持っている薬草:" + GetItem(Item.Yakusou)); } // アイテム数を返す public int GetItem(Item item) { return items[(int)item]; } // アイテム取得表示用テキストのオン・オフ、UIを表示してから10秒たったら非表示にする IEnumerator ShowText() { canvas.SetActive (true); yield return new WaitForSeconds (10.0f); canvas.SetActive (false); canvas.transform.GetChild (0).GetComponent <Text> ().text = ""; } } |
アイテム数分の配列を用意しStartメソッドで全て0に初期化しています。
SetItemメソッドはItem型の引数を受け取り
items[(int)item] += 1;
でアイテムの列挙型をint型に変換しその要素に+1をしています。
こうすることでそのアイテムの要素の数を増やす事が出来ます。
列挙型はItem.Yakusou、Item.Mizuですが、内部的には0、1という値が設定されている為int型にキャストする事でそれぞれの要素に1を足す事が出来ます。
列挙型に関しては
に記述しているので参照してください。
アイテム取得時に『薬草を手に入れた』といった文字列を表示しますが、前の文字に追加する形を取っていきます。
そこでテキストUIにすでに文字が入っていたら改行文字を入れた後に次の文字列を入れるようにしています。
テキストUIが表示されていなければコルーチンを使ってテキストを表示し10秒経ったらテキストUIを非表示にしています。
UIの処理に関してはあんまり力を入れて作らなかったので多少変な処理になっています。
例えば最初にアイテムを取得し9秒経った時はまだUIが表示されていますがその時に別のアイテムを取ると1秒後にはUIが消えてしまいます。
本来であれば別のアイテム取得時からまた10秒経ったら消すようにしたいところです。
コルーチンで時間計測せず時間計測用の変数を用意しUpdateでTime.deltaTimeを時間用変数に足していく形であればその変数値を0に戻してやる事でうまくいきそうですね。
今回はまぁ・・・不具合ありバージョンで・・・・(;一_一)
アイテム取得時にアイテム数を確認する為Debug.Logでそれぞれのアイテム数を表示するようにしています。
主人公がアイテムに乗ってしまう
今の状態だとアイテム取得モードが手動の場合、アイテムのゲームオブジェクトが小さい為に主人公がアイテムの上に乗ってしまう事があります。
↑のようにアイテムに乗っかってしまいます。
そこで主人公にはHumanというレイヤーを設定(親だけで子は変えない)し、アイテムにはItemというレイヤーを設定しお互いの当たり判定をしないようにします。
UnityのEditメニューからProject Settings→Physicsを選択します。
↑のように表示されるので、HumanとItemのチェックを外し当たり判定をしないようにします。
こうすることでHumanレイヤーを設定した主人公とItemレイヤーを設定した主人公があたらないようになります。
これで全ての機能が完成しました!
アイテムドロップ、取得機能の確認
機能が完成したので水、薬草それぞれをドロップする敵キャラクターを量産して配置し攻撃して確認してみます。
主人公の周りに敵を大量に置いて確認してみましょう。
主人公の周りに敵を大量に配置してみました。
ゾンビの朝会ですね(‘_’)
アイテム検知エリアのXとZを3にして少し広げます。
まずは取得モードを自動(auto)にして確認してみます。
↑のようになりました。
アイテム取得の情報が多すぎてオーバーフローしてますが・・・・通常のゲームであればこんなにアイテムを取得しないので大丈夫だとは思いますが、もし多くのアイテムを一気に取得する場合は表示するテキストUIの処理も変更する必要がありますね。
次は取得モードを手動(manual)にして確認してみます。
↑のようになりました。
アイテムを検知する時にOnTriggerEnterを使用している為、アイテムが検知エリア内にある時でも『アイテム取得』の文字が表示されない事があります。
やっぱりOnTriggerStayを使った方がいいのかもしれません・・・(‘_’)
また手動でアイテムを取得する時に押すボタンは攻撃ボタンと同じ条件とタイミングの時に行っている為、アイテムを取得する際にも必ず攻撃をしてしまいます。
これに対処するには攻撃ボタンとアイテムを取得するボタンを分けたり、キャラクターの状態変数を用意しアイテム取得モードの時に攻撃ボタンを押したらアイテムを取得、
通常の状態の時は攻撃と分けるといいと思います。
ただしアイテムが検知エリアにある時にキャラクターの状態をアイテム取得モードにすると近くの敵を攻撃しようと思って攻撃ボタンを押してもアイテム取得をしようとするので、
攻撃が出来なくなってしまいます。
なかなか制御が難しいですね・・・・(-_-)
終わりに
敵を倒した時にアイテムを落としそれを取得するという流れの機能が作成出来ました。
だいぶ改善する必要はありますが基本的な部分は出来たような気がします。
MyItemのSetItemメソッドでは取得する為の処理しか書いていませんがアイテムを使った時の処理も合わせて出来るとよかったですね。
引数で増減する値を渡して処理を変えるといいかもしれませんね。