パスワードを忘れた? アカウント作成

yumeさんのトモダチの日記みんなの日記も見てね。 みんなの日記の更新状況はTwitterの@sradjp_journalsでもチェックできます。

15347181 journal
日記

yumeの日記: またブラウザゲーム作ったよ〜〜 1

日記 by yume

itch.ioで『Breakout: 死中求活』を公開したよ。
ハイスピードなブロック崩しゲーム。

Mini Jam 84: Bubblesの参加作品。制作期間は72時間。
今回の制限は「You are your own ammo」。つまりプレイヤー自身が何らかのリソースである、というもの。

ブロック崩しのパドル(プレイヤーが動かす棒)の長さを消費してボールを追加で打てる、というものにした。
ブロックは通常のものに加え、「パドルの長さを全回復する」ものと「周囲のブロックを破壊する」ものがある。
ゲームは全4レベルで構成されていて、全てのレベルをクリアしたときに合計タイムが表示される。このタイムを短くするのを競う(ランキングないけど)。
1分切りを目標にすると、いろいろ深みが出てくるかも。

--

それと、前回のMini Jamの結果は3位だった。

15285857 journal
日記

yumeの日記: またブラウザゲーム作ったよ〜〜 2

日記 by yume

itch.ioで『Miniventure』を公開したよ。
言葉に頼らない小さなアドベンチャーゲーム。

Mini jam 80: Catsの参加作品。制作期間は72時間。
今回の制限は「8x8 Textures」。つまり全てのテクスチャを8x8ピクセルで描く、というもの。

Miniventureは「言語なしでアドベンチャーゲームが作れるか」というテーマで考えていたいくつかのアイデアのひとつ。
アドベンチャーゲームといえば冒険であろう、ということでコテコテの冒険譚(怪物からお姫様を助け出す)を作った。
コテコテとはいえ、言語に頼らないと結構難しい(しかも絵が8x8の超低解像度)ので、相対的に難易度は落とした。アドベンチャーゲームとしてはかなり小さくて簡単だと思う。
システム自体は親切で、ある場面でできる選択と、できない選択がすべて表示されている。できない選択=持ってないアイテムなので、ある場面がうまくいかなかったら、その場面に必要なアイテムをどこかから拾ってくればよい。
しかし拾ってくるアイテムの順序も考えなければならない、という感じ。正しいエンディング以外にちょっとダークなエンディングもあり。

--

それと、UnityRoomの「Unity1Week」の結果が出てた。
『●●』は総合11位、斬新さ1位だった。
「楽しさ」と「操作性」が比較的低いスコアだったので、次はそこももうちょっと意識してみよう。

15275122 journal
日記

yumeの日記: メデューサ・ゲーム改・11

日記 by yume

◉**able、**er
『●●』を作っていて、イベント(UniRx)と委譲をもっとガンガン使った方がいいと気づいた。そうすることでクラスをもっと切り分けられる。
例えば前回までのTouchableには、実際には複数の機能(触れること・インタラクトできること)が混在している。
機能はどんどん切り分けていって、それぞれを最小単位のクラスにわけていこう。ついでに、それぞれの受動側と能動側のクラスを定義することで、機能の組み合わせでキャラクターにできることを設定していく。次のような簡単な規則を使う。

・**able(Touchable、Interactableなど)
>Touchできるもの、Interactできるもの。受動側。
>onTouchやonInteractイベントを発行する。

・**er(Toucher、Interactorなど)
>Touchするもの、Interactするもの。能動側。
>受動側にイベントを発行させる。

互いの関係性も理解しやすい(ToucherはTouchableに干渉できるのはすぐわかる)。し、あるキャラが何をできるか、ある設置物にはどういう干渉ができるかが明白だ。

というかUniRxすごく便利である。

この辺りの「コンポーネントとして使う」のはUnityエディタ的にはわかりやすいけど、本来はMonoBehaviourを継承しない純粋なクラスやInterfaceとして定義すべきだろう。ITouchableとかIInteractableとかにするわけだ。MonoBehaviourを継承すると不要なもろもろを継承してしまうわけだし。

でもUnity的なので一旦このようにしておこう(MonoBehaviourの機能を後から使いたくなるかもしれないし)。直すのもそんなに難しくはない……。と思う。

--

◉プレッシャー・プレート

プレッシャー・プレートを実装する。
プレッシャー・プレートは:
・重みのあるもの(人やアイテム)が上に乗ると、連動して仕掛けを動かす。
という抽象的な概念を持つ。

Toucherが触れればなんであれ反応する、という仕組みにしておき、鏡や重りのようなアイテムにもToucherをつければひとまずうまくいった。

ただ、この場合メカニズムが動くタイミングがアニメーション中になってしまうので、経路探索などに問題が生じるかもしれない。
TurnManagerに「Pressure感知タイミング」みたいなイベントを用意すべきだろう。

といったところで今日はここまで。

15274530 journal
日記

yumeの日記: またブラウザゲーム作ったよ〜〜 3

日記 by yume

UnityRoomで『●●』を公開したよ。

「Unity1週間ゲームジャム」の参加作品。制作期間は1週間。
テーマは「2」なので、二つのマルを探し出すゲームにした。

『人間 ◉』でやったインタラクティブ・コラージュの延長で、今回はマウスだけで遊ぶもの。
1週間も時間があったので、After Effectを使って動画をUnityに取り込んで、それを活用しようとしたけど、UnityRoomがStreamingAssetsに対応していないため、動画ファイルを使ったネタはお蔵入り。
一部の動画はUnity上でループとアニメーションを使って再現して、なんとか形になった。
動的な動画は面白いのでもっと色々やってみたい。

隠しページを含めて、全16ページ。

15268492 journal
日記

yumeの日記: Mini Jam 78で総合4位だった

日記 by yume

https://itch.io/jam/mini-jam-78-bugs/rate/1002985

結果が出たので、もう少し遊べるようにアップデートしておいた。
・ヒーロー「Samurai」と「Shape Shifter」の追加
・アンロック要素の追加。
・Witchの能力を変更。
・数値の調整。

Ludem Dareにも出ようかな、と思ったけど諸々の予定で断念。

15263484 journal
日記

yumeの日記: またブラウザゲーム作ったよ〜〜 5

日記 by yume

itch.ioで『Bad Guy TV』を公開したよ。
ファンタジーテレビ番組風のチキンレースゲーム。
Mini Jam : 78 の参加作品。期間は72時間。

今回の制限は「You are the Bad Guy」。
Bad Guyは「悪役」という意味だけど、「悪いやつ」を意味するとは限らない、って『シュガーラッシュ』で言ってたのを思い出して、じゃあ悪役を演じる何かを作ろうか。と考えた。
で、最近遊んだ『Loop Hero』をもっと単純化できないかなぁ、というアイデアと合体して「ゲストヒーローを倒れる直前まで戦わせる危ないテレビ番組」という設定に。

ゲームには「ゲストヒーロー」が登場し、勝手に右に進んでいく。
右端に到達するたびに、プレイヤーは次の二つを選ぶ:
・悪役をステージに追加する。
・ボス戦を開始する。
これ以外の操作は不要!

これを繰り返して、ヒーローが倒れるか、ボスを倒すまで番組は続く。
番組が終了したとき、視聴率とスコアに応じて「報酬」が決定され、プレイヤーに支払われる。

視聴率は、主にヒーローの現在体力によって変動する。ヒーローが元気なうちは退屈とみなされて視聴率は徐々に下がり、体力が50%以下になると徐々に上昇する。あと戦闘でも少し増える。

初めのうちはステージ上に悪役が少ないので、ヒーローは簡単に進めるけど、悪役はそれぞれが悪役同士を強化する力を持っていて、数が増えるとダメージが嵩んでいく。ヒーローが倒れてしまうと、多額の治療費を報酬から引かれてしまう(赤字もありえる)ので、ヒーローにボス戦の余力があるギリギリを見極めてボス戦を始めるのが、報酬をたくさん得るミソ。

英語版しかないので、各モンスターの特殊能力をまとめておく:
・Cursed Head(赤丸頭)は悪役全員にクリティカル率を5%追加。
・Count(吸血貴族)は悪役全員に吸血効果確率を5%追加。
・Witch(魔法使い)は2秒ごとに悪役全員を5回復。
・Slime(スライム)は悪役全員の体力を2追加。
・Crab(ヤドカリ)は悪役全員の防御チャンスを5%追加。
・Bat(こうもり)は移動と攻撃速度が早い。

ヒーローは3人いて、それぞれギャラが違う。
・弱いけど無料の暇人。0円。
・強くて扱いやすい戦士。250円。
・体は弱いけど吸血効果持ちの吸血鬼。500円。

--

個人的な挑戦としては:
・アニメーションするUIを作る
・アセットを使う
UIは相変わらず時間がかかる。アセットを使うのも思った以上に大変だ。というのも、イメージ通りのアセットがあることなんて滅多にないし、あったとしても一通り全部揃うということはまずないからだ。
今回は16x16サイズのモブキャラが詰まってるパックを買って、足りない分(ヒーロー2体分)は自分で描いた。ちょうどメデューサゲームのピクセルワールドの時に描いたディアティスがいい感じに16x16だったので助かった。
背景に至っては全くしっくりくるのがなかったので、丸ごと自分で描いた。
22ドルで買ったのに全然使ってないアセットとか余っちゃった……。

お金の使い道があんまりないので、アンロックヒーローを増やしたりしたいなぁ……というところで時間切れ。まぁでも、プログラミング力も結構あがってきた実感があった。以前だったら絶対3日ではできなかっただろう。カードで何かを追加していく仕組みもだいぶこなれてきた。今なら『ハイパーインフレーション!』ももっとうまく作れるだろう。

15260172 journal
日記

yumeの日記: メデューサ・ゲーム改・10 1

日記 by yume

◉高さの表現(ぼつ)
このゲームは典型的な2D top-down視点だ。
完全な真上から見るわけではなく、擬似的に少し角度をつけて見ているタイプ。
こういうタイプのゲームの場合、ゲームオブジェクトの原点は足元にするものが多いと思う。
足元を原点とすれば、高さの違うオブジェクト同士でも何も考えずに配置できるからだ。

しかし、このゲームは「視線」が重要なゲームなので、足元を原点としてタイルを敷いていくと、ちょっと都合が悪い。
原点を足元にして、視線も原点から出るとなると、「視界」足元から視線が伸びているように見えるからだ。
単純に視界の原点を頭の高さまでずらす、という場合、縦方向に差ができて、パズルが直感的でなくなる。
となると視界は原点=タイル中央で、なんとかそこを頭の高さにしたい。

このゲームは本質的には完全見下ろし型だけで完結する。
ゲームの基本的な構造を図に落とし込むと、以下のようになる。
図1:ゲームの表現上の基礎部分

この図のうち、色のついた円はプレイヤーなり敵キャラなりのエンティティを意味する。
頭を基準に視線のラインも決めているのだから、視線を打ち消す高さは壁と床の接する部分ではなく、天井部分が基準となる。その方が見やすいし都合もよい。(前回もそのようにしている)

というわけで、ここからきっかり0.5マス分だけ下に装飾部をぶら下げる。
色の円は頭部、そこから下に動体をぶら下げるイメージだ。
天井も同様に、壁の側面を0.5マス分ぶら下げる。
図2:装飾部を下にぶら下げる

エンティティの高さ、特に視界が影響するエンティティの原点がマス中央になっている。
この方式の場合、1.5マスより身長が高いエンティティを制御するのが面倒くさそうだが、そういうものを作る予定もないのでよしとする。

そうして、2.5頭身くらいのキャラを描き終えたが……。
図3:新しいスプライト

ん〜……。やっぱ微妙だな。パッと見はともかく、遊んでみると問題がある。正確な位置関係が思った以上に掴みづらいのだ。

没にしよう。その代わり、1x1でも違和感のないスプライトを描いてみる。

光の原点が円の中心でないといけない、ということはなく、実際には多少ずれていてもいい。
その代わり、上下左右それぞれに向いたスプライトを作ろう。作業量は増えるけど、兵士の向き(=進行方向)も読めるようになるし、その方が遊びやすいはずだ。

--

◉1x1スプライト

1x1で上下左右の向きがある、と言えばSFC版のドラクエ1とかポケモンとかのイメージだ。頭も大きいので都合がよさそうなので、いくつか探してプロポーションの参考にする。

キャラクターのプロポーションは初代ポケモンくらいが理想に近い。
タイルはSFCドラクエ1が一番わかりやすい。特に壁の高さの表現が明確だ。手前側から見る壁は、1x1ではなく0.5x1のように見えて、側面の表現が0.5を埋めている。もしかすると、この表現の方が視界が通るかどうかが直感的にわかるかもしれない。
ただ、柱の表現のために1マスに収まらない側面の表現は欲しいので、この辺りは調整していく。

まず、基礎となるActorBaseを作ろう。前のように行き当たりばったりでなく、基本的なスケルトンの構成を予め考えておき、それを網羅する仮のActorを作る。

こういうプロポーションになる。

このサンプルではキャラクターは南向きになっている。4方向を埋めるには後東向きと北向きのスプライトとアニメーションを作らなければいけない。これが大変なんだけどとにかくなんとかできた。

動作確認

このキャラクターはスケルトンのベースを組むために作ったので、別に本番では使うわけではない。これをベースにMistyとSoldierを作っていこう。

15256986 journal
日記

yumeの日記: メデューサ・ゲーム改・9

日記 by yume

◉TurnManagerの改善
ターン中のイベントを動作種別に細分化した。
TurnManager
以前は3つのイベント(TurnStart、EvilSight、TurnEnd)で制御していたが、抜本的に変更した。
新たなターンイベントは以下の6つとなる。
(ターン開始時に実行)
・OnDetectPlayer //兵士などがプレイヤーを探知し、探知状態を切り替える。
・OnPathFind //兵士などが目的地への経路を探索し、必要なら移動する。
(ターン開始から0.2秒待って実行 = 移動とかは終えてから実行)
・OnEvilSightReflesh //邪眼の影響状態を更新する
・OnCapture //兵士の捕獲判定タイミング
・OnPetrify //兵士の石化判定タイミング
・OnSolveInputBuffer //プレイヤーの先行入力がある場合、ここでその入力が呼び出される。

これにより、処理の順は:
1. プレイヤーがターン消費行動を入力する(移動など)
2-1. 兵士がプレイヤーを探索し、発見状態を更新する
2-2-1. プレイヤーを発見した兵士は、経路を探索する。
2-2-2. 経路探索の結果を待ち、経路により移動先があるなら移動を開始する。
(TurnBaseTime(0.2秒)だけ待つ。移動のアニメーション時間分の待機)
3-1. 全員が移動したあとの座標での邪眼の影響範囲を更新する
3-2. 兵士が対象に触れているなら、捕獲を実行する
3-3. 兵士が邪眼の影響を確認し、石化ダメージを計算する。また、その結果石化する。
3-4. プレイヤーの先行入力があった場合、その入力で1.を実行する。
先行入力がない場合、入力を待ち、入力があったら1.を実行する。

ここまでに登場した処理のうち、経路探索〜兵士の移動は非同期処理になっている。
兵士の移動は経路探索を待ってから行われるが、0.2秒の待機の間に処理されるので、アニメーションが処理時間分遅れる可能性はあるものの、0.2秒以上ずれない限り、ゲームの結果には影響しない……と思う。
この辺りもちゃんとするなら、処理時間を計測して、兵士の移動アニメーション時間はTurnBaseTime - 処理時間としたらよいかな。
でも、処理時間が0.2秒を超えてしまったら、やっぱりおかしなことになるな。まぁ、現実的には計算時間が0.2秒を超えることは考えづらい……と思うけど、断言はできない(他のことでCPU使ったりとかもあるのかもしれないし)。
やっぱり2-2-1.の処理が終わるまでTurnManagerを待機させる必要がある? でもやり方がちょっと思い浮かばない。TurnManagerは経路探索者をリストで保持して、全員から何らかの通知を待つまで何もしないとか?

--

◉鏡
まぁとにかく要素を増やしていこう。最初の10レベルを構成するのには鏡が必須なので、これを作る。

鏡は邪眼を持ったEntityで、プレイヤーはインタラクトすると鏡を所持できる。
鏡を所持中に、何もない場所でまたインタラクトすると、鏡をそのマスに配置できる。

まずは「所持できる」という概念を作るために、ItemとInventoryの二つのクラスを作る。

抽象クラス Item は:
Touchableコンポーネントを持ち、
Interactorにインタラクトされたとき、
・Interactorの Inventory にItemクラスを保持させて、
・自身のGameObjectを非アクティブ状態にする。
・PlaceOnGroundメソッドを持つ。
・PlaceOnGroundメソッドの挙動は、継承先が実装する。基本的には、Itemを配置者の座標と同じ座標に移動し、GameObjectをアクティブにする。

クラス Inventory は:
・Itemをひとつだけ保持する
・ItemのPlaceOnGroundを呼び出せる。

そして、Itemクラスを継承してMirrorクラスを作る。

クラス Mirror : Item は:
・PlaceOnGround時、座標と共に配置者のFaceDirectionと自分のDirectionを合わせる。

Mirrorクラスを持つGameObjectは、邪眼の行使に必要なコンポーネントを持つ。

動作確認。

よさそうだ。

15256097 journal
日記

yumeの日記: メデューサ・ゲーム改・8 1

日記 by yume

◉石化と捕獲。

で、兵士を構成する最後の要素「見られたら石化する」を実装する。

……の前に、邪眼の効果が発生するタイミングをちゃんと定義しよう。

ゲーム上で、邪眼が発動する瞬間は、今のところ毎フレームだが、実際に必要なタイミングは:
A. プレイヤーが向きを変えたとき(ターンは消費しないが、邪眼の状態が変わるため)
B. ターンが終了したとき。
C. 邪眼が通る条件が変わったとき(柱が動いたときなど)
の3点だろう。このうち、プレイヤーが向きを変えたタイミングは誰も検知していないので、これを検知する必要がありそうだ。

ん〜〜〜。レベルマネージャーのように、レベルごとに邪眼の管理者がいる方がやはりよさそうだな。
EvilSightManagerとしよう。

クラス EvilSightManager は:
・全てのSeeTargetをリストで保持する。
・プレイヤーが向きを変えるか、ターンが終了したとき、
・onRefleshEvilSightイベントと、onRefleshSeeTargetイベントを順番に発行する。

クラス EvilSight は:
・OnRefleshEvilSightイベントを購読し、
・イベントが発行されるとEvilSightを行使して、有効視界内の全てのSeeTargetの邪眼影響状態をOnにする。

クラス SeeTarget は:
・OnRefleshSeeTargetイベントを購読し、
・イベントが発行されると自身のSeeing状態を確認・更新する。

クラス PillarMechanism は:
・柱の状態が切り替わった1フレーム後に、EvilSightManagerに各イベント発行を要請する。
つまり、邪眼によって柱の状態が切り替わったなら、その結果邪眼の影響範囲も変わる可能性があるので、また邪眼の影響状態を再計算する、ということをしたい。
1フレームまたないとうまく動かないので1フレームさしこんでるが……あとでちゃんと検討しよう。

ん〜〜〜。同時に発生する時差イベントが多くなってちょっと怖い感じがするな。
ターン開始:全てのオブジェクトが自分の挙動を開始し、0.2秒数えてターン終了
ターン終了:全てのオブジェクトがターン終了時イベントを実行し、同時に邪眼状態を更新。柱が動いたなら1フレーム後に邪眼がさらに更新(それによって柱が動いたらまた更新を繰り返す)。

ターン開始からターン終了までは0.2秒あるので、大抵の処理は大丈夫だと思うけど、
ターン終了からターン開始まではタイムラグがゼロなので、ここで処理時間がかかることしちゃったら、下手すると予想外の挙動を起こしちゃうかもしれん。
ターン終了時イベントの処理を待機するみたいなことをしないといけないかも。ちょっとやり方がまだ思いつかないけど。

石化の判定は、ターン終了処理を終えたタイミングにしてみよう。
クラス Petrify は:
・SeeTargetを参照して、
・ターン終了時にSeeTargetが邪眼を検知している状態なら、石化状態を1進める。
・石化状態が設定した耐久力以上に達したら、石化したとみなす。

今のところ、OnTurnEnd(ターン終了時)というタイミングで視界の処理をしているので、ここに割り込むとまずい。なので邪眼の処理はOnTurnEndの手前にさらにOnTurnEndSightEventというSubjectを作り、ここに移動する。
処理の順番は:
OnTurnStart
(0.2秒後)
OnTurnEndSightEvent
(終了次第)
OnTurnEnd
となる。

ん〜〜〜。こんな調子で処理タイミング増えていきそうだな。それでもいいのか?
後々困りそうだから、TurnManagerに追加した処理をメモっておくか……。というか、メモる必要もないくらい自明な名前で必要なだけ処理タイミングイベントを増やした方がよいかも。

動作確認
うん、とりあえず動いてはいるな。
ただ、今のところ捕獲と石化が同じタイミングのイベントで処理されてるので、プレイヤーと石化直前の兵士が重なったとき、どちらが優先されるかがランダムになっている。これは要修正だな。

結果的に、TurnManagerは現状では3つのイベントを回してるけど、処理の種類ごとにイベントを増やして、それらを順番に発行した方がよいかもしれない。少なくとも、同時に起きると齟齬がありうるイベントは同時に起こしちゃいけないわけだ。
どれほど増やしたとしても、10個は超えなさそうだし。

15255457 journal
日記

yumeの日記: メデューサ・ゲーム改・7

日記 by yume

◉兵士 - 経路探索

Soldierの経路探索を実装する。
使うのはやはり前回同様A* Pathfinding Projectだ。
ただ、今回はターンベース・グリッドベースな仕組みなので、前回のように外部のサンプルスクリプトそのまま走らせてうごくというものではない。

定義を考えておこう。

経路探索移動は、呼び出されたとき:
・経路探索を呼び出し、現在地に隣接する目標座標、ないし方向を取得する。
・目標に向かって、ターンベースで1マス移動する。

経路探索は、呼び出されたとき:
・現在地とプレイヤー間の、障害物を避けて通れる経路を算出する。
・その経路から、現在地に隣接する1マスを目標地点として返す。

経路探索の準備にあたり、まずシーン上に障害物から計算した通行可能エリアを取り出さなければならない。
これは、A* Pathfinding ProjectのAstarPathクラス(コンポーネントとしてPathfinder)でシーン上の障害物から計算してくれるらしい。

そうして、通行可能エリアを割り出したら、SeekerクラスのStartPath関数でPathを計算する。

PathはGraphNodeのリストを持っている。
GraphNodeは見た感じ、ちょうどマス目ごとのポイント(WayPoint的な)を持っている。
ということは、このリストの1番目の座標目指してStepすれば、うまく動くはずだ。

まだ問題が残っている。この経路探索は、ステージ開始時のマップの障害物の状態をもとに経路を割り出しているが、可動式柱などによって、障害物の状態が変わる可能性がある。

AstarPathクラスには、経路を再スキャンする方法が載っている。
これによると、使用しているグラフに限定して更新した方が良いらしい。
サンプルにある通り、1番目のグリッドグラフだけを更新するコードを、柱が稼働するタイミングで呼び出す。

動作確認。

動く……。すげえあっさり動く。すごい。天才かもしれない。

◉兵士 - 捕獲動作
兵士がプレイヤーを捕獲する条件は:
・ターン終了時に、
・同一マスにプレイヤーが存在するとき
かな。つまり、プレイヤーと兵士が重なったなら、その瞬間だ。
この辺りはちょっと調整するかもしれないけど、一旦これで実装してみよう。

クラスCaptureは、ターン終了時に呼び出され、自身と重なるオブジェクトを調べる。
調べた結果が:
・Playerが重なっているなら、Captureを実行する。

Captureメソッドでは、対象のCapturableコンポーネントを探し、それがあるならCapturableのCaptureメソッドを実行する。
クラスCapturableは、捕獲可能なオブジェクト(プレイヤーなど)にくっついて、BeCapturedメソッドを持つ。このメソッドはCaptureクラスから呼び出され、捕獲時の捕獲された側の挙動を定義する。

これでOK。

typodupeerror

身近な人の偉大さは半減する -- あるアレゲ人

読み込み中...