@hasegawadesu (hasegawa)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Unity C#: 1度に生成するPrehabの個数設定、Prehabを射出先の狙い範囲設定について教えてください。

はじめまして、 hasegawaです。
12月にプログラミングを勉強し始めて、ちょっと困っている為、質問させてください。

今作っている物

Unityで
「盾一枚で依頼人を敵の攻撃から守りながらゴールまで送り届けるランゲーム」
を作っています。

[現状]
https://youtu.be/d3pvsGqeSXs

解決したいこと

①敵の攻撃の狙う範囲の設定
 [1]ランダム打ち
  ⇒現状 盾の前にインターバルごとに1つ射撃する事しか出来ていません。
 [2]依頼人を狙った攻撃
  ⇒敵にはプレイヤーと依頼人をランダムで撃ってほしいです。
※Playerは、自動前進するので、盾をターゲットにすると、盾の後ろに敵の攻撃が飛んでくるので
 苦肉の策でシールド前にcreat emptyで target objを作ってしのいでます。うまいやり方あれば、教えてほしいです。。
emply.png

②敵の攻撃パターン
 [1]弓は、2秒のインターバルごとに3発射出(波状に)
 [2]砲弾は、5秒のインターバルごとに1発射出
 ⇒現状 インターバルごとに1つ射撃する事しか出来ていません。

[射撃コード]

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class throwingscript : MonoBehaviour
{
    //☆
    private float InstantiationTimer = 0f;
    // ターゲットオブジェクトの Transformコンポーネントを格納する変数
    public Transform target;//☆
    // オブジェクトの移動速度を格納する変数
    public float moveSpeed;//☆
    // オブジェクトが停止するターゲットオブジェクトとの距離を格納する変数
    public float stopDistance;//☆
    // オブジェクトがターゲットに向かって移動を開始する距離を格納する変数
    public float moveDistance;//☆  

    //<summary>
    //射出するオブジェクト
    //</summary>
    [SerializeField, Tooltip("ThrowingObject")]
    private GameObject ThrowingObject=null;

    //<summary>
    //標的のオブジェクト
    //</summary>
    [SerializeField, Tooltip("TargetObject")]
    private GameObject TargetObject=null;

    //<summary>
    //射出角度
    //</summary>
    [SerializeField, Range(0F,90F), Tooltip("ThrowingAngle")]
    private float ThrowingAngle=0;

    // Start is called before the first frame update
    private void Start()
    {
     Collider collider = GetComponent<Collider>();
     if(collider != null)
     {
         //干渉しないようにisTriggerをつける 
         collider.isTrigger = true;
     }   
    }

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

     // ゲーム実行中に毎フレーム実行する処理

        //☆ 変数 targetPos を作成してターゲットオブジェクトの座標を格納
        Vector3 targetPos = target.position;//☆
        // 自分自身のY座標を変数 target のY座標に格納
        //(ターゲットオブジェクトのX、Z座標のみ参照)
        targetPos.y = transform.position.y;//☆
        // オブジェクトを変数 targetPos の座標方向に向かせる
        transform.LookAt(targetPos);//☆

        // 変数 distance を作成してオブジェクトの位置とターゲットオブジェクトの距離を格納
        float distance = Vector3.Distance(transform.position, target.position);//☆
        // オブジェクトとターゲットオブジェクトの距離判定
        // 変数 distance(ターゲットオブジェクトとオブジェクトの距離)が変数 moveDistance の値より小さければ
        // さらに変数 distance が変数 stopDistance の値よりも大きい場合



        if (this.transform.position.z-15 < target.transform.position.z)
        {
            Destroy(gameObject);
        }

        else if (distance < moveDistance && distance > stopDistance)//☆
        {
            ThrowingBall();
        }

    }
    //<summary>
    //ボールを射出する
    //</summary>
    private void ThrowingBall()
    {
        InstantiationTimer -= Time.deltaTime;
        if (ThrowingObject !=null && TargetObject !=null && InstantiationTimer <0)
        {
            //Ballオブジェクトの生成
            GameObject ball = Instantiate(ThrowingObject, this.transform.position, Quaternion.identity);
            InstantiationTimer =2f;

            //<old/>
            //GameObject ball = Instantiate(ThrowingObject, this.transform.position, Quaternion.identity);
            //</old>

            //標的の座標
            Vector3 targetPosition = TargetObject.transform.position;

            //射出角度
            float angle = ThrowingAngle;

            //射出速度を算出
            Vector3 velocity = CalculateVelocity(this.transform.position, targetPosition, angle);

            //射出
            Rigidbody rid = ball.GetComponent<Rigidbody>();
            rid.AddForce(velocity * rid.mass, ForceMode.Impulse);
        }

            }






    //<summary>
    //標的に命中する射出速度の清算
    //</summary>
    //<param name="pointA">射出開始座標</param>
    //<param name="pointB">標的の座標</param>
    //<returns>射出速度</returns>
    private Vector3 CalculateVelocity(Vector3 pointA, Vector3 pointB, float angle)
    {
        //射出角をラジアンに変換
        float rad = angle * Mathf.PI / 180;

        //水平方向の距離x
        float x = Vector2.Distance(new Vector2(pointA.x,pointA.z),new Vector2(pointB.x, pointB.z));

        //垂直方向の距離y
        float y = pointA.y - pointB.y;

        //斜方投射のこうしきを初速度について解く
        float speed = Mathf.Sqrt(-Physics.gravity.y * Mathf.Pow(x,2)/(2*Mathf.Pow(Mathf.Cos(rad),2)*(x * Mathf.Tan(rad)+y)));

        if(float.IsNaN(speed))
        {
            //条件を満たす初速を算出できなければVector3.zeroを返す
            return Vector3.zero;
        }
        else
        {
            return (new Vector3(pointB.x - pointA.x, x * Mathf.Tan(rad), pointB.z-pointA.z).normalized*speed);    
        }
    }
}

優しい方、よろしくお願いします。

0 likes

1Answer

課題とご提案

全体の設計が解らないので、アイデアのみのご提案になることをお許しください。

対象をばらけさせる

  • 現在は、targetがひとつだけですが、例えば、target1target2にして、発射毎に対象をランダムに選択してはいかがでしょうか。

照準 (誤差、移動先の予測)

  • 現在は、targetの位置をそのまま利用していますが、座標に乱数を加えて、少し狙いをずらしてはいかがでしょうか。
  • 対象との距離と弾体の速度および対象の速度から、到達までの時間を概算して、その時間と対象の位置と速度から、対象のおおよその位置を予測できるのではないでしょうか。
  • 弾速が小さい場合は軌道が放物線を描くので、少し上(Y軸正方向)を狙うように補正すると良いと思います。

複数の弾体を射出する

  • 同時に発射しても、照準から左右(X軸)へずらしてやれば、距離に応じてばらけると思います。
  • 連射にこだわるなら、コルーチン(StartCoroutine)にして、数フレーム毎に発射するのはいかがでしょうか。

蛇足

  • 記事の右上辺りにある「編集する」リンクから、下記の2箇所を書き替えることで、コードが見やすくなって、回答を得やすくなると思います。
  • 詳しくは、Qiita公式ヘルプの「コードの挿入」をご参照ください。

変更1

[射撃コード]以降の部分を以下のようにしてください。

	[射撃コード]

	```cs
	using System.Collections;
	using UnityEngine;
  • **```cs**の上に空行があります。
  • 行頭のタブ文字は、説明のためのものですので、入力しないでください。

変更2

コードの終端部分を以下のようにしてください。

	}
	``​`

	優しい方、よろしくお願いします。
  • **```**の下に空行があります。
  • 行頭のタブ文字は、説明のためのものですので、入力しないでください。
変更によって以下のような感じになります。 (クリックで開閉)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class throwingscript : MonoBehaviour
{
	//☆
	private float InstantiationTimer = 0f;
	// ターゲットオブジェクトの Transformコンポーネントを格納する変数
	public Transform target;//☆
	// オブジェクトの移動速度を格納する変数
	public float moveSpeed;//☆
	// オブジェクトが停止するターゲットオブジェクトとの距離を格納する変数
	public float stopDistance;//☆
	// オブジェクトがターゲットに向かって移動を開始する距離を格納する変数
	public float moveDistance;//☆

	//<summary>
	//射出するオブジェクト
	//</summary>
	[SerializeField, Tooltip("ThrowingObject")]
	private GameObject ThrowingObject=null;

	//<summary>
	//標的のオブジェクト
	//</summary>
	[SerializeField, Tooltip("TargetObject")]
	private GameObject TargetObject=null;

	//<summary>
	//射出角度
	//</summary>
	[SerializeField, Range(0F,90F), Tooltip("ThrowingAngle")]
	private float ThrowingAngle=0;

	// Start is called before the first frame update
	private void Start()
	{
		Collider collider = GetComponent<Collider>();
		if(collider != null)
		{
			//干渉しないようにisTriggerをつける
			collider.isTrigger = true;
		}
	}

	// Update is called once per frame
	void Update()
	{
		// ゲーム実行中に毎フレーム実行する処理

		//☆ 変数 targetPos を作成してターゲットオブジェクトの座標を格納
		Vector3 targetPos = target.position;//☆
		// 自分自身のY座標を変数 target のY座標に格納
		//(ターゲットオブジェクトのX、Z座標のみ参照)
		targetPos.y = transform.position.y;//☆
		// オブジェクトを変数 targetPos の座標方向に向かせる
		transform.LookAt(targetPos);//☆

		// 変数 distance を作成してオブジェクトの位置とターゲットオブジェクトの距離を格納
		float distance = Vector3.Distance(transform.position, target.position);//☆
		// オブジェクトとターゲットオブジェクトの距離判定
		// 変数 distance(ターゲットオブジェクトとオブジェクトの距離)が変数 moveDistance の値より小さければ
		// さらに変数 distance が変数 stopDistance の値よりも大きい場合

		if (this.transform.position.z-15 < target.transform.position.z)
		{
			Destroy(gameObject);
		}
		else if (distance < moveDistance && distance > stopDistance)//☆
		{
			ThrowingBall();
		}
	}

	//<summary>
	//ボールを射出する
	//</summary>
	private void ThrowingBall()
	{
		InstantiationTimer -= Time.deltaTime;
		if (ThrowingObject !=null && TargetObject !=null && InstantiationTimer <0)
		{
			//Ballオブジェクトの生成
			GameObject ball = Instantiate(ThrowingObject, this.transform.position, Quaternion.identity);
			InstantiationTimer =2f;

			//<old/>
			//GameObject ball = Instantiate(ThrowingObject, this.transform.position, Quaternion.identity);
			//</old>

			//標的の座標
			Vector3 targetPosition = TargetObject.transform.position;

			//射出角度
			float angle = ThrowingAngle;

			//射出速度を算出
			Vector3 velocity = CalculateVelocity(this.transform.position, targetPosition, angle);

			//射出
			Rigidbody rid = ball.GetComponent<Rigidbody>();
			rid.AddForce(velocity * rid.mass, ForceMode.Impulse);
		}
	}

	//<summary>
	//標的に命中する射出速度の清算
	//</summary>
	//<param name="pointA">射出開始座標</param>
	//<param name="pointB">標的の座標</param>
	//<returns>射出速度</returns>
	private Vector3 CalculateVelocity(Vector3 pointA, Vector3 pointB, float angle)
	{
		//射出角をラジアンに変換
		float rad = angle * Mathf.PI / 180;

		//水平方向の距離x
		float x = Vector2.Distance(new Vector2(pointA.x,pointA.z),new Vector2(pointB.x, pointB.z));

		//垂直方向の距離y
		float y = pointA.y - pointB.y;

		//斜方投射のこうしきを初速度について解く
		float speed = Mathf.Sqrt(-Physics.gravity.y * Mathf.Pow(x,2)/(2*Mathf.Pow(Mathf.Cos(rad),2)*(x * Mathf.Tan(rad)+y)));

		if(float.IsNaN(speed))
		{
			//条件を満たす初速を算出できなければVector3.zeroを返す
			return Vector3.zero;
		}
		else
		{
			return (new Vector3(pointB.x - pointA.x, x * Mathf.Tan(rad), pointB.z-pointA.z).normalized*speed);
		}
	}
}
0Like

Comments

  1. @hasegawadesu

    Questioner

    回答ありがとうございます!

    どうすればいいのかも想像ついていなかったので助かりました!

    ・target対象のランダム選択
    ・座標に乱数を加える は、勉強してやってみたいと思います!

    ・複数射出は、センターのオブジェクトを軸に左右に向かう様にしてみます!

    ・```cs ```ありがとうございました!出来ました!

Your answer might help someone💌