コード
今unityのカメラの扱いで様々な移動方法について、勉強しているときに。昔のゲームでAtari2600にH.E.R.Oというゲームがあったのですが、あのようにプレイヤーがカメラ外から移動したら、カメラを移動するプログラムを作成しました。
以下がそのコードです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Camera : MonoBehaviour
{
public SpriteRenderer player;
void Start()
{
player = GetComponent<SpriteRenderer>(); //レンダー取得
}
void Update()
{
if (!player.isVisible)
{
transform.position += new Vector3(-18f, 0,transform.position.z);
}
}
}
テストということなので、プレイヤーが画面の左に移動したときだけの処理を書きました。
このまま実行すると以下のエラーが発生します。
There is no 'SpriteRenderer' attached to the "MainCamera" game object, but a script is trying to access it.
You probably need to add a SpriteRenderer to the game object "MainCamera". Or your script needs to check if the component is attached before using it.
対象のレンダーがアタッチされていないとのことなので、ああ。なぁんだ。と思い、プレイヤーオブジェクトをカメラのスクリプトの欄にアタッチしても、変わりません。あれ?と思い、アタッチしているか再確認したら、ゲームを開始するとなしになってアタッチされていないと表示されることがわかったのです。プログラム自体にミスがあるのか確認するために、ゲーム起動中にアタッチすると、カメラがレンダーの対象を認識してくれて、プレイヤーが画面外に移動すると、しっかりとカメラが指定した座標を毎回移動してくれます。ただ、開始時にはこれが実現できません。これを何とかする方法がないのか、調べても全く分かりません。スクリプトから、アタッチする方法があるのかないのか、そもそも他の方法があるのかわかる方がいましたら教えてください。
今修正したところ、以下のようなコードになり。画面にオブジェクトがいるのにもかかわらず、アップデートがずっとこのメソッドを呼び出し、カメラが変数の最大値の座標まで移動してしまいます。
using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Camera : MonoBehaviour
{
public SpriteRenderer player ;
void Update()
{
if (!player.isVisible)
{
transform.position += new Vector3(-18f, 0,transform.position.z);
}
}
}
いろいろと試行錯誤を加えた結果、様々な関数を試しても、同じ挙動になることがわかりました。なお今は、以下のプログラムを組んである状態です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Cam_1 : MonoBehaviour
{
public SpriteRenderer targetRenderer; // 判定したいオブジェクトのrendererへの参照
float x = 1;
void Update()
{
if (targetRenderer.isVisible)
{
Debug.Log("画面に表示されてるよ");
}
else
{
Invoke("CameraInvoke", 5f);
Debug.Log("画面から消えたよ");
}
}
public void CameraInvoke()
{
Camera.main.transform.position += new Vector3(-18f, 0, 0);
}
}
毎回変に思っていたのが、プレイヤーのオブジェクトがスタート時にデバックログで一度消えたと表示された後に、画面に表示されるみたいです。もしかしたら、その一回の感知だけでカメラが連続的に移動する原因となっているみたいです。
改善を試みた結果。。。以下のスクリプトで以下の問題が発覚しました。
肉眼では支障のないレベルですが、ゲーム開始時の2フレームの間にスプライトが表示されていないことがわかりました。
以下のことを試したけど、毎回同じ問題が発生します。
①カメラ判定を利用したほかの関数を利用した。
➁オブジェクトを再配置
以上のことを試したのですが、変化がないことからもしかしたらUnityの仕様かもしれません。お気づきかもしれませんが、問題を確認するために以下のプログラムを専用メソッドに入れ、Invokeを利用して呼び出しています。そうすると、カメラが指定した時間に-36Fの位置にいることがわかりました。つまり、最初の2フレームを判定してプログラムが実行されたのが原因だと考えられます。
Camera.main.transform.position += new Vector3(-18f, 0, 0);//最初の2フレームで二回も実行された?
どうすればいいのかわかりません。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
MainCameraにSpriteRendererが無いのにアクセスしようとしてエラーが出ています。
SpriteRendererはMainCameraにアタッチされているコンポーネントで間違いないのでしょうか?
プレイヤーオブジェクトにアタッチされているべきコンポーネントではないですか?
Start内の処理でMainCameraのSpriteRendererをplayerに設定していますが、その処理は必要ですか?
GetComponentの前後にDebug.Logを入れてplayerの変化をご自身で確認してください。
--- 追記 ---
Renderer.isVisibleはゲームビューだけではなくシーンビューに写っていてもTrueを返します。
シーンビューにplayerが写っていないか確認してください。
修正内容を取り違えていました。
MainCameraの表示領域とCanvasの領域が合っていないのではないでしょうか?
Unity 2D:Canvasをカメラの表示領域に合わせる
[ゲーム開始時の2フレームの対策案]
プレイヤーオブジェクト側にOnBecameVisible()の処理を追加してはどうでしょうか?
OnBecameVisibleはRenderer(SpriteRenderer)コンポーネントがアタッチされたオブジェクトが見えるようになったときに呼び出されます。
Renderer.OnBecameVisible()
プレイヤーオブジェクト側の処理。
public class Hoge : MonoBehaviour
{
public bool isBecameVisible;
private void OnBecameVisible()
{
Debug.Log("OnBecameVisible");
isBecameVisible = true;
}
}
Cam_1の修正
public class Cam_1 : MonoBehaviour
{
public SpriteRenderer targetRenderer; // 判定したいオブジェクトのrendererへの参照
float x = 1;
public Hoge player; // <--- 追加 --->
void Update()
{
// <--- 追加 --->
if (!player.isBecameVisible)
return;
// <------------>
if (targetRenderer.isVisible)
{
Debug.Log("画面に表示されてるよ");
}
else
{
Invoke("CameraInvoke", 5f);
Debug.Log("画面から消えたよ");
}
}
public void CameraInvoke()
{
Camera.main.transform.position += new Vector3(-18f, 0, 0);
}
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.10%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2020/08/26 23:09 編集
もうないのであれば、別の関数を使うことを検討しています。
2020/08/27 00:17 編集
指定されていなければスクリプトでGetComponentする必要があります。
しかし、player = GetComponent<SpriteRenderer>();と記述した場合はCamera(Kirby2318さんが作成したスクリプト)がアタッチされているオブジェクトからSpriteRendererコンポーネントを取得するという意味になります。
つまり、MainCameraオブジェクトからSpriteRendererコンポーネントを取得しようとして、MainCameraオブジェクトにSpriteRendererコンポーネントが存在しないためnullが返ってきているということです。
2020/08/27 00:32
2020/08/27 01:48
2020/08/27 02:52 編集
上記の私の説明で解決できない、理解できない場合はどのような説明の仕方であれば理解しやすいかをご返答いただければ可能な限り対応したいと思います。
2020/08/27 15:49
2020/08/27 16:46 編集
下記のAとBのGetComponentの違いについては理解できますか?
(A) targetRenderer = GetComponent<Renderer>();
(B) targetRenderer = obj.GetComponent<Renderer>(); // objは判定したいオブジェクト
2020/08/27 17:11
(B)はobjに付与されているRendererコンポーネントをtargetRenderer変数に代入している状態?
あれ、それだと同じなのかな。。。あまり理解できていないかもしれないです。
2020/08/27 19:20
Aの場合は、Cam_1スクリプトがアタッチされているMainCameraオブジェクトのRenderer(SpriteRenderer)コンポーネントを取得します。
しかし、MainCameraオブジェクトにはRenderer(SpriteRenderer)コンポーネントはアタッチされていないためnullを返し、targetRendererはnullとなります。
Bの場合は、objオブジェクトにアタッチされているRenderer(SpriteRenderer)コンポーネントを取得します。
objオブジェクトにはRenderer(SpriteRenderer)コンポーネントがアタッチされているので、targetRendererにはobjオブジェクトのRenderer(SpriteRenderer)が入ります。
Cam_1はinspector上でtargetRendererにRenderer(SpriteRenderer)がセットされているのでStart内でGetComponent<Renderer>()をする必要はありません。
しかし、AのtargetRenderer = GetComponent<Renderer>();をしてしまったためにtargetRendererがnullになっていました。
私がStart内でGetComponentをする必要はないと書いた理由は理解できたでしょうか?
Kirby2318さんのコメント(2020/08/26 23:09)の疑問についても解決したでしょうか?
2020/08/27 20:00
2020/08/27 20:25
2020/08/27 20:50 編集
2020/08/28 09:14
自己解決する方法は、調べることと試すことだと思います。
1.不具合の原因をDebug.Logなどで特定する。
2.原因の解決策を検索する。
3.検索で見つけた情報をもとに修正し試す。
この工程の繰り返ししかないと私は思います。
ただし、どの工程もプログラムの基礎知識がなければ理解できないですから、まずは基礎をしっかりと勉強してください。
経験を積めばそれぞれの工程にかかる時間も短縮できますので、最初は大変だと思いますが頑張ってください。
2020/08/29 00:50