Merge branch 'storyA_sounds' into storyD-3D

# Conflicts:
#	racevisionGame/src/main/java/visualiser/layout/SeaSurface.java
#	racevisionGame/src/main/java/visualiser/layout/SkyBox.java
This commit is contained in:
Joseph Gardner
2017-09-14 14:16:53 +12:00
61 changed files with 1279 additions and 945 deletions
@@ -0,0 +1,39 @@
package visualiser.Commands.VisualiserRaceCommands;
import javafx.scene.media.AudioClip;
import mock.model.commandFactory.Command;
import network.Messages.YachtEvent;
import visualiser.model.VisualiserRaceState;
/**
* Created by zwu18 on 4/09/17.
*/
public class BoatCollisionCommand implements Command {
YachtEvent yachtEvent;
VisualiserRaceState visualiserRace;
public BoatCollisionCommand(YachtEvent yachtEvent, VisualiserRaceState visualiserRace){
this.yachtEvent = yachtEvent;
this.visualiserRace = visualiserRace;
}
@Override
public void execute() {
if(visualiserRace.getPlayerBoatID()==yachtEvent.getSourceID()){
System.out.println("I crashed!");
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/collision.wav").toExternalForm());
sound.play();
} else {
System.out.println("Someone else crashed!");
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/quietcollision.wav").toExternalForm());
sound.play();
}
//System.out.println("Collision command executed!");
}
}
@@ -1,5 +1,6 @@
package visualiser.Commands.VisualiserRaceCommands;
import javafx.scene.media.AudioClip;
import mock.model.commandFactory.Command;
import network.Messages.BoatStatus;
import network.Messages.Enums.BoatStatusEnum;
@@ -176,6 +177,10 @@ public class RaceStatusCommand implements Command {
//Record order in which boat finished leg.
visualiserRace.getLegCompletionOrder().get(boat.getCurrentLeg()).add(boat);
//play sound
AudioClip sound = new AudioClip(getClass().getResource("/visualiser/sounds/passmark.wav").toExternalForm());
sound.play();
//Update boat.
boat.setCurrentLeg(leg);
boat.setTimeAtLastMark(visualiserRace.getRaceClock().getCurrentTime());
@@ -23,7 +23,9 @@ public class VisualiserRaceCommandFactory {
switch (message.getType()) {
case BOATLOCATION: return new BoatLocationCommand((BoatLocation) message, visualiserRace);
case BOATLOCATION:
//System.out.println("Boat location received");
return new BoatLocationCommand((BoatLocation) message, visualiserRace);
case RACESTATUS: return new RaceStatusCommand((RaceStatus) message, visualiserRace);
@@ -31,6 +33,11 @@ public class VisualiserRaceCommandFactory {
case ASSIGN_PLAYER_BOAT: return new AssignPlayerBoatCommand((AssignPlayerBoat) message, visualiserRace);
case YACHTEVENTCODE:
return new BoatCollisionCommand((YachtEvent) message, visualiserRace);
default: throw new CommandConstructionException("Could not create VisualiserRaceCommand. Unrecognised or unsupported MessageType: " + message.getType());
}
@@ -1,28 +1,29 @@
package visualiser.Controllers;
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
import javafx.animation.AnimationTimer;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.shape.MeshView;
import javafx.stage.Stage;
import javafx.scene.media.AudioClip;
import mock.app.Event;
import org.xml.sax.SAXException;
import mock.exceptions.EventConstructionException;
import visualiser.layout.Plane3D;
import visualiser.layout.SeaSurface;
import visualiser.layout.Subject3D;
import visualiser.layout.View3D;
import shared.exceptions.InvalidBoatDataException;
import shared.exceptions.InvalidRaceDataException;
import shared.exceptions.InvalidRegattaDataException;
import shared.exceptions.XMLReaderException;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.net.Socket;
import java.net.URL;
import java.util.Optional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -33,69 +34,48 @@ import java.util.logging.Logger;
public class HostController extends Controller {
// @FXML
// TextField gameNameField;
//
// @FXML
// TextField hostNameField;
@FXML
TextField gameNameField;
@FXML
private ImageView imageView;
TextField hostNameField;
@FXML
AnchorPane hostWrapper;
@FXML
AnchorPane imagePane;
Button previousButton;
@FXML
SplitPane splitPane;
Button nextButton;
@FXML
AnchorPane specPane;
@FXML
GridPane playerContainer;
ImageView mapImage;
private Event game;
private View3D view3D;
private ArrayList<Image> listOfMaps;
private int currentMapIndex = 0;
@Override
public void initialize(URL location, ResourceBundle resources) {
ObservableList<Subject3D> subjects = FXCollections.observableArrayList();
public void initialize(URL location, ResourceBundle resources){
Image ac35Map = new Image(getClass().getClassLoader().getResourceAsStream("images/AC35_Racecourse_MAP.png"));
Image oMap = new Image(getClass().getClassLoader().getResourceAsStream("images/oMapLayout.png"));
Image iMap = new Image(getClass().getClassLoader().getResourceAsStream("images/iMapLayout.png"));
Image mMap = new Image(getClass().getClassLoader().getResourceAsStream("images/mMapLayout.png"));
view3D = new View3D();
view3D.setItems(subjects);
playerContainer.add(view3D, 0,0);
URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl");
StlMeshImporter importer = new StlMeshImporter();
importer.read(asset);
Subject3D subject = new Subject3D(new MeshView(importer.getImport()));
subjects.add(subject);
view3D.setDistance(50);
view3D.setYaw(45);
view3D.setPitch(20);
AnimationTimer rotate = new AnimationTimer() {
@Override
public void handle(long now) {
subject.setHeading(subject.getHeading().getAngle() + 0.1);
}
};
rotate.start();
listOfMaps = new ArrayList(Arrays.asList(ac35Map, oMap, iMap, mMap));
mapImage.setImage(listOfMaps.get(currentMapIndex));
}
/**
* Hosts a game
* @throws IOException if socket cannot be connected to
*/
public void hostGamePressed() {
public void hostGamePressed() throws IOException{
try {
this.game = new Event(false);
this.game = new Event(false, currentMapIndex);
connectSocket("localhost", 4942);
} catch (EventConstructionException e) {
Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e);
@@ -128,10 +108,7 @@ public class HostController extends Controller {
* Hosts a game.
*/
public void hostGame(){
splitPane.setResizableWithParent(specPane, false);
splitPane.lookupAll(".split-pane-divider").stream().forEach(div -> div.setMouseTransparent(true));
imageView.fitWidthProperty().bind(imagePane.widthProperty());
imageView.fitHeightProperty().bind(imagePane.heightProperty());
mapImage.fitWidthProperty().bind(((Stage) mapImage.getScene().getWindow()).widthProperty().multiply(0.6));
hostWrapper.setVisible(true);
}
@@ -139,23 +116,33 @@ public class HostController extends Controller {
* Menu button pressed. Prompt alert then return to menu
*/
public void menuBtnPressed(){
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Quitting race");
alert.setContentText("Do you wish to quit the race?");
alert.setHeaderText("You are about to quit the race");
Optional<ButtonType> result = alert.showAndWait();
if(result.get() == ButtonType.OK){
hostWrapper.setVisible(false);
parent.enterTitle();
}
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
hostWrapper.setVisible(false);
parent.enterTitle();
}
/**
* Start button pressed. Currently only prints out start
*/
public void startBtnPressed(){
//System.out.println("Should start the race. This button is only visible for the host");
hostGamePressed();
public void nextImage(){
increaseIndex();
mapImage.setImage(listOfMaps.get(currentMapIndex));
}
public void previousImage(){
decreaseIndex();
mapImage.setImage(listOfMaps.get(currentMapIndex));
}
private void increaseIndex(){
currentMapIndex = (currentMapIndex + 1)%listOfMaps.size();
}
private void decreaseIndex(){
currentMapIndex = ((((currentMapIndex - 1)%listOfMaps.size())+listOfMaps.size())%listOfMaps.size());
}
public void setGameType(int gameType){
this.currentMapIndex = gameType;
}
public int getGameType(){ return this.currentMapIndex; }
}
@@ -8,6 +8,7 @@ import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.media.AudioClip;
import visualiser.model.RaceConnection;
import java.io.IOException;
@@ -39,6 +40,8 @@ public class LobbyController extends Controller {
private ObservableList<RaceConnection> connections;
private AudioClip sound;
@Override
public void initialize(URL location, ResourceBundle resources) {
@@ -66,6 +69,8 @@ public class LobbyController extends Controller {
* Refreshes the connections in the lobby
*/
public void refreshBtnPressed(){
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
for(RaceConnection connection: connections) {
connection.check();
}
@@ -93,6 +98,8 @@ public class LobbyController extends Controller {
}
public void menuBtnPressed(){
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
lobbyWrapper.setVisible(false);
parent.enterTitle();
}
@@ -101,6 +108,8 @@ public class LobbyController extends Controller {
* adds a new connection
*/
public void addConnectionPressed(){
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
String hostName = addressFld.getText();
String portString = portFld.getText();
try{
@@ -3,6 +3,8 @@ package visualiser.Controllers;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.layout.AnchorPane;
import javafx.scene.media.AudioClip;
import visualiser.app.App;
import visualiser.gameController.ControllerClient;
import visualiser.model.VisualiserBoat;
import visualiser.model.VisualiserRaceEvent;
@@ -26,6 +28,8 @@ public class MainController extends Controller {
@FXML private HostController hostController;
@FXML private LobbyController lobbyController;
private AudioClip sound;
/**
* Ctor.
@@ -53,6 +57,8 @@ public class MainController extends Controller {
* @param isHost is connection a host
*/
public void enterLobby(Socket socket, Boolean isHost) {
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
startController.enterLobby(socket, isHost);
}
@@ -74,18 +80,38 @@ public class MainController extends Controller {
/**
* Transitions into lobby screen
*/
public void enterLobby(){ lobbyController.enterLobby(); }
public void enterLobby(){
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
lobbyController.enterLobby(); }
/**
* Transitions into host game screen
*/
public void hostGame(){ hostController.hostGame(); }
public void hostGame(){
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
hostController.hostGame(); }
/**
* Sets up the css for the start of the program
*/
public void startCss(){titleController.setDayMode();}
/**
* host controller host a game
* @throws IOException throws exception
*/
public void beginGame() throws IOException {
hostController.hostGamePressed();
}
public void setGameType(int gameType){
hostController.setGameType(gameType);
}
public int getGameType(){ return hostController.getGameType(); }
/**
* Main Controller for the applications will house the menu and the displayed pane.
*
@@ -3,6 +3,7 @@ package visualiser.Controllers;
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
@@ -18,14 +19,20 @@ import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.Shape3D;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Translate;
import javafx.util.Callback;
import network.Messages.Enums.RaceStatusEnum;
import shared.dataInput.RaceDataSource;
import shared.model.Leg;
import shared.model.Mark;
import shared.exceptions.BoatNotFoundException;
import shared.model.*;
import visualiser.app.App;
import visualiser.enums.TutorialState;
import visualiser.gameController.ControllerClient;
import visualiser.gameController.Keys.ControlKey;
import visualiser.layout.*;
@@ -34,8 +41,7 @@ import visualiser.utils.GPSConverter;
import java.io.IOException;
import java.net.URL;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -58,6 +64,15 @@ public class RaceController extends Controller {
private boolean isHost;
private TutorialState currentState;
private ArrayList<TutorialState> tutorialStates;
private boolean isTutorial = false;
private String keyToPress;
/**
* state of the info table
*/
@@ -66,6 +81,9 @@ public class RaceController extends Controller {
private View3D view3D;
private ObservableList<Subject3D> viewSubjects;
private Subject3D nextMarkArrow;
private ChangeListener<? super GPSCoordinate> pointToMark;
/**
* The arrow controller.
*/
@@ -75,6 +93,9 @@ public class RaceController extends Controller {
@FXML private SplitPane racePane;
@FXML private Label tutorialText;
/**
* This is the pane we place the actual arrow control inside of.
*/
@@ -94,6 +115,7 @@ public class RaceController extends Controller {
* Ctor.
*/
public RaceController() {
this.nextMarkArrow = new Annotation3D(new Box(1,3,0));
}
@Override
@@ -110,13 +132,27 @@ public class RaceController extends Controller {
if(controlKey != null) {
try {
controlKey.onAction(); // Change key state if applicable
//Check if current race is a tutorial
if (isTutorial){
//Check if current tutorial state has the same boat protocol code as key press
if (controlKey.getProtocolCode().equals(currentState.getAction())){
//Update tutorial
checkTutorialState();
}
}
controllerClient.sendKey(controlKey);
event.consume();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Logger.getGlobal().log(Level.WARNING, "RaceController was interrupted on thread: " + Thread.currentThread() + "while sending: " + controlKey, e);
} catch (Exception e) {
e.printStackTrace();
}
}
if(event.getCode() == KeyCode.ESCAPE) {
try {
@@ -237,7 +273,7 @@ public class RaceController extends Controller {
// Position and add each mark to view
for(Mark mark: race.getVisualiserRaceState().getMarks()) {
MeshView mesh = new MeshView(importerMark.getImport());
Subject3D markModel = new Subject3D(mesh);
Subject3D markModel = new Subject3D(mesh, mark.getSourceID());
markModel.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
markModel.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
@@ -252,7 +288,7 @@ public class RaceController extends Controller {
} else {
mesh = new MeshView(importerBurgerBoat.getImport());
}
Subject3D boatModel = new Subject3D(mesh);
Subject3D boatModel = new Subject3D(mesh, boat.getSourceID());
viewSubjects.add(boatModel);
@@ -266,10 +302,27 @@ public class RaceController extends Controller {
}
};
trackBoat.start();
Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark();
view3D.getShape(nextMark.getMark1().getSourceID()).setMaterial(markColor);
if(nextMark.getMark2() != null) {
view3D.getShape(nextMark.getMark2().getSourceID()).setMaterial(markColor);
}
boat.legProperty().addListener((o, prev, curr) -> swapColours(curr));
}
// Fix initial bird's-eye position
view3D.updatePivot(new Translate(250, 0, 210));
view3D.targetProperty().addListener((o, prev, curr)-> {
if(curr != null && visualiserRace.getVisualiserRaceState().isVisualiserBoat(curr.getSourceID())) {
addThirdPersonAnnotations(curr);
} else {
removeThirdPersonAnnotations(prev);
}
});
// Bind zooming to scrolling
view3D.setOnScroll(e -> {
view3D.updateDistance(e.getDeltaY());
@@ -281,9 +334,33 @@ public class RaceController extends Controller {
if(key != null) {
switch (key.toString()) {
case "Zoom In":
//Check if race is a tutorial
if (isTutorial) {
//Check if the current tutorial state is zoom-in
if (currentState.equals(TutorialState.ZOOMIN)) {
try {
//Update tutorial
checkTutorialState();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
view3D.updateDistance(-10);
break;
case "Zoom Out":
//Check if race is a tutorial
if(isTutorial) {
//Check if current tutorial state is zoom-out
if (currentState.equals(TutorialState.ZOOMOUT)) {
try {
//Update tutorial
checkTutorialState();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
view3D.updateDistance(10);
break;
}
@@ -291,6 +368,64 @@ public class RaceController extends Controller {
});
}
private void addThirdPersonAnnotations(Subject3D subject3D) {
viewSubjects.add(nextMarkArrow);
try {
VisualiserBoat boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
this.pointToMark = (o, prev, curr) -> {
CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
nextMarkArrow.setX(view3D.getPivot().getX());
nextMarkArrow.setY(view3D.getPivot().getY());
nextMarkArrow.setZ(view3D.getPivot().getZ() + 10);
nextMarkArrow.setHeading(headingToMark.degrees());
};
boat.positionProperty().addListener(pointToMark);
} catch (BoatNotFoundException e) {
e.printStackTrace();
}
}
private void removeThirdPersonAnnotations(Subject3D subject3D) {
viewSubjects.remove(nextMarkArrow);
try {
VisualiserBoat boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
boat.positionProperty().removeListener(pointToMark);
} catch (BoatNotFoundException e) {
e.printStackTrace();
}
}
/**
* Swap the colour of the next mark to pass with the last mark passed
* @param leg boat has started on
*/
private void swapColours(Leg leg) {
CompoundMark start = leg.getStartCompoundMark();
CompoundMark end = leg.getEndCompoundMark();
Shape3D start1 = view3D.getShape(start.getMark1().getSourceID());
Shape3D end1 = view3D.getShape(end.getMark1().getSourceID());
Material nextMark = start1.getMaterial();
Material lastMark = end1.getMaterial();
start1.setMaterial(lastMark);
if(start.getMark2() != null) {
Shape3D start2 = view3D.getShape(start.getMark2().getSourceID());
start2.setMaterial(lastMark);
}
end1.setMaterial(nextMark);
if(end.getMark2() != null) {
Shape3D end2 = view3D.getShape(end.getMark2().getSourceID());
end2.setMaterial(nextMark);
}
}
/**
* Initialises the frame rate functionality. This allows for toggling the frame rate, and connect the fps label to the race's fps property.
@@ -468,6 +603,25 @@ public class RaceController extends Controller {
this.controllerClient = controllerClient;
this.isHost = isHost;
//Check if the game is a tutorial
if (parent.getGameType()==4){
isTutorial = true;
tutorialText.setVisible(true);
tutorialStates = new ArrayList<>(Arrays.asList(TutorialState.values()));
currentState = tutorialStates.get(0);
tutorialStates.remove(0);
searchMapForKey("Upwind");
tutorialText.setText("Welcome to the tutorial! Exit at anytime with ESC. \nWe will first learn how to turn upwind. Press " + keyToPress + " to turn upwind.");
} else {
isTutorial = false;
tutorialText.setVisible(false);
}
initialiseRace();
//Display this controller.
@@ -565,4 +719,108 @@ public class RaceController extends Controller {
infoTableShow = !infoTableShow;
}
/**
* Get the next tutorial state
*/
private void updateTutorialState(){
//Next tutorial state is popped from list
currentState = tutorialStates.get(0);
tutorialStates.remove(0);
}
/**
* Search key map for key given string of command
* @param command the command of the key
*/
private void searchMapForKey(String command){
//For loop through keyFactory
for (Map.Entry<String, ControlKey> entry: keyFactory.getKeyState().entrySet()){
if(entry.getValue().toString().equals(command)){
//Found next key required to press
keyToPress = entry.getKey();
}
}
}
/**
* Updates tutorial state and gui display for tutorial text
* @throws Exception Exception thrown
*/
private void checkTutorialState() throws Exception {
//Switch statement to check what the current tutorial state is
switch (currentState){
case UPWIND:
//Set next key to press as the downwind key
searchMapForKey("Downwind");
//Update tutorial text
tutorialText.setText("Nice! To turn downwind press " + keyToPress + ".");
updateTutorialState();
break;
case DOWNWIND:
//Set next key to press as the tack/gybe key
searchMapForKey("Tack/Gybe");
//Update tutorial text
tutorialText.setText("Nice! To tack or gybe press " + keyToPress + ".");
updateTutorialState();
break;
case TACKGYBE:
//Set next key to press as the VMG key
searchMapForKey("VMG");
//Update tutorial text
tutorialText.setText("Nice! To use VMG press " + keyToPress + ". This will turn the boat.");
updateTutorialState();
break;
case VMG:
//Set next key to press as the sails-in key
searchMapForKey("Toggle Sails");
//Update tutorial text
tutorialText.setText("Nice! To sails in press " + keyToPress + ". This will stop the boat.");
updateTutorialState();
break;
case SAILSIN:
//Set next key to press as the sails-out key
searchMapForKey("Toggle Sails");
//Update tutorial text
tutorialText.setText("Nice! To sails out press " + keyToPress + " again. The will start moving again.");
updateTutorialState();
break;
case SAILSOUT:
//Set next key to press as the zoom-in key
searchMapForKey("Zoom In");
//Update tutorial text
tutorialText.setText("Nice! To zoom in press " + keyToPress + ".");
updateTutorialState();
break;
case ZOOMIN:
//Set next key to press as the zoom-out key
searchMapForKey("Zoom Out");
//Update tutorial text
tutorialText.setText("Nice! You will also be able to zoom into boats and marks by clicking them. To zoom out press " + keyToPress + ".");
updateTutorialState();
break;
case ZOOMOUT:
//Finished tutorial. Display pop-up for exiting the tutorial
tutorialText.setText("Congratulations! You're done!");
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Finished Tutorial");
alert.setHeaderText("You have finished the tutorial.");
alert.setContentText("Now you know the controls you are ready to race!");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK) {
parent.endEvent();
racePane.setVisible(false);
App.app.showMainStage(App.getStage());
}
break;
default:
//State not found. Exit tutorial to title menu
parent.endEvent();
racePane.setVisible(false);
App.app.showMainStage(App.getStage());
break;
}
}
}
@@ -86,9 +86,6 @@ public class StartController extends Controller {
/**
* Ctor.
*/
@@ -218,29 +215,30 @@ public class StartController extends Controller {
* Countdown timer until race starts.
*/
private void countdownTimer() {
new AnimationTimer() {
@Override
public void handle(long arg0) {
new AnimationTimer() {
@Override
public void handle(long arg0) {
//Get the current race status.
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
//Get the current race status.
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
//If the race has reached the preparatory phase, or has started...
if (raceStatus == RaceStatusEnum.WARNING
|| raceStatus == RaceStatusEnum.PREPARATORY
|| raceStatus == RaceStatusEnum.STARTED) {
//Stop this timer.
stop();
//If the race has reached the preparatory phase, or has started...
if (raceStatus == RaceStatusEnum.WARNING
|| raceStatus == RaceStatusEnum.PREPARATORY
|| raceStatus == RaceStatusEnum.STARTED) {
//Stop this timer.
stop();
//Hide this, and display the race controller.
startWrapper.setVisible(false);
//start.setVisible(false);//TODO is this needed?
//Hide this, and display the race controller.
startWrapper.setVisible(false);
//start.setVisible(false);//TODO is this needed?
parent.beginRace(visualiserRaceEvent, controllerClient, isHost);
parent.beginRace(visualiserRaceEvent, controllerClient, isHost);
}
}
}
}.start();
}.start();
}
@@ -6,16 +6,29 @@ import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.media.AudioClip;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Modality;
import javafx.stage.Stage;
import mock.app.Event;
import mock.exceptions.EventConstructionException;
import javafx.stage.WindowEvent;
import visualiser.app.App;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Controller for the opening title window.
@@ -33,6 +46,12 @@ public class TitleController extends Controller {
RadioButton dayModeRD;
@FXML
RadioButton nightModeRD;
@FXML
Label tutorialLabel;
@FXML
Pane menuPane;
@FXML
ImageView imgSun;
/**
* Method called when the 'host a game' button is pressed.
@@ -40,8 +59,9 @@ public class TitleController extends Controller {
* Currently used to run the RaceVision mock.
* @throws IOException if main has problems
*/
public void hostAGame() throws IOException {
public void hostAGame() throws IOException, URISyntaxException {
titleWrapper.setVisible(false);
parent.setGameType(0);
parent.hostGame();
App.getStage().setResizable(true);
}
@@ -68,7 +88,10 @@ public class TitleController extends Controller {
*/
public void setDayMode(){
dayModeRD.getScene().getStylesheets().clear();
menuPane.getStylesheets().clear();
imgSun.setImage(new Image(getClass().getResource("/visualiser/images/sun.png").toExternalForm()));
dayModeRD.getScene().getStylesheets().add("/css/dayMode.css");
menuPane.setStyle("-fx-background-color: #6be6ff;");
nightModeRD.setSelected(false);
}
@@ -77,12 +100,17 @@ public class TitleController extends Controller {
*/
public void setNightMode(){
nightModeRD.getScene().getStylesheets().clear();
menuPane.getStylesheets().clear();
imgSun.setImage(new Image(getClass().getResource("/visualiser/images/sunsleep.png").toExternalForm()));
nightModeRD.getScene().getStylesheets().add("/css/nightMode.css");
menuPane.setStyle("-fx-background-color: #1f2c60;");
dayModeRD.setSelected(false);
}
@Override
public void initialize(URL location, ResourceBundle resources) {
tutorialLabel.setWrapText(true);
}
/**
@@ -90,6 +118,8 @@ public class TitleController extends Controller {
*/
public void controlBtnPressed(){
try {
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/visualiser/scenes/keyBindings.fxml"));
Parent layout = loader.load();
@@ -114,4 +144,12 @@ public class TitleController extends Controller {
}
}
public void tutorialStartPressed() throws IOException {
titleWrapper.setVisible(false);
parent.setGameType(4);
parent.beginGame();
}
}
@@ -3,6 +3,7 @@ package visualiser.app;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
@@ -14,6 +15,7 @@ import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressBar;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
@@ -95,7 +97,7 @@ public class App extends Application {
);
updateMessage("Preparing ingredients . . .");
Thread.sleep(200);
Thread.sleep(100);
for (int i = 0; i < burgerFilling.size(); i++) {
Thread.sleep(100);
updateProgress(i + 1, burgerFilling.size());
@@ -0,0 +1,66 @@
package visualiser.enums;
import javafx.scene.input.KeyCode;
import network.Messages.BoatAction;
import network.Messages.Enums.BoatActionEnum;
import visualiser.gameController.Keys.ControlKey;
import static visualiser.app.App.keyFactory;
import java.util.ArrayList;
/**
* State of which stage the tutorial is currently in
*/
public enum TutorialState {
/**
* State for upwind in tutorial
*/
UPWIND(BoatActionEnum.UPWIND),
/**
* State for downwind in tutorial
*/
DOWNWIND(BoatActionEnum.DOWNWIND),
/**
* State for tacking/gybing in tutorial
*/
TACKGYBE(BoatActionEnum.TACK_GYBE),
/**
* State for vmg in tutorial
*/
VMG(BoatActionEnum.AUTO_PILOT),
/**
* State for sails-in in tutorial
*/
SAILSIN(BoatActionEnum.SAILS_IN),
/**
* State for sails-out in tutorial
*/
SAILSOUT(BoatActionEnum.SAILS_OUT),
/**
* State for zoom-in in tutorial
*/
ZOOMIN(null),
/**
* State for zoom-out in tutorial
*/
ZOOMOUT(null);
private BoatActionEnum action;
TutorialState(BoatActionEnum action){
this.action = action;
}
public BoatActionEnum getAction(){
return action;
}
}
@@ -0,0 +1,24 @@
package visualiser.layout;
import javafx.scene.shape.Shape3D;
/**
* Created by connortaylorbrown on 13/09/17.
*/
public class Annotation3D extends Subject3D {
/**
* Constructor for view subject wrapper
*
* @param mesh to be rendered
*/
public Annotation3D(Shape3D mesh) {
super(mesh, 0);
}
/**
* Prevent rescaling of this subject
* @param scale ignored
*/
@Override
public void setScale(double scale) {}
}
@@ -62,12 +62,12 @@ public class Boundary3D {
double c = Math.sqrt(a * a + b * b);
Subject3D bound1 = new Subject3D(new Sphere(thickness * 2));
Subject3D bound1 = new Subject3D(new Sphere(thickness * 2),0);
bound1.setX(graphCoord1.getX());
bound1.setZ(graphCoord1.getY());
boundaryNodes.add(bound1);
Subject3D connector = new Subject3D(new Box(c, thickness, thickness));
Subject3D connector = new Subject3D(new Box(c, thickness, thickness),0);
connector.setX(avgCoord.getX());
connector.setZ(avgCoord.getY());
double angle = 90 + Math.toDegrees(GPSConverter.getAngle(graphCoord2, graphCoord1));
@@ -24,7 +24,7 @@ public class SeaSurface extends Subject3D {
* @param freq frequency the perlin noise is to be generated at
*/
public SeaSurface(int size, double freq){
super(createSurface(size, freq));
super(createSurface(size, freq),0);
image = new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterDown2048.png"));
}
@@ -50,10 +50,10 @@ public class SkyBox {
surface.setRotate(180);
surface.setTranslateX(x);
surface.setTranslateY(y - size/2 + yshift + clipOverlap);
surface.setTranslateY(y - size + 1);
surface.setTranslateZ(z);
Subject3D top = new SkyBoxPlane(surface);
Subject3D top = new Subject3D(surface,0);
skyBoxPlanes.add(top);
}
@@ -72,7 +72,7 @@ public class SkyBox {
surface.setTranslateZ(z + size/2 - clipOverlap);
Subject3D right = new SkyBoxPlane(surface);
Subject3D right = new Subject3D(surface,0);
skyBoxPlanes.add(right);
}
@@ -94,7 +94,7 @@ public class SkyBox {
surface.setTranslateZ(z - size/2 + clipOverlap);
Subject3D left = new SkyBoxPlane(surface);
Subject3D left = new Subject3D(surface,0);
skyBoxPlanes.add(left);
}
@@ -112,7 +112,7 @@ public class SkyBox {
surface.setTranslateY(y + yshift);
surface.setTranslateZ(z);
Subject3D back = new SkyBoxPlane(surface);
Subject3D back = new Subject3D(surface,0);
skyBoxPlanes.add(back);
}
@@ -130,7 +130,7 @@ public class SkyBox {
surface.setTranslateY(y + yshift);
surface.setTranslateZ(z);
Subject3D front = new SkyBoxPlane(surface);
Subject3D front = new Subject3D(surface,0);
skyBoxPlanes.add(front);
}
@@ -13,6 +13,10 @@ public class Subject3D {
* Rendered mesh
*/
private Shape3D mesh;
/**
* Source ID of subject in game model
*/
private int sourceID;
/**
* Position translation updated by state listeners
@@ -30,8 +34,9 @@ public class Subject3D {
* Constructor for view subject wrapper
* @param mesh to be rendered
*/
public Subject3D(Shape3D mesh) {
public Subject3D(Shape3D mesh, int sourceID) {
this.mesh = mesh;
this.sourceID = sourceID;
this.scale = new Scale();
this.position = new Translate();
this.heading = new Rotate(0, Rotate.Y_AXIS);
@@ -43,6 +48,10 @@ public class Subject3D {
return mesh;
}
public int getSourceID() {
return sourceID;
}
public Translate getPosition() {
return this.position;
}
@@ -1,5 +1,7 @@
package visualiser.layout;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
@@ -31,11 +33,15 @@ public class View3D extends Pane {
/**
* Map for selecting Subject3D from Shape3D
*/
private Map<Shape3D, Subject3D> selectionMap;
private Map<Shape3D, Subject3D> shapeMap;
/**
* Map for selecting Shape3D from source ID
*/
private Map<Integer, Shape3D> sourceMap;
/**
* Subject tracked by camera
*/
private Subject3D target;
private ObjectProperty<Subject3D> target;
/**
* Rendering container for shapes
*/
@@ -95,8 +101,9 @@ public class View3D extends Pane {
*/
public View3D(boolean fill) {
this.world = new Group();
this.selectionMap = new HashMap<>();
this.target = null;
this.shapeMap = new HashMap<>();
this.sourceMap = new HashMap<>();
this.target = new SimpleObjectProperty<>(null);
this.scene = new SubScene(world, 300, 300);
scene.widthProperty().bind(this.widthProperty());
@@ -148,17 +155,23 @@ public class View3D extends Pane {
if (c.wasRemoved() || c.wasAdded()) {
for (Subject3D shape : c.getRemoved()) {
world.getChildren().remove(shape.getMesh());
selectionMap.remove(shape.getMesh());
shapeMap.remove(shape.getMesh());
sourceMap.remove(shape.getSourceID());
}
for (Subject3D shape : c.getAddedSubList()) {
world.getChildren().add(shape.getMesh());
selectionMap.put(shape.getMesh(), shape);
shapeMap.put(shape.getMesh(), shape);
sourceMap.put(shape.getSourceID(), shape.getMesh());
}
}
}
});
}
public Shape3D getShape(int sourceID) {
return sourceMap.get(sourceID);
}
/**
* Intercept mouse clicks on subjects in view. The applied listener cannot be removed.
*/
@@ -167,12 +180,16 @@ public class View3D extends Pane {
PickResult result = e.getPickResult();
if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) {
untrackSubject();
trackSubject(selectionMap.get(result.getIntersectedNode()));
trackSubject(shapeMap.get(result.getIntersectedNode()));
setThirdPerson();
}
});
}
public ObjectProperty<Subject3D> targetProperty() {
return target;
}
/**
* Configures camera to third person view
*/
@@ -201,11 +218,12 @@ public class View3D extends Pane {
* Stop camera from following the last selected subject
*/
private void untrackSubject() {
if(target != null) {
target.getPosition().xProperty().removeListener(pivotX);
target.getPosition().yProperty().removeListener(pivotY);
target.getPosition().zProperty().removeListener(pivotZ);
target.getHeading().angleProperty().removeListener(pivotHeading);
if(target.get() != null) {
target.get().getPosition().xProperty().removeListener(pivotX);
target.get().getPosition().yProperty().removeListener(pivotY);
target.get().getPosition().zProperty().removeListener(pivotZ);
target.get().getHeading().angleProperty().removeListener(pivotHeading);
target.setValue(null);
}
}
@@ -214,15 +232,15 @@ public class View3D extends Pane {
* @param subject to track
*/
private void trackSubject(Subject3D subject) {
target = subject;
target.set(subject);
updatePivot(target.getPosition());
setYaw(target.getHeading().getAngle());
updatePivot(target.get().getPosition());
setYaw(target.get().getHeading().getAngle());
target.getPosition().xProperty().addListener(pivotX);
target.getPosition().yProperty().addListener(pivotY);
target.getPosition().zProperty().addListener(pivotZ);
target.getHeading().angleProperty().addListener(pivotHeading);
target.get().getPosition().xProperty().addListener(pivotX);
target.get().getPosition().yProperty().addListener(pivotY);
target.get().getPosition().zProperty().addListener(pivotZ);
target.get().getHeading().angleProperty().addListener(pivotHeading);
}
public void setNearClip(double nearClip) {
@@ -233,6 +251,10 @@ public class View3D extends Pane {
this.farClip = farClip;
}
public Translate getPivot() {
return pivot;
}
/**
* Sets the coordinates of the camera pivot once.
* @param pivot source of coordinates
@@ -65,7 +65,7 @@ public class VisualiserRaceController implements RunnableWithFramePeriod {
compositeRaceCommand.addCommand(command);
} catch (CommandConstructionException e) {
Logger.getGlobal().log(Level.WARNING, "VisualiserRaceController could not create a command for incoming message.");
//Logger.getGlobal().log(Level.WARNING, "VisualiserRaceController could not create a command for incoming message.");
} catch (InterruptedException e) {
Logger.getGlobal().log(Level.SEVERE, "VisualiserRaceController was interrupted on thread: " + Thread.currentThread() + " while waiting for messages.");