読者です 読者をやめる 読者になる 読者になる

いんでぃーづ

個人でゲーム開発してる上で吸収したモノたち紹介。UnityからGIMPまでなんでも。デザインとかゲーム論まで語っちゃうよ的なアレ。

MENU

クッキークリッカーをスクリプト1行で再現する

あのゲームを再現 Unity

「雑再現」シリーズ第三弾。

今回のテーマはクッキークリッカーです。

このゲームは”ひたすら画面のクッキーをクリックしてクリック数を増やしていく”という、比較的簡単な内容のゲームです。

「そんなもん俺だって作れるわ!」

という方。オーケーわかるよ。言いたいことはわかる。

だが今回は、ただUnityで実装するだけではありません。

なんと1行で実装します! (・`ω・)


シーンの作成とソースコード

今回も第一回と同じく、クッキーの代わりに「まるだしくん」を使います。

以下のようにシーンを構成し、「まるだしくん」オブジェクトにコライダとスクリプトを追加します。

f:id:sugar_affordance:20170108164808p:plain

そしてそして、肝心の1行だけ実装したスクリプトが以下のものです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using UnityEngine.UI;

public class Clicker : MonoBehaviour {

    void Start () {
        Observable.EveryUpdate().Where(frames => Input.GetMouseButtonDown(0) && GetComponent<Collider2D>().OverlapPoint(Camera.main.ScreenToWorldPoint(Input.mousePosition))).Select(frames => 1).Scan((prevClicks, currentClicks) => prevClicks + currentClicks).Subscribe(nextClicks => GameObject.Find("CountText").GetComponent<Text>().text = nextClicks.ToString());
    }
    
    // Update is called once per frame
    void Update () {

    }
}

誰がなんと言おうと一行ですね、ええ。

実際に動かすと

f:id:sugar_affordance:20170108165646g:plain

はい。目的は達成できました。みなさまサヨウナラ。

解説

このまま終わっても"なんのこっちゃ"な感じなので、不本意ですが改行を入れてみましょう。

Observable.EveryUpdate()
    .Where(frames => Input.GetMouseButtonDown(0) && GetComponent<Collider2D>().OverlapPoint(Camera.main.ScreenToWorldPoint(Input.mousePosition)))
    .Select(frames => 1)
    .Scan((prevClicks, currentClicks) => prevClicks + currentClicks)
    .Subscribe(nextClicks => GameObject.Find("CountText").GetComponent<Text>().text = nextClicks.ToString());

実はObservable.EveryUpdate()から始まる一連のメソッドチェーンはUnity標準の関数ではなく、UniRxという無料ツールを導入しています。

UniRxは「ソースのいろんなところに散らばりがちな処理をまとめて書けるライブラリ」みたいな感じですが、細かく説明すると何文字かけても足りないので、知りたい方は下記の公式サイトを参考に。

ReactiveX

Observable.EveryUpdate()

まず最初に呼び出しているこのメソッドですが、「一連の判定、処理を毎フレーム実行するよ」という宣言です。

Where

Where文を使うと、条件を満たしている場合のみ後の処理につなげます。

.Where(frames =>
 Input.GetMouseButtonDown(0) &&
 GetComponent<Collider2D>().OverlapPoint(
    Camera.main.ScreenToWorldPoint(Input.mousePosition)
  )
)

この場合は、 左クリックした & クリック位置がコライダとかぶっている という条件が成立した場合のみ、次の処理に行きます。

※OverlapPoint関数の注意として、カメラのProjection設定をOrthographicにしないと正常に検出できません。

Select

.Select(frames => 1)

Selectは、流れてきているデータを加工して後の処理につなげたい場合に使用します。
この場合、Observable.EveryUpdate()から経過フレーム数が流れてきているので、これを強制的に1にします。1クリックずつの処理だから1にするという程度の認識でよいです。

Scan

Scanは、ここまで流れてきたデータをSlectと同様に加工しつつ、加工した結果を次回の処理で使うことができます。

.Scan((prevClicks, currentClicks) => prevClicks + currentClicks)

prevClicksが前回のScanの計算結果、currentClicksはSelectの結果として返している1が固定で入ります。

prevClicks に1を加算して return しているので、今までマウスクリックした回数が結果になります。

Subscribe

Subscribeでは最終的に実行する処理を記述します。

Scanまでで総クリック数の計数が終わっており、その値がそのまま引数に渡されるので、それをuGUIのTextに設定しています。

.Subscribe(nextClicks => GameObject.Find("CountText").GetComponent<Text>().text = nextClicks.ToString());

便利ツールを使えばクッキークリッカーを一行で作成可能だということが証明できました。

あと完全に蛇足ですが、Rx使わなくても1行で書けたりします。

void Update () {
    GameObject.Find("CountText").GetComponent<Text>().text = Input.GetMouseButtonDown(0) && GetComponent<Collider2D>().OverlapPoint(Camera.main.ScreenToWorldPoint(Input.mousePosition)) ? (Int32.Parse(GameObject.Find("CountText").GetComponent<Text>().text) + 1).ToString() : GameObject.Find("CountText").GetComponent<Text>().text;
}

こっちの方がすごく雑で記事のテーマに合ったいいコードだと個人的には思います。(特にGameObject.Find使いまくってるあたりが)

じゃあなんでRx使ったのかっつーと、使いたかっただけです。

自分は満足です!さようなら!

Amazon.co.jpアソシエイト