Compare commits
20 Commits
find-the-s
...
Gameclient
| Author | SHA1 | Date | |
|---|---|---|---|
| 556fdb6090 | |||
| 43a3434e97 | |||
| 48448a9cff | |||
| b872b52632 | |||
| be595da357 | |||
| a1b40ad102 | |||
| 7294466604 | |||
| 67d3ee76c1 | |||
| 94a40e3d14 | |||
| eeaf092780 | |||
| fc22d4f544 | |||
| 3b36d53b39 | |||
| b1fc3ac24a | |||
| 655d378dbb | |||
| 39b42e1e90 | |||
| 0cb7d4b64d | |||
| 1c103a1496 | |||
| 1618ecd432 | |||
| ed6347e6bc | |||
| 59b6708437 |
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"Bash(nul)",
|
|
||||||
"Bash(mkdir:*)",
|
|
||||||
"Bash(dir:*)",
|
|
||||||
"Bash(findstr:*)",
|
|
||||||
"Bash(move \"Assets\\Scripts\\TiltUI.cs\" \"Assets\\Scripts\\TiltUI.cs.DISABLED\")"
|
|
||||||
],
|
|
||||||
"deny": [],
|
|
||||||
"ask": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -303,9 +303,6 @@ PublishScripts/
|
|||||||
*.nupkg
|
*.nupkg
|
||||||
# NuGet Symbol Packages
|
# NuGet Symbol Packages
|
||||||
*.snupkg
|
*.snupkg
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/[Pp]ackages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
#!**/[Pp]ackages/repositories.config
|
#!**/[Pp]ackages/repositories.config
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
@@ -499,5 +496,3 @@ FodyWeavers.xsd
|
|||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/unity,visualstudiocode,visualstudio,vim
|
# End of https://www.toptal.com/developers/gitignore/api/unity,visualstudiocode,visualstudio,vim
|
||||||
|
|
||||||
|
|
||||||
.utmp/
|
|
||||||
|
|||||||
6
.vsconfig
Normal file
6
.vsconfig
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"components": [
|
||||||
|
"Microsoft.VisualStudio.Workload.ManagedGame"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 2f6ce7f674a77664ead5dbb89193e587
|
guid: 799f52449ae21404c9a7593f6dc28c60
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
285
Assets/ClientSDK/Encryption.cs
Normal file
285
Assets/ClientSDK/Encryption.cs
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace GeoSus.Client
|
||||||
|
{
|
||||||
|
// Klientská strana šifrování - generuje session key, šifruje RSA, AES-CBC session
|
||||||
|
// Používá AES-CBC místo AES-GCM pro kompatibilitu s Unity
|
||||||
|
public class ClientEncryption : IDisposable
|
||||||
|
{
|
||||||
|
private byte[] _sessionKey;
|
||||||
|
private byte[] _sessionIv;
|
||||||
|
private long _nonceCounter;
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
|
// Kontrola, zda je session key nastaven
|
||||||
|
public bool HasSessionKey => _sessionKey != null && _sessionIv != null;
|
||||||
|
|
||||||
|
// Generuje nový session key a IV
|
||||||
|
public void GenerateSessionKey()
|
||||||
|
{
|
||||||
|
_sessionKey = new byte[32]; // AES-256
|
||||||
|
_sessionIv = new byte[16]; // CBC IV (16 bytes)
|
||||||
|
|
||||||
|
using (var rng = RandomNumberGenerator.Create())
|
||||||
|
{
|
||||||
|
rng.GetBytes(_sessionKey);
|
||||||
|
rng.GetBytes(_sessionIv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] SessionKey => _sessionKey ?? throw new InvalidOperationException("Session key not generated");
|
||||||
|
public byte[] SessionIV => _sessionIv ?? throw new InvalidOperationException("Session IV not generated");
|
||||||
|
|
||||||
|
// Zašifruje session key pomocí RSA public key serveru
|
||||||
|
public (string EncryptedKey, string EncryptedIV) EncryptSessionKeyForServer(string rsaPublicKeyPem)
|
||||||
|
{
|
||||||
|
if (_sessionKey == null || _sessionIv == null)
|
||||||
|
throw new InvalidOperationException("Session key not generated");
|
||||||
|
|
||||||
|
using (var rsa = RSA.Create())
|
||||||
|
{
|
||||||
|
// Parse PEM - extrahuj Base64 obsah
|
||||||
|
var pemLines = rsaPublicKeyPem.Split('\n');
|
||||||
|
var base64 = new StringBuilder();
|
||||||
|
foreach (var line in pemLines)
|
||||||
|
{
|
||||||
|
var trimmed = line.Trim();
|
||||||
|
if (!trimmed.StartsWith("-----") && !string.IsNullOrEmpty(trimmed))
|
||||||
|
{
|
||||||
|
base64.Append(trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyBytes = Convert.FromBase64String(base64.ToString());
|
||||||
|
|
||||||
|
// Unity kompatibilní import - parsujeme SubjectPublicKeyInfo ručně
|
||||||
|
ImportSubjectPublicKeyInfoManual(rsa, keyBytes);
|
||||||
|
|
||||||
|
// Používáme OaepSHA1 pro Unity kompatibilitu (OaepSHA256 není podporován)
|
||||||
|
var encryptedKey = rsa.Encrypt(_sessionKey, RSAEncryptionPadding.OaepSHA1);
|
||||||
|
var encryptedIv = rsa.Encrypt(_sessionIv, RSAEncryptionPadding.OaepSHA1);
|
||||||
|
|
||||||
|
return (Convert.ToBase64String(encryptedKey), Convert.ToBase64String(encryptedIv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ručně parsuje SubjectPublicKeyInfo (DER) a importuje RSA klíč - Unity kompatibilní
|
||||||
|
private static void ImportSubjectPublicKeyInfoManual(RSA rsa, byte[] subjectPublicKeyInfo)
|
||||||
|
{
|
||||||
|
// SubjectPublicKeyInfo ::= SEQUENCE {
|
||||||
|
// algorithm AlgorithmIdentifier,
|
||||||
|
// subjectPublicKey BIT STRING }
|
||||||
|
// RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent INTEGER }
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
// Outer SEQUENCE
|
||||||
|
if (subjectPublicKeyInfo[index++] != 0x30)
|
||||||
|
throw new InvalidOperationException("Invalid SubjectPublicKeyInfo");
|
||||||
|
ReadLength(subjectPublicKeyInfo, ref index);
|
||||||
|
|
||||||
|
// AlgorithmIdentifier SEQUENCE - skip it
|
||||||
|
if (subjectPublicKeyInfo[index++] != 0x30)
|
||||||
|
throw new InvalidOperationException("Invalid AlgorithmIdentifier");
|
||||||
|
int algLen = ReadLength(subjectPublicKeyInfo, ref index);
|
||||||
|
index += algLen;
|
||||||
|
|
||||||
|
// BIT STRING containing RSAPublicKey
|
||||||
|
if (subjectPublicKeyInfo[index++] != 0x03)
|
||||||
|
throw new InvalidOperationException("Invalid BIT STRING");
|
||||||
|
ReadLength(subjectPublicKeyInfo, ref index);
|
||||||
|
index++; // Skip unused bits byte (should be 0)
|
||||||
|
|
||||||
|
// RSAPublicKey SEQUENCE
|
||||||
|
if (subjectPublicKeyInfo[index++] != 0x30)
|
||||||
|
throw new InvalidOperationException("Invalid RSAPublicKey");
|
||||||
|
ReadLength(subjectPublicKeyInfo, ref index);
|
||||||
|
|
||||||
|
// Modulus INTEGER
|
||||||
|
byte[] modulus = ReadInteger(subjectPublicKeyInfo, ref index);
|
||||||
|
|
||||||
|
// Exponent INTEGER
|
||||||
|
byte[] exponent = ReadInteger(subjectPublicKeyInfo, ref index);
|
||||||
|
|
||||||
|
var parameters = new RSAParameters
|
||||||
|
{
|
||||||
|
Modulus = modulus,
|
||||||
|
Exponent = exponent
|
||||||
|
};
|
||||||
|
rsa.ImportParameters(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ReadLength(byte[] data, ref int index)
|
||||||
|
{
|
||||||
|
int length = data[index++];
|
||||||
|
if ((length & 0x80) != 0)
|
||||||
|
{
|
||||||
|
int numBytes = length & 0x7F;
|
||||||
|
length = 0;
|
||||||
|
for (int i = 0; i < numBytes; i++)
|
||||||
|
{
|
||||||
|
length = (length << 8) | data[index++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] ReadInteger(byte[] data, ref int index)
|
||||||
|
{
|
||||||
|
if (data[index++] != 0x02)
|
||||||
|
throw new InvalidOperationException("Expected INTEGER");
|
||||||
|
int length = ReadLength(data, ref index);
|
||||||
|
|
||||||
|
// Skip leading zero if present (used for positive sign in DER)
|
||||||
|
int originalLength = length;
|
||||||
|
int start = index;
|
||||||
|
if (length > 1 && data[start] == 0x00)
|
||||||
|
{
|
||||||
|
start++;
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] result = new byte[length];
|
||||||
|
Buffer.BlockCopy(data, start, result, 0, length);
|
||||||
|
index += originalLength;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Šifruje zprávu pomocí AES-256-CBC s HMAC
|
||||||
|
public byte[] Encrypt(byte[] plaintext)
|
||||||
|
{
|
||||||
|
if (_sessionKey == null || _sessionIv == null)
|
||||||
|
throw new InvalidOperationException("Session key not set");
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
// Generuj unikátní IV pro tuto zprávu
|
||||||
|
var iv = GetNextIV();
|
||||||
|
|
||||||
|
using (var aes = Aes.Create())
|
||||||
|
{
|
||||||
|
aes.Key = _sessionKey;
|
||||||
|
aes.IV = iv;
|
||||||
|
aes.Mode = CipherMode.CBC;
|
||||||
|
aes.Padding = PaddingMode.PKCS7;
|
||||||
|
|
||||||
|
byte[] ciphertext;
|
||||||
|
using (var encryptor = aes.CreateEncryptor())
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
|
||||||
|
{
|
||||||
|
cs.Write(plaintext, 0, plaintext.Length);
|
||||||
|
}
|
||||||
|
ciphertext = ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute HMAC pro integritu
|
||||||
|
byte[] hmac;
|
||||||
|
using (var hmacSha = new HMACSHA256(_sessionKey))
|
||||||
|
{
|
||||||
|
var toSign = new byte[iv.Length + ciphertext.Length];
|
||||||
|
Buffer.BlockCopy(iv, 0, toSign, 0, iv.Length);
|
||||||
|
Buffer.BlockCopy(ciphertext, 0, toSign, iv.Length, ciphertext.Length);
|
||||||
|
hmac = hmacSha.ComputeHash(toSign);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Výstup: [16 bytes IV][32 bytes HMAC][ciphertext]
|
||||||
|
var result = new byte[16 + 32 + ciphertext.Length];
|
||||||
|
Buffer.BlockCopy(iv, 0, result, 0, 16);
|
||||||
|
Buffer.BlockCopy(hmac, 0, result, 16, 32);
|
||||||
|
Buffer.BlockCopy(ciphertext, 0, result, 48, ciphertext.Length);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dešifruje zprávu pomocí AES-256-CBC s HMAC ověřením
|
||||||
|
public byte[] Decrypt(byte[] encrypted)
|
||||||
|
{
|
||||||
|
if (_sessionKey == null)
|
||||||
|
throw new InvalidOperationException("Session key not set");
|
||||||
|
|
||||||
|
if (encrypted.Length < 48) return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var iv = new byte[16];
|
||||||
|
var hmac = new byte[32];
|
||||||
|
var ciphertext = new byte[encrypted.Length - 48];
|
||||||
|
|
||||||
|
Buffer.BlockCopy(encrypted, 0, iv, 0, 16);
|
||||||
|
Buffer.BlockCopy(encrypted, 16, hmac, 0, 32);
|
||||||
|
Buffer.BlockCopy(encrypted, 48, ciphertext, 0, ciphertext.Length);
|
||||||
|
|
||||||
|
// Ověř HMAC
|
||||||
|
byte[] expectedHmac;
|
||||||
|
using (var hmacSha = new HMACSHA256(_sessionKey))
|
||||||
|
{
|
||||||
|
var toVerify = new byte[iv.Length + ciphertext.Length];
|
||||||
|
Buffer.BlockCopy(iv, 0, toVerify, 0, iv.Length);
|
||||||
|
Buffer.BlockCopy(ciphertext, 0, toVerify, iv.Length, ciphertext.Length);
|
||||||
|
expectedHmac = hmacSha.ComputeHash(toVerify);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constant-time compare
|
||||||
|
var diff = 0;
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
diff |= hmac[i] ^ expectedHmac[i];
|
||||||
|
}
|
||||||
|
if (diff != 0) return null; // HMAC mismatch
|
||||||
|
|
||||||
|
using (var aes = Aes.Create())
|
||||||
|
{
|
||||||
|
aes.Key = _sessionKey;
|
||||||
|
aes.IV = iv;
|
||||||
|
aes.Mode = CipherMode.CBC;
|
||||||
|
aes.Padding = PaddingMode.PKCS7;
|
||||||
|
|
||||||
|
using (var decryptor = aes.CreateDecryptor())
|
||||||
|
using (var ms = new MemoryStream(ciphertext))
|
||||||
|
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
|
||||||
|
using (var output = new MemoryStream())
|
||||||
|
{
|
||||||
|
cs.CopyTo(output);
|
||||||
|
return output.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CryptographicException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] GetNextIV()
|
||||||
|
{
|
||||||
|
if (_sessionIv == null)
|
||||||
|
throw new InvalidOperationException("Session IV not set");
|
||||||
|
|
||||||
|
var iv = new byte[16];
|
||||||
|
Buffer.BlockCopy(_sessionIv, 0, iv, 0, 8);
|
||||||
|
|
||||||
|
var counter = System.Threading.Interlocked.Increment(ref _nonceCounter);
|
||||||
|
var counterBytes = BitConverter.GetBytes(counter);
|
||||||
|
Buffer.BlockCopy(counterBytes, 0, iv, 8, 8);
|
||||||
|
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_sessionKey != null)
|
||||||
|
{
|
||||||
|
Array.Clear(_sessionKey, 0, _sessionKey.Length);
|
||||||
|
_sessionKey = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/ClientSDK/Encryption.cs.meta
Normal file
2
Assets/ClientSDK/Encryption.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bc06bb57786c7e142b06ec231e5cf709
|
||||||
73
Assets/ClientSDK/EventDispatcher.cs
Normal file
73
Assets/ClientSDK/EventDispatcher.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace GeoSus.Client
|
||||||
|
{
|
||||||
|
// Event dispatcher pro Unity main thread
|
||||||
|
// Unity může přidat SynchronizationContext, nebo polling z Update()
|
||||||
|
public class EventDispatcher
|
||||||
|
{
|
||||||
|
private readonly Queue<Action> _pendingActions = new Queue<Action>();
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
private SynchronizationContext? _syncContext;
|
||||||
|
|
||||||
|
public EventDispatcher()
|
||||||
|
{
|
||||||
|
// Pokusíme se zachytit aktuální synchronization context (Unity main thread)
|
||||||
|
_syncContext = SynchronizationContext.Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volat z networking vlákna - naplánuje callback na main thread
|
||||||
|
public void Post(Action action)
|
||||||
|
{
|
||||||
|
if (_syncContext != null)
|
||||||
|
{
|
||||||
|
_syncContext.Post(_ => action(), null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback - přidáme do fronty pro polling
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_pendingActions.Enqueue(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volat z Unity Update() pokud není SynchronizationContext
|
||||||
|
public void ProcessPendingActions()
|
||||||
|
{
|
||||||
|
Action[] actions;
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_pendingActions.Count == 0) return;
|
||||||
|
actions = _pendingActions.ToArray();
|
||||||
|
_pendingActions.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var action in actions)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"EventDispatcher error: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PendingCount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _pendingActions.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/ClientSDK/EventDispatcher.cs.meta
Normal file
2
Assets/ClientSDK/EventDispatcher.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1d2251b279edb0147bd274a884ac878b
|
||||||
607
Assets/ClientSDK/GameClient.cs
Normal file
607
Assets/ClientSDK/GameClient.cs
Normal file
@@ -0,0 +1,607 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GeoSus.Client
|
||||||
|
{
|
||||||
|
// Hlavní klientská třída pro připojení k serveru
|
||||||
|
public class GameClient : IDisposable
|
||||||
|
{
|
||||||
|
private TcpClient? _tcpClient;
|
||||||
|
private NetworkStream? _stream;
|
||||||
|
private ClientEncryption? _encryption;
|
||||||
|
private CancellationTokenSource? _cts;
|
||||||
|
private Task? _receiveTask;
|
||||||
|
private int _clientSeq;
|
||||||
|
private readonly object _sendLock = new object();
|
||||||
|
private bool _handshakeComplete;
|
||||||
|
|
||||||
|
public string ClientUuid { get; }
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
public bool IsConnected => _tcpClient?.Connected ?? false;
|
||||||
|
public bool IsReady => IsConnected && _handshakeComplete && (_encryption?.HasSessionKey ?? false);
|
||||||
|
public EventDispatcher Dispatcher { get; }
|
||||||
|
|
||||||
|
// Events - voláno na main thread přes dispatcher
|
||||||
|
public event Action? OnConnected;
|
||||||
|
public event Action<string>? OnDisconnected;
|
||||||
|
public event Action<string>? OnError;
|
||||||
|
public event Action<Message>? OnMessage;
|
||||||
|
public event Action<GameEvent>? OnGameEvent;
|
||||||
|
|
||||||
|
// Lobby state
|
||||||
|
public string? LobbyId { get; private set; }
|
||||||
|
public string? JoinCode { get; private set; }
|
||||||
|
public LobbyState? CurrentLobbyState { get; private set; }
|
||||||
|
public PlayerRole? MyRole { get; private set; }
|
||||||
|
public List<GameTask> MyTasks { get; } = new List<GameTask>();
|
||||||
|
public Position MyPosition { get; set; }
|
||||||
|
public Dictionary<string, PlayerPositionInfo> PlayerPositions { get; } = new Dictionary<string, PlayerPositionInfo>();
|
||||||
|
public List<Body> Bodies { get; } = new List<Body>();
|
||||||
|
public int Ping { get; private set; }
|
||||||
|
public long LastEventId { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Returns true if this client is the current lobby owner</summary>
|
||||||
|
public bool IsOwner => CurrentLobbyState?.OwnerId == ClientUuid;
|
||||||
|
|
||||||
|
public GameClient(string clientUuid, string displayName)
|
||||||
|
{
|
||||||
|
ClientUuid = clientUuid;
|
||||||
|
DisplayName = displayName;
|
||||||
|
Dispatcher = new EventDispatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Connection
|
||||||
|
|
||||||
|
public async Task<bool> ConnectAsync(string host, int port)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_tcpClient = new TcpClient();
|
||||||
|
await _tcpClient.ConnectAsync(host, port);
|
||||||
|
_stream = _tcpClient.GetStream();
|
||||||
|
_encryption = new ClientEncryption();
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
// Handshake
|
||||||
|
if (!await PerformHandshakeAsync())
|
||||||
|
{
|
||||||
|
Disconnect("Handshake failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spustíme příjem zpráv
|
||||||
|
_receiveTask = Task.Run(() => ReceiveLoopAsync(_cts.Token));
|
||||||
|
|
||||||
|
Dispatcher.Post(() => OnConnected?.Invoke());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Dispatcher.Post(() => OnError?.Invoke(ex.Message));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> PerformHandshakeAsync()
|
||||||
|
{
|
||||||
|
if (_stream == null || _encryption == null) return false;
|
||||||
|
|
||||||
|
// 1. ClientHello
|
||||||
|
var hello = new ClientHello
|
||||||
|
{
|
||||||
|
ClientUuid = ClientUuid,
|
||||||
|
DisplayName = DisplayName
|
||||||
|
};
|
||||||
|
await SendPlainAsync(hello);
|
||||||
|
|
||||||
|
// 2. ServerHello
|
||||||
|
var serverHelloData = await ReadMessageAsync();
|
||||||
|
if (serverHelloData == null) return false;
|
||||||
|
|
||||||
|
var serverHello = MessageSerializer.Deserialize(serverHelloData) as ServerHello;
|
||||||
|
if (serverHello == null) return false;
|
||||||
|
|
||||||
|
// 3. Generujeme session key a šifrujeme RSA
|
||||||
|
_encryption.GenerateSessionKey();
|
||||||
|
var (encKey, encIv) = _encryption.EncryptSessionKeyForServer(serverHello.RsaPublicKeyPem);
|
||||||
|
|
||||||
|
var keyExchange = new KeyExchange
|
||||||
|
{
|
||||||
|
EncryptedSessionKey = encKey,
|
||||||
|
EncryptedIV = encIv
|
||||||
|
};
|
||||||
|
await SendPlainAsync(keyExchange);
|
||||||
|
|
||||||
|
// 4. KeyExchangeAck (šifrovaně)
|
||||||
|
var ackData = await ReadMessageAsync();
|
||||||
|
if (ackData == null) return false;
|
||||||
|
|
||||||
|
var decrypted = _encryption.Decrypt(ackData);
|
||||||
|
if (decrypted == null) return false;
|
||||||
|
|
||||||
|
var ack = MessageSerializer.Deserialize(decrypted) as KeyExchangeAck;
|
||||||
|
if (ack?.Status == "success")
|
||||||
|
{
|
||||||
|
_handshakeComplete = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Disconnect(string reason = "User disconnected")
|
||||||
|
{
|
||||||
|
_cts?.Cancel();
|
||||||
|
_tcpClient?.Close();
|
||||||
|
_tcpClient = null;
|
||||||
|
_stream = null;
|
||||||
|
_encryption?.Dispose();
|
||||||
|
_encryption = null;
|
||||||
|
|
||||||
|
LobbyId = null;
|
||||||
|
JoinCode = null;
|
||||||
|
CurrentLobbyState = null;
|
||||||
|
MyRole = null;
|
||||||
|
MyTasks.Clear();
|
||||||
|
PlayerPositions.Clear();
|
||||||
|
Bodies.Clear();
|
||||||
|
|
||||||
|
Dispatcher.Post(() => OnDisconnected?.Invoke(reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Sending
|
||||||
|
|
||||||
|
public void Send(Message message)
|
||||||
|
{
|
||||||
|
if (_stream == null || _encryption == null || !IsConnected) return;
|
||||||
|
|
||||||
|
message.ClientSeq = Interlocked.Increment(ref _clientSeq);
|
||||||
|
if (string.IsNullOrEmpty(message.ActionId))
|
||||||
|
{
|
||||||
|
message.ActionId = Guid.NewGuid().ToString("N").Substring(0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
var plain = MessageSerializer.Serialize(message);
|
||||||
|
var encrypted = _encryption.Encrypt(plain);
|
||||||
|
|
||||||
|
lock (_sendLock)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SendData(encrypted);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Dispatcher.Post(() => OnError?.Invoke($"Send error: {ex.Message}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendPlainAsync(Message message)
|
||||||
|
{
|
||||||
|
if (_stream == null) return;
|
||||||
|
var data = MessageSerializer.Serialize(message);
|
||||||
|
await SendDataAsync(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendData(byte[] data)
|
||||||
|
{
|
||||||
|
if (_stream == null) return;
|
||||||
|
|
||||||
|
var lengthBuffer = BitConverter.GetBytes(data.Length);
|
||||||
|
if (BitConverter.IsLittleEndian)
|
||||||
|
Array.Reverse(lengthBuffer);
|
||||||
|
|
||||||
|
_stream.Write(lengthBuffer, 0, 4);
|
||||||
|
_stream.Write(data, 0, data.Length);
|
||||||
|
_stream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendDataAsync(byte[] data)
|
||||||
|
{
|
||||||
|
if (_stream == null) return;
|
||||||
|
|
||||||
|
var lengthBuffer = BitConverter.GetBytes(data.Length);
|
||||||
|
if (BitConverter.IsLittleEndian)
|
||||||
|
Array.Reverse(lengthBuffer);
|
||||||
|
|
||||||
|
await _stream.WriteAsync(lengthBuffer, 0, 4);
|
||||||
|
await _stream.WriteAsync(data, 0, data.Length);
|
||||||
|
await _stream.FlushAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Receiving
|
||||||
|
|
||||||
|
private async Task ReceiveLoopAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
int decryptFailures = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!ct.IsCancellationRequested && IsConnected)
|
||||||
|
{
|
||||||
|
var data = await ReadMessageAsync();
|
||||||
|
if (data == null) break;
|
||||||
|
|
||||||
|
var decrypted = _encryption?.Decrypt(data);
|
||||||
|
if (decrypted == null)
|
||||||
|
{
|
||||||
|
decryptFailures++;
|
||||||
|
if (decryptFailures >= 3)
|
||||||
|
{
|
||||||
|
Disconnect("Too many decryption failures");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptFailures = 0;
|
||||||
|
|
||||||
|
var message = MessageSerializer.Deserialize(decrypted);
|
||||||
|
if (message != null)
|
||||||
|
{
|
||||||
|
ProcessMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (!ct.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Disconnect($"Connection error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<byte[]?> ReadMessageAsync()
|
||||||
|
{
|
||||||
|
if (_stream == null) return null;
|
||||||
|
|
||||||
|
var lengthBuffer = new byte[4];
|
||||||
|
var read = await _stream.ReadAsync(lengthBuffer, 0, 4);
|
||||||
|
if (read < 4) return null;
|
||||||
|
|
||||||
|
if (BitConverter.IsLittleEndian)
|
||||||
|
Array.Reverse(lengthBuffer);
|
||||||
|
var length = BitConverter.ToInt32(lengthBuffer, 0);
|
||||||
|
|
||||||
|
if (length <= 0 || length > 1048576) return null;
|
||||||
|
|
||||||
|
var buffer = new byte[length];
|
||||||
|
var totalRead = 0;
|
||||||
|
while (totalRead < length)
|
||||||
|
{
|
||||||
|
read = await _stream.ReadAsync(buffer, totalRead, length - totalRead);
|
||||||
|
if (read == 0) return null;
|
||||||
|
totalRead += read;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessMessage(Message message)
|
||||||
|
{
|
||||||
|
// Zpracujeme speciální typy
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case CreateLobbyResponse r:
|
||||||
|
if (r.Success)
|
||||||
|
{
|
||||||
|
LobbyId = r.LobbyId;
|
||||||
|
JoinCode = r.JoinCode;
|
||||||
|
CurrentLobbyState = r.LobbyState;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JoinLobbyResponse r:
|
||||||
|
if (r.Success)
|
||||||
|
{
|
||||||
|
LobbyId = r.LobbyId;
|
||||||
|
CurrentLobbyState = r.LobbyState;
|
||||||
|
JoinCode = r.LobbyState?.JoinCode;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PositionBroadcast b:
|
||||||
|
ProcessPositionBroadcast(b);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Pong p:
|
||||||
|
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
Ping = (int)(now - p.ClientTime);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GameEvent evt:
|
||||||
|
ProcessGameEvent(evt);
|
||||||
|
Dispatcher.Post(() => OnGameEvent?.Invoke(evt));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatcher.Post(() => OnMessage?.Invoke(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessPositionBroadcast(PositionBroadcast broadcast)
|
||||||
|
{
|
||||||
|
PlayerPositions.Clear();
|
||||||
|
foreach (var player in broadcast.Players)
|
||||||
|
{
|
||||||
|
PlayerPositions[player.ClientUuid] = player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessGameEvent(GameEvent evt)
|
||||||
|
{
|
||||||
|
LastEventId = evt.EventId;
|
||||||
|
|
||||||
|
switch (evt.EventType)
|
||||||
|
{
|
||||||
|
case "PlayerJoined":
|
||||||
|
// Add player to lobby state
|
||||||
|
var joinedPayload = evt.GetPayload<PlayerJoinedPayload>();
|
||||||
|
if (joinedPayload != null && CurrentLobbyState?.Players != null)
|
||||||
|
{
|
||||||
|
// Check if player already exists
|
||||||
|
bool exists = CurrentLobbyState.Players.Any(p => p.ClientUuid == joinedPayload.ClientUuid);
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
CurrentLobbyState.Players.Add(new PlayerInfo
|
||||||
|
{
|
||||||
|
ClientUuid = joinedPayload.ClientUuid,
|
||||||
|
DisplayName = joinedPayload.DisplayName,
|
||||||
|
IsOwner = false,
|
||||||
|
IsReady = false,
|
||||||
|
State = PlayerState.Alive
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "PlayerLeft":
|
||||||
|
// Remove player from lobby state
|
||||||
|
var leftPayload = evt.GetPayload<PlayerLeftPayload>();
|
||||||
|
if (leftPayload != null && CurrentLobbyState?.Players != null)
|
||||||
|
{
|
||||||
|
CurrentLobbyState.Players.RemoveAll(p => p.ClientUuid == leftPayload.ClientUuid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "HostChanged":
|
||||||
|
// Update lobby owner
|
||||||
|
var hostPayload = evt.GetPayload<HostChangedPayload>();
|
||||||
|
if (hostPayload != null && CurrentLobbyState != null)
|
||||||
|
{
|
||||||
|
CurrentLobbyState.OwnerId = hostPayload.NewHostId;
|
||||||
|
// Update IsOwner flag on all players
|
||||||
|
foreach (var player in CurrentLobbyState.Players)
|
||||||
|
{
|
||||||
|
player.IsOwner = player.ClientUuid == hostPayload.NewHostId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "GameStarting":
|
||||||
|
// Game is entering loading phase - update lobby state if available
|
||||||
|
if (CurrentLobbyState != null)
|
||||||
|
{
|
||||||
|
CurrentLobbyState.Phase = GamePhase.Loading;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "MapDataReady":
|
||||||
|
// Map data received - store it and send confirmation
|
||||||
|
var mapDataPayload = evt.GetPayload<MapDataReadyPayload>();
|
||||||
|
if (mapDataPayload != null && CurrentLobbyState != null)
|
||||||
|
{
|
||||||
|
CurrentLobbyState.MapData = mapDataPayload.MapData;
|
||||||
|
CurrentLobbyState.MapDataReady = true;
|
||||||
|
}
|
||||||
|
// Send confirmation to server
|
||||||
|
Send(new MapDataReceived());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "GameStarted":
|
||||||
|
// Game officially started - update phase
|
||||||
|
if (CurrentLobbyState != null)
|
||||||
|
{
|
||||||
|
CurrentLobbyState.Phase = GamePhase.Playing;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "RoleAssigned":
|
||||||
|
var rolePayload = evt.GetPayload<RoleAssignedPayload>();
|
||||||
|
if (rolePayload != null && rolePayload.ClientUuid == ClientUuid)
|
||||||
|
{
|
||||||
|
MyRole = rolePayload.Role;
|
||||||
|
MyTasks.Clear();
|
||||||
|
if (rolePayload.Tasks != null)
|
||||||
|
{
|
||||||
|
MyTasks.AddRange(rolePayload.Tasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "PlayerKilled":
|
||||||
|
var killPayload = evt.GetPayload<PlayerKilledPayload>();
|
||||||
|
if (killPayload != null)
|
||||||
|
{
|
||||||
|
Bodies.Add(new Body
|
||||||
|
{
|
||||||
|
BodyId = killPayload.BodyId,
|
||||||
|
VictimId = killPayload.VictimId,
|
||||||
|
Location = killPayload.Location
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "MeetingStarted":
|
||||||
|
if (CurrentLobbyState != null)
|
||||||
|
{
|
||||||
|
CurrentLobbyState.Phase = GamePhase.Meeting;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "VotingClosed":
|
||||||
|
Bodies.Clear(); // Bodies zmizí po meetingu
|
||||||
|
if (CurrentLobbyState != null)
|
||||||
|
{
|
||||||
|
CurrentLobbyState.Phase = GamePhase.Playing;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "GameEnded":
|
||||||
|
if (CurrentLobbyState != null)
|
||||||
|
{
|
||||||
|
CurrentLobbyState.Phase = GamePhase.Ended;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Game Actions
|
||||||
|
|
||||||
|
public void CreateLobby(Position? center = null, int impostorCount = 1, int taskCount = 5, string? password = null, double playAreaRadius = 500)
|
||||||
|
{
|
||||||
|
Send(new CreateLobby
|
||||||
|
{
|
||||||
|
PlayAreaCenter = center,
|
||||||
|
PlayAreaRadius = playAreaRadius,
|
||||||
|
ImpostorCount = impostorCount,
|
||||||
|
TaskCount = taskCount,
|
||||||
|
Password = password
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void JoinLobby(string joinCode, string? password = null)
|
||||||
|
{
|
||||||
|
Send(new JoinLobby
|
||||||
|
{
|
||||||
|
JoinCode = joinCode.ToUpperInvariant(),
|
||||||
|
Password = password
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LeaveLobby()
|
||||||
|
{
|
||||||
|
Send(new LeaveLobby());
|
||||||
|
LobbyId = null;
|
||||||
|
JoinCode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartGame()
|
||||||
|
{
|
||||||
|
Send(new StartGame());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReturnToLobby()
|
||||||
|
{
|
||||||
|
Send(new ReturnToLobby());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePosition(Position position)
|
||||||
|
{
|
||||||
|
MyPosition = position;
|
||||||
|
Send(new UpdatePosition { Position = position });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Kill(string targetUuid)
|
||||||
|
{
|
||||||
|
Send(new KillAttempt { TargetClientUuid = targetUuid });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReportBody(string bodyId)
|
||||||
|
{
|
||||||
|
Send(new ReportBody { BodyId = bodyId });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CallEmergencyMeeting()
|
||||||
|
{
|
||||||
|
Send(new CallEmergencyMeeting());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Vote(string? targetUuid)
|
||||||
|
{
|
||||||
|
Send(new CastVote { TargetClientUuid = targetUuid });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pokus o dokončení tasku. Server ověří že hráč je na správné pozici.
|
||||||
|
/// </summary>
|
||||||
|
public void CompleteTask(string taskId)
|
||||||
|
{
|
||||||
|
Send(new TaskComplete { TaskId = taskId });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendPing()
|
||||||
|
{
|
||||||
|
Send(new Ping { ClientTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reconnect(string lobbyId)
|
||||||
|
{
|
||||||
|
Send(new Reconnect { LobbyId = lobbyId, LastEventId = LastEventId });
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
|
|
||||||
|
public Body? FindNearbyBody(double maxDistance)
|
||||||
|
{
|
||||||
|
foreach (var body in Bodies)
|
||||||
|
{
|
||||||
|
if (MyPosition.DistanceTo(body.Location) <= maxDistance)
|
||||||
|
{
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? FindNearbyPlayer(double maxDistance, bool aliveOnly = true)
|
||||||
|
{
|
||||||
|
foreach (var (uuid, info) in PlayerPositions)
|
||||||
|
{
|
||||||
|
if (uuid == ClientUuid) continue;
|
||||||
|
if (aliveOnly && info.State != PlayerState.Alive) continue;
|
||||||
|
|
||||||
|
if (MyPosition.DistanceTo(info.Position) <= maxDistance)
|
||||||
|
{
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameTask? FindNearbyTask(double maxDistance)
|
||||||
|
{
|
||||||
|
foreach (var task in MyTasks)
|
||||||
|
{
|
||||||
|
if (MyPosition.DistanceTo(task.Location) <= maxDistance)
|
||||||
|
{
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volat z Unity Update() pro zpracování callbacků
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
Dispatcher.ProcessPendingActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Disconnect("Disposed");
|
||||||
|
_encryption?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/ClientSDK/GameClient.cs.meta
Normal file
2
Assets/ClientSDK/GameClient.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 91e0f647c37b0b94b83f53bb854db28c
|
||||||
1055
Assets/ClientSDK/Protocol.cs
Normal file
1055
Assets/ClientSDK/Protocol.cs
Normal file
File diff suppressed because it is too large
Load Diff
2
Assets/ClientSDK/Protocol.cs.meta
Normal file
2
Assets/ClientSDK/Protocol.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 14463228dfea2264ebfc36c3a7dc4b99
|
||||||
1992
Assets/ClientSDK/SimulatorClient.cs
Normal file
1992
Assets/ClientSDK/SimulatorClient.cs
Normal file
File diff suppressed because it is too large
Load Diff
2
Assets/ClientSDK/SimulatorClient.cs.meta
Normal file
2
Assets/ClientSDK/SimulatorClient.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 80ef0979df5d1fe489225f3e5edadc5c
|
||||||
8
Assets/ClientSDK/bin.meta
Normal file
8
Assets/ClientSDK/bin.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a4035bdb812fee4f96cb1aa1b24c999
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/ClientSDK/obj.meta
Normal file
8
Assets/ClientSDK/obj.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 131d9de257c8edc49991d792c6e702f6
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
@@ -1,23 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 58ebc3ffa125a9949953f6704e0a8c39
|
|
||||||
AudioImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 8
|
|
||||||
defaultSettings:
|
|
||||||
serializedVersion: 2
|
|
||||||
loadType: 0
|
|
||||||
sampleRateSetting: 0
|
|
||||||
sampleRateOverride: 44100
|
|
||||||
compressionFormat: 1
|
|
||||||
quality: 1
|
|
||||||
conversionMode: 0
|
|
||||||
preloadAudioData: 0
|
|
||||||
platformSettingOverrides: {}
|
|
||||||
forceToMono: 0
|
|
||||||
normalize: 1
|
|
||||||
loadInBackground: 0
|
|
||||||
ambisonic: 0
|
|
||||||
3D: 1
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 74c99196e7d046a4497d6aa5ba358650
|
guid: 5fd2bf33031fe9d4ea3439b41d7f4b97
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
|
|||||||
92
Assets/GameManager/AreaMat.mat
Normal file
92
Assets/GameManager/AreaMat.mat
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!21 &2100000
|
||||||
|
Material:
|
||||||
|
serializedVersion: 8
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: AreaMat
|
||||||
|
m_Shader: {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_Parent: {fileID: 0}
|
||||||
|
m_ModifiedSerializedProperties: 0
|
||||||
|
m_ValidKeywords: []
|
||||||
|
m_InvalidKeywords: []
|
||||||
|
m_LightmapFlags: 4
|
||||||
|
m_EnableInstancingVariants: 0
|
||||||
|
m_DoubleSidedGI: 0
|
||||||
|
m_CustomRenderQueue: -1
|
||||||
|
stringTagMap: {}
|
||||||
|
disabledShaderPasses: []
|
||||||
|
m_LockedProperties:
|
||||||
|
m_SavedProperties:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TexEnvs:
|
||||||
|
- _AlphaTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _BumpMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailAlbedoMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailMask:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailNormalMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _EmissionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MainTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MetallicGlossMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _OcclusionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _ParallaxMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_Ints: []
|
||||||
|
m_Floats:
|
||||||
|
- PixelSnap: 0
|
||||||
|
- _BumpScale: 1
|
||||||
|
- _Cutoff: 0.5
|
||||||
|
- _DetailNormalMapScale: 1
|
||||||
|
- _DstBlend: 0
|
||||||
|
- _EnableExternalAlpha: 0
|
||||||
|
- _GlossMapScale: 1
|
||||||
|
- _Glossiness: 0.5
|
||||||
|
- _GlossyReflections: 1
|
||||||
|
- _Metallic: 0
|
||||||
|
- _Mode: 0
|
||||||
|
- _OcclusionStrength: 1
|
||||||
|
- _Parallax: 0.02
|
||||||
|
- _SmoothnessTextureChannel: 0
|
||||||
|
- _SpecularHighlights: 1
|
||||||
|
- _SrcBlend: 1
|
||||||
|
- _UVSec: 0
|
||||||
|
- _ZWrite: 1
|
||||||
|
m_Colors:
|
||||||
|
- _Color: {r: 0.0813297, g: 1, b: 0, a: 1}
|
||||||
|
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
|
- _Flip: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
- _RendererColor: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
m_BuildTextureStacks: []
|
||||||
|
m_AllowLocking: 1
|
||||||
8
Assets/GameManager/AreaMat.mat.meta
Normal file
8
Assets/GameManager/AreaMat.mat.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5a46533bdf4003449bc9146ccef44e27
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 2100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
158
Assets/GameManager/GameManager.cs
Normal file
158
Assets/GameManager/GameManager.cs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using GeoSus.Client;
|
||||||
|
using Subsystems;
|
||||||
|
using System.Collections;
|
||||||
|
using System;
|
||||||
|
using TMPro;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
/*
|
||||||
|
GameManager - hlavní tøida pro správu hry
|
||||||
|
GameManager_Network - subsystém pro správu komunikace se serverem
|
||||||
|
GameManager_Game - subsystém pro správu logiky hry (sabotáže, tasky, atd.)
|
||||||
|
GameManager_Map - subsystém pro správu mapy a prostøedí
|
||||||
|
GameManager_Input - subsystém pro správu vstupu od hráèe
|
||||||
|
GameManager_UI - subsystém pro správu uživatelského rozhraní
|
||||||
|
GamaManager_Stats - subsystém pro správu statistik pro server
|
||||||
|
*/
|
||||||
|
public class GameManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Subsystems")]
|
||||||
|
protected GameManager_Network networkSubsystem;
|
||||||
|
protected GameManager_UI uiSubsystem;
|
||||||
|
protected GameManager_Map mapSubsystem;
|
||||||
|
protected GameManager_Input inputSubsystem;
|
||||||
|
protected GameManager_Game gameSubsystem;
|
||||||
|
|
||||||
|
protected GameClient gameClient;
|
||||||
|
|
||||||
|
[Header("Player Info")]
|
||||||
|
public string displayName;
|
||||||
|
|
||||||
|
[Header("UI Elements")]
|
||||||
|
public Canvas JoinCreateLobby;
|
||||||
|
public Canvas InLobby;
|
||||||
|
public Canvas LoadingScreen;
|
||||||
|
public Canvas GameScreen;
|
||||||
|
|
||||||
|
|
||||||
|
[Header("Map")]
|
||||||
|
public GameObject MapCenterPoint;
|
||||||
|
public BuildingSettings buildingSettings;
|
||||||
|
public PathwaySettings pathwaySettings;
|
||||||
|
public AreaSettings areaSettings;
|
||||||
|
|
||||||
|
[Header("GPS")]
|
||||||
|
public GameObject Player;
|
||||||
|
|
||||||
|
[Header("Debug")]
|
||||||
|
public bool testMode = false;
|
||||||
|
private GameClient _secondClient;
|
||||||
|
private GameClient _thirdClient;
|
||||||
|
private GameManager_Network _secondNetwork;
|
||||||
|
private GameManager_Network _thirdNetwork;
|
||||||
|
|
||||||
|
[Header("Tasks")]
|
||||||
|
public List<TaskData> AvailableTasks = new List<TaskData>();
|
||||||
|
public StationSettings settings = new StationSettings();
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
DontDestroyOnLoad(this);
|
||||||
|
if (displayName == null || displayName == "")
|
||||||
|
{
|
||||||
|
displayName = GenerateUsername();
|
||||||
|
}
|
||||||
|
if (testMode)
|
||||||
|
{
|
||||||
|
_secondClient = new GameClient(GenerateUUID(), GenerateUsername());
|
||||||
|
_secondNetwork = new GameManager_Network(_secondClient);
|
||||||
|
_thirdClient = new GameClient(GenerateUUID(), GenerateUsername());
|
||||||
|
_thirdNetwork = new GameManager_Network(_thirdClient);
|
||||||
|
|
||||||
|
_secondNetwork.OpenConection();
|
||||||
|
_thirdNetwork.OpenConection();
|
||||||
|
}
|
||||||
|
gameClient = new GameClient(GenerateUUID(), displayName);
|
||||||
|
uiSubsystem = new GameManager_UI(gameClient, JoinCreateLobby, InLobby, LoadingScreen, GameScreen);
|
||||||
|
networkSubsystem = new GameManager_Network(gameClient);
|
||||||
|
mapSubsystem = new GameManager_Map(gameClient, MapCenterPoint, buildingSettings, pathwaySettings, areaSettings);
|
||||||
|
inputSubsystem = new GameManager_Input(gameClient, Player, testMode);
|
||||||
|
gameSubsystem = new GameManager_Game(gameClient, Player, MapCenterPoint, AvailableTasks);
|
||||||
|
networkSubsystem.OpenConection();
|
||||||
|
}
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (gameClient.CurrentLobbyState != null)
|
||||||
|
{
|
||||||
|
uiSubsystem.UpdateLobbyUI();
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (gameClient.CurrentLobbyState.MapDataReady)
|
||||||
|
{
|
||||||
|
mapSubsystem.BuildMap();
|
||||||
|
gameClient.CurrentLobbyState.MapDataReady = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NullReferenceException ex) { }
|
||||||
|
inputSubsystem.positionCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected string GenerateUUID()
|
||||||
|
{
|
||||||
|
string UUID = System.Guid.NewGuid().ToString();
|
||||||
|
Debug.Log(UUID);
|
||||||
|
return UUID;
|
||||||
|
}
|
||||||
|
protected string GenerateUsername()
|
||||||
|
{
|
||||||
|
string Username = UnityEngine.Random.Range(0,10).ToString() + UnityEngine.Random.Range(0, 10).ToString() + UnityEngine.Random.Range(0, 10).ToString() + UnityEngine.Random.Range(0, 10).ToString();
|
||||||
|
Debug.Log(Username);
|
||||||
|
return Username;
|
||||||
|
}
|
||||||
|
public void CreateLobbyButton()
|
||||||
|
{
|
||||||
|
networkSubsystem.CrateLobby(50.7727264, 15.0719876);
|
||||||
|
if (testMode)
|
||||||
|
{
|
||||||
|
StartCoroutine(ConnectTestClients());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void JoinLobbyButton()
|
||||||
|
{
|
||||||
|
TMP_InputField joinCode = JoinCreateLobby.transform.Find("InputCode").GetComponent<TMP_InputField>();
|
||||||
|
if (joinCode.text != null && joinCode.text != "")
|
||||||
|
{
|
||||||
|
networkSubsystem.JoinLobby(joinCode.text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("Join code is empty!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void LeaveLobbyButton()
|
||||||
|
{
|
||||||
|
networkSubsystem.LeaveLobby();
|
||||||
|
}
|
||||||
|
public void StartGameButton()
|
||||||
|
{
|
||||||
|
networkSubsystem.StartGame();
|
||||||
|
}
|
||||||
|
public void Interact()
|
||||||
|
{
|
||||||
|
//TODO: Interakce s úkoly
|
||||||
|
}
|
||||||
|
void OnApplicationQuit()
|
||||||
|
{
|
||||||
|
gameClient.Disconnect();
|
||||||
|
_secondClient?.Disconnect();
|
||||||
|
_thirdClient?.Disconnect();
|
||||||
|
}
|
||||||
|
IEnumerator ConnectTestClients()
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(2f);
|
||||||
|
_secondNetwork.JoinLobby(gameClient.CurrentLobbyState.JoinCode);
|
||||||
|
_thirdNetwork.JoinLobby(gameClient.CurrentLobbyState.JoinCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/GameManager/GameManager.cs.meta
Normal file
2
Assets/GameManager/GameManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9e2c3e4ba4e36ea40a686e58feca4d2b
|
||||||
141
Assets/GameManager/GameManager_Game.cs
Normal file
141
Assets/GameManager/GameManager_Game.cs
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
using GeoSus.Client;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
using static UnityEngine.Rendering.RayTracingAccelerationStructure;
|
||||||
|
|
||||||
|
namespace Subsystems
|
||||||
|
{
|
||||||
|
[System.Serializable]
|
||||||
|
public class StationSettings
|
||||||
|
{
|
||||||
|
public GameObject TaskStationPrefab;
|
||||||
|
public GameObject SabotageStationPrefab;
|
||||||
|
public GameObject MeetingStationPrefab;
|
||||||
|
public GameObject BodyStationPrefab;
|
||||||
|
|
||||||
|
}
|
||||||
|
public class GameManager_Game
|
||||||
|
{
|
||||||
|
private GameClient _gameClient;
|
||||||
|
private GameObject _player;
|
||||||
|
private GameObject _map;
|
||||||
|
private float _range;
|
||||||
|
private List<TaskData> _availableTasks;
|
||||||
|
public List<GameObject> Stations { get; private set; }
|
||||||
|
public List<GameObject> TaskStations { get; private set; } = new List<GameObject>();
|
||||||
|
private StationSettings _stationSettings;
|
||||||
|
public GameManager_Game(GameClient client, GameObject player, GameObject map, List<TaskData> availableTasks, float range = 20f, StationSettings stationSettings = null)
|
||||||
|
{
|
||||||
|
_gameClient = client;
|
||||||
|
_player = player;
|
||||||
|
_map = map;
|
||||||
|
_availableTasks = availableTasks;
|
||||||
|
_range = range;
|
||||||
|
_stationSettings = stationSettings;
|
||||||
|
}
|
||||||
|
public bool CheckSightLine(Vector3 target)
|
||||||
|
{
|
||||||
|
RaycastHit hit;
|
||||||
|
Vector3 direction = target - _player.transform.position;
|
||||||
|
Ray ray = new Ray(new Vector3(_player.transform.position.x, 0.1f, _player.transform.position.z), direction);
|
||||||
|
Physics.Raycast(ray, out hit, _range);
|
||||||
|
if (hit.collider.tag == "Player")
|
||||||
|
{
|
||||||
|
Debug.Log("Target is visible");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("Target is not visible");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public void InitializeTaskStations()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < _gameClient.MyTasks.Count; i++)
|
||||||
|
{
|
||||||
|
System.Random rnd = new System.Random();
|
||||||
|
var task = _availableTasks[rnd.Next(0,_availableTasks.Count)];
|
||||||
|
CreateStation(_gameClient.MyTasks[i].Location, StationType.Task, _gameClient.MyTasks[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void CreateStation(Position pos, StationType type)
|
||||||
|
{
|
||||||
|
GameObject stationPrefab = null;
|
||||||
|
PlayerRole? reqRole = null;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case StationType.Task:
|
||||||
|
stationPrefab = _stationSettings.TaskStationPrefab;
|
||||||
|
reqRole = PlayerRole.Crew;
|
||||||
|
Debug.LogError("Task station creation not fully implemented, using task station prefab as placeholder");
|
||||||
|
break;
|
||||||
|
case StationType.Sabotage:
|
||||||
|
stationPrefab = _stationSettings.SabotageStationPrefab;
|
||||||
|
break;
|
||||||
|
case StationType.Meeting:
|
||||||
|
stationPrefab = _stationSettings.MeetingStationPrefab;
|
||||||
|
break;
|
||||||
|
case StationType.Body:
|
||||||
|
stationPrefab = _stationSettings.BodyStationPrefab;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Debug.LogError("Invalid station type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var station = UnityEngine.Object.Instantiate(stationPrefab);
|
||||||
|
station.transform.position = pos.ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center);
|
||||||
|
Stations.Add(station);
|
||||||
|
IInteractable interactable = station.GetComponent<IInteractable>();
|
||||||
|
interactable.Location = pos;
|
||||||
|
interactable.InteractionRange = _range;
|
||||||
|
/*Stations = new List<GameObject>();
|
||||||
|
foreach (var task in _gameClient.MyTasks)
|
||||||
|
{
|
||||||
|
System.Random rnd = new System.Random();
|
||||||
|
int index = rnd.Next(0, _availableTasks.Count);
|
||||||
|
var station = UnityEngine.Object.Instantiate(_stationPrefab);
|
||||||
|
ITask TaskSettings = station.GetComponent<ITask>();
|
||||||
|
TaskSettings.TaskID = task.TaskId;
|
||||||
|
TaskSettings.TaskLocation = task.Location;
|
||||||
|
station.transform.position = TaskSettings.TaskLocation.ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center);
|
||||||
|
station.SetActive(false);
|
||||||
|
|
||||||
|
Stations.Add(station);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
}
|
||||||
|
private void CreateStation(Position pos, StationType type, GameTask taskInfo)
|
||||||
|
{
|
||||||
|
GameObject stationPrefab = _stationSettings.TaskStationPrefab;
|
||||||
|
var station = UnityEngine.Object.Instantiate(stationPrefab);
|
||||||
|
station.transform.position = pos.ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center);
|
||||||
|
Stations.Add(station);
|
||||||
|
TaskStation interactable = station.GetComponent<TaskStation>();
|
||||||
|
interactable.Location = pos;
|
||||||
|
interactable.InteractionRange = _range;
|
||||||
|
interactable.TaskID = taskInfo.TaskId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckForPlayers()
|
||||||
|
{
|
||||||
|
foreach (var player in _gameClient.PlayerPositions.Where(p => p.Value.State == PlayerState.Alive))
|
||||||
|
{
|
||||||
|
if (CheckSightLine(player.Value.Position.ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center)))
|
||||||
|
{
|
||||||
|
Debug.Log($"Player {player.Key} is visible");
|
||||||
|
//TODO: Render player on map
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log($"Player {player.Key} is not visible");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/GameManager/GameManager_Game.cs.meta
Normal file
2
Assets/GameManager/GameManager_Game.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aba57c59fb2a19141a4868fa6a5c924c
|
||||||
254
Assets/GameManager/GameManager_Input.cs
Normal file
254
Assets/GameManager/GameManager_Input.cs
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
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 = new CoroutineHost();
|
||||||
|
public GameManager_Input(GameClient gameClient, GameObject player, bool testMode)
|
||||||
|
{
|
||||||
|
_gameClient = gameClient;
|
||||||
|
_player = player;
|
||||||
|
_testMode = testMode;
|
||||||
|
}
|
||||||
|
public void positionCheck()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_gameClient.CurrentLobbyState.Phase == GamePhase.Playing)
|
||||||
|
{
|
||||||
|
if (_testMode)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_currentPosition == null || _currentPosition == new Position(0, 0))
|
||||||
|
{
|
||||||
|
//Init blok
|
||||||
|
_currentPosition = _gameClient.CurrentLobbyState.MapData.Center;
|
||||||
|
_mapCenter = _gameClient.CurrentLobbyState.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)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_currentPosition != _lastSentPosition)
|
||||||
|
{
|
||||||
|
_gameClient.UpdatePosition(_currentPosition);
|
||||||
|
_lastSentPosition = _currentPosition;
|
||||||
|
_player.transform.position = _currentPosition.ToLocalVector3(_mapCenter);
|
||||||
|
_player.transform.rotation = Quaternion.Euler(0, (float)CalculateHeading(_lastSentPosition.ToLocalVector3(_mapCenter), _currentPosition.ToLocalVector3(_mapCenter)), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.Log(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("GPS failed, trying again...");
|
||||||
|
_GPSState = GPSState.Uninitialized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NullReferenceException ex) { Debug.Log(ex); }
|
||||||
|
}
|
||||||
|
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
|
||||||
|
{
|
||||||
|
if (_currentPosition != _lastSentPosition)
|
||||||
|
{
|
||||||
|
_gameClient.UpdatePosition(_currentPosition);
|
||||||
|
_lastSentPosition = _currentPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_gameClient.UpdatePosition(_currentPosition);
|
||||||
|
_lastSentPosition = _currentPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private double? CalculateHeading(Vector3 first, Vector3 second)
|
||||||
|
{
|
||||||
|
double? heading = null;
|
||||||
|
if ((first - second).magnitude == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if (first.x == second.x && first.z < second.z)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (first.x == second.x && first.z > second.z)
|
||||||
|
{
|
||||||
|
return 180;
|
||||||
|
}
|
||||||
|
else if (first.x > second.x && first.z == second.z)
|
||||||
|
{
|
||||||
|
return 270;
|
||||||
|
}
|
||||||
|
else if (first.x < second.x && first.z == second.z)
|
||||||
|
{
|
||||||
|
return 90;
|
||||||
|
}
|
||||||
|
else if (first.x < second.x && first.z < second.z)
|
||||||
|
{
|
||||||
|
heading = Math.Asin((second.z - first.z) / first.DistanceTo(second));
|
||||||
|
return (heading * 180) / Math.PI;
|
||||||
|
}
|
||||||
|
else if (first.x < second.x && first.z > second.z)
|
||||||
|
{
|
||||||
|
heading = Math.Asin((second.z - first.z) / first.DistanceTo(second));
|
||||||
|
return (heading * 180) / Math.PI + 180;
|
||||||
|
}
|
||||||
|
else if (first.x > second.x && first.z < second.z)
|
||||||
|
{
|
||||||
|
heading = Math.Asin((second.z - first.z) / first.DistanceTo(second));
|
||||||
|
return (heading * 180) / Math.PI - 90;
|
||||||
|
}
|
||||||
|
else if (first.x > second.x && first.z > second.z)
|
||||||
|
{
|
||||||
|
heading = Math.Asin((second.z - first.z) / first.DistanceTo(second));
|
||||||
|
return (heading * 180) / Math.PI - 90;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IEnumerator InitiallizeGPS()
|
||||||
|
{
|
||||||
|
_GPSState = GPSState.Initializing;
|
||||||
|
if (!Input.location.isEnabledByUser)
|
||||||
|
{
|
||||||
|
Debug.LogError("Location not enabled on device or app does not have permission to access location");
|
||||||
|
}
|
||||||
|
// Starts the location service.
|
||||||
|
|
||||||
|
float desiredAccuracyInMeters = 10f;
|
||||||
|
float updateDistanceInMeters = 10f;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
_GPSState = GPSState.Running;
|
||||||
|
yield return _coroutineHost.StartCoroutine(GPSService());
|
||||||
|
}
|
||||||
|
IEnumerator GPSService()
|
||||||
|
{
|
||||||
|
// Check if the user has location service enabled.
|
||||||
|
|
||||||
|
|
||||||
|
// If the connection failed this cancels location service use.
|
||||||
|
if (Input.location.status == LocationServiceStatus.Failed)
|
||||||
|
{
|
||||||
|
Debug.LogError("Unable to determine device location");
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the connection succeeded, this retrieves the device's current location and displays it in the Console window.
|
||||||
|
_currentPosition = new Position(Input.location.lastData.latitude, Input.location.lastData.longitude);
|
||||||
|
Debug.Log("Location: " + Input.location.lastData.latitude + " " + Input.location.lastData.longitude + " " + Input.location.lastData.altitude + " " + Input.location.lastData.horizontalAccuracy + " " + Input.location.lastData.timestamp);
|
||||||
|
yield return new WaitForSeconds(5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stops the location service if there is no need to query location updates continuously.
|
||||||
|
yield return _coroutineHost.StartCoroutine(GPSService());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/GameManager/GameManager_Input.cs.meta
Normal file
2
Assets/GameManager/GameManager_Input.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2ef1abfb1e85a7943925f9dc3cfea742
|
||||||
381
Assets/GameManager/GameManager_Map.cs
Normal file
381
Assets/GameManager/GameManager_Map.cs
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
using GeoSus.Client;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Localization.Pseudo;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Subsystems{
|
||||||
|
[System.Serializable]
|
||||||
|
public class BuildingSettings
|
||||||
|
{
|
||||||
|
public Material ResidentalBuildingsMat;
|
||||||
|
public float ResidentalBuildingHeight;
|
||||||
|
public Material CommercialBuildingsMat;
|
||||||
|
public float CommercialBuildingHeight;
|
||||||
|
public Material IndustrialBuildingsMat;
|
||||||
|
public float IndustrialBuildingHeight;
|
||||||
|
public Material DefaultBuildingMat;
|
||||||
|
public float DefaultBuildingHeight;
|
||||||
|
}
|
||||||
|
[System.Serializable]
|
||||||
|
public class PathwaySettings
|
||||||
|
{
|
||||||
|
public Material FootwayMat;
|
||||||
|
public float FootwayWidth;
|
||||||
|
public Material PathMat;
|
||||||
|
public float PathWidth;
|
||||||
|
public Material StepsMat;
|
||||||
|
public float StepsWidth;
|
||||||
|
public Material CyclewayMat;
|
||||||
|
public float CyclewayWidth;
|
||||||
|
public Material PedestrianMat;
|
||||||
|
public float PedestrianWidth;
|
||||||
|
public Material RoadMat;
|
||||||
|
public float RoadWidth;
|
||||||
|
public Material ServiceMat;
|
||||||
|
public float ServiceWidth;
|
||||||
|
public Material ResidentialMat;
|
||||||
|
public float ResidentialWidth;
|
||||||
|
public Material TrackMat;
|
||||||
|
public float TrackWidth;
|
||||||
|
public Material DefaultMat;
|
||||||
|
public float DefaultWidth;
|
||||||
|
}
|
||||||
|
[System.Serializable]
|
||||||
|
public class AreaSettings
|
||||||
|
{
|
||||||
|
public Material ParkMat;
|
||||||
|
public Material GardenMat;
|
||||||
|
public Material PlaygroundMat;
|
||||||
|
public Material ForestMat;
|
||||||
|
public Material GrassMat;
|
||||||
|
public Material WaterMat;
|
||||||
|
public Material DefaultMat;
|
||||||
|
}
|
||||||
|
public class GameManager_Map
|
||||||
|
{
|
||||||
|
private GameClient _gameClient;
|
||||||
|
private GameObject _mapCenterPoint;
|
||||||
|
private Position _centerPosition;
|
||||||
|
private BuildingSettings _buildingSettings;
|
||||||
|
private PathwaySettings _pathwaySettings;
|
||||||
|
private AreaSettings _areaSettings;
|
||||||
|
private const float _metersPerUnit = 1f;
|
||||||
|
public GameManager_Map(GameClient gameClient, GameObject mapCenterPoint, BuildingSettings buildingSettings, PathwaySettings pathwaySettings, AreaSettings areaSettings)
|
||||||
|
{
|
||||||
|
_gameClient = gameClient;
|
||||||
|
_mapCenterPoint = mapCenterPoint;
|
||||||
|
_buildingSettings = buildingSettings;
|
||||||
|
_pathwaySettings = pathwaySettings;
|
||||||
|
_areaSettings = areaSettings;
|
||||||
|
}
|
||||||
|
public void BuildMap()
|
||||||
|
{
|
||||||
|
ClearChildren();
|
||||||
|
_centerPosition = _gameClient.CurrentLobbyState.MapData.Center;
|
||||||
|
GameObject buildingsRoot = new GameObject("Buildings");
|
||||||
|
buildingsRoot.transform.parent = _mapCenterPoint.transform;
|
||||||
|
|
||||||
|
GameObject pathRoot = new GameObject("Pathways");
|
||||||
|
pathRoot.transform.parent = _mapCenterPoint.transform;
|
||||||
|
|
||||||
|
GameObject areaRoot = new GameObject("Areas");
|
||||||
|
areaRoot.transform.parent = _mapCenterPoint.transform;
|
||||||
|
|
||||||
|
foreach (var building in _gameClient.CurrentLobbyState.MapData.GetBuildings())
|
||||||
|
{
|
||||||
|
string buildingType = "Unknown";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
buildingType = _gameClient.CurrentLobbyState.MapData.BuildingTypes[_gameClient.CurrentLobbyState.MapData.GetBuildings().IndexOf(building)];
|
||||||
|
}
|
||||||
|
catch (Exception ex) { Debug.Log($"Error: {ex.Message}"); }
|
||||||
|
building.Name = buildingType;
|
||||||
|
GameObject b = BuildBuildingMesh(building);
|
||||||
|
b.transform.parent = buildingsRoot.transform;
|
||||||
|
}
|
||||||
|
foreach (var path in _gameClient.CurrentLobbyState.MapData.GetPathways())
|
||||||
|
{
|
||||||
|
GameObject p = BuildPathwayMesh(path);
|
||||||
|
p.transform.parent = pathRoot.transform;
|
||||||
|
}
|
||||||
|
foreach (var area in _gameClient.CurrentLobbyState.MapData.GetAreas())
|
||||||
|
{
|
||||||
|
GameObject a = BuildAreaMesh(area);
|
||||||
|
a.transform.parent = areaRoot.transform;
|
||||||
|
}
|
||||||
|
//TODO: POIs
|
||||||
|
}
|
||||||
|
void ClearChildren()
|
||||||
|
{
|
||||||
|
List<GameObject> toDestroy = new List<GameObject>();
|
||||||
|
foreach (Transform t in _mapCenterPoint.transform)
|
||||||
|
toDestroy.Add(t.gameObject);
|
||||||
|
foreach (var g in toDestroy)
|
||||||
|
{
|
||||||
|
UnityEngine.Object.DestroyImmediate(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#region Mesh Building
|
||||||
|
GameObject BuildBuildingMesh(MapBuilding b)
|
||||||
|
{
|
||||||
|
var building = new GameObject($"Building_{b.Name ?? "Unknown"}");
|
||||||
|
|
||||||
|
// Výpočet středu budovy
|
||||||
|
Vector3 center = CalculatePolygonCenter(b.Outline);
|
||||||
|
building.transform.position = center;
|
||||||
|
|
||||||
|
// Vytvoření mesh pro budovu
|
||||||
|
MeshFilter meshFilter = building.AddComponent<MeshFilter>();
|
||||||
|
MeshRenderer meshRenderer = building.AddComponent<MeshRenderer>();
|
||||||
|
MeshCollider meshCollider = building.AddComponent<MeshCollider>();
|
||||||
|
building.tag = "Map";
|
||||||
|
|
||||||
|
float height;
|
||||||
|
Material mat;
|
||||||
|
switch (b.BuildingType.ToLower())
|
||||||
|
{
|
||||||
|
case "residential":
|
||||||
|
mat = _buildingSettings.ResidentalBuildingsMat;
|
||||||
|
height = _buildingSettings.ResidentalBuildingHeight;
|
||||||
|
break;
|
||||||
|
case "commercial":
|
||||||
|
mat = _buildingSettings.CommercialBuildingsMat;
|
||||||
|
height = _buildingSettings.CommercialBuildingHeight;
|
||||||
|
break;
|
||||||
|
case "industrial":
|
||||||
|
mat = _buildingSettings.IndustrialBuildingsMat;
|
||||||
|
height = _buildingSettings.IndustrialBuildingHeight;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mat = _buildingSettings.DefaultBuildingMat;
|
||||||
|
height = _buildingSettings.DefaultBuildingHeight;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Mesh mesh = CreateExtrudedPolygonMesh(b.Outline, height);
|
||||||
|
meshFilter.mesh = mesh;
|
||||||
|
meshCollider.sharedMesh = mesh;
|
||||||
|
|
||||||
|
//TODO: material by type
|
||||||
|
// Použijeme barvu podle typu budovy
|
||||||
|
meshRenderer.material = mat;
|
||||||
|
|
||||||
|
// Přidání collideru pro interakci
|
||||||
|
building.AddComponent<MeshCollider>();
|
||||||
|
return building;
|
||||||
|
}
|
||||||
|
GameObject BuildPathwayMesh(MapPathway w)
|
||||||
|
{
|
||||||
|
var path = new GameObject($"Path_{w.Name ?? "Unknown"}");
|
||||||
|
path.tag = "Map";
|
||||||
|
|
||||||
|
// Použijeme LineRenderer pro jednoduchost
|
||||||
|
LineRenderer line = path.AddComponent<LineRenderer>();
|
||||||
|
float width;
|
||||||
|
Material mat;
|
||||||
|
|
||||||
|
switch (w.PathType)
|
||||||
|
{
|
||||||
|
case PathType.Footway:
|
||||||
|
mat = _pathwaySettings.FootwayMat;
|
||||||
|
width = _pathwaySettings.FootwayWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Path:
|
||||||
|
mat = _pathwaySettings.PathMat;
|
||||||
|
width = _pathwaySettings.PathWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Steps:
|
||||||
|
mat = _pathwaySettings.StepsMat;
|
||||||
|
width = _pathwaySettings.PathWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Cycleway:
|
||||||
|
mat = _pathwaySettings.CyclewayMat;
|
||||||
|
width = _pathwaySettings.CyclewayWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Pedestrian:
|
||||||
|
mat = _pathwaySettings.PedestrianMat;
|
||||||
|
width = _pathwaySettings.PedestrianWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Road:
|
||||||
|
mat = _pathwaySettings.RoadMat;
|
||||||
|
width = _pathwaySettings.RoadWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Service:
|
||||||
|
mat = _pathwaySettings.ServiceMat;
|
||||||
|
width = _pathwaySettings.ServiceWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Residential:
|
||||||
|
mat = _pathwaySettings.ResidentialMat;
|
||||||
|
width = _pathwaySettings.ResidentialWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Track:
|
||||||
|
mat = _pathwaySettings.TrackMat;
|
||||||
|
width = _pathwaySettings.TrackWidth;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mat = _pathwaySettings.DefaultMat;
|
||||||
|
width = _pathwaySettings.DefaultWidth;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
line.material = mat;
|
||||||
|
line.widthMultiplier = width;
|
||||||
|
|
||||||
|
// Nastavení bodů cesty
|
||||||
|
line.positionCount = w.Points.Count;
|
||||||
|
for (int i = 0; i < w.Points.Count; i++)
|
||||||
|
{
|
||||||
|
Vector3 pos = w.Points[i].ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center);
|
||||||
|
pos.y = 0.1f; // Mírně nad zemí
|
||||||
|
line.SetPosition(i, pos);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
GameObject BuildAreaMesh(MapArea a)
|
||||||
|
{
|
||||||
|
var area = new GameObject($"Area_{a.Name ?? "Unknown"}");
|
||||||
|
area.tag = "Map";
|
||||||
|
|
||||||
|
MeshFilter meshFilter = area.AddComponent<MeshFilter>();
|
||||||
|
MeshRenderer meshRenderer = area.AddComponent<MeshRenderer>();
|
||||||
|
|
||||||
|
// Vytvoření plochého mesh
|
||||||
|
Mesh mesh = CreateFlatPolygonMesh(a.Outline);
|
||||||
|
meshFilter.mesh = mesh;
|
||||||
|
|
||||||
|
|
||||||
|
Material mat;
|
||||||
|
switch (a.AreaType)
|
||||||
|
{
|
||||||
|
case MapAreaType.Park:
|
||||||
|
mat = _areaSettings.ParkMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Garden:
|
||||||
|
mat = _areaSettings.GardenMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Playground:
|
||||||
|
mat = _areaSettings.PlaygroundMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Forest:
|
||||||
|
mat = _areaSettings.ForestMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Grass:
|
||||||
|
mat = _areaSettings.GrassMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Water:
|
||||||
|
mat = _areaSettings.WaterMat;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mat = _areaSettings.DefaultMat;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
meshRenderer.material = mat;
|
||||||
|
|
||||||
|
area.transform.position = new Vector3(0, 0.05f, 0); // Těsně nad zemí
|
||||||
|
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
//TODO: POIs
|
||||||
|
#endregion
|
||||||
|
#region Polygon Utils
|
||||||
|
private Vector3 CalculatePolygonCenter(List<Position> points)
|
||||||
|
{
|
||||||
|
Vector3 center = Vector3.zero;
|
||||||
|
foreach (var point in points)
|
||||||
|
{
|
||||||
|
center += point.ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center);
|
||||||
|
}
|
||||||
|
return center / points.Count;
|
||||||
|
}
|
||||||
|
private Mesh CreateExtrudedPolygonMesh(List<Position> outline, float height)
|
||||||
|
{
|
||||||
|
Mesh mesh = new Mesh();
|
||||||
|
|
||||||
|
int vertexCount = outline.Count;
|
||||||
|
|
||||||
|
// Vertices - spodní a horní podstava
|
||||||
|
Vector3[] vertices = new Vector3[vertexCount * 2];
|
||||||
|
Vector3 center = CalculatePolygonCenter(outline);
|
||||||
|
|
||||||
|
for (int i = 0; i < vertexCount; i++)
|
||||||
|
{
|
||||||
|
Vector3 pos = outline[i].ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center) - center;
|
||||||
|
vertices[i] = pos; // Spodní
|
||||||
|
vertices[i + vertexCount] = pos + Vector3.up * height; // Horní
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangles - jen boční stěny pro jednoduchost
|
||||||
|
List<int> triangles = new List<int>();
|
||||||
|
|
||||||
|
for (int i = 0; i < vertexCount; i++)
|
||||||
|
{
|
||||||
|
int next = (i + 1) % vertexCount;
|
||||||
|
|
||||||
|
// Boční stěna - dva trojúhelníky
|
||||||
|
triangles.Add(i);
|
||||||
|
triangles.Add(i + vertexCount);
|
||||||
|
triangles.Add(next);
|
||||||
|
|
||||||
|
triangles.Add(next);
|
||||||
|
triangles.Add(i + vertexCount);
|
||||||
|
triangles.Add(next + vertexCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horní podstava - zjednodušená triangulace (fan)
|
||||||
|
if (vertexCount >= 3)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < vertexCount - 1; i++)
|
||||||
|
{
|
||||||
|
triangles.Add(vertexCount); // Střed (první bod horní)
|
||||||
|
triangles.Add(vertexCount + i);
|
||||||
|
triangles.Add(vertexCount + i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.vertices = vertices;
|
||||||
|
mesh.triangles = triangles.ToArray();
|
||||||
|
mesh.RecalculateNormals();
|
||||||
|
mesh.RecalculateBounds();
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
private Mesh CreateFlatPolygonMesh(List<Position> outline)
|
||||||
|
{
|
||||||
|
Mesh mesh = new Mesh();
|
||||||
|
|
||||||
|
int vertexCount = outline.Count;
|
||||||
|
Vector3[] vertices = new Vector3[vertexCount];
|
||||||
|
Vector3 center = CalculatePolygonCenter(outline);
|
||||||
|
|
||||||
|
for (int i = 0; i < vertexCount; i++)
|
||||||
|
{
|
||||||
|
vertices[i] = outline[i].ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center) - center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangulace - fan pattern
|
||||||
|
List<int> triangles = new List<int>();
|
||||||
|
if (vertexCount >= 3)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < vertexCount - 1; i++)
|
||||||
|
{
|
||||||
|
triangles.Add(0);
|
||||||
|
triangles.Add(i);
|
||||||
|
triangles.Add(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.vertices = vertices;
|
||||||
|
mesh.triangles = triangles.ToArray();
|
||||||
|
mesh.RecalculateNormals();
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/GameManager/GameManager_Map.cs.meta
Normal file
2
Assets/GameManager/GameManager_Map.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 71870ee18b89dd7438e5362ff9e02a3b
|
||||||
196
Assets/GameManager/GameManager_Network.cs
Normal file
196
Assets/GameManager/GameManager_Network.cs
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
using GeoSus.Client;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Subsystems;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Subsystems
|
||||||
|
{
|
||||||
|
public class GameManager_Network
|
||||||
|
{
|
||||||
|
private const string _serverAddress = "geosus.honzuvkod.dev";
|
||||||
|
private const int _serverPort = 7777;
|
||||||
|
private GameClient _gameClient;
|
||||||
|
private GameManager_Map _mapSubsystem;
|
||||||
|
public async void OpenConection()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Task<bool> state = _gameClient.ConnectAsync(_serverAddress, _serverPort);
|
||||||
|
await state;
|
||||||
|
if (state.Result)
|
||||||
|
{
|
||||||
|
Debug.Log("Connected to server.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("Failed to connect to server");
|
||||||
|
}
|
||||||
|
await Task.Delay(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public GameManager_Network(GameClient gameClient)
|
||||||
|
{
|
||||||
|
_gameClient = gameClient;
|
||||||
|
RegisterEventHandlers();
|
||||||
|
}
|
||||||
|
public void RegisterEventHandlers()
|
||||||
|
{
|
||||||
|
_gameClient.OnConnected += OnConnected;
|
||||||
|
_gameClient.OnDisconnected += OnDisconnected;
|
||||||
|
_gameClient.OnError += OnError;
|
||||||
|
_gameClient.OnMessage += OnMessage;
|
||||||
|
_gameClient.OnGameEvent += OnGameEvent;
|
||||||
|
}
|
||||||
|
private void OnConnected()
|
||||||
|
{
|
||||||
|
Debug.Log("Successfully connected to the server.");
|
||||||
|
}
|
||||||
|
private void OnDisconnected(string reason)
|
||||||
|
{
|
||||||
|
Debug.Log($"Host disconnected due to {reason}");
|
||||||
|
}
|
||||||
|
private void OnError(string error)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Network error: {error}");
|
||||||
|
}
|
||||||
|
private void OnMessage(Message message)
|
||||||
|
{
|
||||||
|
switch (message.Type)
|
||||||
|
{
|
||||||
|
case "GameEvent":
|
||||||
|
OnGameEvent(message as GameEvent);
|
||||||
|
break;
|
||||||
|
case "CreateLobbyResponse":
|
||||||
|
Debug.Log("Received CreateLobbyResponse message");
|
||||||
|
HandleCreateLobbyResponse(message as CreateLobbyResponse);
|
||||||
|
break;
|
||||||
|
case "JoinLobbyResponse":
|
||||||
|
Debug.Log("Received JoinLobbyResponse message");
|
||||||
|
HandleJoinLobbyResponse(message as JoinLobbyResponse);
|
||||||
|
break;
|
||||||
|
case "Ack":
|
||||||
|
Debug.Log("Received Ack message");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Debug.Log("Received message of type: " + message.Type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void OnGameEvent(GameEvent gameEvent)
|
||||||
|
{
|
||||||
|
switch (gameEvent.EventType)
|
||||||
|
{
|
||||||
|
case "PlayerJoined":
|
||||||
|
Debug.Log($"Player {gameEvent.GetPayload<PlayerJoinedPayload>().DisplayName} joined");
|
||||||
|
break;
|
||||||
|
case "PlayerLeft":
|
||||||
|
Debug.Log($"Player {gameEvent.GetPayload<PlayerLeftPayload>()} left");
|
||||||
|
break;
|
||||||
|
case "GameStarting":
|
||||||
|
Debug.Log("Game is starting!");
|
||||||
|
break;
|
||||||
|
case "GameStarted":
|
||||||
|
Debug.Log("Game started");
|
||||||
|
break;
|
||||||
|
case "MapDataReady":
|
||||||
|
Debug.Log("Map data ready");
|
||||||
|
break;
|
||||||
|
case "PlayerMapDataReceived":
|
||||||
|
Debug.Log("Player map data recieved");
|
||||||
|
break;
|
||||||
|
case "MapDataError":
|
||||||
|
Debug.Log("Received MapData server error");
|
||||||
|
break;
|
||||||
|
case "SabotageStarted":
|
||||||
|
Debug.Log("Sabotage started");
|
||||||
|
HandleSabotageStarted(gameEvent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Debug.Log("Received GameEvent of type: " + gameEvent.EventType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void HandleCreateLobbyResponse(CreateLobbyResponse message)
|
||||||
|
{
|
||||||
|
if (message.Success)
|
||||||
|
{
|
||||||
|
Debug.Log("Lobby created successfully. Join Code: " + message.JoinCode + ", Lobby ID: " + message.LobbyId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError("Failed to create lobby: " + message.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void HandleJoinLobbyResponse(JoinLobbyResponse message)
|
||||||
|
{
|
||||||
|
if (message.Success)
|
||||||
|
{
|
||||||
|
Debug.Log("Lobby created successfully." + ", Lobby ID: " + message.LobbyId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError("Failed to create lobby: " + message.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void CrateLobby(double lat, double lon)
|
||||||
|
{
|
||||||
|
_gameClient.CreateLobby(new Position(lat, lon));
|
||||||
|
}
|
||||||
|
public void JoinLobby(string joinCode)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_gameClient.JoinLobby(joinCode);
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError("Error joining lobby: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void LeaveLobby()
|
||||||
|
{
|
||||||
|
_gameClient.Disconnect();
|
||||||
|
Application.Quit();
|
||||||
|
}
|
||||||
|
public void StartGame()
|
||||||
|
{
|
||||||
|
_gameClient.StartGame();
|
||||||
|
|
||||||
|
}
|
||||||
|
#region GameEvent Handlers
|
||||||
|
private void HandleSabotageStarted(GameEvent gameEvent)
|
||||||
|
{
|
||||||
|
SabotageStartedPayload payload = gameEvent.GetPayload<SabotageStartedPayload>();
|
||||||
|
switch (payload.Type)
|
||||||
|
{
|
||||||
|
case SabotageType.CommsBlackout:
|
||||||
|
for(int i = 0;i < payload.RequiredSimultaneousRepairs; i++)
|
||||||
|
{
|
||||||
|
//create stations
|
||||||
|
}
|
||||||
|
//Ui.alert
|
||||||
|
//DisableComms
|
||||||
|
return;
|
||||||
|
case SabotageType.CriticalMeltdown:
|
||||||
|
for (int i = 0; i < payload.RequiredSimultaneousRepairs; i++)
|
||||||
|
{
|
||||||
|
//create stations
|
||||||
|
}
|
||||||
|
//UI.alert
|
||||||
|
//UI Time remain
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Debug.Log($"Sabotage of unknown type: {payload.Type}");
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
2
Assets/GameManager/GameManager_Network.cs.meta
Normal file
2
Assets/GameManager/GameManager_Network.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9c2032ed1184ad7418cc415edf97b69e
|
||||||
71
Assets/GameManager/GameManager_UI.cs
Normal file
71
Assets/GameManager/GameManager_UI.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Subsystems;
|
||||||
|
using GeoSus.Client;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Subsystems
|
||||||
|
{
|
||||||
|
public class GameManager_UI
|
||||||
|
{
|
||||||
|
private GameClient _gameClient;
|
||||||
|
private Canvas _CreateJoinLobby;
|
||||||
|
private Canvas _InLobby;
|
||||||
|
private Canvas _LoadingScreen;
|
||||||
|
private Canvas _GameScreen;
|
||||||
|
public GameManager_UI(GameClient gameClient, Canvas CreateJoinLobby, Canvas InLobby, Canvas LoadingScreen, Canvas GameScreen)
|
||||||
|
{
|
||||||
|
_gameClient = gameClient;
|
||||||
|
_CreateJoinLobby = CreateJoinLobby;
|
||||||
|
_LoadingScreen = LoadingScreen;
|
||||||
|
_GameScreen = GameScreen;
|
||||||
|
_InLobby = InLobby;
|
||||||
|
_CreateJoinLobby.enabled = true;
|
||||||
|
_InLobby.enabled = false;
|
||||||
|
_GameScreen.enabled = false;
|
||||||
|
_LoadingScreen.enabled = false;
|
||||||
|
}
|
||||||
|
public void UpdateLobbyUI()
|
||||||
|
{
|
||||||
|
if (_gameClient.CurrentLobbyState == null)
|
||||||
|
{
|
||||||
|
_CreateJoinLobby.enabled = true;
|
||||||
|
_InLobby.enabled = false;
|
||||||
|
_GameScreen.enabled = false;
|
||||||
|
_LoadingScreen.enabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (_gameClient.CurrentLobbyState.Phase == GamePhase.Loading)
|
||||||
|
{
|
||||||
|
_CreateJoinLobby.enabled = false;
|
||||||
|
_InLobby.enabled = false;
|
||||||
|
_GameScreen.enabled = false;
|
||||||
|
_LoadingScreen.enabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (_gameClient.CurrentLobbyState.Phase == GamePhase.Lobby)
|
||||||
|
{
|
||||||
|
_InLobby.enabled = true;
|
||||||
|
_CreateJoinLobby.enabled = false;
|
||||||
|
var playerList = _InLobby.transform.Find("PlayerList").GetComponent<TMPro.TMP_Text>();
|
||||||
|
playerList.text = "";
|
||||||
|
foreach (var player in _gameClient.CurrentLobbyState.Players)
|
||||||
|
{
|
||||||
|
playerList.text += player.DisplayName + "\n";
|
||||||
|
}
|
||||||
|
_InLobby.transform.Find("JoinCode").GetComponent<TMPro.TMP_Text>().text = _gameClient.CurrentLobbyState.JoinCode;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (_gameClient.CurrentLobbyState.Phase == GamePhase.Playing)
|
||||||
|
{
|
||||||
|
_CreateJoinLobby.enabled = false;
|
||||||
|
_InLobby.enabled = false;
|
||||||
|
_GameScreen.enabled = true;
|
||||||
|
_LoadingScreen.enabled = false;
|
||||||
|
_GameScreen.transform.Find("Role").GetComponent<TMPro.TMP_Text>().text = _gameClient.MyRole.ToString() ;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/GameManager/GameManager_UI.cs.meta
Normal file
2
Assets/GameManager/GameManager_UI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f575016e02384774d88b46ed7f09579f
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 743fed9d96a90254c88556de9fee92b0
|
|
||||||
@@ -1,21 +1,25 @@
|
|||||||
|
using GeoSus.Client;
|
||||||
using System;
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public enum TaskType
|
/*public enum TaskType
|
||||||
{
|
{
|
||||||
Task //TODO: Typy úkolù
|
Task //TODO: Typy úkolù
|
||||||
|
}*/
|
||||||
|
[System.Serializable]
|
||||||
|
public class TaskData
|
||||||
|
{
|
||||||
|
//TaskType
|
||||||
|
public GameObject TaskPrefab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public interface ITask
|
public interface ITask
|
||||||
{
|
{
|
||||||
public string TaskID { get; } // Unikátní ID úkolu pro server
|
public string TaskID { get; set; } // Unikátní ID úkolu pro server
|
||||||
public TaskType TaskType { get; } // Typ úkolu
|
public TaskType TaskType { get; set; } // Typ úkolu
|
||||||
public string TaskName { get; } // Viditelný název úkolu
|
public string TaskName { get; set; } // Viditelný název úkolu
|
||||||
public (double, double) TaskLocation { get; } // Polohy na mapì
|
public Position TaskLocation { get; set; } // Polohy na mapì
|
||||||
public bool IsCompleted { get; } // Stav dokonèení úkolu
|
public bool IsCompleted { get; } // Stav dokončení úkolu
|
||||||
|
|
||||||
|
|
||||||
void Initialize(Action<ITask> onCompleted); // Vytvoøení tasku + naètení postupu
|
void Initialize(Action<ITask> onCompleted); // Vytvoøení tasku + naètení postupu
|
||||||
void ExitTask(Action<ITask> onExit); // Pøi opuštìní úkolu poslat hotovo / uložit postup / reset
|
void ExitTask(Action<ITask> onExit); // Pøi opuštìní úkolu poslat hotovo / uložit postup / reset
|
||||||
@@ -27,7 +31,7 @@ public class Wires : ITask{
|
|||||||
public string TaskID { get; set; } // Unikátní ID úkolu pro server
|
public string TaskID { get; set; } // Unikátní ID úkolu pro server
|
||||||
public TaskType TaskType { get; set; } // Typ úkolu
|
public TaskType TaskType { get; set; } // Typ úkolu
|
||||||
public string TaskName { get; set; } // Viditelný název úkolu
|
public string TaskName { get; set; } // Viditelný název úkolu
|
||||||
public (double, double) TaskLocation { get; set; } // Poloha na mapì
|
public Position TaskLocation { get; set; } // Poloha na mapì
|
||||||
public bool IsCompleted { get; private set; } // Stav dokonèení úkolu
|
public bool IsCompleted { get; private set; } // Stav dokonèení úkolu
|
||||||
private Action<ITask> _onCompleted;
|
private Action<ITask> _onCompleted;
|
||||||
|
|
||||||
@@ -35,6 +39,7 @@ public class Wires : ITask{
|
|||||||
{
|
{
|
||||||
IsCompleted = false;
|
IsCompleted = false;
|
||||||
_onCompleted = onCompleted;
|
_onCompleted = onCompleted;
|
||||||
|
|
||||||
}
|
}
|
||||||
public void ExitTask(Action<ITask> onExit) //Zavøení tasku
|
public void ExitTask(Action<ITask> onExit) //Zavøení tasku
|
||||||
{
|
{
|
||||||
@@ -51,3 +56,18 @@ public class Wires : ITask{
|
|||||||
|
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
public enum StationType
|
||||||
|
{
|
||||||
|
Sabotage,
|
||||||
|
Task,
|
||||||
|
Meeting,
|
||||||
|
Body
|
||||||
|
}
|
||||||
|
public interface IInteractable
|
||||||
|
{
|
||||||
|
public StationType Type { get; set; } // Typ stanice
|
||||||
|
public Position Location { get; set; } // Pozice na mapě
|
||||||
|
public PlayerRole? ReqRole { get; set; } // Požadovaná role hráče Impostor / Crewmate / Any = null
|
||||||
|
public float InteractionRange { get; set; } // Dosah interakce
|
||||||
|
void Interact(PlayerRole role); // Spuštění interakce
|
||||||
|
}
|
||||||
2
Assets/GameManager/Interfaces.cs.meta
Normal file
2
Assets/GameManager/Interfaces.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8e926b313c00d4f48ad68750c88817bf
|
||||||
83
Assets/GameManager/Stations.cs
Normal file
83
Assets/GameManager/Stations.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using GeoSus.Client;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
public class Station : IInteractable
|
||||||
|
{
|
||||||
|
public StationType Type { get; set; }
|
||||||
|
public Position Location { get; set; }
|
||||||
|
public PlayerRole? ReqRole { get; set; }
|
||||||
|
public float InteractionRange { get; set; }
|
||||||
|
protected GameObject interfaceInstance;
|
||||||
|
public GameObject Interface { get; set; } // Prefab pro interakci (napø. UI pro úkol nebo sabotáže)
|
||||||
|
|
||||||
|
public virtual void Interact(PlayerRole role)
|
||||||
|
{
|
||||||
|
if (ReqRole.HasValue && role != ReqRole.Value)
|
||||||
|
{
|
||||||
|
Debug.Log("You do not have the required role to interact with this station.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
interfaceInstance = UnityEngine.Object.Instantiate(Interface); // Zobrazí interakèní UI
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public Station(Position location, float interactionRange)
|
||||||
|
{
|
||||||
|
Location = location;
|
||||||
|
InteractionRange = interactionRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class TaskStation : Station
|
||||||
|
{
|
||||||
|
public string TaskID { get; set; } // Unikátní ID úkolu pro server
|
||||||
|
private GameClient _gameClient;
|
||||||
|
public TaskStation(Position pos, float interactionRange, GameClient gameClient, string taskID) : base(pos, interactionRange)
|
||||||
|
{
|
||||||
|
Type = StationType.Task;
|
||||||
|
ReqRole = PlayerRole.Crew;
|
||||||
|
_gameClient = gameClient;
|
||||||
|
}
|
||||||
|
public ITask Task { get; set; }
|
||||||
|
public override void Interact(PlayerRole role)
|
||||||
|
{
|
||||||
|
if(interfaceInstance != null)
|
||||||
|
{
|
||||||
|
ResumeTask();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
base.Interact(role);
|
||||||
|
Task = interfaceInstance.GetComponent<ITask>();
|
||||||
|
Task.TaskID = TaskID;
|
||||||
|
Task.Initialize(OnTaskCompleted);
|
||||||
|
}
|
||||||
|
private void ResumeTask()
|
||||||
|
{
|
||||||
|
interfaceInstance.SetActive(true); // Zobrazí interakèní UI
|
||||||
|
}
|
||||||
|
private void OnTaskCompleted(ITask task)
|
||||||
|
{
|
||||||
|
_gameClient.CompleteTask(task.TaskID);
|
||||||
|
task.ExitTask(OnTaskExit);
|
||||||
|
Debug.Log($"Task {task.TaskName} completed and sent to server.");
|
||||||
|
}
|
||||||
|
private void OnTaskExit(ITask task)
|
||||||
|
{
|
||||||
|
if (task.IsCompleted)
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(interfaceInstance); // Znièí interakèní UI
|
||||||
|
Debug.Log($"Task {task.TaskName} completed and sent to server.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
interfaceInstance.SetActive(false); // Skryje interakèní UI
|
||||||
|
Debug.Log($"Task {task.TaskName} was not completed, but exited.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
2
Assets/GameManager/Stations.cs.meta
Normal file
2
Assets/GameManager/Stations.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0ca1825585bf9bc42bd3b11985048465
|
||||||
92
Assets/GameManager/TestMaterial.mat
Normal file
92
Assets/GameManager/TestMaterial.mat
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!21 &2100000
|
||||||
|
Material:
|
||||||
|
serializedVersion: 8
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: TestMaterial
|
||||||
|
m_Shader: {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_Parent: {fileID: 0}
|
||||||
|
m_ModifiedSerializedProperties: 0
|
||||||
|
m_ValidKeywords: []
|
||||||
|
m_InvalidKeywords: []
|
||||||
|
m_LightmapFlags: 4
|
||||||
|
m_EnableInstancingVariants: 0
|
||||||
|
m_DoubleSidedGI: 0
|
||||||
|
m_CustomRenderQueue: -1
|
||||||
|
stringTagMap: {}
|
||||||
|
disabledShaderPasses: []
|
||||||
|
m_LockedProperties:
|
||||||
|
m_SavedProperties:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TexEnvs:
|
||||||
|
- _AlphaTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _BumpMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailAlbedoMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailMask:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailNormalMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _EmissionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MainTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MetallicGlossMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _OcclusionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _ParallaxMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_Ints: []
|
||||||
|
m_Floats:
|
||||||
|
- PixelSnap: 0
|
||||||
|
- _BumpScale: 1
|
||||||
|
- _Cutoff: 0.5
|
||||||
|
- _DetailNormalMapScale: 1
|
||||||
|
- _DstBlend: 0
|
||||||
|
- _EnableExternalAlpha: 0
|
||||||
|
- _GlossMapScale: 1
|
||||||
|
- _Glossiness: 0.5
|
||||||
|
- _GlossyReflections: 1
|
||||||
|
- _Metallic: 0
|
||||||
|
- _Mode: 0
|
||||||
|
- _OcclusionStrength: 1
|
||||||
|
- _Parallax: 0.02
|
||||||
|
- _SmoothnessTextureChannel: 0
|
||||||
|
- _SpecularHighlights: 1
|
||||||
|
- _SrcBlend: 1
|
||||||
|
- _UVSec: 0
|
||||||
|
- _ZWrite: 1
|
||||||
|
m_Colors:
|
||||||
|
- _Color: {r: 0, g: 0, b: 0, a: 1}
|
||||||
|
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
|
- _Flip: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
- _RendererColor: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
m_BuildTextureStacks: []
|
||||||
|
m_AllowLocking: 1
|
||||||
8
Assets/GameManager/TestMaterial.mat.meta
Normal file
8
Assets/GameManager/TestMaterial.mat.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6744524496c8e1549882277283c132cc
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 2100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: bfd97d0fee743f945a3d47b571fe9f20
|
guid: 832a89cb6f62a5240a99d84d09f0a0eb
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: fcf7219bab7fe46a1ad266029b2fee19, type: 3}
|
|
||||||
m_Name: Readme
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
icon: {fileID: 2800000, guid: 727a75301c3d24613a3ebcec4a24c2c8, type: 3}
|
|
||||||
title: URP Empty Template
|
|
||||||
sections:
|
|
||||||
- heading: Welcome to the Universal Render Pipeline
|
|
||||||
text: This template includes the settings and assets you need to start creating with the Universal Render Pipeline.
|
|
||||||
linkText:
|
|
||||||
url:
|
|
||||||
- heading: URP Documentation
|
|
||||||
text:
|
|
||||||
linkText: Read more about URP
|
|
||||||
url: https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest
|
|
||||||
- heading: Forums
|
|
||||||
text:
|
|
||||||
linkText: Get answers and support
|
|
||||||
url: https://forum.unity.com/forums/universal-render-pipeline.383/
|
|
||||||
- heading: Report bugs
|
|
||||||
text:
|
|
||||||
linkText: Submit a report
|
|
||||||
url: https://unity3d.com/unity/qa/bug-reporting
|
|
||||||
loadedLayout: 1
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: fe6cecdde8bb6e1489d0b7e5b793ce84
|
guid: fa2dc9f32b7ad7c419b06e4ad6866e09
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
|
|||||||
3438
Assets/Scenes/Client.unity
Normal file
3438
Assets/Scenes/Client.unity
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: f01e25b5a8e60224fa8643bf4f074a71
|
guid: 8f736798e2d13f14f903b26a2df0eed8
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 6a18d9b328b6a794da5d050347950362
|
guid: 3e95f16d8e50b3341925e51e50768027
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ NavMeshSettings:
|
|||||||
debug:
|
debug:
|
||||||
m_Flags: 0
|
m_Flags: 0
|
||||||
m_NavMeshData: {fileID: 0}
|
m_NavMeshData: {fileID: 0}
|
||||||
--- !u!1 &871248414
|
--- !u!1 &564375757
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -127,40 +127,9 @@ GameObject:
|
|||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
serializedVersion: 6
|
serializedVersion: 6
|
||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 871248415}
|
- component: {fileID: 564375760}
|
||||||
m_Layer: 0
|
- component: {fileID: 564375759}
|
||||||
m_Name: gamemanager
|
- component: {fileID: 564375758}
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!4 &871248415
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 871248414}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
|
||||||
m_LocalPosition: {x: 0.27, y: 0, z: 0.21026}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1 &1078774431
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 1078774434}
|
|
||||||
- component: {fileID: 1078774433}
|
|
||||||
- component: {fileID: 1078774432}
|
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Main Camera
|
m_Name: Main Camera
|
||||||
m_TagString: MainCamera
|
m_TagString: MainCamera
|
||||||
@@ -168,21 +137,21 @@ GameObject:
|
|||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
m_StaticEditorFlags: 0
|
m_StaticEditorFlags: 0
|
||||||
m_IsActive: 1
|
m_IsActive: 1
|
||||||
--- !u!81 &1078774432
|
--- !u!81 &564375758
|
||||||
AudioListener:
|
AudioListener:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
m_PrefabInstance: {fileID: 0}
|
m_PrefabInstance: {fileID: 0}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 1078774431}
|
m_GameObject: {fileID: 564375757}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
--- !u!20 &1078774433
|
--- !u!20 &564375759
|
||||||
Camera:
|
Camera:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
m_PrefabInstance: {fileID: 0}
|
m_PrefabInstance: {fileID: 0}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 1078774431}
|
m_GameObject: {fileID: 564375757}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_ClearFlags: 1
|
m_ClearFlags: 1
|
||||||
@@ -227,13 +196,13 @@ Camera:
|
|||||||
m_OcclusionCulling: 1
|
m_OcclusionCulling: 1
|
||||||
m_StereoConvergence: 10
|
m_StereoConvergence: 10
|
||||||
m_StereoSeparation: 0.022
|
m_StereoSeparation: 0.022
|
||||||
--- !u!4 &1078774434
|
--- !u!4 &564375760
|
||||||
Transform:
|
Transform:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
m_PrefabInstance: {fileID: 0}
|
m_PrefabInstance: {fileID: 0}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 1078774431}
|
m_GameObject: {fileID: 564375757}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||||
@@ -242,7 +211,7 @@ Transform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &1757180458
|
--- !u!1 &1274200469
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -250,8 +219,237 @@ GameObject:
|
|||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
serializedVersion: 6
|
serializedVersion: 6
|
||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 1757180460}
|
- component: {fileID: 1274200471}
|
||||||
- component: {fileID: 1757180459}
|
- component: {fileID: 1274200473}
|
||||||
|
- component: {fileID: 1274200472}
|
||||||
|
- component: {fileID: 1274200474}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: APIManager
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &1274200471
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1274200469}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!23 &1274200472
|
||||||
|
MeshRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1274200469}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 1
|
||||||
|
m_ReceiveShadows: 1
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 2
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||||
|
m_RayTracingAccelStructBuildFlags: 1
|
||||||
|
m_SmallMeshCulling: 1
|
||||||
|
m_ForceMeshLod: -1
|
||||||
|
m_MeshLodSelectionBias: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 2100000, guid: 283cf727b4c3ac64c94d59598e221b10, type: 2}
|
||||||
|
m_StaticBatchInfo:
|
||||||
|
firstSubMesh: 0
|
||||||
|
subMeshCount: 0
|
||||||
|
m_StaticBatchRoot: {fileID: 0}
|
||||||
|
m_ProbeAnchor: {fileID: 0}
|
||||||
|
m_LightProbeVolumeOverride: {fileID: 0}
|
||||||
|
m_ScaleInLightmap: 1
|
||||||
|
m_ReceiveGI: 1
|
||||||
|
m_PreserveUVs: 0
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 3
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_GlobalIlluminationMeshLod: 0
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingLayer: 0
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_AdditionalVertexStreams: {fileID: 0}
|
||||||
|
--- !u!33 &1274200473
|
||||||
|
MeshFilter:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1274200469}
|
||||||
|
m_Mesh: {fileID: 0}
|
||||||
|
--- !u!114 &1274200474
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1274200469}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: a58b19ca2646e434ea88b8112260362b, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::MapRenderer
|
||||||
|
queryRadiusMeters: 1000
|
||||||
|
gpsManager: {fileID: 1539417978}
|
||||||
|
buildingMaterial: {fileID: 2100000, guid: 283cf727b4c3ac64c94d59598e221b10, type: 2}
|
||||||
|
defaultFloorHeight: 3
|
||||||
|
defaultBuildingHeight: 6
|
||||||
|
roadMaterial: {fileID: 2100000, guid: 283cf727b4c3ac64c94d59598e221b10, type: 2}
|
||||||
|
defaultRoadWidth: 4
|
||||||
|
motorwayWidth: 10
|
||||||
|
primaryWidth: 8
|
||||||
|
secondaryWidth: 6
|
||||||
|
tertiaryWidth: 5
|
||||||
|
metersPerUnit: 1
|
||||||
|
--- !u!1 &1539417977
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1539417979}
|
||||||
|
- component: {fileID: 1539417978}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: currentGPS
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &1539417978
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1539417977}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 6b892bed07e5c4d45ad23b4a9b108e08, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::GPSManager
|
||||||
|
Accuracy: 10
|
||||||
|
UpdateDistance: 5
|
||||||
|
MaxWait: 20
|
||||||
|
--- !u!4 &1539417979
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1539417977}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: -18.75082, y: 0, z: 46.79584}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &1865882987
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1865882990}
|
||||||
|
- component: {fileID: 1865882989}
|
||||||
|
- component: {fileID: 1865882988}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: EventSystem
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &1865882988
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1865882987}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.EventSystems.StandaloneInputModule
|
||||||
|
m_SendPointerHoverToParent: 1
|
||||||
|
m_HorizontalAxis: Horizontal
|
||||||
|
m_VerticalAxis: Vertical
|
||||||
|
m_SubmitButton: Submit
|
||||||
|
m_CancelButton: Cancel
|
||||||
|
m_InputActionsPerSecond: 10
|
||||||
|
m_RepeatDelay: 0.5
|
||||||
|
m_ForceModuleActive: 0
|
||||||
|
--- !u!114 &1865882989
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1865882987}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.EventSystems.EventSystem
|
||||||
|
m_FirstSelected: {fileID: 0}
|
||||||
|
m_sendNavigationEvents: 1
|
||||||
|
m_DragThreshold: 10
|
||||||
|
--- !u!4 &1865882990
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1865882987}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &1896274046
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1896274048}
|
||||||
|
- component: {fileID: 1896274047}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Directional Light
|
m_Name: Directional Light
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -259,13 +457,13 @@ GameObject:
|
|||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
m_StaticEditorFlags: 0
|
m_StaticEditorFlags: 0
|
||||||
m_IsActive: 1
|
m_IsActive: 1
|
||||||
--- !u!108 &1757180459
|
--- !u!108 &1896274047
|
||||||
Light:
|
Light:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
m_PrefabInstance: {fileID: 0}
|
m_PrefabInstance: {fileID: 0}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 1757180458}
|
m_GameObject: {fileID: 1896274046}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
serializedVersion: 11
|
serializedVersion: 11
|
||||||
m_Type: 1
|
m_Type: 1
|
||||||
@@ -324,13 +522,13 @@ Light:
|
|||||||
m_LightUnit: 1
|
m_LightUnit: 1
|
||||||
m_LuxAtDistance: 1
|
m_LuxAtDistance: 1
|
||||||
m_EnableSpotReflector: 1
|
m_EnableSpotReflector: 1
|
||||||
--- !u!4 &1757180460
|
--- !u!4 &1896274048
|
||||||
Transform:
|
Transform:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
m_PrefabInstance: {fileID: 0}
|
m_PrefabInstance: {fileID: 0}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 1757180458}
|
m_GameObject: {fileID: 1896274046}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||||
@@ -343,6 +541,8 @@ Transform:
|
|||||||
SceneRoots:
|
SceneRoots:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_Roots:
|
m_Roots:
|
||||||
- {fileID: 1078774434}
|
- {fileID: 564375760}
|
||||||
- {fileID: 1757180460}
|
- {fileID: 1896274048}
|
||||||
- {fileID: 871248415}
|
- {fileID: 1865882990}
|
||||||
|
- {fileID: 1274200471}
|
||||||
|
- {fileID: 1539417979}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 74379ac554348f84c8d3abeb440b79b9
|
guid: facb9eb5d6c7d484097d6167562da786
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: b8ce5b30e6200134bae071db3ae43149
|
guid: 59d02e797bf2bf54e8b2aa0c7e0d4c87
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class FollowParentPositionOnly : MonoBehaviour
|
|
||||||
{
|
|
||||||
public Transform parent;
|
|
||||||
private Quaternion initialRotation;
|
|
||||||
private Vector3 worldOffset;
|
|
||||||
|
|
||||||
void Start()
|
|
||||||
{
|
|
||||||
if (parent == null)
|
|
||||||
parent = transform.parent;
|
|
||||||
|
|
||||||
// Store offset in WORLD space
|
|
||||||
worldOffset = transform.position - parent.position;
|
|
||||||
initialRotation = transform.rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LateUpdate()
|
|
||||||
{
|
|
||||||
if (parent == null) return;
|
|
||||||
|
|
||||||
// Move child using stored world offset
|
|
||||||
transform.position = parent.position + worldOffset;
|
|
||||||
transform.rotation = initialRotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: b159266533f64ba4eb30ef6112cc4611
|
|
||||||
68
Assets/Scripts/GPSManager.cs
Normal file
68
Assets/Scripts/GPSManager.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class GPSManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("GPS settings")]
|
||||||
|
public float Accuracy = 10f;
|
||||||
|
public float UpdateDistance = 5f;
|
||||||
|
public int MaxWait = 20;
|
||||||
|
|
||||||
|
[Header("GPS coordinates")]
|
||||||
|
private double[] LastCoords = new double[2];
|
||||||
|
private double[] FailsafeCoords = new double[] { 50.7727878, 15.0718625 };
|
||||||
|
private double? LastTime;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
StartCoroutine(UpdateGPS());
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] GetLastCoords()
|
||||||
|
{
|
||||||
|
if (LastCoords[0] == 0 && LastCoords[1] == 0) { return FailsafeCoords; }
|
||||||
|
return LastCoords;
|
||||||
|
}
|
||||||
|
IEnumerator UpdateGPS()
|
||||||
|
{
|
||||||
|
if (!Input.location.isEnabledByUser)
|
||||||
|
{
|
||||||
|
Debug.Log("GPS not enabled by user");
|
||||||
|
LastCoords = FailsafeCoords;
|
||||||
|
LastTime = null;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Input.location.Start(Accuracy, UpdateDistance);
|
||||||
|
|
||||||
|
while (Input.location.status == LocationServiceStatus.Initializing && MaxWait > 0)
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(1);
|
||||||
|
MaxWait--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MaxWait < 1)
|
||||||
|
{
|
||||||
|
Debug.Log("GPS timed out");
|
||||||
|
LastCoords = FailsafeCoords;
|
||||||
|
LastTime = null;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
if (Input.location.status == LocationServiceStatus.Failed)
|
||||||
|
{
|
||||||
|
Debug.Log("GPS failed to determine device location");
|
||||||
|
LastCoords = FailsafeCoords;
|
||||||
|
LastTime = null;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LastCoords[0] = Input.location.lastData.latitude;
|
||||||
|
LastCoords[1] = Input.location.lastData.longitude;
|
||||||
|
LastTime = Input.location.lastData.timestamp;
|
||||||
|
|
||||||
|
Debug.Log("GPS location: " + LastCoords[0] + ", " + LastCoords[1] + " (time: " + LastTime + ")");
|
||||||
|
}
|
||||||
|
yield return StartCoroutine(UpdateGPS());
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/GPSManager.cs.meta
Normal file
2
Assets/Scripts/GPSManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2d992ef55bc28784f81e79dd5cca414b
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class GyroPlatformController : MonoBehaviour
|
|
||||||
{
|
|
||||||
public Rigidbody ball;
|
|
||||||
public float forceStrength = 30f;
|
|
||||||
public float dampingPerSecond = 0.9f;
|
|
||||||
void Start()
|
|
||||||
{
|
|
||||||
if (SystemInfo.supportsGyroscope)
|
|
||||||
{
|
|
||||||
Input.gyro.enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FixedUpdate()
|
|
||||||
{
|
|
||||||
if (ball == null) return;
|
|
||||||
|
|
||||||
Vector3 g = Input.gyro.gravity;
|
|
||||||
|
|
||||||
Vector3 force = new Vector3(g.x, 0f, g.y);
|
|
||||||
|
|
||||||
ball.AddForce(force * forceStrength, ForceMode.Acceleration);
|
|
||||||
|
|
||||||
float frameDamping = Mathf.Pow(dampingPerSecond, Time.deltaTime);
|
|
||||||
|
|
||||||
ball.linearVelocity *= frameDamping;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d665da208a5a3ad408e85341263c60e5
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class HoleGoalTrigger : MonoBehaviour
|
|
||||||
{
|
|
||||||
[Tooltip("Optional: assign the ball rigidbody or leave empty to accept any Rigidbody with tag Ball.")]
|
|
||||||
public Rigidbody ballRigidbody;
|
|
||||||
|
|
||||||
[Tooltip("If true, checks tag 'Ball' when ballRigidbody not assigned.")]
|
|
||||||
public bool requireBallTag = true;
|
|
||||||
|
|
||||||
public System.Action OnBallScored;
|
|
||||||
|
|
||||||
private void OnTriggerEnter(Collider other)
|
|
||||||
{
|
|
||||||
if (ballRigidbody != null)
|
|
||||||
{
|
|
||||||
if (other.attachedRigidbody == ballRigidbody)
|
|
||||||
{
|
|
||||||
OnBallScored?.Invoke();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other.attachedRigidbody == null) return;
|
|
||||||
|
|
||||||
if (requireBallTag)
|
|
||||||
{
|
|
||||||
if (!other.CompareTag("Ball")) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnBallScored?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e6bad0eb7441229448191433d8a9f758
|
|
||||||
8
Assets/Scripts/IMapDataCollector.cs
Normal file
8
Assets/Scripts/IMapDataCollector.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
public interface IMapDataCollector
|
||||||
|
{
|
||||||
|
public Task<string> CallOverpassApi(FormUrlEncodedContent query);
|
||||||
|
}
|
||||||
2
Assets/Scripts/IMapDataCollector.cs.meta
Normal file
2
Assets/Scripts/IMapDataCollector.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8cf96fedb0da0ff4f947cad1c84e352b
|
||||||
500
Assets/Scripts/MapRenderer.cs
Normal file
500
Assets/Scripts/MapRenderer.cs
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
|
||||||
|
public class MapRenderer : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Overpass settings")]
|
||||||
|
public const string overpassUrl = "https://mapz.honzuvkod.dev/api/interpreter";
|
||||||
|
public float queryRadiusMeters = 200f; // radius around lat/lon to query
|
||||||
|
|
||||||
|
[Header("Location (lat, lon)")]
|
||||||
|
public GPSManager gpsManager;
|
||||||
|
private double latitude = 50.7727878;
|
||||||
|
private double longitude = 15.0718625;
|
||||||
|
|
||||||
|
[Header("Building settings")]
|
||||||
|
public Material buildingMaterial;
|
||||||
|
public float defaultFloorHeight = 3.0f; // meters per level
|
||||||
|
public float defaultBuildingHeight = 6.0f; // if no tags
|
||||||
|
|
||||||
|
[Header("Road settings")]
|
||||||
|
public Material roadMaterial;
|
||||||
|
public float defaultRoadWidth = 4.0f; // meters
|
||||||
|
public float motorwayWidth = 10.0f;
|
||||||
|
public float primaryWidth = 8.0f;
|
||||||
|
public float secondaryWidth = 6.0f;
|
||||||
|
public float tertiaryWidth = 5.0f;
|
||||||
|
|
||||||
|
|
||||||
|
[Header("Misc")]
|
||||||
|
public float _metersPerUnit = 1f; // scale: 1 unit = 1 meter
|
||||||
|
|
||||||
|
|
||||||
|
[Header("Storage")]
|
||||||
|
Dictionary<long, Vector2> nodes = new Dictionary<long, Vector2>(); // id -> latlon
|
||||||
|
List<Way> parsedWays = new List<Way>();
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
StartCoroutine(RenderMap());
|
||||||
|
}
|
||||||
|
IEnumerator RenderMap()
|
||||||
|
{
|
||||||
|
ClearChildren();
|
||||||
|
|
||||||
|
double[] GPS = gpsManager.GetLastCoords();
|
||||||
|
latitude = GPS[0];
|
||||||
|
longitude = GPS[1];
|
||||||
|
|
||||||
|
string q = $"[out:xml][timeout:90];(way[\"building\"](around:{queryRadiusMeters.ToString().Replace(",", ".")},{latitude.ToString().Replace(",", ".")},{longitude.ToString().Replace(",", ".")});way[\"highway\"](around:{queryRadiusMeters.ToString().Replace(",", ".")},{latitude.ToString().Replace(",", ".")},{longitude.ToString().Replace(",", ".")}););(._;>;);out body;";
|
||||||
|
|
||||||
|
WWWForm form = new WWWForm();
|
||||||
|
form.AddField("data", q);
|
||||||
|
|
||||||
|
using (UnityWebRequest www = UnityWebRequest.Post(overpassUrl, form))
|
||||||
|
{
|
||||||
|
www.downloadHandler = new DownloadHandlerBuffer();
|
||||||
|
yield return www.SendWebRequest();
|
||||||
|
|
||||||
|
if (www.result != UnityWebRequest.Result.Success)
|
||||||
|
{
|
||||||
|
Debug.LogError("Overpass request failed: " + www.error);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string xml = www.downloadHandler.text;
|
||||||
|
ParseOverpassXml(xml);
|
||||||
|
|
||||||
|
GameObject buildingsRoot = new GameObject("Buildings");
|
||||||
|
buildingsRoot.transform.parent = this.transform;
|
||||||
|
|
||||||
|
GameObject roadsRoot = new GameObject("Roads");
|
||||||
|
roadsRoot.transform.parent = this.transform;
|
||||||
|
|
||||||
|
foreach (var w in parsedWays)
|
||||||
|
{
|
||||||
|
if (w.tags.ContainsKey("building"))
|
||||||
|
{
|
||||||
|
GameObject b = BuildBuildingMesh(w);
|
||||||
|
b.transform.parent = buildingsRoot.transform;
|
||||||
|
}
|
||||||
|
else if (w.tags.ContainsKey("highway"))
|
||||||
|
{
|
||||||
|
GameObject r = BuildRoadMesh(w);
|
||||||
|
r.transform.parent = roadsRoot.transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("Map generation complete: " + parsedWays.Count + " ways, " + nodes.Count + " nodes.");
|
||||||
|
}
|
||||||
|
yield return StartCoroutine(RenderMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearChildren()
|
||||||
|
{
|
||||||
|
List<GameObject> toDestroy = new List<GameObject>();
|
||||||
|
foreach (Transform t in transform)
|
||||||
|
toDestroy.Add(t.gameObject);
|
||||||
|
foreach (var g in toDestroy)
|
||||||
|
DestroyImmediate(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Overpass XML parsing
|
||||||
|
class Way
|
||||||
|
{
|
||||||
|
public long id;
|
||||||
|
public List<long> nodeRefs = new List<long>();
|
||||||
|
public Dictionary<string, string> tags = new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ParseOverpassXml(string xmlText)
|
||||||
|
{
|
||||||
|
nodes.Clear();
|
||||||
|
parsedWays.Clear();
|
||||||
|
|
||||||
|
XmlDocument doc = new XmlDocument();
|
||||||
|
doc.LoadXml(xmlText);
|
||||||
|
|
||||||
|
XmlNode osm = doc.SelectSingleNode("/osm");
|
||||||
|
if (osm == null) return;
|
||||||
|
|
||||||
|
// parse nodes
|
||||||
|
foreach (XmlNode node in osm.SelectNodes("node"))
|
||||||
|
{
|
||||||
|
long id = long.Parse(node.Attributes["id"].Value, CultureInfo.InvariantCulture);
|
||||||
|
double lat = double.Parse(node.Attributes["lat"].Value, CultureInfo.InvariantCulture);
|
||||||
|
double lon = double.Parse(node.Attributes["lon"].Value, CultureInfo.InvariantCulture);
|
||||||
|
nodes[id] = new Vector2((float)lat, (float)lon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse ways
|
||||||
|
foreach (XmlNode wayNode in osm.SelectNodes("way"))
|
||||||
|
{
|
||||||
|
Way w = new Way();
|
||||||
|
w.id = long.Parse(wayNode.Attributes["id"].Value, CultureInfo.InvariantCulture);
|
||||||
|
foreach (XmlNode child in wayNode.ChildNodes)
|
||||||
|
{
|
||||||
|
if (child.Name == "nd")
|
||||||
|
{
|
||||||
|
long r = long.Parse(child.Attributes["ref"].Value, CultureInfo.InvariantCulture);
|
||||||
|
w.nodeRefs.Add(r);
|
||||||
|
}
|
||||||
|
else if (child.Name == "tag")
|
||||||
|
{
|
||||||
|
string k = child.Attributes["k"].Value;
|
||||||
|
string v = child.Attributes["v"].Value;
|
||||||
|
w.tags[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parsedWays.Add(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Utilities: latlon to local meters
|
||||||
|
// Convert latitude/longitude to local XY meters relative to center point
|
||||||
|
Vector3 LatLonToLocal(double lat, double lon)
|
||||||
|
{
|
||||||
|
// Use simple equirectangular projection around center (latitude, longitude)
|
||||||
|
double lat0 = latitude;
|
||||||
|
double lon0 = longitude;
|
||||||
|
double dLat = (lat - lat0) * Mathf.Deg2Rad;
|
||||||
|
double dLon = (lon - lon0) * Mathf.Deg2Rad;
|
||||||
|
double R = 6378137.0; // Earth radius in meters
|
||||||
|
double x = R * dLon * Math.Cos(lat0 * Mathf.Deg2Rad);
|
||||||
|
double y = R * dLat;
|
||||||
|
return new Vector3((float)x / _metersPerUnit, 0f, (float)y / _metersPerUnit);
|
||||||
|
}
|
||||||
|
Vector3 NodeIdToLocal(long nodeId)
|
||||||
|
{
|
||||||
|
if (!nodes.ContainsKey(nodeId))
|
||||||
|
return Vector3.zero;
|
||||||
|
Vector2 latlon = nodes[nodeId];
|
||||||
|
return LatLonToLocal(latlon.x, latlon.y);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Mesh builders
|
||||||
|
GameObject BuildBuildingMesh(Way w)
|
||||||
|
{
|
||||||
|
// gather polygon points
|
||||||
|
List<Vector3> poly = new List<Vector3>();
|
||||||
|
foreach (var id in w.nodeRefs)
|
||||||
|
{
|
||||||
|
Vector3 p = NodeIdToLocal(id);
|
||||||
|
poly.Add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure closed
|
||||||
|
if (poly.Count < 3) return null;
|
||||||
|
if ((poly[0] - poly[poly.Count - 1]).sqrMagnitude > 0.0001f)
|
||||||
|
poly.Add(poly[0]);
|
||||||
|
|
||||||
|
// determine height
|
||||||
|
float height = defaultBuildingHeight;
|
||||||
|
if (w.tags.ContainsKey("height"))
|
||||||
|
{
|
||||||
|
if (TryParseHeight(w.tags["height"], out float h)) height = h;
|
||||||
|
}
|
||||||
|
else if (w.tags.ContainsKey("building:levels"))
|
||||||
|
{
|
||||||
|
if (float.TryParse(w.tags["building:levels"], NumberStyles.Float, CultureInfo.InvariantCulture, out float levels))
|
||||||
|
height = Mathf.Max(0.5f, levels * defaultFloorHeight);
|
||||||
|
}
|
||||||
|
else if (w.tags.ContainsKey("levels"))
|
||||||
|
{
|
||||||
|
if (float.TryParse(w.tags["levels"], NumberStyles.Float, CultureInfo.InvariantCulture, out float levels))
|
||||||
|
height = Mathf.Max(0.5f, levels * defaultFloorHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create GameObject
|
||||||
|
GameObject go = new GameObject("Building_" + w.id);
|
||||||
|
MeshFilter mf = go.AddComponent<MeshFilter>();
|
||||||
|
MeshRenderer mr = go.AddComponent<MeshRenderer>();
|
||||||
|
mr.material = buildingMaterial;
|
||||||
|
|
||||||
|
// generate mesh: roof (triangulated polygon) + walls (extruded quads)
|
||||||
|
Mesh mesh = new Mesh();
|
||||||
|
mesh.name = "BuildingMesh_" + w.id;
|
||||||
|
|
||||||
|
// Convert poly to 2D points (XZ plane)
|
||||||
|
List<Vector2> poly2D = new List<Vector2>();
|
||||||
|
for (int i = 0; i < poly.Count - 1; i++) // omit last repeated point
|
||||||
|
poly2D.Add(new Vector2(poly[i].x, poly[i].z));
|
||||||
|
|
||||||
|
// triangulate roof
|
||||||
|
List<int> roofTris = Triangulate(poly2D);
|
||||||
|
if (roofTris == null || roofTris.Count == 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Triangulation failed for building " + w.id);
|
||||||
|
return go;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build vertices: roof vertices at y=height, walls vertices (2 per poly vertex)
|
||||||
|
int n = poly2D.Count;
|
||||||
|
// Build vertices and triangles with NO SHARED VERTICES (flat shading)
|
||||||
|
List<Vector3> verts = new List<Vector3>();
|
||||||
|
List<int> triangles = new List<int>();
|
||||||
|
List<Vector2> uvs = new List<Vector2>();
|
||||||
|
|
||||||
|
// Roof triangles - each triangle gets its own vertices
|
||||||
|
for (int i = 0; i < roofTris.Count; i += 3)
|
||||||
|
{
|
||||||
|
int idx0 = roofTris[i];
|
||||||
|
int idx1 = roofTris[i + 1];
|
||||||
|
int idx2 = roofTris[i + 2];
|
||||||
|
|
||||||
|
Vector2 p0 = poly2D[idx0];
|
||||||
|
Vector2 p1 = poly2D[idx1];
|
||||||
|
Vector2 p2 = poly2D[idx2];
|
||||||
|
|
||||||
|
int baseIdx = verts.Count;
|
||||||
|
verts.Add(new Vector3(p0.x, height / _metersPerUnit, p0.y));
|
||||||
|
verts.Add(new Vector3(p1.x, height / _metersPerUnit, p1.y));
|
||||||
|
verts.Add(new Vector3(p2.x, height / _metersPerUnit, p2.y));
|
||||||
|
|
||||||
|
triangles.Add(baseIdx);
|
||||||
|
triangles.Add(baseIdx + 1);
|
||||||
|
triangles.Add(baseIdx + 2);
|
||||||
|
|
||||||
|
uvs.Add(new Vector2(p0.x, p0.y));
|
||||||
|
uvs.Add(new Vector2(p1.x, p1.y));
|
||||||
|
uvs.Add(new Vector2(p2.x, p2.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walls - each quad gets its own 4 vertices
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
int iNext = (i + 1) % n;
|
||||||
|
Vector2 p0 = poly2D[i];
|
||||||
|
Vector2 p1 = poly2D[iNext];
|
||||||
|
|
||||||
|
int baseIdx = verts.Count;
|
||||||
|
verts.Add(new Vector3(p0.x, height / _metersPerUnit, p0.y)); // top left
|
||||||
|
verts.Add(new Vector3(p0.x, 0, p0.y)); // bottom left
|
||||||
|
verts.Add(new Vector3(p1.x, 0, p1.y)); // bottom right
|
||||||
|
verts.Add(new Vector3(p1.x, height / _metersPerUnit, p1.y)); // top right
|
||||||
|
|
||||||
|
triangles.Add(baseIdx);
|
||||||
|
triangles.Add(baseIdx + 1);
|
||||||
|
triangles.Add(baseIdx + 2);
|
||||||
|
|
||||||
|
triangles.Add(baseIdx);
|
||||||
|
triangles.Add(baseIdx + 2);
|
||||||
|
triangles.Add(baseIdx + 3);
|
||||||
|
|
||||||
|
uvs.Add(new Vector2(0, 1));
|
||||||
|
uvs.Add(new Vector2(0, 0));
|
||||||
|
uvs.Add(new Vector2(1, 0));
|
||||||
|
uvs.Add(new Vector2(1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.SetVertices(verts);
|
||||||
|
mesh.SetTriangles(triangles, 0);
|
||||||
|
mesh.SetUVs(0, uvs);
|
||||||
|
mesh.RecalculateNormals();
|
||||||
|
mesh.RecalculateBounds();
|
||||||
|
|
||||||
|
mf.mesh = mesh;
|
||||||
|
|
||||||
|
// Center object (using first roof vertex as reference)
|
||||||
|
Vector3 centroid = Vector3.zero;
|
||||||
|
for (int i = 0; i < roofTris.Count; i += 3)
|
||||||
|
{
|
||||||
|
centroid += verts[i];
|
||||||
|
}
|
||||||
|
centroid /= (roofTris.Count / 3);
|
||||||
|
go.transform.position = centroid * -1f;
|
||||||
|
|
||||||
|
// Move the roof/walls vertices back to local space
|
||||||
|
Vector3[] adjustedVerts = mesh.vertices;
|
||||||
|
for (int i = 0; i < adjustedVerts.Length; i++) adjustedVerts[i] += centroid;
|
||||||
|
mesh.vertices = adjustedVerts;
|
||||||
|
mesh.RecalculateNormals();
|
||||||
|
mesh.RecalculateBounds();
|
||||||
|
|
||||||
|
return go;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject BuildRoadMesh(Way w)
|
||||||
|
{
|
||||||
|
// build polyline
|
||||||
|
List<Vector3> pts = new List<Vector3>();
|
||||||
|
foreach (var id in w.nodeRefs)
|
||||||
|
pts.Add(NodeIdToLocal(id));
|
||||||
|
if (pts.Count < 2) return null;
|
||||||
|
|
||||||
|
float width = defaultRoadWidth;
|
||||||
|
if (w.tags.ContainsKey("width") && float.TryParse(w.tags["width"], NumberStyles.Float, CultureInfo.InvariantCulture, out float wv))
|
||||||
|
width = wv;
|
||||||
|
else if (w.tags.ContainsKey("highway"))
|
||||||
|
{
|
||||||
|
// simple heuristic
|
||||||
|
string h = w.tags["highway"];
|
||||||
|
if (h == "motorway") width = motorwayWidth;
|
||||||
|
else if (h == "primary") width = primaryWidth;
|
||||||
|
else if (h == "secondary") width = secondaryWidth;
|
||||||
|
else if (h == "tertiary") width = tertiaryWidth;
|
||||||
|
else width = defaultRoadWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject go = new GameObject("Road_" + w.id);
|
||||||
|
MeshFilter mf = go.AddComponent<MeshFilter>();
|
||||||
|
MeshRenderer mr = go.AddComponent<MeshRenderer>();
|
||||||
|
mr.material = roadMaterial;
|
||||||
|
|
||||||
|
Mesh mesh = new Mesh();
|
||||||
|
mesh.name = "RoadMesh_" + w.id;
|
||||||
|
|
||||||
|
List<Vector3> verts = new List<Vector3>();
|
||||||
|
List<int> tris = new List<int>();
|
||||||
|
List<Vector2> uvs = new List<Vector2>();
|
||||||
|
|
||||||
|
// build quad strip
|
||||||
|
for (int i = 0; i < pts.Count; i++)
|
||||||
|
{
|
||||||
|
Vector3 p = pts[i];
|
||||||
|
Vector3 dir;
|
||||||
|
if (i == 0) dir = (pts[i + 1] - p).normalized;
|
||||||
|
else if (i == pts.Count - 1) dir = (p - pts[i - 1]).normalized;
|
||||||
|
else dir = (pts[i + 1] - pts[i - 1]).normalized;
|
||||||
|
|
||||||
|
Vector3 normal = Vector3.Cross(dir, Vector3.up).normalized;
|
||||||
|
Vector3 left = p + normal * (width * 0.5f / _metersPerUnit);
|
||||||
|
Vector3 right = p - normal * (width * 0.5f / _metersPerUnit);
|
||||||
|
verts.Add(left);
|
||||||
|
verts.Add(right);
|
||||||
|
uvs.Add(new Vector2(0, i));
|
||||||
|
uvs.Add(new Vector2(1, i));
|
||||||
|
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
int baseIdx = verts.Count - 4;
|
||||||
|
tris.Add(baseIdx + 0);
|
||||||
|
tris.Add(baseIdx + 2);
|
||||||
|
tris.Add(baseIdx + 1);
|
||||||
|
|
||||||
|
tris.Add(baseIdx + 1);
|
||||||
|
tris.Add(baseIdx + 2);
|
||||||
|
tris.Add(baseIdx + 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.SetVertices(verts);
|
||||||
|
mesh.SetTriangles(tris, 0);
|
||||||
|
mesh.SetUVs(0, uvs);
|
||||||
|
mesh.RecalculateNormals();
|
||||||
|
mesh.RecalculateBounds();
|
||||||
|
|
||||||
|
mf.mesh = mesh;
|
||||||
|
go.transform.position = Vector3.zero;
|
||||||
|
return go;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
|
bool TryParseHeight(string s, out float meters)
|
||||||
|
{
|
||||||
|
// try to parse heights like "12", "12.5m", "40 ft"
|
||||||
|
s = s.Trim();
|
||||||
|
meters = 0f;
|
||||||
|
if (s.EndsWith("m")) s = s.Substring(0, s.Length - 1).Trim();
|
||||||
|
if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out float v))
|
||||||
|
{
|
||||||
|
meters = v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// fallback: try to extract number
|
||||||
|
StringBuilder num = new StringBuilder();
|
||||||
|
foreach (char c in s)
|
||||||
|
if ((c >= '0' && c <= '9') || c == '.' || c == ',') num.Append(c == ',' ? '.' : c);
|
||||||
|
if (num.Length > 0 && float.TryParse(num.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out v))
|
||||||
|
{
|
||||||
|
meters = v; return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic ear clipping triangulation for simple polygons (2D)
|
||||||
|
List<int> Triangulate(List<Vector2> poly)
|
||||||
|
{
|
||||||
|
List<int> indices = new List<int>();
|
||||||
|
int n = poly.Count;
|
||||||
|
if (n < 3) return indices;
|
||||||
|
|
||||||
|
List<int> V = new List<int>();
|
||||||
|
for (int i = 0; i < n; i++) V.Add(i);
|
||||||
|
|
||||||
|
int guard = 0;
|
||||||
|
while (V.Count > 3 && guard < 10000)
|
||||||
|
{
|
||||||
|
bool earFound = false;
|
||||||
|
for (int i = 0; i < V.Count; i++)
|
||||||
|
{
|
||||||
|
int prev = V[(i - 1 + V.Count) % V.Count];
|
||||||
|
int curr = V[i];
|
||||||
|
int next = V[(i + 1) % V.Count];
|
||||||
|
|
||||||
|
Vector2 a = poly[prev];
|
||||||
|
Vector2 b = poly[curr];
|
||||||
|
Vector2 c = poly[next];
|
||||||
|
|
||||||
|
if (!IsConvex(a, b, c)) continue;
|
||||||
|
|
||||||
|
bool hasPointInside = false;
|
||||||
|
for (int j = 0; j < V.Count; j++)
|
||||||
|
{
|
||||||
|
int vi = V[j];
|
||||||
|
if (vi == prev || vi == curr || vi == next) continue;
|
||||||
|
if (PointInTriangle(poly[vi], a, b, c)) { hasPointInside = true; break; }
|
||||||
|
}
|
||||||
|
if (hasPointInside) continue;
|
||||||
|
|
||||||
|
// ear found
|
||||||
|
indices.Add(prev);
|
||||||
|
indices.Add(curr);
|
||||||
|
indices.Add(next);
|
||||||
|
V.RemoveAt(i);
|
||||||
|
earFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!earFound) break;
|
||||||
|
guard++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (V.Count == 3)
|
||||||
|
{
|
||||||
|
indices.Add(V[0]); indices.Add(V[1]); indices.Add(V[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsConvex(Vector2 a, Vector2 b, Vector2 c)
|
||||||
|
{
|
||||||
|
return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) < 0f; // changed > to
|
||||||
|
}
|
||||||
|
bool PointInTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
|
||||||
|
{
|
||||||
|
float area = TriangleArea(a, b, c);
|
||||||
|
float area1 = TriangleArea(p, b, c);
|
||||||
|
float area2 = TriangleArea(a, p, c);
|
||||||
|
float area3 = TriangleArea(a, b, p);
|
||||||
|
return Mathf.Abs(area - (area1 + area2 + area3)) < 1e-3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float TriangleArea(Vector2 a, Vector2 b, Vector2 c)
|
||||||
|
{
|
||||||
|
return Mathf.Abs((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) * 0.5f);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
2
Assets/Scripts/MapRenderer.cs.meta
Normal file
2
Assets/Scripts/MapRenderer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 648d9484af013c346bd5ae603a8c7185
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class PlayAudioOnCamera : MonoBehaviour
|
|
||||||
{
|
|
||||||
public AudioClip clip; // assign your audio file in the Inspector
|
|
||||||
private AudioSource audioSource;
|
|
||||||
|
|
||||||
void Start()
|
|
||||||
{
|
|
||||||
// Get or add an AudioSource to this GameObject
|
|
||||||
audioSource = gameObject.GetComponent<AudioSource>();
|
|
||||||
if (audioSource == null)
|
|
||||||
{
|
|
||||||
audioSource = gameObject.AddComponent<AudioSource>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign the audio clip
|
|
||||||
if (clip != null)
|
|
||||||
{
|
|
||||||
audioSource.clip = clip;
|
|
||||||
audioSource.playOnAwake = false;
|
|
||||||
audioSource.loop = false; // change to true if you want looping
|
|
||||||
audioSource.Play();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.LogWarning("PlayAudioOnCamera: No audio clip assigned!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 37f33c8b63e5b384db6a385b909b27aa
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class TeleportOnTrigger : MonoBehaviour
|
|
||||||
{
|
|
||||||
[Header("Teleport Target")]
|
|
||||||
public Transform targetPosition;
|
|
||||||
|
|
||||||
private void OnTriggerEnter(Collider other)
|
|
||||||
{
|
|
||||||
if (targetPosition == null) return;
|
|
||||||
|
|
||||||
Rigidbody rb = other.attachedRigidbody;
|
|
||||||
|
|
||||||
if (rb != null)
|
|
||||||
{
|
|
||||||
// STOP ALL MOTION
|
|
||||||
rb.linearVelocity = Vector3.zero;
|
|
||||||
rb.angularVelocity = Vector3.zero;
|
|
||||||
|
|
||||||
// TELEPORT
|
|
||||||
rb.position = targetPosition.position;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Non-physics objects
|
|
||||||
other.transform.position = targetPosition.position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 2e101cb487f02244592e16548d98f0b5
|
|
||||||
84
Assets/Scripts/TestMaterial.mat
Normal file
84
Assets/Scripts/TestMaterial.mat
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!21 &2100000
|
||||||
|
Material:
|
||||||
|
serializedVersion: 8
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: TestMaterial
|
||||||
|
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_Parent: {fileID: 0}
|
||||||
|
m_ModifiedSerializedProperties: 0
|
||||||
|
m_ValidKeywords: []
|
||||||
|
m_InvalidKeywords: []
|
||||||
|
m_LightmapFlags: 4
|
||||||
|
m_EnableInstancingVariants: 0
|
||||||
|
m_DoubleSidedGI: 0
|
||||||
|
m_CustomRenderQueue: -1
|
||||||
|
stringTagMap: {}
|
||||||
|
disabledShaderPasses: []
|
||||||
|
m_LockedProperties:
|
||||||
|
m_SavedProperties:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TexEnvs:
|
||||||
|
- _BumpMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailAlbedoMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailMask:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailNormalMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _EmissionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MainTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MetallicGlossMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _OcclusionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _ParallaxMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_Ints: []
|
||||||
|
m_Floats:
|
||||||
|
- _BumpScale: 1
|
||||||
|
- _Cutoff: 0.5
|
||||||
|
- _DetailNormalMapScale: 1
|
||||||
|
- _DstBlend: 0
|
||||||
|
- _GlossMapScale: 1
|
||||||
|
- _Glossiness: 0.5
|
||||||
|
- _GlossyReflections: 1
|
||||||
|
- _Metallic: 0
|
||||||
|
- _Mode: 0
|
||||||
|
- _OcclusionStrength: 1
|
||||||
|
- _Parallax: 0.02
|
||||||
|
- _SmoothnessTextureChannel: 0
|
||||||
|
- _SpecularHighlights: 1
|
||||||
|
- _SrcBlend: 1
|
||||||
|
- _UVSec: 0
|
||||||
|
- _ZWrite: 1
|
||||||
|
m_Colors:
|
||||||
|
- _Color: {r: 0.1254902, g: 0.1254902, b: 0.1254902, a: 1}
|
||||||
|
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
|
m_BuildTextureStacks: []
|
||||||
|
m_AllowLocking: 1
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: befd731805f7270498a643333899b479
|
guid: 950ad2ddcd752c84a92eec2603508248
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 0
|
mainObjectFileID: 0
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class TiltHoleMiniGameManager : MonoBehaviour, ITask
|
|
||||||
{
|
|
||||||
// =========================
|
|
||||||
// ITask PROPERTIES
|
|
||||||
// =========================
|
|
||||||
|
|
||||||
public string TaskID { get; private set; } = "TILT_HOLE_01";
|
|
||||||
public TaskType TaskType { get; private set; } = TaskType.Task;
|
|
||||||
public string TaskName { get; private set; } = "Tilt Ball Into Hole";
|
|
||||||
public (double, double) TaskLocation { get; private set; } = (0, 0);
|
|
||||||
public bool IsCompleted { get; private set; }
|
|
||||||
|
|
||||||
private Action<ITask> _onCompletedCallback;
|
|
||||||
|
|
||||||
// =========================
|
|
||||||
// MINIGAME REFERENCES
|
|
||||||
// =========================
|
|
||||||
|
|
||||||
[Header("References")]
|
|
||||||
public GyroPlatformController platformController;
|
|
||||||
public HoleGoalTrigger goalTrigger;
|
|
||||||
public Rigidbody ball;
|
|
||||||
|
|
||||||
[Header("Audio")]
|
|
||||||
public AudioClip startSound;
|
|
||||||
public AudioClip winSound;
|
|
||||||
|
|
||||||
private AudioSource audioSource;
|
|
||||||
|
|
||||||
[Header("Game State")]
|
|
||||||
public bool isActive = false;
|
|
||||||
|
|
||||||
[Header("Win Behavior")]
|
|
||||||
public bool freezeOnWin = true;
|
|
||||||
public bool disableBallOnWin = false;
|
|
||||||
|
|
||||||
// =========================
|
|
||||||
// UNITY LIFECYCLE
|
|
||||||
// =========================
|
|
||||||
|
|
||||||
void Awake()
|
|
||||||
{
|
|
||||||
if (platformController == null) platformController = FindFirstObjectByType<GyroPlatformController>();
|
|
||||||
if (goalTrigger == null) goalTrigger = FindFirstObjectByType<HoleGoalTrigger>();
|
|
||||||
|
|
||||||
if (Camera.main != null)
|
|
||||||
{
|
|
||||||
audioSource = Camera.main.GetComponent<AudioSource>();
|
|
||||||
if (audioSource == null)
|
|
||||||
audioSource = Camera.main.gameObject.AddComponent<AudioSource>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Start()
|
|
||||||
{
|
|
||||||
if (goalTrigger != null)
|
|
||||||
{
|
|
||||||
goalTrigger.OnBallScored += HandleWin;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ball != null && goalTrigger != null)
|
|
||||||
{
|
|
||||||
goalTrigger.ballRigidbody = ball;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnDestroy()
|
|
||||||
{
|
|
||||||
if (goalTrigger != null)
|
|
||||||
goalTrigger.OnBallScored -= HandleWin;
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================
|
|
||||||
// TASK LIFECYCLE
|
|
||||||
// =========================
|
|
||||||
|
|
||||||
public void Initialize(Action<ITask> onCompleted)
|
|
||||||
{
|
|
||||||
Debug.Log("Initializing Tilt Hole Task");
|
|
||||||
|
|
||||||
IsCompleted = false;
|
|
||||||
isActive = true;
|
|
||||||
_onCompletedCallback = onCompleted;
|
|
||||||
|
|
||||||
if (platformController != null)
|
|
||||||
platformController.enabled = true;
|
|
||||||
|
|
||||||
if (ball != null)
|
|
||||||
{
|
|
||||||
ball.isKinematic = false;
|
|
||||||
ball.linearVelocity = Vector3.zero;
|
|
||||||
ball.angularVelocity = Vector3.zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startSound != null && audioSource != null)
|
|
||||||
{
|
|
||||||
audioSource.PlayOneShot(startSound);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ExitTask(Action<ITask> onExit)
|
|
||||||
{
|
|
||||||
Debug.Log("Exiting Tilt Hole Task");
|
|
||||||
|
|
||||||
isActive = false;
|
|
||||||
|
|
||||||
if (platformController != null)
|
|
||||||
platformController.enabled = false;
|
|
||||||
|
|
||||||
onExit?.Invoke(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Complete()
|
|
||||||
{
|
|
||||||
if (IsCompleted) return;
|
|
||||||
|
|
||||||
Debug.Log("Task Complete: Tilt Hole");
|
|
||||||
|
|
||||||
IsCompleted = true;
|
|
||||||
isActive = false;
|
|
||||||
|
|
||||||
if (winSound != null && audioSource != null)
|
|
||||||
{
|
|
||||||
audioSource.PlayOneShot(winSound);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onCompletedCallback?.Invoke(this);
|
|
||||||
ExitTask(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================
|
|
||||||
// MINIGAME WIN EVENT
|
|
||||||
// =========================
|
|
||||||
|
|
||||||
private void HandleWin()
|
|
||||||
{
|
|
||||||
if (!isActive) return;
|
|
||||||
|
|
||||||
Debug.Log("Ball reached hole.");
|
|
||||||
|
|
||||||
if (freezeOnWin)
|
|
||||||
{
|
|
||||||
if (platformController != null)
|
|
||||||
platformController.enabled = false;
|
|
||||||
|
|
||||||
if (ball != null)
|
|
||||||
{
|
|
||||||
ball.linearVelocity = Vector3.zero;
|
|
||||||
ball.angularVelocity = Vector3.zero;
|
|
||||||
ball.isKinematic = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disableBallOnWin && ball != null)
|
|
||||||
{
|
|
||||||
ball.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Complete(); // 🔥 THIS completes the task
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================
|
|
||||||
// DEBUG GUI
|
|
||||||
// =========================
|
|
||||||
|
|
||||||
void OnGUI()
|
|
||||||
{
|
|
||||||
GUIStyle s = new GUIStyle(GUI.skin.label);
|
|
||||||
s.fontSize = 24;
|
|
||||||
|
|
||||||
if (isActive)
|
|
||||||
{
|
|
||||||
s.normal.textColor = Color.white;
|
|
||||||
GUI.Label(new Rect(10, 10, 700, 30),
|
|
||||||
"Goal: Tilt platform to roll the ball into the hole.", s);
|
|
||||||
}
|
|
||||||
else if (IsCompleted)
|
|
||||||
{
|
|
||||||
s.normal.textColor = Color.yellow;
|
|
||||||
GUI.Label(new Rect(10, 10, 700, 30),
|
|
||||||
"Task Completed!", s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 9f70c5a8e211b7142b26b4ea95bf2348
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class WindController : MonoBehaviour
|
|
||||||
{
|
|
||||||
[Header("settings větru")]
|
|
||||||
[Tooltip("Maximální síla větru (kladná i záporná)")]
|
|
||||||
public float maxWindTorque = 8f;
|
|
||||||
|
|
||||||
[Tooltip("Jak rychle se větr mění směrem/sílou")]
|
|
||||||
public float windChangeSpeed = 0.6f;
|
|
||||||
|
|
||||||
[Tooltip("Jak často se objeví silnější vichřice (v sekundách)")]
|
|
||||||
public float gustInterval = 4f;
|
|
||||||
|
|
||||||
[Tooltip("Multiplier pro sílu vichřice")]
|
|
||||||
public float gustMultiplier = 2.0f;
|
|
||||||
|
|
||||||
public float CurrentWindTorque { get; private set; }
|
|
||||||
|
|
||||||
private float targetTorque;
|
|
||||||
private float gustTimer;
|
|
||||||
|
|
||||||
void Start()
|
|
||||||
{
|
|
||||||
PickNewTargetTorque();
|
|
||||||
gustTimer = gustInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update()
|
|
||||||
{
|
|
||||||
// Smoothly move wind toward target torque
|
|
||||||
CurrentWindTorque = Mathf.Lerp(CurrentWindTorque, targetTorque, Time.deltaTime * windChangeSpeed);
|
|
||||||
|
|
||||||
// Occasional gusts
|
|
||||||
gustTimer -= Time.deltaTime;
|
|
||||||
if (gustTimer <= 0f)
|
|
||||||
{
|
|
||||||
// Apply a short gust by shifting target torque more aggressively
|
|
||||||
float gust = Random.Range(-maxWindTorque, maxWindTorque) * gustMultiplier;
|
|
||||||
targetTorque = Mathf.Clamp(gust, -maxWindTorque * gustMultiplier, maxWindTorque * gustMultiplier);
|
|
||||||
|
|
||||||
gustTimer = gustInterval;
|
|
||||||
Invoke(nameof(PickNewTargetTorque), 0.8f); // gust lasts ~0.8s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PickNewTargetTorque()
|
|
||||||
{
|
|
||||||
targetTorque = Random.Range(-maxWindTorque, maxWindTorque);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: a2d62d3ffa507924ab3a6fd97f185f3e
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 3c41f51487bbe394b9b8568d1b254bd4
|
guid: 5a64c672ea90b0f47aa9765ef8cd36bf
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: bbad6d4e785a4584da7dda5f1bda30d5
|
guid: 9fd22110cecc9764db443475c88fb5f8
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 15003, guid: 0000000000000000e000000000000000, type: 0}
|
|
||||||
m_Name: "Android\u2122 1"
|
|
||||||
m_EditorClassIdentifier: UnityEditor.dll::UnityEditor.Build.Profile.BuildProfile
|
|
||||||
m_AssetVersion: 1
|
|
||||||
m_BuildTarget: 13
|
|
||||||
m_Subtarget: 0
|
|
||||||
m_PlatformId: b9b35072a6f44c2e863f17467ea3dc13
|
|
||||||
m_PlatformBuildProfile:
|
|
||||||
rid: 240730859082350731
|
|
||||||
m_OverrideGlobalSceneList: 0
|
|
||||||
m_Scenes: []
|
|
||||||
m_ScriptingDefines: []
|
|
||||||
m_PlayerSettingsYaml:
|
|
||||||
m_Settings: []
|
|
||||||
references:
|
|
||||||
version: 2
|
|
||||||
RefIds:
|
|
||||||
- rid: 240730859082350731
|
|
||||||
type: {class: AndroidPlatformBuildSettings, ns: UnityEditor.Android, asm: UnityEditor.Android.Extensions}
|
|
||||||
data:
|
|
||||||
m_Development: 0
|
|
||||||
m_ConnectProfiler: 0
|
|
||||||
m_BuildWithDeepProfilingSupport: 0
|
|
||||||
m_AllowDebugging: 0
|
|
||||||
m_WaitForManagedDebugger: 0
|
|
||||||
m_ManagedDebuggerFixedPort: 0
|
|
||||||
m_ExplicitNullChecks: 0
|
|
||||||
m_ExplicitDivideByZeroChecks: 0
|
|
||||||
m_ExplicitArrayBoundsChecks: 0
|
|
||||||
m_CompressionType: 2
|
|
||||||
m_InstallInBuildFolder: 0
|
|
||||||
m_InsightsSettingsContainer:
|
|
||||||
m_BuildProfileEngineDiagnosticsState: 2
|
|
||||||
m_BuildSubtarget: 0
|
|
||||||
m_BuildSystem: 1
|
|
||||||
m_ExportAsGoogleAndroidProject: 0
|
|
||||||
m_DebugSymbolLevel: 1
|
|
||||||
m_DebugSymbolFormat: 5
|
|
||||||
m_CurrentDeploymentTargetId: __builtin__target_default
|
|
||||||
m_BuildType: 2
|
|
||||||
m_LinkTimeOptimization: 0
|
|
||||||
m_BuildAppBundle: 0
|
|
||||||
m_IPAddressToConnect:
|
|
||||||
m_SymlinkSources: 0
|
|
||||||
@@ -17,7 +17,7 @@ MonoBehaviour:
|
|||||||
m_Subtarget: 0
|
m_Subtarget: 0
|
||||||
m_PlatformId: b9b35072a6f44c2e863f17467ea3dc13
|
m_PlatformId: b9b35072a6f44c2e863f17467ea3dc13
|
||||||
m_PlatformBuildProfile:
|
m_PlatformBuildProfile:
|
||||||
rid: 4246746364202188844
|
rid: 5796307965217079336
|
||||||
m_OverrideGlobalSceneList: 0
|
m_OverrideGlobalSceneList: 0
|
||||||
m_Scenes: []
|
m_Scenes: []
|
||||||
m_ScriptingDefines: []
|
m_ScriptingDefines: []
|
||||||
@@ -26,10 +26,10 @@ MonoBehaviour:
|
|||||||
references:
|
references:
|
||||||
version: 2
|
version: 2
|
||||||
RefIds:
|
RefIds:
|
||||||
- rid: 4246746364202188844
|
- rid: 5796307965217079336
|
||||||
type: {class: AndroidPlatformBuildSettings, ns: UnityEditor.Android, asm: UnityEditor.Android.Extensions}
|
type: {class: AndroidPlatformBuildSettings, ns: UnityEditor.Android, asm: UnityEditor.Android.Extensions}
|
||||||
data:
|
data:
|
||||||
m_Development: 1
|
m_Development: 0
|
||||||
m_ConnectProfiler: 0
|
m_ConnectProfiler: 0
|
||||||
m_BuildWithDeepProfilingSupport: 0
|
m_BuildWithDeepProfilingSupport: 0
|
||||||
m_AllowDebugging: 0
|
m_AllowDebugging: 0
|
||||||
@@ -45,10 +45,10 @@ MonoBehaviour:
|
|||||||
m_BuildSubtarget: 0
|
m_BuildSubtarget: 0
|
||||||
m_BuildSystem: 1
|
m_BuildSystem: 1
|
||||||
m_ExportAsGoogleAndroidProject: 0
|
m_ExportAsGoogleAndroidProject: 0
|
||||||
m_DebugSymbolLevel: 4
|
m_DebugSymbolLevel: 1
|
||||||
m_DebugSymbolFormat: 5
|
m_DebugSymbolFormat: 5
|
||||||
m_CurrentDeploymentTargetId: __builtin__target_default
|
m_CurrentDeploymentTargetId: __builtin__target_default
|
||||||
m_BuildType: 1
|
m_BuildType: 2
|
||||||
m_LinkTimeOptimization: 0
|
m_LinkTimeOptimization: 0
|
||||||
m_BuildAppBundle: 0
|
m_BuildAppBundle: 0
|
||||||
m_IPAddressToConnect:
|
m_IPAddressToConnect:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 7f18313555a02cd48bc22d29d0f2ff55
|
guid: 1a0d4317fa56b6f4ca3cc36db322f632
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 11400000
|
mainObjectFileID: 0
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
assetBundleVariant:
|
assetBundleVariant:
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 15003, guid: 0000000000000000e000000000000000, type: 0}
|
|
||||||
m_Name: iOS
|
|
||||||
m_EditorClassIdentifier: UnityEditor.dll::UnityEditor.Build.Profile.BuildProfile
|
|
||||||
m_AssetVersion: 1
|
|
||||||
m_BuildTarget: 9
|
|
||||||
m_Subtarget: 0
|
|
||||||
m_PlatformId: ad48d16a66894befa4d8181998c3cb09
|
|
||||||
m_PlatformBuildProfile:
|
|
||||||
rid: 4718407457732296746
|
|
||||||
m_OverrideGlobalSceneList: 0
|
|
||||||
m_Scenes: []
|
|
||||||
m_ScriptingDefines:
|
|
||||||
-
|
|
||||||
m_PlayerSettingsYaml:
|
|
||||||
m_Settings: []
|
|
||||||
references:
|
|
||||||
version: 2
|
|
||||||
RefIds:
|
|
||||||
- rid: 4718407457732296746
|
|
||||||
type: {class: iOSPlatformSettings, ns: UnityEditor.iOS, asm: UnityEditor.iOS.Extensions}
|
|
||||||
data:
|
|
||||||
m_Development: 0
|
|
||||||
m_ConnectProfiler: 0
|
|
||||||
m_BuildWithDeepProfilingSupport: 0
|
|
||||||
m_AllowDebugging: 0
|
|
||||||
m_WaitForManagedDebugger: 0
|
|
||||||
m_ManagedDebuggerFixedPort: 0
|
|
||||||
m_ExplicitNullChecks: 0
|
|
||||||
m_ExplicitDivideByZeroChecks: 0
|
|
||||||
m_ExplicitArrayBoundsChecks: 0
|
|
||||||
m_CompressionType: 0
|
|
||||||
m_InstallInBuildFolder: 0
|
|
||||||
m_InsightsSettingsContainer:
|
|
||||||
m_BuildProfileEngineDiagnosticsState: 2
|
|
||||||
m_iOSXcodeBuildConfig: 1
|
|
||||||
m_SymlinkSources: 0
|
|
||||||
m_PreferredXcode:
|
|
||||||
m_SymlinkTrampoline: 0
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 526aab55ceaf10142a59ee4d62fe8f42
|
guid: 8dda0318c5f0774428ff048cf1cc7903
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 0
|
mainObjectFileID: 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 0e9d05516516cba48aff23bd436701ca
|
guid: 1b1d1c584534f0943bd7782382ca724d
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 0
|
mainObjectFileID: 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: e6ba5b26dda963e43beba7012129f6a5
|
guid: 16789ecfba5bb8b4f9ffe2a76899c9a0
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 0
|
mainObjectFileID: 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 9c73e51f432aa184cb1408179eb09d3f
|
guid: 32964dc92131b7640be075f3a6fba255
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 0
|
mainObjectFileID: 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 1fe0ebf7f09ec7e4fa76e81c31b87dde
|
guid: 9c16494a2393df0429ba240dbfde961b
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 0
|
mainObjectFileID: 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 3dc1b9d50da01394198653f975d9c13d
|
guid: abb54dd1945d1f747981a246f46dfdb4
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 0
|
mainObjectFileID: 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 7bfdf167f826294468ffbde02bb390f1
|
guid: 01963b65aa570034185cb6e241239ba7
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 0
|
mainObjectFileID: 0
|
||||||
|
|||||||
8
Assets/TextMesh Pro.meta
Normal file
8
Assets/TextMesh Pro.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f54d1bd14bd3ca042bd867b519fee8cc
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/TextMesh Pro/Examples & Extras.meta
Normal file
8
Assets/TextMesh Pro/Examples & Extras.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ce51c8e33b734b4db6086586558c53a3
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/TextMesh Pro/Examples & Extras/Fonts.meta
Normal file
8
Assets/TextMesh Pro/Examples & Extras/Fonts.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b63e0053080646b9819789bf3bf9fa17
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
93
Assets/TextMesh Pro/Examples & Extras/Fonts/Anton OFL.txt
Normal file
93
Assets/TextMesh Pro/Examples & Extras/Fonts/Anton OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright (c) 2011, Vernon Adams (vern@newtypography.co.uk),
|
||||||
|
with Reserved Font Name Anton.
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 73a79399807f4e8388c2cbb5494681ca
|
||||||
|
timeCreated: 1484172033
|
||||||
|
licenseType: Pro
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/TextMesh Pro/Examples & Extras/Fonts/Anton.ttf
Normal file
BIN
Assets/TextMesh Pro/Examples & Extras/Fonts/Anton.ttf
Normal file
Binary file not shown.
19
Assets/TextMesh Pro/Examples & Extras/Fonts/Anton.ttf.meta
Normal file
19
Assets/TextMesh Pro/Examples & Extras/Fonts/Anton.ttf.meta
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 997a43b767814dd0a7642ec9b78cba41
|
||||||
|
timeCreated: 1484172033
|
||||||
|
licenseType: Pro
|
||||||
|
TrueTypeFontImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
fontSize: 16
|
||||||
|
forceTextureCase: -2
|
||||||
|
characterSpacing: 1
|
||||||
|
characterPadding: 0
|
||||||
|
includeFontData: 1
|
||||||
|
use2xBehaviour: 0
|
||||||
|
fontNames: []
|
||||||
|
fallbackFontReferences: []
|
||||||
|
customCharacters:
|
||||||
|
fontRenderingMode: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright (c) 2010 by vernon adams (vern@newtypography.co.uk),
|
||||||
|
with Reserved Font Name Bangers.
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: efe0bf4ac872451e91612d1ae593f480
|
||||||
|
timeCreated: 1484171296
|
||||||
|
licenseType: Pro
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/TextMesh Pro/Examples & Extras/Fonts/Bangers.ttf
Normal file
BIN
Assets/TextMesh Pro/Examples & Extras/Fonts/Bangers.ttf
Normal file
Binary file not shown.
19
Assets/TextMesh Pro/Examples & Extras/Fonts/Bangers.ttf.meta
Normal file
19
Assets/TextMesh Pro/Examples & Extras/Fonts/Bangers.ttf.meta
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5dd49b3eacc540408c98eee0de38e0f1
|
||||||
|
timeCreated: 1484171297
|
||||||
|
licenseType: Pro
|
||||||
|
TrueTypeFontImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
fontSize: 16
|
||||||
|
forceTextureCase: -2
|
||||||
|
characterSpacing: 1
|
||||||
|
characterPadding: 0
|
||||||
|
includeFontData: 1
|
||||||
|
use2xBehaviour: 0
|
||||||
|
fontNames: []
|
||||||
|
fallbackFontReferences: []
|
||||||
|
customCharacters:
|
||||||
|
fontRenderingMode: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8a2b9e2a607dd2143b58c44bc32410b4
|
||||||
|
TrueTypeFontImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 4
|
||||||
|
fontSize: 16
|
||||||
|
forceTextureCase: -2
|
||||||
|
characterSpacing: 0
|
||||||
|
characterPadding: 1
|
||||||
|
includeFontData: 1
|
||||||
|
fontName: Electronic Highway Sign
|
||||||
|
fontNames:
|
||||||
|
- Electronic Highway Sign
|
||||||
|
fallbackFontReferences: []
|
||||||
|
customCharacters:
|
||||||
|
fontRenderingMode: 0
|
||||||
|
ascentCalculationMode: 1
|
||||||
|
useLegacyBoundsCalculation: 0
|
||||||
|
shouldRoundAdvanceValue: 1
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
Copyright (c) 2011-2012, Vernon Adams (vern@newtypography.co.uk), with Reserved Font Names 'Oswald'
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d2cf87a8a7a94aa8b80dff1c807c1178
|
||||||
|
timeCreated: 1484171296
|
||||||
|
licenseType: Pro
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/TextMesh Pro/Examples & Extras/Fonts/Oswald-Bold.ttf
Normal file
BIN
Assets/TextMesh Pro/Examples & Extras/Fonts/Oswald-Bold.ttf
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user