这次写了一个网络小游戏飞猪。通过这次练习让我了解如何把界面开发上的某个角色或者物体映射到对应分配的客户端让对应的客户端能实时通过外设进行操作以及怎么让多个客户端对现下所有用户的状态进行同步。这个小飞猪的游戏总体规则就是若干的客户端同时以一个大小随机出现在一个界面界面上面随机遍布若干带有数字权重的食物小飞猪吃了就会相应的变化当窗口的食物都吃完时会显示排名排名最高的有可能会被卖掉吃也有可能成为猪王。首先是怎么实现界面中的角色和客户端实现一一映射就是通过再写一个类把客户端的类和角色类同时作为属性写在里面。我发现计算机的很多技术里面是如果两个不能不能实现就加个中间件如果还不行那就是再加一个一般这样能解决很多问题。packageClient_Server.v5;importjava.io.*;importjava.net.Socket;importjava.util.Random;/*** * 客户端数据对象每个接入的客户端绑定一个 Socket、一只飞猪角色和读写流 */publicclassClientDO{Socketsocket;FlyPigflypig;// 该客户端控制的飞猪BufferedReaderreader;// 读取客户端指令PrintWriterwriter;// 向客户端发送状态Servergemeserver;// 反向引用方便回调RandomrannewRandom();volatilebooleanconnectedtrue;publicClientDO(Socketsocket,Servergemeserver){this.socketsocket;this.serverserver;try{this.readernewBufferedReader(newInputStreamReader(socket.getInputStream()));this.writernewPrintWriter(socket.getOutputStream(),true);}catch(IOExceptione){e.printStackTrace();}// 每只飞猪初始随机位置、大小 60、缓慢移动this.flypignewFlyPig(ran.nextInt(Servergeme.WIDTH-100)50,ran.nextInt(Servergeme.HEIGHT-100)50,60,1,1);}/** 启动独立线程持续读取该客户端发来的移动指令 */publicvoidstartReading(){newThread(()-{try{Stringcmd;while(connected(cmdreader.readLine())!null){server.processCommand(flypig,cmd.trim().toUpperCase());}}catch(IOExceptione){// 客户端断开}finally{connectedfalse;server.removeClient(this);}},Reader-socket.getPort()).start();}/** 向该客户端发送一行消息 */publicvoidsend(Stringmsg){try{writer.println(msg);}catch(Exceptione){connectedfalse;}}}然后是把当前所有角色状态同步到每一个角色一开始可能想到的是把状态遍历到每个客户端哪里但注意每个客户端要拿到对应客户的找到这个客户端需要遍历每个客户端的状态又需要遍历就会导致很慢。场景 1单用户状态更新差距最明显在线 1 万人每秒 200 次外设操作上报链表方案200 * 10000 200 万次循环比对CPU 负载极高哈希表方案200 次哈希计算几乎无压力。场景 2用户频繁上下线链表致命缺陷链表删除 / 插入节点找到目标节点要遍历删除后还要修改前后节点指针哈希表增删仅计算哈希下标直接读写对应槽位开销极小。场景 3实时同步对延迟极度敏感外设操作、多客户端状态同步要求低延迟链表大量循环带来毫秒甚至百毫秒级延迟用户会直观感受到操作手柄后其他客户端很久才同步到动作哈希表查询几乎无延迟操作实时同步。packageClient_Server.v5;importjava.io.*;importjava.net.*;importjava.util.*;importjava.util.concurrent.*;/** * 飞猪大战 —— 服务端 * * 职责 * - 监听端口 88888接受客户端连接 * - 为每个客户端创建一只飞猪并启动专用读线程接收移动指令 * - 主游戏循环移动飞猪 → 碰撞检测吃食物→ 广播状态 → 检查结束/排名 */publicclassServergeme{publicstaticfinalintWIDTH800;// 画板宽publicstaticfinalintHEIGHT700;// 画板高publicstaticfinalintFOOD_COUNT25;// 每轮食物数量publicstaticfinalintTICK_MS50;// 游戏循环间隔 (20 FPS)ServerSocketserversocket;ConcurrentHashMapString,ClientDOclientlistnewConcurrentHashMap();CopyOnWriteArrayListFlyPigflypiglistnewCopyOnWriteArrayList();CopyOnWriteArrayListFoodfoodlistnewCopyOnWriteArrayList();volatilebooleanrunningtrue;/** 启动 ServerSocket */publicvoidinit(){try{serversocketnewServerSocket(8888);System.out.println([服务器] 已启动端口 8888);}catch(IOExceptione){thrownewRuntimeException(无法启动服务器,e);}}/** 持续接受客户端连接独立线程 */publicvoidlisent(){newThread(()-{while(running){try{System.out.println([服务器] 等待客户端连接...);Socketsocketserversocket.accept();System.out.println([服务器] 新客户端接入: socket.getInetAddress():socket.getPort());ClientDOclientnewClientDO(socket,this);clientlist.put(client.flypig.id,client);flypiglist.add(client.flypig);client.startReading();// 为该客户端开启独立读线程// 告知客户端它的飞猪 IDclient.send(YOURID:client.flypig.id);}catch(IOExceptione){if(running)e.printStackTrace();}}},Acceptor).start();}/** 处理客户端发来的移动指令 */publicvoidprocessCommand(FlyPigpig,Stringcmd){if(pignull)return;switch(cmd){caseUP:pig.up();break;caseDOWN:pig.down();break;caseLEFT:pig.left();break;caseRIGHT:pig.right();break;caseSTOP:pig.stop();break;}}/** 移除断开的客户端 */publicvoidremoveClient(ClientDOclient){System.out.println([服务器] 客户端断开: client.flypig.id);clientlist.remove(client.flypig.id);flypiglist.remove(client.flypig);try{client.socket.close();}catch(IOExceptionignored){}}/** 在画板上随机生成 count 颗食物 */publicvoidgenerateFoods(intcount){foodlist.clear();for(inti0;icount;i){foodlist.add(newFood(WIDTH,HEIGHT));}System.out.println([服务器] 生成 count 颗食物);}/** 检测每颗食物是否被某只飞猪吃到中心距离 半径和 */publicvoidcheckCollisions(){for(Foodfood:foodlist){for(FlyPigpig:flypiglist){doubledxpig.x-food.x;doubledypig.y-food.y;doubledistMath.sqrt(dx*dxdy*dy);if(dist(pig.sizefood.size*2)/2.0){pig.sizefood.size;// 飞猪变大foodlist.remove(food);break;}}}}/** 向所有客户端广播当前游戏状态 */publicvoidbroadcast(){StringBuildersbnewStringBuilder(STATE:);// 飞猪数据: id,x,y,size,colorRGB;for(FlyPigpig:flypiglist){sb.append(pig.id).append(,).append(pig.x).append(,).append(pig.y).append(,).append(pig.size).append(,).append(pig.color.getRGB()).append(;);}sb.append(|);// 食物数据: x,y,size;for(Foodfood:foodlist){sb.append(food.x).append(,).append(food.y).append(,).append(food.size).append(;);}Stringstatesb.toString();for(ClientDOclient:clientlist.values()){client.send(state);}}/** 广播排名 */publicvoidbroadcastRank(){StringrankgetRankings();System.out.println([服务器] 游戏结束! rank);for(ClientDOclient:clientlist.values()){client.send(rank);}}/** 按飞猪 size 降序排列生成排名串 */publicStringgetRankings(){ListFlyPigsortednewArrayList(flypiglist);sorted.sort((a,b)-b.size-a.size);StringBuildersbnewStringBuilder(RANK:);for(inti0;isorted.size();i){FlyPigpsorted.get(i);sb.append(i1).append(,).append(p.id).append(,).append(p.size);if(isorted.size()-1)sb.append(;);}returnsb.toString();}/** 主循环独立线程移动 → 碰撞 → 广播 → 检查结束 */publicvoidgameloop(){newThread(()-{generateFoods(FOOD_COUNT);while(running){// 1. 移动所有飞猪 边界反弹for(FlyPigpig:flypiglist){pig.move();if(pig.x0){pig.x0;pig.speedxMath.abs(pig.speedx);}if(pig.xWIDTH){pig.xWIDTH;pig.speedx-Math.abs(pig.speedx);}if(pig.y0){pig.y0;pig.speedyMath.abs(pig.speedy);}if(pig.yHEIGHT){pig.yHEIGHT;pig.speedy-Math.abs(pig.speedy);}}// 2. 碰撞检测飞猪吃食物checkCollisions();// 3. 广播状态broadcast();// 4. 食物吃光 → 排名 → 重置if(foodlist.isEmpty()){broadcastRank();try{Thread.sleep(3000);}catch(InterruptedExceptionignored){}generateFoods(FOOD_COUNT);}// 5. 帧率控制try{Thread.sleep(TICK_MS);}catch(InterruptedExceptione){break;}}},GameLoop).start();}publicstaticvoidmain(String[]args){ServergemeservernewServergeme();server.init();server.lisent();server.gameloop();}}