爱吃鱼的大柚 發表於 2025-12-24 08:51

12/24摇一摇

摇一摇一摇

爱吃鱼的大柚 發表於 2025-12-24 08:53

#在這裡快       摇一摇一摇速回復#

爱吃鱼的大柚 發表於 2025-12-24 09:03

       
摇一摇一摇

爱吃鱼的大柚 發表於 2025-12-24 09:09

摇一摇一摇

爱吃鱼的大柚 發表於 2025-12-24 09:51

摇一摇一摇

爱吃鱼的大柚 發表於 2025-12-24 10:04

摇一摇一摇

爱吃鱼的大柚 發表於 2025-12-24 17:07

摇一摇一摇

爱吃鱼的大柚 發表於 2025-12-24 17:13

摇一摇一摇

爱吃鱼的大柚 發表於 2025-12-24 17:22

摇一摇一摇

爱吃鱼的大柚 發表於 2025-12-24 17:27

关键技术要点说明:

怪物分类显示系统:

主动攻击怪物显示为红点

被动怪物显示为黄点

BOSS级怪物显示为大红点并标注"BOSS"

实时位置同步机制:

服务器发送S_NPCPack封包通知怪物出现

服务器发送S_MoveNpcPacket封包更新怪物位置

客户端接收并立即更新小地图显示

小地图坐标转换:

将游戏世界坐标转换为小地图像素坐标

只显示玩家视野范围内的怪物

玩家固定在小地图中心,怪物位置相对显示

性能优化考虑:

使用CopyOnWriteArrayList保证线程安全

只更新视野内的怪物,减少绘制开销

根据怪物类型决定绘制细节(名称显示等)

扩展性设计:

可轻松添加更多怪物类型和图标

支持动态添加/移除怪物

可扩展为显示NPC、队友等其他实体

这个系统完整模拟了怪物在小地图上的红点显示和实时移动功能,可以直接运行观察效果。
import java.util.*;
import java.util.concurrent.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.util.List;

/**
* 怪物小地图红点显示系统模拟
* 演示如何在小地图上显示所有怪物为红点并实时跟随移动
*/

// 1. 怪物数据包类 - 发送怪物出现信息给客户端
class S_NPCPack {
    private final int monsterId;
    private final int x, y;
    private final int gfxId;
    private final int level;
    private final boolean isAggressive;
    private final int minimapType; // 0=不显示, 1=红点, 2=黄点, 3=BOSS
   
    public S_NPCPack(Monster monster) {
      this.monsterId = monster.getId();
      this.x = monster.getX();
      this.y = monster.getY();
      this.gfxId = monster.getGfxId();
      this.level = monster.getLevel();
      this.isAggressive = monster.isAggressive();
      this.minimapType = calculateMinimapType(monster);
    }
   
    private int calculateMinimapType(Monster monster) {
      if (monster.isBoss()) {
            return 3; // BOSS特殊图标
      } else if (monster.isAggressive() || monster.getLevel() > 10) {
            return 1; // 主动怪或高级怪显示红点
      } else {
            return 0; // 被动低级怪不显示
      }
    }
   
    // 获取封包数据(模拟)
    public byte[] getPacketData() {
      // 实际游戏中会使用二进制协议
      // 这里简化返回关键信息
      System.out.println("发送怪物出现封包: ID=" + monsterId +
                         ", 位置=(" + x + "," + y + ")" +
                         ", 小地图类型=" + minimapType);
      return new byte;
    }
   
    public int getMonsterId() { return monsterId; }
    public int getX() { return x; }
    public int getY() { return y; }
    public int getMinimapType() { return minimapType; }
}

// 2. 怪物移动封包类 - 发送怪物移动信息给客户端
class S_MoveNpcPacket {
    private final int monsterId;
    private final int newX, newY;
    private final int heading;
   
    public S_MoveNpcPacket(Monster monster, int newX, int newY, int heading) {
      this.monsterId = monster.getId();
      this.newX = newX;
      this.newY = newY;
      this.heading = heading;
    }
   
    public byte[] getPacketData() {
      System.out.println("发送怪物移动封包: ID=" + monsterId +
                         ", 新位置=(" + newX + "," + newY + ")" +
                         ", 方向=" + heading);
      return new byte;
    }
   
    public int getMonsterId() { return monsterId; }
    public int getNewX() { return newX; }
    public int getNewY() { return newY; }
}

// 3. 怪物对象类
class Monster {
    private static int nextId = 1;
   
    private final int id;
    private int x, y;
    private int gfxId;
    private int level;
    private boolean aggressive;
    private boolean boss;
    private String name;
   
    public Monster(int x, int y, String name, int level, boolean aggressive, boolean boss) {
      this.id = nextId++;
      this.x = x;
      this.y = y;
      this.name = name;
      this.level = level;
      this.aggressive = aggressive;
      this.boss = boss;
      this.gfxId = level * 10; // 模拟图形ID
    }
   
    public void move(int newX, int newY) {
      this.x = newX;
      this.y = newY;
    }
   
    public void randomMove(int mapWidth, int mapHeight) {
      int dx = (int)(Math.random() * 21) - 10; // -10 到 10
      int dy = (int)(Math.random() * 21) - 10;
      
      int newX = Math.max(0, Math.min(mapWidth - 1, x + dx));
      int newY = Math.max(0, Math.min(mapHeight - 1, y + dy));
      
      move(newX, newY);
    }
   
    // Getters
    public int getId() { return id; }
    public int getX() { return x; }
    public int getY() { return y; }
    public int getGfxId() { return gfxId; }
    public int getLevel() { return level; }
    public boolean isAggressive() { return aggressive; }
    public boolean isBoss() { return boss; }
    public String getName() { return name; }
}

// 4. 游戏服务器类 - 管理怪物和发送封包
class GameServer {
    private final List<Monster> monsters = new ArrayList<>();
    private final List<Client> clients = new ArrayList<>();
    private final int mapWidth = 5000;
    private final int mapHeight = 5000;
   
    // 添加怪物
    public void addMonster(Monster monster) {
      monsters.add(monster);
      // 通知所有客户端怪物出现
      broadcastToAllClients(new S_NPCPack(monster));
    }
   
    // 添加客户端
    public void addClient(Client client) {
      clients.add(client);
      // 发送所有已有怪物信息给新客户端
      for (Monster monster : monsters) {
            client.receivePacket(new S_NPCPack(monster));
      }
    }
   
    // 广播给所有客户端
    private void broadcastToAllClients(Object packet) {
      for (Client client : clients) {
            client.receivePacket(packet);
      }
    }
   
    // 更新所有怪物位置(模拟怪物移动)
    public void updateMonsterPositions() {
      for (Monster monster : monsters) {
            int oldX = monster.getX();
            int oldY = monster.getY();
            
            // 随机移动怪物
            monster.randomMove(mapWidth, mapHeight);
            
            // 如果位置改变,通知客户端
            if (monster.getX() != oldX || monster.getY() != oldY) {
                int heading = calculateHeading(oldX, oldY, monster.getX(), monster.getY());
                broadcastToAllClients(new S_MoveNpcPacket(monster,
                  monster.getX(), monster.getY(), heading));
            }
      }
    }
   
    // 计算移动方向
    private int calculateHeading(int fromX, int fromY, int toX, int toY) {
      double angle = Math.atan2(toY - fromY, toX - fromX);
      double degree = Math.toDegrees(angle);
      if (degree < 0) degree += 360;
      
      // 转换为8方向
      if (degree >= 337.5 || degree < 22.5) return 0;   // 东
      if (degree >= 22.5 && degree < 67.5) return 1;    // 东北
      if (degree >= 67.5 && degree < 112.5) return 2;   // 北
      if (degree >= 112.5 && degree < 157.5) return 3;// 西北
      if (degree >= 157.5 && degree < 202.5) return 4;// 西
      if (degree >= 202.5 && degree < 247.5) return 5;// 西南
      if (degree >= 247.5 && degree < 292.5) return 6;// 南
      return 7; // 东南
    }
   
    public List<Monster> getMonsters() { return monsters; }
    public int getMapWidth() { return mapWidth; }
    public int getMapHeight() { return mapHeight; }
}

// 5. 小地图显示类(客户端)
class MiniMapPanel extends JPanel {
    private final List<MonsterInfo> monsterInfos = new CopyOnWriteArrayList<>();
    private final int playerX = 2500; // 玩家固定在小地图中心
    private final int playerY = 2500;
    private final int minimapSize = 400; // 小地图显示大小
    private final int viewRange = 1000; // 小地图显示范围
   
    // 怪物信息类(客户端存储)
    static class MonsterInfo {
      int id;
      int x, y;
      int type; // 1=红点, 2=黄点, 3=BOSS
      String name;
      
      MonsterInfo(int id, int x, int y, int type, String name) {
            this.id = id;
            this.x = x;
            this.y = y;
            this.type = type;
            this.name = name;
      }
    }
   
    public MiniMapPanel() {
      setPreferredSize(new Dimension(minimapSize + 50, minimapSize + 50));
      setBackground(Color.DARK_GRAY);
    }
   
    // 更新怪物位置
    public void updateMonsterPosition(int monsterId, int x, int y) {
      for (MonsterInfo info : monsterInfos) {
            if (info.id == monsterId) {
                info.x = x;
                info.y = y;
                repaint();
                return;
            }
      }
    }
   
    // 添加怪物到小地图
    public void addMonster(int monsterId, int x, int y, int type, String name) {
      monsterInfos.add(new MonsterInfo(monsterId, x, y, type, name));
      repaint();
    }
   
    // 移除怪物(当怪物死亡时)
    public void removeMonster(int monsterId) {
      monsterInfos.removeIf(info -> info.id == monsterId);
      repaint();
    }
   
    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2d = (Graphics2D) g;
      
      // 绘制小地图背景
      g2d.setColor(new Color(50, 50, 50));
      g2d.fillRect(25, 25, minimapSize, minimapSize);
      
      // 绘制小地图边框
      g2d.setColor(Color.WHITE);
      g2d.drawRect(25, 25, minimapSize, minimapSize);
      
      // 绘制玩家位置(中心点)
      g2d.setColor(Color.GREEN);
      int playerMapX = minimapSize / 2 + 25;
      int playerMapY = minimapSize / 2 + 25;
      g2d.fillOval(playerMapX - 5, playerMapY - 5, 10, 10);
      g2d.setColor(Color.WHITE);
      g2d.drawString("玩家", playerMapX - 10, playerMapY - 10);
      
      // 绘制怪物红点
      for (MonsterInfo monster : monsterInfos) {
            if (monster.type == 0) continue; // 不显示的怪物
            
            // 计算怪物在小地图上的相对位置
            int monsterMapX = 25 + (int)((monster.x - (playerX - viewRange / 2)) *
                minimapSize / viewRange);
            int monsterMapY = 25 + (int)((monster.y - (playerY - viewRange / 2)) *
                minimapSize / viewRange);
            
            // 只显示在视野范围内的怪物
            if (monsterMapX >= 25 && monsterMapX <= 25 + minimapSize &&
                monsterMapY >= 25 && monsterMapY <= 25 + minimapSize) {
               
                // 根据类型设置颜色
                switch (monster.type) {
                  case 1: // 红点(普通主动怪)
                        g2d.setColor(Color.RED);
                        break;
                  case 2: // 黄点(被动怪)
                        g2d.setColor(Color.YELLOW);
                        break;
                  case 3: // BOSS(大红色)
                        g2d.setColor(new Color(255, 0, 0, 200));
                        g2d.fillOval(monsterMapX - 8, monsterMapY - 8, 16, 16);
                        g2d.setColor(Color.WHITE);
                        g2d.drawString("BOSS", monsterMapX - 15, monsterMapY - 15);
                        continue;
                }
               
                // 绘制怪物点
                g2d.fillOval(monsterMapX - 4, monsterMapY - 4, 8, 8);
               
                // 绘制怪物名称(简化显示)
                if (monster.type == 1) {
                  g2d.setColor(Color.WHITE);
                  g2d.drawString(monster.name.substring(0, Math.min(3, monster.name.length())),
                        monsterMapX - 5, monsterMapY - 10);
                }
            }
      }
      
      // 绘制图例
      g2d.setColor(Color.WHITE);
      g2d.drawString("小地图图例:", 25, minimapSize + 50);
      g2d.setColor(Color.GREEN);
      g2d.fillOval(80, minimapSize + 45, 8, 8);
      g2d.setColor(Color.WHITE);
      g2d.drawString("玩家", 95, minimapSize + 52);
      
      g2d.setColor(Color.RED);
      g2d.fillOval(140, minimapSize + 45, 8, 8);
      g2d.setColor(Color.WHITE);
      g2d.drawString("主动怪", 155, minimapSize + 52);
      
      g2d.setColor(Color.YELLOW);
      g2d.fillOval(210, minimapSize + 45, 8, 8);
      g2d.setColor(Color.WHITE);
      g2d.drawString("被动怪", 225, minimapSize + 52);
    }
}

// 6. 客户端类 - 接收服务器封包并更新小地图
class Client {
    private final MiniMapPanel minimap;
    private final String playerName;
   
    public Client(String playerName, MiniMapPanel minimap) {
      this.playerName = playerName;
      this.minimap = minimap;
    }
   
    // 接收服务器封包并处理
    public void receivePacket(Object packet) {
      if (packet instanceof S_NPCPack) {
            S_NPCPack npcPack = (S_NPCPack) packet;
            if (npcPack.getMinimapType() > 0) {
                minimap.addMonster(npcPack.getMonsterId(),
                  npcPack.getX(), npcPack.getY(),
                  npcPack.getMinimapType(), "怪物" + npcPack.getMonsterId());
                System.out.println("客户端收到怪物出现: ID=" + npcPack.getMonsterId());
            }
      } else if (packet instanceof S_MoveNpcPacket) {
            S_MoveNpcPacket movePacket = (S_MoveNpcPacket) packet;
            minimap.updateMonsterPosition(movePacket.getMonsterId(),
                movePacket.getNewX(), movePacket.getNewY());
            System.out.println("客户端收到怪物移动: ID=" + movePacket.getMonsterId() +
                           " -> (" + movePacket.getNewX() + "," + movePacket.getNewY() + ")");
      }
    }
}

// 7. 主程序 - 模拟游戏运行
public class MonsterMinimapSystem {
    private static GameServer server;
    private static MiniMapPanel minimap;
    private static JFrame frame;
   
    public static void main(String[] args) {
      // 初始化服务器
      server = new GameServer();
      
      // 创建小地图UI
      minimap = new MiniMapPanel();
      
      // 创建客户端
      Client client = new Client("测试玩家", minimap);
      server.addClient(client);
      
      // 创建并显示UI
      SwingUtilities.invokeLater(() -> {
            frame = new JFrame("怪物小地图红点显示系统");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new BorderLayout());
            
            // 信息面板
            JPanel infoPanel = new JPanel();
            infoPanel.setLayout(new FlowLayout());
            JLabel infoLabel = new JLabel("模拟所有怪物在小地图显示为红点,红点实时跟随怪物移动");
            infoPanel.add(infoLabel);
            
            // 控制面板
            JPanel controlPanel = new JPanel();
            JButton addMonsterBtn = new JButton("添加新怪物");
            JButton startSimulationBtn = new JButton("开始模拟移动");
            controlPanel.add(addMonsterBtn);
            controlPanel.add(startSimulationBtn);
            
            // 按钮事件
            addMonsterBtn.addActionListener(e -> {
                // 随机生成怪物
                Random rand = new Random();
                int x = rand.nextInt(server.getMapWidth());
                int y = rand.nextInt(server.getMapHeight());
                boolean aggressive = rand.nextBoolean();
                boolean boss = rand.nextInt(10) == 0; // 10%几率生成BOSS
                int level = rand.nextInt(50) + 1;
               
                String name = (boss ? "BOSS_" : "") +
                           (aggressive ? "主动怪" : "被动怪") + "_Lv" + level;
               
                Monster monster = new Monster(x, y, name, level, aggressive, boss);
                server.addMonster(monster);
               
                System.out.println("添加新怪物: " + name + " 位置=(" + x + "," + y + ")");
            });
            
            startSimulationBtn.addActionListener(e -> {
                startMonsterMovementSimulation();
            });
            
            // 添加到框架
            frame.add(infoPanel, BorderLayout.NORTH);
            frame.add(minimap, BorderLayout.CENTER);
            frame.add(controlPanel, BorderLayout.SOUTH);
            
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            
            // 初始添加一些怪物
            initializeMonsters();
      });
    }
   
    private static void initializeMonsters() {
      // 添加初始怪物
      server.addMonster(new Monster(2300, 2400, "狼", 5, true, false));
      server.addMonster(new Monster(2600, 2500, "史莱姆", 3, false, false));
      server.addMonster(new Monster(2400, 2600, "哥布林", 8, true, false));
      server.addMonster(new Monster(2700, 2300, "龙", 45, true, true)); // BOSS
      server.addMonster(new Monster(2200, 2700, "鹿", 2, false, false));
      
      System.out.println("初始化完成,添加了5个怪物(包含1个BOSS)");
    }
   
    private static void startMonsterMovementSimulation() {
      Thread simulationThread = new Thread(() -> {
            try {
                System.out.println("开始模拟怪物移动...");
                for (int i = 0; i < 100; i++) { // 模拟100次移动
                  server.updateMonsterPositions();
                  Thread.sleep(1000); // 每秒移动一次
                  
                  // 更新UI
                  SwingUtilities.invokeLater(() -> {
                        minimap.repaint();
                  });
                }
                System.out.println("模拟结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
      });
      simulationThread.setDaemon(true);
      simulationThread.start();
    }
}

这是ai写的

爱吃鱼的大柚 發表於 2025-12-24 17:35


摇一摇一摇

爱吃鱼的大柚 發表於 2025-12-24 18:10


摇一摇一摇
頁: [1]
查看完整版本: 12/24摇一摇