mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 14:28:43 +00:00
Merge branch 'develop' into 1124_switching_to_finish_screen
# Conflicts: # src/main/java/seng302/gameServer/GameState.java # src/main/java/seng302/gameServer/MainServerThread.java # src/main/java/seng302/gameServer/ServerToClientThread.java # src/main/java/seng302/model/Yacht.java # src/main/java/seng302/visualiser/GameClient.java # src/main/java/seng302/visualiser/GameView.java # src/main/java/seng302/visualiser/controllers/RaceViewController.java # src/test/java/seng302/visualiser/ClientToServerTests/RegularPacketsTest.java
This commit is contained in:
@@ -36,6 +36,8 @@ import seng302.model.stream.packets.StreamPacket;
|
||||
*/
|
||||
public class ClientToServerThread implements Runnable {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Functional interface for receiving packets from client socket.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ import seng302.model.stream.parser.MarkRoundingData;
|
||||
import seng302.model.stream.parser.PositionUpdateData;
|
||||
import seng302.model.stream.parser.PositionUpdateData.DeviceType;
|
||||
import seng302.model.stream.parser.RaceStatusData;
|
||||
import seng302.model.stream.parser.YachtEventData;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.utilities.StreamParser;
|
||||
@@ -102,6 +103,8 @@ public class GameClient {
|
||||
loadStartScreen();
|
||||
}
|
||||
});
|
||||
|
||||
server.setGameClient(this);
|
||||
}
|
||||
|
||||
private void loadStartScreen() {
|
||||
@@ -126,7 +129,8 @@ public class GameClient {
|
||||
* @return the lobby controller.
|
||||
*/
|
||||
private LobbyController loadLobby() {
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(GameClient.class.getResource("/views/LobbyView.fxml"));
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(
|
||||
GameClient.class.getResource("/views/LobbyView.fxml"));
|
||||
try {
|
||||
holderPane.getChildren().clear();
|
||||
holderPane.getChildren().add(fxmlLoader.load());
|
||||
@@ -219,13 +223,18 @@ public class GameClient {
|
||||
case MARK_ROUNDING:
|
||||
updateMarkRounding(StreamParser.extractMarkRounding(packet));
|
||||
break;
|
||||
|
||||
case YACHT_EVENT_CODE:
|
||||
showCollisionAlert(StreamParser.extractYachtEventCode(packet));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startRaceIfAllDataReceived() {
|
||||
if (allXMLReceived() && raceView == null)
|
||||
if (allXMLReceived() && raceView == null) {
|
||||
loadRaceView();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean allXMLReceived() {
|
||||
@@ -271,7 +280,9 @@ public class GameClient {
|
||||
private void processRaceStatusUpdate(RaceStatusData data) {
|
||||
if (allXMLReceived()) {
|
||||
raceState.updateState(data);
|
||||
|
||||
if (raceView != null) {
|
||||
raceView.getGameView().setWindDir(raceState.getWindDirection());
|
||||
}
|
||||
boolean raceFinished = true;
|
||||
for (ClientYacht yacht : allBoatsMap.values()) {
|
||||
if (yacht.getBoatStatus() != 3) {
|
||||
@@ -335,10 +346,26 @@ public class GameClient {
|
||||
switch (e.getCode()) {
|
||||
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
|
||||
case SHIFT: // sails in/sails out
|
||||
socketThread.sendBoatAction(BoatAction.SAILS_IN); break;
|
||||
socketThread.sendBoatAction(BoatAction.SAILS_IN);
|
||||
raceView.getGameView().getPlayerYacht().toggleClientSail();
|
||||
break;
|
||||
case PAGE_UP:
|
||||
case PAGE_DOWN:
|
||||
socketThread.sendBoatAction(BoatAction.MAINTAIN_HEADING); break;
|
||||
}
|
||||
}
|
||||
|
||||
public RaceXMLData getCourseData() {
|
||||
return courseData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells race view to show a collision animation.
|
||||
*/
|
||||
private void showCollisionAlert(YachtEventData yachtEventData) {
|
||||
// 33 is the agreed code to show collision
|
||||
if (yachtEventData.getEventId() == 33) {
|
||||
raceView.showCollision(yachtEventData.getSubjectId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,20 +6,34 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.Event;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.ScrollEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.shape.Circle;
|
||||
import javafx.scene.shape.Polygon;
|
||||
import javafx.scene.shape.StrokeType;
|
||||
import javafx.scene.text.Text;
|
||||
import seng302.model.ClientYacht;
|
||||
import javafx.util.Duration;
|
||||
import seng302.model.Colors;
|
||||
import seng302.model.GeoPoint;
|
||||
import seng302.model.Limit;
|
||||
@@ -40,10 +54,10 @@ import seng302.visualiser.map.CanvasMap;
|
||||
*/
|
||||
public class GameView extends Pane {
|
||||
|
||||
private double bufferSize = 50;
|
||||
private double panelWidth = 1260; // it should be 1280 but, minors 40 to cancel the bias.
|
||||
private double panelHeight = 960;
|
||||
private double canvasWidth = 1100;
|
||||
private double bufferSize = 50;
|
||||
private double panelWidth = 1260; // it should be 1280 but, minors 40 to cancel the bias.
|
||||
private double panelHeight = 960;
|
||||
private double canvasWidth = 1100;
|
||||
private double canvasHeight = 920;
|
||||
private boolean horizontalInversion = false;
|
||||
|
||||
@@ -53,6 +67,8 @@ public class GameView extends Pane {
|
||||
private double referencePointX, referencePointY;
|
||||
private double metersPerPixelX, metersPerPixelY;
|
||||
|
||||
final double SCALE_DELTA = 1.1;
|
||||
|
||||
private Text fpsDisplay = new Text();
|
||||
private Polygon raceBorder = new CourseBoundary();
|
||||
|
||||
@@ -80,13 +96,33 @@ public class GameView extends Pane {
|
||||
private Double frameRate = 60.0;
|
||||
private int frameTimeIndex = 0;
|
||||
private boolean arrayFilled = false;
|
||||
private Yacht playerYacht;
|
||||
private double windDir = 0.0;
|
||||
|
||||
double scaleFactor = 1;
|
||||
|
||||
public void zoomOut() {
|
||||
scaleFactor = 0.95;
|
||||
for (Node child : getChildren()) {
|
||||
child.setScaleX(child.getScaleX() * scaleFactor);
|
||||
child.setScaleY(child.getScaleY() * scaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
public void zoomIn() {
|
||||
scaleFactor = 1.05;
|
||||
for (Node child : getChildren()) {
|
||||
child.setScaleX(child.getScaleX() * scaleFactor);
|
||||
child.setScaleY(child.getScaleY() * scaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
private enum ScaleDirection {
|
||||
HORIZONTAL,
|
||||
VERTICAL
|
||||
}
|
||||
|
||||
public GameView () {
|
||||
public GameView() {
|
||||
gameObjects = this.getChildren();
|
||||
// create image view for map, bind panel size to image
|
||||
gameObjects.add(mapImage);
|
||||
@@ -99,7 +135,7 @@ public class GameView extends Pane {
|
||||
initializeTimer();
|
||||
}
|
||||
|
||||
private void initializeTimer () {
|
||||
private void initializeTimer() {
|
||||
Arrays.fill(frameTimes, 1_000_000_000 / 60);
|
||||
timer = new AnimationTimer() {
|
||||
private long lastTime = 0;
|
||||
@@ -142,8 +178,8 @@ public class GameView extends Pane {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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();
|
||||
@@ -217,7 +253,7 @@ public class GameView extends Pane {
|
||||
gates.add(
|
||||
makeAndBindGate(
|
||||
markerObjects.get(cMark.getSubMark(i)),
|
||||
markerObjects.get(cMark.getSubMark(i+1)),
|
||||
markerObjects.get(cMark.getSubMark(i + 1)),
|
||||
colour
|
||||
)
|
||||
);
|
||||
@@ -280,7 +316,7 @@ public class GameView extends Pane {
|
||||
gate.endYProperty().bind(
|
||||
m2.centerYProperty()
|
||||
);
|
||||
return gate;
|
||||
return gate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,10 +370,10 @@ public class GameView extends Pane {
|
||||
boatObjectGroup.getChildren().add(newBoat);
|
||||
trails.getChildren().add(newBoat.getTrail());
|
||||
// TODO: 1/08/17 Make this less vile to look at.
|
||||
clientYacht.addLocationListener((boat, lat, lon, heading, velocity) -> {
|
||||
clientYacht.addLocationListener((boat, lat, lon, heading, velocity, sailIn) -> {
|
||||
BoatObject bo = boatObjects.get(boat);
|
||||
Point2D p2d = findScaledXY(lat, lon);
|
||||
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity);
|
||||
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
|
||||
// annotations.get(boat).setLayoutX(p2d.getX());
|
||||
// annotations.get(boat).setLayoutY(p2d.getY());
|
||||
// annotations.get(boat).setLocation(100d, 100d);
|
||||
@@ -388,25 +424,25 @@ public class GameView extends Pane {
|
||||
annotations.put(clientYacht, newAnnotation);
|
||||
}
|
||||
|
||||
private void drawFps(Double fps){
|
||||
private void drawFps(Double fps) {
|
||||
Platform.runLater(() -> fpsDisplay.setText(String.format("%d FPS", Math.round(fps))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point
|
||||
* with the leftmost point, rightmost point, southern most point and northern most point
|
||||
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point with
|
||||
* the leftmost point, rightmost point, southern most point and northern most point
|
||||
* respectively.
|
||||
*/
|
||||
private void findMinMaxPoint(List<GeoPoint> points) {
|
||||
List<GeoPoint> sortedPoints = new ArrayList<>(points);
|
||||
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat));
|
||||
minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
|
||||
GeoPoint maxLat = sortedPoints.get(sortedPoints.size()-1);
|
||||
GeoPoint maxLat = sortedPoints.get(sortedPoints.size() - 1);
|
||||
maxLatPoint = new GeoPoint(maxLat.getLat(), maxLat.getLng());
|
||||
|
||||
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLng));
|
||||
minLonPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
|
||||
GeoPoint maxLon = sortedPoints.get(sortedPoints.size()-1);
|
||||
GeoPoint maxLon = sortedPoints.get(sortedPoints.size() - 1);
|
||||
maxLonPoint = new GeoPoint(maxLon.getLat(), maxLon.getLng());
|
||||
if (maxLonPoint.getLng() - minLonPoint.getLng() > 180) {
|
||||
horizontalInversion = true;
|
||||
@@ -426,15 +462,19 @@ public class GameView extends Pane {
|
||||
|
||||
if (scaleDirection == ScaleDirection.HORIZONTAL) {
|
||||
referenceAngle = Math.abs(
|
||||
GeoUtility.getBearingRad(referencePoint, minLonPoint)
|
||||
GeoUtility.getBearingRad(referencePoint, minLonPoint)
|
||||
);
|
||||
referencePointX = bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility.getDistance(referencePoint, minLonPoint);
|
||||
referencePointX =
|
||||
bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, minLonPoint);
|
||||
referenceAngle = Math.abs(GeoUtility.getDistance(referencePoint, maxLatPoint));
|
||||
referencePointY = canvasHeight - (bufferSize + bufferSize);
|
||||
referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility.getDistance(referencePoint, maxLatPoint);
|
||||
referencePointY = referencePointY / 2;
|
||||
referencePointY = canvasHeight - (bufferSize + bufferSize);
|
||||
referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, maxLatPoint);
|
||||
referencePointY = referencePointY / 2;
|
||||
referencePointY += bufferSize;
|
||||
referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility.getDistance(referencePoint, maxLatPoint);
|
||||
referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, maxLatPoint);
|
||||
} else {
|
||||
referencePointY = canvasHeight - bufferSize;
|
||||
referenceAngle = Math.abs(
|
||||
@@ -442,11 +482,14 @@ public class GameView extends Pane {
|
||||
GeoUtility.getDistance(referencePoint, minLonPoint)
|
||||
)
|
||||
);
|
||||
referencePointX = bufferSize;
|
||||
referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility.getDistance(referencePoint, minLonPoint);
|
||||
referencePointX += ((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor)) / 2;
|
||||
referencePointX = bufferSize;
|
||||
referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, minLonPoint);
|
||||
referencePointX +=
|
||||
((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor))
|
||||
/ 2;
|
||||
}
|
||||
if(horizontalInversion) {
|
||||
if (horizontalInversion) {
|
||||
referencePointX = canvasWidth - bufferSize - (referencePointX - bufferSize);
|
||||
}
|
||||
}
|
||||
@@ -459,12 +502,12 @@ public class GameView extends Pane {
|
||||
private double scaleRaceExtremities() {
|
||||
|
||||
double vertAngle = Math.abs(
|
||||
GeoUtility.getBearingRad(minLatPoint, maxLatPoint)
|
||||
GeoUtility.getBearingRad(minLatPoint, maxLatPoint)
|
||||
);
|
||||
double vertDistance =
|
||||
Math.cos(vertAngle) * GeoUtility.getDistance(minLatPoint, maxLatPoint);
|
||||
double horiAngle = Math.abs(
|
||||
GeoUtility.getBearingRad(minLonPoint, maxLonPoint)
|
||||
GeoUtility.getBearingRad(minLonPoint, maxLonPoint)
|
||||
);
|
||||
if (horiAngle <= (Math.PI / 2)) {
|
||||
horiAngle = (Math.PI / 2) - horiAngle;
|
||||
@@ -490,35 +533,43 @@ public class GameView extends Pane {
|
||||
return findScaledXY(unscaled.getLat(), unscaled.getLng());
|
||||
}
|
||||
|
||||
private Point2D findScaledXY (double unscaledLat, double unscaledLon) {
|
||||
private Point2D findScaledXY(double unscaledLat, double unscaledLon) {
|
||||
double distanceFromReference;
|
||||
double angleFromReference;
|
||||
double xAxisLocation = referencePointX;
|
||||
double yAxisLocation = referencePointY;
|
||||
|
||||
angleFromReference = GeoUtility.getBearingRad(
|
||||
minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
|
||||
minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
|
||||
);
|
||||
distanceFromReference = GeoUtility.getDistance(
|
||||
minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
|
||||
);
|
||||
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
|
||||
xAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
xAxisLocation += Math
|
||||
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation -= Math
|
||||
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
} else if (angleFromReference >= 0) {
|
||||
angleFromReference = angleFromReference - Math.PI / 2;
|
||||
xAxisLocation += Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
xAxisLocation += Math
|
||||
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation += Math
|
||||
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
|
||||
angleFromReference = Math.abs(angleFromReference);
|
||||
xAxisLocation -= Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
xAxisLocation -= Math
|
||||
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation -= Math
|
||||
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
} else {
|
||||
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
|
||||
xAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
xAxisLocation -= Math
|
||||
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation += Math
|
||||
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
}
|
||||
if(horizontalInversion) {
|
||||
if (horizontalInversion) {
|
||||
xAxisLocation = canvasWidth - bufferSize - (xAxisLocation - bufferSize);
|
||||
}
|
||||
return new Point2D(xAxisLocation, yAxisLocation);
|
||||
@@ -545,7 +596,7 @@ public class GameView extends Pane {
|
||||
metersPerPixelY = dVertical / dy;
|
||||
}
|
||||
|
||||
public void setAnnotationVisibilities (boolean teamName, boolean velocity, boolean estTime,
|
||||
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);
|
||||
@@ -558,7 +609,7 @@ public class GameView extends Pane {
|
||||
}
|
||||
}
|
||||
|
||||
public void setFPSVisibility (boolean visibility) {
|
||||
public void setFPSVisibility(boolean visibility) {
|
||||
fpsDisplay.setVisible(visibility);
|
||||
}
|
||||
|
||||
@@ -568,15 +619,27 @@ public class GameView extends Pane {
|
||||
);
|
||||
}
|
||||
|
||||
public void pauseRace () {
|
||||
public void pauseRace() {
|
||||
timer.stop();
|
||||
}
|
||||
|
||||
public void startRace () {
|
||||
|
||||
public void setWindDir(double windDir) {
|
||||
this.windDir = windDir;
|
||||
}
|
||||
|
||||
public void startRace() {
|
||||
timer.start();
|
||||
}
|
||||
|
||||
public void setBoatAsPlayer(ClientYacht playerYacht) {
|
||||
public ClientYacht getPlayerYacht() {
|
||||
return playerYacht;
|
||||
}
|
||||
|
||||
|
||||
public void setBoatAsPlayer (ClientYacht playerYacht) {
|
||||
this.playerYacht = playerYacht;
|
||||
this.playerYacht.toggleClientSail();
|
||||
boatObjects.get(playerYacht).setAsPlayer();
|
||||
annotations.get(playerYacht).addAnnotation(
|
||||
"velocity",
|
||||
@@ -590,4 +653,40 @@ public class GameView extends Pane {
|
||||
gameObjects.add(annotations.get(playerYacht));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Platform.runLater(() -> {
|
||||
Point2D point = findScaledXY(collisionPoint);
|
||||
double circleRadius = 0.0;
|
||||
Circle circle = new Circle(point.getX(), point.getY(), circleRadius, Color.RED);
|
||||
gameObjects.add(circle);
|
||||
|
||||
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);
|
||||
timeline.play();
|
||||
|
||||
timeline.setOnFinished(event -> gameObjects.remove(circle));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,9 +208,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
|
||||
|
||||
/**
|
||||
* Used to add any new yachts into the race that may have started late or not have had data received yet
|
||||
* Used to add any new yachts into the race that may have started late or not have had data
|
||||
* received yet
|
||||
*/
|
||||
private void updateSparkLine(){
|
||||
private void updateSparkLine() {
|
||||
// TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed.
|
||||
// Collect the racing yachts that aren't already in the chart
|
||||
sparkLineData.clear();
|
||||
@@ -228,14 +229,14 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
1.0 + participants.size() - yacht.getPositionInteger()
|
||||
)
|
||||
);
|
||||
sparkLineData.add(yachtData);
|
||||
sparkLineData.add(yachtData);
|
||||
});
|
||||
|
||||
// Lambda function to sort the series in order of leg (later legs shown more to the right)
|
||||
sparkLineData.sort((o1, o2) -> {
|
||||
Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue());
|
||||
Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue());
|
||||
if (leg2 < leg1){
|
||||
Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size() - 1).getXValue());
|
||||
Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size() - 1).getXValue());
|
||||
if (leg2 < leg1) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
@@ -248,7 +249,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
.filter(spark -> !raceSparkLine.getData().contains(spark))
|
||||
.forEach(spark -> {
|
||||
raceSparkLine.getData().add(spark);
|
||||
spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
|
||||
spark.getNode().lookup(".chart-series-line")
|
||||
.setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -284,26 +286,27 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
|
||||
/**
|
||||
* gets the rgb string of the yachts colour to use for the chart via css
|
||||
*
|
||||
* @param yachtId id of yacht passed in to get the yachts colour
|
||||
* @return the colour as an rgb string
|
||||
*/
|
||||
private String getBoatColorAsRGB(String yachtId){
|
||||
private String getBoatColorAsRGB(String yachtId) {
|
||||
Color color = participants.get(Integer.valueOf(yachtId)).getColour();
|
||||
if (color == null){
|
||||
return String.format("#%02X%02X%02X",255,255,255);
|
||||
if (color == null) {
|
||||
return String.format("#%02X%02X%02X", 255, 255, 255);
|
||||
}
|
||||
return String.format( "#%02X%02X%02X",
|
||||
(int)( color.getRed() * 255 ),
|
||||
(int)( color.getGreen() * 255 ),
|
||||
(int)( color.getBlue() * 255 )
|
||||
return String.format("#%02X%02X%02X",
|
||||
(int) (color.getRed() * 255),
|
||||
(int) (color.getGreen() * 255),
|
||||
(int) (color.getBlue() * 255)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialises a timer which updates elements of the RaceView such as wind direction, yacht
|
||||
* orderings etc.. which are dependent on the info from the stream parser constantly.
|
||||
* Updates of each of these attributes are called ONCE EACH SECOND
|
||||
* orderings etc.. which are dependent on the info from the stream parser constantly. Updates of
|
||||
* each of these attributes are called ONCE EACH SECOND
|
||||
*/
|
||||
private void initializeUpdateTimer() {
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@@ -318,9 +321,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all corners until ones SeqID matches with the yachts current leg number.
|
||||
* Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark
|
||||
* Returns null if no next mark found.
|
||||
* Iterates over all corners until ones SeqID matches with the yachts current leg number. Then
|
||||
* it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark Returns
|
||||
* null if no next mark found.
|
||||
*
|
||||
* @param bg The BoatGroup to find the next mark of
|
||||
* @return The next Mark or null if none found
|
||||
*/
|
||||
@@ -474,15 +478,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
}
|
||||
|
||||
|
||||
private Point2D getPointRotation(Point2D ref, Double distance, Double angle){
|
||||
Double newX = ref.getX() + (ref.getX() + distance -ref.getX())*Math.cos(angle) - (ref.getY() + distance -ref.getY())*Math.sin(angle);
|
||||
Double newY = ref.getY() + (ref.getX() + distance -ref.getX())*Math.sin(angle) + (ref.getY() + distance -ref.getY())*Math.cos(angle);
|
||||
private Point2D getPointRotation(Point2D ref, Double distance, Double angle) {
|
||||
Double newX = ref.getX() + (ref.getX() + distance - ref.getX()) * Math.cos(angle)
|
||||
- (ref.getY() + distance - ref.getY()) * Math.sin(angle);
|
||||
Double newY = ref.getY() + (ref.getX() + distance - ref.getX()) * Math.sin(angle)
|
||||
+ (ref.getY() + distance - ref.getY()) * Math.cos(angle);
|
||||
|
||||
return new Point2D(newX, newY);
|
||||
}
|
||||
|
||||
|
||||
public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) {
|
||||
public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) {
|
||||
|
||||
Point2D ep = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle);
|
||||
Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY());
|
||||
@@ -501,8 +507,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
|
||||
|
||||
/**
|
||||
* Initialised the combo box with any yachts currently in the race and adds the required listener
|
||||
* for the combobox to take action upon selection
|
||||
* Initialised the combo box with any yachts currently in the race and adds the required
|
||||
* listener for the combobox to take action upon selection
|
||||
*/
|
||||
private void initialiseBoatSelectionComboBox() {
|
||||
yachtSelectionComboBox.setItems(
|
||||
@@ -539,7 +545,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
TimeUnit.MILLISECONDS.toHours(milliseconds),
|
||||
TimeUnit.MILLISECONDS.toMinutes(milliseconds) % 60, //Modulus 60 minutes per hour
|
||||
TimeUnit.MILLISECONDS.toSeconds(milliseconds) % 60 //Modulus 60 seconds per minute
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
private void setAnnotations(Integer annotationLevel) {
|
||||
@@ -590,8 +596,23 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
// }
|
||||
}
|
||||
|
||||
public void updateRaceData (RaceXMLData raceData) {
|
||||
public void updateRaceData(RaceXMLData raceData) {
|
||||
this.courseData = raceData;
|
||||
gameView.updateBorder(raceData.getCourseLimit());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by game client after receiving yacht event packet. Parameter subject id is the
|
||||
* offending yacht. This function in turn will pass the yacht location to game view to display a
|
||||
* collision alert.
|
||||
*
|
||||
* @param subjectId source id of offending yacht
|
||||
*/
|
||||
public void showCollision(Long subjectId) {
|
||||
gameView.drawCollision(participants.get((int) (long) subjectId).getLocation());
|
||||
}
|
||||
|
||||
public GameView getGameView() {
|
||||
return gameView;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class StartScreenController implements Initializable {
|
||||
*/
|
||||
@FXML
|
||||
public void hostButtonPressed() {
|
||||
new GameState(getLocalHostIp());
|
||||
// new GameState(getLocalHostIp());
|
||||
gameClient = new GameClient(holder);
|
||||
gameClient.runAsHost(getLocalHostIp(), 4942);
|
||||
// try {
|
||||
|
||||
@@ -30,9 +30,11 @@ public class BoatObject extends Group {
|
||||
private double xVelocity;
|
||||
private double yVelocity;
|
||||
private double lastHeading;
|
||||
private double sailState;
|
||||
//Graphical objects
|
||||
private Polyline trail = new Polyline();
|
||||
private Polygon boatPoly;
|
||||
private Polygon sail;
|
||||
private Wake wake;
|
||||
private Line leftLayLine;
|
||||
private Line rightLayline;
|
||||
@@ -94,7 +96,16 @@ public class BoatObject extends Group {
|
||||
trail.setCache(true);
|
||||
wake = new Wake(0, -BOAT_HEIGHT);
|
||||
wake.setVisible(true);
|
||||
super.getChildren().addAll(boatPoly);//, annotationBox);
|
||||
|
||||
sail = new Polygon(0.0,BOAT_HEIGHT / 4,
|
||||
0.0, BOAT_HEIGHT);
|
||||
sailState = 0;
|
||||
sail.setStrokeWidth(2.0);
|
||||
sail.setStroke(Color.BLACK);
|
||||
sail.setFill(Color.TRANSPARENT);
|
||||
sail.setCache(true);
|
||||
super.getChildren().clear();
|
||||
super.getChildren().addAll(boatPoly, sail);
|
||||
}
|
||||
|
||||
public void setFill (Paint value) {
|
||||
@@ -105,19 +116,30 @@ public class BoatObject extends Group {
|
||||
|
||||
/**
|
||||
* Moves the boat and its children annotations to coordinates specified
|
||||
*
|
||||
* @param x The X coordinate to move the boat to
|
||||
* @param x The X coordinate to move the boat to
|
||||
* @param y The Y coordinate to move the boat to
|
||||
* @param rotation The rotation by which the boat moves
|
||||
* @param velocity The velocity the boat is moving
|
||||
* @param sailIn
|
||||
*/
|
||||
public void moveTo(double x, double y, double rotation, double velocity) {
|
||||
public void moveTo(double x, double y, double rotation, double velocity, Boolean sailIn, double windDir) {
|
||||
Double dx = Math.abs(boatPoly.getLayoutX() - x);
|
||||
Double dy = Math.abs(boatPoly.getLayoutY() - y);
|
||||
Platform.runLater(() -> {
|
||||
rotateTo(rotation);
|
||||
rotateTo(rotation, sailIn, windDir);
|
||||
boatPoly.setLayoutX(x);
|
||||
boatPoly.setLayoutY(y);
|
||||
if (sailIn) {
|
||||
// sail.getPoints().clear();
|
||||
// sail.getPoints().addAll(0.0, 0.0, 4.0, 1.5, 8.0, 3.0, 12.0, 3.5, 16.0, 3.0, 20.0, 1.5, 24.0, 0.0);
|
||||
// sail.getPoints().addAll(0.0, 0.0, 24.0, 0.0);
|
||||
sail.setLayoutX(x);
|
||||
sail.setLayoutY(y);
|
||||
} else {
|
||||
animateSail();
|
||||
sail.setLayoutX(x);
|
||||
sail.setLayoutY(y);
|
||||
}
|
||||
wake.setLayoutX(x);
|
||||
wake.setLayoutY(y);
|
||||
});
|
||||
@@ -142,8 +164,65 @@ public class BoatObject extends Group {
|
||||
}
|
||||
}
|
||||
|
||||
private void rotateTo(double rotation) {
|
||||
boatPoly.getTransforms().setAll(new Rotate(rotation));
|
||||
private Double normalizeHeading(double heading, double windDirection) {
|
||||
Double normalizedHeading = heading - windDirection;
|
||||
normalizedHeading = (double) Math.floorMod(normalizedHeading.longValue(), 360L);
|
||||
return normalizedHeading;
|
||||
}
|
||||
|
||||
|
||||
private void rotateTo(double heading, boolean sailsIn, double windDir) {
|
||||
boatPoly.getTransforms().setAll(new Rotate(heading));
|
||||
if (sailsIn) {
|
||||
Double sailWindOffset = 30.0;
|
||||
Double upwindAngleLimit = 15.0;
|
||||
Double downwindAngleLimit = 10.0; //Upwind from normalised horizontal
|
||||
Double normalizedHeading = normalizeHeading(heading, windDir);
|
||||
if (normalizedHeading < 180) {
|
||||
sail.getTransforms().setAll(new Rotate(windDir + 90 + sailWindOffset));
|
||||
sail.getPoints().clear();
|
||||
sail.getPoints().addAll(0.0, 0.0, 4.0, -1.5, 8.0, -3.0, 12.0, -3.5, 16.0, -3.0, 20.0, -1.5, 24.0, 0.0);
|
||||
if (normalizedHeading > 90 + sailWindOffset){
|
||||
sail.getTransforms().setAll(new Rotate(heading + downwindAngleLimit));
|
||||
}
|
||||
if (normalizedHeading < sailWindOffset + upwindAngleLimit){
|
||||
sail.getTransforms().setAll(new Rotate(heading + 90 - upwindAngleLimit));
|
||||
}
|
||||
} else {
|
||||
sail.getTransforms().setAll(new Rotate(windDir + 90 - sailWindOffset));
|
||||
sail.getPoints().clear();
|
||||
sail.getPoints().addAll(0.0, 0.0, 4.0, 1.5, 8.0, 3.0, 12.0, 3.5, 16.0, 3.0, 20.0, 1.5, 24.0, 0.0);
|
||||
if (normalizedHeading < 270 - sailWindOffset){
|
||||
sail.getTransforms().setAll(new Rotate(heading + 180 - downwindAngleLimit));
|
||||
}
|
||||
if (normalizedHeading > 360 - (sailWindOffset + upwindAngleLimit)){
|
||||
sail.getTransforms().setAll(new Rotate(heading + 90 + upwindAngleLimit));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sail.getTransforms().setAll(new Rotate(windDir));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void animateSail(){
|
||||
Double[] points = new Double[200];
|
||||
double amplitude = 2.0;
|
||||
double period = 10;
|
||||
for (int i = 0; i < 50; i++) {
|
||||
points[i * 2] = amplitude * Math.sin(((Math.PI * i) / period + sailState));
|
||||
points[i * 2 + 1] = (BOAT_HEIGHT * i) / BOAT_HEIGHT / 2;
|
||||
points[199 - (i * 2)] = (BOAT_HEIGHT * i) / BOAT_HEIGHT / 2;
|
||||
points[199 - (i * 2 + 1)] = amplitude * Math.sin(((Math.PI * i) / period + sailState));
|
||||
}
|
||||
if (sailState == - 2 * Math.PI) {
|
||||
sailState = 0;
|
||||
} else {
|
||||
sailState = sailState - Math.PI / 5;
|
||||
}
|
||||
sail.getPoints().clear();
|
||||
sail.getPoints().addAll(points);
|
||||
|
||||
}
|
||||
|
||||
public void updateLocation() {
|
||||
@@ -275,11 +354,12 @@ public class BoatObject extends Group {
|
||||
boatPoly.setStroke(Color.BLACK);
|
||||
boatPoly.setStrokeWidth(3);
|
||||
isPlayer = true;
|
||||
animateSail();
|
||||
}
|
||||
|
||||
public void setTrajectory(double heading, double velocity) {
|
||||
public void setTrajectory(double heading, double velocity, double windDir) {
|
||||
wake.setRotation(lastHeading - heading, velocity);
|
||||
rotateTo(heading);
|
||||
rotateTo(heading, false, windDir);
|
||||
xVelocity = Math.cos(Math.toRadians(heading)) * velocity;
|
||||
yVelocity = Math.sin(Math.toRadians(heading)) * velocity;
|
||||
lastHeading = heading;
|
||||
|
||||
Reference in New Issue
Block a user