12/24摇一摇
摇一摇一摇#在這裡快 摇一摇一摇速回復#
摇一摇一摇 摇一摇一摇 摇一摇一摇 摇一摇一摇 摇一摇一摇 摇一摇一摇 摇一摇一摇 关键技术要点说明:
怪物分类显示系统:
主动攻击怪物显示为红点
被动怪物显示为黄点
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写的
摇一摇一摇
摇一摇一摇
頁:
[1]