[Unity] ブロック崩しをあのアプリ風にする #6 連射できるようにする

お次はボールを管理するBallManagerクラスを作ります。


...今回画像が少ないので、なんか凄そうな画像をトップに持ってきました。
いかにもディープラーニングとかやってそうな雰囲気ですがそんなものは1ミリも出てこないので安心してください。

BallManagerの作成

玉の発射機能をつけます。欲しい機能は
  1. 設定された数だけ玉を撃つ
  2. 時間差をつけて発射する
  3. 撃ってる最中は入力を受け付けないようにする
  4. 下に当たったらストップ→最初に当たったボールの位置に集合
こんなところですかね。まずは1〜3まで書いてみます。
じゃあスクリプト行ってみよー

using System.Collections.Generic;
using UnityEngine;

public class BallManager : MonoBehaviour {

//Ballプレハブ
    GameObject ball;
//ボールの初期位置
    private Vector3 origin; 
//最大ボールの量
    [SerializeField]
    int MaxBall;
//現在のボールの量
    private List<GameObject> balls = new List<GameObject>();
    [System.NonSerialized]
//撃ってる最中か
    public bool isShooting;  

    void Start () {
        isShooting = false;  
        BallBorn ();
    }

//足りない分リストに追加
    void BallBorn () {
        while(MaxBall > balls.Count) {
            var ob = Instantiate (ballPre,ballBornPoint.position,Quaternion.identity)as GameObject ;
            balls.Add (ob);
        }
    }

//発射 InputControllerから呼ばれる
    public void Shooting(Vector3 vec){
        isShooting = true;  
        Vector3 pos = vec - ballBornPoint.position;
//ベクトルの大きさを1にする
        pos.Normalize ();
        StartCoroutine("shoot",pos);

    }
    IEnumerator shoot(Vector3 vec){
        foreach (GameObject obj in balls) {
            Rigidbody rig = obj.GetComponent<Rigidbody> ();
            rig.velocity = vec * speed;
            yield return new WaitForSeconds (0.2f);
        }
    }  
}

isShootingの上の[System.NonSerialized]はパブリック変数をインスペクターから操作されたくない時に隠すことができます。


Start

この中ではBallBornを呼び出してisShootingをfalseにしているだけです。


BallBorn

呼び出されたBallBornでは設定された数のボールを生み出します。生み出されたボールはタッチ操作で飛ばす方向を決定するので、プレハブのBallについているBall_Moveスクリプトは外しておきます。


Shooting

このメソッドには最後にタッチされていた座標が渡ってきます。Epitaphの時と同じように角度を計算しつつNormalizeでベクトルの大きさを1(正規化)にします。これにスピードをかけてやれば狙った速度ですっ飛んでいく訳です。このシリーズを書いていて見つけた便利メソッドその2です。これに出会うまで大分右往左往しました..


StartCoroutine, IEnumerator

生成したBallたちを一気に動かすのではなく間をとって順番に飛ばしたいのでStartCoroutineでコルーチンを呼び出します。コルーチンを使うと繰り返し処理を任意のタイミングで実行させることができます。行末のyield return new WaitForSeconds(0.2f)で次に呼ばれるタイミングを操作してます。StartCoroutineとIEnumeratorは必ずセットなのでむしろ大事なのは行末のyield return〜 の表記だと思います。
Updateなどの更新タイミングにとらわれない処理をさせたい時に便利です。



InputControllerの修正

次は飛ばす方向の取得と、発射していない時だけ操作できるようにInputControllerを変更します。
 
//〜中略〜
Epitaph epitaph;
//タッチされている座標
private Vector3 releasePoint;
BallManager ballManager;

void Start(){
 epitaph = GetComponent<Epitaph> ();
 ballManager = GetComponent<BallManager> (); 
}
//〜中略〜
    Epitaph epitaph;
//タッチされている座標
private Vector3 releasePoint; 
BallManager ballManager;

void Start(){
    epitaph = GetComponent<Epitaph> ();
    ballManager = GetComponent<BallManager> (); 
}
//〜中略〜

private void OnPress(object sender, System.EventArgs e) {
    if (!ballManager.isShooting) {  
        epitaph.Visualaization ();
        var gesture = sender as PressGesture;
        Vector3 point = gesture.ScreenPosition;
        LineUpdate (point);  
    }
}

private void OnTransformed(object sender, System.EventArgs e) {
    if (!ballManager.isShooting) {  
        var gesture = sender as TransformGesture;
        Vector3 point = gesture.ScreenPosition;
        LineUpdate (point);
    }  
}  

void LineUpdate(Vector3 point){
    point.z = Camera.main.transform.position.y ;
    releasePoint = Camera.main.ScreenToWorldPoint (point);
//p > releasePointに変更
    if (releasePoint.z - 0.1f >= gameObject.transform.position.z) {
        epitaph.SetPoint (releasePoint);
    } else {
        epitaph.Misunderstanding ();
    }
}

private void OnRelease(object sender, System.EventArgs e) {

    if (!ballManager.isShooting) {
        if (releasePoint.z - 0.1f >= gameObject.transform.position.z) {
//点を不可視化
            epitaph.Misunderstanding ();
            ballManager.Shooting(releasePoint); 
        }
    }
}
//〜中略〜


変更点

指を離したタイミングでShootingが呼ばれるようにしたいのでStart内でBallManagerを取得しておいてOnReleaseでShootingメソッドを呼び出します。でもOnRelease内でタッチ位置の取得のためにまた同じスクリプトを書くのは冗長な気がします。書いてダメってことはないけど。
なのでLineUpdate内で変換した位置をreleasePointとして保存しておいて、それを渡すようにしてみました。さらにOnReleaseに関してはリリースされた座標がボールの位置より若干高い位置という条件も加えています。

あとは 各ハンドラをisShootingがfalseの時のみ実行されるようにしてます。

それと、今の状態ではボール同士互いに衝突するのでそれをなんとかします。
Edit > Project Settings > Physics と行くとレイヤー間の当たり判定を変えられるのでBall同士のチェックを外します





あとは4番目の「下の壁に当たったら集合」を実装します。
でもちょっと長くなってきたので次にします

※現状では発射すると垂直もしくは水平にしか動かなくなったり、ボールが徐々に加速していきます。そのあたりの細かな挙動は#10で修正しています。



コメント