cloud-game/web/js/input/touch.js
giongto35 55815cb9ef
Add multiplayer player (#148)
* Add player index

* Add second player

* Add player index slider

* add multiplayer

* WIP

* Clean up

* Add multiplayer play
2019-12-14 12:32:31 +08:00

265 lines
8.5 KiB
JavaScript
Vendored

/**
* Touch controls.
*
* Virtual Gamepad / Joystick
* Left panel - Dpad
*
* @link https://jsfiddle.net/aa0et7tr/5/
* @version 1
*/
const touch = (() => {
const MAX_DIFF = 20; // radius of circle boundary
// vpad state, use for mouse button down
let vpadState = {[KEY.UP]: false, [KEY.DOWN]: false, [KEY.LEFT]: false, [KEY.RIGHT]: false};
let vpadTouchIdx = null;
let vpadTouchDrag = null;
let vpadHolder = $("#circle-pad-holder");
let vpadCircle = $("#circle-pad");
const window_ = $(window);
const buttons = $(".btn");
const playerSlider = $("#playeridx")
const dpad = $(".dpad");
function resetVpadState() {
// trigger up event?
checkVpadState(KEY.UP, false);
checkVpadState(KEY.DOWN, false);
checkVpadState(KEY.LEFT, false);
checkVpadState(KEY.RIGHT, false);
vpadTouchDrag = null;
vpadTouchIdx = null;
dpad.removeClass('pressed');
}
function checkVpadState(axis, state) {
if (state !== vpadState[axis]) {
vpadState[axis] = state;
event.pub(state ? KEY_PRESSED : KEY_RELEASED, {key: axis});
}
}
function handleVpadJoystickDown(event) {
vpadCircle.css('transition', '0s');
vpadCircle.css('-moz-transition', '0s');
vpadCircle.css('-webkit-transition', '0s');
if (event.changedTouches) {
resetVpadState();
vpadTouchIdx = event.changedTouches[0].identifier;
event.clientX = event.changedTouches[0].clientX;
event.clientY = event.changedTouches[0].clientY;
}
vpadTouchDrag = {x: event.clientX, y: event.clientY};
}
function handleVpadJoystickUp() {
if (vpadTouchDrag === null) return;
vpadCircle.css('transition', '.2s');
vpadCircle.css('-moz-transition', '.2s');
vpadCircle.css('-webkit-transition', '.2s');
vpadCircle.css('transform', 'translate3d(0px, 0px, 0px)');
vpadCircle.css('-moz-transform', 'translate3d(0px, 0px, 0px)');
vpadCircle.css('-webkit-transform', 'translate3d(0px, 0px, 0px)');
resetVpadState();
}
function handleVpadJoystickMove(event) {
if (vpadTouchDrag === null) return;
if (event.changedTouches) {
// check if moving source is from other touch?
for (let i = 0; i < event.changedTouches.length; i++) {
if (event.changedTouches[i].identifier === vpadTouchIdx) {
event.clientX = event.changedTouches[i].clientX;
event.clientY = event.changedTouches[i].clientY;
}
}
if (event.clientX === undefined || event.clientY === undefined)
return;
}
let xDiff = event.clientX - vpadTouchDrag.x;
let yDiff = event.clientY - vpadTouchDrag.y;
let angle = Math.atan2(yDiff, xDiff);
let distance = Math.min(MAX_DIFF, Math.hypot(xDiff, yDiff));
let xNew = distance * Math.cos(angle);
let yNew = distance * Math.sin(angle);
if (env.display().isLayoutSwitched) {
let tmp = xNew;
xNew = yNew;
yNew = -tmp;
}
let style = `translate(${xNew}px, ${yNew}px)`;
vpadCircle.css('transform', style);
vpadCircle.css('-webkit-transform', style);
vpadCircle.css('-moz-transform', style);
let xRatio = xNew / MAX_DIFF;
let yRatio = yNew / MAX_DIFF;
checkVpadState(KEY.LEFT, xRatio <= -0.5);
checkVpadState(KEY.RIGHT, xRatio >= 0.5);
checkVpadState(KEY.UP, yRatio <= -0.5);
checkVpadState(KEY.DOWN, yRatio >= 0.5);
}
/*
Right side - Control buttons
*/
function handleButtonDown() {
checkVpadState($(this).attr('value'), true);
// add touchIdx?
}
function handleButtonUp() {
checkVpadState($(this).attr('value'), false);
}
/*
Player index slider
*/
function handlePlayerSlider() {
socket.updatePlayerIndex($(this).val() - 1);
}
/*
Touch menu
*/
let menuTouchIdx = null;
let menuTouchDrag = null;
let menuTouchTime = null;
function handleMenuDown(event) {
// Identify of touch point
if (event.changedTouches) {
menuTouchIdx = event.changedTouches[0].identifier;
event.clientX = event.changedTouches[0].clientX;
event.clientY = event.changedTouches[0].clientY;
}
menuTouchDrag = {x: event.clientX, y: event.clientY,};
menuTouchTime = Date.now();
}
function handleMenuMove(evt) {
if (menuTouchDrag === null) return;
if (evt.changedTouches) {
// check if moving source is from other touch?
for (let i = 0; i < evt.changedTouches.length; i++) {
if (evt.changedTouches[i].identifier === menuTouchIdx) {
evt.clientX = evt.changedTouches[i].clientX;
evt.clientY = evt.changedTouches[i].clientY;
}
}
if (evt.clientX === undefined || evt.clientY === undefined)
return;
}
const pos = env.display().isLayoutSwitched ? evt.clientX - menuTouchDrag.x : menuTouchDrag.y - evt.clientY;
event.pub(MENU_PRESSED, pos);
}
function handleMenuUp(evt) {
if (menuTouchDrag === null) return;
if (evt.changedTouches) {
if (evt.changedTouches[0].identifier !== menuTouchIdx)
return;
evt.clientX = evt.changedTouches[0].clientX;
evt.clientY = evt.changedTouches[0].clientY;
}
let newY = env.display().isLayoutSwitched ? -menuTouchDrag.x + evt.clientX : menuTouchDrag.y - evt.clientY;
let interval = Date.now() - menuTouchTime; // 100ms?
if (interval < 200) {
// calc velo
newY = newY / interval * 250;
}
// current item?
event.pub(MENU_RELEASED, newY);
menuTouchDrag = null;
}
/*
Common events
*/
function handleWindowMove(event) {
event.preventDefault();
handleVpadJoystickMove(event);
handleMenuMove(event);
// moving touch
if (event.changedTouches) {
for (let i = 0; i < event.changedTouches.length; i++) {
if (event.changedTouches[i].identifier !== menuTouchIdx && event.changedTouches[i].identifier !== vpadTouchIdx) {
// check class
let elem = document.elementFromPoint(event.changedTouches[i].clientX, event.changedTouches[i].clientY);
if (elem.classList.contains('btn')) {
$(elem).trigger('touchstart');
} else {
buttons.trigger('touchend');
}
}
}
}
}
function handleWindowUp(ev) {
handleVpadJoystickUp(ev);
handleMenuUp(ev);
buttons.trigger('touchend');
}
// touch/mouse events for control buttons. mouseup events is binded to window.
buttons.on('mousedown', handleButtonDown);
buttons.on('touchstart', handleButtonDown);
buttons.on('touchend', handleButtonUp);
// touch/mouse events for dpad. mouseup events is binded to window.
vpadHolder.on('mousedown', handleVpadJoystickDown);
vpadHolder.on('touchstart', handleVpadJoystickDown);
vpadHolder.on('touchend', handleVpadJoystickUp);
// touch/mouse events for player slider.
playerSlider.on('oninput', handlePlayerSlider);
playerSlider.on('onchange', handlePlayerSlider);
playerSlider.on('mouseup', handlePlayerSlider);
// Bind events for menu
// TODO change this flow
event.pub(MENU_HANDLER_ATTACHED, {event: 'mousedown', handler: handleMenuDown});
event.pub(MENU_HANDLER_ATTACHED, {event: 'touchstart', handler: handleMenuDown});
event.pub(MENU_HANDLER_ATTACHED, {event: 'touchend', handler: handleMenuUp});
return {
init: () => {
// add buttons into the state 🤦
$('.btn, .btn-big').each((_, el) => {
vpadState[$(el).attr('value')] = false;
});
// Bind events for window
window_.on('mousemove', handleWindowMove);
window_[0].addEventListener('touchmove', handleWindowMove, {passive: false});
window_.on('mouseup', handleWindowUp);
log.info('[input] touch input has been initialized');
}
}
})($, document, event, KEY, window);