imog

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

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というよりは方法論の話がメインです。チーム開発でやり方困ってる~という人には何かの参考になるかもしれません。

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

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

MakerFaireでテンション上がってraspberry pi 3を買った

maker faire tokyoに行ってきた。

makezine.jp

ハード系全然やったことなかったけど、作品見てたらテンションが上がったので勢いでraspberry pi 3を買ってしまった。

www.instagram.com

とりあえずセットアップをしたのち、sshvncで入れるように設定して、定点カメラのページを見れるようにした。

DDNSは今さっき登録したので1,2日くらいで外部からアレコレできる環境にはなりそう。 久々にテンションが上がってしまった。

Bluetooth対応してるのでなんか面白ガジェット見つけたら使っていきたいなあ。

ちなみにMaker Faireで個人的に一番面白かったのはデルモンテでした。

www.instagram.com

Unity1週間ゲームジャムに参加した

これ

Unity 1週間ゲームジャム | ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう

最近小さいやつも作ってなかったのでリハビリにやってみた。 結果、寿司が跳ねる奴が生まれた。

はねすし | ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう

イデア出し6日、開発6時間。

スクリプトはUniRx以外、外部アセットは使ってません。

反省点

しばらくやってなかったからか、アイデアが出ない。 どんなのが面白いかなあというところの想像力が欠如し始めたので大変に良くないなと思った。

「油で上がった力士が跳ね回る」という案はある程度掘り下げたあたりで意味がわからなくなったのでお蔵入りになりました。

こういうのは定期的にやらないとダメだなー。

でも跳ねたあたりからは楽しくなってきたので参加してよかったと思う。次もあったらやろう。

RxJavaでPresenterがViewを購読するスタイル

下記を見ながらMVPで書くぞ、という練習をしている。

konifar.hatenablog.com

その中で、ViewとPresenterを書いてるときに、このあたりの関係をストリームでやれたら気持ちいのかなあと思い試してた。

とりあえずは、TwitterをプロバイダにしたFirabeseのユーザ認証。 Presenterはこんな感じ

interface Presenter {
    fun startSubscribe()
}

ビューはこんな感じ、

interface LoginView {
    fun twitterAuthObservable(): Observable<TwitterSession>
    fun showLoginSuccess(session : TwitterAuth)
    fun showLoginFailure(throwable : Throwable)
}

showHogeはログイン成功、失敗時の表示をお願いという処理。

twitterAuthObservableはTwitter認証に成功したら発火するストリーム

Presenter実体はこんな感じ。

class LoginPresenter(private val mView: LoginView) : Presenter {
    override fun startSubscribe() {
        mView.twitterAuthObservable().subscribe({ t ->
            FirebaseLoginUsecase(auth).run().subscribe(
                { t ->
                    TwitterAuthRepositoryImp().saveTwitterAuth(t)
                    mView.showLoginSuccess(t) },
                { t -> loginFailure(t) }
            )
        },
        { throwable -> loginFailure(throwable) })
    }

    fun loginFailure(throwable : Throwable) {
        mView.showLoginFailure(throwable)
    }
}

startSubscribeが呼ばれると、ビューのストリームを購読し始める。

Twitter認証が終わったらFirebaseのログインを行うUseCaseに渡して処理する。

成功したら情報をどこかに保存しつつ、ログイン成功画面へ。 失敗だったらログイン失敗画面へ。

ビューの実体はこんな感じ。

class LoginActivity : AppCompatActivity(), LoginView {
    val mTwitterLoginButton: TwitterLoginButton by bindView(R.id.twitter_login_button)
    val mPresenter: Presenter = LoginPresenter(this)

    val mLoginStream: BehaviorSubject<TwitterSession> = BehaviorSubject.create()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        mTwitterLoginButton.callback = object : Callback<TwitterSession>() {
            override fun success(result: Result<TwitterSession>) = mLoginStream.onNext(result.data)
            override fun failure(exception: TwitterException) = mLoginStream.onError(exception)
        }
        mPresenter.startSubscribe()
    }

    override fun twitterAuthObservable(): Observable<TwitterSession> = mLoginStream

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        mTwitterLoginButton.onActivityResult(requestCode, resultCode, data)
    }

    override fun showLoginSuccess(session: TwitterAuth) {
        Toast.makeText(this, "ログインに成功しました", Toast.LENGTH_LONG).show()
    }

    override fun showLoginFailure(throwable: Throwable) {
        Toast.makeText(this, "ログインに失敗しました", Toast.LENGTH_LONG).show()
    }
}

よくあるonCreateでビューにListnerとかcallbackを与えてあげる処理。 コールバック内では、ロジックは書かずに用意したBehaviourSubjectに流してもらう。 全部終わったらPresenterに購読してもらう。

今まではViewが中でPresenterの特定のメソッドを呼ぶという感じだったけど、今回はViewはPresenterのことをほとんど知らなくなった。 クリックしたら何が呼ばれるとか、認証から帰ってきたら何が始まるか、とかは全部Presenter側のコードを読めば済む。

とはいえ、基本ViewとPresenterは1:1の関係で、使いまわしをすることもないと思うのでお互いが依存しあっててもまあいいんじゃないかな・・という気持ちもある。

とりあえずこれでやって辛くなってきたらまたブログにしたためよう