From c8acfc3188df462263c5647cb1fcea05e699653f Mon Sep 17 00:00:00 2001 From: LoveEevee Date: Fri, 5 Oct 2018 20:03:59 +0300 Subject: [PATCH] View: Add touch controls --- public/index.html | 2 +- public/src/css/game.css | 59 +++++++++ public/src/js/assets.js | 7 +- public/src/js/controller.js | 6 +- public/src/js/game.js | 2 +- public/src/js/keyboard.js | 16 ++- public/src/js/loadsong.js | 9 +- public/src/js/scoresheet.js | 17 ++- public/src/js/songselect.js | 32 +++-- public/src/js/titlescreen.js | 9 +- public/src/js/view.js | 227 ++++++++++++++++++++++++++++++++++- public/src/views/game.html | 9 ++ 12 files changed, 362 insertions(+), 33 deletions(-) diff --git a/public/index.html b/public/index.html index 742ea9a..dc78b49 100644 --- a/public/index.html +++ b/public/index.html @@ -14,7 +14,7 @@ 太鼓の達人ウェブ - Taiko no Tatsujin Web - + diff --git a/public/src/css/game.css b/public/src/css/game.css index 8416bbe..7853dc4 100644 --- a/public/src/css/game.css +++ b/public/src/css/game.css @@ -51,3 +51,62 @@ pointer-events: none; z-index: 1; } +#touch-bg{ + display: none; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 50%; + background: url(/assets/img/touch_bg.png) center bottom; + background-size: cover; + overflow: hidden; +} +#touch-bg-blue{ + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: url(/assets/img/touch_bg_blue.png) center bottom; + background-size: cover; + opacity: 0; +} +#touch-drum{ + position: absolute; + right: 0; + bottom: 0; + left: 0; + text-align: center; + margin: auto; +} +#touch-drum img{ + width: 100%; +} +#touch-buttons{ + display: none; + position: absolute; + top: 7vh; + right: 2vh; + opacity: 0.5; + z-index: 5; +} +#touch-buttons img{ + width: 12vh; +} +.touch-visible #touch-bg, +.touch-visible #touch-buttons{ + display: block; +} +.touch-visible .window{ + width: 80vmin; + height: 53vmin; +} +.touch-visible #pause-menu .window button{ + font-size: 5vmin; +} +.touch-visible #pause-menu .window button.selected{ + color: #000; + background: #fff; + border-color: #ae7a26; +} diff --git a/public/src/js/assets.js b/public/src/js/assets.js index 6f1c873..2ec1a5d 100644 --- a/public/src/js/assets.js +++ b/public/src/js/assets.js @@ -39,7 +39,12 @@ var assets = { "bg_genre_7.png", "bg_score_p1.png", "bg_score_p2.png", - "badge_auto.png" + "badge_auto.png", + "touch_bg.png", + "touch_bg_blue.png", + "touch_drum.png", + "touch_pause.png", + "touch_fullscreen.png" ], "audioSfx": [ "don.wav", diff --git a/public/src/js/controller.js b/public/src/js/controller.js index da8ec3c..4e013db 100644 --- a/public/src/js/controller.js +++ b/public/src/js/controller.js @@ -1,9 +1,10 @@ class Controller{ - constructor(selectedSong, songData, autoPlayEnabled, multiplayer){ + constructor(selectedSong, songData, autoPlayEnabled, multiplayer, touchEnabled){ this.selectedSong = selectedSong this.songData = songData this.autoPlayEnabled = autoPlayEnabled this.multiplayer = multiplayer + this.touchEnabled = touchEnabled this.snd = this.multiplayer ? "_p" + this.multiplayer : "" var backgroundURL = "/songs/" + this.selectedSong.folder + "/bg.png" @@ -202,6 +203,9 @@ class Controller{ } } clean(){ + if(this.syncWith){ + this.syncWith.clean() + } this.stopMainLoop() this.keyboard.clean() this.view.clean() diff --git a/public/src/js/game.js b/public/src/js/game.js index 5498ac9..1b0d657 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -289,10 +289,10 @@ class Game{ this.controller.displayResults() this.musicFadeOut++ }else if(this.musicFadeOut === 3 && (ms >= started + 9600 && ms >= this.controller.mainAsset.duration * 1000 + 1250)){ + this.controller.clean() if(this.controller.scoresheet){ this.controller.scoresheet.startRedraw() } - this.controller.clean() } } } diff --git a/public/src/js/keyboard.js b/public/src/js/keyboard.js index 67f6494..30871d1 100644 --- a/public/src/js/keyboard.js +++ b/public/src/js/keyboard.js @@ -79,11 +79,12 @@ class Keyboard{ this.setKey(keyCode, false) } }) + }else{ + this.checkKeySound(this.kbd["don_l"], "don") + this.checkKeySound(this.kbd["don_r"], "don") + this.checkKeySound(this.kbd["ka_l"], "ka") + this.checkKeySound(this.kbd["ka_r"], "ka") } - this.checkKeySound(this.kbd["don_l"], "don") - this.checkKeySound(this.kbd["don_r"], "don") - this.checkKeySound(this.kbd["ka_l"], "ka") - this.checkKeySound(this.kbd["ka_r"], "ka") } checkMenuKeys(){ if(!this.controller.multiplayer){ @@ -171,7 +172,7 @@ class Keyboard{ var circles = this.controller.getCircles() var circle = circles[this.controller.getCurrentCircle()] if( - (keyCode === this.kbd["don_l"] || keyCode === this.kbd["don_r"]) + sound === "don" && circle && !circle.getPlayed() && circle.getType() === "balloon" @@ -191,6 +192,11 @@ class Keyboard{ if(down){ this.keys[keyCode] = true this.keyTime[keyCode] = ms + if(keyCode == this.kbd.don_l || keyCode == this.kbd.don_r){ + this.checkKeySound(keyCode, "don") + }else if(keyCode == this.kbd.ka_l || keyCode == this.kbd.ka_r){ + this.checkKeySound(keyCode, "ka") + } }else{ this.keys[keyCode] = false this.waitKeyupScore[keyCode] = false diff --git a/public/src/js/loadsong.js b/public/src/js/loadsong.js index 0d58857..3ae4ebf 100644 --- a/public/src/js/loadsong.js +++ b/public/src/js/loadsong.js @@ -1,8 +1,9 @@ class loadSong{ - constructor(selectedSong, autoPlayEnabled, multiplayer){ + constructor(selectedSong, autoPlayEnabled, multiplayer, touchEnabled){ this.selectedSong = selectedSong - this.multiplayer = multiplayer this.autoPlayEnabled = autoPlayEnabled + this.multiplayer = multiplayer + this.touchEnabled = touchEnabled loader.changePage("loadsong") this.run() } @@ -81,7 +82,7 @@ class loadSong{ }else if(event.type === "gamestart"){ this.clean() loader.changePage("game") - var taikoGame1 = new Controller(this.selectedSong, this.songData, false, 1) + var taikoGame1 = new Controller(this.selectedSong, this.songData, false, 1, this.touchEnabled) var taikoGame2 = new Controller(this.selectedSong2, this.song2Data, true, 2) taikoGame1.run(taikoGame2) } @@ -93,7 +94,7 @@ class loadSong{ }else{ this.clean() loader.changePage("game") - var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled) + var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled) taikoGame.run() } } diff --git a/public/src/js/scoresheet.js b/public/src/js/scoresheet.js index 8acd94c..5c2c7e4 100644 --- a/public/src/js/scoresheet.js +++ b/public/src/js/scoresheet.js @@ -29,8 +29,6 @@ class Scoresheet{ this.redrawRunning = true this.redrawBind = this.redraw.bind(this) this.redraw() - pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this)) - pageEvents.add(this.canvas, "mousedown", this.mouseDown.bind(this)) assets.sounds["results"].play() assets.sounds["bgm_result"].playLoop(3, false, 0, 0.847, 17.689) @@ -56,7 +54,11 @@ class Scoresheet{ } } mouseDown(event){ - if(event.which !== 1){ + if(event.type === "touchstart"){ + event.preventDefault() + this.canvas.style.cursor = "" + this.state.pointerLocked = true + }else if(event.which !== 1){ return } this.toNext() @@ -81,6 +83,10 @@ class Scoresheet{ requestAnimationFrame(this.redrawBind) this.winW = null this.winH = null + + pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this)) + pageEvents.add(this.canvas, "mousedown", this.mouseDown.bind(this)) + pageEvents.add(this.canvas, "touchstart", this.mouseDown.bind(this)) } redraw(){ @@ -218,7 +224,9 @@ class Scoresheet{ if(elapsed >= 0){ if(this.state.hasPointer === 0){ this.state.hasPointer = 1 - this.canvas.style.cursor = "pointer" + if(!this.state.pointerLocked){ + this.canvas.style.cursor = "pointer" + } } ctx.save() ctx.setTransform(1, 0, 0, 1, 0, 0) @@ -643,6 +651,7 @@ class Scoresheet{ this.redrawRunning = false pageEvents.keyRemove(this, "all") pageEvents.remove(this.canvas, "mousedown") + pageEvents.remove(this.canvas, "touchstart") delete this.ctx delete this.canvas } diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index 75dbfe1..33a0f83 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -197,6 +197,7 @@ class SongSelect{ pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this)) pageEvents.add(this.canvas, "mousemove", this.mouseMove.bind(this)) pageEvents.add(this.canvas, "mousedown", this.mouseDown.bind(this)) + pageEvents.add(this.canvas, "touchstart", this.mouseDown.bind(this)) } keyDown(event, code){ @@ -256,10 +257,21 @@ class SongSelect{ } mouseDown(event){ - if(event.which !== 1){ - return + if(event.type === "mousedown"){ + if(event.which !== 1){ + return + } + var mouse = this.mouseOffset(event.offsetX, event.offsetY) + var shift = event.shiftKey + var ctrl = event.ctrlKey + var touch = false + }else{ + event.preventDefault() + var mouse = this.mouseOffset(event.touches[0].pageX, event.touches[0].pageY) + var shift = false + var ctrl = false + var touch = true } - var mouse = this.mouseOffset(event) if(this.state.screen === "song"){ var moveBy = this.songSelMouse(mouse.x, mouse.y) if(moveBy === 0){ @@ -276,12 +288,12 @@ class SongSelect{ ){ this.toSongSelect() }else if(moveBy !== null){ - this.toLoadSong(moveBy - 1, event.shiftKey, event.ctrlKey) + this.toLoadSong(moveBy - 1, shift, ctrl, touch) } } } mouseMove(event){ - var mouse = this.mouseOffset(event) + var mouse = this.mouseOffset(event.offsetX, event.offsetY) var moveTo = null if(this.state.screen === "song"){ var moveTo = this.songSelMouse(mouse.x, mouse.y) @@ -298,10 +310,10 @@ class SongSelect{ } this.pointer(moveTo !== null) } - mouseOffset(event){ + mouseOffset(offsetX, offsetY){ return { - x: (event.offsetX * this.pixelRatio - this.winW / 2) / this.ratio + 1024 / 2, - y: (event.offsetY * this.pixelRatio - this.winH / 2) / this.ratio + 720 / 2 + x: (offsetX * this.pixelRatio - this.winW / 2) / this.ratio + 1024 / 2, + y: (offsetY * this.pixelRatio - this.winH / 2) / this.ratio + 720 / 2 } } pointer(enabled){ @@ -425,7 +437,7 @@ class SongSelect{ assets.sounds["cancel"].play() } } - toLoadSong(difficulty, shift, ctrl){ + toLoadSong(difficulty, shift, ctrl, touch){ this.clean() var selectedSong = this.songs[this.selectedSong] assets.sounds["diffsel"].stop() @@ -439,7 +451,7 @@ class SongSelect{ "folder": selectedSong.id, "difficulty": this.difficultyId[difficulty], "category": selectedSong.category - }, shift, ctrl) + }, shift, ctrl, touch) } toTitleScreen(){ assets.sounds["cancel"].play() diff --git a/public/src/js/titlescreen.js b/public/src/js/titlescreen.js index 905d8d4..d5cdfa9 100644 --- a/public/src/js/titlescreen.js +++ b/public/src/js/titlescreen.js @@ -3,7 +3,8 @@ class Titlescreen{ loader.changePage("titlescreen") this.titleScreen = document.getElementById("title-screen") pageEvents.keyOnce(this, 13, "down").then(this.onPressed.bind(this)) - pageEvents.once(this.titleScreen, "click").then(this.onPressed.bind(this)) + pageEvents.once(this.titleScreen, "mousedown").then(this.onPressed.bind(this)) + pageEvents.once(this.titleScreen, "touchstart").then(this.onPressed.bind(this)) assets.sounds["title"].play() this.gamepad = new Gamepad({ "start": ["b", "x", "y", "start"], @@ -15,6 +16,7 @@ class Titlescreen{ }) } onPressed(){ + this.titleScreen.style.cursor = "auto" this.clean() assets.sounds["don"].play() setTimeout(this.goNext.bind(this), 500) @@ -22,7 +24,7 @@ class Titlescreen{ goNext(){ if(localStorage.getItem("tutorial") !== "true"){ new Tutorial() - } else { + }else{ new SongSelect() } } @@ -30,7 +32,8 @@ class Titlescreen{ this.gamepad.clean() assets.sounds["title"].stop() pageEvents.keyRemove(this, 13) - pageEvents.remove(this.titleScreen, "click") + pageEvents.remove(this.titleScreen, "mousedown") + pageEvents.remove(this.titleScreen, "touchstart") delete this.titleScreen } } diff --git a/public/src/js/view.js b/public/src/js/view.js index cdba97b..22e5aec 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -7,6 +7,7 @@ class View{ this.pauseMenu = document.getElementById("pause-menu") this.cursor = document.getElementById("cursor") + this.gameDiv = document.getElementById("game") var docW = document.body.offsetWidth var docH = document.body.offsetHeight @@ -14,7 +15,7 @@ class View{ this.canvas = new ScalableCanvas("canvas-p2", docW, docH / 3 * 2) this.canvas.canvas.style.position = "absolute" this.canvas.canvas.style.top = "33%" - document.getElementById("game").appendChild(this.canvas.canvas) + this.gameDiv.appendChild(this.canvas.canvas) }else{ this.canvas = new ScalableCanvas("canvas", docW, docH) } @@ -49,6 +50,39 @@ class View{ this.beatInterval = this.controller.getSongData().beatInfo.beatInterval this.assets = new ViewAssets(this) + + this.touch = { + don_l: -Infinity, + don_r: -Infinity, + don_c: -Infinity, + ka_l: -Infinity, + ka_r: -Infinity, + ka_c: -Infinity, + cursor: { + ms: -Infinity, + x: 0, + y: 0 + } + } + + if(this.controller.touchEnabled){ + this.touchEnabled = true + + this.touchBgDiv = document.getElementById("touch-bg") + this.touchBgBlueDiv = document.getElementById("touch-bg-blue") + this.touchDrumDiv = document.getElementById("touch-drum") + this.gameDiv.classList.add("touch-visible") + + pageEvents.add(this.canvas.canvas, "touchstart", this.ontouch.bind(this)) + + this.touchFullBtn = document.getElementById("touch-full-btn") + pageEvents.add(this.touchFullBtn, "click", this.toggleFullscreen) + + this.touchPauseBtn = document.getElementById("touch-pause-btn") + pageEvents.add(this.touchPauseBtn, "click", () => { + this.controller.togglePauseMenu() + }) + } } run(){ this.ctx.font = "normal 14pt TnT" @@ -133,6 +167,37 @@ class View{ this.diffW = this.diffH * diffRatio this.diffX = this.taikoX * 0.10 this.diffY = this.taikoY * 1.05 + this.taikoH * 0.19 + this.touchDrum = (() => { + var sw = 842 + var sh = 340 + var x = 0 + var y = this.barY + this.barH + 5 + var paddingTop = this.barH * 0.1 + var w = this.winW + var maxH = this.winH - (this.barY + this.barH + 5) + var h = maxH - paddingTop + if(w / h >= sw / sh){ + w = h / sh * sw + x = (this.winW - w) / 2 + y += paddingTop + }else{ + h = w / sw * sh + y = y + (maxH - h) + } + return { + x: x, y: y, w: w, h: h + } + })() + this.touchCircle = (() => { + var padTop = this.touchDrum.h * 0.062 + var padLeft = this.touchDrum.h * 0.05 + return { + x: this.winW / 2, + y: this.winH + padTop, + rx: this.touchDrum.w / 2 - padLeft * 2, + ry: this.touchDrum.h + } + })() } refresh(){ this.positionning() @@ -158,7 +223,10 @@ class View{ this.updateDonFaces() this.drawGogoTime() this.mouseIdle() - this.assets.drawAssets("foreground") + if(!this.touchEnabled){ + this.assets.drawAssets("foreground") + } + this.drawTouch() //this.drawTime() } updateDonFaces(){ @@ -809,6 +877,147 @@ class View{ don.setAnimationEnd(ms + length * don.speed, don.normalAnimation) } } + drawTouch(){ + if(this.touchEnabled){ + var ms = this.controller.getElapsedTime() + this.ctx.save() + + var bgHeight = (this.winH - (this.barY + this.barH + 5)) / this.canvas.scale + if(bgHeight !== this.touchBgHeight){ + this.touchBgHeight = bgHeight + this.touchBgDiv.style.height = bgHeight + "px" + } + var drumWidth = this.touchDrum.w / this.canvas.scale + var drumHeight = this.touchDrum.h / this.canvas.scale + if(drumHeight !== this.touchDrumHeight || drumWidth !== this.touchDrumWidth){ + this.touchDrumWidth = drumWidth + this.touchDrumHeight = drumHeight + this.touchDrumDiv.style.width = drumWidth + "px" + this.touchDrumDiv.style.height = drumHeight + "px" + } + + var lastKa = this.touch.ka_l > this.touch.ka_r ? this.touch.ka_l : this.touch.ka_r + if(ms < lastKa + 150){ + if(!this.blueBgShown){ + this.blueBgShown = true + this.touchBgBlueDiv.style.opacity = 0.5 + } + }else if(this.blueBgShown){ + this.blueBgShown = false + this.touchBgBlueDiv.style.opacity = 0 + } + + var c = this.touchCircle + var pi = Math.PI + var lastDon = this.touch.don_l > this.touch.don_r ? this.touch.don_l : this.touch.don_r + if(lastDon > ms - 150){ + this.ctx.fillStyle = "rgba(239, 86, 51, 0.5)" + this.ctx.beginPath() + this.ctx.ellipse(c.x, c.y, c.rx * 0.9, c.ry * 0.9, 0, pi, 0) + this.ctx.fill() + } + if(this.touch.don_c > ms - 150){ + this.ctx.beginPath() + this.ctx.ellipse(c.x, c.y, c.rx * 0.6, c.ry * 0.6, 0, pi, 0) + this.ctx.fill() + } + if(this.touch.ka_c > ms - 150){ + this.ctx.fillStyle = "rgba(33, 191, 211, 0.5)" + this.ctx.beginPath() + this.ctx.ellipse(c.x, c.y, c.rx * 1.3, c.ry * 1.3, 0, pi, 0) + this.ctx.ellipse(c.x, c.y, c.rx * 0.9, c.ry * 0.9, 0, 0, pi, true) + this.ctx.fill() + } + + this.ctx.restore() + } + } + ontouch(event){ + for(let touch of event.touches){ + event.preventDefault() + var scale = this.canvas.scale + var pageX = touch.pageX * scale + var pageY = touch.pageY * scale + + this.touch.cursor = { + ms: this.controller.getElapsedTime(), + x: pageX, + y: pageY + } + + var c = this.touchCircle + var pi = Math.PI + var inPath = () => this.ctx.isPointInPath(pageX, pageY) + + this.ctx.beginPath() + this.ctx.ellipse(c.x, c.y, c.rx * 0.6, c.ry * 0.6, 0, pi, 0) + + if(inPath()){ + this.touchNote("don_c") + }else{ + this.ctx.beginPath() + this.ctx.ellipse(c.x, c.y, c.rx * 0.9, c.ry * 0.9, 0, pi, 0) + + if(inPath()){ + if(pageX < this.winW / 2){ + this.touchNote("don_l") + }else{ + this.touchNote("don_r") + } + }else{ + + this.ctx.beginPath() + this.ctx.ellipse(c.x, c.y, c.rx * 1.3, c.ry * 1.3, 0, pi, 0) + + if(inPath()){ + this.touchNote("ka_c") + }else if(pageX < this.winW / 2){ + this.touchNote("ka_l") + }else{ + this.touchNote("ka_r") + } + } + } + } + } + touchNote(note){ + var keyboard = this.controller.keyboard + var kbd = keyboard.getBindings() + var ms = this.controller.game.getAccurateTime() + this.touch[note] = ms + if(note === "don_c"){ + this.touchNote("don_l") + this.touchNote("don_r") + }else if(note === "ka_c"){ + this.touchNote("ka_l") + this.touchNote("ka_r") + }else{ + keyboard.setKey(kbd[note], false) + keyboard.setKey(kbd[note], true, ms) + } + } + toggleFullscreen(){ + var root = document.documentElement + if("requestFullscreen" in root){ + if(document.fullscreenElement){ + document.exitFullscreen() + }else{ + root.requestFullscreen() + } + }else if("webkitRequestFullscreen" in root){ + if(document.webkitFullscreenElement){ + document.webkitExitFullscreen() + }else{ + root.webkitRequestFullscreen() + } + }else if("mozRequestFullScreen" in root){ + if(document.mozFullScreenElement){ + document.mozCancelFullScreen() + }else{ + root.mozRequestFullScreen() + } + } + } onmousemove(event){ this.lastMousemove = this.controller.getElapsedTime() this.cursorHidden = false @@ -832,10 +1041,22 @@ class View{ pageEvents.mouseRemove(this) if(this.controller.multiplayer === 2){ this.canvas.canvas.parentNode.removeChild(this.canvas.canvas) + }else{ + this.cursor.parentNode.removeChild(this.cursor) + } + if(this.touchEnabled){ + pageEvents.remove(this.canvas.canvas, "touchstart") + pageEvents.remove(this.touchFullBtn, "click") + pageEvents.remove(this.touchPauseBtn, "click") + this.gameDiv.classList.remove("touch-visible") + delete this.touchBgDiv + delete this.touchFullBtn + delete this.touchPauseBtn + delete this.touchBgBlueDiv } - this.cursor.parentNode.removeChild(this.cursor) delete this.pauseMenu delete this.cursor + delete this.gameDiv delete this.canvas delete this.ctx } diff --git a/public/src/views/game.html b/public/src/views/game.html index fe5ef6f..c6e44e8 100644 --- a/public/src/views/game.html +++ b/public/src/views/game.html @@ -1,5 +1,11 @@

+
+
+
+ +
+
@@ -8,5 +14,8 @@
+
+ +