Работа с сокетами и ObjectInputStream/ObjectOutputStream
Работаю над созданием сетевого 2D шутера с использованием сокетов и JavaFX. Сервер запускается на компьютере пользователя, после чего к нему может подключиться второй игрок. Для передачи данных я использую Object Output Stream и Object Input Stream и передаю через них информацию в потоке. Проблема в том что пока игроки просто передвигаются - никаких задержек нет, но с каждым выстрелом задержка всё сильнее (у игрока выстрел произошёл, а у оппонента до 3 секунд задержка). Ниже идёт код самого потока (идентичен для класса клиента и сервера)
@Override
public void start() {
try {
outputStream= new ObjectOutputStream(socket.getOutputStream());
inputStream= new ObjectInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
AnimationTimer animationTimer = new AnimationTimer() { //таймер анимации JavaFX ~60 тиков в секунду
@Override
public void handle(long now) {
movePlayerShip(); //обноляем координаты корабля, в этом же методе всё время передаются координаты этого корабля второму игроу
moveBackground(); //двигаем фон
movePlayerBullets(); // двигаем пули на экране, происходит локально, данные не передаются другому игроку
try {
//получаем массив объектов
infoGet = (Object[]) inputStream.readObject();
switch ((String)infoGet[0]){ //проверяем по первому элементу что именно пришло
case "playerMove": //если передвижение оппонента
shipOpponent.setLayoutX((double)infoGet[1]); //на окне двигаем корабль по тем же координатам
shipOpponent.setLayoutY((double)infoGet[2]); //корабль представляет собой ImageView
shipOpponentBox.setLayoutX((double)infoGet[3]); //хитбокс тоже двигаем
shipOpponentBox.setLayoutX((double)infoGet[4]);
infoGet = null; //очищаем для повторного получения данных
break;
case "shootPlayer": //если оппонент выстрелил
//создаём "пулю" на экране с теми же координатами, откуда выстрелил оппонент и задаём такую же траекторию
MultiplayerBullet opBullet = new MultiplayerBullet((double) infoGet[3],(double) infoGet[4],0 );
opBullet.setTarget((double) infoGet[1],(double) infoGet[2] );
gamePane.getChildren().add(opBullet);
opponentBullets.add(opBullet); //добавляем в arraylist с пулями противника на экране
shootSound.play(); //проигрываем звук выстрела
shootSound.seek(Duration.ZERO); //отматываем на начало для повторного проигрывание
break;
default: //если пришли неизвестные данные
System.out.println("got strange stuff");
break;
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
moveOpponentBullets(); //обновление всех пуль противника на экране
}
};
animationTimer.start();//запуск анимации
}
Так выглядит метод для отправки данных когда игрок выстреливает:
private void shootPlayer(MouseEvent event){
MultiplayerBullet bullet = new MultiplayerBullet(shipPlayer.getLayoutX() + shipPlayer.getImage().getWidth() / 2, shipPlayer.getLayoutY() + shipPlayer.getImage().getHeight()/2 , 1);
bullet.setTarget(event.getSceneX(),event.getSceneY());
//System.out.println(event.getSceneX() + " | " + event.getScreenX() + " | " + event.getX());
infoSend[0] = "shootPlayer";
infoSend[1] = event.getSceneX();
infoSend[2] = event.getSceneY();
infoSend[3] = bullet.getLayoutX();
infoSend[4] = bullet.getLayoutY();
try {
outputStream.reset();
outputStream.writeObject(infoSend);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
playerBullets.add(bullet); //добавляем в лист пули
gamePane.getChildren().add(bullet); // добавляем на экран
shipPlayer.toFront();
}
Это метод для отправки позиции игрока оппоненту:
private void movePlayerShip(){
if(isLeftKeyPressed){
if(shipPlayer.getLayoutX() > 0 ){
shipPlayer.setLayoutX(shipPlayer.getLayoutX() - 4 );
shipPlayerBox.setX(shipPlayerBox.getX() - 4);
}
}
//Аналогично для остальных направлений, убрал что бы кода много не было.
//Отправка происходит всегда в конце метода, даже если игрок не двигался
infoSend[0] = "playerMove";
infoSend[1] = shipPlayer.getLayoutX();
infoSend[2] = shipPlayer.getLayoutY();
infoSend[3] = shipPlayerBox.getLayoutX();
infoSend[4] = shipPlayerBox.getLayoutY();
try {
outputStream.reset();
outputStream.writeObject(infoSend);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
Искал информацию по данной теме - нашёл лишь то, что можно выставить SetTcpNoDelay(true) и делать reset потока для вывода. В чём может заключаться данная проблема с появлением задержек? По идее просто отсылается другой массив объектов. P.S. пули убираются с экрана при выходе за границу