mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 14:28:43 +00:00
Changed the lobby map view to the 2D race rather than the 3D one.
Stripped back game view class appropriately. #story[1245]
This commit is contained in:
@@ -1,38 +1,22 @@
|
|||||||
package seng302.visualiser;
|
package seng302.visualiser;
|
||||||
|
|
||||||
import javafx.animation.AnimationTimer;
|
|
||||||
import javafx.animation.KeyFrame;
|
|
||||||
import javafx.animation.KeyValue;
|
|
||||||
import javafx.animation.Timeline;
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.*;
|
import javafx.scene.*;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.paint.Paint;
|
import javafx.scene.paint.Paint;
|
||||||
import javafx.scene.shape.Circle;
|
|
||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.util.Duration;
|
|
||||||
import seng302.gameServer.messages.RoundingSide;
|
import seng302.gameServer.messages.RoundingSide;
|
||||||
import seng302.model.ClientYacht;
|
|
||||||
import seng302.model.GeoPoint;
|
import seng302.model.GeoPoint;
|
||||||
import seng302.model.Limit;
|
import seng302.model.Limit;
|
||||||
import seng302.model.mark.CompoundMark;
|
import seng302.model.mark.CompoundMark;
|
||||||
import seng302.model.mark.Corner;
|
import seng302.model.mark.Corner;
|
||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.model.token.Token;
|
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
import seng302.utilities.Sounds;
|
|
||||||
import seng302.visualiser.fxObjects.assets_2D.*;
|
import seng302.visualiser.fxObjects.assets_2D.*;
|
||||||
import seng302.visualiser.fxObjects.assets_3D.Marker3D;
|
|
||||||
import seng302.visualiser.fxObjects.assets_3D.ModelFactory;
|
|
||||||
import seng302.visualiser.fxObjects.assets_3D.ModelType;
|
|
||||||
import seng302.visualiser.map.Boundary;
|
|
||||||
import seng302.visualiser.map.CanvasMap;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -43,8 +27,6 @@ public class GameView extends Pane {
|
|||||||
|
|
||||||
private double bufferSize = 50;
|
private double bufferSize = 50;
|
||||||
private double horizontalBuffer = 0;
|
private double horizontalBuffer = 0;
|
||||||
private double panelWidth = 1280;
|
|
||||||
private double panelHeight = 960;
|
|
||||||
|
|
||||||
private double canvasWidth = 1100;
|
private double canvasWidth = 1100;
|
||||||
private double canvasHeight = 920;
|
private double canvasHeight = 920;
|
||||||
@@ -54,233 +36,31 @@ public class GameView extends Pane {
|
|||||||
private ScaleDirection scaleDirection;
|
private ScaleDirection scaleDirection;
|
||||||
private GeoPoint minLatPoint, minLonPoint, maxLatPoint, maxLonPoint;
|
private GeoPoint minLatPoint, minLonPoint, maxLatPoint, maxLonPoint;
|
||||||
private double referencePointX, referencePointY;
|
private double referencePointX, referencePointY;
|
||||||
private double metersPerPixelX, metersPerPixelY;
|
|
||||||
|
|
||||||
private boolean isZoom = false;
|
|
||||||
|
|
||||||
private Text fpsDisplay = new Text();
|
|
||||||
private Polygon raceBorder = new CourseBoundary();
|
private Polygon raceBorder = new CourseBoundary();
|
||||||
|
|
||||||
/* Note that if either of these is null then values for it have not been added and the other
|
/* Note that if either of these is null then values for it have not been added and the other
|
||||||
should be used as the limits of the map. */
|
should be used as the limits of the map. */
|
||||||
private List<Limit> borderPoints;
|
private List<Limit> borderPoints;
|
||||||
private Map<Mark, Marker3D> markerObjects;
|
private Map<Mark, Marker2D> markerObjects;
|
||||||
|
|
||||||
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
|
|
||||||
private Map<ClientYacht, AnnotationBox> annotations = new HashMap<>();
|
|
||||||
private ObservableList<Node> gameObjects;
|
private ObservableList<Node> gameObjects;
|
||||||
private BoatObject selectedBoat = null;
|
|
||||||
private Group annotationsGroup = new Group();
|
|
||||||
private Group wakesGroup = new Group();
|
|
||||||
private Group boatObjectGroup = new Group();
|
|
||||||
private Group trails = new Group();
|
|
||||||
private Group markers = new Group();
|
private Group markers = new Group();
|
||||||
private Group tokens = new Group();
|
private Group tokens = new Group();
|
||||||
private List<CompoundMark> course = new ArrayList<>();
|
private List<CompoundMark> course = new ArrayList<>();
|
||||||
private List<Node> mapTokens;
|
|
||||||
|
|
||||||
private ImageView mapImage = new ImageView();
|
private ImageView mapImage = new ImageView();
|
||||||
private Camera camera;
|
|
||||||
|
|
||||||
//FRAME RATE
|
|
||||||
|
|
||||||
private AnimationTimer timer;
|
|
||||||
private int NUM_SAMPLES = 10;
|
|
||||||
private final long[] frameTimes = new long[NUM_SAMPLES];
|
|
||||||
private Double frameRate = 60.0;
|
|
||||||
private int frameTimeIndex = 0;
|
|
||||||
private boolean arrayFilled = false;
|
|
||||||
private ClientYacht playerYacht;
|
|
||||||
private double windDir = 0.0;
|
|
||||||
|
|
||||||
double scaleFactor = 1;
|
|
||||||
|
|
||||||
public void setRes(Integer x, Integer y){
|
|
||||||
this.panelHeight = y;
|
|
||||||
this.panelWidth = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void zoomOut() {
|
|
||||||
scaleFactor = 0.1;
|
|
||||||
if (this.getScaleX() > 0.5) {
|
|
||||||
this.setScaleX(this.getScaleX() - scaleFactor);
|
|
||||||
this.setScaleY(this.getScaleY() - scaleFactor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void zoomIn() {
|
|
||||||
scaleFactor = 0.10;
|
|
||||||
if (this.getScaleX() < 2.5) {
|
|
||||||
this.setScaleX(this.getScaleX() + scaleFactor);
|
|
||||||
this.setScaleY(this.getScaleY() + scaleFactor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum ScaleDirection {
|
private enum ScaleDirection {
|
||||||
HORIZONTAL,
|
HORIZONTAL,
|
||||||
VERTICAL
|
VERTICAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void trackBoat() {
|
|
||||||
if (selectedBoat != null) {
|
|
||||||
double x = selectedBoat.getBoatLayoutX();
|
|
||||||
double y = selectedBoat.getBoatLayoutY();
|
|
||||||
double displacementX = this.getWidth();
|
|
||||||
double displacementY = this.getHeight();
|
|
||||||
this.setLayoutX((-x + (displacementX / 2.0)) * this.getScaleX());
|
|
||||||
this.setLayoutY((-y + (displacementY / 2.0)) * this.getScaleY());
|
|
||||||
} else {
|
|
||||||
this.setLayoutX(0);
|
|
||||||
this.setLayoutY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameView () {
|
public GameView () {
|
||||||
gameObjects = this.getChildren();
|
gameObjects = this.getChildren();
|
||||||
// AmbientLight ambientLight = new AmbientLight(new Color(1,1,1,0.4));
|
gameObjects.addAll(mapImage, raceBorder, markers, tokens);
|
||||||
// ambientLight.setOpacity(0.5);
|
|
||||||
// gameObjects.add(ambientLight);
|
|
||||||
// create image view for map, bind panel size to image
|
|
||||||
camera = new ParallelCamera();
|
|
||||||
camera.setTranslateZ(-500);
|
|
||||||
camera.setFarClip(Double.MAX_VALUE);
|
|
||||||
camera.setNearClip(0.1);
|
|
||||||
PointLight pl = new PointLight();
|
|
||||||
pl.setLightOn(true);
|
|
||||||
pl.layoutYProperty().bind(camera.layoutYProperty());
|
|
||||||
pl.layoutXProperty().bind(camera.layoutXProperty());
|
|
||||||
// gameObjects.add(camera);
|
|
||||||
this.sceneProperty().addListener((obs, oldValue, scene) -> {
|
|
||||||
if (scene != null) {
|
|
||||||
scene.setCamera(camera);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
initializeTimer();
|
|
||||||
gameObjects.addAll(mapImage, raceBorder, markers, tokens, pl);
|
|
||||||
// TODO: 11/09/17 ajm412: do you even zoom bro?
|
|
||||||
// this.sceneProperty().addListener(((observable, oldValue, scene) -> {
|
|
||||||
// if (scene != null) {
|
|
||||||
// setupZoom();
|
|
||||||
// } else {
|
|
||||||
// disableZoom();
|
|
||||||
// }
|
|
||||||
// }));
|
|
||||||
//
|
|
||||||
// this.widthProperty().addListener(new ChangeListener<Number>() {
|
|
||||||
// @Override
|
|
||||||
// public void changed(ObservableValue<? extends Number> observable, Number oldValue,
|
|
||||||
// Number newValue) {
|
|
||||||
// scaleFactor = getWidth() / panelWidth;
|
|
||||||
//
|
|
||||||
// if (panelHeight * scaleFactor < getHeight()) {
|
|
||||||
// Scale scale = new Scale(scaleFactor, scaleFactor, 0, 0);
|
|
||||||
// getTransforms().remove(0, getTransforms().size());
|
|
||||||
// getTransforms().add(scale);
|
|
||||||
//
|
|
||||||
// setPrefWidth(getWidth() / scaleFactor);
|
|
||||||
// setPrefHeight(getHeight() / scaleFactor);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// this.heightProperty().addListener(new ChangeListener<Number>() {
|
|
||||||
// @Override
|
|
||||||
// public void changed(ObservableValue<? extends Number> observable, Number oldValue,
|
|
||||||
// Number newValue) {
|
|
||||||
// scaleFactor = getHeight() / panelHeight;
|
|
||||||
//
|
|
||||||
// if (panelWidth * scaleFactor < getWidth()) {
|
|
||||||
// Scale scale = new Scale(scaleFactor, scaleFactor, 0, 0);
|
|
||||||
// getTransforms().remove(0, getTransforms().size());
|
|
||||||
// getTransforms().add(scale);
|
|
||||||
//
|
|
||||||
// setPrefWidth(getWidth() / scaleFactor);
|
|
||||||
// setPrefHeight(getHeight() / scaleFactor);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeTimer() {
|
|
||||||
Arrays.fill(frameTimes, 1_000_000_000 / 60);
|
|
||||||
timer = new AnimationTimer() {
|
|
||||||
private long lastTime = 0;
|
|
||||||
private int FPSCount = 30;
|
|
||||||
private Double frameRate = 60.0;
|
|
||||||
private int index = 0;
|
|
||||||
private boolean arrayFilled = false;
|
|
||||||
private long sum = 1_000_000_000 / 3;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(long now) {
|
|
||||||
trackBoat();
|
|
||||||
if (lastTime == 0) {
|
|
||||||
lastTime = now;
|
|
||||||
} else {
|
|
||||||
if (now - lastTime >= (1e8 / 60)) { //Fix for framerate going above 60 when minimized
|
|
||||||
long oldFrameTime = frameTimes[frameTimeIndex];
|
|
||||||
frameTimes[frameTimeIndex] = now;
|
|
||||||
frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length;
|
|
||||||
if (frameTimeIndex == 0) {
|
|
||||||
arrayFilled = true;
|
|
||||||
}
|
|
||||||
long elapsedNanos;
|
|
||||||
if (arrayFilled) {
|
|
||||||
elapsedNanos = now - oldFrameTime;
|
|
||||||
long elapsedNanosPerFrame = elapsedNanos / frameTimes.length;
|
|
||||||
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
|
|
||||||
if (FPSCount-- == 0) {
|
|
||||||
FPSCount = 30;
|
|
||||||
drawFps(frameRate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastTime = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* First find the top right and bottom left points' geo locations, then retrieve map from google
|
|
||||||
* to display on image view. - Haoming 22/5/2017
|
|
||||||
*/
|
|
||||||
private void drawGoogleMap() {
|
|
||||||
findMetersPerPixel();
|
|
||||||
Point2D topLeftPoint = findScaledXY(maxLatPoint.getLat(), minLonPoint.getLng());
|
|
||||||
// distance from top left extreme to panel origin (top left corner)
|
|
||||||
double distanceFromTopLeftToOrigin = Math.sqrt(
|
|
||||||
Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math
|
|
||||||
.pow(topLeftPoint.getY() * metersPerPixelY, 2));
|
|
||||||
// angle from top left extreme to panel origin
|
|
||||||
double bearingFromTopLeftToOrigin = Math
|
|
||||||
.toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY()));
|
|
||||||
// the top left extreme
|
|
||||||
GeoPoint topLeftPos = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng());
|
|
||||||
GeoPoint originPos = GeoUtility
|
|
||||||
.getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin);
|
|
||||||
|
|
||||||
// distance from origin corner to bottom right corner of the panel
|
|
||||||
double distanceFromOriginToBottomRight = Math.sqrt(
|
|
||||||
Math.pow(panelHeight * metersPerPixelY, 2) + Math
|
|
||||||
.pow(panelWidth * metersPerPixelX, 2));
|
|
||||||
double bearingFromOriginToBottomRight = Math
|
|
||||||
.toDegrees(Math.atan2(panelWidth, -panelHeight));
|
|
||||||
GeoPoint bottomRightPos = GeoUtility
|
|
||||||
.getGeoCoordinate(originPos, bearingFromOriginToBottomRight,
|
|
||||||
distanceFromOriginToBottomRight);
|
|
||||||
|
|
||||||
Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(),
|
|
||||||
bottomRightPos.getLat(), originPos.getLng());
|
|
||||||
CanvasMap canvasMap = new CanvasMap(boundary);
|
|
||||||
mapImage.setImage(canvasMap.getMapImage());
|
|
||||||
mapImage.fitWidthProperty().bind(((AnchorPane) this.getParent()).heightProperty());
|
|
||||||
mapImage.fitHeightProperty().bind(((AnchorPane) this.getParent()).heightProperty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 16/08/17 Break up this function
|
|
||||||
/**
|
/**
|
||||||
* Adds a course to the GameView. The view is scaled accordingly unless a border is set in which
|
* Adds a course to the GameView. The view is scaled accordingly unless a border is set in which
|
||||||
* case the course is added relative ot the border.
|
* case the course is added relative ot the border.
|
||||||
@@ -343,10 +123,10 @@ public class GameView extends Pane {
|
|||||||
rescaleRace(new ArrayList<>(markerObjects.keySet()));
|
rescaleRace(new ArrayList<>(markerObjects.keySet()));
|
||||||
}
|
}
|
||||||
//Move the Markers to initial position.
|
//Move the Markers to initial position.
|
||||||
markerObjects.forEach(((mark, marker3D) -> {
|
markerObjects.forEach(((mark, marker2D) -> {
|
||||||
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
|
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
|
||||||
marker3D.setLayoutX(p2d.getX());
|
marker2D.setLayoutX(p2d.getX());
|
||||||
marker3D.setLayoutY(p2d.getY());
|
marker2D.setLayoutY(p2d.getY());
|
||||||
}));
|
}));
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
markers.getChildren().clear();
|
markers.getChildren().clear();
|
||||||
@@ -436,9 +216,9 @@ public class GameView extends Pane {
|
|||||||
* @param colour The desired colour of the mark
|
* @param colour The desired colour of the mark
|
||||||
*/
|
*/
|
||||||
private void makeAndBindMarker(Mark observableMark, Paint colour) {
|
private void makeAndBindMarker(Mark observableMark, Paint colour) {
|
||||||
Marker3D marker3D = new Marker3D(colour);
|
Marker2D marker2D = new Marker2D(colour);
|
||||||
// marker.addArrows(MarkArrowFactory.RoundingSide.PORT, ThreadLocalRandom.current().nextDouble(91, 180), ThreadLocalRandom.current().nextDouble(1, 90));
|
// marker.addArrows(MarkArrowFactory.RoundingSide.PORT, ThreadLocalRandom.current().nextDouble(91, 180), ThreadLocalRandom.current().nextDouble(1, 90));
|
||||||
markerObjects.put(observableMark, marker3D);
|
markerObjects.put(observableMark, marker2D);
|
||||||
observableMark.addPositionListener((mark, lat, lon) -> {
|
observableMark.addPositionListener((mark, lat, lon) -> {
|
||||||
Point2D p2d = findScaledXY(lat, lon);
|
Point2D p2d = findScaledXY(lat, lon);
|
||||||
markerObjects.get(mark).setLayoutX(p2d.getX());
|
markerObjects.get(mark).setLayoutX(p2d.getX());
|
||||||
@@ -454,7 +234,7 @@ public class GameView extends Pane {
|
|||||||
* @param colour The desired colour of the gate.
|
* @param colour The desired colour of the gate.
|
||||||
* @return the new gate.
|
* @return the new gate.
|
||||||
*/
|
*/
|
||||||
private Gate makeAndBindGate(Marker3D m1, Marker3D m2, Paint colour) {
|
private Gate makeAndBindGate(Marker2D m1, Marker2D m2, Paint colour) {
|
||||||
Gate gate = new Gate(colour);
|
Gate gate = new Gate(colour);
|
||||||
gate.startXProperty().bind(
|
gate.startXProperty().bind(
|
||||||
m1.layoutXProperty()
|
m1.layoutXProperty()
|
||||||
@@ -494,62 +274,6 @@ public class GameView extends Pane {
|
|||||||
raceBorder.getPoints().setAll(boundaryPoints);
|
raceBorder.getPoints().setAll(boundaryPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Rescales the race to the size of the window.
|
|
||||||
// *
|
|
||||||
// * @param limitingCoordinates the set of geo points that contains the extremities of the race.
|
|
||||||
// */
|
|
||||||
// private void rescaleRace(List<GeoPoint> limitingCoordinates) {
|
|
||||||
// //Check is called once to avoid unnecessarily change the course limits once the race is running
|
|
||||||
// findMinMaxPoint(limitingCoordinates);
|
|
||||||
// double minLonToMaxLon = scaleRaceExtremities();
|
|
||||||
// calculateReferencePointLocation(minLonToMaxLon);
|
|
||||||
//// drawGoogleMap();
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces all tokens in the course with those passed in
|
|
||||||
*
|
|
||||||
* @param newTokens the tokens to be put on the course.
|
|
||||||
*/
|
|
||||||
public void updateTokens(List<Token> newTokens) {
|
|
||||||
mapTokens = new ArrayList<>();
|
|
||||||
for (Token token : newTokens) {
|
|
||||||
Point2D location = findScaledXY(token.getLat(), token.getLng());
|
|
||||||
Node tokenObject = ModelFactory.importModel(ModelType.VELOCITY_PICKUP).getAssets();
|
|
||||||
tokenObject.setLayoutX(location.getX());
|
|
||||||
tokenObject.setLayoutY(location.getY());
|
|
||||||
mapTokens.add(tokenObject);
|
|
||||||
}
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
tokens.getChildren().clear();
|
|
||||||
tokens.getChildren().addAll(mapTokens);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// // TODO: 16/08/17 initialize zooming internal to GameView only
|
|
||||||
// /**
|
|
||||||
// * Enables zoom. Has to be called after this is added to a scene.
|
|
||||||
// */
|
|
||||||
// private void setupZoom() {
|
|
||||||
// this.getScene().addEventHandler(KeyEvent.KEY_PRESSED, (event) -> {
|
|
||||||
// if (event.getCode() == KeyCode.Z) {
|
|
||||||
// zoomIn();
|
|
||||||
// } else if (event.getCode() == KeyCode.X) {
|
|
||||||
// zoomOut();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// enableZoom();
|
|
||||||
// }
|
|
||||||
////
|
|
||||||
// public void enableZoom() {
|
|
||||||
// isZoom = true;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public void disableZoom() {
|
|
||||||
// isZoom = false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rescales the race to the size of the window.
|
* Rescales the race to the size of the window.
|
||||||
*
|
*
|
||||||
@@ -560,97 +284,6 @@ public class GameView extends Pane {
|
|||||||
findMinMaxPoint(limitingCoordinates);
|
findMinMaxPoint(limitingCoordinates);
|
||||||
double minLonToMaxLon = scaleRaceExtremities();
|
double minLonToMaxLon = scaleRaceExtremities();
|
||||||
calculateReferencePointLocation(minLonToMaxLon);
|
calculateReferencePointLocation(minLonToMaxLon);
|
||||||
// drawGoogleMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSelectedBoat(BoatObject bo, Boolean isSelected) {
|
|
||||||
if (this.selectedBoat == bo && !isSelected) {
|
|
||||||
this.selectedBoat = null;
|
|
||||||
boatObjects.forEach((boat, group) ->
|
|
||||||
group.setIsSelected(false)
|
|
||||||
);
|
|
||||||
} else if (isSelected) {
|
|
||||||
this.selectedBoat = bo;
|
|
||||||
for (BoatObject group : boatObjects.values()) {
|
|
||||||
if (group != bo) {
|
|
||||||
group.setIsSelected(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws all the boats.
|
|
||||||
* @param yachts The yachts to set in the race
|
|
||||||
*/
|
|
||||||
public void setBoats(List<ClientYacht> yachts) {
|
|
||||||
BoatObject newBoat;
|
|
||||||
final List<Group> wakes = new ArrayList<>();
|
|
||||||
for (ClientYacht clientYacht : yachts) {
|
|
||||||
Color colour = clientYacht.getColour();
|
|
||||||
newBoat = new BoatObject();
|
|
||||||
newBoat.addSelectedBoatListener(this::setSelectedBoat);
|
|
||||||
newBoat.setFill(colour);
|
|
||||||
boatObjects.put(clientYacht, newBoat);
|
|
||||||
createAndBindAnnotationBox(clientYacht, colour);
|
|
||||||
// wakesGroup.getChildren().add(newBoat.getWake());
|
|
||||||
wakes.add(newBoat.getWake());
|
|
||||||
boatObjectGroup.getChildren().add(newBoat);
|
|
||||||
trails.getChildren().add(newBoat.getTrail());
|
|
||||||
|
|
||||||
clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
|
|
||||||
BoatObject bo = boatObjects.get(boat);
|
|
||||||
Point2D p2d = findScaledXY(lat, lon);
|
|
||||||
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
|
|
||||||
annotations.get(boat).setLocation(p2d.getX(), p2d.getY());
|
|
||||||
bo.setTrajectory(
|
|
||||||
heading,
|
|
||||||
velocity,
|
|
||||||
metersPerPixelX,
|
|
||||||
metersPerPixelY);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
annotationsGroup.getChildren().addAll(annotations.values());
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
gameObjects.addAll(trails);
|
|
||||||
gameObjects.addAll(wakes);
|
|
||||||
gameObjects.addAll(annotationsGroup);
|
|
||||||
gameObjects.addAll(boatObjectGroup);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createAndBindAnnotationBox(ClientYacht clientYacht, Paint colour) {
|
|
||||||
AnnotationBox newAnnotation = new AnnotationBox();
|
|
||||||
newAnnotation.setFill(colour);
|
|
||||||
newAnnotation.addAnnotation(
|
|
||||||
"name", "Player: " + clientYacht.getShortName()
|
|
||||||
);
|
|
||||||
// newAnnotation.addAnnotation(
|
|
||||||
// "velocity",
|
|
||||||
// yacht.getVelocityProperty(),
|
|
||||||
// (velocity) -> String.format("Speed: %.2f ms", velocity.doubleValue())
|
|
||||||
// );
|
|
||||||
// newAnnotation.addAnnotation(
|
|
||||||
// "nextMark",
|
|
||||||
// yacht.timeTillNextProperty(),
|
|
||||||
// (time) -> {
|
|
||||||
// DateFormat format = new SimpleDateFormat("mm:ss");
|
|
||||||
// return format.format(time);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// newAnnotation.addAnnotation(
|
|
||||||
// "lastMark",
|
|
||||||
// yacht.timeTillNextProperty(),
|
|
||||||
// (time) -> {
|
|
||||||
// DateFormat format = new SimpleDateFormat("mm:ss");
|
|
||||||
// return format.format(time);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
annotations.put(clientYacht, newAnnotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawFps(Double fps) {
|
|
||||||
//Platform.runLater(() -> fpsDisplay.setText(String.format("%d FPS", Math.round(fps))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -755,10 +388,6 @@ public class GameView extends Pane {
|
|||||||
return horiDistance;
|
return horiDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Point2D findScaledXY(GeoPoint unscaled) {
|
|
||||||
return findScaledXY(unscaled.getLat(), unscaled.getLng());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Point2D findScaledXY(double unscaledLat, double unscaledLon) {
|
private Point2D findScaledXY(double unscaledLat, double unscaledLon) {
|
||||||
double distanceFromReference;
|
double distanceFromReference;
|
||||||
double angleFromReference;
|
double angleFromReference;
|
||||||
@@ -801,161 +430,10 @@ public class GameView extends Pane {
|
|||||||
return new Point2D(xAxisLocation, yAxisLocation);
|
return new Point2D(xAxisLocation, yAxisLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the number of meters per pixel.
|
|
||||||
*/
|
|
||||||
private void findMetersPerPixel() {
|
|
||||||
Point2D p1, p2;
|
|
||||||
GeoPoint g1, g2;
|
|
||||||
double theta, distance, dx, dy, dHorizontal, dVertical;
|
|
||||||
g1 = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng());
|
|
||||||
g2 = new GeoPoint(minLatPoint.getLat(), maxLatPoint.getLng());
|
|
||||||
p1 = findScaledXY(new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng()));
|
|
||||||
p2 = findScaledXY(new GeoPoint(minLatPoint.getLat(), maxLatPoint.getLng()));
|
|
||||||
theta = GeoUtility.getBearingRad(g1, g2);
|
|
||||||
distance = GeoUtility.getDistance(g1, g2);
|
|
||||||
dHorizontal = Math.abs(Math.sin(theta) * distance);
|
|
||||||
dVertical = Math.abs(Math.cos(theta) * distance);
|
|
||||||
dx = Math.abs(p1.getX() - p2.getX());
|
|
||||||
dy = Math.abs(p1.getY() - p2.getY());
|
|
||||||
metersPerPixelX = dHorizontal / dx;
|
|
||||||
metersPerPixelY = dVertical / dy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAnnotationVisibilities(boolean teamName, boolean velocity, boolean estTime,
|
|
||||||
boolean legTime, boolean trail, boolean wake) {
|
|
||||||
for (BoatObject boatObject : boatObjects.values()) {
|
|
||||||
boatObject.setVisibility(teamName, velocity, estTime, legTime, trail, wake);
|
|
||||||
}
|
|
||||||
for (AnnotationBox ag : annotations.values()) {
|
|
||||||
ag.setAnnotationVisibility("name", teamName);
|
|
||||||
ag.setAnnotationVisibility("velocity", velocity);
|
|
||||||
ag.setAnnotationVisibility("nextMark", estTime);
|
|
||||||
ag.setAnnotationVisibility("lastMark", legTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFPSVisibility(boolean visibility) {
|
|
||||||
fpsDisplay.setVisible(visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void selectBoat(ClientYacht selectedClientYacht) {
|
|
||||||
boatObjects.forEach((boat, group) ->
|
|
||||||
group.setIsSelected(boat == selectedClientYacht)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void pauseRace() {
|
|
||||||
timer.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWindDir(double windDir) {
|
|
||||||
this.windDir = windDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startRace() {
|
|
||||||
timer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientYacht getPlayerYacht() {
|
|
||||||
return playerYacht;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBoatAsPlayer (ClientYacht playerYacht) {
|
|
||||||
this.playerYacht = playerYacht;
|
|
||||||
playerYacht.toggleSail();
|
|
||||||
boatObjects.get(playerYacht).setAsPlayer();
|
|
||||||
CompoundMark currentMark = course.get(playerYacht.getLegNumber());
|
|
||||||
for (Mark mark : currentMark.getMarks()) {
|
|
||||||
markerObjects.get(mark).showNextExitArrow();
|
|
||||||
}
|
|
||||||
annotations.get(playerYacht).addAnnotation(
|
|
||||||
"velocity",
|
|
||||||
playerYacht.getVelocityProperty(),
|
|
||||||
(velocity) -> String.format("Speed: %.2f ms", velocity.doubleValue())
|
|
||||||
);
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
boatObjectGroup.getChildren().remove(boatObjects.get(playerYacht));
|
|
||||||
gameObjects.add(boatObjects.get(playerYacht));
|
|
||||||
annotationsGroup.getChildren().remove(annotations.get(playerYacht));
|
|
||||||
gameObjects.add(annotations.get(playerYacht));
|
|
||||||
});
|
|
||||||
playerYacht.addMarkRoundingListener(this::updateMarkArrows);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateMarkArrows (ClientYacht yacht, int legNumber) {
|
|
||||||
//Only show arrows for this and next leg.
|
|
||||||
CompoundMark nextMark = null;
|
|
||||||
if (legNumber < course.size() - 1) {
|
|
||||||
Sounds.playMarkRoundingSound();
|
|
||||||
nextMark = course.get(legNumber);
|
|
||||||
for (Mark mark : nextMark.getMarks()) {
|
|
||||||
markerObjects.get(mark).showNextEnterArrow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (legNumber - 2 >= 0) {
|
|
||||||
CompoundMark lastMark = course.get(Math.max(0, legNumber - 2));
|
|
||||||
if (lastMark != nextMark) {
|
|
||||||
for (Mark mark : lastMark.getMarks()) {
|
|
||||||
markerObjects.get(mark).hideAllArrows();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (legNumber - 1 >= 0) {
|
|
||||||
CompoundMark thisMark = course.get(Math.max(0, legNumber - 1));
|
|
||||||
if (thisMark != nextMark) {
|
|
||||||
for (Mark mark : thisMark.getMarks()) {
|
|
||||||
markerObjects.get(mark).showNextExitArrow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given yacht geopoint by race view controller, drawCollision will calculate canvas X and Y and
|
|
||||||
* display a flashing red circle on collision point.
|
|
||||||
*
|
|
||||||
* @param collisionPoint yacht collision point
|
|
||||||
*/
|
|
||||||
public void drawCollision(GeoPoint collisionPoint) {
|
|
||||||
Point2D point = findScaledXY(collisionPoint);
|
|
||||||
double circleRadius = 0.0;
|
|
||||||
Circle circle = new Circle(point.getX(), point.getY(), circleRadius, Color.RED);
|
|
||||||
|
|
||||||
circle.setFill(Color.TRANSPARENT);
|
|
||||||
circle.setStroke(Color.RED);
|
|
||||||
circle.setStrokeWidth(3);
|
|
||||||
|
|
||||||
Timeline timeline = new Timeline();
|
|
||||||
timeline.setCycleCount(1);
|
|
||||||
|
|
||||||
KeyFrame keyframe1 = new KeyFrame(Duration.ZERO,
|
|
||||||
new KeyValue(circle.radiusProperty(), 0),
|
|
||||||
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
|
|
||||||
KeyFrame keyFrame2 = new KeyFrame(new Duration(1000),
|
|
||||||
new KeyValue(circle.radiusProperty(), 50),
|
|
||||||
new KeyValue(circle.strokeProperty(), Color.RED));
|
|
||||||
KeyFrame keyFrame3 = new KeyFrame(new Duration(1500),
|
|
||||||
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
|
|
||||||
|
|
||||||
timeline.getKeyFrames().addAll(keyframe1, keyFrame2, keyFrame3);
|
|
||||||
|
|
||||||
Platform.runLater(() -> gameObjects.add(circle));
|
|
||||||
timeline.setOnFinished(event -> Platform.runLater(() -> gameObjects.remove(circle)));
|
|
||||||
timeline.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFrameRateFXText(Text fpsDisplay) {
|
|
||||||
this.fpsDisplay = null;
|
|
||||||
this.fpsDisplay = fpsDisplay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSize(Double width, Double height){
|
public void setSize(Double width, Double height){
|
||||||
this.canvasWidth = width;
|
this.canvasWidth = width;
|
||||||
this.canvasHeight = height;
|
this.canvasHeight = height;
|
||||||
|
|
||||||
this.panelWidth = width;
|
|
||||||
this.panelHeight = height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHorizontalBuffer(Double buff){
|
public void setHorizontalBuffer(Double buff){
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package seng302.visualiser.fxObjects.assets_2D;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.Group;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.Paint;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visual object for a mark. Contains a coloured circle and any specified arrows.
|
||||||
|
*/
|
||||||
|
public class Marker2D extends Group {
|
||||||
|
|
||||||
|
private Circle mark = new Circle();
|
||||||
|
private Paint colour = Color.BLACK;
|
||||||
|
private List<Group> enterArrows = new ArrayList<>();
|
||||||
|
private List<Group> exitArrows = new ArrayList<>();
|
||||||
|
private int enterArrowIndex = 0;
|
||||||
|
private int exitArrowIndex = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Marker containing only a circle. The default colour is black.
|
||||||
|
*/
|
||||||
|
public Marker2D() {
|
||||||
|
mark.setRadius(5);
|
||||||
|
mark.setCenterX(0);
|
||||||
|
mark.setCenterY(0);
|
||||||
|
Platform.runLater(() -> this.getChildren()
|
||||||
|
.addAll(mark, new Group())); //Empty group placeholder or arrows.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Marker containing only a circle of the given colour.
|
||||||
|
*
|
||||||
|
* @param colour the desired colour for the marker.
|
||||||
|
*/
|
||||||
|
public Marker2D(Paint colour) {
|
||||||
|
this();
|
||||||
|
this.colour = colour;
|
||||||
|
mark.setFill(colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an exit and entry arrow pair to the mark. Arrows are hidden and shown in the order they
|
||||||
|
* are created by calling showNextEnterArrow() or showNextExitArrow()
|
||||||
|
*
|
||||||
|
* @param roundingSide the side the marker will be from the perspective of the arrow.
|
||||||
|
* @param entryAngle The angle the arrow will point towards a marker
|
||||||
|
* @param exitAngle The angle the arrow wil point from the marker.
|
||||||
|
*/
|
||||||
|
public void addArrows(MarkArrowFactory.RoundingSide roundingSide, double entryAngle,
|
||||||
|
double exitAngle) {
|
||||||
|
//Change Color.GRAY to this.colour to revert all gray arrows.
|
||||||
|
enterArrows.add(
|
||||||
|
MarkArrowFactory.constructEntryArrow(roundingSide, entryAngle, exitAngle, Color.GRAY)
|
||||||
|
);
|
||||||
|
exitArrows.add(
|
||||||
|
MarkArrowFactory.constructExitArrow(roundingSide, exitAngle, Color.GRAY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the next EnterArrow. Does nothing if there are no more enter arrows. Other arrows
|
||||||
|
* become hidden.
|
||||||
|
*/
|
||||||
|
public void showNextEnterArrow() {
|
||||||
|
showArrow(enterArrows, enterArrowIndex);
|
||||||
|
enterArrowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the next ExitArrow. Does nothing if there are no more enter arrows. Other arrows become
|
||||||
|
* hidden.
|
||||||
|
*/
|
||||||
|
public void showNextExitArrow() {
|
||||||
|
showArrow(exitArrows, exitArrowIndex);
|
||||||
|
exitArrowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showArrow(List<Group> arrowList, int arrowListIndex) {
|
||||||
|
if (arrowListIndex < arrowList.size()) {
|
||||||
|
if (arrowListIndex == 1) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
this.getChildren().remove(1);
|
||||||
|
this.getChildren().add(arrowList.get(arrowListIndex));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides all arrows.
|
||||||
|
*/
|
||||||
|
public void hideAllArrows() {
|
||||||
|
Platform.runLater(() -> this.getChildren().setAll(mark, new Group()));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user