290 lines
11 KiB
C#
290 lines
11 KiB
C#
using UnityEngine;
|
|
using GeoSus.Client;
|
|
using System;
|
|
using System.Collections;
|
|
|
|
namespace Subsystems
|
|
{
|
|
internal class CoroutineHost : MonoBehaviour
|
|
{
|
|
public CoroutineHost() { }
|
|
}
|
|
internal enum GPSState
|
|
{
|
|
Uninitialized,
|
|
Initializing,
|
|
Running,
|
|
Failed
|
|
}
|
|
public static class PositonExtensions
|
|
{
|
|
public static Position ToLocal(this Position position, Position center)
|
|
{
|
|
double latDiff = position.Lat - center.Lat;
|
|
double lonDiff = position.Lon - center.Lon;
|
|
double metersPerDegreeLat = 111320.0;
|
|
double metersPerDegreeLon = 111320.0 * Math.Cos(center.Lat * Math.PI / 180.0);
|
|
float x = (float)(lonDiff * metersPerDegreeLon);
|
|
float z = (float)(latDiff * metersPerDegreeLat);
|
|
return new Position(z, x);
|
|
}
|
|
public static Vector3 ToLocalVector3(this Position position, Position center)
|
|
{
|
|
return position.ToLocal(center).ToVector3(); //TODO: Implementace v subsystemech
|
|
}
|
|
public static Vector3 ToVector3(this Position position)
|
|
{
|
|
return new Vector3((float)position.Lon, 0, (float)position.Lat); //TODO: Implementace v subsystemech
|
|
}
|
|
public static double DistanceTo(this Vector3 pos, Vector3 other)
|
|
{
|
|
return Math.Sqrt((other.x - pos.x) * (other.x - pos.x) + (other.z - pos.z) * (other.z - pos.z));
|
|
}
|
|
|
|
}
|
|
|
|
public class GameManager_Input
|
|
{
|
|
private GameClient _gameClient;
|
|
private Position _currentPosition;
|
|
private Position _lastSentPosition;
|
|
private GameObject _player;
|
|
private bool _testMode;
|
|
|
|
private GPSState _GPSState = GPSState.Uninitialized;
|
|
private float _speed = 0.00001f;
|
|
private Position _mapCenter;
|
|
private CoroutineHost _coroutineHost;
|
|
|
|
private int _gpsRetryCount = 0;
|
|
private const int _maxGpsRetries = 5;
|
|
private float _lastPositionSendTime;
|
|
private const float _positionKeepAliveSeconds = 1.0f;
|
|
|
|
/// <summary>Last known GPS position (for CreateLobby centre point)</summary>
|
|
public Position? LastKnownPosition => _currentPosition.Lat != 0 || _currentPosition.Lon != 0 ? _currentPosition : (Position?)null;
|
|
|
|
public GameManager_Input(GameClient gameClient, GameObject player, bool testMode)
|
|
{
|
|
_gameClient = gameClient;
|
|
_player = player;
|
|
_testMode = testMode;
|
|
// CoroutineHost needs a MonoBehaviour on a real GameObject
|
|
var hostGO = new UnityEngine.GameObject("_CoroutineHost");
|
|
UnityEngine.Object.DontDestroyOnLoad(hostGO);
|
|
_coroutineHost = hostGO.AddComponent<CoroutineHost>();
|
|
}
|
|
|
|
/// <summary>Called from OnSceneLoaded when Client.unity loads so the
|
|
/// Player capsule (which lives in Client.unity) can be wired at runtime.</summary>
|
|
public void SetPlayerObject(GameObject player) { _player = player; }
|
|
public void positionCheck()
|
|
{
|
|
var state = _gameClient?.CurrentLobbyState;
|
|
if (state == null || state.Phase != GamePhase.Playing)
|
|
return;
|
|
|
|
try
|
|
{
|
|
if (_testMode)
|
|
{
|
|
if (_currentPosition == new Position(0, 0))
|
|
{
|
|
if (state.MapData == null)
|
|
return;
|
|
|
|
//Init blok
|
|
_currentPosition = state.MapData.Center;
|
|
_mapCenter = state.MapData.Center;
|
|
_lastSentPosition = _currentPosition;
|
|
}
|
|
|
|
TestPlayerPosition();
|
|
}
|
|
else
|
|
{
|
|
if (_GPSState == GPSState.Uninitialized)
|
|
{
|
|
_coroutineHost.StartCoroutine(InitiallizeGPS());
|
|
return;
|
|
}
|
|
else if (_GPSState == GPSState.Initializing)
|
|
{
|
|
return;
|
|
}
|
|
else if (_GPSState == GPSState.Running)
|
|
{
|
|
EnsureMapCenter();
|
|
TrySendCurrentPosition();
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("GPS failed, trying again...");
|
|
if (_gpsRetryCount < _maxGpsRetries)
|
|
{
|
|
_gpsRetryCount++;
|
|
_GPSState = GPSState.Uninitialized;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("GPS unavailable after max retries. Using last known position.");
|
|
// Keep _GPSState = Failed so we stop retrying
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning($"[Input] positionCheck failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void EnsureMapCenter()
|
|
{
|
|
if (_mapCenter.Lat != 0 || _mapCenter.Lon != 0)
|
|
return;
|
|
|
|
var md = _gameClient?.CurrentLobbyState?.MapData;
|
|
if (md != null)
|
|
_mapCenter = md.Center;
|
|
}
|
|
|
|
private void TrySendCurrentPosition()
|
|
{
|
|
bool moved = _currentPosition != _lastSentPosition;
|
|
bool keepAliveDue = (Time.time - _lastPositionSendTime) >= _positionKeepAliveSeconds;
|
|
if (!moved && !keepAliveDue)
|
|
return;
|
|
|
|
var previous = _lastSentPosition;
|
|
_gameClient.UpdatePosition(_currentPosition);
|
|
_lastSentPosition = _currentPosition;
|
|
_lastPositionSendTime = Time.time;
|
|
|
|
if (_player == null || (_mapCenter.Lat == 0 && _mapCenter.Lon == 0))
|
|
return;
|
|
|
|
var localCurrent = _currentPosition.ToLocalVector3(_mapCenter);
|
|
_player.transform.position = localCurrent;
|
|
|
|
if (previous.Lat == 0 && previous.Lon == 0)
|
|
return;
|
|
|
|
var heading = CalculateHeading(previous.ToLocalVector3(_mapCenter), localCurrent);
|
|
if (heading.HasValue)
|
|
_player.transform.rotation = Quaternion.Euler(0, (float)heading.Value, 0);
|
|
}
|
|
|
|
private void TestPlayerPosition()
|
|
{
|
|
double x = Input.GetAxis("Horizontal");
|
|
double y = Input.GetAxis("Vertical");
|
|
Debug.Log($"Input: {x}, {y}");
|
|
_currentPosition = new Position( _lastSentPosition.Lat + y * _speed, _lastSentPosition.Lon + x * _speed);
|
|
Debug.Log($"Current Position: {_currentPosition.Lat}, {_currentPosition.Lon}");
|
|
var localCurrent = _currentPosition.ToLocalVector3(_mapCenter);
|
|
Debug.Log($"Local Current Position: {localCurrent}");
|
|
var heading = CalculateHeading(_lastSentPosition.ToLocalVector3(_mapCenter), localCurrent);
|
|
if (heading != null)
|
|
{
|
|
Debug.Log($"Heading: {heading}");
|
|
_player.transform.rotation = Quaternion.Euler(0, (float)heading, 0);
|
|
}
|
|
_player.transform.position = localCurrent;
|
|
try
|
|
{
|
|
TrySendCurrentPosition();
|
|
}
|
|
catch
|
|
{
|
|
_gameClient.UpdatePosition(_currentPosition);
|
|
_lastSentPosition = _currentPosition;
|
|
}
|
|
}
|
|
private double? CalculateHeading(Vector3 first, Vector3 second)
|
|
{
|
|
if ((first - second).magnitude < 0.0001f) return null;
|
|
float dx = second.x - first.x;
|
|
float dz = second.z - first.z;
|
|
float heading = Mathf.Atan2(dx, dz) * Mathf.Rad2Deg;
|
|
if (heading < 0) heading += 360f;
|
|
return heading;
|
|
}
|
|
IEnumerator InitiallizeGPS()
|
|
{
|
|
_GPSState = GPSState.Initializing;
|
|
|
|
#if UNITY_ANDROID
|
|
// Request fine location permission if not already granted
|
|
if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.FineLocation))
|
|
{
|
|
UnityEngine.Android.Permission.RequestUserPermission(UnityEngine.Android.Permission.FineLocation);
|
|
// Wait up to 10 seconds for user to respond to the permission dialog
|
|
float waited = 0f;
|
|
while (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.FineLocation) && waited < 10f)
|
|
{
|
|
yield return new WaitForSeconds(0.5f);
|
|
waited += 0.5f;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!Input.location.isEnabledByUser)
|
|
{
|
|
Debug.LogError("Location not enabled on device or app does not have permission to access location");
|
|
_GPSState = GPSState.Failed;
|
|
yield break;
|
|
}
|
|
// Starts the location service.
|
|
|
|
float desiredAccuracyInMeters = 5f;
|
|
float updateDistanceInMeters = 1f;
|
|
|
|
Input.location.Start(desiredAccuracyInMeters, updateDistanceInMeters);
|
|
|
|
// Waits until the location service initializes
|
|
int maxWait = 20;
|
|
while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
|
|
{
|
|
yield return new WaitForSeconds(1);
|
|
maxWait--;
|
|
}
|
|
|
|
// If the service didn't initialize in 20 seconds this cancels location service use.
|
|
if (maxWait < 1)
|
|
{
|
|
_GPSState = GPSState.Failed;
|
|
Debug.LogError("Timed out");
|
|
yield break;
|
|
}
|
|
|
|
if (Input.location.status == LocationServiceStatus.Failed)
|
|
{
|
|
_GPSState = GPSState.Failed;
|
|
Debug.LogError("Unable to determine device location");
|
|
yield break;
|
|
}
|
|
|
|
_GPSState = GPSState.Running;
|
|
_gpsRetryCount = 0;
|
|
_coroutineHost.StartCoroutine(GPSService());
|
|
}
|
|
IEnumerator GPSService()
|
|
{
|
|
while (_GPSState == GPSState.Running)
|
|
{
|
|
if (Input.location.status == LocationServiceStatus.Failed)
|
|
{
|
|
_GPSState = GPSState.Failed;
|
|
Debug.LogError("Unable to determine device location");
|
|
yield break;
|
|
}
|
|
|
|
// Keep current GPS position fresh; sending is throttled in positionCheck().
|
|
var data = Input.location.lastData;
|
|
_currentPosition = new Position(data.latitude, data.longitude);
|
|
yield return new WaitForSeconds(0.5f);
|
|
}
|
|
}
|
|
}
|
|
} |