見出し画像

【unity2D】防御可能な極太ビームを実装してみた

■近況

どうもお久しぶりです。私です。

自由時間の9割をスプラ3にもってかれてしまって牛歩になっていたゲーム制作でしたが、やっとこさ2面のおおよそが完成しました。

ここまで遅くなった要因の一つにドット絵を書きすぎてしまったということもあります。
あんまり得意でない絵に時間を取られすぎてしまいました。

他にもリファクタリングやら演出強化やら操作性改善など色々やってましたが、次面以降のドット絵はもう少し簡素化したいですね。

■前置き

といったところで「ボスの極太ビームを実装してみた」という話です。

最初はなるべくシンプルなゲームにしようと考えていたけど、色々作ってくると「やっぱボスにバリエーションほしいな…」と思ってしまうものです。

なので「ボスを戦車にして、『全ての攻撃を弾く刀』と相性の良さそうな極太レーザーを実装しよう!」と思いついてしまうのもある種の必然だったのでしょう。

仕様としては
『ビームに当たるとダメージ、刀や技などで防いだ所以降は途切れる』って感じです。

分かりやすく言うとこれです。

というかガンハザードの背景クソ素晴らしいですね。もっと早く気づいて参考にしてればよかった


■本題

世の中のゲームで「雨とか滝を傘等で一部防ぐ」とかいう要素は度々目にしてきたので、ググればすぐ見つかるかなーと思ったのですがそんな事なかったので自分のやり方をまとめてみます。

正直もっといいやり方はあるんだろうなぁという気はしています。ray使ったり、判定とSortingLayerを使ったりすればできるかなー?


using UnityEngine;

/// <summary>
/// ビームを右から左向きに出し続ける。ビーム射出点にアタッチ
/// </summary>
public class BeamFire : MonoBehaviour
{
    [SerializeField] bool beamOrigin = true;
    Vector2 scale;  //画像反転用 反転する時はスクリプト要修正
    float vect = -1;   //射出方向
    [SerializeField] GameObject beamChild;
    [SerializeField] float beamMaxDistance = 50;
    float distance;

    [Header("左向きが+、4*4なら0.125")]
    [SerializeField] float instantPosX = 0.125f;    //4*4ならこれ左向き基準
    [SerializeField] float originInstantPosX = 0.3125f;

    // Start is called before the first frame update
    void Start()
    {
        if(!beamOrigin)
        CheckParentDistance();

    }

    // Update is called once per frame
    void Update()
    {
        if(beamMaxDistance < distance)
        {
            return;
        }
        if (transform.childCount == 0)
        {
            var childObj =Instantiate(beamChild, this.transform);
            childObj.GetComponent<BeamFire>().VectCheck(vect);
            childObj.transform.position = new Vector2(transform.position.x +(vect * instantPosX), transform.position.y);
            if (beamOrigin)
            {
                childObj.transform.position = new Vector2(transform.position.x + (vect * originInstantPosX), transform.position.y);
                distance = 0;
            }
        } 
        
    }

    public void VectCheck(float vectX = -1)
    {
        if(0 < vectX)
        {
            vect = 1;
        }
    }

    void CheckParentDistance()
    {
        var rootObj = this.transform.root.gameObject;   //GameManager
        if(rootObj != null)
        {
            var bossPosX = rootObj.transform.GetChild(0).transform.position.x;  //BossのX座標取得
            distance = Mathf.Abs(this.transform.position.x - bossPosX);
        }
    }


    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (beamOrigin)
        {
            return;
        }

        if (collision.gameObject.tag == "Katana")
        {
            Destroy(gameObject);
            Debug.Log("BeamOnkatana");
        }

        if (collision.gameObject.tag == "Skill")
        {
            Destroy(gameObject);
        }

    }

    private void OnDestroy()
    {
        if(transform.childCount == 1)
        {

            Destroy(transform.GetChild(0).gameObject);
        }
    }
}

方針としては『ビームを一つだけ子として左にinstantiateする』を一定距離まで繰り返して、刀などで一部がDestroyされたらもう一度作りなおすという感じです。
これをビームのy軸方向の分割数分(今回は縦96pixを8pixで分割したので12個)だけ作ってまとめてプレハブ化。スクリプト等でInstantiateしてもらうと発射し、終わるときはDestroyしてもらいます。
射出点だけはbeamOriginをtrueとして切っても消えないようにしています。(そもそもコライダーをつけていませんが)

最初は左右の向きに対応しようとしたのですが「現状左向きしか使わねぇしいいか」と思って途中から投げ出しています。VectCheckがその名残りです。

また、ビームは4✕4で細かくしようとしたのですが、あまりに処理が重たくなってしまったので8✕8にしました。
ですがおそらく処理を軽くするのに一番大事なのはレイヤーの設定だと思います。

それまでは特に考えなしにレイヤー設定していて、地面や敵やビーム同士などの本来不要な接触判定まで拾っていましたが、これを自機と刀等に限定したらかなり処理が軽くなりました。
というかこれをきっかけに全てのオブジェクトのレイヤー設定を見直したらかなり負荷が減りました。
レイヤー設定大事

このやり方の問題点としてはビームのスピードが設定できないという点があります。
たまたま良い感じに出来たのでそのままにしてますが、もし「Updateの処理が連続することでフレームあたりの処理数が減る」ということがあればその分遅くなるかと思います(プログラミングに疎いので仕様がわからない)
ただ少なくとも4✕4だと速度が落ちるので、ビームを細かく多く長くするほど、遅く重たくなるのは間違いないでしょう。

そういう都合を考えるとやはり違うスマートなやり方がある気がします。もう手直しはせんけど。


といった感じで以上です。あと2面作ってく過程でシンプルな気付きがあったので近々まとめます。
次はそろそろ解像度対応をしたいですね。

この記事が気に入ったらサポートをしてみませんか?