Sprite.Createのパフォーマンスについて

概要

重たい!
どれくらい重たいのか、計測した。

docs.unity3d.com

なかなか時間がかかった。

環境

Unity 2018.2.0f2
MacOS
Intel Corei5 2GHz
8GB メモリ
1つのTextureで最大100KB

時間の計測

1回実行するごとに、0.09秒

System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

    foreach(var name in nameList){
        var imageData = illustDataIO.LoadIllustTexture2D(name);

        sw.Start();
        var createSprite = Sprite.Create(imageData, new Rect(0,0,imageData.width,imageData.height), new Vector2(0.5f, 0.5f));
        sw.Stop();
                                
        createSprite.name = name;
        spriteList.Add(createSprite);
    }
Debug.Log("画像生成" + nameList.Count);
Debug.Log("time" + sw.ElapsedMilliseconds / nameList.Count);

100枚作ったら、9秒かかる計算

対処した方法

  • ゲーム起動時に必要となる画像を全て生成しておく
  • シングルトンオブジェクトにて、生成した画像をキャッシュ
  • ゲーム進行中に画像が追加されたら、シングルトンにも追加

ビンゴゲーム終了時間シュミレーターを作りました

概要

参加人数や景品数を元に、ビンゴゲームの想定終了時間を算出するツールを作りました。
会社の飲み会や結婚式二次会の幹事の方、よかったら使ってください。

https://coffee-ryo.devel.jp/app_Bingo.html

作った背景

会社の忘年会の幹事頼まれてやったことがあって、タイムスケジュール組むんですが
毎回ビンゴゲームの所要時間がわからなくて困ってたんですね。

確率が絡んでくる以上、予想する時間に幅を持たせなきゃいけないけど、 どれくらい幅を持たせなきゃいけないかとか、よくわからなかったんです。

この時間の見積もりがサクッとできたら便利だなーと思ってて、ついに作ってしまいました。

実力で完全敗北した1週間ゲームジャムに参加した話

f:id:coffee_ryo:20180910151332p:plain

概要

「ゲーム作って遊んでもらってちやほやされたいんや!」

その気持ちで、TOP10%入りを目指し奮闘しました。

今回は以下条件で参加しました。

  • 2人で開発(相手はunity未経験、ゲーム開発未経験)
  • 筆者フル稼働(仕事をやめてた関係で、毎日14時間、total90時間くらい思考&作業できた)

時間もリソースも十分にある中で、TOP10%からは程遠く、もう完全に実力で敗北したと宣言して良いでしょう。
上位に行けなかったと思われる理由を書き残しておきます。

どのゲームジャムに参加したかはこちら⬇︎

何を作ったかはこちら(音量小さめ)⬇︎

Burning Snowman | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう

ステージクリア型の2Dアクションゲームです。

よくなかったところ

難しすぎた!

ゲーム公開から1時間、プレイいただいた方のプレイ履歴を追っていたところ
全員クリアまでたどり着けず、1〜8分の内に離脱 / ゲームオーバー回数1~9回 の内に離脱していました。
またステージの1/5しか進んでいませんでした。

初回プレイでクリアできて達成感を感じるくらいの難易度調整であれば、結果は変わっていたかもしれません。

開発者がずっとプレイしていると、感覚がおかしくなって調整が難しいので、
次回はリアルな場で人に見せて、フィードバックをいただくようにしてみます。例えば、#WeeybleGame さんとか!

weeyble-game.connpass.com

クリアまでの時間が長すぎた!

1個のステージで、1プレイ2分、当初想定していたゲームオーバー数と合わせると6分くらいでクリアを考えていましたが、良評価もらっているゲームと比べると必要プレイ時間が長かったと思いました。

先述の離脱データを見る限り、1分で離脱されてしまう人がいるので
unityroomのゲームジャムで高ランクを狙うには
1分以内に満足いただけるゲームが要求されるみたいです。
最後までプレイすれば面白いゲームでも、最後までプレイしてもらわないと意味がない。。。

ステージクリア型であれば、20秒くらいで終わるステージを複数用意して、
1プレイの時間を短くするアイデアもあったかなと思います。

ビジュアル力が足りなかった!

f:id:coffee_ryo:20180910132527p:plain

閲覧数、上位陣に比べると低かったです。

ツイッターで告知する際の拡散力も全然違うし、絵が可愛かったらSNSシェアしても恥ずかしくないし、
unityroom内のサムネなんかでも判断されてしまうのかなと感じました。

筆者、ドット絵とか2Dイラストとか練習しているんですけど、どうしても絵を書くのが苦痛に感じてしまいしんどいポイントではあります。
苦痛に耐えるしかないかもしれない。。

絵は描けるけどスクリプティングができない方、次回開催時に
もし興味があれば声かけていただけるととっても嬉しいです。

まとめ

なんとか完成までこぎつけられたので、よかったです! 学ぶことも多かった。  

何回か参加しているとだんだん理想が高くなっていくのですが、実力が伴っていませんでした。
非常に残念な結果に終わってしまいましたが、TOP1を取るまで走り続けようと思います。

その他

期間中の動きについて

どのような流れで企画が立ったか、実装面で工夫したところなど。
あと、プレイヤーの行動ログどうやって見たかとかの話など。詳しくはこちら⬇︎

coffee-ryo.hatenablog.com

事前準備

1週間始まる前に準備色々してました。詳しくはこちら⬇︎

coffee-ryo.hatenablog.com

「Burning Snowman」が出来上がるまでの話

概要

雪だるまが松明を持って、町を温めて行くゲームを作りました。

Burning Snowman | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう

あらゆるアクションでヒットポイントが減っていくハードなアクションです。 アクションごとにヒットポイントの減る量は異なります。減りの少なさに応じて、プレイヤーのスキルを要求します。

完全敗北した話も書いてます。よかったらこちらもどうぞ。

coffee-ryo.hatenablog.com

こちらの記事では、
どのような流れで企画が立ったか、実装面で工夫したところなどつらつら書き残しておきます。

  • 2人で開発(相手はunity未経験、ゲーム開発未経験)
  • 筆者フル稼働(毎日14時間、total90時間くらい思考&作業)

企画の話

メンバー全員でブレストして、両者いいねってなったものを採用することにしました。

頭の中にあるものを吐き出す
  • ガリガリ君ガリガリ君を食べて暑い中長生きする
  • 太陽で人を焼き払うゲーム
  • 焼き殺す対応vs夜を待つ人間のオンライン対戦

構図があまりわかなかったのでNG
オンライン対戦は、条件が同じプレイヤー出ないと、調整がしんどそう

みんながわかるもの
  • 焼き土下座のクリッカーゲーム
    • 暑さを我慢するこで、謝罪をするクリッカーゲーム
    • 耐えられなかったら再び謝罪

シンプルすぎるからもう少し駆け引き的なものが欲しいとのことでぼつ

ジレンマが起きる構造を元に思考
  • 洞窟の奥底を目指して、コウモリを火炎放射器で焼き進んでいき深さを競うゲーム
    • 火炎放射器を使っていけばコウモリを倒して道を安全に進めるが、使えば使うほど二酸化炭素が増えて死に至る

暑さでダメージを受けるキャラがいいということで思考した結果、ゆきだるまに行き着く。

  • 雪だるまが松明を使って、世界を救うアクションorアドベンチャー
    • 松明を使えば、世界を暖かくできるが暑さで自身も溶けていく
    • あついがテーマなのに、まさか雪国をステージにする開発者いないよねってなって、目立つかなって思った
    • キャラ設定とジレンマによって平和と悲しさがある壮大なストーリーを成立させることもできそうだ

プログラムの話

実装方針
  • クラス名は、何をするかを明確にしておき、クラス名に関係のない(または薄い)挙動は別のクラスで実装
  • uGUI周りはMV(R)Pのアーキテクチャで実装(UniRx使って)
  • コンポーネントやゲームオブジェクトの参照について、インスペクターからさせないように努力(今回は使い方覚える体でZenject)
  • インスペクターから変更できるパラメタはHeaderアトリビュート必須 パラメタ調整を分業できるように

設計については、過去2Dプラットフォームのゲームを作ってた経験から
なんとなくこういう作りにしておけばいいかなっていう当たりがついていたので、特にクラス図などは書きませんでした。
クラス名明確に付けることを心がけたことで、「あの挙動変更したいな〜」とか思ったらどこ変えればいいかの当たりがすぐに付きました。

例えば、プレイヤーオブジェクトについているコンポーネントはこんな感じです。

f:id:coffee_ryo:20180910142524p:plain

移動の挙動を調整したかったらPlayerMoverを見るとか。

Zenjectは使う必要が薄いというか、参照だけ取るのが目的だったら別の実装でよかったかな、と思ってます。
システムを動かす前提条件を差し替えるのを容易にしてくれるのを手伝ってくれる認識ですが、interfaceとか全く切らずに作ってたので全く使いこなせてなかったです。

最適化

そこそこできた段階でそんなに問題になっていなかったのですが、PCのスペックとかで重たかったりするのかなーとか考え、
少しでも快適に遊んでもらえるようにちょっとだけ入れました。
ほとんど効果ないかも。。

  • 文字列連結周り(FastStringで対応)
  • 敵の攻撃弾オブジェクトの使い回し(UniRxのPoolingで対応)
  • コルーチンのyield returnで毎回newしているのは消してGCAllocate抑える

UIの話

ボタンとツールチップ

Modern UI PackというAssetが見栄えの点で優秀だったので使用させていただきました。
ボタンやツールチップはほぼこちらのアセットです。
(エディタ拡張とかがうまく動かなく動作が怪しいので、商用とかには使いにくいかも)

assetstore.unity.com

操作できるUIは、マウスオーバーしたらSEを鳴らすようにしています。
操作できますよ、という合図がわりです。

選択肢が複数ある場面では、ボタンに必ずツールチップを付けるようにしました。
説明の補足っていう意味合いもあるんですが、実のところは「今どの項目を選択しているか」をプレイヤーに理解いただくのが目的です。

ツールチップなし⬇︎

f:id:coffee_ryo:20180910143658p:plain

ツールチップあり⬇︎

f:id:coffee_ryo:20180910143219p:plain

背景ぼかし(Brur)について

モーダルウィンドウを出した際ウィンドウを集中して見て欲しかったので背景をぼかすようにしました。

f:id:coffee_ryo:20180910170839g:plain

ヒットポイントゲージについて

ピンチ感を煽るのが狙いで、被弾していくごとに青色→赤色にアニメーションするようにしました。
それから、ステージの設計と相談して配置位置は画面下にしてます。

イラストとアニメーションの話

メンバー全員が描けない。。。ってなって、いらすとやさんのものを使わせていただきました。
今回はこちらの面で失敗しました。
SNSでの拡散やサムネのことを考えると、ここは力を抜いてはいけない箇所でした。

サウンドの話

「自作するのもいいけど、他に優先することあるよね」ってなって後回しになった結果
各素材サイトの作品を使わせていただくことになりました。

それぞれのSceneではサウンドを変更し、場面転換したことを音で伝えるようにしておきました。

その他

空気感の表現

f:id:coffee_ryo:20180910165623p:plain

背景の山は遠くにあるものと近くにあるものでスクロール量に変化を持たせ、空気感を与えました。
また、雪のパーティクルについては遠くにあるものほど落下速度をゆっくりに・色を白に近づけるようにしました。

松明で明るくなっているプレイヤーを表現するため、ポストプロセスを使って画面四隅を暗くしてます。

会話シーンスキップ

周回プレイをして高スコア目指す人いるよねって話になって、会話スキップ入れたいという話が出ました。

当初ボタンで済ませようかな〜って考えていたのですが、物語見たかったけどマウスクリックしてたら勝手にスキップしたとか、 じゃあ確認画面挟むかってなっても操作1回増えるので、ボタン以外の方法で実装しました。

f:id:coffee_ryo:20180910144849g:plain

会話シーンでマウスやスペースキー長押しでスキップできるようにしてます。
オクトパストラベラー(と、あとツイッターで誰かがつぶやいていたゲームがあったけど、、思い出せない) で使われている手法です。
物語楽しみたい人、スキップしたい人の折衷が取れていると思います。これ考えた人ほんとに天才。。。

ゴール地点を見せる

ここまでたどり着けばゴールですよ、というのを伝えるのを狙いとして取り入れました。
これがないと、どこまで進めば良いのかわからない。

Hollow Knightのようにあえてゴールを見せずに、不安感を煽るアイデアもあるので、全てに当てはまるわけじゃない。

伝える情報を順に出す

聖徳太子のような、複数の情報をまとめて出して回答させるの、疲れちゃうかなと思ったので
できるところは小分けにして順々に伝えるのを意識しました。
例えばクリア画面の例⬇︎

f:id:coffee_ryo:20180910150331g:plain

「遊び方わからない→離脱」を避ける

キーボードどれ押せばいいの?とかが分からずに、過ぎ去られるのが嫌だったので色々気を使いました。

起動直後の初めの会話シーンは、マウスクリックのアイコンで進めますよアピールをしてます。

f:id:coffee_ryo:20180910151225p:plain

初めの会話シーンが終わった後のタイトル画面では、日本語で何を押せば進めるのか記載しておきました。
これについては、青木ととさんが作られたゲームのUIを参考にしてます。

f:id:coffee_ryo:20180910151332p:plain

青木ととさんのゲームはこちら⬇︎

Out of a Hole | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう

それから、チュートリアルステージを用意しました。
理想を入れば、こんなテキストで説明したくない(テキスト読んで理解するのめんどくさい、マリオのように理解させたい)のですが、
キーボード使う関係上入れておきました。
こちらでも、伝える情報を順に出すのを意識してます。

f:id:coffee_ryo:20180910152145p:plain

チュートリアルステージでは敵がすでに配置されているのですが、
「このオブジェクトは敵だ」というのを認識してもらうためにわざと被弾する配置に組んでます。

f:id:coffee_ryo:20180910152225p:plain

初見では多分敵と認識できてないはず、なので被弾します。

f:id:coffee_ryo:20180910152748p:plain

被弾を繰り返すと、下部のゲージが減少し警告色になるようにしてます。この流れで、「こいつと接触したらいけないんだな、ジャンプで回避しよう」という思考に持っていき、ジャンプの使い道を覚えてもらいます。

少し進んだ先では、ジャンプでは乗り越えられない敵を用意しておいてます。

f:id:coffee_ryo:20180910153436p:plain

こういう場面では、松明を使うことで敵を溶かし先に進めることを覚えてもらいます。

各アクションにはダメージ差があるのですが、遊び方の説明で多分きちんとできてなかったです。
ジャンプでは低ダメージ、松明では中ダメージ、それから敵の被弾は大ダメージで組んでます。
ジャンプはhigh risk / low use、松明はlow risk/ high use の関係としてアクションの取捨をしてもらいたかったのですが。。
申し訳程度に、HPが減った時にキャラの上部にダメージ数をテキスト表示させ、ダメージ数に比例してテキストを大きくするのを入れておきました。

ポーズの一工夫

再開するボタンを押したらすぐ始まるのではなく、一呼吸置いてから始まるようにしています。
画面の状態をinputしてもらって、次のアクションどうしようかなっていうのを考える余地を与えるのが狙いです。
これは音ゲーとかのポーズを参考にしました。

f:id:coffee_ryo:20180910154751g:plain

※ポーズしても敵が動くというバグがあったため、あんまり役に立たなかったかも。。

離脱ポイントの記録

テラセネを作られている方のブログを見て、ファネル分析たる存在を知り良さそうだったので、1weekでも取り入れてみました。

テラセネって何?って方はこちら⬇︎

shakeflower.hatenablog.com

今回作ったゲームでは、いくつかチェックポイントが用意されてます。
チェックポイントを通過したらサーバーにリクエストをかけ、プレイヤーの行動ログを記録しておきます。

  • 特定シーン読み込み時
  • 特定オブジェクト接触

記録しているデータはこんな感じです。

f:id:coffee_ryo:20180910160615p:plain

これを見ることで、「〇〇さん、STAGE_Cのログを最後に記録がない。。つまらなくて途中で去ったんだな。。」とかが分かるようになってます。

用途については、
今回のゲームジャムの振り返りをするのに使ったり、月曜日から遊んでくださる方向けにゲームバランス調整するのに使いました。
日曜は疲れてて月曜から遊ぶ!っていう人いるって信じてる。

ニフクラさんのサービスが優秀で、実装も楽でした。(今見たらenum.tostring使っちまってる。。)

public void RequestRecording (EnumCheckPoint checkPoint){
    #if UNITY_EDITOR
        Debug.Log("チェックポイント" + checkPoint.ToString() + "を記録しました。");
        return;
    #endif
    try{
        // クラスのNCMBObjectを作成
        NCMBObject historyClass = new NCMBObject("HistoryData");
        
        // オブジェクトに値を設定
        historyClass["playerId"]   = playerId;
        historyClass["checkPoint"] = checkPoint.ToString();
        
        // データストアへの登録
        historyClass.SaveAsync();
        
    }catch(System.Exception ex){
        Debug.LogError(ex.Message);
    }
}

次回も使えるまとめ

  • 企画については対立がおきたりジレンマが発生する構造から先に考えるとうまくいく
  • 会話場面でのスキップ機能は長押しでやるのが良案
  • プレイヤー行動ログを残しておくと色々と次に活かしやすい

1週間ゲームジャムの準備をした

事前にインストール
  • 「Camera Play」 主に画面揺らすのに使う
  • 「DoTween」 簡単なアニメーションするのに使う
  • 「Hyperbit Arsenal」 爆発演出に使う
  • 「G2U」 データ管理に使うかも
  • 「Fungus」 ちょっとしたストーリーを挟むのに最適
  • 「UniRx」 「〇〇したら(になったら)◾️◾️する」処理をうまく書ける(理解してない)
  • 「Zenject」 クラス間の依存関係をスッキリさせるのに利用する(理解してない)
  • 「Photon Unity Networking Free」 リアルタイム通信のゲーム用ライブラリ
  • 「Modern UI Pack」 おしゃれなUIのPrefabが詰まってる
  • 「TextMesh Pro」リッチなテキストを作る
  • 「Post-processing」画面全体に適用するお手軽演出
  • 「UnityRoomTweet」unityroom専用のツイート投稿
  • 「unity-simple-ranking」オンラインランキング機能
  • 「ShaderSketches」シェーダーお絵描き 特別な演出を入れる時使う
  • 「SimpleAnimation」アニメーションスクリプト
  • 「PhotonRx」PhotonをRxで記述しやすくする
  • 「TypefaceAnimator」少しの操作でテキストアニメーションをつける

APIキー等の設定も事前に終わらせておいた。 使用経験ない「Modern UI Pack」「PhotonRx」「TypefaceAnimator」、
使い方がちょっと変わった「Post-processing」は前日に素振り。

事前にスクリプティング

どのゲームにも必要になりそうなものだけを準備(音の再生とか)

事前に素材用意
事前にビルド
  • 色々アセット入れていると、エディターだと動くがWebGLだと死んでるケースがある
  • ので、上記のAssetを入れてWebGLビルド通るか確認
  • 通らない場合はバージョンダウンや、エラーを吐くアセットの除外を検討
チーム開発準備

今回初の複数人参加です。

  • タスク管理 Trello準備 作業の受け渡しを考えてリスト作っておいた
  • メンバーの担当範囲をどうするか考えておく

ArialフォントのTextコンポーネントを洗い出すエディタ拡張【Unity】

f:id:coffee_ryo:20180830201509p:plain

概要

WebGLで、デフォルトフォントのArialを使うと日本語が出ない問題がある

qiita.com

Arialを使っているTextコンポーネントの洗い出しがめんどくさい
特に開発終盤になりオブジェクトが増えてきた時。

洗い出しをするエディタ拡張を作る。

動作確認したUnityバージョン

version 2018.2.0f2

使い方

  1. 後述のソースコードを、「Assets/Editor」フォルダに入れる。「Editor」フォルダがない場合はフォルダを作成する。

  2. メニュー上部の「Tools/Arialフォント洗い出し」をクリック

f:id:coffee_ryo:20180830201646p:plain

ソース

FinderTextHavingArialFont.cs

using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

namespace CoffeeR{

    /// <summary>
    /// Arialフォントを洗い出すエディタ拡張クラス
    /// </summary>
    public class FinderTextHavingArialFont{

        [MenuItem("Tools/Arialフォント洗い出し")]
        static void Exec (){

            // シーン中にある、テキストコンポーネント全て取得
            var textComponents = UnityEngine.Resources.FindObjectsOfTypeAll( typeof( Text ) ) as Text[];

            // Arialな奴の取得
            var textsWithArial = textComponents
                            .Where(obj => obj.font.name == "Arial");

            // 該当しなければダイアログを出して終了
            if(textsWithArial.Count() == 0){
                FinderTextHavingArialFont.DrawDialog("問題なし","編集中シーンでフォントがArialのTextはありませんでした。");
                return;
            }

            // 該当するものがあれば、パスを記録していく。
            var explain = "編集中シーンでフォントがArialのTextが見つかりました。";
            foreach(var text in textsWithArial){
                explain += "\n" + GetHierarchyPath(text.gameObject.transform);
            }

            // 該当するオブジェクトのパスをダイアログに表示
            FinderTextHavingArialFont.DrawDialog("問題あり", explain);

            return;
        }

        static void DrawDialog(string title, string text){
            EditorUtility.DisplayDialog(
                    title,
                    text,
                    "OK"
            );
        }

        static string GetHierarchyPath(Transform self){
            string path = self.gameObject.name;
            Transform parent = self.parent;
            while (parent != null){
                path = parent.name + "/" + path;
                parent = parent.parent;
            }
            return path;
        }
    }
}

備考

tsubakit1.hateblo.jp

  • Unity1週間ゲームジャム向けに作りました。開発終盤なんかのチェックに使うのが良いかと思います。

System.Void Zenject.InjectAttribute::.ctor()' that does not exist・・・みたいなエラーが出てWebGLビルドに失敗した話【Unity Zenject】

概要

こんなエラーが出てなんだかビルドに失敗

attempting to call method 'System.Void Zenject.InjectAttribute::.ctor()' that does not exist

UnityとかZenjectのバージョンは忘れた(大事な所なのに!!)

対応方法

ググるとZenjectでバグが起きていた模様。 Zenjectを最新版に更新した。

github.com