Client & network general improvement

This commit is contained in:
Thomas
2020-08-31 15:51:18 +02:00
parent 6ea4249ed9
commit 14be55df67
11 changed files with 125 additions and 117 deletions

View File

@@ -17,6 +17,10 @@ export default {
x: 0, x: 0,
y: 0 y: 0
}, },
camera: {
x: 0,
y: 0
},
renderTimer: null renderTimer: null
} }
}, },
@@ -34,7 +38,20 @@ export default {
return this.$store.state.game.player return this.$store.state.game.player
}, },
players () { players () {
return this.$store.state.game.players const pastUpdate = this.$store.state.game.updates[0]
const nextUpdate = this.$store.state.game.updates[1]
if (pastUpdate === undefined) return []
if (pastUpdate.players === undefined) return []
if (nextUpdate === undefined) return pastUpdate.players
return pastUpdate.players
/*
const currentTime = Date.now() / 1000
const dt = (currentTime - pastUpdate.time) / (nextUpdate.time - pastUpdate.time)
return pastUpdate.players.map(player => this.interpolatePlayer(player, nextUpdate.players.find(nextPlayer => player.color === nextPlayer.color), dt))
*/
}, },
isPaused () { isPaused () {
return this.$store.state.game.paused return this.$store.state.game.paused
@@ -52,26 +69,28 @@ export default {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.renderBorders() this.renderBorders()
if (!this.players) return
this.players.forEach(player => { this.players.forEach(player => {
if (player.color === this.player.color) {
this.camera.x = player.x
this.camera.y = player.y
this.renderDebug(player)
}
this.renderPlayer(player) this.renderPlayer(player)
this.renderWalls(player) this.renderWalls(player)
}) })
this.renderDebug(this.player)
// this.checkPlayerColision()
this.updatePlayer(this.player)
}, },
renderBorders () { renderBorders () {
this.context.strokeStyle = 'black' this.context.strokeStyle = 'black'
this.context.lineWidth = 1 this.context.lineWidth = 1
this.context.strokeRect(this.canvas.width / 2 - this.player.x, this.canvas.height / 2 - this.player.y, this.settings.arenaSize, this.settings.arenaSize) 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) { renderPlayer (player) {
this.context.save() this.context.save()
const canvasX = this.canvas.width / 2 + player.x - this.player.x const canvasX = this.canvas.width / 2 + player.x - this.camera.x
const canvasY = this.canvas.height / 2 + player.y - this.player.y const canvasY = this.canvas.height / 2 + player.y - this.camera.y
this.context.translate(canvasX, canvasY) this.context.translate(canvasX, canvasY)
this.context.rotate(player.angle) this.context.rotate(player.angle)
@@ -86,41 +105,16 @@ export default {
this.context.lineWidth = this.settings.wallSize this.context.lineWidth = this.settings.wallSize
this.context.strokeStyle = player.color this.context.strokeStyle = player.color
player.walls.forEach(wall => { player.walls.forEach(wall => {
const canvasX = this.canvas.width / 2 + wall.x - this.player.x const canvasX = this.canvas.width / 2 + wall.x - this.camera.x
const canvasY = this.canvas.height / 2 + wall.y - this.player.y const canvasY = this.canvas.height / 2 + wall.y - this.camera.y
this.context.lineTo(canvasX, canvasY) this.context.lineTo(canvasX, canvasY)
}) })
this.context.lineTo(this.canvas.width / 2 + player.x - this.player.x, this.canvas.height / 2 + player.y - this.player.y) this.context.lineTo(this.canvas.width / 2 + player.x - this.camera.x, this.canvas.height / 2 + player.y - this.camera.y)
this.context.stroke() this.context.stroke()
}, },
renderDebug (player) { renderDebug (player) {
const canvasX = this.canvas.width / 2 const canvasX = this.canvas.width / 2
const canvasY = this.canvas.height / 2 const canvasY = this.canvas.height / 2
/*
this.context.beginPath()
this.context.lineTo(canvasX, canvasY)
this.context.lineTo(this.mouse.x, this.mouse.y)
this.context.lineWidth = 1
this.context.strokeStyle = 'blue'
this.context.stroke()
const x = canvasX + 30 * Math.cos(player.angle) * 2
const y = canvasY + 30 * Math.sin(player.angle) * 2
this.context.beginPath()
this.context.lineTo(canvasX, canvasY)
this.context.lineTo(x, y)
this.context.lineWidth = 1
this.context.strokeStyle = 'yellow'
this.context.stroke()
const canvasX2 = this.canvas.width / 2
const canvasY2 = this.canvas.height / 2
this.context.beginPath()
this.context.arc(canvasX2, canvasY2, this.settings.playerSize, 0, 2 * Math.PI, false)
this.context.lineWidth = 1
this.context.strokeStyle = 'purple'
this.context.stroke()
*/
this.context.fillText('player x: ' + player.x + ' y:' + player.y, 10, 10) this.context.fillText('player x: ' + player.x + ' y:' + player.y, 10, 10)
this.context.fillText('a:' + player.angle + ' a_t' + player.targetAngle, 10, 20) this.context.fillText('a:' + player.angle + ' a_t' + player.targetAngle, 10, 20)
@@ -152,17 +146,10 @@ export default {
var dy = this.mouse.y - this.canvas.height / 2 var dy = this.mouse.y - this.canvas.height / 2
this.player.targetAngle = Math.atan2(dy, dx) this.player.targetAngle = Math.atan2(dy, dx)
this.player.angle = this.player.targetAngle
send({ message: this.player, type: 'update' }) send({ message: this.player, type: 'update' })
}, },
updatePlayer (player) { updatePlayer (player) {
/*
this.player.lastWall++
if (this.player.lastWall > this.settings.wallUpdate) {
this.player.walls.push({ x: this.player.x, y: this.player.y })
this.player.lastWall = 0
}
*/
player.x += this.settings.playerSpeed * Math.cos(player.angle) player.x += this.settings.playerSpeed * Math.cos(player.angle)
player.y += this.settings.playerSpeed * Math.sin(player.angle) player.y += this.settings.playerSpeed * Math.sin(player.angle)
@@ -177,55 +164,19 @@ export default {
} }
*/ */
}, },
checkPlayerColision () { interpolatePlayer (pastPlayer, nextPlayer, dt) {
const playerX = this.player.x var player = {}
const playerY = this.player.y player.color = pastPlayer.color
// BORDERS player.walls = pastPlayer.walls
if (playerX - this.settings.playerSize < 0 || player.lastWall = pastPlayer.lastWall
playerX + this.settings.playerSize > this.settings.arenaSize ||
playerY - this.settings.playerSize < 0 ||
playerY + this.settings.playerSize > this.settings.arenaSize
) {
this.context.fillText('DED BORDER', this.canvas.width / 2, this.canvas.height / 2)
}
// WALLS player.x = pastPlayer.x + (nextPlayer.x - pastPlayer.x) * dt
for (var i = 0; i < this.player.walls.length - 2; i++) { player.y = pastPlayer.y + (nextPlayer.y - pastPlayer.y) * dt
const wallA = this.player.walls[i]
const wallB = this.player.walls[i + 1]
if (this.isCloseToWall(wallA.x, wallA.y, wallB.x, wallB.y, playerX, playerY, this.settings.playerSize)) {
if (this.isCrossingLine(wallA.x, wallA.y, wallB.x, wallB.y, playerX, playerY, this.settings.playerSize)) {
this.context.fillText('DED WALL OWN', this.canvas.width / 2, this.canvas.height / 2)
this.context.beginPath()
const canvasX = this.canvas.width / 2 + wallA.x - this.player.x
const canvasY = this.canvas.height / 2 + wallA.y - this.player.y
this.context.lineTo(canvasX, canvasY)
const canvasX2 = this.canvas.width / 2 + wallB.x - this.player.x
const canvasY2 = this.canvas.height / 2 + wallB.y - this.player.y
this.context.lineTo(canvasX2, canvasY2)
this.context.lineWidth = this.settings.wallSize
this.context.strokeStyle = 'purple'
this.context.stroke()
}
}
}
},
isCloseToWall (xa, ya, xb, yb, xc, yc, radius) {
var xar = Math.min(xa, xb) - radius
var yar = Math.min(ya, yb) - radius
var xbr = Math.max(xa, xb) + radius
var ybr = Math.max(ya, yb) + radius
return ((xc >= xar && xc <= xbr) && (yc >= yar && yc <= ybr)) player.angle = pastPlayer.angle + (nextPlayer.angle - pastPlayer.angle) * dt
}, player.targetAngle = pastPlayer.targetAngle + (nextPlayer.targetAngle - pastPlayer.targetAngle) * dt
isCrossingLine (xa, ya, xb, yb, xc, yc, radius) {
var a = xc - xa
var b = xb - xa
var c = yc - ya
var d = yb - ya
var result = (a * d - b * c) / Math.sqrt(Math.pow(xa - xb, 2) + Math.pow(ya - yb, 2))
return result < radius return player
} }
} }
} }

View File

@@ -6,9 +6,7 @@ const state = {
targetAngle: 0, targetAngle: 0,
color: 'red' color: 'red'
}, },
players: { updates: [],
},
settings: { settings: {
playerSize: 10, playerSize: 10,
playerSpeed: 2, playerSpeed: 2,
@@ -22,11 +20,20 @@ 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 }) {
commit('SET_CONNECTED') commit('SET_CONNECTED')
commit('CLEAR_UPDATE')
}, },
login ({ commit }, player) { login ({ commit }, player) {
commit('SET_PLAYER', player) commit('SET_PLAYER', player)
@@ -35,10 +42,11 @@ const actions = {
commit('SET_SETTINGS', settings) commit('SET_SETTINGS', settings)
}, },
update ({ commit }, update) { update ({ commit }, update) {
commit('SET_PLAYERS', update) commit('ADD_UPDATE', update)
}, },
dead ({ commit }, player) { dead ({ commit }, player) {
commit('SET_PLAYER', player) commit('SET_PLAYER', player)
commit('CLEAR_UPDATE')
}, },
error ({ commit }, error) { error ({ commit }, error) {
alert('Error: ' + error) alert('Error: ' + error)
@@ -60,9 +68,23 @@ const mutations = {
}, },
SET_CONNECTED (state) { SET_CONNECTED (state) {
state.socketConnected = !state.socketConnected state.socketConnected = !state.socketConnected
},
ADD_UPDATE (state, update) {
if (state.updates.length > 2) {
state.updates.shift()
}
update.time = update.time.epochSecond
state.updates.push(update)
},
CLEAR_UPDATE (state) {
state.updates = []
} }
} }
function interpolatePlayer (player, dt) {
return player
}
export default { export default {
namespaced: true, namespaced: true,
state, state,

View File

@@ -18,7 +18,7 @@ export default function createSocketPlugin () {
} }
connection.onmessage = function (message) { connection.onmessage = function (message) {
console.log('[WS] message', message.data) // console.log('[WS] message', message.data)
var data = JSON.parse(message.data) var data = JSON.parse(message.data)
@@ -43,6 +43,6 @@ export default function createSocketPlugin () {
} }
export function send (message) { export function send (message) {
console.log('[WS] send', message) // console.log('[WS] send', message)
connection.send(JSON.stringify(message)) connection.send(JSON.stringify(message))
} }

View File

@@ -1,6 +1,8 @@
package gltronic.tronio.business; package gltronic.tronio.business;
import java.io.IOException; import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Random; import java.util.Random;
@@ -13,6 +15,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.socket.WebSocketSession; 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.Player; import gltronic.tronio.model.Player;
import gltronic.tronio.model.Wall; import gltronic.tronio.model.Wall;
@@ -54,8 +57,12 @@ public class GameManager implements IGameManager {
} }
@Override @Override
@Scheduled(fixedDelay = 1000 / 60) @Scheduled(fixedDelay = 1000 / 120)
public void step() throws InterruptedException, IOException { public void step() throws InterruptedException, IOException {
Instant currentTime = Instant.now();
Duration durationSinceLastUpdate = Duration.between(game.getLastUpdate(), currentTime);
game.setLastUpdate(currentTime);
// CHECK OUT OF BORDERS & COLISIONS // CHECK OUT OF BORDERS & COLISIONS
game.getPlayers().forEach((id, player) -> { game.getPlayers().forEach((id, player) -> {
// OUT OF BORDERS // OUT OF BORDERS
@@ -92,21 +99,29 @@ public class GameManager implements IGameManager {
// ADD WALLS & MOVE PLAYER // ADD WALLS & MOVE PLAYER
game.getPlayers().forEach((id, player) -> { game.getPlayers().forEach((id, player) -> {
// WALL // On cherche le nombre de pas pour atteindre les 60 up/s
player.setLastWall(player.getLastWall() + 1); long tickToSimulate = ( durationSinceLastUpdate.toMillis() * 120 ) / 1000;
if (player.getLastWall() > game.getSettings().getWallUpdate()) {
player.getWalls().add(new Wall(player.getX(), player.getY()));
player.setLastWall(0);
}
// MOVE for (long i = 0; i <= tickToSimulate; i++) {
player.setX(player.getX() + game.getSettings().getPlayerSpeed() * Math.cos(player.getAngle())); // WALL
player.setY(player.getY() + game.getSettings().getPlayerSpeed() * Math.sin(player.getAngle())); player.setLastWall(player.getLastWall() + 1);
if (player.getLastWall() > game.getSettings().getWallUpdate()) {
player.getWalls().add(new Wall(player.getX(), player.getY()));
player.setLastWall(0);
}
// MOVE
player.setX(player.getX() + game.getSettings().getPlayerSpeed() * Math.cos(player.getAngle()));
player.setY(player.getY() + game.getSettings().getPlayerSpeed() * Math.sin(player.getAngle()));
}
}); });
// Broadcast une fois sur deux // Broadcast une fois sur deux
if (game.isUpdateNeeded()) { if (game.isUpdateNeeded()) {
SocketUtils.broadcast(game, "gameUpdate", new ObjectMapper().writeValueAsString(game.getPlayers().values())); GameUpdate update = new GameUpdate();
update.setTime(game.getLastUpdate());
update.setPlayers(game.getPlayers().values());
SocketUtils.broadcast(game, "gameUpdate", new ObjectMapper().writeValueAsString(update));
game.setUpdateNeeded(false); game.setUpdateNeeded(false);
} else game.setUpdateNeeded(true); } else game.setUpdateNeeded(true);
} }

View File

@@ -1,5 +1,6 @@
package gltronic.tronio.model; package gltronic.tronio.model;
import java.time.Instant;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -17,18 +18,13 @@ public class Game {
private Map<String, WebSocketSession> sessions; private Map<String, WebSocketSession> sessions;
private GameSettings settings; private GameSettings settings;
private boolean updateNeeded; private boolean updateNeeded;
private Instant lastUpdate;
public Game() { public Game() {
this.updateNeeded = false; this.updateNeeded = false;
this.lastUpdate = Instant.now();
this.players = new HashMap<>(); this.players = new HashMap<>();
this.sessions = new HashMap<>(); this.sessions = new HashMap<>();
this.settings = new GameSettings(); this.settings = new GameSettings();
this.settings.setArenaSize(1000);
this.settings.setPlayerSize(10);
this.settings.setPlayerSpeed(2);
this.settings.setPlayerTurnSpeed(10);
this.settings.setWallSize(8);
this.settings.setWallUpdate(5);
} }
} }

View File

@@ -1,12 +1,10 @@
package gltronic.tronio.model; package gltronic.tronio.model;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
@Getter @Getter
@Setter @Setter
@NoArgsConstructor
public class GameSettings { public class GameSettings {
private double playerSize; private double playerSize;
private double playerSpeed; private double playerSpeed;
@@ -14,4 +12,13 @@ public class GameSettings {
private double wallSize; private double wallSize;
private double wallUpdate; private double wallUpdate;
private double arenaSize; private double arenaSize;
public GameSettings () {
this.arenaSize = 1000;
this.playerSize = 10;
this.playerSpeed = 2;
this.playerTurnSpeed = 10;
this.wallSize = 8;
this.wallUpdate = 5;
}
} }

View File

@@ -0,0 +1,17 @@
package gltronic.tronio.model;
import java.time.Instant;
import java.util.Collection;
import org.springframework.stereotype.Component;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Component
public class GameUpdate {
private Collection<Player> players;
private Instant time;
}