diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index 2bc1ed91..88d588cf 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.Timer; import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.beans.property.ReadOnlyIntegerProperty; @@ -16,6 +17,7 @@ import javafx.scene.paint.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; +import seng302.visualiser.fxObjects.assets_3D.BoatObject; /** * Yacht class for the racing boat.

Class created to store more variables (eg. boat statuses) @@ -57,12 +59,17 @@ public class ClientYacht extends Observable { private Integer boatStatus; private Double currentVelocity; + Timer t; + + private BoatObject boatObject; + private List locationListeners = new ArrayList<>(); private List markRoundingListeners = new ArrayList<>(); private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper(); private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper(); private ReadOnlyIntegerWrapper placingProperty = new ReadOnlyIntegerWrapper(); + private ReadOnlyDoubleWrapper headingProperty = new ReadOnlyDoubleWrapper(); private Color colour; public ClientYacht(BoatMeshType boatType, Integer sourceId, String hullID, String shortName, @@ -75,6 +82,7 @@ public class ClientYacht extends Observable { this.country = country; this.location = new GeoPoint(57.670341, 11.826856); this.heading = 120.0; //In degrees + this.headingProperty.set(this.heading); this.currentVelocity = 0d; this.boatStatus = 1; this.colour = Color.rgb(0, 0, 0, 1.0); @@ -222,6 +230,7 @@ public class ClientYacht extends Observable { public void setHeading(Double heading) { this.heading = heading; + setHeadingProperty(); } @Override @@ -250,10 +259,10 @@ public class ClientYacht extends Observable { this.colour = colour; } - public void updateLocation(double lat, double lng, double heading, double velocity) { setLocation(lat, lng); this.heading = heading; + setHeadingProperty(); this.currentVelocity = velocity; updateVelocityProperty(velocity); for (YachtLocationListener yll : locationListeners) { @@ -261,6 +270,10 @@ public class ClientYacht extends Observable { } } + private void setHeadingProperty() { + headingProperty.set(heading); + } + public void addLocationListener(YachtLocationListener listener) { locationListeners.add(listener); } @@ -289,4 +302,17 @@ public class ClientYacht extends Observable { public Double getCurrentVelocity() { return currentVelocity; } + + public void setBoatObject(BoatObject newBoatObject) { + this.boatObject = newBoatObject; + } + + public BoatObject getBoatObject() { + return this.boatObject; + } + + public ReadOnlyDoubleWrapper getHeadingProperty() { + return headingProperty; + } + } diff --git a/src/main/java/seng302/model/GameKeyBind.java b/src/main/java/seng302/model/GameKeyBind.java index 1c765adc..5627625c 100644 --- a/src/main/java/seng302/model/GameKeyBind.java +++ b/src/main/java/seng302/model/GameKeyBind.java @@ -30,7 +30,12 @@ public class GameKeyBind { keys.add(KeyCode.ENTER); keys.add(KeyCode.PAGE_UP); keys.add(KeyCode.PAGE_DOWN); - for (int i = 0; i < 7; i++) { + keys.add(KeyCode.F1); + keys.add(KeyCode.D); + keys.add(KeyCode.A); + keys.add(KeyCode.W); + keys.add(KeyCode.S); + for (int i = 0; i < 12; i++) { actionToKeyMap.put(KeyAction.getType(i + 1), keys.get(i)); keyToActionMap.put(keys.get(i), KeyAction.getType(i + 1)); } @@ -47,6 +52,10 @@ public class GameKeyBind { return instance.actionToKeyMap.get(keyAction); } + public KeyAction getKeyAction(KeyCode keyCode) { + return instance.keyToActionMap.get(keyCode); + } + /** * Binds a key to a key action * diff --git a/src/main/java/seng302/model/KeyAction.java b/src/main/java/seng302/model/KeyAction.java index 1b8c2fa1..51509027 100644 --- a/src/main/java/seng302/model/KeyAction.java +++ b/src/main/java/seng302/model/KeyAction.java @@ -10,7 +10,12 @@ public enum KeyAction { SAILS_STATE(4), TACK_GYBE(5), UPWIND(6), - DOWNWIND(7); + DOWNWIND(7), + VIEW(8), + RIGHT(9), + LEFT(10), + FORWARD(11), + BACKWARD(12); private final int type; private static final Map intToTypeMap = new HashMap<>(); diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index 5822b248..f7cdaeba 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -156,11 +156,11 @@ public class ServerYacht { /** * Enables the boats auto pilot feature, which will move the boat towards a given heading. * - * @param thisHeading The heading to move the boat towards. + * @param newHeading The heading to move the boat towards. */ - private void setAutoPilot(Double thisHeading) { + private void setAutoPilot(Double newHeading) { isAuto = true; - autoHeading = thisHeading; + autoHeading = newHeading; } /** @@ -178,8 +178,9 @@ public class ServerYacht { if (isAuto) { turnTowardsHeading(autoHeading); if (Math.abs(heading - autoHeading) - <= turnStep) { //Cancel when within 1 turn step of target. + <= turnStep*1.5) { isAuto = false; + setHeading(autoHeading); } } } @@ -265,7 +266,7 @@ public class ServerYacht { // Take optimal heading and turn into a boat heading rather than a wind heading. optimalHeading = - optimalHeading + GameState.getWindDirection(); + (optimalHeading + GameState.getWindDirection()) % 360; setAutoPilot(optimalHeading); } diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 20bf4553..938e998e 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -14,8 +14,6 @@ import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXMLLoader; -import javafx.scene.control.Alert; -import javafx.scene.control.Alert.AlertType; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Pane; @@ -46,6 +44,7 @@ import seng302.utilities.XMLParser; import seng302.visualiser.controllers.LobbyController; import seng302.visualiser.controllers.RaceViewController; import seng302.visualiser.controllers.ViewManager; +import seng302.visualiser.controllers.dialogs.PopupDialogController; /** * This class is a client side instance of a yacht racing game in JavaFX. The game is instantiated @@ -171,10 +170,12 @@ public class GameClient { private void showConnectionError (String message) { Platform.runLater(() -> { - Alert alert = new Alert(AlertType.ERROR); - alert.setHeaderText("Connection Error"); - alert.setContentText(message); - alert.showAndWait(); + PopupDialogController controller = ViewManager.getInstance().showPopupDialog(); + controller.setHeader("Oops"); + controller.setContent(message); + controller.setOptionButtonText("GO HOME"); + controller + .setOptionButtonEventHandler(event -> ViewManager.getInstance().goToStartView()); }); } diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index 1b3c3562..f397b06b 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -1,6 +1,8 @@ package seng302.visualiser; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -8,6 +10,7 @@ import javafx.animation.AnimationTimer; import javafx.application.Platform; import javafx.geometry.Point2D; import javafx.geometry.Point3D; +import javafx.scene.Camera; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.PerspectiveCamera; @@ -20,6 +23,9 @@ import javafx.scene.transform.Scale; import javafx.scene.transform.Translate; import seng302.gameServer.messages.RoundingSide; import seng302.model.ClientYacht; +import seng302.model.GameKeyBind; +import seng302.model.GeoPoint; +import seng302.model.KeyAction; import seng302.model.Limit; import seng302.model.ScaledPoint; import seng302.model.mark.CompoundMark; @@ -28,6 +34,11 @@ import seng302.model.mark.Mark; import seng302.model.token.Token; import seng302.utilities.GeoUtility; import seng302.utilities.Sounds; +import seng302.visualiser.cameras.ChaseCamera; +import seng302.visualiser.cameras.IsometricCamera; +import seng302.visualiser.cameras.RaceCamera; +import seng302.visualiser.cameras.TopDownCamera; +import seng302.visualiser.controllers.ViewManager; import seng302.visualiser.fxObjects.MarkArrowFactory; import seng302.visualiser.fxObjects.assets_3D.BoatObject; import seng302.visualiser.fxObjects.assets_3D.Marker3D; @@ -37,18 +48,19 @@ import seng302.visualiser.fxObjects.assets_3D.ModelType; /** * Collection of animated3D assets that displays a race. */ - public class GameView3D extends GameView{ private final double FOV = 60; - private final double DEFAULT_CAMERA_DEPTH = -125; private final double DEFAULT_CAMERA_X = 0; private final double DEFAULT_CAMERA_Y = 100; private Group root3D; private SubScene view; - private PerspectiveCamera camera; private Group raceBorder = new Group(); + // Cameras + private PerspectiveCamera isometricCam; + private PerspectiveCamera topDownCam; + private PerspectiveCamera chaseCam; /* 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. */ @@ -63,21 +75,22 @@ public class GameView3D extends GameView{ private Double windDir; public GameView3D () { - canvasWidth = canvasHeight = 220; - camera = new PerspectiveCamera(true); - camera.getTransforms().addAll( - new Translate(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y, DEFAULT_CAMERA_DEPTH) - ); - camera.setFarClip(600); - camera.setNearClip(0.1); - camera.setFieldOfView(FOV); + isometricCam = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y); + topDownCam = new TopDownCamera(); + chaseCam = new ChaseCamera(); + + for (PerspectiveCamera pc : Arrays.asList(isometricCam, topDownCam, chaseCam)) { + pc.setFarClip(600); + pc.setNearClip(0.1); + pc.setFieldOfView(FOV); + } + gameObjects = new Group(); - root3D = new Group(camera, gameObjects); + root3D = new Group(isometricCam, gameObjects); view = new SubScene( root3D, 1000, 1000, true, SceneAntialiasing.BALANCED ); - view.setCamera(camera); - camera.getTransforms().add(new Rotate(30, new Point3D(1,0,0))); + view.setCamera(isometricCam); gameObjects.getChildren().addAll( ModelFactory.importModel(ModelType.OCEAN).getAssets(), @@ -88,6 +101,8 @@ public class GameView3D extends GameView{ scene.addEventHandler(KeyEvent.KEY_PRESSED, this::cameraMovement); } }); + + } @Override @@ -245,40 +260,45 @@ public class GameView3D extends GameView{ } public void cameraMovement(KeyEvent event) { - switch (event.getCode()) { - case NUMPAD8: - camera.getTransforms().addAll(new Rotate(0.5, new Point3D(1,0,0))); + GameKeyBind keyBinds = GameKeyBind.getInstance(); + KeyAction keyPressed = keyBinds.getKeyAction(event.getCode()); + switch (keyPressed) { + case ZOOM_IN: + ((RaceCamera) view.getCamera()).zoomIn(); break; - case NUMPAD2: - camera.getTransforms().addAll(new Rotate(-0.5, new Point3D(1,0,0))); + case ZOOM_OUT: + ((RaceCamera) view.getCamera()).zoomOut(); break; - case NUMPAD4: - camera.getTransforms().addAll(new Rotate(-0.5, new Point3D(0,1,0))); + case FORWARD: + ((RaceCamera) view.getCamera()).panUp(); break; - case NUMPAD6: - camera.getTransforms().addAll(new Rotate(0.5, new Point3D(0,1,0))); + case BACKWARD: + ((RaceCamera) view.getCamera()).panDown(); break; - case Z: - camera.getTransforms().addAll(new Translate(0, 0, 1.5)); + case LEFT: + ((RaceCamera) view.getCamera()).panLeft(); break; - case X: - camera.getTransforms().addAll(new Translate(0, 0, -1.5)); + case RIGHT: + ((RaceCamera) view.getCamera()).panRight(); break; - case W: - camera.getTransforms().addAll(new Translate(0, -1, 0)); - break; - case S: - camera.getTransforms().addAll(new Translate(0, 1, 0)); - break; - case A: - camera.getTransforms().addAll(new Translate(-1, 0, 0)); - break; - case D: - camera.getTransforms().addAll(new Translate(1, 0, 0)); + case VIEW: + toggleCamera(); break; } } + private void toggleCamera() { + Camera currCamera = view.getCamera(); + + if (currCamera.equals(isometricCam)) { + view.setCamera(topDownCam); + } else if (currCamera.equals(topDownCam)) { + view.setCamera(chaseCam); + } else { + view.setCamera(isometricCam); + } + } + /** * Draws all the boats. * @param yachts The yachts to set in the race @@ -299,6 +319,12 @@ public class GameView3D extends GameView{ Point2D p2d = scaledPoint.findScaledXY(lat, lon); bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir); }); + + if (clientYacht.getSourceId().equals( + ViewManager.getInstance().getGameClient().getServerThread().getClientId())) { + ((ChaseCamera) chaseCam).setPlayerBoat(newBoat); + ((TopDownCamera) topDownCam).setPlayerBoat(newBoat); + } } Platform.runLater(() -> { gameObjects.getChildren().addAll(wakes); diff --git a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java new file mode 100644 index 00000000..0647a473 --- /dev/null +++ b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java @@ -0,0 +1,130 @@ +package seng302.visualiser.cameras; + +import java.util.Arrays; +import javafx.beans.property.DoubleProperty; +import javafx.collections.ObservableList; +import javafx.geometry.Point3D; +import javafx.scene.PerspectiveCamera; +import javafx.scene.transform.Rotate; +import javafx.scene.transform.Transform; +import javafx.scene.transform.Translate; +import seng302.visualiser.fxObjects.assets_3D.BoatObject; + + +public class ChaseCamera extends PerspectiveCamera implements RaceCamera { + + private final Double VERTICAL_PAN_LIMIT = 20.0; + private final Double NEAR_ZOOM_LIMIT = -15.0; + private final Double FAR_ZOOM_LIMIT = -125.0; + + private final Double ZOOM_STEP = 2.5; + private final Double PAN_STEP = 2.5; + + private ObservableList transforms; + private BoatObject playerBoat; + + private Double zoomFactor; + private Double horizontalPan; + private Double verticalPan; + + + public ChaseCamera() { + super(true); + transforms = this.getTransforms(); + + zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0; + this.horizontalPan = 0.0; + this.verticalPan = 0.0; + } + + /** + * Sets a player boat object to observe and update the camera with. + * + * @param playerBoat The player boat to be observed. + */ + public void setPlayerBoat(BoatObject playerBoat) { + this.playerBoat = playerBoat; + + for (DoubleProperty o : Arrays + .asList(playerBoat.getRotationProperty(), playerBoat.layoutYProperty(), + playerBoat.layoutXProperty())) { + o.addListener((obs, oldVal, newVal) -> repositionCamera()); + } + } + + /** + * Moves the camera to a new position after some change (Zooming or Panning) + */ + private void repositionCamera() { + transforms.clear(); + transforms.addAll( + new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), 0), + new Rotate(playerBoat.getRotationProperty().getValue() + horizontalPan, + new Point3D(0, 0, 1)), + new Rotate(60 + verticalPan, new Point3D(1, 0, 0)), + new Translate(0, 0, zoomFactor) + ); + } + + /** + * Adjusts the zoom amount (camera depth) by some adjustment value + * @param adjustment the adjustment to be made to the camera + */ + private void adjustZoomFactor(Double adjustment) { + if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) { + zoomFactor = zoomFactor + adjustment; + repositionCamera(); + } + } + + /** + * Adjusts the Vertical Panning of the Camera + * @param adjustment the adjustment to be made to the camera + */ + private void adjustVerticalPan(Double adjustment) { + if (verticalPan + adjustment >= -VERTICAL_PAN_LIMIT + && verticalPan + adjustment <= VERTICAL_PAN_LIMIT) { + verticalPan += adjustment; + repositionCamera(); + } + } + + /** + * Adjusts the Horizontal Panning of the Camera. + * @param adjustment the adjustment to be made to the camera + */ + private void adjustHorizontalPan(Double adjustment) { + this.horizontalPan += adjustment; + repositionCamera(); + } + + @Override + public void zoomIn() { + adjustZoomFactor(ZOOM_STEP); + } + + @Override + public void zoomOut() { + adjustZoomFactor(-ZOOM_STEP); + } + + @Override + public void panLeft() { + adjustHorizontalPan(-PAN_STEP); + } + + @Override + public void panRight() { + adjustHorizontalPan(PAN_STEP); + } + + @Override + public void panUp() { + adjustVerticalPan(-PAN_STEP); + } + + @Override + public void panDown() { + adjustVerticalPan(PAN_STEP); + } +} diff --git a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java new file mode 100644 index 00000000..85a0e502 --- /dev/null +++ b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java @@ -0,0 +1,113 @@ +package seng302.visualiser.cameras; + +import javafx.collections.ObservableList; +import javafx.geometry.Point3D; +import javafx.scene.PerspectiveCamera; +import javafx.scene.transform.Rotate; +import javafx.scene.transform.Transform; +import javafx.scene.transform.Translate; + +public class IsometricCamera extends PerspectiveCamera implements RaceCamera { + + private final Double MIN_X = -120.0; + private final Double MAX_X = 125.0; + + private final Double MIN_Y = 40.0; + private final Double MAX_Y = 170.0; + + private final Double PAN_LIMIT = 160.0; + private final Double NEAR_ZOOM_LIMIT = -50.0; + private final Double FAR_ZOOM_LIMIT = -160.0; + + private Double horizontalPan; + private Double verticalPan; + private Double zoomFactor; + + private ObservableList transforms; + + public IsometricCamera(Double cameraStartX, Double cameraStartY) { + super(true); + transforms = this.getTransforms(); + + zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0; + horizontalPan = cameraStartX; + verticalPan = cameraStartY; + + updateCamera(); + } + + /** + * Moves the camera to a new position after some change (Zooming or Panning) + */ + private void updateCamera() { + transforms.clear(); + transforms.addAll( + new Translate(horizontalPan, verticalPan, zoomFactor), + new Rotate(30, new Point3D(1, 0, 0)) + ); + } + + /** + * Adjusts the zoom amount (camera depth) by some adjustment value + * + * @param adjustment the adjustment to be made to the camera + */ + private void adjustZoomFactor(Double adjustment) { + if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) { + zoomFactor = zoomFactor + adjustment; + updateCamera(); + } + } + + /** + * Adjusts the Vertical Panning of the Camera + * @param adjustment the adjustment to be made to the camera + */ + private void adjustVerticalPan(Double adjustment) { + if (verticalPan + adjustment >= MIN_Y && verticalPan + adjustment <= MAX_Y) { + verticalPan += adjustment; + updateCamera(); + } + } + + /** + * Adjusts the Horizontal Panning of the Camera. + * @param adjustment the adjustment to be made to the camera + */ + private void adjustHorizontalPan(Double adjustment) { + if (horizontalPan + adjustment >= MIN_X && horizontalPan + adjustment <= MIN_Y) { + this.horizontalPan += adjustment; + updateCamera(); + } + } + + @Override + public void zoomIn() { + adjustZoomFactor(-2.5); + } + + @Override + public void zoomOut() { + adjustZoomFactor(2.5); + } + + @Override + public void panLeft() { + adjustHorizontalPan(-2.5); + } + + @Override + public void panRight() { + adjustHorizontalPan(2.5); + } + + @Override + public void panUp() { + adjustVerticalPan(-2.5); + } + + @Override + public void panDown() { + adjustVerticalPan(2.5); + } +} diff --git a/src/main/java/seng302/visualiser/cameras/RaceCamera.java b/src/main/java/seng302/visualiser/cameras/RaceCamera.java new file mode 100644 index 00000000..7416ae4a --- /dev/null +++ b/src/main/java/seng302/visualiser/cameras/RaceCamera.java @@ -0,0 +1,18 @@ +package seng302.visualiser.cameras; + + +public interface RaceCamera { + + void zoomIn(); + + void zoomOut(); + + void panLeft(); + + void panRight(); + + void panUp(); + + void panDown(); + +} diff --git a/src/main/java/seng302/visualiser/cameras/TopDownCamera.java b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java new file mode 100644 index 00000000..72d58707 --- /dev/null +++ b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java @@ -0,0 +1,123 @@ +package seng302.visualiser.cameras; + + +import java.util.Arrays; +import javafx.beans.property.DoubleProperty; +import javafx.collections.ObservableList; +import javafx.scene.PerspectiveCamera; +import javafx.scene.transform.Transform; +import javafx.scene.transform.Translate; +import seng302.visualiser.fxObjects.assets_3D.BoatObject; + +public class TopDownCamera extends PerspectiveCamera implements RaceCamera { + + private final Double PAN_LIMIT = 30.0; + private final Double NEAR_ZOOM_LIMIT = -30.0; + private final Double FAR_ZOOM_LIMIT = -130.0; + private final Double ZOOM_STEP = 2.5; + + private ObservableList transforms; + private BoatObject playerBoat; + + private Double zoomFactor; + private Double horizontalPan; + private Double verticalPan; + + public TopDownCamera() { + super(true); + transforms = this.getTransforms(); + + zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0; + horizontalPan = 0.0; + verticalPan = 0.0; + } + + /** + * Sets a player boat object to observe and update the camera with. + * + * @param playerBoat The player boat to be observed. + */ + public void setPlayerBoat(BoatObject playerBoat) { + this.playerBoat = playerBoat; + + for (DoubleProperty o : Arrays + .asList(playerBoat.layoutXProperty(), playerBoat.layoutYProperty())) { + o.addListener((obs, oldVal, newVal) -> updateCamera()); + } + } + + /** + * Moves the camera to a new position after some change (Zooming or Panning) + */ + private void updateCamera() { + transforms.clear(); + transforms.addAll( + new Translate(playerBoat.getLayoutX() + horizontalPan, + playerBoat.getLayoutY() + verticalPan, zoomFactor) + ); + } + + /** + * Adjusts the zoom amount (camera depth) by some adjustment value + * @param adjustment the adjustment to be made to the camera + */ + private void adjustZoomFactor(Double adjustment) { + if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) { + zoomFactor = zoomFactor + adjustment; + updateCamera(); + } + } + + /** + * Adjusts the Vertical Panning of the Camera + * @param adjustment the adjustment to be made to the camera + */ + private void adjustVerticalPan(Double adjustment) { + if (verticalPan + adjustment >= -PAN_LIMIT && verticalPan + adjustment <= PAN_LIMIT) { + verticalPan += adjustment; + updateCamera(); + } + } + + /** + * Adjusts the Horizontal Panning of the Camera. + * @param adjustment the adjustment to be made to the camera + */ + private void adjustHorizontalPan(Double adjustment) { + if (horizontalPan + adjustment >= -PAN_LIMIT && horizontalPan + adjustment <= PAN_LIMIT) { + horizontalPan += adjustment; + updateCamera(); + } + } + + @Override + public void zoomIn() { + adjustZoomFactor(ZOOM_STEP); + } + + @Override + public void zoomOut() { + adjustZoomFactor(-ZOOM_STEP); + } + + @Override + public void panLeft() { + adjustHorizontalPan(-1.0); + } + + @Override + public void panRight() { + adjustHorizontalPan(1.0); + } + + @Override + public void panUp() { + adjustVerticalPan(-1.0); + } + + @Override + public void panDown() { + adjustVerticalPan(1.0); + } + +} diff --git a/src/main/java/seng302/visualiser/controllers/ViewManager.java b/src/main/java/seng302/visualiser/controllers/ViewManager.java index 8f98824c..2f843a4c 100644 --- a/src/main/java/seng302/visualiser/controllers/ViewManager.java +++ b/src/main/java/seng302/visualiser/controllers/ViewManager.java @@ -28,6 +28,7 @@ import seng302.utilities.BonjourInstallChecker; import seng302.utilities.Sounds; import seng302.visualiser.GameClient; import seng302.visualiser.controllers.dialogs.KeyBindingDialogController; +import seng302.visualiser.controllers.dialogs.PopupDialogController; public class ViewManager { @@ -220,6 +221,7 @@ public class ViewManager { .getController(); keyBindingDialogController.setGameClient(this.gameClient); keyBindingDialog.show(); + decorator.requestFocus(); Sounds.playButtonClick(); } } @@ -229,6 +231,26 @@ public class ViewManager { keyBindingDialog.close(); } + public PopupDialogController showPopupDialog() { + FXMLLoader dialogContent = new FXMLLoader( + getClass().getResource("/views/dialogs/PopupDialog.fxml")); + for (Node node : decorator.getChildren()) { + if (node instanceof StackPane) { + try { + JFXDialog dialog = new JFXDialog((StackPane) node, dialogContent.load(), + DialogTransition.CENTER); + PopupDialogController popupDialogController = dialogContent.getController(); + popupDialogController.setPopupDialog(dialog); + dialog.show(); + return popupDialogController; + } catch (IOException e) { + logger.error("Cannot load Popup dialog"); + } + } + } + return null; + } + /** * Show a snackbar at the bottom of the app for 1 second. * diff --git a/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java b/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java index 2a286ec4..1f638c5a 100644 --- a/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java +++ b/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java @@ -48,6 +48,16 @@ public class KeyBindingDialogController implements Initializable { private Label downwindLabel; @FXML private JFXToggleButton turningToggle; + @FXML + private JFXButton viewButton; + @FXML + private JFXButton rightButton; + @FXML + private JFXButton leftButton; + @FXML + private JFXButton forwardButton; + @FXML + private JFXButton backwardButton; //---------FXML END---------// private GameKeyBind gameKeyBind; @@ -60,7 +70,8 @@ public class KeyBindingDialogController implements Initializable { gameKeyBind = GameKeyBind.getInstance(); buttons = new ArrayList<>(); Collections.addAll(buttons, - zoomInbtn, zoomOutBtn, vmgBtn, sailInOutBtn, tackGybeBtn, upwindBtn, downwindBtn); + zoomInbtn, zoomOutBtn, vmgBtn, sailInOutBtn, tackGybeBtn, upwindBtn, downwindBtn, + viewButton, rightButton, leftButton, forwardButton, backwardButton); bindButtonWithAction(); loadKeyBind(); @@ -79,9 +90,6 @@ public class KeyBindingDialogController implements Initializable { }); closeLabel.setOnMouseClicked(event -> ViewManager.getInstance().closeKeyBindingDialog()); - - keyBindingDialogHeader.setFocusTraversable(true); - keyBindingDialogHeader.requestFocus(); } /** @@ -106,7 +114,7 @@ public class KeyBindingDialogController implements Initializable { */ private void bindButtonWithAction() { buttonActionMap = new HashMap<>(); - for (int i = 0; i < 7; i++) { + for (int i = 0; i < 12; i++) { buttonActionMap.put(buttons.get(i), KeyAction.getType(i + 1)); } } @@ -149,6 +157,7 @@ public class KeyBindingDialogController implements Initializable { + "-fx-background-color: -fx-pp-front-color; " + "-fx-text-fill: -fx-pp-theme-color; " + "-fx-font-size: 13;"); + keyBindingDialogHeader.requestFocus(); } /** diff --git a/src/main/java/seng302/visualiser/controllers/dialogs/PopupDialogController.java b/src/main/java/seng302/visualiser/controllers/dialogs/PopupDialogController.java new file mode 100644 index 00000000..6d294e50 --- /dev/null +++ b/src/main/java/seng302/visualiser/controllers/dialogs/PopupDialogController.java @@ -0,0 +1,56 @@ +package seng302.visualiser.controllers.dialogs; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXDialog; +import java.net.URL; +import java.util.ResourceBundle; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; + +public class PopupDialogController implements Initializable { + + @FXML + private Label headerLabel; + @FXML + private Label contentLabel; + @FXML + private Label closeLabel; + @FXML + private JFXButton optionButton; + + @FXML + private JFXDialog popupDialog; + + @Override + public void initialize(URL location, ResourceBundle resources) { + + } + + public void setContent(String content) { + this.contentLabel.setText(content); + } + + public void setHeader(String header) { + this.headerLabel.setText(header); + } + + public void setOptionButton(JFXButton jfxButton) { + this.optionButton = jfxButton; + } + + public void setOptionButtonText(String text) { + this.optionButton.setText(text); + } + + public void setOptionButtonEventHandler(EventHandler eventHandler) { + this.optionButton.setOnMouseClicked(eventHandler); + } + + public void setPopupDialog(JFXDialog popupDialog) { + this.popupDialog = popupDialog; + this.closeLabel.setOnMouseClicked(event -> this.popupDialog.close()); + } +} diff --git a/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatObject.java b/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatObject.java index abf969a8..025ab820 100644 --- a/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatObject.java +++ b/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatObject.java @@ -3,6 +3,7 @@ package seng302.visualiser.fxObjects.assets_3D; import java.util.ArrayList; import java.util.List; import javafx.application.Platform; +import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.geometry.Point3D; import javafx.scene.Group; import javafx.scene.paint.Color; @@ -30,12 +31,15 @@ public class BoatObject extends Group { private Boolean isSelected = false; private Rotate rotation = new Rotate(0, new Point3D(0,0,1)); + private ReadOnlyDoubleWrapper rotationProperty; + private List selectedBoatListenerListeners = new ArrayList<>(); /** * Creates a BoatGroup with the default triangular boat polygon. */ public BoatObject(BoatMeshType boatMeshType) { + rotationProperty = new ReadOnlyDoubleWrapper(0.0); boatAssets = ModelFactory.boatGameView(boatMeshType, colour); boatAssets.hideSail(); boatAssets.getAssets().getTransforms().addAll( @@ -83,6 +87,7 @@ public class BoatObject extends Group { private void rotateTo(double heading, boolean sailsIn, double windDir) { + rotationProperty.set(heading); rotation.setAngle(heading); wake.getTransforms().setAll(new Rotate(heading, new Point3D(0,0,1))); if (sailsIn) { @@ -130,4 +135,8 @@ public class BoatObject extends Group { public void addSelectedBoatListener(SelectedBoatListener sbl) { selectedBoatListenerListeners.add(sbl); } + + public ReadOnlyDoubleWrapper getRotationProperty() { + return rotationProperty; + } } \ No newline at end of file diff --git a/src/main/resources/css/Master.css b/src/main/resources/css/Master.css index cb50a645..478d7cf0 100644 --- a/src/main/resources/css/Master.css +++ b/src/main/resources/css/Master.css @@ -51,6 +51,11 @@ /********* customised scroll bar for scroll pane ***********/ +.scroll-pane { + -fx-focus-traversable: false; + -fx-border-style: none; +} + /* The main scrollbar **track** CSS class */ .scroll-bar:horizontal .track, .scroll-bar:vertical .track { diff --git a/src/main/resources/css/dialogs/KeyBindingDialog.css b/src/main/resources/css/dialogs/KeyBindingDialog.css index 8d09e130..6c7bff26 100644 --- a/src/main/resources/css/dialogs/KeyBindingDialog.css +++ b/src/main/resources/css/dialogs/KeyBindingDialog.css @@ -9,8 +9,13 @@ } #closeLabel:hover { - -fx-text-fill: -fx-pp-theme-color; - -fx-font-size: 33; + -fx-text-fill: red; + -fx-font-size: 33px; +} + +.sectionLabel { + -fx-text-fill: -fx-pp-dark-text-color; + -fx-font-size: 20px; } JFXButton { diff --git a/src/main/resources/css/dialogs/Popup.css b/src/main/resources/css/dialogs/Popup.css new file mode 100644 index 00000000..d0dd364b --- /dev/null +++ b/src/main/resources/css/dialogs/Popup.css @@ -0,0 +1,33 @@ +#headerLabel { + -fx-font-size: 20px; + -fx-text-fill: -fx-pp-dark-text-color; +} + +#closeLabel { + -fx-font-size: 22px; + -fx-text-fill: -fx-pp-dark-text-color; +} + +#closeLabel:hover { + -fx-font-size: 24px; + -fx-text-fill: red; +} + +#contentLabel { + -fx-font-size: 22px; + -fx-text-fill: -fx-pp-dark-text-color; +} + +#optionButton { + -fx-background-color: -fx-pp-theme-color; + -fx-text-fill: -fx-pp-light-text-color; + -fx-font-size: 18px; + -fx-effect: -fx-pp-dropshadow-light; + -fx-max-height: 55; + -fx-focus-traversable: false; +} + +#optionButton:hover { + -fx-font-size: 20px !important; + -fx-background-color: -fx-pp-light-theme-color; +} \ No newline at end of file diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 879b2f22..4ee22b65 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -7,17 +7,16 @@ - + @@ -38,17 +37,15 @@ valignment="BOTTOM" vgrow="SOMETIMES"/> - + - + - + @@ -80,25 +77,20 @@ - - + + - + - + - + - + @@ -121,22 +113,21 @@ minWidth="90.0" prefWidth="90.0"/> - + - - + - + @@ -158,29 +149,27 @@ prefHeight="150.0" prefWidth="240.0" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="BOTTOM"> - - + - + -