ことばアルバム

にわかエンジニアのにわか備忘録

Unityでツムツム風のロジックを作ってみる

友人がディズニーツムツムにドハマりしてたので、それっぽく作れるもんかなぁ。。と思って試してみたときのメモ。
ゲームとしてリリースする予定は当然無く、ただの自習用のプロジェクトなので本当はcocos2d-x 3.0rcでやりたかったのですが、正直ちょっとめんどくさそうだったのUnityでやりました。
今回は基本ロジックだけで、なぞった時のエフェクトだとか制限時間だとかスコアとかは特に何もやっていません。
最近ロジックだけ作ったら満足しちゃうんです(´・ω・`)


まずは、最終的な完成イメージから。
↓こんな感じです。

f:id:is4cafe:20140323025106g:plain

↓サンプルはこちら
https://dl.dropboxusercontent.com/u/73913606/TsumiTsumi/TsumiTsumi_basic.html


準備

今回、画像系はちょっと気合い入れてみました。

 

背景っぽい楕円チックのやつは、後でMesh Collider使って楽したかったのBlenderのプリセットのやつをちょっと引き延ばして色つけただけのお手軽なやつです。

f:id:is4cafe:20140323025451p:plain

 

カラフルなボールっぽいやつはスキル無いなりにフォトショで5色作りました。パズドラカラーです。

f:id:is4cafe:20140323025551p:plain


実装

今回は、タップしたところにボールがあるかどうかなどの所は極力端折って、このゲーム独自で用意したところを中心に見て行きたいと思います。
 

続いて、5色のボールを用意してプレハブ化します。
ボールにはそれぞれGreenBallRedBallなどの、それぞれ色が判別できるタグを設定します。
ボールのほうにスクリプトを付加しますが、内容はこんな程度です。

public class BallScript : MonoBehaviour {
    private Quaternion defaultRotation;

    void Start () {
        defaultRotation = transform.rotation;
    }
    
    void Update () {
        transform.rotation = defaultRotation;
    }

    void TappedDestroy()
    {
        Destroy(gameObject);
    }
}

へんな回転をして欲しくないので初期値のrotationを保とうとしています。
rigidbodyのConstrainsFreeze Rotationは力が加えられたことによっての回転はしなくなりますが、transfromのrotationが変化する場合には対処できないので、今回こうしました。
あと、消す時に呼ばれるメソッドを用意しています。
いまはただDestroyしてるだけですが、あとでここでパーティクルシステムを生成したりする予定です。

 

続いて、メインのロジックを作って行きます。
まずはボールを生成する様のメソッドを作ります。

  public GameObject redBallPrehab;
    public GameObject yellowBallPrehab;
    public GameObject blueBallPrehab;
    public GameObject greenBallPrehab;
    public GameObject purpleBallPrehab;

    public GameObject ballSpawnPoint;

    void PrepareBalls(int number)
    {
        for (var i = 0; i < number; i++)
        {
            GameObject ball = ChoosePrehab();
            Vector3 position = ballSpawnPoint.transform.position;
            position.x = Random.Range(position.x - 1.2f, position.x + 1.2f);
            position.y = Random.Range(position.y - 1.0f, position.y + 1.3f);
            Instantiate(ball, position, ball.transform.rotation);
        }
    }

    GameObject ChoosePrehab()
    {
        int v = Random.Range(1, 6);
        if (v == 1)
        {
            return redBallPrehab;
        }
        else if (v == 2)
        {
            return yellowBallPrehab;
        }
        else if (v == 3)
        {
            return greenBallPrehab;
        }
        else if (v == 4)
        {
            return purpleBallPrehab;
        }
        else
        {
            return blueBallPrehab;
        }
    }

ゲームスタート時(スクリプトのStart()メソッド内)で、このPrepareBalls()を任意の数を引数にして呼びます。
あと、ボールを消した時にも消した数を引数にして呼び出しています。
ballSpawnPointはボールを生成したい場所に置いてあるだけの空のEmptyObjectです。
 

続いて、タップしたりなぞったりしたときの処理などのメインのロジックです。
新たに二つの変数を用意します。
lastKeepBallは最後になぞったボールオブジェクトを保持しておくためのものです。
keepBallsはなぞって消す候補のオブジェクトが入ったリストです。

private GameObject lastKeepBall;
List<GameObject> keepBalls = new List<GameObject>();

あとは、タップした時に消す候補のボールになりえるかどうかのチェックを下記の3つを使って行います。

  bool IsBall(GameObject obj)
    {
        if (obj == null) {
            return false;
        }
        return (obj.tag == "GreenBall" || obj.tag == "RedBall" || obj.tag == "BlueBall" || obj.tag == "PurpleBall" || obj.tag == "YellowBall");
    }

    bool IsAvailableTag(GameObject obj)
    {
        if (obj == null) {
            return false;
        }
        return (obj.tag == lastKeepBall.tag);
    }

    bool IsAvailableDistance(GameObject obj)
    {
        if (obj == null) {
            return false;
        }
        float distance = Vector3.Distance(obj.transform.position, lastKeepBall.transform.position);
        return distance < 1.0f;
    }

IsBall()は、タップした所にあったオブジェクトがボールか否かのboolを返しています。
IsAvailableTag()は、最後になぞったオブジェクトと同じタグを持っているオブジェクトか否かのboolを返しています。
IsAvailableDistance()は、最後になぞったオブジェクトからの距離を見て1.0f未満か否かのboolを返しています。

 

消す候補にするかどうかの判定は下記のような条件文にしました。

GameObject hitObject = hit.collider.gameObject;
if (IsBall(hitObject) && (lastKeepBall == null || (lastKeepBall != hitObject && IsAvailableTag(hitObject) && IsAvailableDistance(hitObject))))
{
    lastKeepBall = hitObject;
        keepBalls.Add(hitObject);
}

 
こんな感じで消す候補のボールを貯めて行き、マウスのクリックが離された時に下記のような感じで消す候補が3つ以上あればボールを消し、新しいボールを生成しています。

  if (Input.GetMouseButtonUp(0))
    {
        if (keepBalls.Count >= 3)
        {
            for (int i = 0; i < keepBalls.Count; i++)
            {
                keepBalls[i].SendMessage("TappedDestroy");
            }
            PrepareBalls(keepBalls.Count);
        }
        lastKeepBall = null;
        keepBalls.Clear();
    }

 
これだけのソースコードでもあのツムツムっぽい挙動に近づけられました。
面白いゲームを写経のように真似て作ってみるのは非常に勉強になりますね。