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

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

ただいまの
回答率

89.21%

[Unity2D]偏差射撃について

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,022

shorter

score 13

実現したいこと

上方向に高く飛ぶプレイヤーに対して、プレイヤーの真横を平行移動する敵が偏差射撃をする機能を実現したいと考えています。

現在の状況

・敵のY座標にプレイヤーのY座標を代入することで、敵がプレイヤーの真横を平行移動するようにしています。

・偏差射撃の予測位置として、プレイヤーの現在位置に速度ベクトルを加えた座標に敵を向けています。

行き詰まっていること

上記の状態で弾を発射したところ、プレイヤーと敵が弾を置き去りにしてしまったので、その問題を解消するためにプレイヤーのフレーム毎の上昇値を弾の座標に加えました。弾が置き去りになることは無くなったのですが、このままだと弾がプレイヤーと同じ距離だけ上昇してしまうので、衝突することなく弾が過ぎ去ってしまいます。

弾がちょうどプレイヤーに衝突するよう、弾の上昇値を変えなければならないとは思っているのですが、具体的にどの要素が必要なのかが分かっていない状況です。

ヒントやアドバイス、解決法の提案等、何でも構いませんので、お答え頂きたく思います。
よろしくお願い致します。

追記

文章のみの質問で申し訳ありませんでした。

以下に当問題に関係があると考えているスクリプトを添付させて頂きます。
読みにくいコードかとは思いますが、よろしくお願い致します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerParameter : MonoBehaviour {

    //PlayerのRigidbody2D
    private Rigidbody2D rb2d;
    //Playerの速度
    private float v = 0.0f;
    //Playerの速度ベクトル
    private Vector3 vVector;
    //Playerの現在位置
    private Vector3 nowPos;
    //Playerの前フレームの位置
    private Vector3 prevPos = Vector3.zero;
    //Playerの移動ベクトル
    private Vector3 moveVec = Vector3.zero;

    // Use this for initialization
    void Start () {
        //Rigidbody2Dコンポーネントを取得
        rb2d = this.GetComponent<Rigidbody2D> ();
    }

    // Update is called once per frame
    void Update () {
        //Playerの現在位置を格納
        nowPos = this.transform.position;
        //Playerの速度値を格納
        v = rb2d.velocity.magnitude;
        //Playerの速度ベクトルを格納
        vVector = rb2d.velocity;
        //Playerの移動ベクトルを格納
        moveVec = nowPos - prevPos;
        //Playerの現在位置を前フレームの位置にする
        prevPos = nowPos;
    }

    //Playerの速度を返す関数
    public float GetVelocity(){
        return v;
    }

    //Playerの速度ベクトルを返す関数
    public Vector3 GetVelocityVector(){
        return vVector;
    }

    //Playerの移動ベクトルを返す関数
    public Vector3 GetMoveVector(){
        return moveVec;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour {

    //標的(Player)
    private GameObject playerObj;
    //自分(Enemy)の座標
    private Vector3 mPos;
    //Playerの座標
    private Vector3 pPos;
    //Playerの移動予測座標(偏差射撃のため)
    private Vector3 forecastPos;
    //Playerの速度
    private float p_velocity;
    //PlayerとEnemy間の距離
    private float p_distance;

    // Use this for initialization
    void Start () {
        //Playerオブジェクトを取得
        playerObj = GameObject.FindWithTag ("Player");
    }

    // Update is called once per frame
    void Update () {
        //PlayerにアタッチされているPlayerParameterスクリプトを参照
        PlayerParameter pp = playerObj.GetComponent<PlayerParameter> ();
        //Shootingスクリプトを参照
        Shooting st = GetComponent<Shooting> ();
        //自分(Enemy)の座標を取得
        mPos = transform.position;
        //Playerの座標を取得
        pPos = playerObj.transform.position;
        //Playerの速度を格納
        p_velocity = pp.GetVelocity();
        //PlayerとEnemy間の距離を格納
        //p_distance = Vector3.Distance(mPos, pPos);
        //Playerの速度を送る
        st.SetPlayerVelocity (p_velocity);
        //Playerの予測移動位置を格納
        forecastPos = pPos + pp.GetVelocityVector();
        //targetを向く
        transform.rotation = Quaternion.FromToRotation( Vector3.up,  forecastPos);
        //PlayerのY座標を取得し、自分(Enemy)の座標に反映(=Playerと平行移動)
        mPos.y = pPos.y;
        transform.position = mPos;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//Rigidbody2Dコンポーネントを必須にする
[RequireComponent(typeof(Rigidbody2D))]
public class Shooting : MonoBehaviour {

    //Bulletプレハブ
    [SerializeField]
    private GameObject muzzle;
    //弾を撃つ間隔
    [SerializeField]
    private float shotDelay = 1f;
    //弾の速度
    [SerializeField]
    private float bulletSpeed = 5f;
    //Playerの速度
    private float p_velocity = 0f;

    // Update is called once per frame
    void Update () {

    }

    // Startメソッドをコルーチンとして呼び出す
    IEnumerator Start ()
    {
        while (true) {
            //Debug.Log (GetBulletSpeed());

            // 弾を敵オブジェクトと同じ位置/角度で作成
            Instantiate (muzzle, transform.position, transform.rotation);
            // shotDelay秒待つ
            yield return new WaitForSeconds (shotDelay);
        }
    }

    //Enemyの速度を加算した弾の速度を返す
    public float GetBulletSpeed(){
        return bulletSpeed;
    }

    //Playerの速度をセットする関数
    public void SetPlayerVelocity(float v){
        p_velocity = v;
    }

    //Playerの速度を取得する関数
    public float GetPlayerVelocity(){
        return p_velocity;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//Rigidbody2Dコンポーネントを必須にする
[RequireComponent(typeof(Rigidbody2D))]
public class Bullet : MonoBehaviour {

    //敵オブジェクト
    private GameObject eneObj;
    //Playerオブジェクト
    private GameObject playerObj;

    // Use this for initialization
    void Start () {
        //Playerオブジェクトを取得
        playerObj = GameObject.FindWithTag ("Player");
        //Enemyオブジェクトを取得
        eneObj = GameObject.FindWithTag ("Enemy");
        //EnemyオブジェクトのShootingスクリプトを参照
        Shooting st = eneObj.GetComponent<Shooting> ();
        //弾を発射する
        GetComponent<Rigidbody2D> ().AddForce (transform.up.normalized * st.GetBulletSpeed(),ForceMode2D.Impulse);
    }

    // Update is called once per frame
    void Update () {
        //PlayerにアタッチされているPlayerParameterスクリプトを参照
        PlayerParameter pp = playerObj.GetComponent<PlayerParameter> ();
        //Playerの移動ベクトルを加える
        transform.position += pp.GetMoveVector ();
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • Bongo

    2017/05/21 22:09

    おっとすみません、投稿タイミングでお返事を見逃してしまいました。敵にアタッチされているのですね。この条件でもう少し考えてみます。

    キャンセル

  • shorter

    2017/05/21 22:37

    ご質問ありがとうございます。ご質問の内容を私が勘違いしていないか心配なのですが、ShootingクラスとEnemyクラスには親子関係はなく、独立して作っているつもりです。どちらのスクリプトも敵オブジェクトにアタッチされており、Enemyクラスでは敵オブジェクトの向きを変え、Shootingクラスでは敵オブジェクトの向きに合わせて弾を生成、射撃しています。ご質問の意味を取り違えていたら申し訳ありません。

    キャンセル

  • shorter

    2017/05/21 22:48

    Bongoさんの22:09のご投稿を見逃し投稿してしまいました。Bongoさんの仰る通り、ShootingクラスとEnemyクラスはそれぞれ敵オブジェクトにアタッチされています。重ね重ね申し訳ありません。

    キャンセル

回答 1

0

おかしな動きの原因と関係あるかもしれないと思った箇所を挙げさせていただきます。
Bulletが新たに作られたとき、BulletのStartの中でGetComponent<Rigidbody2D> ().AddForce (transform.up.normalized * st.GetBulletSpeed(),ForceMode2D.Impulse);で初速度を与えているご様子ですが、Impulseは力積を意味し、その前の引数として与えるべきは弾の運動量の変化量になるかと思います。
試しにここのImpulseをVelocityChangeに変えてみて、さらにUpdateの中のtransform.position += pp.GetMoveVector ();はコメントアウトしてしまい、一旦Startで初速度を与えたら、Updateでむやみに動きを操作せずに物理計算エンジンが自動的に移動させるに任せてみるとどうなるでしょうか。
最初に「弾を置き去りにしてしまった」という動きをしたのも、弾の質量が大きすぎて実際の初速度がかなり小さくなってしまったためかもしれません。

[追記]
うっかりしておりました。ForceMode2DではVelocityChangeが使えなかったかもしれませんね。モードはImpulseのままにして、代わりにその前の引数にさらにBulletのRigidBody2Dのmassを掛けてみるといかがでしょう。

[さらに追記]
もしかしてBulletのRigidBody2DのisKinematicがオンになっている、ということはありませんでしょうか。もしそうだと、AddForceで力積を加えようとしても無効になるかもしれません。isKinematicを切るか、あるいはAddForceを使う代わりにRigidBody2Dのvelocityにtransform.up.normalized * st.GetBulletSpeed()をセットする(こちらなら多分いける、と思います...)、などの手段が必要かと思います。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/28 20:34

    いえいえ、お気になさらずに。
    先の回答で申し上げましたのは、弾の質量をmassとして、GetComponentで持ってきたプレイヤーオブジェクトをppとしますと、現状transform.up.normalized * st.GetBulletSpeed()となっている部分を((transform.up.normalized * st.GetBulletSpeed()) + pp.GetMoveVector()) * massとするという意図でした。このようになっていてもうまくいかなかったということでしょうか?
    もしそうでしたら、一つ確認したいのですが、もしかしてプレイヤーオブジェクトは毎フレームAddForceで加速していて、時間が経つにつれてプレイヤー(と敵とカメラ)の速度は上方向にどんどん大きくなっていたりしますか(つまり、一定の速度で飛んでいるわけではない)?この場合、「弾を発射した瞬間のプレイヤー速度ベクトル」を弾に与えても、プレイヤーが加速しているので弾が置き去りに(実際にはプレイヤーが上に逃げて)しまうかもしれません。

    キャンセル

  • 2017/05/29 00:24

    私の勘違いで、ご提示頂いた「pp.GetMoveVector()」の部分を「pp.GetVelocityVector()」にしていた(他は合っていました)ので、「pp.GetMoveVector()」で試してみたのですが、うまくいきませんでした。
    プレイヤーに上方向の力が加わるのは一度のみとなっています。ただ重力(GravityScaleは0.75)がかかっていますので、常に一定の速度で飛んでいる訳ではありませんが、速度が上がることはありません。

    キャンセル

  • 2017/05/29 11:23

    おっとすみません、GetVelocityVectorが秒速での速度ベクトルなので、GetMoveVectorよりそちらが適切ですね。それでもダメ、プレイヤーもほぼ等速で上昇しているとなると...あとは何でしょうか。何か思いついたらコメントします。すみません。
    ※もし可能でしたら、そのダメな動きをGIFなどで見せていただけますでしょうか。お手持ちのソフトにそういったものがなければ結構です。

    キャンセル

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

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