実力で完全敗北した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

1週間ゲームジャム参加の心得メモ

概要

1週間ゲームジャムの開催が近づいてきた。 開発中迷わないようにするため、これまで参加してきた時に得られた気づきを備忘録としてまとめておく。

参加する上での目標設定

  • 順位でTOP6入りを目指す
  • 新しい表現方法を習得する
  • これまで作ったことのないジャンルの設計を習得する
  • 特殊なゲームギミックを実験(面白いか、面白くないのか)
  • とにかく完成させて友達や他開発者とわいわい

どのように&どんなゲームを作るかが変わってくる。
「いろんな人に見てもらえる機会」を使って実現できるものを設定すると良さみ。
自分の場合大抵、TOP6入り&友達とわいわいするのを目標。

企画は初日に決める

初日に決めておくと、残りの6日間を実装に回せる。
頭の中にあるイメージをラクガキで残しておく。
テキストだけだとイメージするのが難しい。

土曜に企画立てて作った利用規約ゲームもあるが、全然作り込めなかった。

土曜日にはプレイ可能なBuildを1つ用意する

日曜日は予備日と思っておく。 だいたい遅延するのでやっぱり日曜日使っちゃうけど。

作る内容に優先順位をつける

  • キャラクターを動かす→必須
  • SNSシェア機能→余裕があれば

みたいなリストを作っておく。時間的に厳しい場面がどうしても出てくるから。
作らない勇気、大事。

機能追加かバランス調整か迷ったらバランス調整を優先する

新しいルールや機能は、ミステリー小説の登場人物を1人増やすようなもの。
開発後半で悩んだらバランス調整優先。

終わった後にブログを書く

事故った経験を忘れないようにしておく。 ライブラリ使おうとしたら〇〇でハマった、とか。 他の人の役に立ったり、1年後の自分が同じことでハマった時に役立つ。

積極的に告知しておく

Unityroom外からの流入を作る。主にTwitter。 頑張って作ったけど、プレイしてもらえたいのは結構悲しいから。 TOP6入りしている人はTwitterで話題になっているのが多い印象。

進捗報告用に動画編集できるようにしておく

Twitterで告知する際はスクショだけだとゲームの面白さが伝わらない。 ので、gifや動画を投稿することを推奨。

他開発者のモチベーションをあげる

面白そうだなと思ったゲームには、Twitter上でいいねをつけておく。
Twitterのいいねは、ゲームジャムにおいて大変良いモチベーションになる。
なんかハマっている時は、自分ができる範囲で協力する。
通信対戦のデバッグとか結構しんどい。

新しい技術はあまり入れない(人によりけり)

入れすぎるとゲーム開発の進捗が上がらない。

こだわらない部分を決める

  • UIのボタン→デフォルトのままで良い
  • 設計→神クラスになっても良い
  • 音楽→自作したい気持ちを抑えて素材サイト様のものを使わせていただく

夢のない話かもしれないが、やらない勇気が完成を導く。

少ないプレイ時間で満足できるようにする

200作品近く投稿される。
プレイ時間が長いとクリア前に他ゲームに遷移してしまう。

操作内容を理解させる

ゲームのファーストビューで見せたり、1プレイ目で操作を説明するとか。
操作がわからなくて離脱してしまうことがしばしばある。

後、開発者ではない身近な友人にプレイしてもらうと良い。 プレイ中の視線の動きやコントローラー/キーボード/マウスの動かし方を見ていれば、理解できているかどうかがわかる。
※開発者はゲームプレイがうますぎるので、難しい操作でも難なくプレイできてしまう。

必要なライブラリは事前にプロジェクトに入れておく

どうせやる作業は事前にやっておく。 自分の場合はUniRx,Zenject,DoTween, Fungus, TextMeshPro

よく使う素材サイトのurlをメモっておく

上記に同じ。ライセンスに気をつける。
あまり注目されないから問題ないだろう。。。とか思っていると、ツイッターでバズった時に死ぬ。

週半ばでWebGLビルドして動作確認する

WebGLのバグ対策。実はこのアセット死んでるとかある。

UnityRoomさんのゲームジャム説明ページをよく見ておく

Unityのバージョンについての注意事項とか書いてある。
後、開発する上でのお役立ち情報とかキャンペーン情報が掲載されていることも。

クソゲーだった時作り直すかどうかの判断

結構難しい問題だと思う。

  • 変えずに完成させる→モチベーションがしんどい。
  • 切り替えて完成させる→スケジュールがしんどい。

自分の場合は開始2日目まで内容の大幅変更を許容。
それ以降は作りこみが間に合わなそうなのでクソゲーだったとしても作りきる。
実は他の人が遊んだら新しい遊び方が発見されて神ゲーになる可能性もある。

クソゲーを受け入れられる人間になる

半年後にリベンジとして、より面白いものを作れば良い。焦らない!

(社会人)有給を1ヶ月前に申請しておく

残業は作業時間だけでなく開発モチベーションを削る。
社会人&マジでやる人は社内の根回しから行わなければならない。

スーパーアーマーの実装メモ【Unity】

f:id:coffee_ryo:20180730165929g:plain

仕様
  • 対象はキャラクター
  • オブジェクト衝突時にダメージを受ける
  • 一度ダメージを受けるとしばらくスーパーアーマー状態
  • スーパーアーマー中はダメージを受けない
  • 時間の経過でスーパーアーマーは解除される
  • スーパーアーマー中キャラを点滅
Unityバージョン

2018.1.0f2 Personal ※UniRx使う

クラス図

f:id:coffee_ryo:20180730152655p:plain

  • ReactivePropertyを使って、スーパーアーマーである時間を設定
  • ダメージを与えるコンポーネントで、スーパーアーマー残り時間が0よりも大きければ攻撃しない
  • スーパーアーマー中は、rendererコンポーネントのenableの値を1フレーム置きに更新する
ダメージ処理のフローチャート

f:id:coffee_ryo:20180730165354p:plain

コード

IStatus ステータスのinterface ※ここinterfaceにしておけば、ダメージ処理が簡潔に書けると思ってる

using UniRx;

public interface IStatus{
    ReactiveProperty<float> TimeSuperArmor{get; set;}
}

PlayerStatus ステータスの実装 キャラクターオブジェクトにアタッチする

using UnityEngine;
using UniRx;

public class PlayerStatus : MonoBehaviour , IStatus{

    public ReactiveProperty<float> TimeSuperArmor{get; set;}

    void Awake (){
        TimeSuperArmor = new ReactiveProperty<float>();
    }
}

SuperArmorTimeUpdater 経過時間をinputとして、スーパーアーマーの残り時間を減らしていく キャラクターオブジェクトにアタッチする

using UnityEngine;
using UniRx;
using UniRx.Triggers;

public class SuperArmorTimeUpdater : MonoBehaviour {
    IStatus status;

    void Start () {
        status = this.GetComponent<IStatus>();

        this.UpdateAsObservable()
        .Where(x => status.TimeSuperArmor.Value > 0)
        .Subscribe(x => {
            status.TimeSuperArmor.Value -= Time.deltaTime;
        });
    }
}

SuperArmorBlinker スーパーアーマー中にレンダラー(キャラクター画像に当たる)を点滅させる キャラクターオブジェクトにアタッチする

using UnityEngine;
using UniRx;
using UniRx.Triggers;

public class SuperArmorBlinker : MonoBehaviour {
    IStatus status;
    SpriteRenderer renderer;

    void Start () {
        status = this.GetComponent<IStatus>();
        renderer = this.GetComponent<SpriteRenderer>();

        this.UpdateAsObservable()
        .Where(x => status.TimeSuperArmor.Value > 0)
        .Subscribe(x => {
            renderer.enabled = this.BoolSwitch(renderer.enabled);
        });
        
        // スーパーアーマーが終わったら、レンダラーを必ず有効にしてあげる
        status.TimeSuperArmor
        .Where(time => time <= 0)
        .Subscribe(time => {
            renderer.enabled = true;
        });
    }

    bool BoolSwitch(bool flg){
        if(flg == true){
            return false;
        }else{
            return true;
        }
    }
}

Damager ダメージを与えるコンポーネント なんらかの攻撃を行うオブジェクトにアタッチする

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

public class Damager : MonoBehaviour {

    void OnTriggerEnter2D(Collider2D collider) {

        // ステータスコンポーネントを取得
        var status = collider.gameObject.GetComponent<IStatus>();

        // コンポーネントがなければ、ダメージを与えられないオブジェクトとして処理終わり
        if(status == null){
            Debug.Log("ステータスコンポーネントを持っていないオブジェクトと衝突");
            return;
        }
        
        // スーパーアーマー中であればダメージを与えない
        if(status.TimeSuperArmor.Value > 0){
            Debug.Log("スーパーアーマー中");
            return;
        }

        // ここにダメージ処理記述
        Debug.Log("ダメージを与えた!!");

        // スーパーアーマー状態にする(動作確認用に2.0fにしてます)
        status.TimeSuperArmor.Value  = 2.0f;
    }
}
完成したもの

f:id:coffee_ryo:20180730165929g:plain

未検証事項
  • rendererのenableを切り替えて点滅させるのではなく、シェーダーを使って点滅させた方が実行速度でる?