imog

主にUnityとかの事を書いています

昔作ったゲームを公開した

外付けHDを漁っていたら、昔に作ったゲームが発掘されたので、せっかくだし公開しました。

https://otoshiai.e-f-b.jp/

東方落試合という4人対戦型アクションゲームです。 弾幕とかでワイワイしながら相手を倒しましょう。

2~4人用なので1人では遊べないのと、2010年製なのでそもそも動くかわかりません。

手元のWindows10だと動きました。.NETすごい。

テンション高い学生時代に作ったゲームで色々恥ずかしいところあるけど、多分作ってて一番楽しかったし知らない人から面白かったですと直接感想もらえたりと思い出深いやつなので永久保存しとこうと思います。

リファクタリングは、コードを5秒見たあたりで諦めました

出稼ぎダンジョン進捗報告 2

割と時間開けてしまった。

今回は見た目の変更はほぼなし。その代わり、急ピッチで作った部分を色々とリファクタリングなどしていた。

Zenjectの導入

戦闘システム周りなど、敵と味方が相互に依存しあったりして割とつらいコードになっていた。そのあたりのリソースの参照を一か所に集中させるためにシーンごとにDataStore的なクラスを用意してそこから参照するようにしていたが、毎度用意するのがやや面倒だった。

また、Storeがシングルトンという役割を持つので、シングルトンにしたいPureClassなどもここに追いやられていた。

namespace Game.Store
{
    /// <summary>
    /// Battleシーンのデータを全て保持しているやつ
    /// </summary>
    public class BattleStore
    {
        public BattlePartyView Party { get; private set; }
        public BattleEnemyView Enemy { get; private set; }
        public ActionResult InBattleResult { get; set; }
        public ActionDisplayView ActionDisplay { get; private set; }
        public View.MoneyView MoneyDisplay { get; private set; }
        public Model.Money Money { get; private set; }
        public void Initialize(){
            // それぞれfindしたりなど
            // PureClassはここで生成したりなど
            Money = new Money();
        }
    }

    public class Hoge {
        private BattleStore store;
        private Model.Money money;
        void Start(){
            var party = store.Party;
            money = store.Money;
            // ほげほげ
        }
    }
}

こういうのを用意して、全部Store経由で取ってたりした。 流石に将来的にしんどくなる気がしたので、ここにZenjectを導入して、データは極力直接injectする方針に変更した

github.com

namespace Game.DI
{
    public class BattleInstaller : MonoInstaller<BattleInstaller>
    {
        public override void InstallBindings()
        {            
            Container.Bind<Model.Money>().FromNew().AsSingle();
        }
    }
}

public class Hoge {
    [Inject]
    private BattlePartyView party;
    [Inject]
    private Model.Money money;
    void Start(){
        // ほげほげ
    }
}
  • Storeが不要になった
  • PureClassの生成はInstallerに移行できたので置き場所がわかりやすくなった

コード量がだいぶ減って見やすくなったのだった。 まだ使いこなせたとは言い難いけど、現状でも十分便利。

BehaviorDesignerを導入

敵キャラの行動パターンは、完全なランダムで技を繰り出すだけだったので細かい思考を調整できるようにしたかった。

なので、昔に買ったけど放置していたBehaviorDesignerを導入することにした。

https://www.assetstore.unity3d.com/jp/#!/content/15277

BehaviorTreeを作ること自体はじめてだったけど、その辺はググりながらでサッと解決した。

上記のTreeは以下の思考パターンになってる

  • HPが30%より大きかったら、「たいあたり」「れんぞくたいあたり」のどちらかを繰り出す
  • HPが30%以下だったら、「すごいいちげき」「あばれる」のどちらかを繰り出す

いわゆる、ピンチになると発狂する挙動だけど、これくらいなら数分で書けるので非常に便利。そして楽しい

自前の戦闘システムに組み込むため、攻撃を行うActionとHPをチェックするConditionalは自分で実装した。 この辺りは仕組みが簡単なので割と量産しやすそう。

今のところ敵キャラだけに実装してるけど、味方もBehavior Treeで動けるような仕組みにはするので、HPが低かったら回復とかいい感じに思考してくれるやつをこれから作る予定。

裏側はだいぶよくなったので、そろそろ見えるとこも直していこう・・。

出稼ぎダンジョン進捗報告 1

定期的に書いていけってばっちゃが言ってた

前回のお産合宿の時からの変更点としては以下

  • テンションシステム
  • テンションアップコマンド

テンションシステム

パーティは戦闘時にテンションが変化します。テンションは攻撃したり、ダメージを受けたり様々な要素で上下します。

テンションが最高潮に達するとハイテンションモードになり、一定時間パーティが強化され戦闘を優位に進めることができます。逆にテンションが最低になればローテンションモードになりパーティは弱体化、ピンチになっていきます。うまいことコントロールしていきましょう。

テンションアップコマンド

先のテンションゲージを一回で最高潮にさせる大技です。具体的にはパーティにお金を払います。それなりの出費はかかるのでここぞというときに使うと良いでしょう。

あとダンジョンを一新したり。

f:id:adarapata328:20170919231047p:plain

ダンジョンは現状Nostalgia2を使ってます。

https://assetstore.unity.com/packages/tools/sprite-management/auto-tile-available-nostalgia-2-70610

次やりたいこと

まだコアである収益計算部分ができてないので、そっちをやりたいところ。

ただ、そろそろコードの依存関係がしんどくなってきたので、Zenjectを入れてDIしたい。Findしているアレコレをこの世から抹消したい。

https://www.assetstore.unity3d.com/jp/#!/content/17758

普段開発するとき1画面でも結構シーン分割して、動的にシーンをマージして全部結合したらFind云々をよくやってるんだけどその辺もZenjectで解決できるだろうか。

skipLast, takeLastオペレータはデータを流すタイミングをずらす

ちょっと面白かったのでメモ。

例えば複数のSubscriberを並行して処理したいなと思った時に、autoConnectで流すタイミングを合わせたとする。

Observable<Integer> foo = Observable.just(3, 4, 5).publish().autoConnect(2);

foo.subscribe(data -> System.out.println("A:" + String.valueOf(data)));
foo.subscribe(data -> System.out.println("B:" + String.valueOf(data)));
A:3
B:3
A:4
B:4
A:5
B:5

もちろん順番に値が流れることになる。

この時にskipLastで片方だけ流す量を調整してみる。

Observable<Integer> foo = Observable.just(3, 4, 5).publish().autoConnect(2);

foo.subscribe(data -> System.out.println("A:" + String.valueOf(data)));
// 5だけskipされる
foo.skipLast(1).subscribe(data -> System.out.println("B:" + String.valueOf(data)));

するとこんな感じになる。

A:3
A:4
B:3
A:5
B:4

ABABAと最後のBが流れないのかと思いきや、AABABと一つ目のBがなくなってしまった。 なんでだろうと思ってソース読んだらかなりシンプルだった。

github.com

指定した数だけキューに溜めて、数を満たしたら順次OnNextに流していく実装になっている。 オペレータから見た時に、どのくらいの数のデータが流れてくるのかという情報はないので、先に指定分ずらしておくようだ。 なるほどという感じ。

ちなみにtakeLastもほぼ同じ感じ。

https://github.com/ReactiveX/RxJava/blob/d3455d0c9d57d522c31b5c25af83e8f2b8df12b6/src/main/java/io/reactivex/internal/operators/observable/ObservableTakeLast.java#L59-L64

普通に使っててこの仕様にひっかかることはなさそう。

お産合宿11でゲーム作った

ペパボでは毎年お産合宿という創り出す系の合宿イベントをやっています。 今年はSUZURANというチーム名で参加してきました。僕はイーグルです。 osan.pepabo.com

僕は今までアクションゲームばかりでRPGを作ったことが無かったので、やってみたいなという気持ちからスマホ向けRPGを作ることにしました。

体調は良くなかったです。

www.instagram.com

そしてできたプロトタイプがこちら。

www.instagram.com

出稼ぎダンジョンというRPGができました。 借金を背負った主人公が金で傭兵を雇ってダンジョンに潜ってお金を稼ぎ返済するRPGです。いかにコストを抑えて稼ぐかというのが肝になります。

バトルシステムはSFC時代のFFを踏襲したアクティブタイムゲージによるリアルタイムコマンド式です。一方で、味方の隊列は半熟英雄仕立てになっていて、先頭だけダメージをうける「ちょくれつ」か、全体でダメージを分散する「へいれつ」の二つの陣形を駆使して戦います。

割といい感じにできたのでデジゲー博に申し込みました。間に合うといいなあ。

デジゲー博 | 同人&インディーゲームオンリー展示・即売会

おまけ

今回MVPアーキテクチャを採用しており、View = MonobehaviourとしてPureClassとしてPresenter、Modelを作成して開発してみた。 しかしながら、ゲームにおけるビューの責務は結構大きく、あれもこれもビューじゃね?みたいなことになってしまい結果的にモデルがないビューも存在したりしてしまった。あとビュー同士の関連(敵が攻撃を通知したら味方に影響を与えたりとか)もめっちゃあるので、その都度モデルにロジック書いて・・とするのが正しかったかもしれないがそれはそれで面倒さが増してたなあと思う。

下記のブログに書かれている Viewを積極的に拡大解釈していく というのは非常に大事だなと思った

yutakaseda3216.hatenablog.com

あと、今回初めてRPG作ったけど今までと全然違ったのでどうやったらいいだろうみたいなのを苦労しまくった。 そのときに戦闘のフローをPlantUMLで書いたものが出てきたので、せっかくだし公開。

バトルシーン全体は開始 -> 戦闘ループ -> 終了 -> 結果という一方通行なので下記のような感じ

BattleFlow@1-13

その中で戦闘ループを展開するとこんな感じ。アクションを受け付ける状態、アクションを実行している状態、アクションの結果を反映させる状態

InBattle@1-11

Wait

Wait@1-10

受け付けるアクションをICommandableインタフェースで抽象化した様々なアクションをキューに入れる。基本一個でもキューがあるなら即時にActionに映る。

このキューはWait状態以外の時でも突っ込むことは可能で、その場合は積まれていき、Waitに戻った場合にまた先頭を取り出してActionに移る。

ちなみにこのキューはUniRxのReactiveCollectionを拡張して作った。

Action

Action@1-8

再生するだけのアレ。ここは一方通行なので特に言うことはなく、Waitから送られてきたICommandableを実行するだけ。Animationだけ再生する予定が結局ICommandableががっつりロジック持ってたりするので失敗したっぽい。

Result

Reult@1-14

Actionの結果によってどうなったかというのを処理して、もし終了であるならInBattleのループから脱出する。

ゲームの良い設計、作ろうとしているものによって勝手が違い過ぎるので永遠の課題に思えてきた

大学のサークル合宿でUnityのハンズオンやってきた

8/19,20の二日間で、じょぎ(大学時代に所属していた技術系サークル)の合宿に参加してきた。

www.instagram.com

今回は講師として招かれたので、学生10人を対象にちょっとしたハンズオンを行った。

割と口頭ベースだったので資料は少ない

サンプルプロジェクト

こちら http://shicappa.chicappa.jp/jyogi/jyogi-bootcamp-2017.zip

公式の2Dゲームをベースにして、フィールド上のコインを集めるゲームにルールを変更しています。

この作品はユニティちゃんライセンス条項の元に提供されています

f:id:adarapata328:20170822010715p:plain

やったこと

サンプルプロジェクトをベースにUnityのハンズオンをやった。 流れは以下。

  1. UnityのGUIだけでオブジェクトを追加したり動かしたりして改造してもらう

  2. スクリプトを実際に書いてゲームに少し動きを付ける

  3. 残り時間フルに使って好きにゲーム作ってもらう

  4. 全員でお互いのゲームで遊んでみて、一番面白かった作品を投票する

1日目に1~3を行い、翌日の朝に4を行った。

なぜこのカリキュラムにしたか

参加者の情報がまったくわからなかったので、まずは事前アンケートでいくつかヒアリングをしてみた。

その中で、「作品を完成させたことがあるか?」という項目でほとんどがいいえと答えていたが、求める講座内容は「コードの書き方、技術の基礎部分を知りたい」という座学の要望が多いところが気になった。

個人的には、最初は技術レベルを上げることよりモノを作り上げるという経験を重ねたほうが良いと思っている。作って公開して遊んで感想をもらうというサイクルがモチベーション維持になるし、その中で苦労した部分を学べばいいのではと思う。知識が歯抜けになるのは否めない。

ということで、今回は 作る -> 見せる -> 改良する のサイクルを経験してほしかったのでハンズオン形式にした。

気を付けたこと

「遊んでもらう」という点をめちゃくちゃ重要視した。

4の試遊会はもちろんだけど、3の開発時間も定期的に隣の人に遊んでもらって感想をもらうようにしてもらった。 発表まで隠しておくというのをやるのは一つの楽しみ方だけど、他人に遊ばせずに作るゲームは往々にして高難易度ゲーで面白くなくなりがち。それに、定期的にフィードバックもらった方がモチベーションは維持できるし、他人に見せるハードルが下がっていくのでそのあたりを体験してほしかった。

どうだったか

全員無事に改造したゲームを作れていた。 10本全部遊んだけど、少なくともハンズオン終了時のままという作品はなかった。

というか、横スクロールシューティングになってたり、操作キャラが2体に増えたパズルアクションみたいになってたり割と別ゲーも散見された。

所感

全員熱意がすごかった。

講座自体は夜22時で終了していて、あとは自由時間なので、困ったら自分の部屋に来てくれたら教えますよというアナウンスをしていたら、割と代わる代わるで学生が部屋に相談に来たので、結局3時くらいまで相談に乗っていた。なので講座終了時と比べてだいぶブラッシュアップされてる作品が多かった。

元々熱意が凄かったのだと思うけど、今回の講座でそういう面白くなるサイクルにカッチリハマってくれたのであれば嬉しいなあと思うけどその辺はよくわからない。成功かどうかの判断は、11月の学園祭でどのくらい作品が出てくるかでいいと思う。

夜中にシャイアのjust do itを再生してたらやる気が出てきたけど、結局頭働かなくて寝てしまったし、睡眠は大事だなと思った。

www.youtube.com

UNIBOOK8の執筆に参加しました

夏コミ1日目、Unity部によるUnityのためのUnityの本、UNIBOOK8が頒布されます。

www.unity-bu.com

もともと気になってはいましたが、今回ご縁があって僕も参加させていただきました。僕はマルチシーンの活用方法について書いてます。この章は、技術的なtipsというよりは方法論の話がメインです。チーム開発でやり方困ってる~という人には何かの参考になるかもしれません。

もちろん他の章も見応えある内容なので是非読んでみてください。

ちなみに僕は三日目にお手伝いでサークル側にいるので縁があったらお会いしましょう。