仮想パッド2021/01/20

 仮想パッドによるキー入力を行うためには、画面上に配置したボタン画像が、押されているのかどうかを常に認識し続ける必要があります。

 左右ボタンでリトルが左右に移動し、上ボタンでジャンプするサンプルです。

 pointerdownイベントとpointermoveイベントは区別せず、ポインタリストに無ければ新たに押されたとして処理しています。
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
var frame = document.getElementById("frame");
var chara = { node:document.getElementById("chara"), x:0, y:64, u:1, v:0, count:0 };
var keys = new Array(3);
var bUpdateKeyPress = false;
initialize();

function initialize()
{
	var ic, ib, nodes;
	// ボタン構造配列
	for(ic=0; ic<keys.length; ic++)
	{
		keys[ic] = { node:document.getElementById("key"+ic), bDown:0, ip:null };
		nodes = keys[ic].node.childNodes;
		for(ib=0; ib<nodes.length; ib++)
		{
			if(nodes[ib].getAttribute && nodes[ib].getAttribute("class") == "button"){ keys[ic].button = nodes[ib]; }
		}
	}
	// イベント登録
	if(window.PointerEvent)
	{
		frame.addEventListener("pointerdown", onPress, false);
		document.addEventListener("pointermove", onPress, false);
		document.addEventListener("pointerup", onRelease, false);
	}else
	{
		frame.addEventListener("touchstart", onPress, false);
		frame.addEventListener("mousedown", onPress, false);
		document.addEventListener("touchmove", onPress, false);
		document.addEventListener("mousemove", onPress, false);
		document.addEventListener("touchend", onRelease, false);
		document.addEventListener("mouseup", onRelease, false);
	}
	document.addEventListener("keydown", onKey, false);
	document.addEventListener("keyup", onKey, false);
	frame.addEventListener("selectstart", function(event){ event.preventDefault(); }, false);
	frame.addEventListener("contextmenu", function(event){ event.preventDefault(); }, false);
	if(requestAnimationFrame){ requestAnimationFrame(onAnimationFrame) }else{ setInterval(onInterval, 16); }
}
// ポインタイベント
function onPress(event)
{
	var it, ic, ip, x, y, key, rect;
	if(event.touches)
	{
		for(it=0; it<event.touches.length; it++){ onPress(event.touches[it]); }
		if(event.cancelable){ event.preventDefault(); }
		return;
	}
	ip = event.pointerId || event.identifier || 0;
	x = event.clientX; y = event.clientY;
	for(ic=0; ic<keys.length; ic++)
	{
		key = keys[ic];
		rect = key.node.getClientRects()[0];
		if(x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom)
		{
			if(key.ip != ip && ((event.type != "pointermove" || event.pointerType != "mouse") && event.type != "mousemove" || event.buttons > 0))
			{
				key.ip = ip;
				if(!key.bDown){ key.bDown = true; bUpdateKeyPress = true; }
			}
		}else
		{
			if(key.ip == ip)
			{
				key.ip = null;
				key.bDown = false; bUpdateKeyPress = true;
			}
		}
	}
}
function onRelease(event)
{
	var it, ic, ip, key;
	if(event.touches)
	{
		for(ic=0; ic<keys.length; ic++)
		{
			for(var it=0; it<event.touches.length; it++){ if(keys[ic].ip == event.touches[it].identifier){ break; } }
			if(it == event.touches.length){ onRelease({ identifier:keys[ic].ip }); }
		}
		if(event.cancelable){ event.preventDefault(); }
		return;
	}
	ip = event.pointerId || event.identifier || 0;
	for(ic=0; ic<keys.length; ic++)
	{
		key = keys[ic];
		if(key.ip == ip)
		{
			key.ip = null;
			key.bDown = false; bUpdateKeyPress = true;
		}
	}
}
// キーイベント
function onKey(event)
{
	var ic = -1, code = event.code;;
	if(code != undefined)
	{
		if(code == "ArrowLeft" || code == "Numpad4" || code == "KeyA"){ ic = 0; }
		if(code == "ArrowRight" || code == "Numpad6" || code == "KeyD"){ ic = 1; }
		if(code == "ArrowUp" || code == "Numpad8" || code == "KeyW" || code ==  "KeyZ" || code ==  "KeyX" || code == "Space"){ ic = 2; }
	}else
	{
		code = event.keyCode;
		if(code == 37 || code == 100 || code == 65){ ic = 0; }
		if(code == 39 || code == 102 || code == 68){ ic = 1; }
		if(code == 38 || code == 104 || code == 87 || code == 88 || code ==  90 || code == 32){ ic = 2; }
	}
	if(ic >= 0)
	{
		var before = keys[ic].bDown;
		keys[ic].bDown = event.type == "keydown";
		if(before != keys[ic].bDown){ bUpdateKeyPress = true; }
		event.preventDefault();
	}
}
// 定期処理
function onAnimationFrame(){ onInterval(); requestAnimationFrame(onAnimationFrame); }
function onInterval()
{
	// ボタン押下チェック
	var ic;
	if(bUpdateKeyPress)
	{
		bUpdateKeyPress = false;
		for(ic=0; ic<keys.length; ic++)
		{
			if(keys[ic].button != null){ keys[ic].button.style.fillOpacity = keys[ic].bDown ? "1":""; }
		}
	}
	/*
	1フレームの処理
	*/
}