改绘制错误 bug + 将地图行,列,障碍物数量改为后端获取
This commit is contained in:
parent
e6c49ad600
commit
1224f16414
|
@ -21,10 +21,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
@Component
|
||||
@ServerEndpoint("/websocket/{token}") // 注意不要以'/'结尾
|
||||
public class WebSocketServer {
|
||||
// 后端向前端发送信息,首先需要创建一个 session
|
||||
private Session session = null;
|
||||
// 用户信息:定义一个成员变量
|
||||
private User user;
|
||||
/*
|
||||
存储所有链接:对所有 websocket 可见的全局变量,存储为 static 静态变量
|
||||
因为每个 websocket 实例都在一个独立的线程里,所以该公共变量应该是线程安全的
|
||||
|
@ -36,6 +32,10 @@ public class WebSocketServer {
|
|||
private static final CopyOnWriteArraySet<User> matchPool = new CopyOnWriteArraySet<>();
|
||||
// 在 WebSocketServer 中注入数据库的方法演示-> 使用 static 定义为独一份的变量
|
||||
private static UserMapper userMapper;
|
||||
// 后端向前端发送信息,首先需要创建一个 session
|
||||
private Session session = null;
|
||||
// 用户信息:定义一个成员变量
|
||||
private User user;
|
||||
|
||||
// 注入方法
|
||||
@Autowired
|
||||
|
@ -85,6 +85,54 @@ public class WebSocketServer {
|
|||
}
|
||||
}
|
||||
|
||||
// 开始匹配的逻辑部分
|
||||
private void startMatching() {
|
||||
System.out.println("start matching");
|
||||
matchPool.add(this.user);
|
||||
|
||||
while (matchPool.size()>=2){
|
||||
// 迭代器用于枚举前两个人进行匹配
|
||||
Iterator<User> it = matchPool.iterator();
|
||||
User a = it.next(), b = it.next();
|
||||
// 取出两个人之后,从匹配池中将他们删除
|
||||
matchPool.remove(a);
|
||||
matchPool.remove(b);
|
||||
|
||||
// 匹配成功时,创建联机地图
|
||||
Game game = new Game(13,14,20);
|
||||
game.createMap(); // 初始化地图
|
||||
|
||||
JSONObject respGameData = new JSONObject();
|
||||
respGameData.put("game_map",game.getG());
|
||||
respGameData.put("rows",game.getRows());
|
||||
respGameData.put("cols",game.getCols());
|
||||
respGameData.put("inner_walls_count",game.getInnerWallsCount());
|
||||
|
||||
// 将 a 配对成功的消息传回客户端
|
||||
JSONObject respA = new JSONObject();
|
||||
respA.put("event","start-matching");
|
||||
respA.put("opponent_username",b.getUsername());
|
||||
respA.put("opponent_photo",b.getPhoto());
|
||||
respA.put("game_data",respGameData);
|
||||
// 获取 a 的链接,并通过 sendMessage 将消息传给前端
|
||||
users.get(a.getId()).sendMessage(respA.toJSONString());
|
||||
|
||||
// 同理,将 b 的匹配成功信息传回给前端
|
||||
JSONObject respB = new JSONObject();
|
||||
respB.put("event","start-matching");
|
||||
respB.put("opponent_username",a.getUsername());
|
||||
respB.put("opponent_photo",a.getPhoto());
|
||||
respB.put("game_data",respGameData);
|
||||
users.get(b.getId()).sendMessage(respB.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
// 取消匹配的逻辑部分
|
||||
private void stopMatching() {
|
||||
System.out.println("stop matching");
|
||||
matchPool.remove(this.user);
|
||||
}
|
||||
|
||||
// @OnMessage 用于从前端接收请求: 一般 onMessage 当成路由使用,做为消息判断处理的中间部分
|
||||
@OnMessage
|
||||
public void onMessage(String message, Session session) {
|
||||
|
@ -121,47 +169,6 @@ public class WebSocketServer {
|
|||
}
|
||||
}
|
||||
|
||||
// 开始匹配的逻辑部分
|
||||
private void startMatching() {
|
||||
System.out.println("start matching");
|
||||
matchPool.add(this.user);
|
||||
|
||||
while (matchPool.size()>=2){
|
||||
// 迭代器用于枚举前两个人进行匹配
|
||||
Iterator<User> it = matchPool.iterator();
|
||||
User a = it.next(), b = it.next();
|
||||
// 取出两个人之后,从匹配池中将他们删除
|
||||
matchPool.remove(a);
|
||||
matchPool.remove(b);
|
||||
|
||||
// 匹配成功时,创建联机地图
|
||||
Game game = new Game(13,14,20);
|
||||
game.createMap(); // 初始化地图
|
||||
|
||||
// 将 a 配对成功的消息传回客户端
|
||||
JSONObject respA = new JSONObject();
|
||||
respA.put("event","start-matching");
|
||||
respA.put("opponent_username",b.getUsername());
|
||||
respA.put("opponent_photo",b.getPhoto());
|
||||
respA.put("game_map",game.getG());
|
||||
// 获取 a 的链接,并通过 sendMessage 将消息传给前端
|
||||
users.get(a.getId()).sendMessage(respA.toJSONString());
|
||||
|
||||
// 同理,将 b 的匹配成功信息传回给前端
|
||||
JSONObject respB = new JSONObject();
|
||||
respB.put("event","start-matching");
|
||||
respB.put("opponent_username",a.getUsername());
|
||||
respB.put("opponent_photo",a.getPhoto());
|
||||
respB.put("game_map",game.getG());
|
||||
users.get(b.getId()).sendMessage(respB.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
// 取消匹配的逻辑部分
|
||||
private void stopMatching() {
|
||||
System.out.println("stop matching");
|
||||
matchPool.remove(this.user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,15 +17,51 @@ public class Game {
|
|||
public Game(Integer rows, Integer cols, Integer inner_walls_count) {
|
||||
this.rows = rows;
|
||||
this.cols = cols;
|
||||
this.inner_walls_count = rows;
|
||||
this.inner_walls_count = inner_walls_count;
|
||||
this.g = new int[rows][cols];
|
||||
}
|
||||
|
||||
public int getRows(){
|
||||
return rows;
|
||||
}
|
||||
public int getCols(){
|
||||
return cols;
|
||||
}
|
||||
public int getInnerWallsCount(){
|
||||
return inner_walls_count;
|
||||
}
|
||||
|
||||
// 返回生成的地图
|
||||
public int[][] getG() {
|
||||
return g;
|
||||
}
|
||||
|
||||
// 联通检测方法---true(联通)---false(不通),参数: 起点坐标 sx,sy ,终点坐标 tx,ty
|
||||
private boolean check_connectivity(int sx, int sy, int tx, int ty) {
|
||||
// 起点就是终点时,结果联通,直接返回 true
|
||||
if (sx == tx && sy == ty) return true;
|
||||
g[sx][sy] = 1;
|
||||
|
||||
//枚举"上右下左"四个方向,求当前点下一个相邻点的坐标
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int x = sx + dx[i];
|
||||
int y = sy + dy[i];
|
||||
|
||||
// 判断是否撞到障碍物( g[x][y] == 0 表示没有碰到障碍物 ),如果没有赚到障碍物,且可以找到重点的话,返回 true(联通)
|
||||
if (x >= 0 && x < this.rows && y >= 0 && y < this.cols && g[x][y] == 0) {
|
||||
if (check_connectivity(x, y, tx, ty)) {
|
||||
// 还原状态
|
||||
g[sx][sy] = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 还原状态
|
||||
g[sx][sy] = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 画地图
|
||||
public boolean draw() {
|
||||
// 一开始现将所有障碍物初始化为 false
|
||||
|
@ -74,32 +110,6 @@ public class Game {
|
|||
return check_connectivity(this.rows - 2, 1, 1, this.cols - 2);
|
||||
}
|
||||
|
||||
// 联通检测方法---true(联通)---false(不通),参数: 起点坐标 sx,sy ,终点坐标 tx,ty
|
||||
private boolean check_connectivity(int sx, int sy, int tx, int ty) {
|
||||
// 起点就是终点时,结果联通,直接返回 true
|
||||
if (sx == tx && sy == ty) return true;
|
||||
g[sx][sy] = 1;
|
||||
|
||||
//枚举"上右下左"四个方向,求当前点下一个相邻点的坐标
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int x = sx + dx[i];
|
||||
int y = sy + dy[i];
|
||||
|
||||
// 判断是否撞到障碍物( g[x][y] == 0 表示没有碰到障碍物 ),如果没有赚到障碍物,且可以找到重点的话,返回 true(联通)
|
||||
if (x >= 0 && x < this.rows && y >= 0 && y < this.cols && g[x][y] == 0) {
|
||||
if (check_connectivity(x, y, tx, ty)) {
|
||||
// 还原状态
|
||||
g[sx][sy] = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 还原状态
|
||||
g[sx][sy] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void createMap() {
|
||||
// 循环绘制:如果发现哪次循环中画地图成功了,则跳出循环,绘制结束
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
|
|
|
@ -6,11 +6,10 @@ import io.jsonwebtoken.Claims;
|
|||
//Jwt 验证配置工具类
|
||||
public class JwtAuthenticationUtil {
|
||||
public static Integer getUserId(String token) {
|
||||
// 核心验证逻辑
|
||||
// 默认 userid 赋值为 -1 ,表示不存在
|
||||
// 默认 userid 赋值为 -1 ,表示不存在
|
||||
int userId = -1;
|
||||
try {
|
||||
// 将 token 解析,如果能成功解析出 userid 表示合法,否则表示不合法
|
||||
// 将 token 解析,如果能成功解析出 userid 表示合法,否则表示不合法
|
||||
Claims claims = JwtUtil.parseJWT(token);
|
||||
userId = Integer.parseInt(claims.getSubject());
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -15,18 +15,19 @@ export class GameMap extends AcGameObject {
|
|||
this.ctx = ctx;
|
||||
this.parent = parent;
|
||||
this.store = store;
|
||||
|
||||
// 存下每个格子的绝对距离
|
||||
this.L = 0;
|
||||
|
||||
/*
|
||||
// 定义棋盘格的行数和列数(前端生成地图时使用)
|
||||
/* // 定义棋盘格的行数和列数(前端生成地图时使用)
|
||||
// 行数和列数不同时设置为偶数或者不同时设置为奇数,可以避免AB两蛇同时进入同一个格子,避免因此对优势者不公平
|
||||
this.rows = 13;
|
||||
this.cols = 14;
|
||||
// 绘制棋盘内部区域的障碍物(墙)的数量(前端生成地图时使用)
|
||||
this.inner_walls_count = 30;
|
||||
*/
|
||||
this.inner_walls_count = 20; */
|
||||
|
||||
this.rows = this.store.state.pk.rows;
|
||||
this.cols = this.store.state.pk.cols;
|
||||
this.inner_walls_count = this.store.state.pk.inner_walls_count;
|
||||
|
||||
// 存储所有的墙
|
||||
// 上面的 super() 会先将 AcGameObject 先绘制, walls 的绘制在后面执行,因此墙最后会覆盖原棋盘格进行绘制
|
||||
|
@ -40,7 +41,7 @@ export class GameMap extends AcGameObject {
|
|||
];
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
// 判断函数:判断角色路径是否联通。传入参数:g数组,起点和终点的横纵坐标(前端生成地图时使用)
|
||||
check_connectivity(g, sx, sy, tx, ty) {
|
||||
// 当起点坐标和中点坐标一致时,判断联通,直接返回
|
||||
|
@ -64,11 +65,10 @@ export class GameMap extends AcGameObject {
|
|||
*/
|
||||
|
||||
// 创建障碍物(后端生成地图版本)
|
||||
create_wall() {
|
||||
create_walls() {
|
||||
// 取出后端生成后传到前端 store 中的 game_map
|
||||
const g = this.store.state.pk.game_map;
|
||||
|
||||
|
||||
// 枚举数组,将 g[r][c] == true 的部分绘制出来
|
||||
// 如果上一步连通性检测失败,则退出 this.create_wall() 函数,本步骤不再执行生成新对象的操作
|
||||
for (let r = 0; r < this.rows; r++) {
|
||||
|
@ -79,7 +79,6 @@ export class GameMap extends AcGameObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制成功则 return turn
|
||||
return true;
|
||||
}
|
||||
|
@ -188,10 +187,10 @@ export class GameMap extends AcGameObject {
|
|||
start() {
|
||||
// 开始时调用一次创建墙的函数
|
||||
// 循环 1000 次,如果成功创建则 break ,否则继续循环创建(前端生成地图时使用这一条)
|
||||
// for (let i = 0; i < 1000; i++) if (this.create_wall()) break;
|
||||
// for (let i = 0; i < 1000; i++) if (this.create_walls()) break;
|
||||
|
||||
// 使用后端生成地图时,这里只需要调用一次就好
|
||||
this.create_wall();
|
||||
this.create_walls();
|
||||
|
||||
// 开始时启动监听方法
|
||||
this.add_listening_events();
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import { AcGameObject } from "./AcGameObject";
|
||||
import { Wall } from "./Wall";
|
||||
import { Snake } from './Snake';
|
||||
|
||||
export class GameMap extends AcGameObject {
|
||||
constructor(ctx, parent, store) {
|
||||
super();
|
||||
|
||||
this.ctx = ctx;
|
||||
this.parent = parent;
|
||||
this.store = store;
|
||||
this.L = 0;
|
||||
|
||||
this.rows = 13;
|
||||
this.cols = 14;
|
||||
|
||||
this.inner_walls_count = 20;
|
||||
this.walls = [];
|
||||
|
||||
this.snakes = [
|
||||
new Snake({id: 0, color: "#4876EC", r: this.rows - 2, c: 1}, this),
|
||||
new Snake({id: 1, color: "#F94848", r: 1, c: this.cols - 2}, this),
|
||||
];
|
||||
}
|
||||
|
||||
create_walls() {
|
||||
const g = this.store.state.pk.gamemap;
|
||||
|
||||
for (let r = 0; r < this.rows; r ++ ) {
|
||||
for (let c = 0; c < this.cols; c ++ ) {
|
||||
if (g[r][c]) {
|
||||
this.walls.push(new Wall(r, c, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_listening_events() {
|
||||
this.ctx.canvas.focus();
|
||||
|
||||
const [snake0, snake1] = this.snakes;
|
||||
this.ctx.canvas.addEventListener("keydown", e => {
|
||||
if (e.key === 'w') snake0.set_direction(0);
|
||||
else if (e.key === 'd') snake0.set_direction(1);
|
||||
else if (e.key === 's') snake0.set_direction(2);
|
||||
else if (e.key === 'a') snake0.set_direction(3);
|
||||
else if (e.key === 'ArrowUp') snake1.set_direction(0);
|
||||
else if (e.key === 'ArrowRight') snake1.set_direction(1);
|
||||
else if (e.key === 'ArrowDown') snake1.set_direction(2);
|
||||
else if (e.key === 'ArrowLeft') snake1.set_direction(3);
|
||||
});
|
||||
}
|
||||
|
||||
start() {
|
||||
this.create_walls();
|
||||
|
||||
this.add_listening_events();
|
||||
}
|
||||
|
||||
update_size() {
|
||||
this.L = parseInt(Math.min(this.parent.clientWidth / this.cols, this.parent.clientHeight / this.rows));
|
||||
this.ctx.canvas.width = this.L * this.cols;
|
||||
this.ctx.canvas.height = this.L * this.rows;
|
||||
}
|
||||
|
||||
check_ready() { // 判断两条蛇是否都准备好下一回合了
|
||||
for (const snake of this.snakes) {
|
||||
if (snake.status !== "idle") return false;
|
||||
if (snake.direction === -1) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
next_step() { // 让两条蛇进入下一回合
|
||||
for (const snake of this.snakes) {
|
||||
snake.next_step();
|
||||
}
|
||||
}
|
||||
|
||||
check_valid(cell) { // 检测目标位置是否合法:没有撞到两条蛇的身体和障碍物
|
||||
for (const wall of this.walls) {
|
||||
if (wall.r === cell.r && wall.c === cell.c)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const snake of this.snakes) {
|
||||
let k = snake.cells.length;
|
||||
if (!snake.check_tail_increasing()) { // 当蛇尾会前进的时候,蛇尾不要判断
|
||||
k -- ;
|
||||
}
|
||||
for (let i = 0; i < k; i ++ ) {
|
||||
if (snake.cells[i].r === cell.r && snake.cells[i].c === cell.c)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.update_size();
|
||||
if (this.check_ready()) {
|
||||
this.next_step();
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
const color_even = "#AAD751", color_odd = "#A2D149";
|
||||
for (let r = 0; r < this.rows; r ++ ) {
|
||||
for (let c = 0; c < this.cols; c ++ ) {
|
||||
if ((r + c) % 2 == 0) {
|
||||
this.ctx.fillStyle = color_even;
|
||||
} else {
|
||||
this.ctx.fillStyle = color_odd;
|
||||
}
|
||||
this.ctx.fillRect(c * this.L, r * this.L, this.L, this.L);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,9 @@ export default {
|
|||
opponent_username: "",
|
||||
opponent_photo: "",
|
||||
game_map: null,
|
||||
rows: 0,
|
||||
cols: 0,
|
||||
inner_walls_count: 0,
|
||||
},
|
||||
getters: {},
|
||||
mutations: {
|
||||
|
@ -25,9 +28,12 @@ export default {
|
|||
state.status = status;
|
||||
},
|
||||
// 更新地图
|
||||
updateGameMap(state,game_map){
|
||||
state.game_map = game_map;
|
||||
}
|
||||
updateGameMap(state, game_data) {
|
||||
state.game_map = game_data.game_map;
|
||||
state.rows = game_data.rows;
|
||||
state.cols = game_data.cols;
|
||||
state.inner_walls_count = game_data.inner_walls_count;
|
||||
},
|
||||
},
|
||||
actions: {},
|
||||
module: {},
|
||||
|
|
|
@ -56,9 +56,10 @@ export default {
|
|||
// 匹配成功,延迟两秒后:更改匹配状态 matching -> playing
|
||||
setTimeout(() => {
|
||||
store.commit("updateStatus", "playing");
|
||||
}, 2000);
|
||||
}, 200);
|
||||
// 匹配成功,从后端更新地图
|
||||
store.commit("updateGameMap",data.game_map)
|
||||
store.commit("updateGameMap", data.game_data);
|
||||
console.log(data.game_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue