エンジニアもどきの雑記

メモ帳です。きっと。macアプリ配信windowsでもできるようになるまで絶対にメイン機にしないマン。特筆しない限り環境は【win10home,i5-7200u,8g,64bit,巨乳が好き】

【Unity】誰でも理解できる放置ゲーの作り方……につながる記事

adventar.org の21日目用の記事でございます。

完全にタイトル死んだ すっげぇ長く画像もたくさん使って書いたのに、 パソコンが何度も止まってしまったのでもう細かくわけずにとりあえずババっと書き直した(というかもう前回の痕跡が一部しかない)ので それを載せます。 記事も、コードも、全部全部全部だッ!! 本当はゲームを一本簡単なのを作成する予定だった。 ふぁっく

さて、気を取り直して

絵が下手なのは通勤時間が長いせい
私です。

流行ってるよね。放置ゲー、もしくはクリッカー

私が一番やってたのはファイナルタップタジーという放置+クリッカーのやつでした。
今はもうスマホスターオーシャン専用機になったので消してしまったけどね。
スターオッサンになりつつあります。
バインバインちゃん達の水着姿がみたい。待ってます。

f:id:dddpga1:20171222005412j:plain
最近だとどうぶつの森が話題になってるけど、あれもある種の放置ゲーだよね。
ちなみにあれはUnity産だよ。

さて、じゃあそれをどうやってやっているのか、というのが今回の話題。


簡単に言えば以下です。

・トリガー事に時間を保存(ゲームを落とす、止めるも含む。セーブ機能はUnityのを使う)
・それをバイナリ(文字)にしてセーブ
・再起動した時点での時間を取得
・セーブデータよりバイナリを取得して時間へと変換。
・比較
・演算
・実行


先に書いた通り、ゲーム一本作る予定だったので、
ちゃんとオブジェクト指向的にファイルも分けて作ってたけど死んだ。
と、いうか時間的に間に合わなかったので以下。
今ほんと書いたのでくそ汚いし、足りない部分もあるんだけど、
とりあえず動くので以下のコードで流れを解説していく。

放置ゲー根底の仕組みの話なので、このまま流用とかは絶対に無理だけど、
これがわかればとりあえず誰でも放置ゲー作れるようになります。

マジで。

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

public class GameManager : MonoBehaviour {

    public GameObject orbPrefab;
    public GameObject orbCounter;
    public GameObject time;
    public GameObject saveTime;

    private const string KEY_TIME = "TIME";
    private const string KEY_ORB = "ORB";

    private const int RESPAWN_TIME = 3;

    private DateTime lastDateTime;

    private int orbNum;
    private TimeSpan now;
    private TimeSpan oldTime;

 // Use this for initialization
 void Start () {
        String tmpTime = PlayerPrefs.GetString(KEY_TIME, "");
        if (tmpTime == "")
        {
            lastDateTime = DateTime.UtcNow;
        }
        else
        {
            long temp = Convert.ToInt64(tmpTime);
            lastDateTime = DateTime.FromBinary(temp);
        }

        orbNum = PlayerPrefs.GetInt(KEY_ORB, 0);
        now = DateTime.UtcNow.TimeOfDay;
        oldTime = lastDateTime.TimeOfDay;
    }
    
 // Update is called once per frame
 void Update () {
        UpdateText();
        TimeSpan timeSpan = DateTime.UtcNow - lastDateTime;

        if (timeSpan >= TimeSpan.FromSeconds(RESPAWN_TIME))
        {
            while (timeSpan >= TimeSpan.FromSeconds(RESPAWN_TIME))
            {
                PopOrb();
                orbNum++;
                timeSpan -= TimeSpan.FromSeconds(RESPAWN_TIME);
            }
        }
    }

    //orb出現
    void PopOrb()
    {
        lastDateTime = DateTime.UtcNow;

        GameObject orb = (GameObject)Instantiate(orbPrefab);
        orb.transform.localPosition = new Vector2(
           UnityEngine.Random.Range(-3.0f, 3.0f),
           UnityEngine.Random.Range(-3.0f, 3.0f)
           );

        Save(lastDateTime);
    }

    void UpdateText()
    {
        orbCounter.GetComponent<Text>().text = "orb数:" + orbNum;
        time.GetComponent<Text>().text = "時刻:" + now;
        saveTime.GetComponent<Text>().text = "保存:" + oldTime;
    }

    void Save(DateTime lastDateTime)
    {
        PlayerPrefs.SetInt(KEY_ORB, orbNum);
        PlayerPrefs.SetString(KEY_TIME, lastDateTime.ToBinary().ToString());
        PlayerPrefs.DeleteAll();
    }
}



追加でusingしているのは以下の二つ

using UnityEngine.UI;
using System;

text用のUnityEngine.UIとDateTimeとか用のSystem

クラスのメンバが以下

//GameObjectはUnityEngine上でアタッチ
public GameObject orbPrefab;    //2dobjectにknobで見た目だけ作ったorb(ただの白い〇)
public GameObject orbCounter;    //orbの数を表示するtext
public GameObject time;    //起動時刻を表示するtext
public GameObject saveTime;  //セーブ時刻を表示するtext

private const string KEY_TIME = "TIME";   //時間save用のkey
private const string KEY_ORB = "ORB";    //orb数save用のkey

private const int RESPAWN_TIME = 3;    //出現間隔の定数

private DateTime lastDateTime;    //saveTimeとかで使う

private int orbNum;    // text表示用の変数 以下同じ(ようなもの)
private int fieldOrbNum;
private TimeSpan now;
private TimeSpan oldTime;


Start()関数はscriptが起動して呼び出される関数。
詳しくはUnityやって。

// Use this for initialization
 void Start () {
        String tmpTime = PlayerPrefs.GetString(KEY_TIME, "");
        if (tmpTime == "")
        {
            lastDateTime = DateTime.UtcNow;
        }
        else
        {
            long temp = Convert.ToInt64(tmpTime);
            lastDateTime = DateTime.FromBinary(temp);
        }

        orbNum = PlayerPrefs.GetInt(KEY_ORB, 0);
        now = DateTime.UtcNow.TimeOfDay;
        oldTime = lastDateTime;
    }


Start()内ではまぁconstructorみたいなことをしてる。
違うものだけどね。今回やってることは同じ。
セーブデータを取得、なければ空をtmpTime変数に入れる。
tmpTimeを判定してlastDateTimeに現在時刻を入れるか、バイナリ文字列にしてセーブしたデータを入れるか判定。
その後、orbの方も同様にした後、現在時刻と保存時刻をそれぞれの変数に入れてる。



Update()関数はフレーム毎に(基本は)呼び出されるUnityの関数。
ここは詳しく触れたかったんだけど、時間ないのでまた別記事で。

※別記事(適当連載、未完)

dddpga1.hateblo.jp

// Update is called once per frame
void Update () {
        UpdateText();
        TimeSpan timeSpan = DateTime.UtcNow - lastDateTime;

        if (timeSpan >= TimeSpan.FromSeconds(RESPAWN_TIME))
        {
            while (timeSpan >= TimeSpan.FromSeconds(RESPAWN_TIME))
            {
                PopOrb();
                orbNum++;
                fieldOrbNum = orbNum;
                timeSpan -= TimeSpan.FromSeconds(RESPAWN_TIME);
            }
        }
}

リアルタイムで更新され続けるので(イメージね)
テキストの変化と、orbを出現させる判定とかとってる。
実行は別メソッド。
このテキストの更新の仕方、あまりよくないけど、時間なかったので許してね。

残りは機能的な関数

//orb出現
void PopOrb()
{
    lastDateTime = DateTime.UtcNow;

    GameObject orb = (GameObject)Instantiate(orbPrefab);
    orb.transform.localPosition = new Vector2(
       UnityEngine.Random.Range(-3.0f, 3.0f),
       UnityEngine.Random.Range(-3.0f, 3.0f)
       );

    Save(lastDateTime);
}
//テキスト表示
void UpdateText()
{
    orbCounter.GetComponent<Text>().text = "orb数:" + orbNum;
    time.GetComponent<Text>().text = "時刻:" + now;
    saveTime.GetComponent<Text>().text = "保存:" + oldTime;
}
//データセーブメソッド
void Save(DateTime lastDateTime)
{
    PlayerPrefs.SetInt(KEY_ORB, orbNum);
    PlayerPrefs.SetString(KEY_TIME, lastDateTime.ToBinary().ToString());
    //PlayerPrefs.DeleteAll();
}


オーブを出現させるところはprefabでinstantiateしてるだけ。
ここはunity少しでも触ったことあれば、あー、ってなるはず。

テキスト表示は今回まとめた。わかりゃいいんだよ精神

セーブはUnityのPlayerPrefs機能を使う。
最後の行の

PlayerPrefs.DeleteAll();

はセーブデータを消すやつ。デバッグ用。
使わない時はコメントアウト。ビルドする時は消しちゃおうね。

詳しい解説は別記事でします。いつか

で、実際にみてみよう。


f:id:dddpga1:20171222012445g:plain
停止してもその後の再開で増える、
一旦消してもしっかりカウントは増える
のが確認できたのではないでしょうか。

orb画像増えてねぇ、って、そりゃそうだ。
その機能入れてないんだから。ほんとに一から慌てて書き直したんだ、許してくれ

と、いうわけで以上が放置ゲーの基礎です。

これで君も明日には放置ゲー作れるように!



放置ゲーについては所謂和尚本で学んだ。二冊とも持ってる。
Unity初めて触る人には超優しい一冊目と、その次のステップの二冊目、って感じで今出てて、
今回やった放置もその二冊目から学んだとこが大きい。(見ながら書いたりなんてしてないけど、学んだのがこの本なので似てるはず)

ここから買えるので是非見てみてね。アドセンスじゃないよ。


今回、非常に悔しい思いをしたのでいつか似たような記事でより丁寧なものを書こうと思いました。
と、同時に久々にUnity動かしたらこんな程度のことでもやっぱり心が躍りました

おしまい。