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
95 changes: 95 additions & 0 deletions robots/src/game/GameVisualizer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package game;

import java.awt.Color;
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.awt.geom.Point2D;
import java.util.TimerTask;

import javax.swing.JPanel;

public class GameVisualizer extends JPanel
{
private final RobotLogic logic;

public GameVisualizer(RobotLogic logic)
{
this.logic = logic;

logic.addActionToTimer(new TimerTask()
{
@Override
public void run()
{
repaint();
}
}, 50);

addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
Point clickPoint = e.getPoint();

logic.setTarget(new Target(clickPoint.getX(), clickPoint.getY()));
logic.setWindowBounds(new Point2D.Double(getWidth(), getHeight()));
repaint();
}
});

setDoubleBuffered(true);
}

@Override
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
drawRobot(g2d, logic.getRobot());
drawTarget(g2d, logic.getTarget());
}

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, Robot robot)
{
int robotCenterX = (int) Math.round(robot.getPosition().getX());
int robotCenterY = (int) Math.round(robot.getPosition().getY());
AffineTransform t = AffineTransform.getRotateInstance(robot.getDirection(), 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, Target target)
{
AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0);
g.setTransform(t);

g.setColor(Color.GREEN);
fillOval(g, (int) target.getPosition().getX(), (int) target.getPosition().getY(), 5, 5);
g.setColor(Color.BLACK);
drawOval(g, (int) target.getPosition().getX(), (int) target.getPosition().getY(), 5, 5);
}
}
39 changes: 39 additions & 0 deletions robots/src/game/GameWindow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package game;

import gui.AbstractWindowState;

import java.awt.BorderLayout;

import javax.swing.JPanel;

public class GameWindow extends AbstractWindowState
{
private final GameVisualizer m_visualizer;
private final RobotLogic logic;
public GameWindow(String title, RobotLogic logic)
{
super();

this.logic = logic;
logic.startTimer();

setTitle(title);
setResizable(true);
setClosable(true);
setMaximizable(true);
setIconifiable(true);

m_visualizer = new GameVisualizer(logic);

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

@Override
public void dispose() {
super.dispose();
logic.stopTimer();
}
}
38 changes: 38 additions & 0 deletions robots/src/game/Robot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package game;

import java.awt.geom.Point2D;

public class Robot{
private final Point2D.Double position = new Point2D.Double(100, 100);
private volatile double direction = 0;
private double angularVelocity = 0;
private final double velocity = 0.1;

public double getVelocity() {
return velocity;
}

public Point2D.Double getPosition() {
return position;
}

public double getDirection() {
return direction;
}

public void setDirection(double direction) {
this.direction = direction;
}

public void setAngularVelocity(double angularVelocity) {
this.angularVelocity = angularVelocity;
}

public double getAngularVelocity() {
return angularVelocity;
}

public void move(Point2D.Double dv) {
position.setLocation(getPosition().getX() + dv.getX(), getPosition().getY() + dv.getY());
}
}
34 changes: 34 additions & 0 deletions robots/src/game/RobotInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package game;

import gui.AbstractWindowState;

import java.awt.*;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JLabel;

public class RobotInfo extends AbstractWindowState implements Observer {
private final JLabel label;


public RobotInfo(RobotLogic logic) {
super();
this.label = new JLabel();

logic.addObserver(this);
setResizable(true);
setClosable(true);
setMaximizable(true);
setIconifiable(true);

getContentPane().add(label, BorderLayout.CENTER);
pack();
}

@Override
public void update(Observable o, Object arg) {
RobotLogic lg = (RobotLogic) o;
label.setText("x=%f y=%f dir=%f".formatted(lg.getRobot().getPosition().getX(),
lg.getRobot().getPosition().getY(), lg.getRobot().getDirection()));
}
}
125 changes: 125 additions & 0 deletions robots/src/game/RobotLogic.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package game;

import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
import java.util.Observable;
import java.util.Timer;
import java.util.TimerTask;

public class RobotLogic extends Observable {
private static final double ANGULAR_VELOCITY = 0.001;
private static final double TARGET_CLOSE_ENOUGH = 5;
private final static double EPSILON = 0.05;

private final Robot robot;
private Target target;

private final long dt = 5;
private Timer timer;
private Point2D.Double windowBounds = new Point2D.Double(300, 300);

public RobotLogic() {
robot = new Robot();
target = new Target(10, 10);
setTarget(target);
moveRobot();
}

public void startTimer() {
timer = new Timer("event generator", true);
addActionToTimer(new TimerTask() {
@Override
public void run() {
moveRobot();

setChanged();
notifyObservers();
}
}, dt);
}

public void addActionToTimer(TimerTask task, long timeout) {
timer.schedule(task, 0, timeout);
}

public void stopTimer() {
timer.cancel();
}

public Robot getRobot() {
return robot;
}

public Target getTarget() {
return target;
}

public void setTarget(Target target) {
this.target = target;

if (RobotsMath.angleTo(robot.getPosition(), target.getPosition()) > robot.getDirection()) {
robot.setAngularVelocity(-ANGULAR_VELOCITY);
} else {
robot.setAngularVelocity(ANGULAR_VELOCITY);
}
}

public void setWindowBounds(Point2D.Double windowBounds) {
this.windowBounds = windowBounds;
}

public void moveRobot() {
if (robot.getPosition().distance(target.getPosition()) < TARGET_CLOSE_ENOUGH) {
return;
}

final double angleRobotTarget = RobotsMath.angleTo(robot.getPosition(), target.getPosition());

if (Math.abs(robot.getAngularVelocity()) < ANGULAR_VELOCITY ||
Math.abs(robot.getDirection() - angleRobotTarget) < EPSILON) {
robot.move(new Double(
robot.getVelocity() * Math.cos(robot.getDirection()) * dt,
robot.getVelocity() * Math.sin(robot.getDirection()) * dt
));

return;
}

final double newAngle = RobotsMath.asNormalizedRadians(
robot.getDirection() + robot.getAngularVelocity() * dt);

final double dx = robot.getVelocity() / robot.getAngularVelocity() *
(Math.sin(newAngle) - Math.sin(robot.getDirection()));
final double dy = robot.getVelocity() / robot.getAngularVelocity() *
(Math.cos(newAngle) - Math.cos(robot.getDirection()));

robot.move(new Point2D.Double(
dx * RobotsMath.speedCoefficient(robot.getPosition().getX(), windowBounds.getX()),
- dy * RobotsMath.speedCoefficient(robot.getPosition().getY(), windowBounds.getY())
));
robot.setDirection(newAngle);
}

private static class RobotsMath {
public static double angleTo(Point2D.Double p0, Point2D.Double p1) {
final double dx = p1.getX() - p0.getX();
final double dy = p1.getY() - p0.getY();

return asNormalizedRadians(Math.atan2(dy, dx));
}

private static double asNormalizedRadians(double angle) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

это просто Math

final double TAU = 2 * Math.PI;

if (angle < 0) {
return TAU - ((-angle) % TAU);
}

return angle % TAU;
}

private static double speedCoefficient(double t, double upperBoundT) {
return Math.max(1 - 2 * Math.abs((upperBoundT - t) / upperBoundT - 0.5), 0.01);
}
}
}
16 changes: 16 additions & 0 deletions robots/src/game/Target.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package game;

import java.awt.geom.Point2D;

public class Target {
private final Point2D.Double position = new Point2D.Double();

public Target(double x, double y) {
double rate = 3/2f;
position.setLocation(x * rate, y * rate);
}

public Point2D.Double getPosition() {
return position;
}
}
4 changes: 4 additions & 0 deletions robots/src/gui/AbstractWindowState.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ public AbstractWindowState(String string, boolean b1, boolean b2, boolean b3, bo
super(string, b1, b2, b3, b4);
}

public AbstractWindowState() {
super();
}

private static Preferences getPreferences() {
return Preferences.userRoot().node(prefixWindowPreferences);
}
Expand Down
Loading