Added leaderboard, music & respawn screen
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<Game v-if="isLoggedIn"/>
|
<Game v-if="isLoggedIn && isSocketConnected"/>
|
||||||
<div v-else class="container mainMenu">
|
<div v-else class="container mainMenu">
|
||||||
<img src="./assets/logo.png" alt="TronIo logo" width="150px"/>
|
<img src="./assets/logo.png" alt="TronIo logo" width="150px"/>
|
||||||
<h1 class="title">tron.io</h1>
|
<h1 class="title">tron.io</h1>
|
||||||
@@ -12,7 +12,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { send } from './store/socketPlugin'
|
import { send } from './store/socketPlugin'
|
||||||
|
import { sound } from '@/game/sound.js'
|
||||||
import Game from './components/Game'
|
import Game from './components/Game'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
@@ -29,17 +31,20 @@ export default {
|
|||||||
return this.$store.state.game.loggedIn
|
return this.$store.state.game.loggedIn
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
sound.startBackgroundMusic()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loginPrompt () {
|
loginPrompt () {
|
||||||
this.$buefy.dialog.prompt({
|
this.$buefy.dialog.prompt({
|
||||||
message: 'Choose a name',
|
message: 'Choose a name',
|
||||||
trapFocus: true,
|
trapFocus: true,
|
||||||
inputAttrs: {
|
inputAttrs: {
|
||||||
placeholder: 'pedro',
|
placeholder: 'user',
|
||||||
minlength: 3,
|
minlength: 1,
|
||||||
maxlength: 30
|
maxlength: 15
|
||||||
},
|
},
|
||||||
confirmText: 'KK',
|
confirmText: 'Go',
|
||||||
onConfirm: (name) => {
|
onConfirm: (name) => {
|
||||||
localStorage.setItem('name', name)
|
localStorage.setItem('name', name)
|
||||||
this.login(name)
|
this.login(name)
|
||||||
|
|||||||
BIN
client/src/assets/sound/explosion.mp3
Normal file
BIN
client/src/assets/sound/explosion.mp3
Normal file
Binary file not shown.
BIN
client/src/assets/sound/revengeOfCats.mp3
Normal file
BIN
client/src/assets/sound/revengeOfCats.mp3
Normal file
Binary file not shown.
@@ -12,7 +12,7 @@ $blue: #3498db;
|
|||||||
$purple: #8e44ad;
|
$purple: #8e44ad;
|
||||||
$red: #e74c3c;
|
$red: #e74c3c;
|
||||||
$white-ter: #ecf0f1;
|
$white-ter: #ecf0f1;
|
||||||
$primary: $turquoise;
|
$primary: $purple;
|
||||||
$yellow-invert: #fff;
|
$yellow-invert: #fff;
|
||||||
|
|
||||||
$family-sans-serif: "Gravity Regular", "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
$family-sans-serif: "Gravity Regular", "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="game">
|
<div class="game">
|
||||||
|
<b-modal v-model="playerIsDead">
|
||||||
|
<h1 class="title">DED</h1>
|
||||||
|
<h2 class="title">Score: {{player.score}}</h2>
|
||||||
|
<h3 class="subtitle">Best score: {{player.bestScore}}</h3>
|
||||||
|
<b-button @click="respawn">Respawn</b-button>
|
||||||
|
</b-modal>
|
||||||
<canvas
|
<canvas
|
||||||
class="game-canvas"
|
class="game-canvas"
|
||||||
ref="canvas">
|
ref="canvas">
|
||||||
@@ -9,6 +15,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { send } from '@/store/socketPlugin'
|
import { send } from '@/store/socketPlugin'
|
||||||
|
import { render } from '@/game/render.js'
|
||||||
|
import { sound } from '@/game/sound.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Game',
|
name: 'Game',
|
||||||
data () {
|
data () {
|
||||||
@@ -22,7 +31,6 @@ export default {
|
|||||||
y: 0
|
y: 0
|
||||||
},
|
},
|
||||||
stats: {
|
stats: {
|
||||||
leaderboard: [],
|
|
||||||
totalWalls: 0,
|
totalWalls: 0,
|
||||||
lastUpdateTime: 0,
|
lastUpdateTime: 0,
|
||||||
lastFrame: 0
|
lastFrame: 0
|
||||||
@@ -51,7 +59,7 @@ export default {
|
|||||||
if (pastUpdate.players === undefined) return []
|
if (pastUpdate.players === undefined) return []
|
||||||
if (nextUpdate === undefined) return pastUpdate.players
|
if (nextUpdate === undefined) return pastUpdate.players
|
||||||
|
|
||||||
return pastUpdate.players
|
return nextUpdate.players
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const currentTime = Date.now() / 1000
|
const currentTime = Date.now() / 1000
|
||||||
@@ -61,26 +69,29 @@ export default {
|
|||||||
},
|
},
|
||||||
isPaused () {
|
isPaused () {
|
||||||
return this.$store.state.game.paused
|
return this.$store.state.game.paused
|
||||||
|
},
|
||||||
|
leaderboard () {
|
||||||
|
return this.$store.state.game.leaderboard
|
||||||
|
},
|
||||||
|
playerIsDead () {
|
||||||
|
if (this.player.state === 'DEAD') sound.explosion()
|
||||||
|
return this.player.state === 'DEAD'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.canvas.width = window.innerWidth
|
this.setCanvasSize()
|
||||||
this.canvas.height = window.innerHeight
|
|
||||||
this.canvas.addEventListener('mousemove', this.mouseEvent)
|
this.canvas.addEventListener('mousemove', this.mouseEvent)
|
||||||
this.canvas.addEventListener('touchmove', this.touchEvent)
|
this.canvas.addEventListener('touchmove', this.touchEvent)
|
||||||
this.renderTimer = setInterval(this.render, 1000 / 120)
|
this.canvas.addEventListener('resize', this.setCanvasSize)
|
||||||
|
// this.renderTimer = setInterval(this.render, 1000 / 120)
|
||||||
|
this.render()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
render () {
|
render () {
|
||||||
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
||||||
this.renderBorders()
|
render.borders(this.context, this.canvas, this.camera, this.settings)
|
||||||
|
|
||||||
if (!this.players) return
|
if (!this.players) return
|
||||||
|
|
||||||
this.players.sort((a, b) => {
|
|
||||||
return b.walls.length - a.walls.length
|
|
||||||
})
|
|
||||||
this.stats.leaderboard = []
|
|
||||||
this.stats.totalWalls = 0
|
this.stats.totalWalls = 0
|
||||||
|
|
||||||
this.players.forEach(player => {
|
this.players.forEach(player => {
|
||||||
@@ -88,78 +99,25 @@ export default {
|
|||||||
this.camera.x = player.x
|
this.camera.x = player.x
|
||||||
this.camera.y = player.y
|
this.camera.y = player.y
|
||||||
}
|
}
|
||||||
this.renderPlayer(player)
|
|
||||||
this.renderWalls(player)
|
if (player.state === 'DEAD') this.context.globalAlpha = 0.1
|
||||||
this.stats.leaderboard.push(player.name + ' - ' + player.walls.length)
|
render.walls(this.context, this.canvas, this.camera, this.settings, player)
|
||||||
|
render.player(this.context, this.canvas, this.camera, this.settings, player)
|
||||||
|
if (player.state === 'DEAD') this.context.globalAlpha = 1
|
||||||
|
|
||||||
this.stats.totalWalls += player.walls.length
|
this.stats.totalWalls += player.walls.length
|
||||||
})
|
})
|
||||||
|
|
||||||
this.renderLeaderboard()
|
render.leaderboard(this.context, this.canvas, this.leaderboard)
|
||||||
this.renderMouse()
|
render.mouse(this.context, this.mouse, this.player)
|
||||||
|
render.debug(this.context, this.camera, this.mouse, this.canvas, this.stats)
|
||||||
this.renderDebug()
|
|
||||||
|
|
||||||
this.stats.lastFrame = performance.now()
|
this.stats.lastFrame = performance.now()
|
||||||
},
|
|
||||||
renderBorders () {
|
|
||||||
this.context.strokeStyle = 'white'
|
|
||||||
this.context.lineWidth = 1
|
|
||||||
this.context.strokeRect(this.canvas.width / 2 - this.camera.x, this.canvas.height / 2 - this.camera.y, this.settings.arenaSize, this.settings.arenaSize)
|
|
||||||
},
|
|
||||||
renderPlayer (player) {
|
|
||||||
this.context.save()
|
|
||||||
|
|
||||||
const canvasX = this.canvas.width / 2 + player.x - this.camera.x
|
const nextUpdate = this.$store.state.game.updates[1]
|
||||||
const canvasY = this.canvas.height / 2 + player.y - this.camera.y
|
if (nextUpdate !== undefined) this.stats.lastUpdateTime = nextUpdate.time
|
||||||
|
|
||||||
this.context.translate(canvasX, canvasY)
|
requestAnimationFrame(this.render, this.canvas)
|
||||||
this.context.rotate(player.angle)
|
|
||||||
this.context.fillStyle = player.color
|
|
||||||
this.context.fillRect(-this.settings.playerSize, -this.settings.playerSize, this.settings.playerSize * 2, this.settings.playerSize * 2)
|
|
||||||
this.context.translate(0, 0)
|
|
||||||
|
|
||||||
this.context.restore()
|
|
||||||
},
|
|
||||||
renderWalls (player) {
|
|
||||||
this.context.beginPath()
|
|
||||||
this.context.lineWidth = this.settings.wallSize
|
|
||||||
this.context.strokeStyle = player.color
|
|
||||||
player.walls.forEach(wall => {
|
|
||||||
const canvasX = this.canvas.width / 2 + wall.x - this.camera.x
|
|
||||||
const canvasY = this.canvas.height / 2 + wall.y - this.camera.y
|
|
||||||
this.context.lineTo(canvasX, canvasY)
|
|
||||||
})
|
|
||||||
this.context.lineTo(this.canvas.width / 2 + player.x - this.camera.x, this.canvas.height / 2 + player.y - this.camera.y)
|
|
||||||
this.context.stroke()
|
|
||||||
},
|
|
||||||
renderDebug () {
|
|
||||||
const canvasX = this.canvas.width / 2
|
|
||||||
const canvasY = this.canvas.height / 2
|
|
||||||
|
|
||||||
this.context.fillStyle = 'white'
|
|
||||||
this.context.textAlign = 'start'
|
|
||||||
this.context.fillText('camera x: ' + this.camera.x + ' y:' + this.camera.y, 10, 12)
|
|
||||||
this.context.fillText('mouse x: ' + this.mouse.x + ' y:' + this.mouse.y, 10, 24)
|
|
||||||
this.context.fillText('canvasX: ' + canvasX + ' canvasY: ' + canvasY, 10, 36)
|
|
||||||
this.context.fillText('Total walls: ' + this.stats.totalWalls, 10, 48)
|
|
||||||
this.context.fillText('Last update: ' + this.stats.lastUpdateTime, 10, 60)
|
|
||||||
const fps = 1000 / (performance.now() - this.stats.lastFrame)
|
|
||||||
this.context.fillText('FPS: ' + fps, 10, 72)
|
|
||||||
},
|
|
||||||
renderMouse () {
|
|
||||||
this.context.beginPath()
|
|
||||||
this.context.arc(this.mouse.x, this.mouse.y, 25, 0, 2 * Math.PI, false)
|
|
||||||
this.context.lineWidth = 1
|
|
||||||
this.context.strokeStyle = this.player.color
|
|
||||||
this.context.stroke()
|
|
||||||
},
|
|
||||||
renderLeaderboard () {
|
|
||||||
this.context.fillStyle = 'white'
|
|
||||||
this.context.textAlign = 'end'
|
|
||||||
this.context.fillText('Leaderboard: ', this.canvas.width - 50, 10)
|
|
||||||
for (var i = 0; i < this.stats.leaderboard.length; i++) {
|
|
||||||
this.context.fillText(this.players[i].name + ' - ' + this.players[i].walls.length, this.canvas.width - 50, 15 + (i + 1) * 10)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mouseEvent (event) {
|
mouseEvent (event) {
|
||||||
var rect = this.canvas.getBoundingClientRect()
|
var rect = this.canvas.getBoundingClientRect()
|
||||||
@@ -211,6 +169,13 @@ export default {
|
|||||||
player.targetAngle = pastPlayer.targetAngle + (nextPlayer.targetAngle - pastPlayer.targetAngle) * dt
|
player.targetAngle = pastPlayer.targetAngle + (nextPlayer.targetAngle - pastPlayer.targetAngle) * dt
|
||||||
|
|
||||||
return player
|
return player
|
||||||
|
},
|
||||||
|
setCanvasSize () {
|
||||||
|
this.canvas.width = window.innerWidth
|
||||||
|
this.canvas.height = window.innerHeight
|
||||||
|
},
|
||||||
|
respawn () {
|
||||||
|
send({ type: 'respawn' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
86
client/src/game/render.js
Normal file
86
client/src/game/render.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
export const render = {
|
||||||
|
borders (context, canvas, camera, settings) {
|
||||||
|
context.strokeStyle = 'white'
|
||||||
|
// context.lineWidth = 1
|
||||||
|
// context.shadowBlur = 20
|
||||||
|
context.shadowColor = 'white'
|
||||||
|
context.strokeRect(canvas.width / 2 - camera.x, canvas.height / 2 - camera.y, settings.arenaSize, settings.arenaSize)
|
||||||
|
},
|
||||||
|
player (context, canvas, camera, settings, player) {
|
||||||
|
const canvasX = canvas.width / 2 + player.x - camera.x
|
||||||
|
const canvasY = canvas.height / 2 + player.y - camera.y
|
||||||
|
|
||||||
|
context.save()
|
||||||
|
|
||||||
|
context.translate(canvasX, canvasY)
|
||||||
|
context.rotate(player.angle)
|
||||||
|
context.fillStyle = player.color
|
||||||
|
// context.shadowBlur = 10
|
||||||
|
// context.shadowColor = player.color
|
||||||
|
context.fillRect(-settings.playerSize, -settings.playerSize, settings.playerSize * 2, settings.playerSize * 2)
|
||||||
|
context.translate(0, 0)
|
||||||
|
|
||||||
|
context.restore()
|
||||||
|
|
||||||
|
context.fillStyle = 'white'
|
||||||
|
context.textAlign = 'center'
|
||||||
|
context.fillText(player.name, canvasX, canvasY - settings.playerSize - 5)
|
||||||
|
},
|
||||||
|
walls (context, canvas, camera, settings, player) {
|
||||||
|
context.beginPath()
|
||||||
|
context.lineWidth = settings.wallSize
|
||||||
|
context.strokeStyle = player.color
|
||||||
|
// context.shadowBlur = 0
|
||||||
|
// context.shadowColor = player.color
|
||||||
|
player.walls.forEach(wall => {
|
||||||
|
const canvasX = canvas.width / 2 + wall.x - camera.x
|
||||||
|
const canvasY = canvas.height / 2 + wall.y - camera.y
|
||||||
|
context.lineTo(canvasX, canvasY)
|
||||||
|
})
|
||||||
|
context.lineTo(canvas.width / 2 + player.x - camera.x, canvas.height / 2 + player.y - camera.y)
|
||||||
|
context.stroke()
|
||||||
|
},
|
||||||
|
debug (context, camera, mouse, canvas, stats) {
|
||||||
|
const canvasX = canvas.width / 2
|
||||||
|
const canvasY = canvas.height / 2
|
||||||
|
|
||||||
|
context.fillStyle = 'white'
|
||||||
|
context.textAlign = 'start'
|
||||||
|
context.fillText('camera x: ' + camera.x + ' y:' + camera.y, 10, 12)
|
||||||
|
context.fillText('mouse x: ' + mouse.x + ' y:' + mouse.y, 10, 24)
|
||||||
|
context.fillText('canvasX: ' + canvasX + ' canvasY: ' + canvasY, 10, 36)
|
||||||
|
context.fillText('Total walls: ' + stats.totalWalls, 10, 48)
|
||||||
|
context.fillText('Server update: ' + stats.lastUpdateTime, 10, 60)
|
||||||
|
const fps = 1000 / (performance.now() - stats.lastFrame)
|
||||||
|
context.fillText('FPS: ' + fps, 10, 72)
|
||||||
|
},
|
||||||
|
mouse (context, mouse, player) {
|
||||||
|
context.beginPath()
|
||||||
|
context.arc(mouse.x, mouse.y, 25, 0, 2 * Math.PI, false)
|
||||||
|
context.lineWidth = 1
|
||||||
|
context.strokeStyle = player.color
|
||||||
|
context.stroke()
|
||||||
|
},
|
||||||
|
leaderboard (context, canvas, leaderboard) {
|
||||||
|
context.fillStyle = 'white'
|
||||||
|
context.textAlign = 'end'
|
||||||
|
context.fillText('Leaderboard: ', canvas.width - 50, 10)
|
||||||
|
var i = 1
|
||||||
|
leaderboard.forEach(player => {
|
||||||
|
context.fillStyle = player.color
|
||||||
|
context.fillText(player.name + ' - ' + player.score + ' (' + player.bestScore + ')', canvas.width - 50, 15 + i * 10)
|
||||||
|
i++
|
||||||
|
})
|
||||||
|
},
|
||||||
|
dead (context, canvas, player) {
|
||||||
|
const canvasX = canvas.width / 2
|
||||||
|
const canvasY = canvas.height / 2
|
||||||
|
|
||||||
|
context.fillStyle = 'white'
|
||||||
|
context.textAlign = 'center'
|
||||||
|
context.fillText('YO DED', canvasX, canvasY - 50)
|
||||||
|
context.fillText('Score: ' + player.score, canvasX, canvasY)
|
||||||
|
context.fillText('Best score so far ' + player.bestScore, canvasX, canvasY + 25)
|
||||||
|
context.fillText('Tap to respawn', canvasX, canvasY + 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
23
client/src/game/sound.js
Normal file
23
client/src/game/sound.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const music = new Audio(require('@/assets/sound/revengeOfCats.mp3'))
|
||||||
|
// const motor = new Audio(require('@/assets/sound/motor.mp3'))
|
||||||
|
const explosion = new Audio(require('@/assets/sound/explosion.mp3'))
|
||||||
|
|
||||||
|
export const sound = {
|
||||||
|
startBackgroundMusic () {
|
||||||
|
music.loop = true
|
||||||
|
music.play()
|
||||||
|
},
|
||||||
|
stopBackgroundMusic () {
|
||||||
|
music.pause()
|
||||||
|
},
|
||||||
|
startMotor () {
|
||||||
|
motor.loop = true
|
||||||
|
motor.play()
|
||||||
|
},
|
||||||
|
stopMotor () {
|
||||||
|
motor.pause()
|
||||||
|
},
|
||||||
|
explosion () {
|
||||||
|
explosion.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import Buefy from 'buefy'
|
|||||||
import './registerServiceWorker'
|
import './registerServiceWorker'
|
||||||
|
|
||||||
// import 'buefy/dist/buefy.css'
|
// import 'buefy/dist/buefy.css'
|
||||||
import './assets/style.scss'
|
import './assets/style/style.scss'
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
Vue.use(Buefy)
|
Vue.use(Buefy)
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ const state = {
|
|||||||
y: 100,
|
y: 100,
|
||||||
angle: 0,
|
angle: 0,
|
||||||
targetAngle: 0,
|
targetAngle: 0,
|
||||||
color: 'red'
|
color: 'red',
|
||||||
|
state: 'DEAD'
|
||||||
},
|
},
|
||||||
|
leaderboard: [],
|
||||||
updates: [],
|
updates: [],
|
||||||
settings: {
|
settings: {
|
||||||
playerSize: 10,
|
playerSize: 10,
|
||||||
@@ -21,19 +23,11 @@ const state = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getters = {
|
const getters = {
|
||||||
// Interpolation position des joueurs
|
|
||||||
currentPlayers: state => {
|
|
||||||
if (state.updates.length < 2) return state.updates[0].players
|
|
||||||
else {
|
|
||||||
const dt = state.updates[0].time - state.updates[1].time
|
|
||||||
return state.updates[1].players.map(player => interpolatePlayer(player, dt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
socketConnected ({ commit }) {
|
socketConnected ({ commit }, connected) {
|
||||||
commit('SET_CONNECTED')
|
commit('SET_CONNECTED', connected)
|
||||||
commit('CLEAR_UPDATE')
|
commit('CLEAR_UPDATE')
|
||||||
},
|
},
|
||||||
login ({ commit }, player) {
|
login ({ commit }, player) {
|
||||||
@@ -45,10 +39,15 @@ const actions = {
|
|||||||
},
|
},
|
||||||
update ({ commit }, update) {
|
update ({ commit }, update) {
|
||||||
commit('ADD_UPDATE', update)
|
commit('ADD_UPDATE', update)
|
||||||
|
commit('SET_LEADERBOARD', update.players)
|
||||||
},
|
},
|
||||||
dead ({ commit }, player) {
|
dead ({ commit }, player) {
|
||||||
commit('SET_PLAYER', player)
|
commit('SET_PLAYER', player)
|
||||||
commit('CLEAR_UPDATE')
|
// commit('CLEAR_UPDATE')
|
||||||
|
},
|
||||||
|
spawn ({ commit }, player) {
|
||||||
|
commit('SET_PLAYER', player)
|
||||||
|
// commit('CLEAR_UPDATE')
|
||||||
},
|
},
|
||||||
error ({ commit }, error) {
|
error ({ commit }, error) {
|
||||||
alert('Error: ' + error)
|
alert('Error: ' + error)
|
||||||
@@ -71,8 +70,8 @@ const mutations = {
|
|||||||
SET_PAUSE (state) {
|
SET_PAUSE (state) {
|
||||||
state.paused = !state.paused
|
state.paused = !state.paused
|
||||||
},
|
},
|
||||||
SET_CONNECTED (state) {
|
SET_CONNECTED (state, connected) {
|
||||||
state.socketConnected = !state.socketConnected
|
state.socketConnected = connected
|
||||||
},
|
},
|
||||||
ADD_UPDATE (state, update) {
|
ADD_UPDATE (state, update) {
|
||||||
if (state.updates.length > 2) {
|
if (state.updates.length > 2) {
|
||||||
@@ -83,11 +82,23 @@ const mutations = {
|
|||||||
},
|
},
|
||||||
CLEAR_UPDATE (state) {
|
CLEAR_UPDATE (state) {
|
||||||
state.updates = []
|
state.updates = []
|
||||||
}
|
},
|
||||||
}
|
SET_LEADERBOARD (state, players) {
|
||||||
|
state.leaderboard = []
|
||||||
|
|
||||||
function interpolatePlayer (player, dt) {
|
players.forEach(player => {
|
||||||
return player
|
state.leaderboard.push({
|
||||||
|
name: player.name,
|
||||||
|
score: player.score,
|
||||||
|
bestScore: player.bestScore,
|
||||||
|
color: player.color
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
state.leaderboard.sort((a, b) => {
|
||||||
|
return b.score - a.score
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import { ToastProgrammatic as Toast } from 'buefy'
|
import { ToastProgrammatic as Toast } from 'buefy'
|
||||||
|
|
||||||
// const connection = new WebSocket('ws://localhost:8181/socket')
|
const connection = new WebSocket('ws://localhost:8181/socket')
|
||||||
const connection = new WebSocket('wss://tronio.gltronic.ovh/socket')
|
// const connection = new WebSocket('wss://tronio.gltronic.ovh/socket')
|
||||||
|
|
||||||
export default function createSocketPlugin () {
|
export default function createSocketPlugin () {
|
||||||
return store => {
|
return store => {
|
||||||
connection.onopen = function () {
|
connection.onopen = function () {
|
||||||
console.log('[WS] connected')
|
console.log('[WS] connected')
|
||||||
store.dispatch('game/socketConnected')
|
store.dispatch('game/socketConnected', true)
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.onclose = function () {
|
connection.onclose = function () {
|
||||||
console.log('[WS] closed')
|
console.log('[WS] closed')
|
||||||
|
store.dispatch('game/socketConnected', false)
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.onerror = function (error) {
|
connection.onerror = function (error) {
|
||||||
@@ -41,6 +42,9 @@ export default function createSocketPlugin () {
|
|||||||
case 'gamePlayerDead':
|
case 'gamePlayerDead':
|
||||||
store.dispatch('game/dead', data.message)
|
store.dispatch('game/dead', data.message)
|
||||||
break
|
break
|
||||||
|
case 'gamePlayerSpawn':
|
||||||
|
store.dispatch('game/spawn', data.message)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.springframework.web.socket.WebSocketSession;
|
|||||||
import gltronic.tronio.model.Game;
|
import gltronic.tronio.model.Game;
|
||||||
import gltronic.tronio.model.GameUpdate;
|
import gltronic.tronio.model.GameUpdate;
|
||||||
import gltronic.tronio.model.Player;
|
import gltronic.tronio.model.Player;
|
||||||
|
import gltronic.tronio.model.PlayerState;
|
||||||
import gltronic.tronio.model.Wall;
|
import gltronic.tronio.model.Wall;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -33,6 +34,7 @@ public class GameManager implements IGameManager {
|
|||||||
|
|
||||||
Player player = initPlayer(new Player());
|
Player player = initPlayer(new Player());
|
||||||
player.setName(name);
|
player.setName(name);
|
||||||
|
player.setBestScore(0);
|
||||||
|
|
||||||
game.getSessions().put(session.getId(), session);
|
game.getSessions().put(session.getId(), session);
|
||||||
game.getPlayers().put(session.getId(), player);
|
game.getPlayers().put(session.getId(), player);
|
||||||
@@ -58,7 +60,7 @@ public class GameManager implements IGameManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Scheduled(fixedDelay = 1000 / 120)
|
@Scheduled(fixedDelay = 1000 / 60)
|
||||||
public void step() throws InterruptedException, IOException {
|
public void step() throws InterruptedException, IOException {
|
||||||
Instant currentTime = Instant.now();
|
Instant currentTime = Instant.now();
|
||||||
Duration durationSinceLastUpdate = Duration.between(game.getLastUpdate(), currentTime);
|
Duration durationSinceLastUpdate = Duration.between(game.getLastUpdate(), currentTime);
|
||||||
@@ -66,6 +68,8 @@ public class GameManager implements IGameManager {
|
|||||||
|
|
||||||
// CHECK OUT OF BORDERS & COLISIONS
|
// CHECK OUT OF BORDERS & COLISIONS
|
||||||
game.getPlayers().forEach((id, player) -> {
|
game.getPlayers().forEach((id, player) -> {
|
||||||
|
if (!player.getState().equals(PlayerState.ALIVE)) return;
|
||||||
|
|
||||||
// OUT OF BORDERS
|
// OUT OF BORDERS
|
||||||
if (player.getX() - game.getSettings().getPlayerSize() < 0
|
if (player.getX() - game.getSettings().getPlayerSize() < 0
|
||||||
|| player.getX() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize()
|
|| player.getX() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize()
|
||||||
@@ -76,20 +80,31 @@ public class GameManager implements IGameManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// BORDER WRAP
|
||||||
|
if (player.getX() - game.getSettings().getPlayerSize() < 0) player.setX(game.getSettings().getPlayerSize());
|
||||||
|
else if (player.getX() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize()) player.setX(0);
|
||||||
|
else if (player.getY() - game.getSettings().getPlayerSize() < 0) player.setY(game.getSettings().getPlayerSize());
|
||||||
|
else if (player.getY() + game.getSettings().getPlayerSize() > game.getSettings().getArenaSize()) player.setY(0);
|
||||||
|
*/
|
||||||
|
|
||||||
// COLLISIONS
|
// COLLISIONS
|
||||||
game.getPlayers().forEach((id2, player2) -> {
|
game.getPlayers().forEach((id2, player2) -> {
|
||||||
|
if (!player2.getState().equals(PlayerState.ALIVE)) return;
|
||||||
|
|
||||||
for (var i = 0; i < player2.getWalls().size() - 2; i++) {
|
for (var i = 0; i < player2.getWalls().size() - 2; i++) {
|
||||||
// Pour evité la collision avec un mur venant d'être placé par le même joueur
|
// Pour evité la collision avec un mur venant d'être placé par le même joueur
|
||||||
if (id.equals(id2) && i >= player2.getWalls().size() - 4) break;
|
if (id.equals(id2) && i >= player2.getWalls().size() - 2) break;
|
||||||
|
|
||||||
Wall wallA = player2.getWalls().get(i);
|
Wall wallA = player2.getWalls().get(i);
|
||||||
Wall wallB = player2.getWalls().get(i + 1);
|
Wall wallB = player2.getWalls().get(i + 1);
|
||||||
if (isCloseToWall(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(), player.getY(),
|
if (isCloseToWall(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(), player.getY(),
|
||||||
game.getSettings().getPlayerSize())) {
|
game.getSettings().getPlayerSize())) {
|
||||||
System.out.println("[GAME] close to wall");
|
System.out.println("[GAME] " + player.getName() + " close to wall");
|
||||||
if (this.isCrossingLine(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(),
|
if (this.isCrossingLine(wallA.getX(), wallA.getY(), wallB.getX(), wallB.getY(), player.getX(),
|
||||||
player.getY(), game.getSettings().getPlayerSize())) {
|
player.getY(), game.getSettings().getPlayerSize())) {
|
||||||
System.out.println("[GAME] touch da wall");
|
System.out.println("[GAME] " + player.getName() + " killed by " + player2.getName());
|
||||||
|
if (!player.equals(player2)) player2.setScore(player.getScore() + 300);
|
||||||
killPlayer(id);
|
killPlayer(id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -100,6 +115,8 @@ public class GameManager implements IGameManager {
|
|||||||
|
|
||||||
// ADD WALLS & MOVE PLAYER
|
// ADD WALLS & MOVE PLAYER
|
||||||
game.getPlayers().forEach((id, player) -> {
|
game.getPlayers().forEach((id, player) -> {
|
||||||
|
if (!player.getState().equals(PlayerState.ALIVE)) return;
|
||||||
|
|
||||||
// On cherche le nombre de pas pour atteindre les 60 up/s
|
// On cherche le nombre de pas pour atteindre les 60 up/s
|
||||||
long tickToSimulate = ( durationSinceLastUpdate.toMillis() * 120 ) / 1000;
|
long tickToSimulate = ( durationSinceLastUpdate.toMillis() * 120 ) / 1000;
|
||||||
|
|
||||||
@@ -108,6 +125,7 @@ public class GameManager implements IGameManager {
|
|||||||
player.setLastWall(player.getLastWall() + 1);
|
player.setLastWall(player.getLastWall() + 1);
|
||||||
if (player.getLastWall() > game.getSettings().getWallUpdate()) {
|
if (player.getLastWall() > game.getSettings().getWallUpdate()) {
|
||||||
player.getWalls().add(new Wall(player.getX(), player.getY()));
|
player.getWalls().add(new Wall(player.getX(), player.getY()));
|
||||||
|
player.setScore(player.getScore() + 1);
|
||||||
player.setLastWall(0);
|
player.setLastWall(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,11 +147,13 @@ public class GameManager implements IGameManager {
|
|||||||
|
|
||||||
private void killPlayer(String id) {
|
private void killPlayer(String id) {
|
||||||
Player player = game.getPlayers().get(id);
|
Player player = game.getPlayers().get(id);
|
||||||
initPlayer(player);
|
|
||||||
|
if (player.getBestScore() < player.getScore()) player.setBestScore(player.getScore());
|
||||||
|
|
||||||
|
player.setState(PlayerState.DEAD);
|
||||||
System.out.println("[GAME] Player " + player.getName() + " (" + id + ") is dead | status: " + player.getX() + " " + player.getY() + " " + player.getColor());
|
System.out.println("[GAME] Player " + player.getName() + " (" + id + ") is dead | status: " + player.getX() + " " + player.getY() + " " + player.getColor());
|
||||||
try {
|
try {
|
||||||
SocketUtils.sendObject(game.getSessions().get(id), "gamePlayerDead",
|
SocketUtils.sendObject(game.getSessions().get(id), "gamePlayerDead", new ObjectMapper().writeValueAsString(player));
|
||||||
new ObjectMapper().writeValueAsString(player));
|
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -166,7 +186,22 @@ public class GameManager implements IGameManager {
|
|||||||
player.setColor(String.format("#%06x", rand.nextInt(0xffffff + 1)));
|
player.setColor(String.format("#%06x", rand.nextInt(0xffffff + 1)));
|
||||||
player.setX(x);
|
player.setX(x);
|
||||||
player.setY(y);
|
player.setY(y);
|
||||||
|
player.setScore(0);
|
||||||
|
player.setState(PlayerState.ALIVE);
|
||||||
|
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void respawn(WebSocketSession session) throws InterruptedException, IOException {
|
||||||
|
Player player = game.getPlayers().get(session.getId());
|
||||||
|
initPlayer(player);
|
||||||
|
player.setState(PlayerState.ALIVE);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SocketUtils.sendObject(session, "gamePlayerSpawn", new ObjectMapper().writeValueAsString(player));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ public interface IGameManager {
|
|||||||
void login(WebSocketSession session, String name) throws InterruptedException, IOException;
|
void login(WebSocketSession session, String name) throws InterruptedException, IOException;
|
||||||
void leave(WebSocketSession session) throws InterruptedException, IOException;
|
void leave(WebSocketSession session) throws InterruptedException, IOException;
|
||||||
void updatePlayer(WebSocketSession session, Player player) throws InterruptedException, IOException;
|
void updatePlayer(WebSocketSession session, Player player) throws InterruptedException, IOException;
|
||||||
|
void respawn(WebSocketSession session) throws InterruptedException, IOException;
|
||||||
|
|
||||||
void step() throws InterruptedException, IOException;
|
void step() throws InterruptedException, IOException;
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,7 @@ import lombok.Setter;
|
|||||||
public class Game {
|
public class Game {
|
||||||
private Map<String, Player> players;
|
private Map<String, Player> players;
|
||||||
private Map<String, WebSocketSession> sessions;
|
private Map<String, WebSocketSession> sessions;
|
||||||
|
private Map<String, Integer> leaderboard;
|
||||||
private GameSettings settings;
|
private GameSettings settings;
|
||||||
private boolean updateNeeded;
|
private boolean updateNeeded;
|
||||||
private Instant lastUpdate;
|
private Instant lastUpdate;
|
||||||
@@ -25,6 +26,7 @@ public class Game {
|
|||||||
this.lastUpdate = Instant.now();
|
this.lastUpdate = Instant.now();
|
||||||
this.players = new HashMap<>();
|
this.players = new HashMap<>();
|
||||||
this.sessions = new HashMap<>();
|
this.sessions = new HashMap<>();
|
||||||
|
this.leaderboard = new HashMap<>();
|
||||||
this.settings = new GameSettings();
|
this.settings = new GameSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,9 +16,9 @@ public class GameSettings {
|
|||||||
public GameSettings () {
|
public GameSettings () {
|
||||||
this.arenaSize = 1000;
|
this.arenaSize = 1000;
|
||||||
this.playerSize = 10;
|
this.playerSize = 10;
|
||||||
this.playerSpeed = 2;
|
this.playerSpeed = 2.3;
|
||||||
this.playerTurnSpeed = 10;
|
this.playerTurnSpeed = 10;
|
||||||
this.wallSize = 8;
|
this.wallSize = 8;
|
||||||
this.wallUpdate = 5;
|
this.wallUpdate = 20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,12 @@ public class Player {
|
|||||||
|
|
||||||
private String color;
|
private String color;
|
||||||
|
|
||||||
|
private int score;
|
||||||
|
|
||||||
|
private int bestScore;
|
||||||
|
|
||||||
|
private PlayerState state;
|
||||||
|
|
||||||
private double x;
|
private double x;
|
||||||
|
|
||||||
private double y;
|
private double y;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package gltronic.tronio.model;
|
||||||
|
|
||||||
|
public enum PlayerState {
|
||||||
|
ALIVE, DEAD, PAUSED
|
||||||
|
}
|
||||||
@@ -35,6 +35,9 @@ public class SocketHandler extends TextWebSocketHandler {
|
|||||||
case "login":
|
case "login":
|
||||||
gameManager.login(session, rootObject.getString("message"));
|
gameManager.login(session, rootObject.getString("message"));
|
||||||
break;
|
break;
|
||||||
|
case "respawn":
|
||||||
|
gameManager.respawn(session);
|
||||||
|
break;
|
||||||
case "update":
|
case "update":
|
||||||
Player player = new ObjectMapper().readValue(rootObject.get("message").toString(), Player.class);
|
Player player = new ObjectMapper().readValue(rootObject.get("message").toString(), Player.class);
|
||||||
gameManager.updatePlayer(session, player);
|
gameManager.updatePlayer(session, player);
|
||||||
|
|||||||
Reference in New Issue
Block a user