質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

1回答

4784閲覧

特定のタグもしくはレイヤーが設定された物体に接触しているか否かを判定したい

TomoeEbihara

総合スコア6

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2018/02/03 23:07

編集2022/01/12 10:55

初めて質問させて頂きます。TomoeEbihara と申します。ゲーム作りに興味を持ちまして、Unity の勉強を始めました。Unity のバージョンは「2017.3.0f3」です。練習のつもりで、2D の『倉庫番』を作ってみようと思いたち、色々な教材を参考にしながら、ようやくゲームパッドの左スティックで主人公を上下左右に動かすところまで作りました。以下のスクリプトを、主人公「Player」にセットしています。ゲームパッドで「target」を動かし、主人公は常にそこに向かって歩き続けているという仕組みです。

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Player : MonoBehaviour 6{ 7 float x_axis; 8 float y_axis; 9 10 Vector3 target = new Vector3(0, 0, 0); 11 int grid = 1; 12 float speed = 3f; 13 14 // Use this for initialization 15 void Start(){} 16 17 // Update is called once per frame 18 void Update() 19 { 20 PlayerControll(); 21 Walk(); 22 } 23 24 void PlayerControll() 25 { 26 x_axis = Input.GetAxisRaw("Horizontal"); 27 y_axis = Input.GetAxisRaw("Vertical"); 28 29 //「Raycast」という機能を使って、主人公の周囲に線を引く 30 RaycastHit2D HitTop, HitRight, HitBottom, HitLeft; 31 HitTop = Physics2D.Raycast(transform.position + new Vector3(-0.4f, 0.5f), Vector2.right, 0.8f); 32 HitRight = Physics2D.Raycast(transform.position + new Vector3(0.5f, 0.4f), Vector2.down, 0.8f); 33 HitBottom = Physics2D.Raycast(transform.position + new Vector3(-0.4f, -0.5f), Vector2.right, 0.8f); 34 HitLeft = Physics2D.Raycast(transform.position + new Vector3(-0.5f, 0.4f), Vector2.down, 0.8f); 35 36 //主人公が「target」と同じ位置にいて、進行方向に障害物が無い場合、ゲームパッドで「target」の座標を変更する 37 if (target == transform.position) 38 { 39 if (HitTop.transform == null) {if (y_axis > 0) { target += new Vector3(0, grid, 0); x_axis = 0;}} 40 if (HitRight.transform == null) {if (x_axis > 0) { target += new Vector3(grid, 0, 0); y_axis = 0;}} 41 if (HitBottom.transform == null) {if (y_axis < 0) { target -= new Vector3(0, grid, 0); x_axis = 0;}} 42 if (HitLeft.transform == null) {if (x_axis < 0) { target -= new Vector3(grid, 0, 0); y_axis = 0;}} 43 } 44 45 } 46 47 //主人公を「target」に向かって歩かせる 48 void Walk() {transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);} 49}

ブロックの壁「Wall」がある場合は、それより先には進めない様にしたいと思っています。そこで、今は「Raycast」機能で、進行方向にブロックの壁があるかどうかを判定し、無い場合にのみ、進める様にしています。その為に、ブロックの壁には「Box Collider 2D」をセットしています。

ただ『倉庫番』は倉庫の中に散らばった荷物を所定の位置まで運ぶという内容のゲームですので、主人公が荷物に接触している事を判定させる為には、壁だけでなく、荷物の方にも「Box Collider 2D」をセットしなければならないのかな、と考えています。

しかし、どうやら今のスクリプトだと「Wall」だけでなく「Box Collider 2D」をセットした荷物の方まで障害物となってしまう様なのです。しかし「Box Collider 2D」を外してしまうと、主人公と荷物が接触しているかどうかを判定できない気がしています。なので、ブロックの壁には「Wall」荷物の方には「Baggage」というタグまたはレイヤーを設定して、下記の様に、主人公が接触した場合の処理を書き込む事ができれば便利だなと思っています。

現状

C#

1if (HitTop.transform == null) 2if (HitRight.transform == null) 3if (HitBottom.transform == null) 4if (HitLeft.transform == null)
書き換え案(タグ)

C#

1//壁と接触 2if (HitTop が「Wall」というタグが設定された物と接触) {上に進めない} 3if (HitRight が「Wall」というタグが設定された物と接触) {右に進めない} 4if (HitBottom が「Wall」というタグが設定された物と接触) {下に進めない} 5if (HitLeft が「Wall」というタグが設定された物と接触) {左に進めない} 6 7//荷物と接触 8if (HitTop が「Baggage」というタグが設定された物と接触) {上に荷物を押せる} 9if (HitRight が「Baggage」というタグが設定された物と接触) {右に荷物を押せる} 10if (HitBottom が「Baggage」というタグが設定された物と接触) {下に荷物を押せる} 11if (HitLeft が「Baggage」というタグが設定された物と接触) {左に荷物を押せる}
書き換え案(レイヤー)

C#

1//壁と接触 2if (HitTop がレイヤー「Wall」に設置された物と接触) {上に進めない} 3if (HitRight がレイヤー「Wall」に設置された物と接触) {右に進めない} 4if (HitBottom がレイヤー「Wall」に設置された物と接触) {下に進めない} 5if (HitLeft がレイヤー「Wall」に設置された物と接触) {左に進めない} 6 7//荷物と接触 8if (HitTop がレイヤー「Baggage」に設置された物と接触) {上に荷物を押せる} 9if (HitRight がレイヤー「Baggage」に設置された物と接触) {右に荷物を押せる} 10if (HitBottom がレイヤー「Baggage」に設置された物と接触) {下に荷物を押せる} 11if (HitLeft がレイヤー「Baggage」に設置された物と接触) {左に荷物を押せる}

参考にしていただけるかどうか分かりませんが、以下に Unity の画像を貼り付けておきます。

Player

イメージ説明

Wall

イメージ説明

ゲーム画面

イメージ説明

参考リンク

倉庫番 - Wikipedia
倉庫番(ゲームボーイ版の動画)

まだまだ、Unity やプログラミングの知識が全然足りなくて、試行錯誤しながら学習を進めているところです。こういう場合にはタグを使うのが良いのか、それともレイヤーを使う方が良いのか、もしくは他にもっと良い方法があるのかなど、是非ご教示いただきたいです。質問した部分以外にも、もし何か気づいた事があればアドバイスを頂きたいです。よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

こういう場合にはタグを使うのが良いのか、それともレイヤーを使う方が良いのか

この場合はレイヤーの方がいいかなと思います。
BaggageオブジェクトのLayerを「Ignore Raycast」に変更してください。
このレイヤーが設定されているオブジェクトはRaycastの衝突対象外になるので、障害物と見なされなくなります。

あるいは、Raycastを呼ぶ時にBaggageオブジェクトのLayerを除外する方法もあります。
RayCastその2、衝突するオブジェクトの制限【Unity】 - (:3[kanのメモ帳]
この方法の利点は、そもそも衝突判定が実行されなくなるのでちょっとだけ処理が軽くなります(体感出来る差ではないとは思いますが)。

もしもRaycastの衝突は維持しつつif文で書く場合はタグの方が簡潔です。
if (HitTop.transform.tag == "Wall") {HitTop が「Wall」というタグが設定された物と接触している}
if (LayerMask.LayerToName(HitTop.transform.gameObject.layer) == "Wall") {HitTop が「Wall」というレイヤーが設定された物と接触している}

投稿2018/02/03 23:39

sakura_hana

総合スコア11425

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TomoeEbihara

2018/02/04 08:09 編集

sakura_hana さま 迅速な回答ありがとうございます! 荷物のレイヤーを「Ignore Raycast」に変更する方法は上手くいきました。 しかし、それ以外の方法が上手く行きませんでしたので、よろしければ補足して頂けると嬉しいです。ブロックの壁のプレハブに「Wall」というタグとレイヤーを設定し、主人公との接触を判定できるか否かを検証してみました。 【1】LayerMask 教えて頂いたホームページを参考に、Player.cs に以下のコードを書き加えてみました。 ''' int layerMask = 0; Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask); ''' すると「(ray, out」の部分に赤い波線が引かれ、この様なエラーが出てしまいました。 ''' エラー: Feature 'tuples' is not available in C# 4. Please use language version 7 or greater. エラー: Invalid token '(' in class, struct, or interface member declaration ''' 【2】変換メソッドでレイヤーマスクを取得する 以下のコードを、Player.cs に書き加えてみました。 ''' int layerMask = LayerMask.GetMask(new string[] { "Wall" }); ''' すると、以下のエラーが出てしまいました。 ''' UnityException: NameToLayer is not allowed to be called from a MonoBehaviour constructor (or instance field initializer), call it in Awake or Start instead. Called from MonoBehaviour 'Player' on game object 'Player'. See "Script Serialization" page in the Unity Manual for further details. UnityEngine.LayerMask.GetMask (System.String[] layerNames) (at /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/LayerMaskBindings.gen.cs:43) ''' 【3】Inspectorからレイヤーマスクを指定する 以下のコードを、Player.cs に書き加えてみました。 ''' [SerializeField] private LayerMask layerMask; ''' エラーも無く、主人公の Inspector に「Layer Mask」という項目が追加され、プルダウンメニューから「Wall」を選べる様になりましたが、それ以外は何も変化がありませんでした。 【4】定数クラスでレイヤーマスクを指定 すみません。 こちらは内容が難しくて、まだ試せていません。 【4】トリガーは検出しない 「PhysicsManager」に「Raycasts Hit Triggers」という項目がありませんでした。同じ位置に、代わりに「Queries Hit Triggers」という項目があったので、チェックを外してみましたが、変化はありませんでした。ブロックの壁のプレハブの「Is Trigger」のチェックを付けたり外したりしてみましたが、変化はありませんでした。(どの場合も、やはり「Box Collider 2D」が付いている状態では主人公が通り抜けられず「Ignore Raycast」を使った時の様に、すり抜けることはできませんでした) 【5】教えて頂いた if 文について また、if 文の条件を「if (HitTop.transform.tag == "Wall")」または「if (LayerMask.LayerToName(HitTop.transform.gameObject.layer) == "Wall")」へと変更する方法を試してみたところ、どちらの場合も以下の様なエラーが出てしまいました。 ''' NullReferenceException: Object reference not set to an instance of an object Player.PlayerControll () (at Assets/Player.cs:48) Player.Update () (at Assets/Player.cs:30) ''' もしかすると、スクリプトが「Wall」タグ、またはレイヤーを見つけられていないという意味かなと思いまして「void Start」の中に、下の一行を書き加えてもみたのですが、改善しませんでした。 ''' GameObject[] wall = GameObject.FindGameObjectsWithTag("Wall"); ''' タグの取得については、これらの記事を参考にしました。 -> nekosuko.jp/blog/?p=365 -> tech.pjin.jp/blog/2016/06/28/unity_skill_1/ 「PhysicsManager」の「Layer Collision Matrix」で、主人公がいる「Default」レイヤーと「Wall」レイヤーが交わるチェックを外してみたりもしましたが、やはり変化はありませんでした。 「Layer Collision Matrix」については、こちらのページを参考にしました。 https://docs.unity3d.com/ja/530/Manual/LayerBasedCollision.html 長文になりまして、申し訳ありません。 お時間のある時で構いませんので、アドバイスを頂けると嬉しいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問