Skip to content

Commit

Permalink
v1.0.0a
Browse files Browse the repository at this point in the history
  • Loading branch information
archtaurus committed Mar 22, 2021
1 parent e55c7a0 commit 11b5296
Show file tree
Hide file tree
Showing 10 changed files with 845 additions and 1 deletion.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,27 @@
# multiplayer-snake-game
# Multiplayer Snake Game

Screenshot on PC

![](screenshot_pc.png)

Screenshot on phone

![](screenshot_phone.png)

## Run

``` shell
yarn install
yarn start
firefox http://localhost:8000
```

## Features

- [X] simple, clear and responsive UI
- [x] multiplayer and observers

## History

- Version 1.0.0a
- alpha version
22 changes: 22 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "snake",
"version": "1.0.0",
"main": "server.js",
"repository": "https://github.com/archtaurus/snake.git",
"author": "Zhao Xin <7176466@qq.com>",
"license": "MIT",
"description": "multiplayer snake game with socket.io",
"keywords": [
"socket.io",
"multiplayer",
"snake",
"game"
],
"scripts": {
"start": "npx nodemon server.js"
},
"dependencies": {
"express": "^4.17.1",
"socket.io": "^4.0.0"
}
}
68 changes: 68 additions & 0 deletions public/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const client = io()
const context = canvas.getContext('2d')
const backgroudColor = '#231f20'
const playerColor = '#ccc'
const enemyColor = '#666'
const player1Color = '#c33'
const player2Color = '#33c'
const appleColor = '#e66916'
let canvasSize

function drawGame(game) {
const cellSize = canvasSize / game.grids
context.fillStyle = backgroudColor
context.fillRect(0, 0, canvasSize, canvasSize)

const isPlayer = client.id == game.players[0].id || client.id == game.players[1].id

context.fillStyle = isPlayer ? (game.players[0].id == client.id ? playerColor : enemyColor) : player1Color
game.players[0].snake.map((cell) => context.fillRect(cellSize * cell.x, cellSize * cell.y, cellSize, cellSize))

context.fillStyle = isPlayer ? (game.players[1].id == client.id ? playerColor : enemyColor) : player2Color
game.players[1].snake.map((cell) => context.fillRect(cellSize * cell.x, cellSize * cell.y, cellSize, cellSize))

context.fillStyle = appleColor
context.fillRect(cellSize * game.apple.x, cellSize * game.apple.y, cellSize, cellSize)

// if (!isPlayer) {
// document.getElementById('player1score').innerText = game.players[0].snake.length
// document.getElementById('player2score').innerText = game.players[1].snake.length
// }

if (isPlayer) {
document.getElementById('player1score').classList.value = 'player'
document.getElementById('player2score').classList.value = 'enemy'

if (game.players[0].id == client.id) {
document.getElementById('player1score').innerText = game.players[0].snake.length
document.getElementById('player2score').innerText = game.players[1].snake.length
} else {
document.getElementById('player1score').innerText = game.players[1].snake.length
document.getElementById('player2score').innerText = game.players[0].snake.length
}
} else {
document.getElementById('player1score').classList.value = 'player1'
document.getElementById('player1score').innerText = game.players[0].snake.length

document.getElementById('player2score').classList.value = 'player2'
document.getElementById('player2score').innerText = game.players[1].snake.length
}
// if (game.players[0].id == client.id) {
// } else {
// document.getElementById('player1score').innerText = game.players[1].snake.length
// document.getElementById('player2score').innerText = game.players[0].snake.length
// }

if (!isPlayer) document.getElementById('controller').classList.add('hidden')
}

const resize = () => {
canvasSize = canvas.height = canvas.width
}

window.addEventListener('resize', resize)
window.addEventListener('keydown', (e) => client.emit('keydown', e.key))

client.on('game', (game) => drawGame(game))

resize()
Binary file added public/favicon.ico
Binary file not shown.
25 changes: 25 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Multiplayer Snake Game</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<h1>Multiplayer Snake Game</h1>
<canvas id="canvas"></canvas>
<p id="scores">
<span id="player1score">3</span>
<span id="player2score">3</span>
</p>
<div id="controller">
<button id="up" onclick="client.emit('keydown', 'ArrowUp')"></button>
<button id="left" onclick="client.emit('keydown', 'ArrowLeft')"></button>
<button id="right" onclick="client.emit('keydown', 'ArrowRight')"></button>
<button id="down" onclick="client.emit('keydown', 'ArrowDown')"></button>
</div>
<script src="/socket.io/socket.io.min.js"></script>
<script src="/client.js"></script>
</body>
</html>
105 changes: 105 additions & 0 deletions public/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
* {
margin: 0px;
padding: 0px;
box-sizing: border-box;
}

body {
padding: 8px;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #ccc;
background-color: #333;
}
h1 {
margin-top: 1em;
}
canvas {
margin-top: 2em;
width: 100%;
max-width: 600px;
border: 1px solid #ccc;
}
#scores {
margin-top: 2em;
max-width: 600px;
width: 100%;
display: flex;
justify-content: space-around;
gap: 8px;
font-size: 1.5em;
}

#controller {
width: 160px;
height: 120px;
margin-top: 2rem;
display: none;
/* gap: 8px; */
grid-template-areas:
'. U U .'
'L L R R'
'. D D .';
}
#controller button {
font-size: 1.5em;
border: 2px solid #ccc;
background-color: #666;
color: #ccc;
border-radius: 100px;
width: 50px;
height: 50px;
outline: none;
}
#controller button:hover {
color: #666;
background-color: yellowgreen;
}

#up {
grid-area: U;
}
#left {
grid-area: L;
}
#right {
grid-area: R;
}
#down {
grid-area: D;
}
.hidden {
display: none !important;
}
@media only screen and (max-width: 600px) {
body {
justify-content: start !important;
}
h1 {
font-size: 1.5em;
}
#controller {
display: grid;
}
}

#scores span {
padding: 0.5rem 1rem;
}

.player1 {
background-color: #c33;
}
.player2 {
background-color: #33c;
}
.player {
background-color: #ccc;
color: #333;
}
.enemy {
background-color: #666;
}
Binary file added screenshot_pc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshot_phone.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 111 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
const express = require('express')
const app = express()
app.use(express.static('public'))
const host = process.env.HOST || '0.0.0.0'
const port = process.env.PORT || 8000
const server = app.listen(port, host, () => console.log(`server started at http://${host}:${port}/`))
const io = require('socket.io')(server, {
cors: {
origin: '*',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
preflightContinue: false,
optionsSuccessStatus: 204,
},
})
const game = {
grids: 20,
apple: { x: 10, y: 10 },
players: [
{
id: '',
key: '',
pos: { x: 3, y: 1 },
vel: { x: 1, y: 0 },
snake: [
{ x: 3, y: 1 },
{ x: 2, y: 1 },
{ x: 1, y: 1 },
],
},
{
id: '',
key: '',
pos: { x: 16, y: 18 },
vel: { x: -1, y: 0 },
snake: [
{ x: 16, y: 18 },
{ x: 17, y: 18 },
{ x: 18, y: 18 },
],
},
],
}

const player1 = game.players[0]
const player2 = game.players[1]

io.on('connection', (client) => {
// 有空位即注册为玩家,否则是观众
if (!player1.id) player1.id = client.id
else if (!player2.id) player2.id = client.id
else return
client.on('keydown', (key) => {
if (player1.id == client.id) player1.key = key
else player2.key = key
})
client.on('disconnect', () => {
if (player1.id == client.id) player1.id = ''
else player2.id = ''
})
})

function randomInRange(start, end) {
return Math.floor(Math.random() * (end - start + 1) + start)
}

setInterval(() => {
let reset = false

if (player1.key == 'ArrowDown' && player1.vel.y == 0) player1.vel = { x: 0, y: 1 }
else if (player1.key == 'ArrowUp' && player1.vel.y == 0) player1.vel = { x: 0, y: -1 }
else if (player1.key == 'ArrowLeft' && player1.vel.x == 0) player1.vel = { x: -1, y: 0 }
else if (player1.key == 'ArrowRight' && player1.vel.x == 0) player1.vel = { x: 1, y: 0 }
player1.key = ''

player1.pos.x = (player1.pos.x + player1.vel.x + game.grids) % game.grids
player1.pos.y = (player1.pos.y + player1.vel.y + game.grids) % game.grids
player1.snake.unshift({ x: player1.pos.x, y: player1.pos.y })
if (player1.pos.x != game.apple.x || player1.pos.y != game.apple.y) player1.snake.pop()
else reset = true

if (
player1.snake.length > 3 &&
(player1.snake.slice(1).some((c) => player1.pos.x == c.x && player1.pos.y == c.y) ||
player1.snake.slice(1).some((c) => player1.pos.x == c.x && player1.pos.y == c.y))
)
player1.snake.pop()

if (player2.key == 'ArrowDown' && player2.vel.y == 0) player2.vel = { x: 0, y: 1 }
else if (player2.key == 'ArrowUp' && player2.vel.y == 0) player2.vel = { x: 0, y: -1 }
else if (player2.key == 'ArrowLeft' && player2.vel.x == 0) player2.vel = { x: -1, y: 0 }
else if (player2.key == 'ArrowRight' && player2.vel.x == 0) player2.vel = { x: 1, y: 0 }
player2.key = ''
player2.pos.x = (player2.pos.x + player2.vel.x + game.grids) % game.grids
player2.pos.y = (player2.pos.y + player2.vel.y + game.grids) % game.grids
player2.snake.unshift({ x: player2.pos.x, y: player2.pos.y })
if (player2.pos.x != game.apple.x || player2.pos.y != game.apple.y) player2.snake.pop()
else reset = true
if (
player2.snake.length > 3 &&
(player1.snake.slice(1).some((c) => player2.pos.x == c.x && player2.pos.y == c.y) ||
player2.snake.slice(1).some((c) => player2.pos.x == c.x && player2.pos.y == c.y))
)
player2.snake.pop()

if (reset) {
game.apple.x = randomInRange(0, game.grids - 1)
game.apple.y = randomInRange(0, game.grids - 1)
}

io.emit('game', game)
}, 200)
Loading

0 comments on commit 11b5296

Please sign in to comment.