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

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

ただいまの
回答率

91.37%

  • C#

    4769questions

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

  • Unity

    2303questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity3D

    785questions

    Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

カメラの追従と、位置リセットを実装したいが追従しない + リセット後プレイヤーの正面方向を奥方向としたい

解決済

回答 1

投稿 2017/11/22 18:40

  • 評価
  • クリップ 0
  • VIEW 120

toricotori

score 3

質問失礼いたします。
現在、「プレイヤーの位置から一定距離保ちながらカメラが追従し、一定のキーを押すとプレイヤーの向いている方向にあわせてカメラが回転する」というスクリプトを書いています。
以下の通りに書いてみたのですが、まず、カメラがプレイヤーに追従しません。
何が問題なのかご教授いただきたいです。

// キャラクターのTransform
public Transform player;
// カメラの移動スピード
public float cameraMoveSpeed;
// カメラの回転スピード
public float cameraRotateSpeed;
// カメラのキャラクターからの相対値を指定
private Vector3 offset;
//  カメラの回転値を保存
Quaternion camRot;
// 
bool flag = false;
float timer = 0;

void Start ()
{
    //プレイヤーとカメラの距離を計算する
    offset = transform.position - player.position;
  //距離分離す
    transform.position = player.position + offset;
  //初期回転値を保存
    camRot = transform.rotation;
}

void Update () 
{
  //通常はプレイヤーの位置に一定距離を保って追従する
    if (flag == false)
    {
        transform.rotation = camRot;
        offset = transform.position - player.position;
        transform.position = player.position + offset;
    } 
  //ALtキーを押すとカメラの位置をプレイヤーの真後ろへリセットする
    if (Input.GetKeyDown (KeyCode.LeftAlt) || Input.GetKeyDown (KeyCode.RightAlt))
    {
        flag = true;
    }
    if (flag == true) 
    {
        timer += Time.deltaTime;
    //プレイヤーの背後へ移動
        transform.position = Vector3.Lerp (transform.position, player.position + (-player.forward * offset.z) + (Vector3.up * offset.y * 1.0f), cameraMoveSpeed * Time.deltaTime);
    //プレーヤーの方向へ回転
        transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation (player.position - (transform.position - new Vector3 (0.0f, offset.y, 0.0f))), cameraRotateSpeed * Time.deltaTime);
    //回転が終わったら通常の追従状態に戻る?
        if (timer >= cameraRotateSpeed * Time.deltaTime) 
        {
            camRot = transform.rotation;
            timer = 0;
            flag = false;
        }
    }
}


最初はoffsetをpublic変数にして距離を指定していたのですが、
ワールド座標としての位置関係になってしまったため、
距離を計算する方法にしたのですが、うまくいきませんでした。

また、プレイヤーの移動スクリプトは以下のようになっています。
上下キーで奥移動、左右キーで横移動となっていますが、
例えば右を向いてカメラをリセットした際(カメラがプレイヤーの背後に来た際、画面奥をキャラクターが向いているように映した際)、このままだと左右キーで奥移動、上下キーで横移動となってしまうので、
カメラがリセットされると上下キーで奥移動、左右キーで横移動となるようこちらもリセットされるようにしたいと思っています。

float dirH, dirV;
loat dirSpeed = 0.1f;
dirH = Input.GetAxis ("Horizontal");
dirV = Input.GetAxis ("Vertical");

transform.position = new Vector3 (transform.position.x - dirH * 0.1f, //横移動
                      transform.position.y,
                              transform.position.z - dirV * 0.1f); //奥移動
vec = new Vector3 (-dirH, 0.0f, -dirV);
vec.Normalize ();
//方向を求める
dirRot = Mathf.Atan2 (vec.x, vec.z) / 3.14159265f * 180.0f;
transform.eulerAngles = new Vector3 (0.0f, dirRot, 0.0f);

長くなりましたが、どちらかでもいいので、ご教授いただけますと幸いです。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

カメラが初期位置から移動しなかったのは、オフセットがUpdate中のoffset = transform.position - player.position;のために毎回「その時点のカメラの位置 - その時点のプレイヤーの位置」に更新されてしまっていたためかと思います。

ご参考として修正案を考えてみましたが、いかがでしょうか?
なるべく元のコードを残そうとしたつもりでしたが、ご質問者さんの意図する動きを想像しながらいじっているうちに、結構変わってしまいました。すみません...
左右上下キー押下時のプレイヤー移動方向の切り替え案として、基準とするべき右方向・前方向をカメラに持たせておいて、プレイヤーはそれを見て進む方向を決めるようにしてみました。
スクリプト名はご質問者さんのものに合わせて読み替えてください。変数などをいくらか追加しましたが、名前がちょっと冗長な気がするので、お好きな名前を付けていただくといいかと思います。

カメラのスクリプト(LookRotation版)

using UnityEngine;

public class CameraController : MonoBehaviour
{
    private const float CameraDeltaOffsetSqrMagnitudeThreshold = 0.0001f;

    // キャラクターのTransform
    public Transform player;
    // カメラの移動スピード
    public float cameraMoveSpeed;
    // カメラの回転スピード
    public float cameraRotateSpeed;
    // カメラのキャラクターからの相対値を指定
    private Vector3 offset;
    // Start時のキャラクターに対するカメラの相対位置をプレイヤーのローカル座標系で保存
    private Vector3 initialPlayerLocalOffset;

    public Vector3 CameraRight { get; private set; } // 変更...バッキングフィールドとしてcameraRightを用意するより、CameraRightをCameraControllerだけから設定可能なプロパティとした方がコードがシンプルになるかと思いました
    public Vector3 CameraForward { get; private set; } // 変更...CameraRightと同じく、CameraControllerだけから設定可能なプロパティとしました

    bool flag = false;

    void Start ()
    {
        //プレイヤーとカメラの距離を計算する
        offset = transform.position - player.position;
        transform.rotation = Quaternion.LookRotation(-offset); // offsetが更新されたので、カメラ回転も更新

        initialPlayerLocalOffset = player.InverseTransformDirection(offset); // offsetをプレイヤーローカル座標系に直すパターンを採用

        CameraRight = transform.right; // カメラの右方向を設定

        var newForward = transform.forward; // 追加

        newForward.y = 0.0f; // 追加...カメラの前方向をXZ平面上に投影
        newForward.Normalize(); // 追加...正規化し長さを1に
        CameraForward = newForward; // 変更...新しい前方向ベクトルを設定
    }

    void Update () 
    {
        //ALtキーを押すとカメラの位置をプレイヤーの真後ろへリセットする
        if (Input.GetKeyDown (KeyCode.LeftAlt) || Input.GetKeyDown (KeyCode.RightAlt))
        {
            flag = true;
        }
        if (flag == true) 
        {
            //プレイヤーの背後へ移動
            Vector3 destinationOffset = player.TransformDirection(initialPlayerLocalOffset); // Start時にプレイヤーの座標系で見たカメラの位置を保存しておいたので、そこからカメラの目標オフセットを求める

            // offset = Vector3.Slerp (offset, destinationOffset, cameraMoveSpeed * Time.deltaTime); // 変更...LerpをSlerpにして、カメラの動きが球面上の軌道を取るようにしてみました(Lerpの方がお好みでしたら、そちらでも問題ありません)
            offset = InterpolateOffset(offset, destinationOffset, cameraMoveSpeed * Time.deltaTime); // 追加...Slerpの代わりにカスタム補間メソッドを使った場合
            transform.rotation = Quaternion.LookRotation(-offset); // offsetが更新されたので、カメラ回転も更新
            //回転が終わったら通常の追従状態に戻る?
            if ((offset - destinationOffset).sqrMagnitude < CameraDeltaOffsetSqrMagnitudeThreshold)
            {
                offset = destinationOffset; // 目標オフセットに完全に移動する
                transform.rotation = Quaternion.LookRotation(-offset); // offsetが更新されたので、カメラ回転も更新
                CameraRight = transform.right; // カメラの右方向を更新、以後プレイヤーは新しい右方向を基準に移動するようになる

                var newForward = transform.forward; // 追加

                newForward.y = 0.0f; // 追加...カメラの前方向をXZ平面上に投影
                newForward.Normalize(); // 追加...正規化し長さを1に
                CameraForward = newForward; // 変更...新しい前方向ベクトルを設定、以後プレイヤーは新しい前方向を基準に移動するようになる
                flag = false;
            }
        }
        transform.position = player.position + offset;
    }

    // 追加...offsetの補間用メソッド
    static Vector3 InterpolateOffset(Vector3 a, Vector3 b, float t)
    {
        var aXZ = new Vector2(a.x, a.z); // 真上から見たa
        var aR = aXZ.magnitude; // aのY軸からの距離
        var aRIsNotZero = aR > 1E-05; // aRが(ほぼ)0より大きいか?
        var aD = aRIsNotZero ? aXZ / aR : Vector2.zero; // 真上から見たaの方角、aRがほぼ0ならVector2.zeroとして無回転にする
        var bXZ = new Vector2(b.x, b.z); // 真上から見たb
        var bR = bXZ.magnitude; // bのY軸からの距離
        var bD = (bR > 1E-05) ? bXZ / bR : Vector2.zero; // 真上から見たbの方角、bRがほぼ0ならVector2.zeroとして無回転にする
        var theta = Mathf.Sign(aD.x * bD.y - bD.x * aD.y) * Mathf.Acos(Mathf.Clamp(Vector2.Dot(aD, bD), -1.0f, 1.0f)); // aDとbDがなす弧度法による符号付き角度
        var alpha = aRIsNotZero ? Mathf.Atan2(aD.y, aD.x) : Mathf.Atan2(bD.y, bD.x); // 基準の角度...aDとX軸のなす角(aRがほぼ0ならbDとX軸のなす角)
        var newTheta = Mathf.Lerp(0.0f, theta, t); // aDとbDがなす角度を線形補間
        var newPhi = alpha + newTheta; // 基準の角度に先ほど線形補間した角度を足して、これを真上から見たときの新たな方角とする
        var newR = Mathf.Lerp(aR, bR, t); // Y軸からの距離を線形補間
        var newX = Mathf.Cos(newPhi) * newR; // 新しいX座標
        var newY = Mathf.Lerp(a.y, b.y, t); // 新しいY座標(aとbのY座標を線形補間)
        var newZ = Mathf.Sin(newPhi) * newR; // 新しいZ座標

        return new Vector3(newX, newY, newZ);
    }
}

プレイヤーのスクリプト

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public CameraController cameraController; // 追加...カメラの右・前方向を知りたいので、カメラのスクリプトを保持するようにしました(インスペクタでカメラをセットしておいてください)

    // Use this for initialization
    private void Start()
    {

    }

    // Update is called once per frame
    private void Update()
    {
        float dirH, dirV;
        float dirSpeed = 0.1f; // これは使用していないようですが、一応残しています
        dirH = Input.GetAxis ("Horizontal");
        dirV = Input.GetAxis ("Vertical");

        // 削除...移動はvecを求めてから行う
        /*
        transform.position = new Vector3 (transform.position.x - dirH * 0.1f, //横移動
            transform.position.y,
            transform.position.z - dirV * 0.1f); //奥移動
        */

        var vec = cameraController.CameraRight * (dirH * 0.1f) + cameraController.CameraForward * (dirV * 0.1f); // 変更...入力された移動量について、水平はCameraRight、垂直はCameraForwardの向きへの移動と考え、これをvecとする

        transform.Translate(vec, Space.World); // 追加...位置をvecだけ移動

        // 追加...立ち止まったときに初期の向きに戻ってしまうのを防ぐため、入力がゼロ以外の時しか向きを回転しない
        // カメラのスクリプトでやったのと同じく、vec.sqrMagnitudeが一定値未満かどうかで判定すれば、入力の有無の判定にあそびを設けることもできると思います
        if (vec != Vector3.zero)
        {
            // ここから...
            vec.Normalize ();
            //方向を求める
            var dirRot = Mathf.Atan2 (vec.x, vec.z) / 3.14159265f * 180.0f;
            transform.eulerAngles = new Vector3 (0.0f, dirRot, 0.0f);
            // ここまでの代わりに、下記のような回転方法もアリかと思います
            // transform.rotation = Quaternion.FromToRotation(Vector3.forward, vec);
        }
    }
}

投稿 2017/11/23 21:02

編集 2017/11/25 06:30

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/23 23:16

    詳しくご回答いただきまして、ほんとうにありがとうございます。
    実行してみましたところ、カメラのスクリプトのほうで、Altキーを押したときに向いていてほしい方向と真逆の方向を向いてしまうようです。
    initialCamRotOffset, destinationRotation等をいじってみたのですが、私の力では解決しませんでした。
    こちらの解決方法もご教授いただけますと幸いです。

    キャンセル

  • 2017/11/24 00:42

    プレイヤーの初期の向きをカメラの初期位置・初期回転に反映しようとしたのがまずかったかもしれません...
    カメラのスクリプトを変更して、プレイヤーの最初の向きを考慮しないようにしてみましたが、こちらだとどうでしょうか?
    ※結局、initialCamRotOffsetは当初ご質問者さんが作成されたcamRotと同じ形に戻りましたね。

    キャンセル

  • 2017/11/24 15:10

    ありがとうございます。
    ご指示くださった、初期位置、初期回転のパターン2を適用したところ、正しい挙動になりました。ほんとうに助かりました。

    できればプレイヤーの方向へ回転する際、プレイヤーを画面に収めながら回転させたいと思っているのですが、
    Quaternion.LookRotationを活用する形になるでしょうか。
    Quaternion.Slerp(tranform.rotation, Quaternion.LookRotation(player.position - transform.position), cameraRotateSpeed * Time.deltaTime);
    と書いてみたのですが、プレイヤーを見ながら回転後、カクッと回転Xが正しい値に戻るようになってしまいました。
    回転中、回転Xが変化してしまうようです。
    至らないところが多く申し訳ありません。
    こちらも、少しでもご教授いただけますと幸いです。

    キャンセル

  • 2017/11/24 22:35

    仕事終わりまでお返事できずすみませんでした。パターン2が有効でしたか。カメラ位置に関してはこれでよさそうですね。

    カメラの向きに関してですが、このさいoffsetに変化があるたびにカメラを-offsetの方向に向けるというのはどうでしょう。カメラ位置はなめらかに移動しているので、あとは常時カメラをプレイヤーに向ければ、レンダリングされた映像にも不自然な途切れはなくなるかと思います。このパターンを、以前のカメラのスクリプトの後に追記しました。

    なお、私の実験したパターンではLookRotationを使うとカメラがキャラクターを少し見下ろすような形になってしまい、カメラのforwardがXZ平面上から外れてしまうため、これをそのままCameraForwardとしてしまうとキャラクターの前後移動が正常に行われなくなってしまったため(地面にめり込んだり宙に浮いたり)、forwardをXZ平面上に投影する処置を加えています。ご質問者さんの場合はもしかするとこれは不要かもしれません。適宜書き換えてみてください。

    キャンセル

  • 2017/11/25 00:04

    お忙しい中回答いただきまして、ありがとうございます。
    -offset方向を見ながらというのは思いつきませんでした。理想的な形になり感激しております。

    offsetの移動をSlerpにした場合、プレイヤーが手前を向いているときにAltキーで位置リセットを行うと、カメラがプレイヤーの頭上を移動するようなのですが、プレイヤーの横を通るようにすることは可能でしょうか。
    Slerpのやり方など調べてみたのですが、やはり自分だけでは解決できず……
    もう少々お力添えしていただけると幸いです。

    キャンセル

  • 2017/11/25 06:25

    Slerpの置き換え案として、大円の代わりに緯線に沿った軌道で補間されるようなメソッドを追加してみました(珍妙なコードですみません...)。こんな動きでいかがでしょうか?

    投稿字数の制限を超えてしまった様子ですので、旧カメラスクリプト(非LookRotation版)を削除しました。もしそちらのコードをご覧になりたい場合は編集履歴をご参照ください。

    キャンセル

  • 2017/11/25 21:22

    お返事遅れまして申し訳ございません。
    適用しましたところ、理想の動きになりました。100点では足りません。
    このたびは、お忙しい中何度も詳しくご回答いただき、感謝してもしきれないくらいです。
    ほんとうにほんとうに、ありがとうございました。

    キャンセル

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

ただいまの回答率

91.37%

関連した質問

  • 受付中

    Unityで、Lerpというものを使いたいのですがなにが間違っているのかわかりません。

    2つの操作キャラクターに一つのカメラをつける。 これをspeceボタンを押すことで切り替えるようにしたい。その時、Lerpというものを使いいきなり画面が変わるのではなく徐々に追従す

  • 解決済

    [Unity] MoveTowardsAngle関数の回転

    MoveTowardsAngle関数で、x軸の回転(左の球)だけ90度地点で止まります。 右の球のような動きで、x軸に360度の回転をさせたいです。どうしたらいいですか?

  • 受付中

    カメラの操作用スクリプトについて

    unityでカメラをyを軸に回転させ、向いた方向を前として、wで前進、sで後進、aで左、dで右に動かすスクリプトを書きたいのですが、軸で回ることはできても、どうしてもワールドのx,

  • 解決済

    Unityでの線形補間について

    前提・実現したいこと Unity5.6でオブジェクトを持ち、 線形補間で一定間隔スライドさせようとしています。 プレイヤーがオブジェクトを持ち スライドさせるところまで

  • 解決済

    3Dゲームでバイオハザードと同じ様な操作を(自機に)したい。

    ・カメラの位置は自機の頭部の少し後ろ上に固定。 ・カメラのポジションはxとy軸のみ変更可能。 ・W,S,A,Dのそれぞれのキーで前進、後退、左移動、右移動。 上記の三点を行いた

  • 解決済

    カメラ視点をリセットするには?

    カメラの視点をリセットするにはどうすればいいのか教えていただきたいです。 キャラの向きとは別にカメラワークによる上下左右の確認をした後にそのカメラ視点を初期状態ローテーションを(0

  • 解決済

    Unityで回転制限をつけたいです。

    前提・実現したいこと 下記コードでAキー、Dキーを押している間それぞれY軸の-方向、Y軸の+方向に回転するよう書きましたが、90度回転したら回転できないようにする機能をつけたいです

  • 解決済

    unityでInputTracking.disablePositionalTrackingを使った、...

    前提・実現したいこと unityのcardboard GoogleVRで InputTracking.disablePositionalTrackingを使ってカメラを起動時に固定

同じタグがついた質問を見る

  • C#

    4769questions

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

  • Unity

    2303questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity3D

    785questions

    Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

  • トップ
  • C#に関する質問
  • カメラの追従と、位置リセットを実装したいが追従しない + リセット後プレイヤーの正面方向を奥方向としたい