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

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

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

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

Q&A

解決済

1回答

4538閲覧

Unity2Dで同じスクリプトを持つオブジェクトが複数あるとき、クリックされたオブジェクトのみ移動処理を施したい。

shushudayo

総合スコア3

C#

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

0グッド

0クリップ

投稿2020/06/20 16:27

前提・実現したいこと

Unity2Dで同じスクリプトを持つオブジェクトが複数あるとき、クリックされたオブジェクトのみ移動処理を施したい。

発生している問題・エラーメッセージ

クリックでキャラを操作できるスクリプトを用意したのですが、同じキャラを複数体作ったときに特定のキャラをクリックすると、全てのキャラが反応して移動してしまいます。なんとかキャラを識別させたいです。

該当のソースコード

using System.Collections; using System.Collections.Generic; using UnityEngine; public class SoldierMove : MonoBehaviour { private float speed; public enum State { Idle, Ready, Move }; public State state = State.Idle; GameObject clickedGameObject; Vector2 vec // Start is called before the first frame update void Start() { speed = 0.35f; } // Update is called once per frame void Update() { //Stateを3つに分けてます。 //Idleでキャラをクリックしてキャラのゲームオブジェクトを取得 //Readyで移動先をクリック //Moveは移動処理および移動中に再びキャラをクリックするとReadyに遷移できるように switch (state) { case State.Idle: if (Input.GetMouseButtonUp(0)) { clickedGameObject = null; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit2D hit2d = Physics2D.Raycast((Vector2)ray.origin, (Vector2)ray.direction); if (hit2d) { clickedGameObject = hit2d.transform.gameObject; state = State.Ready; } else { state = State.Idle; } }break; case State.Ready: if (Input.GetMouseButtonUp(0)) { vec = Camera.main.ScreenToWorldPoint(Input.mousePosition); var pos = Camera.main.WorldToScreenPoint(transform.localPosition); var rotation = Quaternion.LookRotation(Vector3.forward, Input.mousePosition - pos); transform.localRotation = rotation; state = State.Move; }break; case State.Move: transform.position = Vector2.MoveTowards(transform.position, new Vector2(vec.x, vec.y), this.speed * Time.deltaTime); Vector2 t1 = new Vector2(vec.x, vec.y); Vector2 t2 = transform.position; Vector2 dir1 = t1 - t2; float d1 = dir1.magnitude; if (d1 == 0) { state = State.Idle; } //移動中にクリックすると再びReadyと同じ状態になる。State.Idleと同じ処理です。 if (Input.GetMouseButtonUp(0)) { clickedGameObject = null; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit2D hit2d = Physics2D.Raycast((Vector2)ray.origin, (Vector2)ray.direction); if (hit2d) { clickedGameObject = hit2d.transform.gameObject; state = State.Ready; } }break; } } }

試したこと

補足情報(FW/ツールのバージョンなど)

c#初心者かつ初投稿ゆえわかりづらい書き方をしているかもしれません。申し訳ありません。

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

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

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

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

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

sakura_hana

2020/06/22 00:59

上手くいくか分かりませんが if (clickedGameObject == gameObject) をどこかに挟むとかですかね?(クリックされたオブジェクトは自分自身であることを確認する)
shushudayo

2020/06/22 09:34

ご回答ありがとうございます。いろいろ試してみたのですが、どこに挟んでもうまくいきませんでした…せっかく教えていただいたのに力不足で申し訳ないです。stateがIdleの時点で複数キャラ全員の位置情報が取得されてしまうのが問題だと思うので、stateがIdleの時にオブジェクトを識別できる処理を施せたらいいのですが…
guest

回答1

0

ベストアンサー

ひとつのスクリプトにすべて書くと難しいと思うので「ユーザー入力」と「オブジェクト」でスクリプト分けてみてはいかがでしょうか?

ということで書いてみました

オブジェクト

C#

1using UnityEngine; 2 3public sealed class SoldierMover : MonoBehaviour 4{ 5 [SerializeField] Transform parent; 6 [SerializeField] float speed; 7 8 public enum State { Idle, Ready, Move }; 9 public State CurrentState { get; private set; } 10 11 Vector2 targetPos; 12 13 public void Forcus() 14 { 15 CurrentState = State.Ready; 16 } 17 18 public void FocusOut() 19 { 20 CurrentState = State.Idle; 21 } 22 23 public void MoveTo(Vector2 pos) 24 { 25 CurrentState = State.Move; 26 targetPos = pos; 27 } 28 29 void Update() 30 { 31 if(CurrentState == State.Move) 32 { 33 parent.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime); 34 35 var distance = ((Vector2)parent.position - targetPos).sqrMagnitude; 36 if (System.Math.Abs(distance) < 0.01f) 37 { 38 CurrentState = State.Idle; 39 } 40 } 41 } 42 43 void Reset() 44 { 45 parent = transform; 46 } 47}

** ユーザー入力を受けてオブジェクトに命令を出すスクリプト **

C#

1 2using UnityEngine; 3 4public sealed class SoldierMoverController : MonoBehaviour 5{ 6 SoldierMover forcusObj; 7 8 void Update() 9 { 10 if (Input.GetMouseButtonUp(0)) 11 { 12 var ray = Camera.main.ScreenPointToRay(Input.mousePosition); 13 var hit2d = Physics2D.Raycast(ray.origin, ray.direction); 14 15 if (hit2d.collider != null) 16 { 17 var s = hit2d.collider.gameObject.GetComponent<SoldierMover>(); 18 if (s != null) 19 { 20 switch (s.CurrentState) 21 { 22 case SoldierMover.State.Idle: 23 forcusObj = s; 24 forcusObj.Forcus(); 25 break; 26 case SoldierMover.State.Move: 27 s.FocusOut(); 28 break; 29 } 30 } 31 } 32 else if(forcusObj != null) 33 { 34 switch (forcusObj.CurrentState) 35 { 36 case SoldierMover.State.Ready: 37 forcusObj.MoveTo(Camera.main.ScreenToWorldPoint(Input.mousePosition)); 38 break; 39 } 40 } 41 } 42 } 43} 44

サンプルプロジェクト

SolidierMoveTest.unitypackage
https://19.gigafile.nu/0824-de8c99927f01c7db326bf99c2ac8004bf

コードのヒント

▼ 「Transform parent」
transformは参照(transform.position = など)する度に内部的に取得処理を行なっております。
Updateなどフレーム単位でアクセスが必要な場合はparent = transformなどとしてキャッシュした方が高速に動作します。今回のようにインスペクタから設定するのが最速なのでおすすめです。

▼ 「void Reset()」
インスペクタから設定するのが最速と言いましたが、毎回設定するのは面倒です。
そこでResetを使うと便利です。Reset内に取得設定処理parent = transformを書くことでスクリプトをアタッチした時やインスペクタからアタッチしたスクリプトの右上の歯車マーク > Resetを選択するとResetが走ります。

▼ var distance = ((Vector2)parent.position - targetPos).sqrMagnitude;
Magnitudeはルートを使って計算するので重いです。距離を測る程度であればsqrMagnitudeで十分なのでこっちを使ってください。

▼ System.Math.Abs(distance) < 0.01f
C#のfloatで a == 0は正しい結果が返らない時があります。fpsによって0にならない可能性もあります。
なので必ず 大なり(>) 小なり(<) を使います。今回のようにそれだけでは成り立たない場合Math.Absで絶対値に変換して計算するのがおすすめです。Mathf.Approximatelyという渡された2つのfloatが大体同じか確認する関数もありますが、僕は使ったことないです。

投稿2020/06/24 15:44

編集2020/06/25 11:58
IShix

総合スコア1724

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

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

shushudayo

2020/06/24 16:18

こんなに丁寧にご回答していただき本当に感謝してます!初心者ゆえわからない部分もありそうで不安ですが、明日精読してみます。良い勉強材料になりそうでワクワクしましたのでベストアンサーにさせていただきます!
IShix

2020/06/25 09:47

プログラム楽しいですよね! Unity歴5年ほどですがまだまだワクワクすることが多いです。 簡単にコードのヒントも追加しましたので合わせてみてみてください。 お互い頑張りましょう!
shushudayo

2020/06/25 14:24

なかなか解けず、かなり時間を費やし頭を抱えることも多いのですが、やはり解決できたときの爽快感があるためなんとか続けられてます!処理を速くするための書き方まで追加してくださりありがとうございます!初心者にはこのようなヒントはありがたいです。こちらも応援しています!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問