From 7e79dc8a391506429823e586117fa6d65b40949d Mon Sep 17 00:00:00 2001 From: Legionboot Date: Fri, 3 Oct 2025 21:29:52 +0600 Subject: [PATCH] Add touch controls to Minecraft page --- Minecraft.html | 498 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 443 insertions(+), 55 deletions(-) diff --git a/Minecraft.html b/Minecraft.html index bbbc0b9..2a1c5a6 100644 --- a/Minecraft.html +++ b/Minecraft.html @@ -7,13 +7,16 @@ Minecraft - - - - - - - - - - - @@ -193,6 +288,8 @@ window.worlds = document.getElementById("worlds") window.quota = document.getElementById("quota") var hoverbox = document.getElementById("onhover") +const touchControlsContainer = document.getElementById("touch-controls") +const touchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints && navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints && navigator.msMaxTouchPoints > 0) ctx.canvas.width = window.innerWidth ctx.canvas.height = window.innerHeight @@ -1216,12 +1313,15 @@ html[screen].onexit() } - previousScreen = screen - screen = newScene - mouseDown = false - drawScreens[screen]() - Button.draw() - Slider.draw() + previousScreen = screen + screen = newScene + mouseDown = false + if (setupTouchControls.setActive) { + setupTouchControls.setActive(newScene === "play") + } + drawScreens[screen]() + Button.draw() + Slider.draw() } let hitBox = {} let holding = 0 @@ -1266,17 +1366,301 @@ changeScene("play") } - let gl - function getPointer() { - if (canvas.requestPointerLock) { - canvas.requestPointerLock() - } - } - function releasePointer() { - if (doc.exitPointerLock) { - doc.exitPointerLock() - } - } + let gl + function getPointer() { + if (touchDevice) { + return + } + if (canvas.requestPointerLock) { + canvas.requestPointerLock() + } + } + function releasePointer() { + if (touchDevice) { + return + } + if (doc.exitPointerLock) { + doc.exitPointerLock() + } + } + + function setupTouchControls() { + if (!touchDevice || !touchControlsContainer || setupTouchControls.initialized) { + return + } + touchControlsContainer.classList.remove("hidden") + const moveJoystick = touchControlsContainer.querySelector("#move-joystick") + const lookJoystick = touchControlsContainer.querySelector("#look-joystick") + if (!moveJoystick || !lookJoystick) { + return + } + const moveThumb = moveJoystick.querySelector(".joystick-thumb") + const lookThumb = lookJoystick.querySelector(".joystick-thumb") + if (!moveThumb || !lookThumb) { + return + } + + setupTouchControls.initialized = true + + const moveLimit = (moveJoystick.offsetWidth - moveThumb.offsetWidth) / 2 + const lookLimit = (lookJoystick.offsetWidth - lookThumb.offsetWidth) / 2 + const MOVE_DEADZONE = 12 + const LOOK_MULTIPLIER = 2 + const moveState = { id: null, startX: 0, startY: 0 } + const lookState = { id: null, startX: 0, startY: 0, prevX: 0, prevY: 0 } + + function clampToRadius(dx, dy, limit) { + const distance = Math.sqrt(dx * dx + dy * dy) + if (!distance) { + return { x: 0, y: 0 } + } + if (distance <= limit) { + return { x: dx, y: dy } + } + const angle = Math.atan2(dy, dx) + return { + x: Math.cos(angle) * limit, + y: Math.sin(angle) * limit, + } + } + + function updateMove(dx, dy) { + const clamped = clampToRadius(dx, dy, moveLimit) + moveThumb.style.transform = `translate(${clamped.x}px, ${clamped.y}px)` + Key.w = clamped.y < -MOVE_DEADZONE + Key.s = clamped.y > MOVE_DEADZONE + Key.a = clamped.x < -MOVE_DEADZONE + Key.d = clamped.x > MOVE_DEADZONE + } + + function resetMove() { + moveThumb.style.transform = "translate(0px, 0px)" + Key.w = false + Key.s = false + Key.a = false + Key.d = false + moveState.id = null + } + + function updateLook(dx, dy) { + const clamped = clampToRadius(dx, dy, lookLimit) + lookThumb.style.transform = `translate(${clamped.x}px, ${clamped.y}px)` + } + + function resetLook() { + lookThumb.style.transform = "translate(0px, 0px)" + lookState.id = null + } + + moveJoystick.addEventListener("touchstart", e => { + if (!touchControlsContainer.classList.contains("touch-active")) { + return + } + if (moveState.id !== null || !e.changedTouches.length) { + return + } + const touch = e.changedTouches[0] + moveState.id = touch.identifier + moveState.startX = touch.clientX + moveState.startY = touch.clientY + updateMove(0, 0) + e.preventDefault() + }, { passive: false }) + + lookJoystick.addEventListener("touchstart", e => { + if (!touchControlsContainer.classList.contains("touch-active")) { + return + } + if (lookState.id !== null || !e.changedTouches.length) { + return + } + const touch = e.changedTouches[0] + lookState.id = touch.identifier + lookState.startX = touch.clientX + lookState.startY = touch.clientY + lookState.prevX = touch.clientX + lookState.prevY = touch.clientY + updateLook(0, 0) + e.preventDefault() + }, { passive: false }) + + const handleDocumentMove = e => { + if (!touchControlsContainer.classList.contains("touch-active")) { + return + } + let prevented = false + if (moveState.id !== null) { + const touch = Array.from(e.touches).find(t => t.identifier === moveState.id) + if (touch) { + updateMove(touch.clientX - moveState.startX, touch.clientY - moveState.startY) + prevented = true + } + } + if (lookState.id !== null) { + const touch = Array.from(e.touches).find(t => t.identifier === lookState.id) + if (touch) { + updateLook(touch.clientX - lookState.startX, touch.clientY - lookState.startY) + const deltaX = (touch.clientX - lookState.prevX) * LOOK_MULTIPLIER + const deltaY = (touch.clientY - lookState.prevY) * LOOK_MULTIPLIER + lookState.prevX = touch.clientX + lookState.prevY = touch.clientY + if (screen === "play") { + mmoved({ movementX: deltaX, movementY: deltaY }) + } + prevented = true + } + } + if (prevented) { + e.preventDefault() + } + } + + const handleDocumentEnd = e => { + let prevented = false + if (moveState.id !== null) { + for (let touch of e.changedTouches) { + if (touch.identifier === moveState.id) { + resetMove() + prevented = true + break + } + } + } + if (lookState.id !== null) { + for (let touch of e.changedTouches) { + if (touch.identifier === lookState.id) { + resetLook() + prevented = true + break + } + } + } + if (prevented) { + e.preventDefault() + } + } + + document.addEventListener("touchmove", handleDocumentMove, { passive: false }) + document.addEventListener("touchend", handleDocumentEnd, { passive: false }) + document.addEventListener("touchcancel", handleDocumentEnd, { passive: false }) + + const setActive = active => { + if (active) { + touchControlsContainer.classList.add("touch-active") + } else { + touchControlsContainer.classList.remove("touch-active") + resetMove() + resetLook() + Key.leftMouse = false + Key.rightMouse = false + Key[" "] = false + Key.shift = false + mouseDown = false + if (screen === "play" && p && p.sneaking) { + p.sneaking = false + p.speed = 0.075 + p.bottomH = 1.62 + } + } + } + setupTouchControls.setActive = setActive + setActive(false) + + const bindHoldButton = (id, onStart, onEnd) => { + const element = document.getElementById(id) + if (!element) { + return + } + let activeId = null + const start = e => { + if (!touchControlsContainer.classList.contains("touch-active")) { + return + } + if (activeId !== null || !e.changedTouches.length) { + return + } + const touch = e.changedTouches[0] + activeId = touch.identifier + onStart && onStart() + element.classList.add("active") + e.preventDefault() + } + const end = e => { + if (activeId === null) { + return + } + for (let touch of e.changedTouches) { + if (touch.identifier === activeId) { + activeId = null + onEnd && onEnd() + element.classList.remove("active") + e.preventDefault() + break + } + } + } + element.addEventListener("touchstart", start, { passive: false }) + element.addEventListener("touchend", end, { passive: false }) + element.addEventListener("touchcancel", end, { passive: false }) + } + + bindHoldButton("attack-button", () => { + Key.leftMouse = true + mouseDown = true + p.lastBreak = Date.now() - 300 + if (screen === "play") { + changeWorldBlock(0) + } + }, () => { + Key.leftMouse = false + mouseDown = Key.rightMouse + }) + + bindHoldButton("place-button", () => { + Key.rightMouse = true + mouseDown = true + p.lastPlace = Date.now() - 300 + if (screen === "play") { + newWorldBlock() + } + }, () => { + Key.rightMouse = false + mouseDown = Key.leftMouse + }) + + bindHoldButton("jump-button", () => { + Key[" "] = true + if (screen === "play" && p && !p.spectator) { + if (Date.now() < p.lastJump + 400) { + p.flying ^= true + } else { + p.lastJump = Date.now() + } + } + }, () => { + Key[" "] = false + }) + + bindHoldButton("sneak-button", () => { + Key.shift = true + if (screen === "play" && p && !p.flying) { + p.sneaking = true + if (p.sprinting) { + p.FOV(settings.fov, 100) + } + p.sprinting = false + p.speed = 0.03 + p.bottomH = 1.32 + } + }, () => { + Key.shift = false + if (screen === "play" && p && p.sneaking) { + p.sneaking = false + p.speed = 0.075 + p.bottomH = 1.62 + } + }) + } let Block = { top: 0x4, @@ -5427,16 +5811,20 @@ message.innerHTML = '.oot lanigiro eht tuo kcehc ot>rbrb<.dralliW yb >a/"wen_"=tegrat "8676731005517465/cm/sc/gro.ymedacanahk.www//:sptth"=ferh a< fo>rb