KazumaLab.

流行りとリラックマと嵐が大好きです。

スマブラが発売されたけどホームランコンテストがなかったのでちょっと作ってみる

はじめに

みんなのウェディングのエンジニア@kazumalabです。 この記事はくふうカンパニーアドベントカレンダーのなんと10日目になります!

記事のお題をGraphQLにしてたのですが、タイミングがスマブラ発売と被ってしまいゲームのやりすぎで記事を書けそうありません。 (ほんとミス!)

ですのでお題をチェンジして書こうと思います。全くWeb関係からは離れます!←

スマッシュブラザーズSP

先ほども言いましたが、スマブラが発売されました。 みにいくだけ...と思ってふらっとヤマダ電機に行ったら PayPayで買えば20%Off、運良ければ全額返ってくるという「PayPayキャンペーン」をやっていました。

ええ、その誘惑に負けたわけですね。 ついつい買ってしまいました。

さらにアドカレ当日なのについついやってしまいました。それが以下の写真です。

今年の夏ぐらいにプロジェクターをかったのでその大画面でやるスマブラは格別でした。(そんなことは置いといて...)

今回のアドカレネタ

このスマブラ...ホームランコンテストがない!3DSWiiUまではあったのに... え、じゃあ作ってみてもいいんじゃない?それをアドカレのネタにしちゃおう!というのが今回です。 まぁ結論からいうとホームランを撃ち抜くところまではいかなかったです...。けどダメージを受ければ受けるほどぶっ飛び率が上がっていくところはできたはずです!

そういえば以前に僕は格ゲーを作っていたのを覚えてますでしょうか...はるか昔の記憶です。

これは3年前だったんですね。もっと最近だと思ってました。 時間もないのでどんどん作っていきましょー!

キャラクターを配置

右側がプレイヤ、左側がサンドバックくんです。 サンドバックはもう面倒なのでCylinderそのままです。笑

Playerは有料アセットを使っています。

プレイヤを動かす

今回は簡易的に作るのでCharactorControllerを使ってPlayer.csを実装します。 さらにPlayerのタグをつけておきます。(のちに使う)

private CharacterController charactorController;

void Start () {
    charactorController = this.GetComponent<CharacterController> ();
}

void Update () {
    direction = (Vector3.right * Input.GetAxis ("Horizontal") + Vector3.forward * Input.GetAxis ("Vertical")) * moveSpeed;
    Move (direction);
}

void Move (Vector3 v) {
    charactorController.Move (v);
    if (v.magnitude > 0.01f) { // 向いている方向に向かせる
        transform.rotation = Quaternion.LookRotation (v);
    }
}

とりあえずこんな感じでセットします。 実際にはanimatorで歩くモーションを実装していますが今回は省いています。

あ、カメラはいい感じに(追従する感じなどなど)設定してくださいね!

攻撃する

さて、次はキモとなる攻撃部分を作っていきます。 スマブラではいくつか組み合わせ技がありますが、まずはパンチ一つに絞って実装します。(しかし複数使えるようにはしておく)

今回は与えるダメージ(吹っ飛ばし力)を計算するために必要なデータを準備します。

サンドバックくんのWeight50に設定し、 プレイヤのパンチの威力は以下の図のようになりました。

DMG BKB KBG
5.0 30 100

BKBやKBGについては

www30.atwiki.jp

上記サイトを参考にし、算出についてはスマッシュブラザーズ for WiiUの情報を参考にしました。

Duck Hunt - Kurogane Hammer

ちなみにダックハントのジャブと同じです。

以下のように先ほど動かすために作ったPlayer.csに追記します。

public Dictionary<string, Dictionary<string, float>> actions;
public enum Actions {
    panch
};
public Actions action;
public bool enableHit = false;

private void Start () {
    actions = new Dictionary<string, Dictionary<string, float>>();
    Dictionary<string, float> actionInfo = new Dictionary<string, float>();
    actionInfo.Add("DMG", 5.0f);
    actionInfo.Add("BKB", 30f);
    actionInfo.Add("KBG", 100f);
    actions.Add(Actions.punch.ToString(), actionInfo);
}

private void Update () {
    Attack();
}

public void Attack () {
    if (Input.GetMouseButton (0)) {
        animator.SetInteger ("Attack", 4);
        action = Actions.punch;
        StartCoroutine (AttackTime ());
    } else {
        animator.SetInteger ("Attack", 0); // Attack0にしないと無限に攻撃が動く
    }
}

private IEnumerator AttackTime () { // すこし時間を開けないと攻撃があたり判定されない
    enableHit = true;
    yield return new WaitForSeconds (0.5f);
    enableHit = false;
}

public float getDamage () {
    return actions[action.ToString()]["DMG"];
}

public float getKBG () {
    return actions[action.ToString()]["KBG"];
}

public float getBKB() {
    return actions[action.ToString()]["BKB"];
}

actionsのkeyには攻撃名、valueには与えるダメージが入るようになっています。 Enumで定義したActions一覧からactionsを作ります。

今回はアニメーションをSetIntegerで設定しました。 enableHittrueの時だけ相手に攻撃が当たるようにしています。

action = Actions.punch;

ここでは何の攻撃をしたかを残すようにしています。 これでクリックをすると攻撃アニメーションになり、攻撃可能時間が0.5秒だけ有効になります。(ちょっと微妙仕様)

攻撃を与える、受ける

さてついにサンドバックくんが攻撃を受けるようにします。 まずは攻撃を受ける部分をEnemy.csを作り書いていきます。これはサンドバックくんにコンポーネントとして貼り付けます。

private Rigidbody rb;
private float weight = 50f;

private void Start() {
    rb = GetComponent<Rigidbody>();
}

public void ReceiveDamage(Player target) {
    if (isHit) {
        isHit = false;
        hitpoint += target.getDamage();
        if (rb) {
            // ここのVector.up本当は技の角度とユーザーのスティックから変更するべき
            rb.AddForce((target.transform.forward + Vector3.up) * KnockBack(target));
        }
    }
}

private float KnockBack(Player target) {
    return ((hitpoint * (0.1f + target.getDamage() * 0.05f) * 200 / (weight + 100) * 1.4f + 18) * target.getKBG() * 0.01f + target.getBKB());
}

吹っ飛ばし力を計算する式が以下のサイトに載っていたので引用します。

www30.atwiki.jp

KB = [{敵% * (0.1 + ダメージ * 0.05) * 200 / (敵重量 + 100) * 1.4 + 18} * KBG * 0.01 + BKB] * 補正値

これが先ほどEnemy.csに記述したKnockBack関数になります。

次に攻撃した時に当たった判定をする部分を実装します。

まずは攻撃する側(プレイヤ)の手の部分にColliderをつけます。この時isTriggerにチェックを入れます。Rigidbodyはなくても良さそうです。 スクリプトを書き、手の部分にアタッチします。上の画像でいうとCollisionController.csになります。

public class CollisionController : MonoBehaviour {

    private Player player;
    private GameObject particle; // これは当たった時に発火するParticleなのでお好み
    private Enemy hitEnemy;

    // Use this for initialization
    void Start() {
        player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
    }

    // Update is called once per frame
    void OnTriggerStay (Collider other) {
        if (other.tag == "Enemy") {
            hitEnemy = other.GetComponent<Enemy> ();
            if (player.enableHit && hitEnemy) {
                if (particle == null) {
                    particle = (GameObject)Instantiate (player.hit_particles [0], this.transform.position, player.hit_particles [0].transform.rotation);
                    other.GetComponent<Enemy>().isHit = true;
                    other.GetComponent<Enemy>().ReceiveDamage(player);
                }
            } else {
                particle = null;
                hitEnemy = null;
            }
        }
    }
}

あとは最後にUIをごにょっとすると...

ちょっとスマブラっぽくなりました!

まとめ

今回はノックバックのみの実装になってしまいましたが、継続して開発して「タメ技」とか「バットを使ったタメ技」などを入れてみたいですね。 なかなかスマブラを作るのは厳しいのであれは7000円でも安いと思う...

みんな、、、スマブラ休暇とろうな!(嘘です!!!!!!!!!!!!!!!!!笑)

今回書いたコードは本当はInterfaceを作ってベースとなるクラスを用意して、Enemy、Playerに継承する形にしていました。 ただ、多分そこまで解説できなさそうだったので、冗長ですが、別ものとしてブログ用に実装しました。ホームランコンテスト自分が攻撃を受けることないですし!

明日は @tatsuo48さんのAWSの新サービスについて何か書いてくれるみたいです! お楽しみに!