Up-in-the-Air – commitdiff

You can use Git to clone the repository via the web URL. Download snapshot (zip)
Virtual thumbstick support
authorJulian Fietkau <git@fietkau.software>
Thu, 26 Sep 2024 23:08:29 +0000 (01:08 +0200)
committerJulian Fietkau <git@fietkau.software>
Thu, 26 Sep 2024 23:08:29 +0000 (01:08 +0200)
index.html
main.js

index 157aeafcb55b9040a6a032c1e2c49f561f727e4a..e56f4721a1307af6bfc905af5fb965f294e083ad 100644 (file)
   }
   @media (min-width: 15cm) and (min-height: 15cm) {
     @container (min-width: calc(100cqh - 4cm - 1rem - 4rem)) {
-      .game-upintheair .ui-container.control-touchpad {
+      .game-upintheair .ui-container.control-touchpad, .game-upintheair .ui-container.control-thumbstick {
         margin: 0 calc(1rem + min(4cm, 100cqw, calc(100cqh - 2 * var(--game-margin))));
         align-self: flex-end;
       }
     }
     @container (min-width: calc(100cqh - 4cm - 1rem - 4rem)) and (max-width: calc(100cqh + 2 * 4cm + 2rem)) {
-      .game-upintheair .ui-container.control-touchpad {
+      .game-upintheair .ui-container.control-touchpad, .game-upintheair .ui-container.control-thumbstick {
         margin: 0 calc(1rem + min(4cm, 100cqw, calc(100cqh - 2 * var(--game-margin)))) 0 0;
         align-self: flex-end;
       }
       right: 1rem;
     }
     @container (min-width: calc(100cqh - 4cm - 2rem)) {
-      .game-upintheair .ui-container.control-touchpad {
+      .game-upintheair .ui-container.control-touchpad, .game-upintheair .ui-container.control-thumbstick {
         margin: 0 calc(2rem + min(4cm, 100cqw, calc(100cqh - 2 * var(--game-margin))));
         align-self: flex-end;
       }
     }
     @container (min-width: calc(100cqh - 4cm - 2rem)) and (max-width: calc(100cqh + 2 * 4cm + 4rem)) {
-      .game-upintheair .ui-container.control-touchpad {
+      .game-upintheair .ui-container.control-touchpad, .game-upintheair .ui-container.control-thumbstick {
         margin: 0 calc(2rem + min(4cm, 100cqw, calc(100cqh - 2 * var(--game-margin)))) 0 0;
         align-self: flex-end;
       }
     border-radius: 3%;
     border: 2px inset #808080;
   }
+  .virtual-input-widget.thumbstick {
+    display: block;
+    background-color: #585858;
+    background-image: radial-gradient(circle, #808080, #484848);
+    border-radius: 50%;
+    border: 2px inset #808080;
+  }
+  .virtual-input-widget.thumbstick .circle {
+    position: relative;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    display: block;
+    width: 50%;
+    aspect-ratio: 1 / 1;
+    border-radius: 50%;
+    background-color: #ccc;
+    background-image: radial-gradient(#ccc 0%, #ccc 20%, #999 25%, #ccc 30%, #ccc 40%, #999 45%, #ccc 50%, #444 100%);
+    box-shadow: 0 0 .3cm #000;
+  }
   .ui-page {
     position: absolute;
     left: 0;
 </div>
 </div>
 <div class="virtual-input-widget">
-
+<div class="circle"></div>
 </div>
 </div>
 <script type="module" src="main.js"></script>
diff --git a/main.js b/main.js
index 6745c2de2fe93b0fd14428b1046b6a7fd3978b10..1e80baf29791ecb02e446fc0f80694b07a774e2b 100644 (file)
--- a/main.js
+++ b/main.js
@@ -172,7 +172,7 @@ function init(game, canvas) {
   game.objects = {};
   game.view = {};
   game.view.canvas = canvas;
-  game.view.virtualInput = canvas.closest('.game-upintheair').querySelector('.virtual-input-widget');
+  game.ui.virtualInput = canvas.closest('.game-upintheair').querySelector('.virtual-input-widget');
 
   const scene = new THREE.Scene();
   game.view.camera = new THREE.PerspectiveCamera(75, canvas.width / canvas.height, 0.1, 1000);
@@ -253,11 +253,11 @@ function init(game, canvas) {
     game.controls.positionY = - viewportHeight * viewportY;
   }
 
-  function cursorMoveEvent(game, viewportLocalX, viewportLocalY, pressed) {
+  function cursorMoveEvent(game, target, viewportLocalX, viewportLocalY, pressed) {
     if(game.settings['controls'] == 'mouse' || game.settings['controls'] == 'touchpad') {
       let sensorElem = game.view.canvas;
       if(game.settings['controls'] == 'touchpad') {
-        sensorElem = game.view.virtualInput;
+        sensorElem = game.ui.virtualInput;
       }
       let bbox = sensorElem.getBoundingClientRect();
       // Intentional division by height instead of width in the following line, since
@@ -275,6 +275,39 @@ function init(game, canvas) {
         pinwheelPositionUpdate(game, x, y);
       }
     }
+    if(game.settings['controls'] == 'thumbstick') {
+      if(!game.ui.virtualInput.inProgress) {
+        return;
+      }
+      let bbox = game.ui.virtualInput.getBoundingClientRect();
+      let x, y;
+      if(pressed) {
+        x = (viewportLocalX - bbox.x - (bbox.width / 2)) / bbox.height;
+        y = (viewportLocalY - bbox.y - (bbox.height / 2)) / bbox.height;
+        let vLen = Math.sqrt(4 * x * x + 4 * y * y);
+        x = x / Math.max(vLen, 0.6);
+        y = y / Math.max(vLen, 0.6);
+      } else {
+        x = 0;
+        y = 0;
+      }
+      let speedScale = 7.0;
+      let deadZone = 0.2;
+      let speedX = x * 2;
+      if(Math.abs(speedX) < deadZone) {
+        speedX = 0.0;
+      }
+      let speedY = y * 2;
+      if(Math.abs(speedY) < deadZone) {
+        speedY = 0.0;
+      }
+      game.controls.speedX = speedScale * speedX;
+      game.controls.speedY = -1 * speedScale * speedY;
+      x *= 0.6;
+      y *= 0.6;
+      game.ui.virtualInput.children[0].style.left = ((0.5 + x) * 100) + '%';
+      game.ui.virtualInput.children[0].style.top = ((0.5 + y) * 100) + '%';
+    }
   }
 
   function keyboardEvent(game, key, motion) {
@@ -343,10 +376,40 @@ function init(game, canvas) {
     }
   }
 
-  document.body.addEventListener('mousemove', e => cursorMoveEvent(game, e.clientX, e.clientY, (e.buttons % 2 == 1)));
-  document.body.addEventListener('mousedown', e => cursorMoveEvent(game, e.clientX, e.clientY, (e.buttons % 2 == 1)));
-  document.body.addEventListener('mouseup', e => cursorMoveEvent(game, e.clientX, e.clientY, (e.buttons % 2 == 1)));
-  document.body.addEventListener('touchmove', e => cursorMoveEvent(game, e.touches[0].clientX, e.touches[0].clientY, true));
+  document.body.addEventListener('mousemove', e => cursorMoveEvent(game, e.target, e.clientX, e.clientY, (e.buttons % 2 == 1)));
+  document.body.addEventListener('mousedown', e => {
+    if(game.settings['controls'] == 'thumbstick') {
+      if(e.target.closest('.virtual-input-widget') == game.ui.virtualInput) {
+        game.ui.virtualInput.inProgress = true;
+      } else {
+        game.ui.virtualInput.inProgress = false;
+      }
+    }
+    cursorMoveEvent(game, e.target, e.clientX, e.clientY, (e.buttons % 2 == 1));
+  });
+  document.body.addEventListener('mouseup', e => {
+    cursorMoveEvent(game, e.target, e.clientX, e.clientY, (e.buttons % 2 == 1));
+    if(game.settings['controls'] == 'thumbstick') {
+      game.ui.virtualInput.inProgress = false;
+    }
+  });
+  document.body.addEventListener('touchmove', e => cursorMoveEvent(game, e.target, e.touches[0].clientX, e.touches[0].clientY, true));
+  document.body.addEventListener('touchstart', e => {
+    if(game.settings['controls'] == 'thumbstick') {
+      if(e.target.closest('.virtual-input-widget') == game.ui.virtualInput) {
+        game.ui.virtualInput.inProgress = true;
+      } else {
+        game.ui.virtualInput.inProgress = false;
+      }
+    }
+    cursorMoveEvent(game, e.target, e.touches[0].clientX, e.touches[0].clientY, true);
+  });
+  document.body.addEventListener('touchend', e => {
+    if(game.settings['controls'] == 'thumbstick') {
+      cursorMoveEvent(game, e.target, 0, 0, false);
+      game.ui.virtualInput.inProgress = false;
+    }
+  });
   document.body.addEventListener('keydown', e => keyboardEvent(game, e.key, 'down'));
   document.body.addEventListener('keyup', e => keyboardEvent(game, e.key, 'up'));