snake: add gamepad support
Hunter
Friday, August 1st, 2025 at 11:54 PM
386
387 function gameOver() {
388 gameRunning = false;
- document.getElementById('gameOver').innerHTML = 'Game Over!<br>Press Space to restart';
389 +
390 + if (gamepadConnected) {
391 + document.getElementById('gameOver').innerHTML = 'Game Over!<br>Press START button to restart';
392 + } else {
393 + document.getElementById('gameOver').innerHTML = 'Game Over!<br>Press Space to restart';
394 + }
395
396 // Show restart button and mobile message
397 const centerBtn = document.getElementById('centerBtn');
402 centerBtn.classList.remove('hidden');
403 }
404 if (mobileGameOver) {
- mobileGameOver.innerHTML = 'Game Over!<br>Press 🟢 to restart';
405 + if (gamepadConnected) {
406 + mobileGameOver.innerHTML = 'Game Over!<br>Press START button to restart';
407 + } else {
408 + mobileGameOver.innerHTML = 'Game Over!<br>Press 🟢 to restart';
409 + }
410 mobileGameOver.style.display = 'block';
411 }
412
608 document.addEventListener('touchmove', handleTouchMove);
609 document.addEventListener('touchend', handleTouchEnd);
610
611 + // Gamepad support
612 + let gamepadConnected = false;
613 + let lastGamepadState = {};
614 +
615 + function handleGamepadConnected(event) {
616 + gamepadConnected = true;
617 + console.log('Gamepad connected:', event.gamepad.id);
618 + updateStartHints();
619 + }
620 +
621 + function handleGamepadDisconnected(event) {
622 + gamepadConnected = false;
623 + updateStartHints();
624 + }
625 +
626 + function updateStartHints() {
627 + if (gamepadConnected) {
628 + document.getElementById('startHint').textContent = 'Press START button to start!';
629 + document.getElementById('mobileStartHint').textContent = 'Press START button to start!';
630 + } else {
631 + document.getElementById('startHint').textContent = 'Press Space to start!';
632 + document.getElementById('mobileStartHint').textContent = 'Press 🟢 to start!';
633 + }
634 + }
635 +
636 + function checkGamepadAvailability() {
637 + // Check if Gamepad API is available
638 + if (!navigator.getGamepads) {
639 + return false;
640 + }
641 +
642 + // Check for connected gamepads (some browsers need button press first)
643 + const gamepads = navigator.getGamepads();
644 + for (let i = 0; i < gamepads.length; i++) {
645 + if (gamepads[i]) {
646 + if (!gamepadConnected) {
647 + gamepadConnected = true;
648 + updateStartHints();
649 + }
650 + return true;
651 + }
652 + }
653 +
654 + return false;
655 + }
656 +
657 + function updateGamepad() {
658 + // Always check for gamepads (Firefox sometimes needs this)
659 + if (!checkGamepadAvailability()) return;
660 +
661 + const gamepads = navigator.getGamepads();
662 + let gamepad = null;
663 +
664 + // Find first connected gamepad
665 + for (let i = 0; i < gamepads.length; i++) {
666 + if (gamepads[i]) {
667 + gamepad = gamepads[i];
668 + break;
669 + }
670 + }
671 +
672 + if (!gamepad) return;
673 +
674 + // Check both D-pad buttons AND left analog stick for broader compatibility
675 + const currentState = {
676 + // D-pad buttons (standard mapping)
677 + up: gamepad.buttons[12]?.pressed || gamepad.axes[1] < -0.5 || false,
678 + down: gamepad.buttons[13]?.pressed || gamepad.axes[1] > 0.5 || false,
679 + left: gamepad.buttons[14]?.pressed || gamepad.axes[0] < -0.5 || false,
680 + right: gamepad.buttons[15]?.pressed || gamepad.axes[0] > 0.5 || false,
681 + // Accept face buttons and Start (+ on Switch Pro controller) as "START button"
682 + action: gamepad.buttons[0]?.pressed || gamepad.buttons[1]?.pressed || gamepad.buttons[2]?.pressed || gamepad.buttons[3]?.pressed || gamepad.buttons[9]?.pressed || false
683 + };
684 +
685 + // Check for newly pressed buttons (edge detection)
686 + if (currentState.up && !lastGamepadState.up) {
687 + if (gameRunning) {
688 + setDirection({x: 0, y: -1});
689 + }
690 + }
691 + if (currentState.down && !lastGamepadState.down) {
692 + if (gameRunning) {
693 + setDirection({x: 0, y: 1});
694 + }
695 + }
696 + if (currentState.left && !lastGamepadState.left) {
697 + if (gameRunning) {
698 + setDirection({x: -1, y: 0});
699 + }
700 + }
701 + if (currentState.right && !lastGamepadState.right) {
702 + if (gameRunning) {
703 + setDirection({x: 1, y: 0});
704 + }
705 + }
706 + if (currentState.action && !lastGamepadState.action) {
707 + if (!gameRunning) {
708 + startGame();
709 + }
710 + }
711 +
712 + lastGamepadState = currentState;
713 + }
714 +
715 + // Add gamepad event listeners
716 + window.addEventListener('gamepadconnected', handleGamepadConnected);
717 + window.addEventListener('gamepaddisconnected', handleGamepadDisconnected);
718 +
719 + // Poll gamepad state regularly
720 + setInterval(updateGamepad, 10); // Poll 100 times a second
721 +
722 + // Initial check
723 + setTimeout(checkGamepadAvailability, 1000);
724 +
725 +
726 // Handle window resize and orientation changes
727 window.addEventListener('resize', () => {
728 updateBoardSize();