Skip to content

Web oyunları için Gamepad API ​

Gamepad API, web oyunlarına konsol kalitesinde kontrolcü desteği getirir. Xbox, PlayStation, Switch Pro ve genel amaçlı kontrolcüleri destekleyin.

1) Gamepad'leri algılama ​

js
window.addEventListener('gamepadconnected', (e) => {
  console.log('Gamepad bağlandı:', e.gamepad.id)
  console.log('Dizin:', e.gamepad.index)
  console.log('Düğmeler:', e.gamepad.buttons.length)
  console.log('Eksenler:', e.gamepad.axes.length)
})

window.addEventListener('gamepaddisconnected', (e) => {
  console.log('Gamepad bağlantısı kesildi:', e.gamepad.id)
})

2) Gamepad durumunu yoklama ​

Gamepad durumu yoklanmalıdır (olay tabanlı değildir):

js
function pollGamepads() {
  const gamepads = navigator.getGamepads()
  
  for (const gamepad of gamepads) {
    if (gamepad) {
      processGamepad(gamepad)
    }
  }
}

// Oyun döngünüzde çağırın
function gameLoop() {
  pollGamepads()
  update()
  render()
  requestAnimationFrame(gameLoop)
}

3) Standart düğme eşleme ​

"Standart" eşleme (çoğu Xbox/PlayStation kontrolcüsü):

js
const BUTTONS = {
  A: 0,           // Çarpı (PS) / A (Xbox)
  B: 1,           // Daire (PS) / B (Xbox)
  X: 2,           // Kare (PS) / X (Xbox)
  Y: 3,           // Üçgen (PS) / Y (Xbox)
  LB: 4,          // L1 (PS) / LB (Xbox)
  RB: 5,          // R1 (PS) / RB (Xbox)
  LT: 6,          // L2 (PS) / LT (Xbox)
  RT: 7,          // R2 (PS) / RT (Xbox)
  SELECT: 8,      // Share (PS) / Back (Xbox)
  START: 9,       // Options (PS) / Start (Xbox)
  L3: 10,         // Sol çubuğa basma
  R3: 11,         // Sağ çubuğa basma
  DPAD_UP: 12,
  DPAD_DOWN: 13,
  DPAD_LEFT: 14,
  DPAD_RIGHT: 15,
  HOME: 16,       // PS düğmesi / Xbox düğmesi
}

const AXES = {
  LEFT_X: 0,
  LEFT_Y: 1,
  RIGHT_X: 2,
  RIGHT_Y: 3,
}

4) Düğmeleri ve çubukları okuma ​

js
function processGamepad(gamepad) {
  // Düğmeler (dijital veya analog)
  const jump = gamepad.buttons[BUTTONS.A].pressed
  const shoot = gamepad.buttons[BUTTONS.X].pressed
  
  // Tetikler (analog 0-1)
  const leftTrigger = gamepad.buttons[BUTTONS.LT].value
  const rightTrigger = gamepad.buttons[BUTTONS.RT].value
  
  // Çubuklar (analog -1 ile 1 arası)
  const leftX = gamepad.axes[AXES.LEFT_X]
  const leftY = gamepad.axes[AXES.LEFT_Y]
  const rightX = gamepad.axes[AXES.RIGHT_X]
  const rightY = gamepad.axes[AXES.RIGHT_Y]
  
  return { jump, shoot, leftTrigger, rightTrigger, leftX, leftY, rightX, rightY }
}

5) Ölü bölge yönetimi ​

Çubuklar boşta nadiren tam olarak 0 değerinde kalır:

js
function applyDeadzone(value, deadzone = 0.15) {
  if (Math.abs(value) < deadzone) return 0
  
  // Kalan aralığı 0-1 ölçeğine yeniden ayarla
  const sign = Math.sign(value)
  const adjusted = (Math.abs(value) - deadzone) / (1 - deadzone)
  return sign * adjusted
}

function getStickInput(gamepad) {
  return {
    leftX: applyDeadzone(gamepad.axes[AXES.LEFT_X]),
    leftY: applyDeadzone(gamepad.axes[AXES.LEFT_Y]),
    rightX: applyDeadzone(gamepad.axes[AXES.RIGHT_X]),
    rightY: applyDeadzone(gamepad.axes[AXES.RIGHT_Y]),
  }
}

6) Radyal ölü bölge (2D hareket için daha iyi) ​

js
function applyRadialDeadzone(x, y, deadzone = 0.15) {
  const magnitude = Math.sqrt(x * x + y * y)
  
  if (magnitude < deadzone) {
    return { x: 0, y: 0 }
  }
  
  const normalized = {
    x: x / magnitude,
    y: y / magnitude,
  }
  
  const adjusted = (magnitude - deadzone) / (1 - deadzone)
  
  return {
    x: normalized.x * adjusted,
    y: normalized.y * adjusted,
  }
}

// Kullanım
const stick = applyRadialDeadzone(
  gamepad.axes[AXES.LEFT_X],
  gamepad.axes[AXES.LEFT_Y]
)
player.vx = stick.x * player.speed
player.vy = stick.y * player.speed

7) Eksiksiz gamepad yöneticisi ​

js
class GamepadManager {
  constructor() {
    this.gamepads = new Map()
    this.prevState = new Map()
    
    window.addEventListener('gamepadconnected', (e) => {
      this.gamepads.set(e.gamepad.index, e.gamepad)
      console.log('Bağlandı:', e.gamepad.id)
    })
    
    window.addEventListener('gamepaddisconnected', (e) => {
      this.gamepads.delete(e.gamepad.index)
      this.prevState.delete(e.gamepad.index)
    })
  }
  
  poll() {
    const gamepads = navigator.getGamepads()
    for (const gp of gamepads) {
      if (gp) this.gamepads.set(gp.index, gp)
    }
  }
  
  getState(index = 0) {
    const gp = this.gamepads.get(index)
    if (!gp) return null
    
    const stick = applyRadialDeadzone(gp.axes[0], gp.axes[1])
    const rightStick = applyRadialDeadzone(gp.axes[2], gp.axes[3])
    
    return {
      connected: true,
      leftStick: stick,
      rightStick: rightStick,
      
      // Ön yüz düğmeleri
      a: gp.buttons[0].pressed,
      b: gp.buttons[1].pressed,
      x: gp.buttons[2].pressed,
      y: gp.buttons[3].pressed,
      
      // Omuz düğmeleri
      lb: gp.buttons[4].pressed,
      rb: gp.buttons[5].pressed,
      
      // Tetikler (analog)
      lt: gp.buttons[6].value,
      rt: gp.buttons[7].value,
      
      // Yön düğmeleri
      dpadUp: gp.buttons[12].pressed,
      dpadDown: gp.buttons[13].pressed,
      dpadLeft: gp.buttons[14].pressed,
      dpadRight: gp.buttons[15].pressed,
      
      // Meta
      select: gp.buttons[8].pressed,
      start: gp.buttons[9].pressed,
    }
  }
  
  isPressed(index, button) {
    const gp = this.gamepads.get(index)
    return gp?.buttons[button]?.pressed || false
  }
  
  wasJustPressed(index, button) {
    const current = this.isPressed(index, button)
    const prev = this.prevState.get(index)?.[button] || false
    return current && !prev
  }
  
  endFrame() {
    for (const [index, gp] of this.gamepads) {
      const state = {}
      for (let i = 0; i < gp.buttons.length; i++) {
        state[i] = gp.buttons[i].pressed
      }
      this.prevState.set(index, state)
    }
  }
}

// Kullanım
const gamepadManager = new GamepadManager()

function update() {
  gamepadManager.poll()
  
  const state = gamepadManager.getState(0)
  if (state) {
    player.vx = state.leftStick.x * player.speed
    player.vy = state.leftStick.y * player.speed
    
    if (gamepadManager.wasJustPressed(0, BUTTONS.A)) {
      player.jump()
    }
  }
  
  gamepadManager.endFrame()
}

8) Titreşim / dokunsal geri bildirim ​

js
function vibrate(gamepad, duration = 200, weakMagnitude = 0.5, strongMagnitude = 0.5) {
  if (gamepad.vibrationActuator) {
    gamepad.vibrationActuator.playEffect('dual-rumble', {
      startDelay: 0,
      duration,
      weakMagnitude,   // Yüksek frekanslı motor
      strongMagnitude, // Düşük frekanslı motor
    })
  }
}

// Kullanım
function onPlayerHit() {
  const gp = navigator.getGamepads()[0]
  if (gp) vibrate(gp, 150, 0.3, 0.6)
}

function onExplosion() {
  const gp = navigator.getGamepads()[0]
  if (gp) vibrate(gp, 300, 0.8, 1.0)
}

9) Birden fazla oyuncu ​

js
class LocalMultiplayer {
  constructor(maxPlayers = 4) {
    this.maxPlayers = maxPlayers
    this.players = []
  }
  
  getActivePlayers() {
    const gamepads = navigator.getGamepads()
    const active = []
    
    for (let i = 0; i < this.maxPlayers; i++) {
      if (gamepads[i]) {
        active.push({
          index: i,
          gamepad: gamepads[i],
          state: this.getState(gamepads[i])
        })
      }
    }
    
    return active
  }
  
  getState(gp) {
    // ... öncekiyle aynı
  }
}

// Oyun döngüsünde
const multiplayer = new LocalMultiplayer(4)

function update() {
  const players = multiplayer.getActivePlayers()
  
  for (const { index, state } of players) {
    updatePlayer(index, state)
  }
}

10) Yedek ve birleşik giriş ​

Hem klavyeyi hem de gamepad'i destekleyin:

js
class InputManager {
  constructor() {
    this.keyboard = new KeyboardInput()
    this.gamepad = new GamepadManager()
  }
  
  poll() {
    this.gamepad.poll()
  }
  
  getInput(playerIndex = 0) {
    const gpState = this.gamepad.getState(playerIndex)
    const kbState = this.keyboard.getState()
    
    // Girişleri birleştir (analog girişlerde gamepad önceliklidir)
    return {
      moveX: gpState?.leftStick.x || (kbState.left ? -1 : kbState.right ? 1 : 0),
      moveY: gpState?.leftStick.y || (kbState.up ? -1 : kbState.down ? 1 : 0),
      jump: gpState?.a || kbState.space,
      shoot: gpState?.x || kbState.z,
      pause: gpState?.start || kbState.escape,
    }
  }
  
  endFrame() {
    this.gamepad.endFrame()
    this.keyboard.endFrame()
  }
}

İlgili içerikler ​

Harici Kaynaklar ​