文章目录
一、创建基础pojo类
1.Instruct指令类
public class Instruct{
private String order;//指令类型
private long time;//指令的发送时间戳
private Object obj;//指令包含的内容
private int roomId;//指令
private int uid;//userId
}
基础逻辑是前端发送固定指令类型的Json对象,服务端解析后根据order的不同来调用不同的函数
这显然对后期的维护和扩展有些阻碍,后续会重构这个类
2.Player玩家类
public class Player {
private double x;//x坐标
private double y;//y坐标
private int uid;
private int token;
private boolean move = false;
private double angle;//玩家角度
private float speed = 30;//玩家速度
public PlayerMoving playerMoving;//聚合的移动玩家类(继承Player类)
}
这里不得不提到PlayerMoving类
由于指令为move时玩家为移动状态,服务端需要新建一个线程不断的更新玩家的坐标数据,于是新建PlayerMoving extends Player类用于这个线程
public class PlayerMoving extends Player implements Runnable {
private long time;
public PlayerMoving(double x, double y, int uid, int token, boolean move, double angle, float speed) {
super(x, y, uid, token, move, angle, speed);
}
public long getTime() {
return time;
}
public PlayerMoving setTime(long time) {
this.time = time;
return this;
}
@Override
public void run() {
//System.out.println("跑动线程启动了,当前move情况" + this.getMove() + "超类情况" + super.getMove());
long timeDiffer = 0;
while(this.getMove()){
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized ((Long)time) {
timeDiffer = System.currentTimeMillis() - time;
time = System.currentTimeMillis();
this.setX(this.getX() + (timeDiffer * this.getSpeed() * 0.1F * Math.sin(Math.PI / 180 * this.getAngle())));
//System.out.println("角度=" + this.getAngle() + "X=" + timeDiffer + "*" + this.getSpeed() + "* 0.1 *" + Math.sin(Math.PI / 180 * this.getAngle()));
this.setY(this.getY() - (timeDiffer * this.getSpeed() * 0.1F * Math.cos(Math.PI / 180 * this.getAngle())));
//System.out.println("Y=" + timeDiffer + "*" + this.getSpeed() + "* 0.1 *" + Math.cos(Math.PI / 180 * this.getAngle()));
//System.out.println("修改后 x:" + this.getX() + "y:" + this.getY());
Player player = Room.players.get(this.getUid() + "");
//System.out.println(Room.users.toString());
//System.out.println(user);
player.setX(this.getX());
player.setY(this.getY());
//System.out.println("跑动线程..." + user.toString());
}
}
}
}
其中跑动函数用玩家的角度和速度,通过简单的三角函数不断计算一定时间内玩家xy值的变化量后更新。
3.Room类
public class Room {
public static ConcurrentHashMap<String,Player> players = new ConcurrentHashMap<>();
}
Room为一个游戏房间,里面包含所有玩家
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
二、游戏逻辑
1.前端发送的指令处理
在channelHandler中,对接收消息进行以下处理
@Override
protected void channelRead0(ChannelHandlerContext ctx,
TextWebSocketFrame msg) throws Exception { // (1)
//System.out.println("接收到text消息..." + msg.text());
//先对比uid是否正确
//将消息转为Instruct指令形式,然后交付给manage处理
Channel incoming = ctx.channel();
JSONObject jsonObject = JSONObject.fromObject(msg.text());
Instruct instruct = new Instruct(jsonObject.get("order")+"",jsonObject.getLong("time"),jsonObject.get("obj"),jsonObject.getInt("roomId"),Integer.parseInt(jsonObject.get("uid").toString()));
if (instruct.getOrder().equals("JOIN")){
if (!channelMap.containsKey(incoming.id()+"")){
System.out.println("指令异常返回");
return;
}
System.out.println("map更新" + channelMap.toString());
channelMap.put(instruct.getUid() + "",channelMap.remove(incoming.id()+""));
System.out.println("map更新完毕" + channelMap.toString());
}
if (!channelMap.containsKey(instruct.getUid() + "")){
System.out.println("不存在该UID...");
return;
}
//System.out.println("指令存入队列中...");
MsgManage.addInstruct(instruct);
}
2.MsgManage接收到指令后进行处理
public class MsgManage implements Runnable{
private static Queue<Instruct> instructs = new MyLinkedList();
public static Instruct instruct;
private boolean manageRun = true;
public static void addInstruct(Instruct instruct){
instructs.offer(instruct);
}
这里包含一个MyLinkedList对offer方法进行了重写,实现了一个简单的代理监听器
public class MyLinkedList extends LinkedList<Instruct> {
@Override
public boolean offer(Instruct instruct) {
super.offer(instruct);
MsgManage.instruct = this.poll();//返回后删除
switch (instruct.getOrder()) {
case "MOVE":
MsgManage.move();
break;
case "JOIN":
MsgManage.join();
break;
case "MOVEOUT":
MsgManage.moveOut();
break;
}
return true;
}
}
这里只是使用了最简单的switch进行指令识别,对后续扩展有很大阻碍,后面会对这里进行重构。
3.MsgManage的指令处理
public static void moveOut(){
Player player = Room.players.get(instruct.getUid()+"");
if (player == null)
return;
player.playerMoving.setMove(false);
player.setMove(false);
}
public static void move(){
Player player = Room.players.get(instruct.getUid()+"");
if (player==null)
return;
if (player.playerMoving == null){
player.instanceMoving(instruct.getTime());
}else{
player.playerMoving.setTime(System.currentTimeMillis());
}
if (instruct.getObj().getClass().equals(Integer.class)){
player.playerMoving.setAngle(Double.valueOf(instruct.getObj() + ""));
}else {
player.playerMoving.setAngle((Double) instruct.getObj());
}
if (player.playerMoving.getMove() == true){
}else {
player.playerMoving.setMove(true);
player.setMove(true);
new Thread(player.playerMoving).start();
}
}
public static void join(){
//在Room里面加入...
System.out.println(instruct.toString());
if (instruct.getUid() == 9){
Room.players.put(instruct.getUid()+"",new Player(0,0,instruct.getUid(),123,false,0,20));
}else {
System.out.println("当前Room" + Room.players.toString());
System.out.println("Room添加数据");
Room.players.put(instruct.getUid() + "", new Player(0, 0, instruct.getUid(), 123, false, 0, 1));
System.out.println("Room添加完毕" +Room.players.toString());
}
}
三、游戏房间广播逻辑
//每个房间独有的一个线程,存放在线程池中
public class MsgLogic implements Runnable{
Room room = new Room();
@Override
public void run() {
while(true) {
//广播房间信息...
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Room.players == null || Room.players.isEmpty()) {
continue;
}
for (Map.Entry<String, IPlayer> entry : Room.players.entrySet()) {
JSONObject jsonObject = JSONObject.fromObject(Room.players);
//System.out.println("uid" + entry.getKey() + "广播内容" + jsonObject.toString());
TextWebSocketFrameHandler.channelMap.get(entry.getKey() + "").writeAndFlush(new TextWebSocketFrame(jsonObject.toString()));
}
}
}
}