Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions robots/src/gui/AppStateManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package gui;

import java.beans.PropertyVetoException;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import log.Logger;

public class AppStateManager {
private final WindowConfigManager configManager;

public AppStateManager() {
this.configManager = new WindowConfigManager();
}

public void load() {
configManager.load();
Logger.debug("Менеджер состояний: загрузка конфигурации запущена");
}

public void save() {
configManager.save();
}

public void restoreMain(JFrame mainFrame, int defaultX, int defaultY, int defaultW, int defaultH) {
mainFrame.setBounds(
configManager.getInt("main.x", defaultX),
configManager.getInt("main.y", defaultY),
configManager.getInt("main.w", defaultW),
configManager.getInt("main.h", defaultH)
);
mainFrame.setExtendedState(configManager.getInt("main.state", JFrame.NORMAL));
}

public void restoreInternalFrame(JInternalFrame frame, String prefix, int defaultX, int defaultY, int defaultW, int defaultH) {
int w = configManager.getInt(prefix + ".w", defaultW);
int h = configManager.getInt(prefix + ".h", defaultH);
int x = configManager.getInt(prefix + ".x", defaultX);
int y = configManager.getInt(prefix + ".y", defaultY);

frame.setBounds(x, y, w, h);

try {
if (configManager.getBool(prefix + ".max", false)) {
frame.setMaximum(true);
} else if (configManager.getBool(prefix + ".icon", false)) {
frame.setIcon(true);
}
} catch (PropertyVetoException e) {
Logger.error("Не удалось восстановить состояние окна '" + prefix + "': " + e.getMessage());
}
}

public void saveMain(JFrame mainFrame) {
configManager.saveMain(
mainFrame.getX(), mainFrame.getY(),
mainFrame.getWidth(), mainFrame.getHeight(),
mainFrame.getExtendedState()
);
}

public void saveInternalFrame(JInternalFrame frame, String prefix) {
configManager.saveInternal(prefix,
frame.getX(), frame.getY(),
frame.getWidth(), frame.getHeight(),
frame.isIcon(), frame.isMaximum()
);
}

public void saveAllFrames(JDesktopPane desktopPane) {
for (JInternalFrame frame : desktopPane.getAllFrames()) {
String prefix = frame.getTitle().replaceAll("\\s+", "_").toLowerCase();
saveInternalFrame(frame, prefix);
}
}
}
221 changes: 39 additions & 182 deletions robots/src/gui/GameVisualizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,207 +4,64 @@
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JPanel;

public class GameVisualizer extends JPanel
{
private final Timer m_timer = initTimer();

private static Timer initTimer()
{
Timer timer = new Timer("events generator", true);
return timer;
}

private volatile double m_robotPositionX = 100;
private volatile double m_robotPositionY = 100;
private volatile double m_robotDirection = 0;
public class GameVisualizer extends JPanel implements RobotModel.Observer {
private final RobotModel model;
private final Timer timer = new Timer("events generator", true);

private volatile int m_targetPositionX = 150;
private volatile int m_targetPositionY = 100;

private static final double maxVelocity = 0.1;
private static final double maxAngularVelocity = 0.001;

public GameVisualizer()
{
m_timer.schedule(new TimerTask()
{
@Override
public void run()
{
onRedrawEvent();
}
public GameVisualizer(RobotModel model) {
this.model = model;
model.addObserver(this); // Подписываемся на обновления

timer.schedule(new TimerTask() {
@Override public void run() { EventQueue.invokeLater(GameVisualizer.this::repaint); }
}, 0, 50);
m_timer.schedule(new TimerTask()
{
@Override
public void run()
{
onModelUpdateEvent();
}

timer.schedule(new TimerTask() {
@Override public void run() { model.update(); }
}, 0, 10);
addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
setTargetPosition(e.getPoint());
repaint();

addMouseListener(new MouseAdapter() {
@Override public void mouseClicked(MouseEvent e) {
model.setTarget(e.getX(), e.getY());
}
});
setDoubleBuffered(true);
}

protected void setTargetPosition(Point p)
{
m_targetPositionX = p.x;
m_targetPositionY = p.y;
}

protected void onRedrawEvent()
{
EventQueue.invokeLater(this::repaint);
}

private static double distance(double x1, double y1, double x2, double y2)
{
double diffX = x1 - x2;
double diffY = y1 - y2;
return Math.sqrt(diffX * diffX + diffY * diffY);
}

private static double angleTo(double fromX, double fromY, double toX, double toY)
{
double diffX = toX - fromX;
double diffY = toY - fromY;

return asNormalizedRadians(Math.atan2(diffY, diffX));
}

protected void onModelUpdateEvent()
{
double distance = distance(m_targetPositionX, m_targetPositionY,
m_robotPositionX, m_robotPositionY);
if (distance < 0.5)
{
return;
}
double velocity = maxVelocity;
double angleToTarget = angleTo(m_robotPositionX, m_robotPositionY, m_targetPositionX, m_targetPositionY);
double angularVelocity = 0;
if (angleToTarget > m_robotDirection)
{
angularVelocity = maxAngularVelocity;
}
if (angleToTarget < m_robotDirection)
{
angularVelocity = -maxAngularVelocity;
}

moveRobot(velocity, angularVelocity, 10);
}

private static double applyLimits(double value, double min, double max)
{
if (value < min)
return min;
if (value > max)
return max;
return value;
}

private void moveRobot(double velocity, double angularVelocity, double duration)
{
velocity = applyLimits(velocity, 0, maxVelocity);
angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
double newX = m_robotPositionX + velocity / angularVelocity *
(Math.sin(m_robotDirection + angularVelocity * duration) -
Math.sin(m_robotDirection));
if (!Double.isFinite(newX))
{
newX = m_robotPositionX + velocity * duration * Math.cos(m_robotDirection);
}
double newY = m_robotPositionY - velocity / angularVelocity *
(Math.cos(m_robotDirection + angularVelocity * duration) -
Math.cos(m_robotDirection));
if (!Double.isFinite(newY))
{
newY = m_robotPositionY + velocity * duration * Math.sin(m_robotDirection);
}
m_robotPositionX = newX;
m_robotPositionY = newY;
double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration);
m_robotDirection = newDirection;
}
private static int round(double v) { return (int)(v + 0.5); }

private static double asNormalizedRadians(double angle)
{
while (angle < 0)
{
angle += 2*Math.PI;
}
while (angle >= 2*Math.PI)
{
angle -= 2*Math.PI;
}
return angle;
}

private static int round(double value)
{
return (int)(value + 0.5);
}

@Override
public void paint(Graphics g)
{
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
drawRobot(g2d, round(m_robotPositionX), round(m_robotPositionY), m_robotDirection);
drawTarget(g2d, m_targetPositionX, m_targetPositionY);
Graphics2D g2d = (Graphics2D) g;
drawRobot(g2d, round(model.getX()), round(model.getY()), model.getDir());
drawTarget(g2d, model.getTargetX(), model.getTargetY());
}

private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2)
{
g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);
}

private static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2)
{
g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2);

private void drawRobot(Graphics2D g, int x, int y, double dir) {
g.setTransform(AffineTransform.getRotateInstance(dir, x, y));
g.setColor(Color.MAGENTA); g.fillOval(x-15, y-5, 30, 10);
g.setColor(Color.BLACK); g.drawOval(x-15, y-5, 30, 10);
g.setColor(Color.WHITE); g.fillOval(x+5, y-2, 5, 5);
g.setColor(Color.BLACK); g.drawOval(x+5, y-2, 5, 5);
}

private void drawRobot(Graphics2D g, int x, int y, double direction)
{
int robotCenterX = round(m_robotPositionX);
int robotCenterY = round(m_robotPositionY);
AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY);
g.setTransform(t);
g.setColor(Color.MAGENTA);
fillOval(g, robotCenterX, robotCenterY, 30, 10);
g.setColor(Color.BLACK);
drawOval(g, robotCenterX, robotCenterY, 30, 10);
g.setColor(Color.WHITE);
fillOval(g, robotCenterX + 10, robotCenterY, 5, 5);
g.setColor(Color.BLACK);
drawOval(g, robotCenterX + 10, robotCenterY, 5, 5);

private void drawTarget(Graphics2D g, int x, int y) {
g.setTransform(new AffineTransform());
g.setColor(Color.GREEN); g.fillOval(x-2, y-2, 5, 5);
g.setColor(Color.BLACK); g.drawOval(x-2, y-2, 5, 5);
}

private void drawTarget(Graphics2D g, int x, int y)
{
AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0);
g.setTransform(t);
g.setColor(Color.GREEN);
fillOval(g, x, y, 5, 5);
g.setColor(Color.BLACK);
drawOval(g, x, y, 5, 5);

@Override
public void onStateChanged(double x, double y, double dir, int tx, int ty) {
// Перерисовка при изменении модели
repaint();
}
}
}
23 changes: 14 additions & 9 deletions robots/src/gui/GameWindow.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
package gui;

import java.awt.BorderLayout;

import javax.swing.JInternalFrame;
import javax.swing.JPanel;

public class GameWindow extends JInternalFrame
{
private final GameVisualizer m_visualizer;
public GameWindow()
{
public class GameWindow extends JInternalFrame {
private final RobotModel model;
private final GameVisualizer visualizer;
private final RobotInfoWindow infoWindow;

public GameWindow() {
super("Игровое поле", true, true, true, true);
m_visualizer = new GameVisualizer();
model = new RobotModel();
visualizer = new GameVisualizer(model);
infoWindow = new RobotInfoWindow(model);

JPanel panel = new JPanel(new BorderLayout());
panel.add(m_visualizer, BorderLayout.CENTER);
panel.add(visualizer, BorderLayout.CENTER);
getContentPane().add(panel);
pack();
}
}

public RobotInfoWindow getInfoWindow() { return infoWindow; }
}
Loading