├── index.html ├── style.css └── script.js /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Table-Tennis AI 11 | 12 | 13 |
14 |
0
15 |
0
16 |
17 |
18 |
19 |
20 | 21 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::after, 3 | *::before { 4 | box-sizing: border-box; 5 | } 6 | 7 | :root { 8 | --hue: 200; 9 | --saturation: 50%; 10 | --foreground-color: hsl(var(--hue), var(--saturation), 75%); 11 | --background-color: hsl(var(--hue), var(--saturation), 20%); 12 | } 13 | 14 | body { 15 | margin: 0; 16 | background-color: var(--background-color); 17 | overflow: hidden; 18 | } 19 | 20 | .paddle { 21 | --position: 50; 22 | 23 | position: absolute; 24 | background-color: var(--foreground-color); 25 | top: calc(var(--position) * 1vh); 26 | transform: translateY(-50%); 27 | width: 1vh; 28 | height: 10vh; 29 | } 30 | 31 | .paddle.left { 32 | left: 1vw; 33 | } 34 | 35 | .paddle.right { 36 | right: 1vw; 37 | } 38 | 39 | .ball { 40 | --x: 50; 41 | --y: 50; 42 | 43 | position: absolute; 44 | background-color: var(--foreground-color); 45 | left: calc(var(--x) * 1vw); 46 | top: calc(var(--y) * 1vh); 47 | border-radius: 50%; 48 | transform: translate(-50%, -50%); 49 | width: 2.5vh; 50 | height: 2.5vh; 51 | } 52 | 53 | .score { 54 | display: flex; 55 | justify-content: center; 56 | font-weight: bold; 57 | font-size: 7vh; 58 | color: var(--foreground-color); 59 | } 60 | 61 | .score>* { 62 | flex-grow: 1; 63 | flex-basis: 0; 64 | padding: 0 2vh; 65 | margin: 1vh 0; 66 | opacity: .5; 67 | } 68 | 69 | .score> :first-child { 70 | text-align: right; 71 | border-right: .5vh solid var(--foreground-color); 72 | } -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | import Ball from "./Ball.js" 2 | import Paddle from "./Paddle.js" 3 | 4 | const ball = new Ball(document.getElementById("ball")) 5 | const playerPaddle = new Paddle(document.getElementById("player-paddle")) 6 | const computerPaddle = new Paddle(document.getElementById("computer-paddle")) 7 | const playerScoreElem = document.getElementById("player-score") 8 | const computerScoreElem = document.getElementById("computer-score") 9 | 10 | let lastTime 11 | function update(time) { 12 | if (lastTime != null) { 13 | const delta = time - lastTime 14 | ball.update(delta, [playerPaddle.rect(), computerPaddle.rect()]) 15 | computerPaddle.update(delta, ball.y) 16 | const hue = parseFloat( 17 | getComputedStyle(document.documentElement).getPropertyValue("--hue") 18 | ) 19 | 20 | document.documentElement.style.setProperty("--hue", hue + delta * 0.01) 21 | 22 | if (isLose()) handleLose() 23 | } 24 | 25 | lastTime = time 26 | window.requestAnimationFrame(update) 27 | } 28 | 29 | function isLose() { 30 | const rect = ball.rect() 31 | return rect.right >= window.innerWidth || rect.left <= 0 32 | } 33 | 34 | function handleLose() { 35 | const rect = ball.rect() 36 | if (rect.right >= window.innerWidth) { 37 | playerScoreElem.textContent = parseInt(playerScoreElem.textContent) + 1 38 | } else { 39 | computerScoreElem.textContent = parseInt(computerScoreElem.textContent) + 1 40 | } 41 | ball.reset() 42 | computerPaddle.reset() 43 | } 44 | 45 | document.addEventListener("mousemove", e => { 46 | playerPaddle.position = (e.y / window.innerHeight) * 100 47 | }) 48 | 49 | window.requestAnimationFrame(update) --------------------------------------------------------------------------------