Java UDP多人坦克游戏客户端坦克闪烁异常排查求助
多人坦克游戏客户端绘制闪烁问题解决方案
问题根因
你当前将所有坦克的状态属性(x、y、方向、坦克编号等)都存储在同一个类的成员变量中,每次解析新的UDP坦克消息都会直接覆盖这些成员变量的值。
你在parsemessage中打印日志是刚完成赋值时打印的,所以数值对应正确;但绘制线程执行时,成员变量已经被后续收到的多条坦克消息覆盖为最后一条消息的坦克属性,因此绘制时绝大多数时候取到的都是最后一台坦克的参数,导致其他坦克出现闪烁。
解决方案
- 单独定义坦克实体类,每个坦克对应一个独立实例,各自存储自己的属性,不和网络处理类的公共变量混用
- 全局维护一个坦克列表,按坦克编号匹配对应实例
- 解析UDP消息时,将属性更新到对应编号的独立坦克实例上,不再覆盖公共变量
- 绘制时遍历坦克列表,调用每个坦克自身实例的绘制方法完成渲染
代码实现
1. 定义独立坦克实体类
public class Tank { private int tankno; private int x; private int y; private int direction; private int health; private int say; private int tip; public Tank(int tankno) { this.tankno = tankno; } // 对应get/set方法 public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getDir() { return direction; } public void setDir(int direction) { this.direction = direction; } public int getTankno() { return tankno; } public void setHealth(int health) { this.health = health; } public int getTip() { return tip; } public void setTip(int tip) { this.tip = tip; } // 坦克自身的绘制方法 public void draw(Graphics g, Bufferedimagex bf1) { g.drawImage(bf1.sprite(this.getDir()*32-32, this.getTip()*16), this.getX(), this.getY(), 48, 48, null); } }
2. 修改网络处理类逻辑
// 全局维护坦克列表,提前初始化已存在的坦克 private List<Tank> tankList = new ArrayList<>(); public void parsemessage(String messagereceived) { String[] msg= messagereceived.split(","); // 加锁防止并发修改异常 synchronized (tankList) { if(msg[0].equalsIgnoreCase("playtank")) { int tankno = Integer.parseInt(msg[1]); int x = Integer.parseInt(msg[2]); int y = Integer.parseInt(msg[3]); int direction = Integer.parseInt(msg[4]); // 匹配对应坦克更新属性 for (Tank tank : tankList) { if (tank.getTankno() == tankno) { tank.setX(x); tank.setY(y); tank.setDir(direction); break; } } } if(msg[0].equalsIgnoreCase("addnewtanks")) { int tankno = Integer.parseInt(msg[1]); int x = Integer.parseInt(msg[2]); int y = Integer.parseInt(msg[3]); int direction = Integer.parseInt(msg[4]); int health = Integer.parseInt(msg[5]); int say = Integer.parseInt(msg[6]); int tip = Integer.parseInt(msg[7]); // 新增坦克实例加入列表 Tank newTank = new Tank(tankno); newTank.setX(x); newTank.setY(y); newTank.setDir(direction); newTank.setHealth(health); newTank.setTip(tip); tankList.add(newTank); } } }
3. 修改全局绘制逻辑
public void drawAllTanks(Graphics g, Bufferedimagex bf1) { // 加锁防止和网络更新线程并发冲突 synchronized (tankList) { for (Tank tank : tankList) { tank.draw(g, bf1); } } }
注意事项
UDP接收逻辑运行在子线程,绘制逻辑运行在UI主线程,两个线程同时操作坦克列表时必须加锁,避免出现并发修改异常。
内容的提问来源于stack exchange,提问作者Recai Furfur
相关产品推荐
相关产品推荐

