巴列斯
  
- 威望
- 5
- 精華
- 0
- 貢獻
- 0
- 鑽石
- 0
- 閱讀權限
- 60
- 積分
- 15042
- 在線時間
- 104 小時
- 相冊
- 1
- 日誌
- 0
- 好友
- 1
|

樓主 |
發表於 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[0];
- }
-
- 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[0];
- }
-
- 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写的
| |  | |  |
|
|