🧠 AndroidビルドでPlayFabのSecretKeyが丸見えだった話と、ビルド時に自動変換する対策

📌 この記事は Zenn に投稿した内容のアーカイブです。 はじめに PlayFab を使ったプロジェクトで、 TitleId や DeveloperSecretKey をどう管理するかは、最初に一度は悩むポイントだと思います。 自分も最初は深く考えずに、 const string TitleId = "XXXX"; const string DeveloperSecretKey = "YYYY"; のようにコードに直接書いていました。 ところが Android ビルド後に apk を確認してみると、 strings コマンドで 平文のままキーが抜ける ことに気づき、 「これはさすがにまずいな……」となったのが今回のきっかけです。 問題点:apk 内に平文で残る C# のコードに直接書いた文字列は、 IL2CPP でも 難読化をしていても 最終的に 文字列リテラルとして apk 内に残ります 。 つまり、 strings app.apk を叩くだけで、TitleId や SecretKey が見えてしまう状態でした。 もちろん「完全に隠す」ことは不可能ですが、 平文で置いておくのはリスクが高すぎる と判断しました。 方針:コードには平文を置かない 今回の方針はシンプルです。 ソースコード内に 平文のキーを一切書かない ビルド時に Editor 側で変換・生成する ランタイムでは最小限の復号処理だけ行う いわゆる「秘匿」というより、 apk を覗かれたときに即バレしない状態を作る のが目的です。 実装概要 実装は以下の流れにしました。 Editor フォルダ内 ...

2026年2月5日 · 1 分 · 157 文字 · Wuyukwi

🐈 UnityのEditor拡張で「設定の保存先」に毎回悩む話

📌 この記事は Zenn に投稿した内容のアーカイブです。 Unity でエディタ拡張を書いていると、 「この設定、どこに保存するのが正解なんだろう?」 と悩む場面がよくあります。 そのときにまず候補に上がるのが EditorPrefs です。 bool isOpen = EditorPrefs.GetBool("MyTool_Foldout", false); とても手軽で便利ですが、使い方を間違えると チーム開発では地味に事故りやすい API でもあります。 この記事では、 EditorPrefs は何のための仕組みなのか どんな用途に向いているのか 逆に使ってはいけないケース を整理してみます。 EditorPrefs とは何か EditorPrefs は、Unity エディタ専用の設定保存機構 です。 プロジェクト単位ではない ユーザー(マシン)単位で保存される バージョン管理の対象外 という特徴があります。 つまり、 「この PC で、この Unity を使っている自分だけの設定」 を保存するためのものです。 保存場所はどこ? EditorPrefs の中身は、OS ごとに以下の場所に保存されます。 Windows:レジストリ macOS:~/Library/Preferences Linux:~/.config この時点で分かる通り、 Git にも SVN にも乗りません 。 向いているユースケース 1. エディタ UI の表示状態 例えば、 ツールウィンドウの開閉状態 折りたたみ(Foldout)の ON / OFF 最後に選択していたタブ こういった「見た目の状態」は、EditorPrefs と非常に相性が良いです。 bool isOpen = EditorPrefs.GetBool("MyTool_Foldout", false); ...

2026年2月2日 · 1 分 · 183 文字 · Wuyukwi

😎 「リンクリストはもう使われない」のか?

📌 この記事は Zenn に投稿した内容のアーカイブです。 ――2026年のハードウェア前提で考えるデータ構造の現実 エンジニア向けの記事や SNS で、 「リンクリストはもう終わった」 という表現を見かけることがあります。 最初にこの話を聞いたとき、正直かなり極端だなと思いました。 ただ、実務でパフォーマンスを意識した設計や実装を重ねていくと、この言い方が完全に間違っているとも言い切れない 、という感覚になってきます。 この記事では、「リンクリストがなぜ避けられるようになったのか」「それでも今なお使われ続けている理由」を、現代の CPU・メモリ構成を前提に 整理してみます。 教科書的な理解と、現実のズレ 学生時代にデータ構造を学んだとき、ほとんどの人が次のように習ったはずです。 配列:途中への挿入・削除は O(N) リンクリスト:挿入・削除は O(1) 理論としては正しいです。 ただし、ここには重要な前提条件 があります。 メモリアクセスのコストはすべて同じである 現実のハードウェアでは、これは成立しません。 キャッシュ階層という「無視できない現実」 現代の CPU では、メモリは階層構造になっています。 L1 キャッシュ:数サイクル L2 キャッシュ:十数サイクル L3 キャッシュ:数十サイクル メインメモリ(RAM):数百サイクル この差は無視できるものではありません。 特に問題になるのが、アクセスパターンが予測できない場合 です。 リンクリストが不利になる理由 典型的なリンクリストは、ノードがメモリ上に散らばって配置されます。 Node A -> Node B -> Node C 見た目はシンプルですが、実際のアドレスは次のようになっているかもしれません。 0x1000 -> 0x8F20 -> 0x3A10 リンクリストを走査する場合、 現在のノードを読み込む next ポインタを読む 次のアドレスが分かってから、次の読み込みを行う という強い依存関係のある処理 になります。 CPU のプリフェッチ機構は、連続したアクセス(配列など)は得意ですが、 リンクリストのようなポインタ追跡はほとんど先読みできません。 結果として、キャッシュミスが頻発 します。 配列(vector)が速い本当の理由 std::vector や配列を順番に処理しているとき、 実際には CPU はすでに次のデータをキャッシュに読み込んでいます。 ...

2026年2月2日 · 1 分 · 187 文字 · Wuyukwi

📃 UE5 における Gameplay Ability System & Game Features を「いつ使うべきか」という個人的な考察

📌 この記事は Zenn に投稿した内容のアーカイブです。 UE5 には Gameplay Ability System(GAS) や Game Features(GF) といった、拡張性と再利用性を強く意識した公式フレームワークが用意されています。 最近では Epic Games もこれらの利用を積極的に推奨しており、 「公式が用意しているのだから、最初から使うべきでは?」 と感じる方も少なくないと思います。 しかし、すべてのプロジェクト・すべての開発フェーズで導入すべきか というと、 個人的には 明確にそうではない と考えています。 この記事では なぜプロジェクト初期で GAS / Game Features を導入すると負担になりやすいのか どの段階で導入すると真価を発揮するのか について、自身の経験をもとに整理します。 結論:GAS / Game Features は優秀だが「導入時期」が重要 まず前提として GAS は長年運用され、大規模タイトルで実績のあるシステム Game Features も 機能分離・モジュール化を重視した現代的な設計 であり、品質や思想そのものに問題があるわけではありません。 問題になるのは 「いつ導入するか」 です。 プロジェクト初期・プロトタイプ段階で起きやすい問題 ① フレームワークが過剰に重い GAS や Game Features は 高い汎用性 拡張を前提とした設計 長期運用への耐性 を重視しています。 その結果 クラス構造が複雑 Ability / Attribute / Effect / GameplayTag など前提概念が多い 正しく使い始めるまでの学習コストが高い という特徴があります。 ...

2026年1月21日 · 1 分 · 169 文字 · Wuyukwi

🦔 Overwatchにおけるネットコードと予測技術の解説

📌 この記事は Zenn に投稿した内容のアーカイブです。 はじめに:高速対戦ゲームにおけるネットコードの本質 ゲームプレイ(GamePlay)エンジニアにとって、最重要テーマのひとつが ネット同期(netcode) です。 特に Overwatch のような高速リアルタイムアクションでは、「レスポンスの速さ(responsive)」 がゲーム性を決定づけます。 そのため、プレイヤーの操作は サーバーの応答を待たずに即時反映 する必要があります。 これを実現するのが 予測(prediction / pre-presentation) です。 ただし、クライアントを信頼できない(チート対策)というFPSの前提は20年変わっていません。 そのうえで「即時応答」と「サーバー権威」の両立を図るのがネットコード設計の核心になります。 即時応答が必要な操作 移動 スキル 武器(発射や装填などのスキル付き武器) 当たり判定(Hit Registration) 共通原則はただ一つ: 「プレイヤーがボタンを押したら即、見た目の挙動が起きること」 どれほど ping が高くても、遅延を感じさせてはならない。 しかし、予測を行う以上、避けられない問題があります── それが 予測ミス(misprediction) です。 予測ミスは、 「クライアント側では成功したように見えた操作が、サーバー上では成立していない」 状態を指します。 Overwatch では、予測ミスが起きても「操作を遅延させる」のではなく、 できる限り予測ミスを減らす設計(=確定性 Determinism) が採用されています。 予測ミスの例:サーバーとの食い違い ping 250ms。 クライアントでは「ジャンプした」つもりでも、サーバー上では Mei の氷結を受けていた 。 その結果、クライアントは一度ジャンプの予測を行った後、サーバーからの正しい状態(氷結)で「強制的に巻き戻される」。 このように、超高速応答を目指すほど、予測ミスが発生しやすくなります。 ▼ ここから:低ミス予測を支える「確定性(Determinism)」 以下では Overwatch が採用している ECS + Deterministic Simulation の要点を整理します。 1. 時間の量子化:Command Frame 確定的なシミュレーションは、 時間の同期 固定更新周期 値の量子化 ...

2025年11月21日 · 2 分 · 229 文字 · Wuyukwi

🧠 モダンなフレームワークは「低レイヤの理解」から遠ざけるか?

📌 この記事は Zenn に投稿した内容のアーカイブです。 〜意図(ビジネス要件)と低レイヤ知識の役割について考える〜 「最近のエンジニアは低レイヤを知らない」──そんな声を耳にすることが増えました。 それは本当に問題なのでしょうか? 1. プログラミングの本質:意図をコンピュータに伝えること プログラミングとは、人間の意図をコードという形でコンピュータに伝える行為 です。 言語やフレームワークはそのための「伝達チャンネル」にすぎません。 用途や目的が異なるからこそ、多様な言語やフレームワークが存在しています。 業務アプリ:可読性・保守性を重視 ゲームやリアルタイム処理:性能やレイテンシを重視 研究・プロトタイピング:表現力・試行錯誤のしやすさを重視 重要なのは「何を伝えたいのか(ビジネスの意図) 」であり、 低レイヤ(ハードウェアや OS の仕組み)はそれを実現するための手段 にすぎません。 2. 抽象化は「ノイズ除去」の進化である ソフトウェア開発の歴史を振り返ると、それは常に「ノイズを減らす」試みの連続でした。 アセンブリ言語:レジスタやメモリ操作のノイズを扱う 高級言語:ポインタ操作や手動メモリ管理のノイズを隠蔽 モダンフレームワーク:接続プールやリトライなど運用上のノイズを隠蔽 この観点から見ると、抽象化は「情報理論的に正しい方向への進化」です。 不要な実装詳細を隠すことで、開発者はより本質的な意図に集中できる ようになります。 3. それでも低レイヤ知識は不要にならない 抽象化が進んだからといって、低レイヤの知識が不要になるわけではありません。 実際には「抽象の漏れ(Leaky Abstraction)」や、フレームワークの誤用によって問題が生じることがあります。 そうしたときに求められるのは、下のレイヤを理解し、意図が正しく実現されているかを検証する力 です。 ただし、低レイヤの学習そのものが目的化してしまうと、肝心の「ビジネス意図の理解」が置き去りになります。 4. 最適化の本質は「情報の圧縮」である 最適化とは、突き詰めれば「情報の圧縮」です。 プログラムはビジネスの意図を機械命令に符号化したものとも言えます。 良い最適化とは、業務知識(確信)をもとに冗長性を取り除くこと によって成立します。 たとえば: キャッシュ導入の根拠は「そのデータは一定期間変化しない」という業務知見 インデックス設計の根拠は「特定フィールドの検索頻度が高い」という利用傾向 つまり、最適化とは単なる技術的チューニングではなく、業務理解と技術の融合による意思決定 なのです。 5. 戦術的勤勉 vs 戦略的怠惰 低レイヤを極めること自体は悪いことではありません。 しかし、それが「業務理解を放棄する言い訳」になってしまっては本末転倒です。 戦術的勤勉:低レイヤを丹念に学ぶ(手段) 戦略的怠惰:業務要件の理解を怠る(目的の喪失) まずは正しい問題設定──すなわち「ビジネスの意図」を明確にすること。 そのうえで、低レイヤの知識を活用して実装や最適化を行う。 この順序こそが、エンジニアリングにおける本質的なアプローチです。 6. 結論:抽象化の流れは正しい。ただし、バランスが鍵 モダンな抽象化は「意図に近づく」ための進化であり、退化ではない。 低レイヤ知識は「検証・トラブルシューティング・高性能化」のために必要だが、目的ではない。 成功するエンジニアは、**業務理解(何を伝えるか)と 技術力(どう実現するか)**をバランスよく使い分ける。 本質は「技術」ではなく「意図」。 技術は、意図を実現するための道具にすぎない。

2025年11月6日 · 1 分 · 69 文字 · Wuyukwi

🌟 C++11 が左辺値と右辺値を5分類に拡張した理由

📌 この記事は Zenn に投稿した内容のアーカイブです。 はじめに C++11 で「右辺値参照(rvalue reference)」が導入されたことで、C++ の値分類(value category)は大きく進化しました。 C++98 では「左辺値(lvalue)」と「右辺値(rvalue)」の二分法しかありませんでしたが、C++11 ではさらに細かく分類されます: C++11で導入された新しい概念である xvalue(期限切れ右辺値) と prvalue(純粋右辺値) が、 この章の中核的な進化ポイントです。 C++98 時代の「単純な世界」 C++98 の時代はとてもシンプルでした。 左辺値 (lvalue) :代入の左側に置けるもの。永続的なアドレスを持つ。 例:a, *ptr, array[i] 右辺値 (rvalue) :一時的な値。すぐに破棄される。 例:a + 5, 42, func() ルールも直感的でした: int a = 3; &a; // OK 左辺値はアドレスが取れる &(a + 5); // エラー!右辺値はアドレスが取れない しかしこの単純さが、性能の壁 になっていました。 問題:無駄なコピーが多すぎる std::vector<int> create_vector() { std::vector<int> temp; temp.push_back(1); temp.push_back(2); temp.push_back(3); return temp; // ここで何が起こる? } std::vector<int> v = create_vector(); C++98 の仕様では return temp; で コピーが発生 します。 temp のヒープメモリをまるごと複製し、呼び出し側に返すのです。 ...

2025年10月27日 · 2 分 · 319 文字 · Wuyukwi

💡 【Unity UGUI】Maskを使わずに実現する「穴あき」ハイライト演出

📌 この記事は Zenn に投稿した内容のアーカイブです。 はじめに UGUIで部分的にハイライトを当てたい場面、特にチュートリアル中の誘導UI では「特定のボタンだけを明るく見せ、他の部分を半透明で覆う」演出がよく使われます。 一般的には Mask や RectMask2D を利用しますが、これらにはいくつかの課題があります。 Mask :Stencilバッファを使うため、描画コストが高い RectMask2D :矩形領域しか扱えず、穴あき表現(逆マスク) はできない そこで今回は、MaskableGraphic を継承し、完全プログラム制御で「逆マスク(穴あきマスク)」を生成する方法 を紹介します。 最終的には、ハイライト部分がクリック可能で、他の部分はブロックされる 、高性能な HollowOutMask コンポーネントを実装していきます。 目標仕様 機能 説明 視覚効果 自身の RectTransform 領域内に半透明のマスクを描画し、指定の RectTransform 部分を「くり抜く」 射線処理 穴の部分はクリックが通過 し、マスク部分はクリックをブロック 動的追従 対象オブジェクトが動いたりスケールが変化しても、くり抜き領域が追従 これを、Mask 系コンポーネントを使わずに、頂点操作 だけで実現します。 実装の基本構造 HollowOutMask は MaskableGraphic を継承し、OnPopulateMesh() をオーバーライドします。 頂点生成の考え方 想像してみてください。 マスク全体を表す「外側の矩形(Outer Rect)」と、くり抜く「内側の矩形(Inner Rect)」があるとします。 この2つの矩形を組み合わせ、外側を塗りつつ内側を抜くには、8つの頂点 と8枚の三角形 が必要です。 コード例:OnPopulateMesh() protected override void OnPopulateMesh(VertexHelper vh) { vh.Clear(); if (_isTargetNull) { base.OnPopulateMesh(vh); return; } // Outer Rect(自身のRectTransform基準) float outerLx = rectTransform.rect.xMin; float outerBy = rectTransform.rect.yMin; float outerRx = rectTransform.rect.xMax; float outerTy = rectTransform.rect.yMax; vh.AddVert(new Vector3(outerLx, outerTy), color, Vector2.zero); vh.AddVert(new Vector3(outerRx, outerTy), color, Vector2.zero); vh.AddVert(new Vector3(outerRx, outerBy), color, Vector2.zero); vh.AddVert(new Vector3(outerLx, outerBy), color, Vector2.zero); // Inner Rect(穴部分) float innerLx = _targetMin.x; float innerBy = _targetMin.y; float innerRx = _targetMax.x; float innerTy = _targetMax.y; vh.AddVert(new Vector3(innerLx, innerTy), color, Vector2.zero); vh.AddVert(new Vector3(innerRx, innerTy), color, Vector2.zero); vh.AddVert(new Vector3(innerRx, innerBy), color, Vector2.zero); vh.AddVert(new Vector3(innerLx, innerBy), color, Vector2.zero); // Triangles(外側と内側の間を埋める) vh.AddTriangle(0, 1, 5); vh.AddTriangle(5, 4, 0); vh.AddTriangle(1, 2, 6); vh.AddTriangle(6, 5, 1); vh.AddTriangle(2, 3, 7); vh.AddTriangle(7, 6, 2); vh.AddTriangle(3, 0, 4); vh.AddTriangle(4, 7, 3); } これで、中央が透明な「リング状ポリゴン を生成できます。 ...

2025年10月20日 · 2 分 · 341 文字 · Wuyukwi