Для взаимодействием с пользователем компьютерной игре нужно отслеживать состояния контроллеров: кнопки, мышь, регуляторы и т.п. Для унификации используется единая структура для хранения текущего состояния контроллеров, которая обновляться при изменении состояний контроллов.

!!! При обработке ввода, кажется вполне естественным сразу выполнять нужные действия, по событиям от контроллеров, а не сохранять их в структуре. Однако это усложняет логику работы игры (=>framework/main), разбрасывая действия по всей программе. Так при создании игры сложнее тетриса получается мешанина из обработчиков, в каждом из которых приходиться учитывать действия остальных.

Рассмотрим реализацию на javascript+html5. Для хранения состояний будем использовать ассоциативный массив -- controllers: ключ -- имя контролла, значение -- его состояние. В input хранятся, только те контроллы, которые нужны игре. Например:

Код:
var controllers = {
	w:0,
	s:0,
	a:0,
	d:0,
	mouse1:0,
	mouse2:0,
	mouse3:0,
	mousePos:0,
};

Для ввода используем функцию, проверяющую нужно ли сохранять состояние и сохраняющую его.

Код:
function input(ctr, val){
    if(controllers[ctr] !== undefined){
        controllers[ctr] = val;
    }
}

На javascript+html5 изначально доступно два контроллера -- клавиатура и мышь.

   


Клавиатура.

Для учета текущего состояния кнопки, достаточно хранить либо 0, либо 1. Комбинации нажатий кнопок определяются по установленным 1 в input.

!!! Фиксировать клики или двойные клики, мало полезно, так как не учитывает как давно они произошли.

Для того, что бы элемент-контейнер мог перехватывать нажатия клавиш необходимо установить его свойство tabIndex=1 и добавить обработчики событий:

Код:
addEventListener("keydown", function(event){gp_input(event.key,1);});
addEventListener("keyup", function(event){gp_input(event.key,0);});

   


Мышь.

Кроме состояний кнопок мыши, необходимо сохранять точку в которой было нажатие, и позицию мыши. Вместо хранения 1, будем хранить структуру содержащую координаты мыши во время нажатия на кнопку. Текущие координаты, хранятся в специальном поле, если мышь находится в пределах игрового фрема.

Код:
addEventListener("mousedown", function(event){gp_input("mouse"+event.which,{x:event.clientX,y:event.clientY});});
addEventListener("mouseup", function(event){gp_input("mouse"+event.which,0);});
addEventListener('mousemove', function(event){gp_input("mousePos",{x:event.clientX,y:event.clientY});});
addEventListener('mouseleave', function(event){gp_input("mousePos",0);});

??? добавить захват мыши, если возможно в html5

   


При инициализации фрейма, необходимо учитывать какие обработчики нужны. Для этого используем значения маски-фильтра -- INPUTMASK и заодно инициализируем ассоциативный массив:

Код:
var INPUTMASK={keyb:['w','s'],mouseBtn:[1,2],mousePos:1};

gp.tabIndex=1;
if(INPUTMASK.keyb!==undefined){
    gp.addEventListener("keydown", function(event){gp_input(event.key,1);});
    gp.addEventListener("keyup", function(event){gp_input(event.key,0);});
    INPUTMASK.keyb.forEach((e)=>{
        gp_Controllers[e] = 0;
    });
}
if(INPUTMASK.mouseBtn!==undefined){
    gp.addEventListener("mousedown", function(event){gp_input("mouse"+event.which,{x:event.clientX,y:event.clientY});});
    gp.addEventListener("mouseup", function(event){gp_input("mouse"+event.which,0);});
    INPUTMASK.mouseBtn.forEach((e)=>{
        gp_Controllers["mouse"+e] = 0;
    });
}
if(INPUTMASK.mousePos!==undefined){
    gp.addEventListener('mousemove', function(event){gp_input("mousePos",{x:event.clientX,y:event.clientY});});
    gp.addEventListener('mouseleave', function(event){gp_input("mousePos",0);});
    gp_Controllers["mousePos"] = 0;
}

   


Для демонстрации дополним предыдущий пример: будем проверять позицию, где нажата клавиша мыши и изменим направление движения в ее сторону. Полный код:

Код:
<html>
<head>
    <meta charset="utf-8">
    <script type="text/javascript">

const WIDTH = 640;
const HEIGHT = 480;
var INPUTMASK={keyb:null,mouseBtn:[1],mousePos:null};

var gp_Controllers = {
};
var gp_CanvasContex;


function gp_input(ctr, val){
    if(gp_Controllers[ctr] !== undefined){
        gp_Controllers[ctr] = val;
    }
}

function gp_draw(image, x, y){
    gp_CanvasContex.drawImage(image, x, y);
}

function gp_clear(){
    gp_CanvasContex.clearRect(0,0,WIDTH, HEIGHT);
}

function gp_play(sound){
    sound.load();
    sound.play();
}


function gp_init(){
    var gp = document.getElementById("gamePanel");

    gp.tabIndex=1;
    if(INPUTMASK.keyb!==null){
        gp.addEventListener("keydown", function(event){gp_input(event.key,1);});
        gp.addEventListener("keyup", function(event){gp_input(event.key,0);});
        INPUTMASK.keyb.forEach((e)=>{
            gp_Controllers[e] = 0;
        });
    }
    if(INPUTMASK.mouseBtn!==null){
        gp.addEventListener("mousedown", function(event){gp_input("mouse"+event.which,{x:event.clientX,y:event.clientY});});
        gp.addEventListener("mouseup", function(event){gp_input("mouse"+event.which,0);});
        INPUTMASK.mouseBtn.forEach((e)=>{
            gp_Controllers["mouse"+e] = 0;
        });
    }
    if(INPUTMASK.mousePos!==null){
        gp.addEventListener('mousemove', function(event){gp_input("mousePos",{x:event.clientX,y:event.clientY});});
        gp.addEventListener('mouseleave', function(event){gp_input("mousePos",0);});
        gp_Controllers["mousePos"] = 0;
    }

    var canvas = document.createElement("canvas");
    canvas.id = "gameCanvas";
    canvas.width = WIDTH;
    canvas.height = HEIGHT;
    canvas.style="background-color:black;";
    gp.appendChild(canvas);
    gp_CanvasContex = canvas.getContext('2d');
}

function main(){
    gp_init();
    document.getElementById('snd1').load();

    gp_clear();
    var dx=10,dy=10;
    var x=10,y=10;
    window.setInterval(()=>{
        gp_draw(document.getElementById('img1'),x,y);
        gp_play(document.getElementById('snd1'));

        if(gp_Controllers['mouse1']!=0){
            dx = (x > gp_Controllers['mouse1'].x)? -10:10;
            dy = (y > gp_Controllers['mouse1'].y)? -10:10;
        }

        x+=dx;y+=dy;
        if(x>WIDTH || y > HEIGHT){
            gp_clear();
            x=10;y=10;
        }
    },1000);

}
    </script>

</head>
<body>
    <button onclick="main();">start</button>
    <div class="gamepanel" id="gamePanel">
        <img id="img1" style="display:none;" src="http://vt.chuvsu.ru/misc/favicon.ico">
        <audio id="snd1" src="http://www.sounds.beachware.com/2illionzayp3may/ovbzl/PIECES.mp3"></audio>
    </div>
</body>
</html>