はじめに
ゲームの攻略情報を画面に重ねたり、自作のタイマーを表示させたりする「オーバーレイ」。非常に便利ですが、いざ作るとなると
など、特有の技術的ハードルがあります。
今回はモダンな技術スタックを使って、実用的なオーバーレイウィンドウを実装する方法を解説します。
[!caution]本記事で扱うオーバーレイは、画面表示や入力補助を目的としたものであり、
ゲームの内部メモリ改変・パケット解析・自動操作などは一切行いません。
利用にあたっては各ゲームの利用規約を必ず確認してください。
1. 言語選択
オーバーレイ開発において、言語選びの基準は「低レイヤーの操作(OSのウィンドウ制御)」と「UIの作りやすさ」のバランスにあります。
- Python : 開発は早いが配布サイズが大きく、実行速度やリソース消費がゲームの邪魔になることがある。署名しないとWindows Defenderで弾かれる
- C#(.NET/WPF) : Windowsとの相性は抜群だが、マルチプラットフォーム展開が難しく、ランタイムの重さが気になる。+α、UIデザインが初期設定だと非常に古臭い
- C++ : 性能は最高だがメモリ管理やモダンなUI構築のハードルが非常に高い。
- Rust : 高いパフォーマンスと安全性を両立。近年、GUIフレームワークが充実しており、OSのAPIを叩くのも得意。
2. 私はRustを選んだ
今回、私はRustを選択しました。理由は明確です。
ゲームの裏で動かすツールは、「極限まで低リソースであること」と「クラッシュしない安定性」が求められるからです。
RustならC++並みの速度を保ちつつ、メモリ安全性が保証された状態で開発を進められます。
RustでGUIを作る選択肢はいくつかありますが、今回はTauriとSvelteの組み合わせを採用しました。
なぜ Tauri と Svelte なのか
- Tauriの圧倒的な軽量さ : Electronと異なり、OS標準のWebviewを利用するため、実行ファイルが(規模にもよるが)数MB単位と非常に小さく、メモリ消費量も抑えられる。
- Web技術でUIが作れる : HTML / CSS / JavaScript(TypeScript)が使えるため、モダンで美しいデザインが容易に作れる。特にSvelteはコンパイル時にコードを最適化するため、ランタイムが非常に軽く、オーバーレイのような「軽さ」が求められる用途に最適
- Rustとの親和性 : バックエンド(OS操作)はRust、フロントエンド(表示)はWeb技術という役割分担が明確で、開発効率が非常に高い
4. コーディングするときのディレクトリ構成
Tauriプロジェクト(Svelte構成)の標準的な構成は以下のようになります。
my-overlay-app/
├── src/ # フロントエンド (Svelte / TS / CSS)
│ ├── lib/ # コンポーネント
│ └── App.svelte # メインUI
├── src-tauri/ # バックエンド (Rust)
│ ├── src/
│ │ └── main.rs # ウィンドウ制御やAPIの定義
│ ├── tauri.conf.json # ウィンドウ設定(透明化、最前面など)
│ └── Cargo.toml # Rustの依存関係
├── package.json # Node.jsの依存関係
└── ...
5. 準備: Cargo.tomlの設定
まずは必要なライブラリを揃えましょう。
キーフックには低レイヤーのイベントを拾えるrdevを使用します。
[dependencies]
tauri = { version = "1.5", features = ["window-set-ignore-cursor-events", "window-set-opacity"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
rdev = "0.5.3"
[!caution]バージョン情報はあくまでも例です。crates.ioで確認してください。
6. 実際にオーバーレイウィンドウを表示させてみる
まず、オーバーレイとして成立させるためにはウィンドウの枠を消し、背景を透明にする必要があります。これらはtauri.conf.jsonで設定可能です。
{
"tauri": {
"windows": [
{
"title": "OverlayWindow",
"width": 800,
"height": 600,
"resizable": false,
"decorations": false, // 枠を消す
"transparent": true, // 背景を透明にする
"alwaysOnTop": true // 最初から最前面に
}
]
}
}
7. オーバーレイウィンドウをTopmostにする
ゲーム画面の上に常に表示させるには、ウィンドウを「Topmost(最前面固定)」にする必要があります。設定ファイルでも可能ですが、Rust側から動的に制御することもできます。
use tauri::Manager;
fn main() {
tauri::Builder::default()
.setup(|app| {
let window = app.get_window("main").unwrap();
window.set_always_on_top(true).unwrap();
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
8. ウィンドウ全体の透過率(Opacity)を調整する
背景の透明度(Alpha)だけでなく、ウィンドウに表示されている文字やボタン自体の透過率を調整したい場合があります。
use tauri::Runtime;
#[tauri::command]
fn set_overlay_opacity<R: Runtime>(window: tauri::Window<R>, opacity: f64) {
window.set_opacity(opacity).unwrap();
}
[!point]背景を透明にするならCSSのbackground: rgba(0,0,0,0)で十分ですが、UI全体を透かせたい場合はOSレベルのset_opacityが非常に強力です。
9. 左クリックの透過処理
オーバーレイの最大の難所が「表示はされているが、クリックは後ろのゲームに届くようにする」というクリック透過処理です。
Tauriではset_ignore_cursor_events(true)を呼び出す事で、そのウィンドウに対するマウス操作を無視させることができます。
let window = app.get_window("main").unwrap();
window.set_ignore_cursor_events(true).expect("failed to set click-through");
[!point]実用的なツールにする場合は「設定画面を開いている間は操作可能にし、ゲーム中は透過させる」といった切り替えを、Rust側のcommandを通じてフロントエンドから制御するのが一般的です。
10. グローバルキー入力のフック(Hotkeys)
オーバーレイの表示・非表示を切り替えたり、機能を実行したりするためにゲームがアクティブな状態でもキー入力を検知する必要があります。
Tauriのプラグインを利用する場合
標準のglobal-shortcutプラグインを使うのが最も簡単です。
- メリット : 実装が容易、マルチプラットフォーム対応
- 用途 :
Ctrl + Shift + Hでウィンドウを隠すといったショートカット機能
より高度なフック(rdevなど)を利用する場合
「特定のキー(例: Fキー)」が押された瞬間を検知してUIを更新する」といった、より低レイヤーな制御が必要な場合は、Rustのrdevクレートなどを別スレッドで回します。
use rdev::{listen, Event};
std::thread::spawn(|| {
if let Err(error) = listen(| event | {
match event.event_type {
rdev::EventType::KeyPress(key) => println!("Key pressed: {:?}", key), _ => (),
}
}) {
println!("Error: {:?}", error);
}
});
[!caution]キーフックは強力な反面、アンチチートシステムに検知される可能性や、セキュリティ上の懸念もあるため、利用目的を明確にして実装しましょう。
[!tips]rdevのコールバックは別スレッドで実行されるため、
UI操作はTauriのイベントループにディスパッチする設計も検討できます。
(規模が大きくなった場合の安定性向上に有効)
11. F1キーを押している間だけ操作可能にするサンプル
ここが本記事のハイライトです。「普段はクリック透過してゲームを邪魔せず、F1を押している間だけUIをクリックできるようにする」という処理を実装します。
Rust側でのキーフックとウィンドウ制御
rdevでキーイベントを監視し、F1の押下状況に合わせてset_ignore_cursor_eventsを切り替えます。
use rdev::{listen, Event, EventType, Key};
use tauri::Manager;
fn main() {
tauri::Builder::default()
.setup(|app| {
let window = app.get_window("main").unwrap();
let w_handle = window.clone();
std::thread::spawn(move || {
listen(move |event| {
match event.event_type {
EventType::KeyPress(Key::F1) => {
w_handle.set_ignore_cursor_events(false).unwrap();
}
EventType::KeyRelease(Key::F1) => {
w_handle.set_ignore_cursor_events(true).unwrap();
}
_ => {}
}
}).expect("Could not listen");
});
window.set_ignore_cursor_events(true).unwrap();
Ok(())
})
.invoke_handler(tauri::generate_handler![set_overlay_opacity])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
12. Svelte側での考慮事項
Rust側でクリック透過を切り替えた際、フロントエンド(Svelte)側でも「今編集モードですよ」という状態を知る必要があります。
Tauriのemit(イベント送信)を使うとスムーズです。
Rust側(main.rsのループ内)
w_handle.emit("mode-change", "edit").unwrap();
w_handle.emit("mode-change", "transparent").unwrap();
[!memo]サンプルコードでは可読性を優先して unwrap() を使用しています。
実運用では if let Err(e) やログ出力を含めたエラーハンドリングを推奨します。
フロントエンド側(App.svelte)
<script lang="ts">
import { onMount } from 'svelte';
import { listen } from '@tauri-apps/api/event';
let isEditMode = false;
onMount(() => {
listen('mode-change', (event) => {
isEditMode = event.payload === 'edit';
});
});
</script>
<div class="overlay-container" class:active={isEditMode}>
{#if isEditMode}
<div class="badge badge-primary">F1 Hold: Edit Mode</div>
{/if}
</div>
<style>
.active { border: 2px solid #570df8; }
</style>
13. 動作しない時の落とし穴
実装が完璧でも、いざゲームと一緒に起動すると「キー入力が反応しない」「オーバーレイウィンドウが裏に隠れる」といった現象が起きることがあります。その原因の多くはWindowsの権限(整合性レベル)にあります。
なぜ管理者権限が必要なのか
WindowsにはUIPI(User Interface Privilege Isolation)というセキュリティ機能があり、権限の低いプロセスから権限の高いプロセス(管理者権限で実行されているアプリなど)への干渉を制限しています。
- ゲームが管理者権限で動いている場合 : 標準ユーザー権限で起動したオーバーレイツールは、ゲームへの入力フックやゲーム画面より前面への描画がOSによってブロックされてしまいます。
- 解決策 : 開発中のデバッグやツールの実行時に管理者権限にする
[!tips]配布を考えるなら、cargo-packagerなどを使ってマニフェストファイルにrequireAdministratorを設定し、常に管理者権限で昇格して起動するようにビルドするのも一つの手です。
まとめ
Rust + Tauri + Svelteの組み合わせは、軽量・高速・美麗なオーバーレイツールを作るための最強の選択肢です。
rdevでOS全体のキー入力を監視
Tauriでウィンドウの透過・操作可否を制御
Svelte (+daisyUI)でリッチなUIを提供
この3ステップを抑えれば、ゲーム体験を損なわないプロ仕様のツールが完成します。特に「必要な時だけ操作可能にする」というギミックは、ユーザーの利便性を劇的に向上させます。
「何かゲームの上にオーバーレイで表示させたい」と思っている方はぜひRust + Tauri + Svelteで作ってみてください。