『Unityの寺子屋 定番スマホゲーム開発入門』「Chapter04 クッキークリッカーに改造しよう」

Unityの寺子屋 定番スマホゲーム開発入門

Unityの寺子屋 定番スマホゲーム開発入門

序文

お久しぶりです。

少し帰省していました。

その関係で前回の勉強会には参加できなかったので、自習として学習しました。

weeyble-game.connpass.com

進捗

  • Chapter04 クッキークリッカーに改造しよう
    • 4-1 木魚をタップして点数が入るように改造する
    • 4-2 ゲームバランスを調整する

コード実装部分(一部)

180715TheTemple\Assets\Scripts\GameManager.cs

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

using UnityEngine.UI;
using System;

public class GameManager : MonoBehaviour
{

    // 定数定義
    private const int MAX_ORB = 30; //オーブ最大値
    private const int RESPAWN_TIME = 30; //オーブが発生する秒数
                                        //private const float RESPAWN_TIME = 0.1f; //Test用-徳大売り出し
    private const int MAX_LEVEL = 2;    //最大お寺レベル

    //データセーブ用キー
    private const string KEY_SCORE = "SCORE";   //スコア
    private const string KEY_LEVEL = "LEVEL";   //レベル
    private const string KEY_ORB = "ORB";   //オーブ数
    private const string KEY_TIME = "TIME"; //時間

    //オブジェクト参照
    public GameObject orbPrefab;    //オーブプレハブ
    public GameObject smokePrefab;  //煙プレハブ
    public GameObject kusudamaPrefab;   //くす玉プレハブ
    public GameObject canvasGame;   //ゲームキャンパス
    public GameObject textScore;    //スコアテキスト
    public GameObject imageTemple;  //お寺
    public GameObject imageMokugyo; //木魚

    // スコア加算SE
    public AudioClip getScoreSE;
    // レベルアップ時SE
    public AudioClip levelUpSE;
    // ゲームクリア時SE
    public AudioClip clearSE;

    // メンバ変数
    private int score = 0;  //現在のスコア
    private int nextScore = 10; //レベルアップまでに必要なスコア
    private int currentOrb = 0; //現在のオーブ数
    private int templeLevel = 0;    //寺のレベル
    private DateTime lastDateTime;  //前回オーブを生成した時間
    private int[] nextScoreTable = new int[] { 100, 1000, 10000 }; //レベルアップ値

    private AudioSource audioSource;

    // まとめて生成するオーブの数
    private int numOfOrb;

    void Start()
    {
        // 木魚イメージ取得
        imageMokugyo = GameObject.Find("ImageMokugyo");
        PlayerPrefs.DeleteAll();    //Test用PlayerPrefs初期化用
        // オーディオソース取得
        audioSource = this.gameObject.GetComponent<AudioSource>();

        // 初期設定
        score = PlayerPrefs.GetInt(KEY_SCORE, 0);
        templeLevel = PlayerPrefs.GetInt(KEY_LEVEL, 0);

        nextScore = nextScoreTable[templeLevel];
        imageTemple.GetComponent<TempleManager>().SetTemplePicture(templeLevel);
        imageTemple.GetComponent<TempleManager>().SetTempleScale(score, nextScore);
        RefreshScoreText();
    }


    //ゲームデータをセーブ
    void SaveGameData()
    {
        PlayerPrefs.SetInt(KEY_SCORE, score);
        PlayerPrefs.SetInt(KEY_LEVEL, templeLevel);
        PlayerPrefs.SetInt(KEY_ORB, currentOrb);
        // DateTime型をString型に変換して保存する
        PlayerPrefs.SetString(KEY_TIME, lastDateTime.ToBinary().ToString());

        PlayerPrefs.Save();
    }



    // Update is called once per frame
    void Update()
    {
        // まとめて生成するオーブがあれば生成
        while (numOfOrb > 0)
        {
            // 0.1sずつすらしてメソッドを呼び出すことで動きをつける
            Invoke("CreateNewOrb", 0.1f * numOfOrb);
            numOfOrb--;
        }
    }

    void OnApplicationPause(bool pauseStatus)
    {
        if (pauseStatus)
        {
            // アプリがバックグラウンドへ移行
        }
        else
        {
            // バックグラウンドから復帰
            // 時間の復元
            string time = PlayerPrefs.GetString(KEY_TIME, "");
            // 時間がセーブされていない場合は
            if (time == "")
            {
                // セーブ時間を設定する
                lastDateTime = DateTime.UtcNow;
            }
            else
            {
                // セーブ時間を復元する
                long temp = Convert.ToInt64(time);
                lastDateTime = DateTime.FromBinary(temp);
            }

            numOfOrb = 0;
            // 時間によるオーブ自動生成
            // 最終セーブ時間との差分
            TimeSpan timeSpan = DateTime.UtcNow - lastDateTime;

            // 差分がRESPAWN_TIME以上なら時間分のオーブを生み出す
            if (timeSpan >= TimeSpan.FromSeconds(RESPAWN_TIME))
            {
                while (timeSpan > TimeSpan.FromSeconds(RESPAWN_TIME))
                {
                    if (numOfOrb < MAX_ORB)
                    {
                        numOfOrb++;
                    }
                    timeSpan -= TimeSpan.FromSeconds(RESPAWN_TIME);
                }
            }
        }        
    }

    // 新しいオーブの生成
    public void CreateNewOrb()
    {
        // 新しいオーブが生成されるタイミングでセーブ処理を行う
        lastDateTime = DateTime.UtcNow;
        Debug.Log("currentOrb = " + currentOrb);
        Debug.Log("MAX_ORB = " + MAX_ORB);
        if (currentOrb >= MAX_ORB)
        {
            return;
        }
        CreateOrb();
        currentOrb++;

        SaveGameData();
    }

    // オーブ生成
    public void CreateOrb()
    {
        // Debug.Log("CREATE ORB");
        GameObject orb = (GameObject)Instantiate(orbPrefab);
        // orbをCanvasGameの子要素に設定する
        orb.transform.SetParent(canvasGame.transform, false);
        // オーブをランダムに配置
        orb.transform.localPosition = new Vector3(
            UnityEngine.Random.Range(-100.0f, 100.0f),
            UnityEngine.Random.Range(-300.0f, -450.0f),
            0f);

        // オーブの種類を設定
        int kind = UnityEngine.Random.Range(0, templeLevel + 1);
        switch (kind)
        {
            case 0:
                orb.GetComponent<OrbManager>().SetKind(OrbManager.ORB_KIND.BLUE);
                break;
            case 1:
                orb.GetComponent<OrbManager>().SetKind(OrbManager.ORB_KIND.GREEN);
                break;
            case 2:
                orb.GetComponent<OrbManager>().SetKind(OrbManager.ORB_KIND.PURPLE);
                break;
        }

        // オーブを飛ばす
        orb.GetComponent<OrbManager>().FlyOrb();

        audioSource.PlayOneShot(getScoreSE);
        // 木魚アニメ再生
        // 現在のアニメーションステートを取得
        AnimatorStateInfo stateInfo
            = imageMokugyo.GetComponent<Animator>().GetCurrentAnimatorStateInfo(0);
        if(stateInfo.fullPathHash == Animator.StringToHash("Base Layer.get@ImageMokugyo"))
        {
            // すでに再生中なら先頭から
            imageMokugyo.GetComponent<Animator>().Play(stateInfo.fullPathHash, 0, 0.0f);
        }
        else
        {
            imageMokugyo.GetComponent<Animator>().SetTrigger("isGetScore");
        }
    }

    //オーブ入手
    public void GetOrb(int getScore)
    {
        if (score < nextScore)
        {
            score += getScore;

            //レベルアップ値を超えないよう制限
            if (score > nextScore)
            {
                score = nextScore;
            }

            // 寺レベルアップのチェック
            TempleLevelUp();
            // スコア更新
            RefreshScoreText();
            // 次のレベルが近づくに連れてスケールを大きくしていく
            imageTemple.GetComponent<TempleManager>().SetTempleScale(score, nextScore);

            //ゲームクリア判定
            if ((score == nextScore) && (templeLevel == MAX_LEVEL))
            {
                ClearEffect();
            }
        }
        currentOrb--;

        SaveGameData();
    }

    //スコアテキスト更新
    void RefreshScoreText()
    {
        textScore.GetComponent<Text>().text =
            "徳:" + score + " / " + nextScore;

    }
    //寺のレベル管理
    void TempleLevelUp()
    {
        if (score >= nextScore)
        {
            if (templeLevel < MAX_LEVEL)
            {
                templeLevel++;
                score = 0;
                // レベルアップエフェクト
                TempleLevelUpEffect();
                // 次のレベルに必要なスコアをセットする
                nextScore = nextScoreTable[templeLevel];
                // スプライトを変える
                imageTemple.GetComponent<TempleManager>().SetTemplePicture(templeLevel);
            }
        }
    }

    //レベルアップ時の演出
    void TempleLevelUpEffect()
    {
        // 煙オブジェクトをプレハブから生成
        GameObject smoke = (GameObject)Instantiate(smokePrefab);
        // 煙オブジェクトをcanvasGameの子要素に設定
        smoke.transform.SetParent(canvasGame.transform, false);
        // 並び順を調整する
        smoke.transform.SetSiblingIndex(2);
        // 0.5秒後に削除
        Destroy(smoke, 0.5f);

        // レベルアップSEを鳴らす
        audioSource.PlayOneShot(levelUpSE);
    }

    //寺が最後まで育った時の演出
    void ClearEffect()
    {
        // プレハブからくす玉オブジェクトを生成
        GameObject kusudama = (GameObject)Instantiate(kusudamaPrefab);
        kusudama.transform.SetParent(canvasGame.transform, false);

        // SEをならす
        audioSource.PlayOneShot(clearSE);
    }
}

実行結果

www.youtube.com …おもしろいのこれ…?

感想

クッキークリッカーというジャンルらしいが、普段あんまりゲームをやらないからか(昨日からどうぶつの森始めたけど)、おもしろさがよくわからない。
いや、おもしろくないよね、これ…?

まだ、改造前のなぞってオーブを集めるやつのほうがおもしろかったなー。

ちょっと個人的な事情で勉強以上に優先すべきタスクができてしまったので、しばらくブログの更新が滞りがちになると思います。

ユニティ雀!の開発は続行します。

yjkym.hatenablog.com

勉強会への参加はなるべく続けたいと思っていますが、面倒くさいなぁと思っています(笑)。

カロリーメイトください。

BGM

トランキライザー / 小西真奈美

www.youtube.com

『UNITYゲーム プログラミング・バイブル』「ENTRY No.05 3D 迷路」学習記録1

Unityゲーム プログラミング・バイブル

Unityゲーム プログラミング・バイブル

序文

『UNITYゲーム プログラミング・バイブル』12日目。

進捗

(学習時間:???)

コード実装部分

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

public class MazeMake : MonoBehaviour {
    [SerializeField]int mapSize = 5;      // マップサイズ(必ず奇数値)
    int[,] maze;           // マップ生成用(0=壁  1=通路)
    int cnt = 0;          // 迷路の壁を生成した回数

    [SerializeField]
    GameObject wallObject;  // 壁オブジェクトを設定

    [SerializeField]
    GameObject groundObject;// 地面オブジェクトを設定

    [SerializeField]
    GameObject GoalObject;  // ゴールオブジェクトを設定

    void Start () {

        // マップ生成終了条件を算出(マップ内で通路が置ける場所-1)
        // 3×3の場合、2×2-1=3
        // 5×5の場合、3×3-1=8
        // 7×7の場合、4×4-1=15
        int endNum = ((mapSize + 1) / 2) * ((mapSize + 1) / 2) - 1;

        // 迷路マップ
        // +2は外周の壁分
        maze = new int[mapSize+2, mapSize+2];

        while(endNum > cnt)
        {
            //Debug.Log(cnt);
            // ランダムで迷路を掘る場所を決める(必ず偶数になるようにする)
            // 3×3の場合、0,2
            // 5×5の場合、0,2,4
            // 7×7の場合、0,2,4,6
            int x = Random.Range (0, (mapSize + 1) / 2) * 2;
            int y = Random.Range (0, (mapSize + 1) / 2) * 2;


            if (cnt == 0)
            {
                // 外周分を考慮して+1
                maze[x + 1, y + 1] = 1;                     // 初回だけ予め穴を開けておく
            }

            // 外周分を考慮して+1
            // 開始地点が通路ならその地点を起点として穴を堀り始める
            if (maze [x+1, y+1] == 1)
                WallDig (x, y, 0);

        }
            
        // パズル画面の表示
        Output ();

        // ゴールを描画
        Instantiate (GoalObject, new Vector3 (mapSize, 0, mapSize), Quaternion.identity);
    }



    // 迷路生成(穴掘り法)
    // 引数のx,yは外周を考慮していない
    void WallDig(int x, int y, int oldVec){
        // [0]:上
        // [1]:右
        // [2]:下
        // [3]:左
        int[] vx = { 0, 2, 0, -2 };
        int[] vy = { -2, 0, 2, 0 };

        bool retFlg = false;

        // 四方向をランダムで選んで2マス先が掘れるかどうかをチェック
        // r = 0:上
        // r = 1:右
        // r = 2:下
        // r = 3:左
        int r = Random.Range (0, 4);

        // マップの端は掘ることが出来ない
        // y = 0は一番上だから上に掘ることはできない
        if (r == 0 && y <= 0) retFlg = true;
        // 右に掘ったらマップ外に出る時、右に掘ることはできない
        if (r == 1 && (x+2) >= mapSize) retFlg = true;
        // 下に掘ったらマップ外に出る時、下に掘ることはできない
        if (r == 2 && (y+2) >= mapSize) retFlg = true;
        // x == 0は一番左だから左に掘ることはできない
        if (r == 3 && x <= 0) retFlg = true;

        // 問題があるときはもう一度やり直す
        // 失敗したら違う方向
        if (retFlg) {
            WallDig (x, y, oldVec);
            return;
        }

        // 壁を掘れそうなら掘る
        // (+1は外周分を考慮)
        if (maze [x+1 + vx [r], y+1 + vy [r]] == 0) {
            // 右辺の1は通路を示す
            // 次の地点に穴を掘る
            maze[x+1 + vx [r], y+1 + vy [r]] = 1;
            // 経由地点にも穴を掘る
            maze[x+1 + vx [r] / 2, y+1 + vy [r] / 2] = 1;
            // 穴の数を増やす
            cnt++;

            // 再帰ループ
            // 次の地点から穴を掘る
            WallDig (x + vx[r], y + vy[r], r);
        }

        // 堀った先がすでに通路だったときはreturn
    }




    // パズルをモデルを使って出力
    void Output () {
        GameObject obj = new GameObject();    // 親オブジェクトの中に格納する
        obj.name = "Mize";

        for (int x = 0; x < mapSize+2; x++) {
            for (int y = 0; y < mapSize+2; y++) {   
                if (maze [x, y] == 0) {
                    Instantiate (wallObject, new Vector3 (x, 0, y), Quaternion.identity).transform.parent = obj.transform;
                } else {
                    Instantiate (groundObject, new Vector3 (x, 0, y), Quaternion.identity).transform.parent = obj.transform;
                }
            }
        }
    }
}

実行結果

なし

感想

ノベルゲームのソースコードを読み解くことを諦めて、3D編に戻る。

「穴掘り法」と呼ばれるアルゴリズムで迷路を生成していくらしい。

ソースコードの中で外周の壁を考慮する部分と考慮しない部分が混在していて少し混乱したが、一応理解することができたと思う。

でもなんかアナログなアルゴリズムだねぇ。

カロリーメイトください。

BGM

今夜はブギー・バック / TOKYO No.1 SOUL SETHALCALI www.youtube.com

ユニティ雀!~得点計算編~(仮)開発中のお知らせ

「ユニティ雀!~得点計算編~」という麻雀の得点計算が勉強できるアプリを開発しています。
対象は麻雀の基本ルールとアガリ役一覧を覚えた程度の初心者の方を想定しています。

開発中のアプリをWebGLでアップロードしましたので、もしおもしろそうと思っていただけた方は触ってみていただければと思います。

Unity WebGL Player | 180712UnityJong

f:id:yjkym:20180725162247p:plain

麻雀の基本ルールを知らないとなんのこっちゃって感じですが。

開発中ですので普通に未実装部分とかいっぱいあります。
麻雀牌がいまだに1枚も登場していません。
少しずつアップデートしていって、8月いっぱいでコンテンツ部分の開発のおおむね終了を目指しています。
(つまり大した機能は実装されないということですね☆)

カロリーメイトください。

『UNITYゲーム プログラミング・バイブル』「ENTRY No.13 ノベルゲーム」学習記録3

Unityゲーム プログラミング・バイブル

Unityゲーム プログラミング・バイブル

序文

『UNITYゲーム プログラミング・バイブル』11日目?

Unityの勉強会があんまり盛況じゃない(気がする)のは、職業プログラマ的には「なんか違う」感があるんだろうか。とか考えた。
Rails使ってます!ってのとあんまり変わらない気がするけど。

進捗

(学習時間:2時間)

コード実装部分

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// 属性を自作する
// AttributeUsage(AttributeTargets.Method)で
// メソッドにのみ適用できる属性であることを示している
[AttributeUsage(AttributeTargets.Method)]
class SampleAttribute : Attribute
{
}

// 引数ありの属性の作り方1
class Sample2Attribute : Attribute
{
    internal readonly string Name;
    internal Sample2Attribute(string name)
    {
        this.Name = name;
    }
}

// 引数ありの属性の作り方2
class Sample3Attribute : Attribute
{
    public string Name { get; set; }
}

namespace _180724AttributeSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // #ifを使ったデバッグビルドでだけ実行させる書き方(面倒くさい)
#if DEBUG
            ifDebugDump();
#endif

            // 属性を使用したデバッグビルドでだけ実行させる
            attrDebugDump();

            // 属性を使用して非推奨メソッドであることを示す
            oldMethod();

            // プログラムからpublicなメソッドをとりだして
            foreach(var n in typeof(Program).GetMethods())
            {
                // [SampleAttribute]属性をもつメソッドだけを実行する
                // [SampleAttribute]は[Sample]と書いても良い
                // GetCustomAttributes()メソッドの第二引数がtrueだと継承元を検索しに行くらしい
                if (n.GetCustomAttributes(typeof(SampleAttribute), false).Length > 0)
                {
                    n.Invoke(null, null);
                }

                // [Sample2Attribute]属性を取り出して
                foreach (var m in n.GetCustomAttributes(typeof(Sample2Attribute), false))
                {
                    // 属性の引数を表示する
                    Console.WriteLine(((Sample2Attribute)m).Name);
                }

                // [Sample2Attribute]属性を取り出して
                foreach (var m in n.GetCustomAttributes(typeof(Sample3Attribute), false))
                {
                    // 属性の引数を表示する
                    Console.WriteLine(((Sample3Attribute)m).Name);
                }
            }
        }

        // #ifを使ったデバッグビルドでだけ実行させる書き方(面倒くさい)
#if DEBUG
        private static void ifDebugDump()
        {
            Console.WriteLine("This is a if debug version.");
        }
#endif

        // 属性を使用してデバッグビルドでだけ実行させる
        [Conditional("DEBUG")]
        private static void attrDebugDump()
        {
            Console.WriteLine("This is a attribute debug version.");
        }

        // 属性を使用して非推奨メソッドであることを示す
        [Obsolete]
        private static void oldMethod()
        {
            Console.WriteLine("This is a old version.");
        }
        
        // 独自の属性も使える
        [Sample]
        public static void sampleMethod()
        {
            Console.WriteLine("This is a sample.");
        }

        // 引数ありの属性
        [Sample2("Taro")]
        public static void sample2Method()
        {
            Console.WriteLine("This is a sample2.");
        }

        // 引数ありの属性
        [Sample3(Name = "Hanako")]
        public static void sample3Method()
        {
            Console.WriteLine("This is a sample3.");
        }
    }
}

実行結果

f:id:yjkym:20180724154714p:plain

感想

TextMesh Proの概要を学習して、さぁいよいよノベルゲームの実装だ!と思ったのもつかの間、サンプルコード見て壁に激突。

作っていきますとか言って、ほとんど説明してくれてないしさぁ。
とりあえずソースコード読んでいくかと思って、[System.Serializable]をコメントアウトした瞬間、シナリオデータが消去されて心が折れた。笑

そういえば、あんまり属性とかわかってないなぁと思って、今日はC#の属性のふるまいについて勉強していました。
実際ほとんど使ったことないなぁ…。それこそ[System.Serializable]だけちょこっと使った記憶があるけど…。

一応、書き方とか[Obsolete]属性とかは理解できたけど、自作属性をどういう場合に使えば効果的なのかとかはよくわかってない。

カロリーメイトください。

BGM

ある光 / 小沢健二
www.youtube.com これヤバいよね

『Unityの寺子屋 定番スマホゲーム開発入門』「Chapter03 放置ゲームに演出を加えよう」+ 秋葉原Weeyble勉強会 学習記録3

Unityの寺子屋 定番スマホゲーム開発入門

Unityの寺子屋 定番スマホゲーム開発入門

序文

先程終わったばかりの勉強会を残業しながらまとめています。

weeyble-game.connpass.com

進捗

  • Chapter03 放置ゲームに演出を加えよう
    • 3-1 サウンドを追加する
    • 3-2 アニメーションを作成しよう
    • 3-3 アニメーションをスクリプトと連動させる
    • 3-4 DOTweenを使ってアニメーションをつくる
    • 3-5 スコアデータを保存する

コード実装部分(一部)

180715TheTemple\Assets\Scripts\OrbManager.cs

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

public class OrbManager : MonoBehaviour
{

    //オブジェクト参照
    private GameObject gameManager; // ゲームマネージャー
    void Start()
    {
        gameManager = GameObject.Find("GameManager");
    }

    public Sprite[] orbPicture = new Sprite[3]; //オーブの絵
    public enum ORB_KIND
    {   //オーブの種類を定義
        BLUE,
        GREEN,
        PURPLE,
    }

    private ORB_KIND orbKind;   //オーブの種類

    //オーブ取得
    public void TouchOrb()
    {
        if (Input.GetMouseButton(0) == false)
        {
            return;
        }

        // RectTransformコンポーネントを取得
        RectTransform rect = GetComponent<RectTransform>();

        //オーブの軌跡
        Vector3[] path = {
            new Vector3(rect.localPosition.x * 1.5f, 300f, 0f),    //中間点
            new Vector3(0f, 150f, 0f),                         //終点
        };

        //DOTweenを使ったアニメ作成
        // DOLocalPath 
        // 第一引数:軌跡
        // 第二引数:時間
        // 第三引数:パスタイプCatmullRom→曲線軌道 Linear→直線軌道
        rect.DOLocalPath(path, 0.5f, PathType.CatmullRom)
            // イージング(アニメーションの速度の変化具合?)の設定
            .SetEase(Ease.OutQuad)
            // アニメーションが終了したらAddOrbPoint()を実行する
            .OnComplete(AddOrbPoint);

        //同時にサイズ変更
        rect.DOScale(
            // 目標サイズ
            new Vector3(0.5f, 0.5f, 0f),
            // 変化する時間
            0.5f
            );
    }

    //オーブアニメ終了後にポイント加算処理をする
    void AddOrbPoint()
    {
        switch (orbKind)
        {
            case ORB_KIND.BLUE:
                gameManager.GetComponent<GameManager>().GetOrb(1);
                break;
            case ORB_KIND.GREEN:
                gameManager.GetComponent<GameManager>().GetOrb(5);
                break;
            case ORB_KIND.PURPLE:
                gameManager.GetComponent<GameManager>().GetOrb(10);
                break;
        }
        Destroy(this.gameObject);
    }

    // オーブの種類を設定
    public void SetKind(ORB_KIND kind)
    {
        orbKind = kind;

        switch (orbKind)
        {
            case ORB_KIND.BLUE:
                GetComponent<Image>().sprite = orbPicture[0];
                break;
            case ORB_KIND.GREEN:
                GetComponent<Image>().sprite = orbPicture[1];
                break;
            case ORB_KIND.PURPLE:
                GetComponent<Image>().sprite = orbPicture[2];
                break;
        }
    }
}

実行結果

www.youtube.com

感想

さっき終わったばかりです。

DOTweenというアセットを使ってアニメーションを実装するという内容はUnity本でもなかなか画期的なのでは。

また学習する内容が増えるのか…とうんざりしたりはするけど、まぁ今回ぐらいの内容ならそんなに学習コストもいらずに使えそうな感じ

正直、自分のプロジェクトのほうで大分煮詰まっていた(誤用)感じがあるので、いい気分転換になった気がします。
来週は帰省する予定なのでお休みになるんですが。

カロリーメイトください。

BGM

Night Camel feat. FBI / chelmico

www.youtube.com

『UNITYゲーム プログラミング・バイブル』「ENTRY No.12 ノベルゲーム」学習記録2

Unityゲーム プログラミング・バイブル

Unityゲーム プログラミング・バイブル

序文

『UNITYゲーム プログラミング・バイブル』10日目

Unityのオブジェクト管理の方法がよくわからん…
そもそもオブジェクト指向がよくわかってないんだな…

進捗

  • ENTRY No.12 ノベルゲーム
    • テキスト入力時に使えるリッチテキスト機能
    • フォントアセットの作成方法

(学習時間:1.5時間)

コード実装部分

なし

実行結果

f:id:yjkym:20180722154941p:plain

感想

日本語フォントでTextMesh Proを使ってみる。

あらかかじめ使う文字を設定しておかないと使えないのと、フォントアセットの作成に想像以上に時間がかかるのとであんまり使い勝手はよくない感じ。

ただリッチテキストの機能は使いたいんだよなぁ。
なかなか…うーん。

カロリーメイトください。

BGM

アナーキーモーニング / つしまみれ

www.youtube.com

『UNITYゲーム プログラミング・バイブル』「ENTRY No.12 ノベルゲーム」学習記録1

Unityゲーム プログラミング・バイブル

Unityゲーム プログラミング・バイブル

序文

『UNITYゲーム プログラミング・バイブル』9日目

現在制作中のアプリに直接役に立ちそうな内容だったので、No.12を先にやってみることにしました。

進捗

  • ENTRY No.12 ノベルゲーム
    • TextMesh Proの概要
    • TextMesh Proの機能
    • マテリアルのインスペクターの詳細
    • テキスト入力時に使えるリッチテキスト機能

(学習時間:2.5時間)

コード実装部分

なし

実行結果

f:id:yjkym:20180721154602p:plain

感想

こないだの勉強会のLTのネタにもなっていたTextMesh Proについていろいろやってみる。

現在のTextコンポーネントをより高機能にしたもので、近々Unityの標準機能として搭載されるとかなんとか。
まだまだ開発中の機能のようで、参考書の中にも「筆者の環境では再現できませんでした」みたいな記述が頻出するのが味わい深い。

高機能なのはわかったけど、あまりに設定できることが多すぎて使いこなせる気がしないというよくあるパターン。
使いやすそうな設定をいくつかプリセットしといてほしいなー。

フォントファイル(.ttfとか)をそのまま使うことはできないようで、ウェブフォント化するような作業を経ないと使えないっぽいのが一番面倒。
(ここらへんはこれからやる内容)
もうちょっと気楽に使えるようになるといいなー。

カロリーメイトください。

BGM

アナーキーモーニング / つしまみれ

www.youtube.com

露骨にMVに制作費かけれなくなってるのが見えて(略)。