YAMADA TAISHI’s diary

ゲームについてとか私の日記とか。このブログのあらゆるコードは好きにどうぞ。利用規約があるものは記事内のGitHubのRepositoryのリンクで貼られていると思うので、そちらを参照ください。

【雑記】朝飯前って面白い言葉だよな

こんにちは、やまだたいし( https://twitter.com/OrotiYamatano )です。
今日はただ思ったから書いた雑記です。

目次


ぼんやりとお腹をすかせながら歩いていたときふと思う

何をやるにもご飯を食べないとやる気は出ないよな。
腹が減っては戦はできぬって戦国時代の武将も言ってたし、
でも、朝飯前ってのは、そんなやる気がない場合でもできるってことだよな。

よくよく考えてみると面白い言葉だ。
簡単である様子が朝飯前だなんてどうやったら思い浮かぶんだ。

一体どこの誰が言い出したんだろう。
もしかしたら明確な言い出した人は居ないかもしれない。

中国が起源なのか?

どうにも気になりだしてAIに聞いてみた。

朝飯前の起源

聞いてみたところどうやら日本起源らしい。

初めて朝飯前と出てきたのは、1681年(天和元年)の俳諧集『西鶴大矢数』に出る「朝食をとる前」という意味の文字どおりの用法らしい。

kotobank.jp

じゃあ慣用句としては?
↑同様にコトバンクに八笑人由来と書いてある。

恥ずかしながら八笑人とは何なのかまるで知らなかったので、AIに八笑人って何と聞いてみた。

どうやら八笑人(はっしょうじん)と読み、正式名称は 花暦八笑人(はなごよみ はっしょうじん) というらしい。

なんでも、茶番をテーマにした江戸時代の小説なんだとか。

内容は江戸の閑人仲間8人が、四季の遊山で茶番を演じて失敗する話らしい。

超簡単にいうと、狂言好きが集まって、揉め事を起こして、仲直りしたフリをしてただ酒をしようとしたところ、本物の助太刀があらわれてしまい皆して慌てて逃げることになり大失敗という茶番らしい。

そうなんだと思うと同時に、AIに任せきりで受動的に聞くだけでいいのか?と我が身に瞬時問答。

段々と気になってきて該当の慣用句が出てくるシーンを探してみることに。

古い文書

どこにあるのかだけはAIに聞くと国立国会図書館デジタルアーカイブを教えてもらい見れて読むことが出来た。

dl.ndl.go.jp

おお、旧字だ。
でも、よく見てみると比較的読みやすい。

なぜだろうと思ったら、よく見てみるとそれは明治翻刻版だった。
該当箇所は93ページあたり。

原本はないのかと調べてみると
早稲田大学図書館の古典籍総合データベースで無料で見ることが出来た。

www.wul.waseda.ac.jp

ほほう、旧字でほとんど読めないが、単語一つ一つは読める部分もある。

流れはほとんど同じだろうし明治翻刻版の前後の言葉じりからおおよその箇所が推測つくであろうとにらめっこを始めた。

詳しい流れは定かではないが明治翻刻版では、

呑七「其儀い少しも 「 案じ玉ふなたかゞ 彌五郎位の端役い朝飯前の仕事だどせ繐ざらひきい眞質き立て見ゐから宜しと其所で然ば然ばと両方へ立別れて

と続く言葉がある。
(掠れていて読めなかったり旧字がウェブの関係上入力できなかったりするので間違っていたら目をつぶってください)

どうも朝飯前の仕事とされていたのは呑七(のんしち)という登場人物が「彌五郎という役を演じること」は朝飯前だと言っている。

ちなみにココで言う呑七は酒好きで怠け者の遊民のキャラクターの一人らしい。
「呑」ってつくからお酒関係あるか?なないよな?って思ったけど関係あった。

にらめっこをしているうちに原本でも該当箇所を見つける。

(四編 下の後ろ近く)

https://archive.wul.waseda.ac.jp/kosho/he13/he13_03094/he13_03094_0011/he13_03094_0011_p0021.jpg

おや、どうも明治翻刻版とは違う。

明治翻刻版はよくわからないがナレーションらしき地の文と混ざり合うような書かれ方がしているが原文ははっきりと呑七のセリフである。

どのような話のくだりなのか、さっぱりわからない。
現代風に読みやすくなってくれたものは存在しないのだろうか。

多分ではあるが明治翻刻版にするにあたり大きくかさ増ししたのであろうと思う。

微塵も読めないが前後ページを何気なくチラリとみる。

あれ

なんか、きん玉って書いてない?

いや、わたしの読み間違いか?

明治翻刻版を検索してみる。

やっぱり、きん玉という単語が出てくる。

呑七「きんたまと云うもの 出目「なんだ突然ふざけたことを 呑七「口を効く度 上がったり

明治翻刻版の方はきん玉という言葉が睾丸になったりしているようだが他にも確かに存在する。例えば75ページだ。
現代っぽくすると↓かな?わからん、日本の古文の読み方をもう少し勉強しておけばよかった。

眼七のセリフで

へん即功紙(江戸時代の貼り薬の常備薬のブランド名)を張ったきん玉をみるように、「まったくどうも仕方ない居候の言う事だ、まぁ黙っていうもんいたしやしょう」

古文は地の文と混ざっているのか読みづらいが、めちゃくちゃ嫌そうな顔をしているのか肝を据えたのかは分からないが嫌々そうに覚悟を決めたのはすごく伝わってくる。

こんな面白い文章使う人が朝飯前って言葉を出して書いてたんだなぁって思うとかなり時代を跨ぐが完成は現代人と変わらない部分も多くあるように感じられる。

こんなに時代が離れていても、なんだかんだ日本人なのかもなぁ。とふと江戸時代の人を身近に感じた。

とりあえず、朝飯前が使われた初出箇所をみるという目標は達したので、
学のない私がこれ以上読もうとしても誤読するばかりだと思ったのでこれ以上読むことは諦めた。

誰か、読みやすい八笑人があったら教えてほしい。

【後編】Unity じゃんけん × AI Phi4 miniで戦略じゃんけんゲーム作ってみた-AIコーディングツール利用編

こんにちは、やまだたいし( https://twitter.com/OrotiYamatano )です。
前編では、Unity上でPhi-4 miniを動かしてじゃんけんゲームを作った実装についてお話ししました。
今回は、その後編で開発過程でAIコーディングツールを活用した際の所感をまとめます。

orotiyamatano.hatenablog.com

目次


なぜAIコーディングツールを使ったのか


今回のプロジェクトでは、AIコーディングツールを積極的に活用しました。
そもそもとしてAIコーディングが現実問題実務に耐えゆるのか気になったため全体をAIにコーディングに任せてみました。

使ってみて不便だったため、途中で使い方を見直したので本記事も2回にわけて所感を投稿します。

1回目の試行


1回目の利用ツール


まずはツールの概要から

CLI


今回使用したツールの多くはCLI(Command Line Interface)形式です。
CLIとは、コマンドライン(ターミナル)から実行するツールのことで、
GUIグラフィカルユーザーインターフェース)ではなく、
テキストベースで操作するインターフェースです。

AIコーディングツールにおいてCLI形式のツールを使うことで、

  • エディタやIDEに依存せずに利用できる
  • スクリプト化や自動化が容易
  • 複数のツールを組み合わせやすい

といった利点があります。

MCPとは


MCP(Model Context Protocol)は、AIアプリケーションと外部ツールやサービスを連携させるための標準化されたプロトコルです。
このプロトコルにより、AIモデルがアプリケーションの状態を理解し、
操作を実行できるようになります。

MCPの特徴として、

  • AIモデルと外部ツール間の標準化された通信方式
  • アプリケーションの状態を取得・操作できる
  • 複数のツールやサービスと統合しやすい
  • AIがアプリケーションを直接操作できる

などがあります。

Codex CLI


最初に利用したツールはメインとしてCodex CLIを採用しました。

Codex CLIは、OpenAI社が開発したCodexというコード生成用のAIモデルを、
コマンドラインから利用できるようにしたツールです。

まず、Codexについて説明します。
Codexは、OpenAIが開発したコード生成専用のAIモデルで、
GPT-3をベースにコード生成に特化して訓練されています。

Codexの特徴として、

などがあります。

Codex CLIは、このCodexモデルをコマンドラインから利用できるようにしたツールで、
通常のAIコーディングツールがコードの生成や補完を行うのに対し、
Codex CLIは実際にコードを編集したり、gitコマンドを実行したりと、
開発作業そのものを自動化してくれる点が特徴です。

具体的には、

  • CodexのパラメーターをTOMLで指定して、
    行動全てをApproveにするように設定することで、
    Codexに自然言語で命令すると処理を実行してくれます

自分のコーディング環境とは別にセットアップして動くため、
既存のワークフローを崩さずに試すことができます。

github.com

UnityMCPとは


UnityMCPは、このMCPプロトコルを使ってUnity Editorと連携するツールです。
DomainリロードやAsset Refreshなど、Unity Editor上での作業を自動化できます。
AIコーディングツールと連携することで、コードの生成だけでなく、
Unity Editor上での操作まで自動化できるようになります。

UnityのMPCでも一番有名だったため、こちらを利用しました。

github.com

1回目:やってどうだったか


Codex CLIは自分のコーディング環境とも別にセットアップし動くようにしたのですが、
モデルを揃えていても環境によって精度が違う(ABテストでもしてるのかな)という印象でした。

gitコマンドやghコマンドも命令すれば実行してくれるため、
多分AGENT.mdに詳細を書けばPRまで自動で出してくれるかもしれません。

実際に使ってみて、以下のような問題に遭遇しました。

  • 何故か書き込み権限があるのに読み取り権限しかないと勘違いして
    「書ける」と言っても「readonlyになっていて、編集できません」と言ってきたり
  • R3なのに頑なに存在しないIObservableを使おうとして変なコードを書いたり
  • gitの操作に詰まってlockファイルを直接削除したり
  • シーン上からComponent全Findしたり
  • MCPでアタッチして」と言ってもAwakeでGetComponentを書き始めたり

やりたい放題で、指示出しの精度にもよるのかもしれませんが、
コンテキストの理解が限定的で、デバッグに時間がかかるという課題もありました。
正直「多分自分で書いたほうが早かった……。」というのが率直な感想です。

一方で、「たたき台となるコードの作成や簡単で物量が多いコードにはよさそう」だと感じました。

2回目:複数ツールを組み合わせた改善試行


これを踏まえ更に使い易いようにするにはどうすればいいだろうと考え、次のようなツールを導入しました。

2回目で使ったツール


使ったツールは以下の通りです。

  • UnityMCP:1回目から引き続き使用
  • Copilot CLI:新たに追加
  • SpecKit:新たに追加
  • Context7:新たに追加
  • CodeRabbit: 新たに追加

Copilot CLI


Copilot CLIは、GitHubが提供する「GitHub Copilot」をターミナルから使うためのCLIツールです。
Codex CLIとは違いGithubが提供することもありGitHubのフローと非常に相性が良く使い勝手が良いです。
特に特徴的なのはコーディング エージェントのAI部分をどのモデルを選択するか設定可能な点です。
ChatGPTのCodexはもちろん、Claude Sonnetも利用可能です。
特にCludeCodeはそのまま使うとかなり高いですがColipotCLIを介して使うことでかなり安く使うことが出来ます。

今回はSonnet4.5をメインで使いました。

後CodexはTOMLファイルを変更しましたが、こちらはJsonです。
(CodexよりCludeCodeのが使い勝手は良かったです)

github.com

SpecKit


仕様書を元にコード生成を行うツールです。
ココでいう仕様書とはユーザー側で何度かコマンドで「このようなライブラリが使っている」だとか
「このような仕様でつくろうとしている」だとかを対話的に入力することで
リポジトリ内に仕様書を量産してくれてAIが見る専用のドキュメントとなります。
ここで最新のAPI仕様やドキュメントを提供することで、
AIが古いバージョンの書き方をしてしまう問題を軽減します。
公式ドキュメントや仕様書を参照しながらコードを生成するため、
より正確で最新の情報に基づいたコードが生成されます。

ただ難点としてはAIも必ずすべてのドキュメントに目を通してくれる訳ではないのでプロンプト内容は丁寧に考える必要はあります。

github.com

Context7


コンテキスト(文脈)を提供してコード生成の精度を上げるツールです。
簡単に言うとAI専用のドキュメントを提供するツール。
プロジェクトの全体構造や、他のクラスとの関係性などのコンテキスト情報を提供することで、
AIコーディングツールの文脈理解が向上します。
これにより、プロジェクト全体の設計方針に沿ったコードが生成されやすくなります。

context7.com

CodeRabbit


これは実際にエージェントを使っている間ではなく、コードレビューとして入れたツールです。
Github上でPRが作られた際に挙動し、内容についてレビューをしてくれるツールです。
正直今回使ったツールで一番満足度が高いツールでした。
コードレビューだけでなくPRを空で上げるとPRの内容まで詳細に作成し、
なおかつ、コードのフロー図や既存コードの懸念点などまであげてくれます、素晴らしい。
しかも、コードレビューに対して返信するとお返事も返ってくる。
〇〇だからこうしました→なるほど!ではこうしてみたら如何でしょうか!みたいな。

www.coderabbit.ai

2回目:やってどうだったか


これらのツールを組み合わせることで、
ほぼAIコーディングすることにしてみたところ、
実際に使ってみたところ、一通りコーディングできるようになりました。
あと一歩というところもありつつも使い方やプロンプトを調整することで使えるレベルになったように感じます。

改善された点


  • 古いバージョンの書き方が減った

SpecKitやContext7を利用することで、最新のAPI情報を提供できるようになり、
古いバージョンの書き方をすることがぐっと減りました。

これにより、後から修正する手間が大幅に減りました。

  • 自動化できる作業が増えた

ghコマンドを入れていれば、
自身でPRも作ってくれるし、OKといえばmergeもしてくれる、commit、pushもやってくれる
丁寧に説明を書けば、UnityのAsset/Refreshを叩き、コンソールのErrorが消えるまで一通りの修正はやってくれる
まさにフローが完結されたかのような感覚です。

新たに分かった問題点


  • Promptの書き方による問題

一方でちゃんと動くようになったことで、問題が顕在化しPromptの書き方によってはうまく動いてくれないことに気が付きました。
人間に命令するのと同じように感情に訴えかけるようなPromptや、このコードに至った経緯などを話すと、
回数を重ねるとauto-compact(プロンプトややり取りが多くなった際にコンパクトに要約する機能)があってもポンコツ化することが多々ありました。

具体的には、
返事をするだけで作業を何もしなかったり
出来る作業を「出来ません」と口答えしてきたり

実際のエージェントとやり取りを晒すとこんな感じです。

AI「すぐ作業します!」(何もやってない)

AIに任せるような抽象的なPromptは、
すでに記述済みの類似コードを多重に書いてしまったり、
クリティカルな部分のコーディングをそれっぽい理由をつけて(例えばモックとしましたなどいって)後回しにしてしまう

そのため、Promptを書く場合は
具体的で、誰が書いても同じようになるように簡潔に圧縮された内容を示すことが必要のようです。

駄目な例

チャット機能を作っていてApplication側は出来たのでViewを完成させて

正しい例

YYYYYYAsyncメソッドを作成せよ。 ApplicationのXXXXX.csにAIが返信を返すXXXXObservableとユーザーがAIにメッセージを渡すXXXXXXメソッドがある。 現在のXXXXXXView.csにはインプットフィールドのシリアライズフィールドとボタンのシリアライズフィールドとテキストフィールドのシリアライズフィールドが存在する。 ボタンを押下した際にインプットフィールドの内容をユーザーがAIにメッセージを渡すXXXXXXメソッドの引数に渡し、XXXXObservableをawaitし取得した文字列をテキストフィールドへ表示

またどうしてもAIとやり取りをしているとポンコツ化してくるので定期的にコンテキストリセットが必要です。
またイチから説明し直しになったりするのですが、しっかりとSpeckitで資料を残していれば過不足なく毎回同じように学習してくれます。
正直このあたりのフローはSpeckitのSpecの切り方同様粒度を考えて使う必要がありそうでまだまだ使いこなせていないです。
私が実際に使っていた内容としては毎回コードが出来たらAssets/Refreshを叩き数秒待ってコンソールを確認、エラーが出ている際はエラーを直すことや
一通り作業が終わったらコミット(metaあげ漏れチェックも)、pushしghコマンドでPR作成などです。

これだけでも大分使い勝手がよくなった感じがしました。

まとめ


と、このように 今回の開発を通じて、AIコーディングツールを使って見ましたが
まだまだ言ったとおりにしか作ってくれないし、まだまだだなと思う点も多いですが
初回の試行ではCodex CLI単体では「多分自分で書いたほうが早かった」と言える結果でしたが、
2回目では複数のツールを組み合わせることで、「使えなくはないかな」と思えるレベルにまで達したと感じはしています。

逆に言えばちゃんと言えば言ったとおりに作ってくれるという感覚もあります。
有用だなと思った点としてはエラー解決の時間を大幅に短縮できたという点です。
コーディング歴がそろそろ10年になる私ですが未だにエラーで何だっけとなることはあります。
そこでAIに任せると考えるより先に直してくれることもあり単純作業はAIだなと思いました。
またまっさらな状態からそれっぽいコードを書くのもAIは得意でたたき台となるコードの作成にはかなり有効だと感じました。
ただ既存のコードを変更となるとコード量が多くなるほどコンテキストを学ぶ必要があるためトンチンカンなコードも書くことが多かったり、
一つのクラスで書きたがったりするので注意が必要だなと思いました。
また他の注意点としてはそれっぽくみえても生成されたコードは必ず検証し、理解してから使わないととんでもないコードを書いていたりすることもありしっかり読む必要があることを感じたり、
権限の問題で自分でgitのロックを削除するなどの暴挙も見られました。

そのため、AIコーディングツールは、あくまで「コードを書いてくれる存在」ではなく、「開発を支援してくれる存在」として使うのが、最も効果的ではないかと思います。
人間側で細かい仕様や作りを策定しAIに説明、そしてAIが書いている間に次の仕様(プロンプト)を考えることで擬似的にタイピング速度を上げるかのような使い方ができそうです。

とはいえ、今現在AI周りの技術の進歩が早いので、近い未来もしかしたらUnityのような環境でもAIに大部分を任されられるような日が来るかも……しれません。

今後もAIには目が離せなさそうです。

以上、やまだたいしでした。

【前半】Unity じゃんけん × AI Phi4 miniで戦略じゃんけんゲーム作ってみた-AIの中身編

こんにちは、やまだたいし( https://twitter.com/OrotiYamatano )です。
AI、流行っていますよね。
とくにコンテンツ作成の分野では、AIを使うことが当たり前になりつつあります。

一方で、ゲーム内でAIを活用している事例は、まだそれほど多くありません。
少なくとも、既存ゲームに本格的に組み込まれているケースは限定的に感じます。

なぜゲーム内AIの導入は進んでいないのか。
その疑問を晴らすべく軽く使ってみたので、その検証結果をブログにまとめました

本記事は2025年ギルドアドベントカレンダーに投稿予定だったブログです。

後編はこちら orotiyamatano.hatenablog.com

目次


本記事の対象者


本記事は、以下のような読者を想定しています。

Unity を用いたゲーム・ツール開発の経験がある ローカル環境で AI を動かすことに関心がある

一方で、

商用利用を前提とした最適解 を提供するものではありません。

本記事の目的は、

Unity 上で SLM を実際に動かした場合、 どの程度の環境・労力で、どこまで出来るのか を、実装・失敗・所感をまとめた上でソレが誰かの役に立てばな、というぐらいの記事です。

今回やったこと:AIが心理戦を仕掛けてくる「じゃんけん」


実際問題AIってどのくらい使えるの?
ゲームに使えるの?
って思ったのが理由です。

では、なぜ「じゃんけん」なのか。 それはルールがこれ以上なくシンプルで、
かつ「相手の裏を読む」という心理戦の要素が強いからです。
AIの意思決定が結果に直結するため、AIの「賢さ」や「ボロ」が一番見えやすい題材だと判断しました。

記事は前半と後半の二篇となります。
前半はSLMを使ったゲームの実装についてAIの中身編と称しその中身の解説とか。
後半はAIコーディングツールを使ったことについて所感など。

まずは前編、じゃんけんゲームの実装についてお話しします。

動作環境


AIを使ったゲームを作るにあたり
以下の技術・ツール群を利用しました。

使用モデル

SLM:Phi-4 mini(ONNX)
推論ランタイム:ONNX Runtime GenAI

github.com

ハードウェア・開発環境

OS:Windows 11 64bit
CPU:AMD Ryzen 7 8845HS
メモリ:16GB
GPU:GeForce 4060 Laptop GPU(未使用)
Unity Editor:6000.0.62f1(途中でUnity6000.3に)
検証形態:Editor 上での動作確認

なぜ LLM ではなく SLM を選んだのか


今回の検証では、ChatGPT などの LLM(Large Language Model)は使用していません。
理由は、ゲーム内での利用を考えたときに現実的ではないと感じた点が複数あったためです。

具体的には、以下の点が気になりました。

  • 利用料金が高い
  • 速いものを使おうとすると、さらにお金がかかる ユーザーが遊べば遊ぶほど、開発者の財布からAPI利用料が消えていくモデルは、買い切り型ゲームや小規模な運営タイトルではリスクが大きすぎ
  • ネットワーク越しに AI を利用することへの不安
    (開発を任せるステークホルダーや、実際にゲームをプレイするユーザーの双方) 情報漏洩リスクやGPL汚染の問題(ほぼ同一の内容をコード内に生成しGPL汚染する)
  • 外部サービスに強く依存している感覚がある
    「外部サービスの仕様変更で、ある日突然キャラクターの性格が変わった」「サービス終了でゲームが動かなくなった」という事態は、開発者として避けたい。

性能の高い AI を使うこと自体は可能ですが、
その前提として、コスト面や挙動の面で不安定さを感じるサービスに依存する必要があるように思えました。

一方で、SLM(Small Language Model)であれば、

  • ローカル環境で動作させられる
  • 大規模な演算装置を必要としない

という特徴があり、
まず試す対象としては現実的ではないかと考えました。

もちろん SLM は万能ではありません。
そのため今回は、昔の AI と比べてどの程度使えるようになっているのか、
という点の検証も兼ねて、この選択をしています。

Phi-4 mini の選定理由


SLM を使う方針を決めたあと、次に考えたのがどのモデルを選ぶかです。

候補としては、Llama 系、Gemma、Qwen、Phi 系など、いくつか選択肢がありましたが、
今回はMicrosoftPhi-4 mini を選択しました。

選定理由は、

  • Microsoft 製であり、情報源が比較的明確であること
  • ONNX 形式のモデルが公式に提供されていること
  • C# / .NET での利用を前提とした情報が揃っていること

です。

理由は単純明快。「Microsoft製だから、C#/.NET周りの導線がしっかりしている」です。
AI業界はどうしてもPython中心に回りがちですが、ゲーム開発者としてはC#で使いたい。
Pythonの環境構築に頭を悩ませることなく、NuGetやライブラリを通じてスムーズにUnityへ組み込めること。
これが今回の検証において、何よりも優先すべき「手離れの良さ」でした。

また、Phiにもいくつかありますが
Phi-4 mini はパラメータ数が約 3.8B と、
SLM としてはやや大きめですが、

  • 家庭用端末で動かせること
  • CPU 実行を前提にできること

を考えると、
今回の検証目的にはちょうど良いサイズ感だと判断しました。

今回は使用モデルは CPU 実行版を利用しています。
GPU 実行版のモデルも利用可能ですが、将来的にスマートフォンや、
GPU リソースをグラフィックスに全振りしたいゲームへの組み込みを想定しています。

私は既存のAIも残りつつ、みなさんの周りには小さなツールとしてAIが残ると思っています。

企業で使うPCとみなさんが使うスマホのような位置づけでしょうか。
大きな企業では相変わらずAIを使うけれど、身の回りのタスクなどやちょっとした家具家電にAIが搭載されると思います。
そこで現実的なのがCPUってわけです。

ONNX Runtime GenAI 選定理由

Phi-4 mini を Unity 上で動かすにあたって、
次に考える必要があったのが 推論ランタイムの選定 です。

今回は、ONNX Runtime GenAI を使用しています。

Unity から C# で扱える形で、現実的に選択できるランタイムだったためです。

Phi-4 mini は ONNX 形式で提供されており、
ONNX Runtime を使えば、

  • モデル形式をそのまま利用できる
  • Python を介さずに C# から呼び出せる

という前提を崩さずに検証ができます。

Unity で AI を扱う場合、
外部プロセスを立てたり、
別言語のランタイムと常時通信する構成も考えられますが、
今回はそこまで複雑な構成を取りたくありませんでした。

あくまで、

  • Unity Editor 上で動かせること
  • 実装と検証を素早く回せること

を優先しています。

その点で ONNX Runtime GenAI は、

  • 生成系 AI 向けの API が用意されている
  • ONNX Runtime をベースにしており、情報が追いやすい

先述の通り、C#で完結させたかったのでランタイムも
今回の検証目的に合っていると判断し
ONNX Runtime GenAI を採用しています。

じゃんけんAIゲームについて


今回作成したのは、
AIと会話しながらじゃんけんを行うシンプルなゲームです。

いきなり複雑なゲームに AI を組み込むのではなく、

ルールが単純であること
勝敗が明確であること
AIの意思決定が結果に直結すること

を重視し、題材としてじゃんけんを選びました。

じゃんけんであれば、
プレイヤーの行動(グー・チョキ・パー)
勝敗の履歴
会話の流れ

といった情報を元に、
AIの振る舞いを観察しやすいと考えたためです。

ゲームの基本的な流れは以下の通りです。

ゲームの実装


プレイヤーが「じゃんけんの手」を出した場合

  1. 以下の情報を入力として、
    AI が次に出すじゃんけんの手を決定する

    • これまでの会話内容
    • ユーザーのメッセージの過激度
    • AI が保持しているモノローグ
  2. 勝敗結果を判定し、
    その結果を元に AI がモノローグを追加生成する

  3. 勝敗結果に応じて、
    ユーザーの過激度などの内部パラメータを更新する

  4. これまでの勝敗履歴、モノローグ、会話内容をまとめて
    AI に渡す

  5. AI が会話を生成する


プレイヤーが「メッセージ」を送った場合

  1. メッセージ内容を元に、
    AI がモノローグを追加生成する

  2. ユーザーの過激度などの内部パラメータを更新する

  3. AI が会話を生成する


じゃんけんの勝敗と会話は完全に独立しているわけではなく、
プロンプトの一部として組み込むことで、
結びついている構成になっています。

このように内部的に AI のモノローグ(心理状況)を別途持たせることで、
心理戦らしさを演出できるのではないかと考えました。

AIのコア部分実装


せっかくなので組み込み方法についても解説しておきます。
といっても組み込み自体は難しくなく、問題があるとするなら公式の資料が少し古くなっていることです。
根本は難しくなく基本的に元となるReferenceを読めば大体わかると思います。

github.com

まず始めにモデルとなるaiのコアであるPhi4 miniをインストールします。
たしか私はコチラからダウンロードしました。

azure.microsoft.com

CPUで動かすかGPUで動かすかなどもこのモデルの情報により決定されます。

NuGetForUnityなどを使いONNX Runtime GenAIを入れていきます。

www.nuget.org

入れることで諸々のパッケージがインストールされます。

なお、ONNX Runtime GenAI を NuGet 経由で導入した際、
Unity に同梱されている .NET 系 DLL と競合し、
エディタ起動時にエラーが発生しました。

当時は NuGet 側で導入された一部 DLL を削除することで回避できましたが、
具体的な DLL 名や API Compatibility Level までは記録していません。

本記事執筆時点では再検証していないため、
導入時には Unity のバージョンや環境差分にご注意ください。

実装の核となるのは、UniTaskを使って別スレッドで推論を回し、結果をパースする部分です。

using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks;
using Microsoft.ML.OnnxRuntimeGenAI;

namespace AiRockPaperScissors.Domain
{
    /// <summary>
    /// Phi による推論ロジックのみを提供するユーティリティクラス。
    /// Model や GeneratorParams の設定ポリシーは呼び出し側で管理する。
    /// </summary>
    public static class PhiExecutor
    {
        private static readonly string[] StopMarkers =
        {
            "<|end|>",
            "<|user|>",
            "<|system|>",
            "<|assistant|>",
        };

        private const int TailWindowSize = 128;

        internal sealed class InferenceResult
        {
            public InferenceResult(string text, int promptTokens, int generatedTokens)
            {
                Text = text;
                PromptTokens = promptTokens;
                GeneratedTokens = generatedTokens;
            }

            public string Text { get; }
            public int PromptTokens { get; }
            public int GeneratedTokens { get; }
            public int TotalTokens => PromptTokens + GeneratedTokens;
        }

        public static UniTask<string> InferFullAsync(Model model, string prompt, CancellationToken ct)
        {
            return InferFullAsync(
                model,
                prompt,
                ct,
                genParams =>
                {
                    genParams.SetSearchOption("max_length", 256);
                    genParams.SetSearchOption("min_length", 0);
                    genParams.SetSearchOption("temperature", 0.7f);
                    genParams.SetSearchOption("top_p", 0.9f);
                    genParams.SetSearchOption("repetition_penalty", 1.1f);
                },
                maxGeneratedTokensOverride: 64);
        }

        public static async UniTask<string> InferFullAsync(
            Model model,
            string prompt,
            CancellationToken ct,
            Action<GeneratorParams> configure,
            int? maxGeneratedTokensOverride = null,
            System.Threading.Channels.ChannelWriter<string> streamWriter = null)
        {
            var result = await InferFullWithStatsAsync(
                model,
                prompt,
                ct,
                configure,
                maxGeneratedTokensOverride,
                streamWriter);
            return result.Text;
        }

        internal static async UniTask<InferenceResult> InferFullWithStatsAsync(
            Model model,
            string prompt,
            CancellationToken ct,
            Action<GeneratorParams> configure,
            int? maxGeneratedTokensOverride = null,
            System.Threading.Channels.ChannelWriter<string> streamWriter = null)
        {
            return await UniTask.RunOnThreadPool(async () =>
            {
                Exception caughtException = null;

                try
                {
                    ct.ThrowIfCancellationRequested();

                    using var genParams = new GeneratorParams(model);
                    configure?.Invoke(genParams);

                    using var tokenizer = new Tokenizer(model);
                    using var tokenStream = tokenizer.CreateStream();
                    using var generator = new Generator(model, genParams);

                    using var sequences = tokenizer.Encode(prompt);
                    generator.AppendTokenSequences(sequences);

                    var promptTokens = generator.GetSequence(0).Length;
                    var generatedTokens = 0;
                    var maxGeneratedTokens = maxGeneratedTokensOverride.GetValueOrDefault(-1);
                    var generatedTextBuilder =
                        new StringBuilder(Math.Max(256, prompt.Length * 2));
                    var tailWindow = string.Empty;

                    while (true)
                    {
                        ct.ThrowIfCancellationRequested();

                        if (generator.IsDone())
                        {
                            return CreateInferenceResult(
                                generatedTextBuilder,
                                promptTokens,
                                generatedTokens);
                        }

                        generator.GenerateNextToken();

                        var generatedTokenIds = CopyTokens(generator);
                        if (generatedTokenIds.Length == 0)
                        {
                            return CreateInferenceResult(
                                generatedTextBuilder,
                                promptTokens,
                                generatedTokens);
                        }

                        foreach (var val in generatedTokenIds)
                        {
                            if (maxGeneratedTokens > 0 && generatedTokens >= maxGeneratedTokens)
                            {
                                return CreateInferenceResult(
                                    generatedTextBuilder,
                                    promptTokens,
                                    generatedTokens);
                            }

                            var decodedToken = tokenStream.Decode(val);
                            generatedTokens++;

                            generatedTextBuilder.Append(decodedToken);

                            if (streamWriter != null)
                            {
                                await streamWriter.WriteAsync(decodedToken, ct);
                            }

                            if (decodedToken.IndexOf('<') < 0)
                            {
                                continue;
                            }

                            tailWindow += decodedToken;
                            if (tailWindow.Length > TailWindowSize)
                            {
                                tailWindow = tailWindow[^TailWindowSize..];
                            }

                            if (StopMarkers.Any(mark => tailWindow.Contains(mark, StringComparison.Ordinal)))
                            {
                                return CreateInferenceResult(
                                    generatedTextBuilder,
                                    promptTokens,
                                    generatedTokens);
                            }
                        }
                    }
                }
                catch (Exception exception)
                {
                    caughtException = exception;
                    throw;
                }
                finally
                {
                    streamWriter?.TryComplete(caughtException);
                }
            }, cancellationToken: ct);
        }

        private static InferenceResult CreateInferenceResult(
            StringBuilder generatedTextBuilder,
            int promptTokens,
            int generatedTokens)
        {
            var text = generatedTextBuilder
                .ToString()
                .Replace("<|end|>", string.Empty)
                .Replace("<|user|>", string.Empty)
                .Replace("<|system|>", string.Empty)
                .Replace("<|assistant|>", string.Empty)
                .Trim();

            return new InferenceResult(text, promptTokens, generatedTokens);
        }

        private static int[] CopyTokens(Generator generator)
        {
            return generator.GetNextTokens().ToArray();
        }

        /// <summary>
        /// 指定されたテキストのトークン数を計測する。
        /// </summary>
        public static int CountTokens(Model model, string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                return 0;
            }

            using var tokenizer = new Tokenizer(model);
            using var sequences = tokenizer.Encode(text);
            using var genParams = new GeneratorParams(model);
            genParams.SetSearchOption("max_length", 256);
            using var generator = new Generator(model, genParams);
            generator.AppendTokenSequences(sequences);
            return generator.GetSequence(0).Length;
        }
    }
}

モデルの読み込みは StreamingAssets から行う

 var modelDir = $"{UnityEngine.Application.streamingAssetsPath}/Models/cpu-int4-rtn-block-32-acc-level-4/";
 _model = new Model(modelDir);

learn.microsoft.com

Phi系は公式にあるとおりプロンプト規則を文書化する必要があります。(公式の参照モデルカードはphi3だけどほぼ同じ)
このモデルの形式は Huggingface モデル カード に記載

このようにプロンプトの最後をチェックしなければ、ユーザーのセリフまで動的に生成してしまったり、プロンプトのシステムメッセージを誤読したりしてしまい精度が低くなる。

ちなみにこれが別スレッドで動かしているのはAIが思考中に画面が硬直化してしまうため。
その対応策。

ざっと出しましたが、簡単にいうと PhiExecutor.InferFullWithStatsAsync のprompt が
AIとしてのメッセージと過去のやり取りどっちも含まれた文字列群で、
下のgenParamsは現状のAIに対する設定値です。
じゃんけんの手を出してくださいのような命令プロンプトと過去のじゃんけん結果や、モノローグを考慮にじゃんけんの手を確定させる。

AI側がPaperなどと発言するのでその文字列をパースし該当の手を出したと確定させる。

という流れです。
正直しっかり作りきらず途中でうち切っていたりするので
トークンオーバーになったりしますが、とりあえず今回は検証用として許容し公開しました。

実際にじゃんけんするようすを動画でみてみましょう。

検証動画


youtu.be

検証の結果:SLMは組み込んでゲームに使えるのか?


で、結局どうだったかというと、正直「よくわからない!」というのが答えでした。

会話面がとにかく統一感がない。
グーを出したことを「岩手(Rockだから?)」と言ったり、パーを「ペーパー」と呼んだり日本語と英語が入り混じったような内容になっていました。
こうなると、AIが心理戦としてブラフを読んできたのか、それとも単に偶然そうなっただけなのかがさっぱり判別できません。
「戦略的に動いているな」という手応えは、残念ながら今回の検証では得られませんでした。

おそらく、プロンプトを英語に集約しきれなかったのが、
挙動の不安定さにそのまま出ちゃったかな、という感じです。

以前も言った通り、SLMに高度な意思決定を丸投げするのはまだ荷が重そうに感じました。
内部パラメータを更新して、それに連動する文章を裏で選ばせる……といった、
「思考ロジックの中核」として使うのが今のところは現実的かなと思いました。

とはいえ、組み込むことは可能でした。
GPUを使うことでパソコン等なら早い処理を見込めると思えるので、
今後このような使い方は増えていくのではないかなと感じさせてくれた実験内容になりました。

まとめ


AIを直接触るのは高コストだが、今後ローカルSLMは使うことがかなり増えてきそう。
今回は日本語特化モデルではなかったのと面白さを出すために何度も思考させ
CPUで動かしたため思考速度が遅くなったがGPUを使えば比較的に早く解答が出てくるはずだ
またプロンプト調整などするとハンターハンターのグリードアイランドのNPC並のCPUは作れそうだなぁと思いました。

次の記事はAIコーディングツールを使ったことについて所感です。
よろしくお願いします。

【備忘録】SSH接続のための公開鍵・秘密鍵作成法(ssh-keygen)

こんにちは、やまだたいし( https://twitter.com/OrotiYamatano )です。
GithubのアクセスやSSH接続をするため公開鍵、秘密鍵作る必要がありますが、
毎回どうやって作ろうと思うことが多いです。

そこで今回はこの際ちゃんと勉強しまとめておこうと思ったのでその備忘録、雑記です。

目次


そもそも公開鍵秘密鍵とは


暗号化の仕組み

公開鍵・秘密鍵非対称暗号化と呼ばれる暗号化技術の一種です。

  • 公開鍵(Public Key): 誰にでも公開できる鍵。相手がデータを暗号化したり、署名を検証するために使用する
  • 秘密鍵(Private Key): 自分だけが持つ秘密の鍵。受け取ったデータを復号したり、自分が送るデータに署名を行うために使用する

この2つの鍵は数学的に関連しており、片方で暗号化したデータはもう片方でのみ復号化できます。

SSH接続での役割

SSH接続では、この仕組みを使って安全な認証を行います。

  1. サーバー側: あなたの公開鍵を保存
  2. クライアント側: あなたの秘密鍵を使用
  3. 認証時: サーバーがチャレンジ(ランダムなデータ)を送信し、クライアントが秘密鍵で署名して応答し、サーバーが公開鍵でその署名を検証する

これにより、パスワードを使わずに安全な接続が可能になります。

今回はその代表的なSSHKeyの作り方として、ssh-keygenを利用し作成します。

動作環境


この記事では以下の環境が前提となります。

  • OS: Windows 11
  • シェル: PowerShellWindows Terminal推奨)
  • OpenSSH: Windows標準搭載版
  • 管理者権限: 基本的には不要(一部設定で必要)

ssh-keygenとは


ssh-keygenは、SSH(Secure Shell)で使用する公開鍵・秘密鍵ペアを生成するためのコマンドラインツールです。

主な機能

主な機能としては以下の通り。

  • 鍵ペアの生成: 公開鍵と秘密鍵のペアを作成
  • 鍵の変換: 異なる形式間での鍵の変換
  • 鍵の管理: パスフレーズの変更、鍵の指紋確認など
  • 認証用キーの作成: SSH接続用の認証キー生成

鍵のことなら、これですみそう。

歴史と進化

ssh-keygenは1995年頃から存在する長い歴史を持つコマンドでレガシーに思えるかも知れません。
しかし、現在も活発に開発・更新が続いています。

2025年10月にもFIDOトークンサポート、WebAuthn対応など最新機能を追加
などなど更新が続いています。

また7年前の2018年には、
デフォルトの鍵フォーマットがPEM形式からOpenSSH形式に変更されるという非常に大きな変更がありました。
このように、古いコマンドでありながら現在も最新のセキュリティ要件に対応し続けている重要なツールです。

SSH keyについて


鍵の種類と特徴

ssh-keygenで作成できる主な鍵の種類は以下の通り

種類 ビット数 特徴 推奨用途
RSA 2048以上(推奨4096) 広くサポートされている、長い歴史 レガシーシステム
Ed25519 固定256ビット 高速でセキュア、短いキー 新規作成時推奨
ECDSA 256/384/521ビット 軽量、楕円曲線暗号 組み込みシステム

どの鍵を選ぶべきか

Ed25519 を推奨

  • セキュリティが高く、処理速度も速い
    • Ed25519の優先使用: より安全で高速、2014年頃から本格採用
  • 鍵のサイズが小さく、管理しやすい
    • RSA鍵の最小ビット数: 2048ビット(推奨4096ビット)、以前は1024ビットが標準だった
  • 現在の最新システムで広くサポートされている
    • ECDSA鍵の注意: 一部のシステムでセキュリティ問題が報告されている

RSA を使う場合

  • 古いシステムや特定のサービスで Ed25519 がサポートされていない場合
  • 最低でも 2048ビット(推奨は 4096ビット)を使用する

事前準備(OpenSSHの確認)


ssh-keygenを使用する前に、
Windowsであれば標準で入っているはずですが
OpenSSHが正しくインストールされているか確認しましょう。

# OpenSSHクライアントのバージョン確認
ssh -V

# OpenSSHのインストール状況確認
Get-WindowsCapability -Online | Where-Object Name -like "OpenSSH*"

# ちなみにSSH関連サービス一覧を確認するにはこのコマンドを叩くと良いです
Get-Service *ssh*

ssh-keygenの使い方


基本的な使い方

ssh-keygen -t ed25519 -C "your_email@example.com"

オプションの説明

  • -t ed25519: 鍵の種類を指定(RSA、Ed25519、ECDSAなど)
  • -b 4096: 鍵のビット数を指定(RSAの場合、最低2048ビット推奨)
  • -C "email": コメント(通常はメールアドレス)を追加
  • -m PEM: 出力形式を指定(PEM形式で古いシステムと互換性を保つ)
  • -f filename: 出力ファイル名を指定

推奨コマンド

# Ed25519(推奨)
ssh-keygen -t ed25519 -C "your_email@example.com"

# RSA(レガシーシステム対応の場合)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

Windows PowerShellでの実際の手順

1.PowerShellを開く

  • Win + XWindows PowerShell (多分日本語環境ならカタカナでターミナルとあるはず)

2.鍵生成

ssh-keygen -t ed25519 -C "your_email@example.com"

3.保存場所の指定

Enter a file in which to save the key (C:\Users\YourUsername/.ssh/id_ed25519): [Enter]

Windowsでは C:\Users\YourUsername\.ssh\ に保存されます。

4.パスフレーズの設定

Enter passphrase (empty for no passphrase): [パスフレーズを入力]
Enter same passphrase again: [同じパスフレーズを再入力]

5.生成されたファイルの確認

# .sshディレクトリの内容を確認
ls C:\Users\$env:USERNAME\.ssh\

SSH Agentの活用

ssh-agentとは

ssh-agent秘密鍵パスフレーズを安全にキャッシュして、毎回の入力手間を省くツールです。

Windows PowerShellでの基本的な使い方

# SSH Agentを起動
Start-Service ssh-agent

# 秘密鍵をSSH Agentに追加
ssh-add C:\Users\$env:USERNAME\.ssh\id_ed25519

# 追加された鍵を確認
ssh-add -l

補足

Windowsでは、SSH Agentサービスを自動起動に設定できます(補足:Set-Service ssh-agent -StartupType Automatic)。
既存のサーバーに新しいSSH鍵を追加する場合、ssh-copy-idコマンドを使用します(補足:ssh-copy-id -i C:\Users\$env:USERNAME\.ssh\id_ed25519.pub username@server-ip)。

GitHubでの設定方法


1. 公開鍵の確認

# 公開鍵の内容を表示
Get-Content C:\Users\$env:USERNAME\.ssh\id_ed25519.pub

# クリップボードにコピー(Windows)
Get-Content C:\Users\$env:USERNAME\.ssh\id_ed25519.pub | Set-Clipboard

2. GitHubに公開鍵を登録

  1. GitHubにログイン
  2. 右上のプロフィールアイコン → Settings
  3. 左サイドバーの SSH and GPG keys
  4. New SSH key をクリック
  5. Title: わかりやすい名前(例:「Windows PC」)
  6. Key: 先ほどコピーした公開鍵を貼り付け
  7. Add SSH key をクリック

3. 接続テスト

必要であれば接続テストを行います。

ssh -T git@github.com

成功すると以下のメッセージが表示されます:

Hi username! You've successfully authenticated, but GitHub does not provide shell access.

PEM/OpenSSH互換性についてのTips


フォーマットの違いと互換性

OpenSSH形式 vs PEM形式

OpenSSH形式(現在のデフォルト) - 2018年8月以降のデフォルト - よりセキュアで効率的 - 新しいOpenSSHクライアントで推奨

PEM形式(レガシー) - 従来の標準形式 - 古いシステムとの互換性が高い - -m PEMオプションで指定可能

互換性チェック

# 鍵の形式を確認
ssh-keygen -l -f C:\Users\$env:USERNAME\.ssh\id_ed25519

# PEM形式で生成(古いシステム対応)
ssh-keygen -t rsa -b 4096 -m PEM -C "your_email@example.com"

Windows固有の設定とTips

Windows Defenderとの連携

Windows DefenderがSSH鍵を安全なファイルとして認識するよう設定:

# .sshディレクトリを除外リストに追加(必要に応じて)
Add-MpPreference -ExclusionPath "C:\Users\$env:USERNAME\.ssh"

気をつけたいセキュリティ


鍵の管理

  1. パスフレーズを必ず設定する
  2. 定期的に鍵を更新する(推奨:1-2年)
  3. 不要になった鍵は削除する
  4. 秘密鍵は絶対に他人と共有しない
  5. 鍵の適切な権限設定: Windowsではicaclsコマンドでディレクトリ権限を設定

鍵の保管場所

Windows固有の注意点

# .sshディレクトリの権限設定(必要に応じて)
icacls "C:\Users\$env:USERNAME\.ssh" /inheritance:r /grant:r "$env:USERNAME:(OI)(CI)F"

まとめ


Windows環境でもOpenSSHが標準搭載されているため、Linux/macOSと同じようにSSH鍵を使用できます。
SSH鍵を使うことで、パスワードよりも安全で便利な認証が可能になります。特にGitHubでの開発では必須の技術なので、この機会にしっかりと身につけておきたいですね。


参考リンク

【雑記】投資はいいぞの話

こんにちは、やまだたいし(@OrotiYamatano)です。

今回はUnityや設計ではなく、投資について語ります。
なんとなくです。
対象ユーザーは微塵も投資したことがなかった人、投資初心者です。
中堅者、上級者は下がってもろて。

目次


なぜ投資の話?


この記事を書くに至った理由がいくつかあります。

  • 投資詐欺の急増

近年投資詐欺が増えています。
2024年、SNS型投資詐欺の認知件数は2,797件、被害額は344.1億円でいずれも前年の約2倍(※2024年上期=1〜6月)。被害総額の8割超が投資名目という異常事態らしいです。

参照:警視庁 特殊詐欺及びSNS型投資・ロマンス詐欺の 認知・検挙状況等について

また巷には怪しいセミナー系の動画やサロンなども横行しています。
銀行や投資会社の広告もあります。
この状態は良くないと私は考えています。そのため、記事を書こうと思ったのがひとつ。

  • NISA(少額投資 非課税制度)への関心が高まる

金融庁調査によれば、2025年3月末時点でNISA口座は2,647万口座、累計買付額59兆円と過去最高を更新したらしいです。
さらに同庁は「成人の48%が新NISAに関心あり」と発表しています。

参照:金融庁 NISA口座の 利用状況に関する調査結果の公表について

だったら儲けてる私が記事書けば需要あるんじゃね?と思ったのがもうひとつ。

  • お金の話が好きだから

ゲスい話、お金の話好きでしょ?
私も好きです。しかし知り合いに投資やお金の話をすると、こいつ大丈夫か?
と思われること待ったなし、お金の話はセンシティブですから触れたがらない人も多いです。
盛り上がりたいですが、盛り上がる相手がいないので鬱憤晴らしの自己満足ですね。はい。

と、まぁ3つの理由から記事を書くに至りました。

私の投資ヒストリー


とはいえ微塵も儲けてないやつから話を受けてもありがたみはありません。

投資成績はというと2021年の夏から初めて投資金額+60%といった感じです。
とはいえ巷では5年で10倍にみたいな話もあるのであまりいい成績には思えないですよね?
中途半端にさえ思えます。

まぁ酸いも甘いも噛み分けて来たので経歴を書きます。

ちなみに本格的に始めたのが2022年の冬でした。
とはいえ投資した額は結構多くて、1000万以上の利益を得ています。

簡単に私のヒストリーを書きます。

年月 投資額 イベント 評価損益
2021/8 30万円 旧NISAでインデックス初購入 その後 +20%
2022/1 +1,500万 相続資金の一部を追加投資 -
2022/5 +200万 追加 ロシア戦争で▲400万
2023/4 +200万 コロナ逆張り成功 +200万
割愛 割愛
2025/7 現在 秘密 年リターンは約+13%はある +60%

2021年夏30万円ほどで投資をスタートさせました。
最初はインデックス投資とかだった気がします。
旧NISAでちょいちょい投資。
他にもいくつか購入したりしました。

2022年はじめ1,500万円を追加投資しました。
なんでいきなり!と思われる方もいるでしょう。 2021年夏から2022年はじめまでに20%の益があり、手堅く伸びているように思えたことがありました。祖父が亡くなり予想外の相続を受け取り、泡銭は身につかないと考えました。遊ばせておくより一括投資かなと余剰資金だったこともあり、後押ししました。
一気に投資しました。

よく覚えています。その後ロシアがウクライナ開戦しました。
みるみるうちにお金が溶けました。

調子はよくなかったのですが伸びると思い、更に2022年5月頃に200万円ほど追加投資しました。
ロシア関連がさらに悪化しマイナス400万円でした。アホです。

でもあまり心配していませんでした。
泡銭ですし、最初の数カ月ドルコスト平均法の投資ではマイナスに転じた局面があってもプラスに戻ってくることを体感していたのと、安ければ安く買えるというマインドも手に入れていました。
ま〜そもそもこんなギャンブラーな性格でなければゲームプログラマーなんかにならないでしょう。

そして続くコロナショックです。
正直世界はコロナから復活すると私は信じて疑いませんでした。
マイナス200万円まで戻ってきており、2023年4月、更に200万円追加投資です。

そこで逆張りに成功しました。
一気にプラスに転じプラス200万円。

普段無表情で怒っているように見られるそんな私ですが、思わず小踊りしました。……嘘です。
まあ心が躍ったのは本当です。

その後、今では下がることはあるものの基本的にはプラスを維持し、
徐々に複利的に伸びています。大体年プラス13%は堅い感じです。

これが私の投資のリアルなヒストリーです。
他にもAI投資に手を出してみたり、個別株に手を出したり仮想通貨を10万円だけ買ってみたり色々していましたが、結果投資信託を主軸に据えています。

あくまで本記事はどこぞのあやしいセミナーとは無関係ですし、
完全自己資金です。

ほか記事を見てわかるようにただのゲームプログラマーです。
一切収益目的ではないので安心してご覧ください。
あ、広告アフィリエイトだけはつけています。はてなプロ課金分の料金を稼ぐためですので悪しからず……。

本文


投資をしよう


投資を私は勧めています。
投資ってリスクあるんでしょ?なんか怖い。
めっちゃ分かる。でも、私はインフレも怖い。

預金をしているとお金は利息がつきますが微々たるものです。
日本の消費者物価指数(CPI)は 2022 年以降ずっと +2%超。直近2025年5月は +3.7%らしい です。
そして、日銀は3年間インフレ率2%超が続くと言ってます。 参照元:日本銀行 最近の金融経済情勢と金融政策運営

つまり貯蓄で0.5%の利息はつく可能性がありますが、なにもしないと2%は少なくとも価値が下がるので、
実質毎年1.5%の価値を失うことになります。
それが貯蓄の怖さです。

しかし、考えてください。それでおさまるでしょうか?

インフレというのは日本円が欲しいという人の割合に対して、実際にあるお金の量に対しての割合で決まってきます。

ご存知日本人口は急速に減ってます。日本円は海外需要もありますが、基本的には日本人が貯蓄として大半を所持しています。

そして相続により一人あたりの貯蓄量は増えますがそれと同時にインフレが進みます。
インフレはいつかの条件が整うと起こるとされていますが、その1つとしてマネーストック統計というものがあります。
それによると皆の所持するお金の平均(M) + 市場で回ってるお金(V)が実質生産(Y)を上回るとインフレが起きるとのこと。
またこれ以外のシナリオとして円が売られるとインフレするという局面もあります。

投資家が日本円を今買っているのは日本円が変動に強い通貨だからというのがあります。
しかし、近年日本の国籍の格付けが徐々に変わろうとしています。
そうなると買われなくなってきます。

このまま貯蓄しておいてよいのでしょうか?

── ここまで読んで「円の価値が目減りするのは分かった。じゃあどう防ぐの?」という疑問が出ます。

若者は投資信託(インデックスファンド)をやれ!


本ブログで伝えたいことはこれだけです。
私がこれ教えたところで私にはメリットはありませんが、正直皆もったいないことしてるなぁと思ったので書いてます。

ところで、なぜ投資の中でも投資信託なのかと感じた方もいるでしょう。

話します。

その前に私の運用ポリシーについて箇条書きしておきます。

・生活費1年分は円預金で死守
・借入投資は絶対にしない
・あくまで失っても困らない余剰金で行う
・資産形成の核は「全世界株/S&P500インデックス」
・暴落期は"買い増しセール"と割り切り、売らない

まず投資信託とは


まぁこの記事は投資初心者or全くしてこなかった人が見ることを想定しているので焦らず用語解説から。

投資信託(ファンド)とは、 「投資家から集めたお金をひとつの大きなプールにまとめ、運用の専門家が株・債券・不動産などに分散投資してくれる仕組み」 です。

つまり皆から金を集めていい感じに投資してくれる会社に委託できる仕組み!

ちなみに、
販売会社(証券会社や銀行)で買い付け、
委託会社(運用会社)がポートフォリオを組み、
受託会社(信託銀行)が資産を分別管理する三者分業が日本のスタンダードらしい。

少額で世界中のものに投資できる1回でいろんなものを平均的に分散できる。
手数料はかかるけれど、ネット証券などではめっちゃ格安。

インデックスファンドとは


世界にはインデックスと呼ばれる市場平均値が存在します。
日本でいうと朝のニュースで日経平均という言葉を聞いたことがある方もいるでしょう。
日経平均TOPIX・S&P500などなど存在します。
で、インデックスファンドとは対象指数の構成銘柄を「ほぼそのまま」組み入れたり、統計的な最適化でほぼ同じ値動きになる組み合わせを作ったりして運用します。
たとえばS&P500連動型なら米大型株500社に、全世界株なら50か国以上・3,000銘柄超に一括投資しているイメージです。
なので、この商品は証券会社の変な意思が介在することもありません。
運用コストも低い。

投資の中で投資信託(のインデックスファンド)を勧める理由


1.NISAの税制メリットが強烈

年360万円・生涯1,800万円まで、売却益と分配金が非課税。
正直このメリットはでかい。
通常投資は利益に対して税率は20.315 %取られてしまう。
つまり1800万から数百万の利益が出ても非課税ってことだ嬉しい。
普通に働いたって所得税やらかかるのに!

2.インデックス投資=世界経済の平均点を買う

経済は長期的にはインフレを超えるペースで成長。
投資信託複利で雪だるま式に増える。
長期では6~8割のアクティブファンド(指標に頼らない運用会社の意思によって作られるファンド銘柄)が指数に負ける らしい。
つまり投資のプロが立てた計画をだいたい上回るのだ!

3.手間がほぼゼロ

株価監視や難解な決算分析は不要。

4.インフレに強い

(まぁこれはほぼ2の深堀りですが……)

円はこの10年でドルに対して約19.2%下落。
アメリカはここ10年でトータル+35.1%のインフレを起こしているものの、
経済成長率(S&P)はそれを上回り+254%となっています。

預金はインフレに追従しませんがS&Pなどの組み込み会社は変わるものの指標、もといインデックスは経済成長そのものに追従します。

取らぬ狸のなんとやらですが、
つまり10年前にインデックスファンドへ1万円投資していたと仮定するなら、約4.4〜5.0万円になっていたということになります。
(ChatGPTによる計算)
100万円なら440〜500万円、いいですね。

よく考えてください。
ここ数年コロナやロシアの戦争がありましたが、ここまでのプラスに転じるのです。
安定したら更に差が出ます。

つまり……

答えは単純で、
円だけに頼らず、円より速く伸びる世界の成長に乗りインフレ率が2〜3%でも、
そのある程度上澄みで実質的な購買力が増える。これこそがインデックス投資の真骨頂。
円の希薄化に対抗する最もシンプルな盾が、インデックスファンド+NISAというわけです。

ほか投資手段との比較


あくまで個人の意見ですが、比較していきます。

個別株

メリット:大化けの可能性、優待
デメリット:情報・時間コスト大、失敗で致命傷

張り付いてやるのが好きならいいけど、
Fireとか考えてる人は働きたくないのだろうし
張り付いたり勉強が必要な個別株はオススメできない……
少なくとも投機的じゃなく投資的ならあり。
優待を含めると利回りが良いものもいくつかあるので、それは買っても良いでしょう。
優待出せるような会社は安定している証拠でもあるのでジワ伸びも見込める。

債権

メリット:元本安定
デメリット:利回り低くインフレ負けしやすい

日本の国債なら為替レートに影響されることがない上に日本円保証です。
だけど、ぶっちゃけ買うメリットが少なすぎる……。
海外国籍は結構利回りがあるので、先見の明があれば数年で数倍になる可能性もあります。
10万円だけブラジル・レアルかったけど、今2.5倍になってたので、もっと買えばよかったかなぁと思ったり。
同時期にでた私が買わなかった別の国の債権では暴落してるもあるのですけど。

金/プラチナ

メリット:価値保存
デメリット:利息ゼロ、横ばい期間が長い

いざという世界での恐慌などが起こったときに
絶対的な価値を発揮するのでリスク対策用資産としてはいいかも?

暗号資産

メリット:短期爆益の可能性、流行の最先端
デメリット:法整備遅れ(雑所得扱い)

日本で買うメリットがなさすぎる。
雑所得扱いなので税率も高く嬉しくない。
日本の法律が変わったら買うのも選択肢の1つです。

不動産

メリット:現物資産になる、自分で改良できる
デメリット:維持費・空室・複利効果なし

DIYが得意とか宅建を持っている人はコスト削減ができるので、買っても良いでしょう。
人によっては海外資産より良いという人もいそう。
日本だと少子化が進んでいるので都市集中で都市部の価格が上る可能性はありそう。
地方は下がるので要注意……。

海外不動産は安いと思ったら現地でやばい工事とかあったりすることもあるし買うには怖い感じ。
まだ個別株ETF買ってたほうがマシ。

月1万円から始める実践ガイド


・ネット証券口座を開き、新 NISA を設定
・全世界株(MSCI ACWI)か S&P500 に連動する低コスト投信を選択
・毎月1万円自動積立。相場が下がっても粛々と続ける

で、儲かるのかどうかですが
仮に過去5年積立してたとしましょう。

今が2025/7なので、2020/7からですね。

ChatGPT調べですが……
S&P500 はコロナ底(2020/3/23)2,237→ 2025/7/18 終値 6,296 と 約+180%。
暴落期(22 年初)の買い増しが平均取得単価を押し上げ、トータルリターンを底上げします。

60か月で毎月1万円積立(計60万)
仮にeMAXIS Slim 米国株式を買ってたとしましょう。

結果
評価額およそ約106万円(+46 万円/+77 %)
※分配再投資・税引前。
(ドル円差考慮無し)

すごい儲かるわけじゃないですがただ貯蓄してるのと比べればマシです。
手元に46万プラスであれば貴方は何に使いますか?

ちなみに投資の神様と言われるウォーレン・バフェットでさえ年利率20%ほどらしいです。
都合のいい話はリスクも大きいので無難にいきましょう。

毎月1万円自動積立の理由

効率を言うとインデックスファンドに投資するならドルコスト平均法ではなく一括投資でも問題はないことが多いです。
上がり下がりはあるものの複利を考えるとだいたいのケースで、はじめに投資していたほうがお得でもあります。

じゃあなんで私が毎月1万円自動積立を最初に勧めるのかというと
一番はメンタルを鍛えることに繋がるからです。

インデックスファンドは世界情勢に大きく揺り動かされます。
最初のうちはなんで下がったの!?と驚くことばかりでしょう。

例えばアメリカの雇用統計の集計結果がダイレクトに反映されます。
市場の予測に対してどうだったかで決まるので数値が良くても予測に届かない場合は下にブレます。
日本ではアメリカの雇用統計が昨年と比べてどうだったかは報道しますが、市場の予測に対してどうであったかなどは一部の報道機関しか触れません。
しかも日本はアメリカに比べて遅れて報道するので、わかんないけど値下がりした!?と動揺することもあるかと思います。

私はVR関連のインデックスファンドを買っているのですがポートフォリオがAI関連と重複する部分が多いためDeepSeekショックのときは何が原因か分からず困惑しました。

しかし積立投資なら最初のうちは痛手が少ないので心は揺り動きません。
何度かそういった局面に立ち会えば、そういった局面が訪れても安く買えるチャンス、
上がるから動じなくてもいいと思えるメンタルがつきます。

また世界情勢に対してちゃんと知っていこうと思えるので勉強にもなります。

そのようなメンタルがついてから多額の投資を始めても遅くはないはずです。
また、今は預金が少なくてやる意味があるのかと疑問に感じる方もいらっしゃるでしょう。
それでも、貯蓄して眠らせておくよりは良いですし、そうやって投資の知識を徐々につけておけば、いざ多額のお金が入った際に投資という選択肢が自然と出てくるメリットもあります。

ついでに……Q.若者じゃないんですけど


A.現在の資産次第で投資はしても良いでしょう

やはり時間という資源を使ってしまっている場合、億などを目指すには複利的に伸びていく投資は厳しいものがあります。
利益が大きく伸びるのは7,000万円あたりからと言われており、棚ぼた的に資産が手に入らない限り難しいです。
それこそ、30年や50年といった長い期間が必要です。
すでに資産がある方なら投資しつつ資産の減りを遅くしながら、配当が多い銘柄で毎月の利益を得ると良さそうです。
配当がある銘柄は残存価格が減りやすいですが、維持されることも少なくありません。
使い切るつもりで投資するのも良いでしょう。

チャレンジ精神のある方は、個別株で一発逆転を狙うのも選択肢です。
2015~2018年あたりは激伸びした株がいくつかありました。
実際に、300万円から5年で資産を1億円に増やしたケースや、少額から億り人になった事例も日本で見られます。

ある程度まで資産を集中して増やした後は、インデックスファンドへ資産を組み換えて元本を確保しつつ利益を享受する方法もあります。

ぜひ始めてみてはいかがでしょうか?

まとめ


詐欺急増&NISA熱&インフレという時代こそ正しい知識が必須。
都合のいい話はないです。
手堅く凡人の手法でやりましょう。

あーあ、個別株バク当たりしないかな~
以上、やまだたいしでした。

ゲーム開発者のための実践的設計 #2

こんにちは、やまだたいし(@OrotiYamatano)です。
今回は「設計に使える本」というテーマで、設計を考えるにあたり参考になる本を紹介します。

目次


はじめに


前回の記事では、なぜ設計が必要なのかについて説明しました。
今回は、その設計の基本となる考え方ではなく設計に使える本というテーマの記事になります。
というのも私は業務傍らで執筆しており不定期に記事を更新しているため手っ取り早く学びたいという方には
直接本を読んでいただいたほうが早いからです。
今後更新する内容もこれらの本に準拠した形の執筆になります。

前回の記事↓

orotiyamatano.hatenablog.com

おすすめの本


私が業務で触れるにあたって、後輩にどのような本を読めばいいですかと聞かれることもあるため、
学習パスとして理解しやすく、その中でも体系的で読みやすいと思う順番に並べて紹介しようと思います。
言語については特に定めてませんが、C++JavaC#あたりになっています。
基本的にはオブジェクト指向パラダイム前提です。

1. Tidy First? ―個人で実践する経験主義的ソフトウェア設計


Tidy First? ―個人で実践する経験主義的ソフトウェア設計 | Kent Beck, 吉羽 龍太郎, 永瀬 美穂, 細澤 あゆみ |本 | 通販 | Amazon

Kent Beck著の「Tidy First?」は、個人開発者向けの実践的な設計手法を解説した本です。
この本は初心者でも読みやすく三部は上級者にもおすすめできる内容になっています。
何を捨てて何を取り入れるかその考え方を取り入れるには一番の本だと思います。

2. リーダブルコード


https://amzn.asia/d/6nNojcY

Dustin Boswell、Trevor Foucher著の「リーダブルコード」は、コードの可読性を高めるための実践的なガイドブックです。
この本は初心者向けでどのようなコードが読みやすいかということに重点においた本です。
基本的にこの考えを主軸に様々なコーディングをしていけばハズレはないでしょう。
少し古い本ではあるのですがまだまだ使える教本です。
GOTOは使うな

3. 現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法


https://amzn.asia/d/hVn7lVU

増田亨著の「現場で役立つシステム設計の原則」は、実践的なオブジェクト指向設計を解説した本です。
この本は読みやすさを主軸に設計の基礎を教えてくれます。
ゲームの話ではないですが実務に近い話を例により実践的な内容を解説しているので、
現場や今まさにコーディングをしている人には優しい本だと思います。

また設計の基礎であるSOLID原則を教えてくれます。

4. Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)


https://amzn.asia/d/ih69ZvO

Robert C. Martin著の「クリーンアーキテクチャ」は、ソフトウェアアーキテクチャの設計原則を解説した本です。
この本は上の本でも解説されているSOLID原則から始まり、なぜ設計をこうするのかという根本的な考え方を学べる本です。
クリーンシリーズはボブおじさんが書いている本ですが、その中でも一番人気がある本だと思います。
とはいえ、この本の後半部分に関しては賛否両論あり、後半部分に関してはそういう考え方もあるのかぐらいにとどめて置くと良いでしょう。

5. オブジェクト指向における再利用のためのデザインパターン


https://amzn.asia/d/7sFQkyb

エリック・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディース著の「オブジェクト指向における再利用のためのデザインパターン」は、
デザインパターンの原典とも言える本です。
この本はGoFGang of Four)の本として知られており、23のデザインパターンを解説しています。
設計といえばこの本と言われるぐらい原典です。
かなり古い本ではありますが、デザインパターンという設計の議論の中心となる部分を作り出した本なので学ぶべきところが多いです。

実際に実装すると同じクラス構造になるパターンもあったりします。
ここで生きてくるのが先程のクリーンアーキテクチャの知識で、なぜこのような構造にするのかというのを念頭に読んでみると良いかもしれません。
また現代においては設計として、ふさわしくないとされている部分もあり、どこがふさわしくないと言われているのか考えながら読んで
Webで他の人の意見を読んでみるとより考えが習熟されると思います。
現代ではそもそも言語の仕組み自体に取り込まれている部分もあり、意識する必要がない箇所もあったりします。

勉強するにはちょうどいい本です。
ただし、この本は実装例がC++で書かれているため、C++を理解していないと読みにくいかもしれません。
その場合は、「増補改訂版 Java言語で学ぶデザインパターン入門」を読んでから、
この本に挑戦することをお勧めします。

6. テスト駆動開発


ここまでくれば後の読む順番は自由な気がします。

https://amzn.asia/d/9BvCrpB

Kent Beck著の「テスト駆動開発」は、TDDの実践方法を解説した本です。
Kent BeckさんはTidy First?でもあげられましたが、Tidy First?より古い本です。
これは設計に関する本ではありませんが、テストという概念を学ぶことで、
疎結合なコードとはなにかをより具体的に学ぶことが出来ます。

AIの時代が来ましたがテストの概念はより一層重要視されるようになってきているのを感じるのでぜひご一読を。
また翻訳者である和田さんことt_wadaさんは有名でテストについて登壇されていたりするので、
ネットで探して読んでみるとより理解が深まると思います。

テスト書いてないとかお前それ @t_wada の前でも同じ事言えんの?

7. レガシーコード改善ガイド


https://amzn.asia/d/c7WdxTm

Michael Feathers著の「レガシーコード改善ガイド」は、既存コードの改善手法を解説した本です。
この本は実践的に古臭くなったコードを治すための技術書です。
内容は快活などではなく、血みどろにまみれた泥臭い内容ではあります。
でも、どんなひどい現場でも参考になるようなノウハウや妥協点が詰め込まれています。
レガシーコードと戦う貴方のお供にどうぞ。

8. エリック・エヴァンスのドメイン駆動設計


https://amzn.asia/d/7Q0XkdG

Eric Evans著の「エリック・エヴァンスのドメイン駆動設計」はエリック・エヴァンスのドメイン駆動設計を解説した本です。
正直この本は翻訳も微妙で非常に読みづらいので、次で紹介するドメイン駆動設計入門を読んでからでもいいかもしれません。
しかしながらこの本を先に上げる理由はあり、小手先の技術ではなく考え方や筆者があまり自信がない様子も含めて
この本の内容を本当の意味で理解してほしいからです。

これに続くドメインアーキテクチャの本では実践的に取り組む内容を解説された本が多いのですが、
どうしても小手先だけの内容になってしまっていたり、そもそもやりきってしまう必要のない箇所まで実践内容に含めてしまっているように感じます。

そのためあえてこの本をバイアスが掛かる前に読んでほしいのです。

9. ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本


https://amzn.asia/d/gwuczmO

成瀬允著の「ドメイン駆動設計入門 ボトムアップでわかる!」は、実践的なドメイン駆動設計を解説した本です。
ドメイン駆動開発をより実践的に学べる本でエリック・エヴァンスのドメイン駆動設計に挫折した人がよく読みます。
冗談です。ドメイン駆動設計のわかりにくい箇所をより分かりやすく学べるのでオススメです。

10. ゲームエンジンアーキテクチャ 第2版


https://amzn.asia/d/9xcSHFW

Jason Gregory著の「ゲームエンジンアーキテクチャ 第2版」は、ゲームエンジンの設計を解説した本です。
これはどちらかというとゲームの実装なのですが、ゲームでは実際にこのような構造で実装されることが多く設計としても十分に参考になると思ったので紹介。

本の選び方


さまざまな本を紹介してきましたが、いい本の探し方はいくつかあります。
・ロングセラーかどうか
10 年生き残っている技術書は “現場でリピート買い” される価値がある証拠です。
買っていて損はないでしょう。
また、出版社がそういった技術系に強い良書を刊行しているか知っておくのも良いかもしれません。
オライリーはハズレが少ない。

・コミュニティの温度感を確認
尊敬するプログラマーやエンジニアをツイッターやQiita,Zennさまざまなところで探し、コミュニティの温度感を知っておくといい本を逃さずにすみます。

・著者の実績をチェック
無名のエンジニアや名ばかりの人が本を書かれることも少なくないです。
変にあおりが入っていたりするなら注意しましょう。

まとめ


とりあえず、いくつかの本をあげてみましたが、他にも良書はたくさんあります。
例えば、.NETのクラスライブラリ設計やそもそもの言語の基礎を知るEffective C# 6.0/7.0、なぜ依存を注入するのか DIの原理・原則とパターン、
などなど上げ始めるとキリがありません。
とはいえレベルに合わせた本というのは難しいと思います。
私の思う良い学習パスは上記になるのでぜひ参考にしてみてください。

以上、やまだたいしでした。

外部からの掲載依頼について当ブログの基本方針

こんにちは、やまだたいし( https://twitter.com/OrotiYamatano )です。
コチラの記事では、外部からの掲載依頼について当ブログの基本方針をお伝えします。

目次


はじめに

これまで多数の企業様や代理店様から記事掲載のご相談を頂戴しておりましたが、
当ブログでは原則として「ご依頼いただいた記事の掲載」は行わない旨の表明記事です。
本記事では、その理由と今後のご理解のお願いをまとめています。


掲載依頼の受付について

当ブログでは以下のいずれの場合も、企業様からの有償・無償問わず「記事掲載のご依頼」は基本お断りしております。
基本的には本ブログはグーグル広告の公式アフィリエイトしか利用いたしません。

  • ASP案件やアフィリエイト案件のご紹介依頼
  • プレスリリースや広告記事の執筆依頼
  • 外部スポンサーによる連載企画のご提案

また、しかし例外的に ある程度の交流がある方からのご相談については、個別に前向きに検討させていただきます

掲載を検討するケースと理由

当ブログで掲載を検討するのは、以下のケースに限ります:

必須

  • ガイドラインへの準拠:当ブログの記事構成や編集方針に沿った原稿をご提供いただけるため

推奨

  • 交流実績のある方:過去のやり取りや共同作業を通じて信頼関係が構築されているため
  • テーマとの高い親和性:ゲーム開発やサブカルチャー、個人の日記と強く関連するコンテンツであるため

例外

  • 私が面白いと思ったもの:また当ブログに掲載を考慮するにあたって面白い!と思ったものは例外的に推奨理由を無視して掲載する可能性はあります。

これらの条件を満たす場合に限り、個別にご相談を承りますが基本的には掲載しないものとご容赦ください。


掲載しない理由

当ブログでご依頼内容を掲載しない主な理由は以下のとおりです:

  1. 読者信頼の担保
    当ブログは個人の日記と技術情報を融合したスタイルを特徴としており、商業的な広告記事が混在すると読者の信頼を損ねるおそれがあります。

  2. ブログの性質との不一致
    「YAMADA TAISHI’s diary」は私個人の日記であったり、ゲーム開発の知見を綴る場所であり、外部案件の掲載はテーマの一貫性とパーソナルな視点を損なう可能性があります。

  3. 運営リソースの最適化
    記事の取材・確認・修正には時間を要し、限られた制作リソースを本来のコンテンツ作成に集中できなくなります。


今後のご理解のお願い

  • ブログ運営方針に変更があった場合は、本記事を更新のうえ改めてお知らせいたします。
  • 技術的なご質問やフィードバックは、コメント欄やTwitterでお気軽にお寄せください。
  • 正式な問い合わせは右側のフォームよりお願いします。

まとめ

当ブログでは外部からの掲載依頼は原則お断りしています。
既に交流実績のある方のご相談のみ個別に前向きに検討する場合があります。

以上、やまだたいしでした。

【後編】UnityのAddressableを個人制作で使いこなす

こんにちは、やまだたいし(https://twitter.com/OrotiYamatano)です。
コチラの記事の後編です。

orotiyamatano.hatenablog.com

目次


前回まで


前回はAddressableの概要とインストール、
AddressableAssetSettingsの設定一覧を確認しました。

今回は


AddressableAssetSettingsの実践的な設定から、実際のアセットの準備、ロード処理の実装、そしてビルドまでを解説します。
前回から結構日付が立ってしまったので2.2.2のバージョンを使うことにします。

前回と同様な設定の箇所は割愛します。

AddressableAssetSettingsの実践的な設定


まずDefault Local Groupのデフォルト設定を直接変更します。

Build & Load Paths


ローカルロードをしたいのでLocalを選択します。
リモートの場合はCDNなどのサーバーを用意する必要がありますが、個人開発では基本的にローカルで十分です。

AddressableAssetSettingsの基本設定


  • Profile In Use
    こちらはまだデフォルトしかないはずです。そのままデフォルトを選択します。
    今回はローカルを使うのでBuilt-Inということになります。

  • Diagnostics
    Log Runtime Exceptionsにチェックを付けたままにします。
    開発中はログを取得できた方が問題の特定が容易になるためです。

Catalog設定


カタログはアセットとしてダウンロードすべきアセットを一覧化したものです。
以下の設定を行います:

  • BuildRemoteCatalog: チェックなし
    リモートなら手元を最新化してほしいだろうと思うのでチェックつけると思いますが、今回はローカルなのでなしです。

  • EnableJsonCatalog: チェックなし
    Json形式のカタログを書くかどうかです。デバッグ時には便利ですが、
    実はこのカタログを読むことでユーザーに次の入れられるキャラがバレたみたいな話もあるので、チェックはしないほうが良いでしょう。

  • Internal Asset Naming Mode: Dynamic
    一番どうするか悩みどころですが、今回は推奨のDynamicに挑戦してみることにします。
    FullPathでも良いのですが、リモート&大企業さんの場合はGUIDが良いかもしれません。
    というのも、カタログはネットワークから取得する場合、FullPathだと大規模容量の時にカタログ自体も情報量が多くなりすぎて、
    カタログファイル自体を読むのに時間がかかってしまうからです。

  • Asset Load Mode: Requested Asset and Dependencies
    推奨設定です。必要なアセットとその依存関係のみを読み込むため、メモリ効率が良くなります。

Downloads設定


  • Use Unity WebRequest for Local Assets Bundles: チェックあり
    開発が大規模化したときのことも考えてWebRequest経由で読み込めるように変えておきます。
    そうすることで、今後リモート化したときも同じコードが使い回せます。

  • タイムアウト関連: デフォルト(最大値)
    ローカルでの読み込みなのでタイムアウトは基本的に発生しないため、デフォルトのままで問題ありません。

Build設定


  • Build Addressables on Player Build: Use global Settings
    これは好みによるところがあります。エディターの環境設定に従う形にしておくことで、
    チーム開発時にも統一した設定を使用できます。

  • Strip Unity Version From AssetBundles: チェックなし
    Unityバージョンを含めるかどうかの設定です。ビルドされるアセットの中身がUnityバージョンによって
    変わる可能性があるので、含めた方が安全です。

これらの設定により、ローカルでの利用に最適化しつつ、将来的な拡張性も確保した構成が整います。

カタログとアセット管理の基礎


カタログについて


カタログ(catalog.json)は、Addressableシステムの心臓部とも言える重要なファイルです。
これは単なる目録ではなく、アセットとしてダウンロードすべきアセットを一覧化したものです。 独自で作成することも、分割することも可能です(今回は分割方法については割愛します)。

Addressableは基本的に以下の流れでアセットを読み込みます:

  1. 初期化時にカタログを読み込む
  2. カタログ情報を元に必要なアセットをダウンロード
  3. アセットの依存関係を解決して読み込み

よくあるスマホゲーム起動時の「データダウンロード」画面で行われているのは、
このカタログ情報の取得とそれに基づくアセットのダウンロードです。

Note
カタログはリモートに置く場合もあれば、ローカルに置く場合もあります。
今回はローカルでの利用を想定しています。

アセットの設定方法


さて、アセット設定ができたところで、実際にアセットをAddressableのアセットとして登録します。
しかし、Resourcesが多くなるほど管理が複雑になり、自前での管理は辛い状況になります。

とくに以下のような作業は手作業では限界があります:

  • 大量のアセットへのアドレス割り当て
  • アドレスの一貫性維持
  • 依存関係の管理

そこで、今回はサイバーエージェントさんが公開している「Smart Addresser」を活用します。
これはアドレスの割り振りを自動化してくれる非常に便利なツールです。

詳しい使い方は以下の公式ドキュメントを参照してください:

個人利用でもその恩恵は十分に得られます:

  • アドレスの自動割り振り
  • 一貫性のある命名規則の適用
  • アセット管理の効率化

Smart Addresserの実践的な設定


Window→Asset Management→Addressables→Groupsをクリックし、
Addressable Groups Windowを立ち上げます。

Profileは前回設定したDefaultが設定されているのを確認します。

Window内で右クリックしCreate→New Group→PackedAssetsをクリックすると、

新しいグループが追加されます。

ちなみにAssets>AddressableAssesDataフォルダーの中にある該当名のScriptableObjectが実体となります。

名前はF2で変更可能です。
わかりやすいようにTestに変えておきましょう。

Testの行を選択すると該当のScriptableObjectがProjectWindowで選択され、
InspectorWindowにグループ設定が表示されます。

グループの設定内容


内容については前回解説済みですが、今回は以下のように設定します:

設定の意図は以下の通りです:

  • Build & Load Path: Local
    ローカル読み込みを行うため

  • Asset Bundle CRC: Enabled
    念のためCRCチェックを有効化

  • Include in Catalog Settings

    • Include Addresses in Catalog: チェックあり
      アドレスでの読み込みを行うため
    • Include GUIDs in Catalog: チェックなし
      AssetReferenceを使用しないため
    • Include Labels in Catalog: チェックなし
      ラベルによる管理を行わないため
  • Bundle Mode: Pack Separately
    個別に読み込みたいアセットが多いため

Smart Addresserでの管理設定


Window>SmartAddresser>LayoutRuleEditorでLayoutRuleEditorWindowを開きます。

追加したグループのControlにチェックを入れ、Smart Addresserでの管理下に置きます。
そして、どのようなアセットをアドレス管理にするかを決めるため、指定したグループのAssetsGroupsにObjectFilterを追加します。

Included Assetsを選択し、実際にダウンロード設定したいAssets配下のフォルダーを指定します。

AssetProviderにはAssetsPathを指定します。
(fileNameなどでもいいですが、名前が重複することもありえるのでAssetsPathを推奨します)

アドレス割り当ての自動化


アドレス割り当てのタイミングは、コードで制御することも可能ですが、
今回はProjectSettingsのSmartAddresserのPrimaryDataに該当のレイアウト設定を指定することで、
該当フォルダーに新しくデータをインポートする際に自動でアドレス割り振りされるようにします。

これで設定は完了です。

エディターでの動作確認


実装したコードを使って、エディター上で設定が正しく機能しているか確認しましょう。

まず、Window→Asset Management→Addressables→Groupsから、
Tools→Play Mode Script→Use Asset Databaseを選択します。
これにより、エディター上でアセットの読み込みをテストできます。

また、Window→Analysis→Profilerを開き、Addressablesモジュールを有効にすることで、
ロード状況やメモリの使用状況をリアルタイムで確認できます。

Note
エディターでの確認時は、アセットのビルドは不要です。
アセットデータベースから直接読み込むため、設定の変更をすぐに確認できます。

もし読み込みに失敗する場合は、以下を確認してください:

  • アセットが正しくグループに割り当てられているか
  • アドレスが正しく設定されているか
  • 指定したパスが正しいか

エディターでの動作確認が完了したら、次はアセットのビルドに進みます。

アセットのロード方法


基本的なロード方法


Addressablesでのアセットロードは非同期処理として実装します。
実際のロード処理は以下のGistで公開しているコードを利用します:

このユーティリティクラスには以下のような特徴があります:

  1. ハンドル管理の自動化

    • 読み込んだアセットのハンドルを自動的に管理
    • 同じアセットの重複ロードを防止
  2. メモリリークの防止

    • IDisposableの実装によるリソース解放の保証
    • 使用していないハンドルの適切な解放
  3. エラーハンドリング

    • 初期化時のエラー処理
    • キャンセル処理のサポート

実装例


実際の使用例を示します:

using UnityEngine;
using UnityEngine.UI;
using System;
using System.Threading;
using Cysharp.Threading.Tasks;

public class SampleLoader : MonoBehaviour
{
    [SerializeField] private Image _image;
    private CancellationTokenSource _cts;

    private async void Start()
    {
        _cts = new CancellationTokenSource();

        try
        {
            await AddressableUtil.InitAsync();
            await LoadAssetAsync(_cts.Token);
        }
        catch (Exception ex)
        {
            Debug.LogError($"Addressablesの処理に失敗: {ex}");
        }
    }

    private async UniTask LoadAssetAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            var sprite = await AddressableUtil.LoadAssetAsync<Sprite>(
                "Assets/MyProject/ResourceAssets/Addressable/me.png",
                cancellationToken
            );
            _image.sprite = sprite;
        }
        catch (OperationCanceledException)
        {
            Debug.Log("アセットのロードがキャンセルされました");
        }
        catch (Exception ex)
        {
            Debug.LogError($"アセットのロードに失敗: {ex}");
        }
    }

    private void OnDestroy()
    {
        _cts?.Cancel();
        _cts?.Dispose();
    }
}

このように分けることで:

  • 初期化とロード処理が明確に分離される
  • 必要に応じてロード処理だけを再実行できる
  • 他の初期化処理と順序を調整しやすい

という利点があります。

なお、このサンプルでは初期化直後にロードしていますが、 ボタンクリック時やシーン遷移後など、任意のタイミングで LoadAssetAsyncを呼び出すことも可能です。

メモリ管理

Addressablesでは、ロードしたアセットの解放を明示的に行う必要があります:

// アセットの解放
Addressables.Release(handle);

Event Viewerによるデバッグ


Addressablesのデバッグには、Event Viewerという機能が用意されています。
バージョンによって利用方法が異なりますので、それぞれの方法を説明します。

2.2.2以降の場合

2.2.2からはUnityのプロファイラーに統合されています。
Window→Analysis→Profilerを開き、Addressablesモジュールを有効にすることで利用できます。

1.x系の場合


1.x系では、パッケージマネージャーからEvent Viewerを明示的に有効化する必要があります。
その後後、以下の設定を行います:

  1. AddressableAssetSettingsのインスペクターで「Send Profiler Events」を有効化
  2. 新しいコンテンツビルドを作成

その後、Window→Asset Management→Addressables→Event Viewerから利用できます。

共通の機能


Event Viewerでは:

  • アセットのロード時間
  • メモリ使用量
  • ロードの成功/失敗
  • 重複ロードの検出

などを確認できます。

とくに開発中は、このツールを使ってメモリリークやロードの問題を早期に発見することをオススメします。

アセットのビルド


エディターでの動作確認が完了したら、実機やビルド環境で使用するためのアセットビルドを行います。

Window→Asset Management→Addressables→Groupsから、
Build→New Build→Default Buildを選択します。

ビルドが成功すると、Library/com.unity.addressables/にアセットバンドルが生成されます。
このビルドは、アセットの追加や変更を行った際に実行する必要があります。

また、Play Mode ScriptをUse Existing Buildに変更することで、
エディター上でもビルドしたアセットバンドルを使用した動作確認ができます。

注意
アセットをビルドせずにアプリケーションをビルドすると、
ランタイムでアセットが見つからずエラーになります。
必ずアセットのビルドを行ってからアプリケーションをビルドしてください。

ビルドの確認


ビルドが完了したら、Play Mode ScriptをUse Existing Buildに変更して動作確認を行います。

Window→Asset Management→Addressables→Groupsから、
Play Mode Script欄をUse Existing Buildに変更します。

これにより、エディター上でもビルドされたアセットバンドルを使用した動作確認が可能になります。

実際のビルドファイルは以下の場所に生成されています:

  • Library/com.unity.addressables/ - ビルド時の中間ファイル
  • ServerData/ - リモートビルド用のファイル
  • Assets/StreamingAssets/aa/ - ローカルビルド用のファイル

今回はローカルビルドなので、Assets/StreamingAssets/aa/内のファイルが実際に使用されます。

ビルドしたアプリケーションでは、このStreamingAssets内のファイルからアセットがロードされます。 エディター上でUse Existing Buildを選択することで、同様の動作を確認できます。

もし動作に問題がある場合は、以下を確認してください:

  • アセットが正しくグループに割り当てられているか
  • 最新のアセットがビルドされているか
  • アドレスが正しく設定されているか
  • StreamingAssetsフォルダーが存在し、必要なファイルが含まれているか

実機での確認


エディター上での動作確認が完了したら、実際にビルドして動作を確認します。

  1. まずAddressablesのビルドを行います(上記の手順通り)

  2. File→Build Settings(Ctrl+Shift+B)を開き、Build And Runを実行

重要
必ずAddressablesのビルドを先に行ってください。
アプリケーションのビルド時にStreamingAssetsフォルダーの内容が
実行ファイルと同じ場所にコピーされます。

ビルドしたアプリケーションを実行し、以下を確認します:

  • アセットが正しくロードされるか
  • ロード時のパフォーマンスは問題ないか
  • メモリの使用量は想定通りか

もし問題が発生した場合は、Development Buildを有効にして
ログを確認することで原因の特定が容易になります。

ビルドとデプロイメント


ビルドファイルについて


Addressablesでビルドされたアセットは以下の場所に生成されます:

  • Library/com.unity.addressables/ - ビルド時の中間ファイル
  • ServerData/ - リモートビルド用のファイル
  • Assets/StreamingAssets/aa/ - ローカルビルド用のファイル

今回はローカルビルドを使用しているため、Assets/StreamingAssets/aa/内のファイルが実際に使用されます。

アプリケーションビルド時の注意点


重要
必ずAddressablesのビルドを先に行ってください。
アプリケーションのビルド時にStreamingAssetsフォルダーの内容が 実行ファイルと同じ場所にコピーされます。

手順:
1. まずAddressablesのビルドを実行
2. File→Build Settings(Ctrl+Shift+B)からアプリケーションをビルド

もし問題が発生した場合は、Development Buildを有効にして
ログを確認することで原因の特定が容易になります。

ちなみにunityroomで動かしたい場合はJuhaさんの↓などがおすすめです。 zenn.dev

まとめ


今回は以下の内容を解説しました:

  • Addressablesの実践的な設定方法
  • Smart Addresserを使ったアセット管理
  • 基本的なアセットロードの実装

個人開発でもAddressablesを活用することで、アセット管理がより効率的になります。
とくにSmart Addresserとの組み合わせは、開発効率を大きく向上させてくれるでしょう。
以上やまだたいしでした。

ゲーム開発者のための実践的設計 #1

こんにちは、やまだたいし(@OrotiYamatano)です。

今回は「なぜ設計が必要なのか」というテーマで、ゲーム開発における設計思想について、私の考えを共有したいと思います。

目次


はじめに


この記事は、「ゲーム開発者のための実践的設計」の第1回目となります。

今後、設計についてさまざまな観点から体系的にまとまった記事として連載します。

私がこの連載を始めようと思ったきっかけは、ゲーム開発の現場で「設計」について十分な検討がされていないと感じることが多かったためです。
機能の実装に重きを置くあまり、保守性や拡張性といった長期的な視点での設計が後回しにされがちです。

また、若手プログラマーの教育や技術指導の場面でも、設計の考え方を体系的に説明できる資料の不足を感じてきました。
この連載が、現場のプログラマーの方々の設計の参考になるとともに、チームでの技術指導における共通の指針となれば幸いです。

ゲーム開発における設計の重要性は、年々増しています。
とくに近年のゲーム開発では、規模の拡大、プラットフォームの多様化、そしてチーム開発の複雑化により、適切な設計なしでは開発が立ち行かなくなってきています。

この連載では、私の経験に基づいた設計思想を、具体的な例を交えながら共有していきたいと思います。

対象読者


  • ゲーム開発の設計について興味のあるプログラマー
  • ゲーム開発における設計の考え方を学びたい方
  • 私の設計に関する経験と考えを参考にしたい方

この連載の位置づけ


この連載で書かれる内容は、あくまでもゲーム開発における私個人の考え方であり、絶対的な正解ではありません。
ゲームのジャンル、規模、プラットフォーム、チームの状況によって最適な設計は大きく変化します。
ここで紹介する考え方を、皆さんの開発状況に合わせて取捨選択していただければと思います。


良いプログラムとは何か?


「良いプログラム」とは何でしょうか。
多くの場合、以下のような要素が挙げられます:

  • 保守性が高い
  • 可読性が高い
  • パフォーマンスが良い
  • バグが少ない
  • 拡張性がある

しかし、これらは理想論に過ぎません。
現実的には、「儲かるプログラム」こそが良いプログラムと言えるでしょう。

なぜ「儲かるプログラム」なのか


ゲーム開発においてもっとも重要なのは、プロダクトをリリースし、収益を上げることです。
以下のような状況を考えてみましょう:

  • 保守性は低いが、早期にリリースできるプログラム
  • 可読性は低いが、ユーザー体験が優れているプログラム
  • アーキテクチャは美しくないが、バグが少なく安定して動作するプログラム

これらは一見「良くない」プログラムに見えるかもしれません。
しかし、以下のような「理想的な」プログラムよりも価値があります:

  • 完璧な設計を目指すあまり、いつまでもリリースできない
  • 美しい構造を持つが、パフォーマンスに問題がある
  • 理論的には優れているが、実装に時間がかかりすぎる

つまり、「良いプログラム」とは:

  • リリースできること
  • 収益を生み出せること
  • 必要最低限の品質を満たしていること

これらを満たすプログラムこそが、現実的な「良いプログラム」だと私は考えています。

理想を追求することは大切ですが、それは手段であって目的ではありません。
最終的には、プロダクトとして成功し、収益を生み出せるプログラムを書くことが、
私たちプログラマーの目指すべき到達点なのかもしれません。

とはいえ、現状のゲーム開発現場では、設計の重要性が低く評価されていることも多く、
設計に関する知識や経験が乏しいプログラマーが多いです。

そこで、この連載では設計の重要性を伝え、
設計に関する知識や経験を深めることを目的とします。

なぜ設計が必要なのか


その「儲かるプログラム」を作るために、なぜ設計が必要なのでしょうか。
それは、以下のような理由があります:

開発速度の向上


適切な設計があることで:

  • チームメンバーが迷わずに実装できる
  • 仕様変更への対応が素早くできる
  • 既存機能の再利用が容易になる

これらは、結果として開発期間の短縮につながり、早期リリースを可能にします。

保守コストの削減


良い設計は:

  • バグの発見と修正を容易にする
  • 機能追加時の影響範囲を限定できる
  • コードの理解・引継ぎにかかる時間を減らせる

運用フェーズでのコスト削減は、直接的な収益向上につながります。
もちろん運用が存在しないゲーム開発では、このコストは発生しません。
しかし、長期間にわたる開発では、たとえ運用が存在しない場合でも、コードを改良し頻繁に更新を行います。
その場合は運用が必要なタイトル同様、保守コストを抑えることが重要です。
また、整ったプログラムは流用も可能になります。

リスク管理


設計を通じて:

  • 技術的な問題を早期に発見できる
  • スケールアップへの対応が計画的に行える
  • チーム間の認識齟齬を防げる

これらは、プロジェクトの失敗リスクを大きく低減します。

資産としての価値


適切に設計されたプログラムは、他のプロジェクトへの流用が可能です:

  • 他のプロジェクトでも再利用できる
  • 新規開発のテンプレートとして活用できる
  • チームの技術的知見として蓄積される

つまり、良い設計に基づくプログラムは、単なるコードではなく、企業の重要な資産となります。
この資産は、将来の開発コストを大きく削減し、競争力の源泉となります。

設計とは「儲かるプログラム」を作り、さらには「価値ある資産」を生み出すための重要な投資なのです。
ただし、投資である以上、その見返りを常に意識する必要があります。
過度な設計は逆効果となり得ることを、私たちは忘れてはいけません。

まとめ


ゲーム開発における設計とは、技術的な美しさを追求することが目的ではありません。
それは、より良いゲーム体験を実現し、効率的な開発を可能にするための重要な手段です。

この記事では設計の重要性について概念的な説明を行いましたが、以降の連載ではまずは設計の基本を解説します。
読みやすいコードを書くための設計の基本を解説します。

次回の内容↓

orotiyamatano.hatenablog.com

補足


本記事ははじめてAIを使って記事を書いてみました。
結構面白かったので、今後も使っていきたいです。

2024年の振り返りと2025年の抱負

やまだたいし( https://twitter.com/OrotiYamatano )です。
新年あけましておめでとうございます。
本記事では何をしたのか振り返り、今年は何をしていくのか考えていこうと思います。

去年の記事↓
orotiyamatano.hatenablog.com

目次


2024年の目標達成度


去年の目標はコチラでした。
・婚活を進める
・何かしらゲームをリリースする
・健康に気を配る
・引っ越し

一つずつ振り返っていきます。

婚活を進める


正直に言います二桁万円を使って惨敗でした。
マッチングアプリや紹介サービスなどを使ったりしましたが全然でした。
色々理由はあるとは思います。いくつか上げてみようと思います。

・恋愛市場価値化
本来恋愛とはその人がどうであるかという中身や振る舞いなどを見て決められることが多いと思っています。
しかしマッチングアプリなどでは人柄などは分からないため、市場価値として年収・身長・体重・学歴・顔・趣味などの数値から市場価値が決まり、高い順番に選ばれる傾向があるように思います。
一つ一つが無理のない価値基準でもフィルタリングされると一極的な人が並ぶことになります。
そこから初めてその人の人柄をみることになります。
私はどちらかというと市場価値的には冴えてるとは言い難いです。
ということでほぼ1000件以上のイイネを飛ばしてマッチングがほぼない、
マッチングしても数回やり取りして終わってしまったり、1回食事して終わってしまうなどでした。
やはり年収と顔は大事だなぁと思いました……。

・そもそも話題提供が苦手
苦手というのは業務上でのやり取りはできますが、相手を気遣い話を盛り上げるのが苦手という意味です。
これは会話にも関係ありますが、相手が盛り上がる会話というのがどうにも苦手であると感じました。
話は共感が大事と言われますが、大体はそうだと思いますが、
本当に同じような考えであれば会話が必要ないです。
なので私は基本会話というのは少し対立した方が面白くなると考えます。
しかし、私は意見が食い違っても「そういう考えもあるか」と「フーン」と言って話が終わってしまいます。
なにを言っても暖簾の腕押しでは相手は退屈してしまいます。
かといって過剰なリアクションも得意ではないです。
女性が好きな旅行の話もたいして出来ないし、TVはそこまで見ないので芸能人も知らない。正直、ディズニーは興味ない。

プログラミングやゲームばかりしているので小説の話も合わないし、ゲームやアニメは人によってみるジャンルというのは変わりますから結局話が噛み合うかといわれると噛み合わない。
すると、どうしたことでしょう会話が弾まない友達としか思えないとか男性として見られないなどという結果になりますションボリ。

・趣味が微妙
例えば、同じように趣味が一辺倒でも
趣味が車の男性がいるならソチラに惹かれるでしょうし、
サッカーが好きで休日にもサッカーをやっているとか、
ボルダリングが趣味とか女性が思う男性像に近い男性がやはり市場として好感を持たれやすい気がします。

ゲームやアニメなど完全にインドア派だと、普段どこに遊びにいくの→家にいるなどとなりどうにもイマイチな反応をされてしまいます。
それはそう……。

というような感じです。

とりあえず、1つ目の市場価値判断されないように紹介型サービスに切り替えてみました。
すると最初こそ全然マッチングしなくて自信を消失していたのですが、

1.相手にとってマイナスにとれる振り方をしない
2.相手に質問させない(話題提供を考えさせると一瞬でアウト多分話題考えるの面倒くさいとかリードしてくれないって思われる)
(ちょっと聞いてもいいですがと話題を振られたときはだいたい断られた)
3.話の流れを作っておいて、まるで話がハズんだと思わせる
(だいたい相手のプロフィールが決まっているので褒めるor誰と言ったのか聞く。
一緒に行った人と仲がいいんですねという、少し話したあと関連した他の話題に映す)
4.最初は笑顔(女性は表情を作って会話するのが普通なので男性同士のぶっきらぼうな表情は良くないです。少なくとも最初30秒は笑顔でいたいものです)
5.カメラを見て話す(カメラと目があわないと挙動不審だと思われる)
6.遅刻/時間をズルズル引き伸ばしない(当たり前ですが、他でプラスにはならないです。女性はなぜか原点形式で相手を判断するので他で盛り返すのは無理です)

これらを守ることでようやく3割くらいメッセージ先交換されるようになりました。
(まぁ連続で断られることもありますが)

それで、メッセージ交換に成功したとしてもまだ油断は出来なくて、ココでは女性はまだ積極的ではないです。
正直メッセージ交換した段階で積極的になってない女性とやり取りして上手くいくとは思えませんが、とりあえず連絡先の交換は出来るラインまでは来ました。

で、私の場合、婚活の紹介型サービスなのでマッチングした相手と早々にカフェなどでデートをとりつけます。
正直メッセージは保たないです。
相手を自分はよく知らないですし、相手も自分のことを知らないです。
日に1度メッセージが返ってくるぐらいになりがち……なので早めにデート約束をします。
できればカフェが良いです。互いに日程が合わないときはディナーで。
すると1~2時間会話出来ます。
ココからが重要で、まだ相手のフィルター通過しただけでしかありません。
ココで「素敵な人だな」と思わせられない状況が私の課題です。

数回同じ状況になって分かったのが、
相手は自分の状況を大体は話します。
将来の不安や現在の自分どう思ってるかなどを話してくれます。
多分、すり合わせなのかなと思います。
私はこれまでココで共感などを示して、やり取りが「おしまい」になってしまっていたのが8割ぐらいでした。
分析するにただ共感するだけでは相手から見る貴方の個性が埋もれてしまい普通の人で納まってしまいます。
そうするとお付き合いは出来ないとなるのかなと思いました。

つまり何が言いたいのかと言うと多分この時点では相手は私自身には興味はないので、この1回目デートを乗り切るのには私が思うに「相手をめっちゃ持ち上げる」のと、「自分も積極的に自己開示」する必要があるのかなと思います。
(もしかしたら会ってみたら相手にとって容姿が微妙という可能性も有り得ますが……)
まあ来年は頑張って1回目を突破して2回目以降のデートを回数を増やしたいです。

結局去年は23人ぐらいとWebカメラでやり取りして(私から断ったのも数人いたが)全部駄目だったので泣きました。

何かしらゲームをリリースする


今年こそは!!と思っていたので取り組めずがっかりでした。
Addressableを利用できるようにしたり、(https://orotiyamatano.hatenablog.com/entry/Addressabes1)
CRIに乗り換えたり( https://orotiyamatano.hatenablog.com/entry/Criware )
したが微塵も進まなかった。UniRxをR3に切り替えもしたい……。

ブログ更新も怪しかった。
まぁ自分用ライブラリが少し充実したので良しとしたい……。

進まなかったのには理由があって
私は「1箇所、外に出かけると気疲れする人間」というのがデカい。
なので、日に複数予定をいれるのが辛い。
1ヶ月に2回は歯医者or心療内科
2週間に1回くらい紹介された方とデート、
また気疲れする予定をいれると大体片方の休みは体や脳の充電で消費型のコンテンツを消化するしか体力がなくなってしまう。

ゆるして……

健康に気を配る


これは私にとっては今年満点とは言えないものの高得点でした。
残業も多くなってしまうような状況でしたが、風邪も微熱で済み休まずに済みました。
筋トレは引っ越しの際に気軽に通えるジムが遠くなったので通わなくなってしまったのですが、
かわりに「あすけん」という栄養管理アプリを入れて栄養バランス管理を始めました。
体脂肪率21%、体重61kgという筋肉量もないし体重も低いという状況に一時期なりましたが、
体重管理をすることで体脂肪率18%台、体重64kgと念願の体重を増やしながら脂肪を減らすというのを達成しました。
運動量が減ったのに筋力が増えたので摂る栄養が何より大事なんだなぁと思いました。PFCバランス大事。

一昨年は一時期体脂肪率最低16.8%だったので今後は運動できるような場所も探していきたい……。

引っ越し


9月に某会社で作ってたソシャゲをリリースした後に引っ越しました。
特に休みをとったりはせず、暇を見つけて部屋を探し、
会社に30分で行け、ペットが飼えてソコソコ広い部屋を借り引っ越しました。
家が近いって最高。

今年はペット、ねこを飼いたいと思ってます。

他、振り返り


会社のアプリリリース


会社の某プロジェクトで頑張りました。
一昨年から作っており、プロジェクト発足の最初の一人目のエンジニアだったりします。
プログラマーとしてインゲーム部分の中心人物となって完成させました。
具体的なタイトル名はココでは出しませんが、ひらがな表記で「やまだたいし」とクレジットに載ってるので、わかる人にはわかると思います。
コントリビューション数は去年だけで1,925コントリビューション。
趣味プロジェクトもgithubに移行して135コントリビューション。

少ないじゃんって言われる方もいるかもしれないが、1000行を超えるクソデカコミットなんかをやってたりしたので致し方なし……。
1,925のコントリビューションのうち半分、もとい1,067がcommitである。
仕様調整しながらレビューも少しして作業員2人に作業割り振りもして進めてたので、まぁ悪い感じではないかなと思います。
とはいえ休まず出社してた割に、ちょっと作業スピード遅い感あるので、なんとかしたいなーって思います。
あ、そういえば正社員になったのも去年か。

歯並び矯正


コロナになった際にマスクで口元隠すし、
歯の矯正をしなければ一生しないと思い、
いい大人になってからではあったが歯並びの矯正を始めた。
そして去年の3月頃とりあえず終わった。
2年半……長かった。

60万以上かかった。
とりあえず半年戻らないようにマウスピースをつけていたが、マウスピースが割れてきてしまったので最近はほぼつけていない。
また戻ってきたら調整考えようと思う。

投資信託、株ボロ儲け


実は数年前爺さんが死んだときにソコソコな遺産を相続しています。
私は泡銭は身につかないという発想の持ち主です。

小学生の頃、父親方の田舎に帰省した際に近所に挨拶回りを親としました。
よくある田舎ぐるみの付き合いでそのお陰で各家庭から数千円のお小遣いを頂きその時はトータル数万円近くになったのを覚えています。
そこで帰りのフェリー内に設置されているゲーセンで2万近く使ってしまい反省したことがあります。

そこから泡銭はなくなるものだし、できる限り丁寧に使おうと子どもながら考えが染み付きました。

その考えから、どうせ無くなるならと、相続された数千万のうち大体7割を手を付けずに投資に突っ込むことにしました。

3年前は400万の赤字を垂れ流したのですが、
去年は爆上げでした。

実際一昨年に赤字が無くなるほどの利益をあげ、
去年は11月頃から大幅に値上がり最終的には
(現金には替えてませんが)今年だけで利益率が30%を余裕で超え年収を上回るほどの利益が出ました。
(まぁ現金に替える際に利益の約20%近くを税金で取られるのですが)

ヤベェ

今年も同じだけ利益出れば130%の130%!!つまり元の1.69倍!!
5000兆円!!

まぁ世の中そんなに甘くない。
今後も夢を見すぎない程度に期待したい。

2025年の目標


今年の目標はコチラです!
・婚活に本気出す
・猫を飼う
・ゲームをリリースする
・実装力を鍛える
・無計画な時間を過ごさない

です。

婚活に本気出す


これまで本気じゃなかったのかと言われると一人一人にちゃんと私なりに真摯に向き合って本気でした。
しかし今年はもう少し婚活に割り当てる時間やお金を増やしてみようかと思います。

なんでそんなに焦っているのかというと、
私はできれば子どもが2人欲しいと考えているからです。
子どもを二人育てるなら3歳差くらいあったほうがいいです。
一人目が生まれる時期というのを考えると、逆算すると
今からお付き合いを始めたとして同棲して結婚してなどと
そこから具体的な話になるとめっちゃ健全な関係を妄想します。
数ヶ月お付き合いして
同棲期間が1年だったとして、
そこから結婚して数ヶ月、
2年経過3年目で子どもが生まれたとしましょう。
そして更に3年後に2人目さて私は何歳37歳です。

そこから一番下の子どもが20歳になったときの年齢は?57歳です。
だいぶ、いい歳。

30後半から体力が落ちるのを考えると
40歳で子育ては辛いとも考えられるので、やはり急ぐべきだと思います。

もし相手と話して家を買うことになるならローンも考えなければいけません。
東京に腰を据えると考えるのは結婚してからになると思います。
フラット35は35年ローンです。
つまり結婚したときにローンを組んだとして35年先までお金を払い続けることになります。
結婚するのが2年後なら33歳での結婚です。
そこからローンを組むと完済は68歳……!マジか!
とてもじゃないですが退職して何年もローンで悩みたくないです。
だとするならいろいろな人生の判断をするためにも早めに結婚する必要性があります。

ということで結婚を焦っています。

具体的に何をするかというと積極的にそういったイベントに申し込んだり、
アプリに課金してみたりなどです。
半年経って無理そうなら最終手段。結構な額になるかと思いますが相談所に駆け込みます。
やってきます。

猫を飼う


猫が飼いたいです。
実家で猫を飼っていたのですが、やっぱり猫は癒やし。
最近一人で寂しいのもあるしペットを飼いたいなと思っているからです。
そのためにペット可の部屋に引っ越した。
長く飼うのは難しい可能性あるので保護猫を貰いたいなと思います。
まだ何も準備出来ていないので準備して3月ぐらいにはお出迎えできるといいなぁ。

ゲームをリリースする


N年目N回目個人ゲームのリリースが何年経っても出来ていない。
他に集中すべきことや注意を逸らされることがあるとしてもかかりすぎ……。
まじでなんとかしたい。
でも他優先度を考えると後回しになりがち……。
今年こそは……!6月ぐらいに出せるといいな。

実装力を鍛える


会社の仕事もそうだし、個人開発もそうだけれど全体的に実装力が足りない感じがする。
実装力というのはプログラミングだけに限らず段取りして組み立てる過程的なものすべて。
音を準備したり絵を準備したり、仕事だけならプログラミングがほとんどだが、個人開発ならそうもいかない。

これまでプログラミング経験はあっても運用であったりクリエイティビティを発揮して不確実なものを作り上げるという経験が少ないのが原因だと思われる。

実装しても考慮漏れがあったり、ちょっと作りが甘かったり反省するべき点がいくつかあるが、何より作ってリリースする、リリースしようとするという経験が少なすぎるのが問題だと思えた。

リリースできなくてもいいのでゲームジャムなどに参加するなどやっていきたい。

無計画な時間を過ごさない


先程の実装力にもつながるが、ADHDなこともあり虚無な時間を過ごしがちだ。
様々なことを先延ばしにしたり、辞め時を見失ったり、注意が逸れてしまったり。

なので、明確にカレンダーにスケジュールとして組み込んで漠然と「実装する」などと目標を立てず、
◯◯時までに〇〇するなど具体的な計画をつけ、
その時間の使い方を反省するサイクルを身に着けたいと思った。
今年は計画的な人間になるぞ!

とまぁ目標は以上です。

その他今年やること


別のADHDの薬を試そうと思ってます。
許可が降りて試せて、マシになるといいなぁ。

後、奥歯の親知らずを抜けと歯医者に言われているので年明け早々抜く予定。
痛いしやだな。

社内LTに出る回数増やす。

所感


去年は会社でアプリをリリースした以外では特に何もなかった。
まぁ引っ越しはしたけれど、頭の中は仕事仕事で、いっぱいだった気がする。
今年は激動だった、節目だったといえる年にしたい。
私の最終目標は人がいないガランとしたカフェで美少女に「マスターこんなに客がいないと潰れちゃいますよ~」って言われながら、コーヒーを飲みながらノートパソコンでカタカタとゲームを作ることです。
投資信託/株と趣味のゲーム開発でバカ儲けてゲームはアニメ化なんていう妄想……。

夢のまた夢だね。
口に出さないと叶うものも叶わない。
妄言でもいいから言うべきだと個人的には思っている。
なのでココに書き捨てておく。

どうか今年は私にとってもこのブログを読んでいる方々にもいい年になりますように。
以上、やまだたいしでした。