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

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

ただいまの
回答率

87.59%

Unityで、時間と高さ幅と着地地点を指定してジャンプをコントロールしたい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,698

score 16

前提・実現したいこと

時間と高さ幅と着地地点を指定してオブジェクトをジャンプさせたいです。

言葉足らずだと思いますが、ご教授よろしくお願いいたします。

下記のURLを参考に作成しています。
https://teratail.com/questions/173195?nli=5c5ceb2c-ab38-4558-9e36-49d90a28000d

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

幅ぶんの座標まで移動を終えていないので、Y軸の到着地点に来ると横に瞬間移動をする

該当のソースコード

public class jump : MonoBehaviour {
            private float jumpTime2 = 2f;//ジャンプしている時間
        private float width2 = 5;//幅
        private float height2 = 6;//高さ
        private float time2;//ジャンプ中の折り返し地点の時間
        private float fixedGravityY2;//固定重力
        private float initVelocityY2;//速度
        private float initVelocityX2;//速度
        private float elapsedTime2 = 0f;
        private bool jumpChecker2 = false;//ジャンプしているかのチェック
        public bool jumpStart2 = false;//ジャンプ処理の前の処理通過フラグ
        private Vector3 JumpPos2;
        private float initPositionX2; // 初期X座標のためのフィールドを追加
        private  float initPositionY2; // 初期Y座標のためのフィールドを追加

        private float LandingPosY = 2f;

        private bool LandingPointY_Flag = false;



    void Update() {
        if (Input.GetKey("a") && !jumpChecker2) {
                           //((Y軸+止まる位置)/Y軸)
                //float hoge = ((height2 + LandingPosY) / height2);
                float hoge = ((LandingPosY * 2) / height2);

                float hoho = hoge <= 0 ? 1 : hoge;
                //0以下のチェック
                //double hohoho = Math.Truncate(hoge) <= 0 ? 1 : Math.Truncate(hoge);
                Debug.Log("倍数:" + hoho);

                jumpChecker2 = true;
                time2 = jumpTime2 / 2;

                //Y軸
                fixedGravityY2 = -2 * height2 / (time2 * time2);
                initVelocityY2 = 2 * height2 / time2;
                initPositionY2 = transform.position.y;

                //X軸
                initVelocityX2 = (width2*hoho) / jumpTime2; // 距離widthをjumpTime秒かけて移動する場合の速度
                initPositionX2 = transform.position.x; // ジャンプ開始時のX座標を覚えておく


            }
            if (jumpChecker2)
            {
                elapsedTime2 += Time.deltaTime;
                Vector3 pos = target.transform.position;
                pos.x = initPositionX2 + initVelocityX2 * elapsedTime2; // 水平方向は等速とする
                pos.y = initVelocityY2 * elapsedTime2 + fixedGravityY2 * elapsedTime2 * elapsedTime2 / 2;

                //高さの頂点に到達したかの判定
                if (pos.y > (initPositionY2 + height2) - 0.2)
                {
                    //高さの頂点に到達
                    LandingPointY_Flag = true;

                }

                //Y軸の所定の位置に到達したかの判定
                if (LandingPointY_Flag)
                {
                    if (pos.y < LandingPosY)
                    {
                        pos.y = LandingPosY;
                        pos.x = initPositionX2 + width2; // 初期位置からwidthだけ進んだ位置を最終的なX座標とする
                        elapsedTime2 = 0;
                        jumpChecker2 = false;
                    }
                }

                transform.position = pos;
            }

}

試したこと

移動しきれていない幅をを計算で求めてみたが駄目だった。

補足情報

参考にしたソースコード

public class jump : MonoBehaviour
{
    public float jumpTime = 1f;
    float time;
    public float height = 2f;
    public float width = 2f;
    float fixedGravity;
    float initVelocity;
    // float fixedGravityX; // 水平方向の加速度は不要
    float initPositionX; // 初期X座標のためのフィールドを追加
    float initVelocityX;
    float elapsedTime = 0f;
    public bool jumpChecker = false;

    void Update()
    {
        if (Input.GetKey("a") && !jumpChecker)
        {
            jumpChecker = true;
            time = jumpTime / 2;
            fixedGravity = -2 * height / (time * time);
            initVelocity = 2 * height / time;

            // fixedGravityX = -2 * width / (time * time); // 水平方向の加速度は不要
            initVelocityX = width / jumpTime; // 距離widthをjumpTime秒かけて移動する場合の速度
            initPositionX = transform.position.x; // ジャンプ開始時のX座標を覚えておく
        }
        if (jumpChecker)
        {
            elapsedTime += Time.deltaTime;
            Vector3 pos = transform.position;
            pos.x = initPositionX + initVelocityX * elapsedTime; // 水平方向は等速とする
            pos.y = initVelocity * elapsedTime + fixedGravity * elapsedTime * elapsedTime / 2;

            if (pos.y < 0)
            {
                pos.y = 0;
                pos.x = initPositionX + width; // 初期位置からwidthだけ進んだ位置を最終的なX座標とする
                elapsedTime = 0;
                jumpChecker = false;
            }
            transform.position = pos;
        }
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • SaharaDesert

    2019/02/13 16:38

    どう直せばいいでしょうか?
    教えていただいてもいいでしょうか。

    キャンセル

  • stdio

    2019/02/13 17:09

    //高さの頂点に到達したかの判定
    if (pos.y > (initPositionY2 + height2) - 0.2)
    とコメント書いている下あたりなんでこんなコードになっているのか説明して下さい。
    なんとなくおかしいよ。

    キャンセル

  • SaharaDesert

    2019/02/13 17:27

    最初に上に飛ぶときに、着地地点がすぐに処理されてしまうため
    頂点に到達してから着地地点で移動処理を停止させています。

    キャンセル

回答 1

checkベストアンサー

0

だんだんややこしくなってきましたね...
ひとまず初期位置とか水平移動は無視して、鉛直投げ上げに戻ってみてはいかがでしょうか。

原点から真上にオブジェクトを投げ上げ、ある時刻tHで最高点Hに達し、その後落下して最終時刻Tで高さhに至るとすると、縦軸に垂直位置y、横軸に時刻tをとると下図のような二次曲線が描かれるでしょう。

グラフ

この未知の二次曲線を
y = at2 + bt + c
として図中の3点の(t, y)を代入すると、まず
c = 0
と確定でき、
H = atH2 + btH、h = aT2 + bT
の2式が得られます。これらを変形して一つにまとめると
h = aT2 + TH/tH - aTtH
の関係が得られます。
次に、二次曲線の導関数
dy/dt = 2at + b
に注目します。時刻tHにおいて曲線の傾きが0になるので
2atH + b = 0
となります。このbに①を変形した
b = (H - atH2) / tH
を代入して整理すると
a = -H / tH2
となります。これを②に代入し変形すると
htH2 - 2HTtH + HT2 = 0
の二次方程式が出てきます。これの解を求めると
tH = T((H±√H(H - h)) / h)
となります。解が2つありますが、
tH <= T、h <= H
でなくてはならないので、採用する解は
tH = T((H-√H(H - h)) / h)
の方ということになります。最高点に到達する時刻を決定できましたので、これをtime2として使用すればいいでしょう。
下記のようなコードを実行してみたところ...

using UnityEngine;

public class jump : MonoBehaviour
{
    [SerializeField] private float jumpTime2 = 2f; // 時間
    [SerializeField] private float width2 = 5f; // 幅
    [SerializeField] private float height2 = 6f; // 高さ
    [SerializeField] private float LandingPosY = 2f; // 着地高さ

    private bool jumpChecker2 = false; //ジャンプしているかのチェック

    private float fixedGravityY2; // 重力加速度Y

    private float initVelocityX2; // 初速度X
    private float initVelocityY2; // 初速度Y

    private float initPositionX2; // 初期X座標
    private float initPositionY2; // 初期Y座標

    private float elapsedTime2 = 0f; 

    void Update()
    {
        if (Input.GetKey("a") && !jumpChecker2)
        {
            elapsedTime2 = 0;
            jumpChecker2 = true;

            float time2;
            if (LandingPosY > height2 || Mathf.Approximately(LandingPosY, 0.0f))
            {
                // もし着地点の方が最高到達点より高かったり、着地点がちょうど0だった場合
                // time2を求められないため、従来通りの平地ジャンプとする
                time2 = jumpTime2 / 2;
            }
            else
            {
                time2 = jumpTime2 * ((height2 - Mathf.Sqrt(height2 * (height2 - LandingPosY))) / LandingPosY);
            }

            // Y軸
            fixedGravityY2 = -2 * height2 / (time2 * time2);
            initVelocityY2 = 2 * height2 / time2;
            initPositionY2 = transform.position.y;

            // X軸
            initVelocityX2 = width2 / jumpTime2;
            initPositionX2 = transform.position.x;
        }

        if (jumpChecker2)
        {
            elapsedTime2 += Time.deltaTime;

            // 経過時間がjumpTime2を越えたら...
            if (elapsedTime2 > jumpTime2)
            {
                // このタイミングでelapsedTime2を最終時刻であるjumpTime2に合わせ
                // jumpChecker2をfalseにするといいでしょう
                elapsedTime2 = jumpTime2;
                jumpChecker2 = false;
            }

            Vector3 pos = transform.position;

            pos.x = initPositionX2 + initVelocityX2 * elapsedTime2;
            pos.y = initPositionY2 + initVelocityY2 * elapsedTime2 + fixedGravityY2 * elapsedTime2 * elapsedTime2 / 2;

            transform.position = pos;

            // ジャンプ中に経過時間や座標をコンソールに出力してみると、動きの様子をとらえやすいかもしれません
            Debug.Log($"Time:{this.elapsedTime2:F4} X:{transform.position.x:F4} Y:{transform.position.y:F4}");

            // 他にも、軌跡にオブジェクトを残して動きを可視化するのも面白いでしょう
            Transform dot = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
            dot.localScale = new Vector3(0.125f, 0.125f, 0.125f);
            dot.position = transform.position;
        }
    }
}

オブジェクトは下図のような軌道で飛びました。

投射

ここまで書いておきながらなんですが、もしニュートン力学の学習目的とかではなくアクションゲームの作成を目指しているのでしたら、オブジェクトの位置を二次関数の形で求めるよりも、stdioさんがおっしゃるように「現在の速度」を使った数値積分的アプローチの方が面倒が少ないと思います。むしろ、もっとColliderやRigidbodyに頼った作りにしてしまっていいんじゃないでしょうか...?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/02/18 09:49

    返信遅くなり申し訳ありません。
    返信ありがとうございます。
    試してみます。

    キャンセル

  • 2019/02/18 10:08

    出来ましたありがとうございます。
    ColliderやRigidbodyに頼った作りにしてしまっていいんじゃないでしょうか...?
    ↳今現在ColliderやRigidbody不慣れだったため使っていませんでした、もう少し知見などを得たいと思います。

    ありがとうございました。

    キャンセル

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

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

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