見出し画像

【unity】2D横スク忍者アクションゲームを作る

前々から「ミニマムなテストゲームを作ったほうが良いのかな〜」とは思いつつも、これと言ったネタもないし「結局遠回りになるだけかな」という気持ちもありやらずにいたのですが、「作っても良いかな」というネタが思いついたのでやってみようかと思います。

ざっくり目指すのは
「ジャンプが気持ちいいだけの2D横スク忍者無双アクションゲーム」です。具体的に言うとほぼ影の伝説です。

ステージは5個くらいでボスはなし。敵も5体くらいな感じで。

背景が結構手間食いそうなのでネックですが、そこさえ用意できればそんなに負担はかからず出来るのではないかと甘く見積もっています。
BGMやSE、セーブ関連の実装は手を付けたことがないのでやってみたいですね。

事が決まればそこは行動力のある私なので、早速ドット絵やら曲やらプログラミングをしています。

こんなんとか
こんなんとか
こんなんとか

当然FC風でいきます。というかそうしないと書けない。そしてやっぱデカキャラだと圧倒的に俺の画力が足りねー。
製作中のローグライクゲームとは異なり厳密にFCパレットを守っていますが、容量やバンクに関しては気にしない方向で。
主人公の忍者以外はなるべく枚数も抑えていきます。

プログラミング的にはプレイヤーの操作はざっくり出来上がって、敵のパターン作ってます。敵の放物線移動がうまく実装できない…。

unity初心者本のサンプルだとrigidbody2DのAddForceを例として上げていたのですが「それだとボタン長押しによるジャンプ力の調整ができないのかな?」と思いtransformによる移動とAddForceを組み合わせて作ってみました。けどこれさてはForceMode.Forceにすりゃ良かった話ですかね…。
しかもtransformで長いこと重力に逆らってるとブルブルしだすのにちょっと困りました。以下プレイヤーのコード

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

public class PlayerController : MonoBehaviour
{
    [SerializeField]  float moveSpeed = 4;
    [SerializeField] float jumpSpeed = 12;
    [SerializeField] float jumpMax = 8; //ジャンプ終わりの浮力
    [SerializeField] Rigidbody2D rbody;
    [SerializeField] Animator anime;
    int jumpCount = 0;
    float jumpTimer = 0;
    public float jumpTimerMax = 1; //ジャンプボタンを押してる時間

    int nowDir; //アニメと攻撃用向き
    Vector2 scale;  //画像反転用
    public int jumpDir;    //ジャンプ時の方向
    public float jumpInertia = 0.05f ;  //ジャンプ時の逆入力反映値
    float xjump;    //ジャンプ中移動量

    public GameObject attackPrefab;

    bool onGround;
    public LayerMask groundLayer;

    private readonly int hashRun = Animator.StringToHash("Run");
    private readonly int hashJump = Animator.StringToHash("Jump");
    private readonly int hashAttack = Animator.StringToHash("Attack");
    
    //入力関連
    int inputX;
    bool jump;
    bool jumpButtonUp;
    float jumpButtonUpSec;  //jumpButtonUpだと1フレームのtrueをFixedUploadが見逃すので時間経過を拾う。
    bool attack;

    private void Start()
    {
        scale = transform.localScale;
    }


    // Update is called once per frame
    void Update()
    {
        inputX = (int)(Input.GetAxisRaw("Horizontal"));
        jump = Input.GetButton("Jump");
        attack = Input.GetButtonDown("Fire1");
        jumpButtonUp = Input.GetButtonUp("Jump");



        //ジャンプ長押しで再度ジャンプさせない
        if (onGround && jump == false)
        {
            jumpCount = 0;
        }

        //地上移動
        if (inputX != 0 && onGround )
        {
            anime.SetBool(hashRun, true);
            transform.position = transform.position + (new Vector3(inputX * moveSpeed, 0) * Time.deltaTime);
            nowDir = inputX;
            jumpDir = inputX;
            xjump = jumpDir * moveSpeed;    //ジャンプ中の慣性。移動中に決定
            anime.SetBool(hashJump, false);
        }
        if(inputX == 0 && onGround)
        {
            jumpDir = inputX;
            xjump = jumpDir * moveSpeed;
            anime.SetBool(hashJump, false);
            anime.SetBool(hashRun, false);
        }

        //空中移動
        if (!onGround)  
        {
            //if (inputX != jumpDir && !(-0.1 <= xjump && xjump <= 0.1) )   //止まるところまでパターン」
            //実際はmoveSpeedを超えてしまうので移動可能回数にムラ
            if (inputX != jumpDir &&(-moveSpeed <= xjump && xjump <= moveSpeed) )   
            {
                xjump += (inputX * jumpInertia);
            }
            transform.position = transform.position + (new Vector3(xjump, 0) * Time.deltaTime);
            anime.SetBool(hashJump, true);
        }
        //入力なしの時も慣性移動
        if(inputX != 0 && !onGround)
        {
            nowDir = inputX;
            anime.SetBool(hashJump, true);
        }

        //ジャンプ、jumpTimerMaxまではそのまま
        if (jumpCount == 0 && jump && (jumpTimer < jumpTimerMax))
        {
            transform.localPosition += new Vector3(0, jumpSpeed) * Time.deltaTime;
            jumpTimer += Time.deltaTime;
            anime.SetBool(hashJump, true);
        }

        //jumpTimerMax超えたら強制浮力
        if (jumpTimerMax <= jumpTimer)
        {
            jumpButtonUpSec += Time.deltaTime;
        }
        //上昇中にジャンプを離しても強制浮力 地面じゃ飛ばない
        if (jumpButtonUp && jumpCount == 0 && onGround != true)
        {
            jumpButtonUpSec += Time.deltaTime;
        }


        //攻撃
        if (attack)
        {
            GameObject newGameObject = Instantiate(attackPrefab) as GameObject;
            newGameObject.transform.SetParent(this.transform, false);
            Vector2 vect = transform.position;
            if(0 < nowDir) { vect.x += 0.2f; }
            else { vect.x += -0.2f; }
            newGameObject.transform.position = vect;
            anime.SetTrigger(hashAttack);

            Destroy(newGameObject, 0.3f);
        }

        //反転
        if (nowDir < 0) { scale.x = -1; }
        else { scale.x = 1; }
        transform.localScale = scale;


    }

    private void FixedUpdate()
    {
        //接地判定
        onGround = Physics2D.Linecast(transform.position, transform.position - (transform.up * 0.1f), groundLayer);

        if (jumpButtonUpSec != 0)
        {
            Debug.Log(jumpButtonUpSec);
            JumpFloat();
        }

    }


    void JumpFloat()
    {
        jumpCount++;
        jumpTimer = 0;
        var vect = transform.position + new Vector3(0, jumpMax);
        vect.x = 0;
        rbody.AddForce(vect, ForceMode2D.Impulse);
        jumpButtonUpSec = 0;
    }

    private void LateUpdate()
    {
        Vector3 pos = this.transform.position;  //自分の位置を変数に取得
        pos.z = -10;  //カメラなので手前に移動
        Camera.main.gameObject.transform.position = pos;  //移動

    }
}

個人的に思うジャンプが気持ち良いゲーム達(ロックマンDASH、GravityDaze2)はジャンプに慣性が効いていたのでほどほどに効かせています。
段差から降りたときに空中ジャンプが出来るのは「それはそれでいいかな〜」と思ってたのですが、前述のブルブルが強くなるのでやめるかもです。

…しかし自分でコードを考えて色々やるのは楽しいですが「こんなif文ばっかのコードでいいのか…?」という気はしますね。敵の方はSwitch文でそれなりにさっぱりしてるのですが…。
クラスの単機能化とか出来てるのかよくわからないし…
やっぱこういうのは本などから学ばないと内からは湧いてこないですね…。

とりあえずこのまま完成させようと思いますが、やっぱ背景がネックだなぁ…

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