input-systems について
input-systems は、ゲームの入力設計に使うエンジン非依存のスキルで、操作系やアクセシビリティ機能を作る開発者向けです。物理入力を名前付きアクションから分離し、アナログ入力、バッファリング、再割り当てを扱います。
主な機能
- 01物理入力を名前付きアクションに対応
- 02キーボード・ゲームパッド・タッチに対応
- 03アナログ入力のデッドゾーンと感度調整
- 04入力バッファとコヨーテタイム
- 05操作の再割り当てと競合検出
活用シーン
- 01複数デバイス向けの操作設計
- 02アナログスティックのデッドゾーン設定
- 03操作再割り当て画面の実装
- 04ジャンプ入力の応答調整
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ gamedev-skills/ awesome-gamedev-agent-skills --skill input-systemsインストール後、Claude Code で/input-systemsを入力して読み込まれたか確認します。
同梱ファイルreferences/buffering-and-accessibility.md
詳細
入力システム
ゲームプレイを生のキー入力に直接結び付けないでください。物理入力(キー、ボタン、タッチ)を名前付きのアクション(jump、interact、move)に割り当て、ゲームプレイ側ではアクションを読み取ります。この間接化をひとつ加えるだけで、キー割り当ての変更、複数デバイスへの対応、アクセシビリティをほぼ手間なく実現できます。このスキルはエンジンに依存しないアーキテクチャです。unity-input-system、unreal-enhanced-input、または Godot の InputMap に割り当ててください。
使用する場面
- アクション、割り当て、複数デバイス、競合検出と割り当ての保存に対応するキー割り当て変更 UI など、入力レイヤーを設計するときに使用します。
- アナログ入力の処理(デッドゾーン、感度)や、ゲームフィール機能(入力バッファリング、コヨーテタイム)を追加するときに使用します。
- 操作をアクセシブルにするときに使用します(すべての操作の再割り当て、押し続ける操作と切り替え操作、感度、同時押しの必須化を避けるなど)。
使用しない場面: エンジン固有の入力パッケージ/API については、unity-input-system、unreal-enhanced-input、または Godot の InputMap を使用してください。バッファが適用される移動/ジャンプの物理演算については、physics-tuning とエンジンの移動スキルを参照してください。割り当てのディスクへの保存には save-systems を使用してください。
基本的なワークフロー
- キーではなくアクションを定義します。 ゲームプレイ側では「
jumpが押されたか?」を確認し、「Space が押されたか?」とは確認しません。アクションが安定した契約となり、割り当てはデータになります。 - デバイスごとに割り当てます。 各アクションには、キーボード、ゲームパッド、タッチ用の割り当てを保持させます。最後に入力を送信したデバイスをアクティブデバイスとし、それに合わせて UI の操作ガイドを切り替えます。
- 適切な入力のエッジを読み取ります。 離散的なアクション(ジャンプ、インタラクト)にはこのフレームで押された(エッジ)状態を、連続的なアクション(移動、照準)には押され続けている(レベル)状態を使います。この2つを混同すると、入力が二重に実行されたり、押下を見逃したりします。
- アナログ入力をフィルタリングします。 スティックやトリガーにデッドゾーンを適用し、静止時のドリフトをゼロとして読み取れるようにします。また、好みに合わせて感度やカーブを調整します。
- 操作感のためにバッファリングします。 アクションが押された状態を短時間記憶することで、少し早めの入力でも実行できるようにします(入力バッファリング)。また、足場を離れた直後にもジャンプできるようにします(コヨーテタイム)。
- キー割り当て変更を第一級の機能として扱います。 次の入力をキャプチャし、競合を検出して割り当てを保存できる UI と、デフォルトに戻す機能を用意します。保存には
save-systemsを使用します。 - すべてのデバイスで、割り当て変更後も検証します。 キーボード、ゲームパッド、タッチで確認し、ゲーム中にアクションの割り当てを変更して、ゲームプレイと操作ガイドに反映されることを確認します。
パターン
1. 生のキーではなくアクションを使う:エッジと押し続ける状態
# Gameplay reads ACTIONS. The mapping from key/button to action lives in data.
# Discrete (edge): fire once on the press frame.
if Input.is_action_just_pressed("jump"):
try_jump()
# Continuous (held): read every frame as an axis.
var move := Input.get_axis("move_left", "move_right") # -1..1
player.velocity.x = move * RUN_SPEED
# RIGHT: name actions ("jump"); rebinding/devices just change the binding data.
# WRONG: `if Input.is_key_pressed(KEY_SPACE)` — unrebindable, keyboard-only,
# and `is_key_pressed` is a held check that would re-fire jump every frame.
エンジンごとの対応機能:Godot の InputMap と Input.is_action_just_pressed、Unity Input System の InputAction/アクションマップ、Unreal Enhanced Input の Input Actions と Input Mapping Contexts。
2. アナログのデッドゾーンと感度
# Raw sticks never rest at exactly zero. Apply a RADIAL deadzone (on the vector
# length), not per-axis, so diagonals aren't clipped into the axes.
func apply_deadzone(stick: Vector2, dead := 0.2, sens := 1.0) -> Vector2:
var mag := stick.length()
if mag < dead:
return Vector2.ZERO # inside deadzone -> no movement
# Rescale so motion ramps from 0 at the edge of the deadzone, not from `dead`.
var scaled := (mag - dead) / (1.0 - dead)
return stick.normalized() * pow(scaled, sens) # sens>1 = finer near center
# WRONG: clamping each axis separately — it carves a square hole and snaps to axes.
3. 入力バッファリング+コヨーテタイム(許容性が高く、応答性のよい操作感)
# Buffer: a jump pressed slightly BEFORE landing still triggers on touchdown.
# Coyote: a jump pressed slightly AFTER walking off a ledge still works.
const BUFFER := 0.12 # seconds an early press stays "remembered"
const COYOTE := 0.10 # seconds after leaving ground you can still jump
var _buffer_timer := 0.0
var _coyote_timer := 0.0
func _physics_process(dt):
_buffer_timer -= dt
_coyote_timer = COYOTE if is_on_floor() else _coyote_timer - dt
if Input.is_action_just_pressed("jump"):
_buffer_timer = BUFFER # remember the press
if _buffer_timer > 0.0 and _coyote_timer > 0.0:
velocity.y = JUMP_VELOCITY
_buffer_timer = 0.0; _coyote_timer = 0.0 # consume both so it fires once
4. 競合検出を伴うキー割り当て変更
# Capture the next physical input, reject duplicates, then persist.
func rebind(action: String, event: InputEvent) -> bool:
for other in actions: # conflict check across actions
if other != action and binding_of(other) == event:
return false # already used -> let UI warn/swap
set_binding(action, event) # engine: erase old + add new event
save_bindings() # persist (see save-systems)
return true
# Always provide "reset to defaults", and never let the player unbind a key they
# need to reach the menu without an alternative.
注意点
- ゲームプレイでキーをハードコードすると、 割り当て変更ができなくなり、ゲームパッドやタッチが使えなくなり、入力ロジックがあちこちに散らばります。名前付きアクションのみを読み取ってください。
- エッジと押し続ける状態の混同: ジャンプに押し続ける状態の確認を使うと、毎フレーム再実行されます。移動にエッジの確認を使うと、押し続けた入力が反映されません。アクションに合った確認方法を使ってください。
- 軸ごとのデッドゾーンを使うと、 スティックの斜め入力が切り取られ、移動が軸方向にスナップします。ベクトルの大きさに対する放射状のデッドゾーンを使ってください。
- 入力バッファリングやコヨーテタイムがないと、 物理演算が正しくても、シビアなプラットフォーマーは理不尽に感じられます。プレイヤーには「明らかにジャンプを押した」のです。短い猶予時間を設けてください。
- 競合処理なしでキー割り当てを変更すると、 複数のアクションに同じキーが割り当てられたり、メニュー操作の割り当てが解除されてプレイヤーが操作できなくなったりします。競合を検出し、元に戻す方法を必ず用意してください。
- デバイス変更時に操作ガイドを切り替えないと、 ゲームパッドのプレイヤーにも「Space を押してください」と表示されてしまいます。最後に使用したデバイスを追跡し、ボタン表示を切り替えてください。
- アクセシビリティを無視すると、 同時押しが必須になったり、再割り当てできなかったり、感度が固定されたり、押し続ける操作しか使えなかったりします。再割り当て、切り替え操作と押し続ける操作の選択、感度調整を提供してください。
- 誤ったループで入力を読み取ると、 問題が発生します。一貫した移動のために物理ステップで押し続ける状態をポーリングし、フレーム間で離散的な押下を見逃さないように取得してください。
参考資料
references/buffering-and-accessibility.md— バッファリング/コヨーテタイムの調整、ジャンプの感触 (可変ジャンプ高さ、頂点)、デバイス検出とプロンプトの切り替え、タッチ操作、 およびアクセシビリティチェックリスト(再割り当て、切り替え/長押し、感度、遅延)。
関連スキル
unity-input-system、unreal-enhanced-input— 各エンジンの具体的な入力 API (Godot ではInputMapとInputシングルトンを使用します)。save-systems— カスタムキー割り当てと入力設定を保存します。physics-tuning— バッファ/コヨーテタイムのウィンドウが作用する動きを調整します。platformer、fps-shooter— 操作感が入力処理に左右されるジャンルです。