Repaired hole minigame
This commit is contained in:
8
Assets/ClientSDK.meta
Normal file
8
Assets/ClientSDK.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 799f52449ae21404c9a7593f6dc28c60
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
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:
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
//using GeoSus.Client;
|
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ù
|
||||||
@@ -14,7 +15,7 @@ public interface ITask
|
|||||||
public string TaskID { get; } // Unikátní ID úkolu pro server
|
public string TaskID { get; } // Unikátní ID úkolu pro server
|
||||||
public TaskType TaskType { get; } // Typ úkolu
|
public TaskType TaskType { get; } // Typ úkolu
|
||||||
public string TaskName { get; } // Viditelný název úkolu
|
public string TaskName { get; } // Viditelný název úkolu
|
||||||
public (double, double) TaskLocation { get; } // Polohy na mapì
|
public Position TaskLocation { get; } // 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
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ GameObject:
|
|||||||
- component: {fileID: 6503151}
|
- component: {fileID: 6503151}
|
||||||
- component: {fileID: 6503150}
|
- component: {fileID: 6503150}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Cylinder
|
m_Name: SpecialHole
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
@@ -151,11 +151,8 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: ca7423fcca5f83249a2574cd84b7f806, type: 3}
|
m_Script: {fileID: 11500000, guid: ca7423fcca5f83249a2574cd84b7f806, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::Hole
|
m_EditorClassIdentifier: Assembly-CSharp::Hole
|
||||||
catchRadius: 1
|
catchRadius: 10
|
||||||
pullForce: 4
|
pullForce: 4
|
||||||
hasMovement: 0
|
|
||||||
moveSpeed: 2
|
|
||||||
moveRange: {x: 1.5, y: 0}
|
|
||||||
glowRenderer: {fileID: 0}
|
glowRenderer: {fileID: 0}
|
||||||
--- !u!136 &6503151
|
--- !u!136 &6503151
|
||||||
CapsuleCollider:
|
CapsuleCollider:
|
||||||
@@ -172,14 +169,14 @@ CapsuleCollider:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Bits: 0
|
m_Bits: 0
|
||||||
m_LayerOverridePriority: 0
|
m_LayerOverridePriority: 0
|
||||||
m_IsTrigger: 0
|
m_IsTrigger: 1
|
||||||
m_ProvidesContacts: 0
|
m_ProvidesContacts: 0
|
||||||
m_Enabled: 0
|
m_Enabled: 1
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Radius: 0.5000001
|
m_Radius: 0.5000001
|
||||||
m_Height: 2
|
m_Height: 50
|
||||||
m_Direction: 1
|
m_Direction: 1
|
||||||
m_Center: {x: 0.000000059604645, y: 0, z: -0.00000008940697}
|
m_Center: {x: 0.00000006274173, y: 7.2227635, z: -0.0000000941126}
|
||||||
--- !u!23 &6503152
|
--- !u!23 &6503152
|
||||||
MeshRenderer:
|
MeshRenderer:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -1600,179 +1597,38 @@ Transform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 50, y: 180, z: 0}
|
m_LocalEulerAnglesHint: {x: 50, y: 180, z: 0}
|
||||||
--- !u!1 &727261126
|
--- !u!1 &727261126 stripped
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_CorrespondingSourceObject: {fileID: 4463416604269783915, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_PrefabInstance: {fileID: 9074197596846296259}
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
serializedVersion: 6
|
--- !u!54 &727261133
|
||||||
m_Component:
|
Rigidbody:
|
||||||
- component: {fileID: 727261130}
|
|
||||||
- component: {fileID: 727261129}
|
|
||||||
- component: {fileID: 727261128}
|
|
||||||
- component: {fileID: 727261133}
|
|
||||||
- component: {fileID: 727261132}
|
|
||||||
- component: {fileID: 727261131}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: Sphere
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!23 &727261128
|
|
||||||
MeshRenderer:
|
|
||||||
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: 727261126}
|
m_GameObject: {fileID: 727261126}
|
||||||
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: 1a424fd36745ea14682570c13681319e, 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 &727261129
|
|
||||||
MeshFilter:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 727261126}
|
|
||||||
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
|
|
||||||
--- !u!4 &727261130
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 727261126}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
|
||||||
m_LocalPosition: {x: -13.138, y: -2.91, z: -0.63}
|
|
||||||
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!50 &727261131
|
|
||||||
Rigidbody2D:
|
|
||||||
serializedVersion: 5
|
serializedVersion: 5
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 727261126}
|
|
||||||
m_BodyType: 0
|
|
||||||
m_Simulated: 1
|
|
||||||
m_UseFullKinematicContacts: 0
|
|
||||||
m_UseAutoMass: 0
|
|
||||||
m_Mass: 1
|
m_Mass: 1
|
||||||
m_LinearDamping: 0
|
m_LinearDamping: 0
|
||||||
m_AngularDamping: 0.05
|
m_AngularDamping: 0.05
|
||||||
m_GravityScale: 1
|
m_CenterOfMass: {x: 0, y: 0, z: 0}
|
||||||
m_Material: {fileID: 0}
|
m_InertiaTensor: {x: 1, y: 1, z: 1}
|
||||||
|
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
m_IncludeLayers:
|
m_IncludeLayers:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Bits: 0
|
m_Bits: 0
|
||||||
m_ExcludeLayers:
|
m_ExcludeLayers:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Bits: 0
|
m_Bits: 0
|
||||||
|
m_ImplicitCom: 1
|
||||||
|
m_ImplicitTensor: 1
|
||||||
|
m_UseGravity: 1
|
||||||
|
m_IsKinematic: 0
|
||||||
m_Interpolate: 0
|
m_Interpolate: 0
|
||||||
m_SleepingMode: 1
|
|
||||||
m_CollisionDetection: 0
|
|
||||||
m_Constraints: 0
|
m_Constraints: 0
|
||||||
--- !u!114 &727261132
|
m_CollisionDetection: 0
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 727261126}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: fb5157d7cd78450439c40cd6f5afe6ac, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::DraggableObject
|
|
||||||
dragSmoothness: 15
|
|
||||||
spriteRenderer: {fileID: 0}
|
|
||||||
normalColor: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
dragColor: {r: 1, g: 1, b: 0.5, a: 1}
|
|
||||||
scaleOnDrag: 1.15
|
|
||||||
--- !u!70 &727261133
|
|
||||||
CapsuleCollider2D:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 727261126}
|
|
||||||
m_Enabled: 1
|
|
||||||
serializedVersion: 3
|
|
||||||
m_Density: 1
|
|
||||||
m_Material: {fileID: 0}
|
|
||||||
m_IncludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_ExcludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_LayerOverridePriority: 0
|
|
||||||
m_ForceSendLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_ForceReceiveLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_ContactCaptureLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_CallbackLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_IsTrigger: 1
|
|
||||||
m_UsedByEffector: 0
|
|
||||||
m_CompositeOperation: 0
|
|
||||||
m_CompositeOrder: 0
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
m_Size: {x: 1, y: 1}
|
|
||||||
m_Direction: 0
|
|
||||||
--- !u!1 &776138489
|
--- !u!1 &776138489
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -3068,7 +2924,7 @@ GameObject:
|
|||||||
- component: {fileID: 1190939575}
|
- component: {fileID: 1190939575}
|
||||||
- component: {fileID: 1190939574}
|
- component: {fileID: 1190939574}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: GameObject
|
m_Name: Spawner
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
@@ -3086,9 +2942,11 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: 071f79f81861c2741a92d8b044457d94, type: 3}
|
m_Script: {fileID: 11500000, guid: 071f79f81861c2741a92d8b044457d94, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::ObjectSpawner
|
m_EditorClassIdentifier: Assembly-CSharp::ObjectSpawner
|
||||||
|
spawnedHoles:
|
||||||
|
- {fileID: 6503149}
|
||||||
|
- {fileID: 1386843746}
|
||||||
objectPrefabs:
|
objectPrefabs:
|
||||||
- {fileID: 727261126}
|
- {fileID: 727261126}
|
||||||
holePrefab: {fileID: 6503149}
|
|
||||||
objectCount: 3
|
objectCount: 3
|
||||||
holeCount: 0
|
holeCount: 0
|
||||||
holesMove: 0
|
holesMove: 0
|
||||||
@@ -3097,8 +2955,6 @@ MonoBehaviour:
|
|||||||
maxX: 3.5
|
maxX: 3.5
|
||||||
minY: -5
|
minY: -5
|
||||||
maxY: 4
|
maxY: 4
|
||||||
objectParent: {fileID: 727261130}
|
|
||||||
holeParent: {fileID: 6503154}
|
|
||||||
--- !u!4 &1190939575
|
--- !u!4 &1190939575
|
||||||
Transform:
|
Transform:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -3683,7 +3539,7 @@ GameObject:
|
|||||||
- component: {fileID: 1386843748}
|
- component: {fileID: 1386843748}
|
||||||
- component: {fileID: 1386843747}
|
- component: {fileID: 1386843747}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Cylinder (1)
|
m_Name: ClassicHole
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
@@ -3701,11 +3557,8 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: ca7423fcca5f83249a2574cd84b7f806, type: 3}
|
m_Script: {fileID: 11500000, guid: ca7423fcca5f83249a2574cd84b7f806, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::Hole
|
m_EditorClassIdentifier: Assembly-CSharp::Hole
|
||||||
catchRadius: 1
|
catchRadius: 10
|
||||||
pullForce: 4
|
pullForce: 4
|
||||||
hasMovement: 0
|
|
||||||
moveSpeed: 2
|
|
||||||
moveRange: {x: 1.5, y: 0}
|
|
||||||
glowRenderer: {fileID: 0}
|
glowRenderer: {fileID: 0}
|
||||||
--- !u!136 &1386843748
|
--- !u!136 &1386843748
|
||||||
CapsuleCollider:
|
CapsuleCollider:
|
||||||
@@ -3722,14 +3575,14 @@ CapsuleCollider:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Bits: 0
|
m_Bits: 0
|
||||||
m_LayerOverridePriority: 0
|
m_LayerOverridePriority: 0
|
||||||
m_IsTrigger: 0
|
m_IsTrigger: 1
|
||||||
m_ProvidesContacts: 0
|
m_ProvidesContacts: 0
|
||||||
m_Enabled: 0
|
m_Enabled: 1
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Radius: 0.5000001
|
m_Radius: 0.5000001
|
||||||
m_Height: 2
|
m_Height: 50
|
||||||
m_Direction: 1
|
m_Direction: 1
|
||||||
m_Center: {x: 0.000000059604645, y: 0, z: -0.00000008940697}
|
m_Center: {x: 0.00000006274173, y: 7.0164957, z: -0.0000000941126}
|
||||||
--- !u!23 &1386843749
|
--- !u!23 &1386843749
|
||||||
MeshRenderer:
|
MeshRenderer:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -5156,6 +5009,68 @@ Transform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||||
|
--- !u!1001 &9074197596846296259
|
||||||
|
PrefabInstance:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Modification:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TransformParent: {fileID: 0}
|
||||||
|
m_Modifications:
|
||||||
|
- target: {fileID: 4463416604269783915, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_Name
|
||||||
|
value: Sphere
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.x
|
||||||
|
value: -13.138
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.y
|
||||||
|
value: -2.91
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalPosition.z
|
||||||
|
value: -0.63
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.w
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.x
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.y
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalRotation.z
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.x
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.y
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 5999486385027004331, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
propertyPath: m_LocalEulerAnglesHint.z
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
m_RemovedComponents:
|
||||||
|
- {fileID: 8982888368890146633, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
- {fileID: 1594777189990218189, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
m_RemovedGameObjects: []
|
||||||
|
m_AddedGameObjects: []
|
||||||
|
m_AddedComponents:
|
||||||
|
- targetCorrespondingSourceObject: {fileID: 4463416604269783915, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
|
insertIndex: -1
|
||||||
|
addedObject: {fileID: 727261133}
|
||||||
|
m_SourcePrefab: {fileID: 100100000, guid: 4c53ddc4a767a214e853d6ef8e5841a1, type: 3}
|
||||||
--- !u!1660057539 &9223372036854775807
|
--- !u!1660057539 &9223372036854775807
|
||||||
SceneRoots:
|
SceneRoots:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -5199,7 +5114,7 @@ SceneRoots:
|
|||||||
- {fileID: 1280898516}
|
- {fileID: 1280898516}
|
||||||
- {fileID: 1092836727}
|
- {fileID: 1092836727}
|
||||||
- {fileID: 1169290264}
|
- {fileID: 1169290264}
|
||||||
- {fileID: 727261130}
|
- {fileID: 9074197596846296259}
|
||||||
- {fileID: 6503154}
|
- {fileID: 6503154}
|
||||||
- {fileID: 1386843751}
|
- {fileID: 1386843751}
|
||||||
- {fileID: 1704821284}
|
- {fileID: 1704821284}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
|
using GeoSus.Client;
|
||||||
|
|
||||||
public class FlappyBirdAllInOne : MonoBehaviour, ITask
|
public class FlappyBirdAllInOne : MonoBehaviour, ITask
|
||||||
{
|
{
|
||||||
@@ -35,7 +36,7 @@ public class FlappyBirdAllInOne : MonoBehaviour, ITask
|
|||||||
public string TaskID { get; set; }
|
public string TaskID { get; set; }
|
||||||
public TaskType TaskType { get; set; }
|
public TaskType TaskType { get; set; }
|
||||||
public string TaskName { get; set; }
|
public string TaskName { get; set; }
|
||||||
public (double, double) TaskLocation { get; set; }
|
public Position TaskLocation { get; set; }
|
||||||
public bool IsCompleted { get; private set; }
|
public bool IsCompleted { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,46 +1,55 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
[RequireComponent(typeof(Rigidbody2D))]
|
[RequireComponent(typeof(Rigidbody))]
|
||||||
[RequireComponent(typeof(Collider2D))]
|
[RequireComponent(typeof(Collider))]
|
||||||
public class DraggableObject : MonoBehaviour
|
public class DraggableObject : MonoBehaviour
|
||||||
{
|
{
|
||||||
[Header("Přetahování")]
|
[Header("Přetahování")]
|
||||||
public float dragSmoothness = 15f;
|
public float dragSmoothness = 15f;
|
||||||
|
|
||||||
[Header("Vizuální zpětná vazba")]
|
[Header("Vizuální zpětná vazba")]
|
||||||
public SpriteRenderer spriteRenderer;
|
public Renderer Renderer;
|
||||||
public Color normalColor = Color.white;
|
public Color normalColor = Color.white;
|
||||||
public Color dragColor = new Color(1f, 1f, 0.5f);
|
public Color dragColor = new Color(1f, 1f, 0.5f);
|
||||||
public float scaleOnDrag = 1.15f;
|
public float scaleOnDrag = 1.15f;
|
||||||
|
|
||||||
private Rigidbody2D rb;
|
private Rigidbody rb;
|
||||||
private Camera mainCamera;
|
private Camera mainCamera;
|
||||||
private bool isDragging = false;
|
private bool isDragging = false;
|
||||||
private Vector3 targetPosition;
|
private Vector3 targetPosition;
|
||||||
private Vector3 originalScale;
|
private Vector3 originalScale;
|
||||||
private bool hasBeenScored = false;
|
private bool hasBeenScored = false;
|
||||||
|
private Plane _dragPlane;
|
||||||
|
private Collider col;
|
||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
rb = GetComponent<Rigidbody2D>();
|
rb = GetComponent<Rigidbody>();
|
||||||
|
col = GetComponent<Collider>();
|
||||||
mainCamera = Camera.main;
|
mainCamera = Camera.main;
|
||||||
originalScale = transform.localScale;
|
originalScale = transform.localScale;
|
||||||
if (spriteRenderer == null)
|
if (Renderer == null)
|
||||||
spriteRenderer = GetComponent<SpriteRenderer>();
|
Renderer = GetComponent<Renderer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
rb.gravityScale = 0f;
|
rb.useGravity = false;
|
||||||
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
|
rb.constraints = RigidbodyConstraints.FreezeRotation;
|
||||||
targetPosition = transform.position;
|
targetPosition = transform.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
HandleInput();
|
HandleInput();
|
||||||
|
}
|
||||||
|
void FixedUpdate()
|
||||||
|
{
|
||||||
if (isDragging)
|
if (isDragging)
|
||||||
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * dragSmoothness);
|
{
|
||||||
|
Vector3 newPos = Vector3.Lerp(rb.position, targetPosition, Time.fixedDeltaTime * dragSmoothness);
|
||||||
|
rb.MovePosition(newPos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleInput()
|
void HandleInput()
|
||||||
@@ -48,13 +57,13 @@ public class DraggableObject : MonoBehaviour
|
|||||||
if (Input.touchCount > 0)
|
if (Input.touchCount > 0)
|
||||||
{
|
{
|
||||||
Touch touch = Input.GetTouch(0);
|
Touch touch = Input.GetTouch(0);
|
||||||
Vector3 worldPos = mainCamera.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10f));
|
//Vector3 worldPos = mainCamera.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10f));
|
||||||
|
|
||||||
if (touch.phase == TouchPhase.Began)
|
if (touch.phase == TouchPhase.Began)
|
||||||
TryStartDrag(worldPos);
|
TryStartDrag(touch.position);
|
||||||
else if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
|
else if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
|
||||||
{
|
{
|
||||||
if (isDragging) targetPosition = worldPos;
|
if (isDragging) UpdateDrag(touch.position);
|
||||||
}
|
}
|
||||||
else if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
|
else if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
|
||||||
{
|
{
|
||||||
@@ -63,49 +72,70 @@ public class DraggableObject : MonoBehaviour
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Vector3 worldPos = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10f));
|
//Vector3 worldPos = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10f));
|
||||||
|
|
||||||
if (Input.GetMouseButtonDown(0))
|
if (Input.GetMouseButtonDown(0))
|
||||||
TryStartDrag(worldPos);
|
TryStartDrag(Input.mousePosition);
|
||||||
else if (Input.GetMouseButton(0) && isDragging)
|
else if (Input.GetMouseButton(0) && isDragging)
|
||||||
targetPosition = worldPos;
|
UpdateDrag(Input.mousePosition);
|
||||||
else if (Input.GetMouseButtonUp(0) && isDragging)
|
else if (Input.GetMouseButtonUp(0) && isDragging)
|
||||||
EndDrag();
|
EndDrag();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TryStartDrag(Vector3 worldPos)
|
void TryStartDrag(Vector3 screenPos)
|
||||||
{
|
{
|
||||||
if (GetComponent<Collider2D>().OverlapPoint(worldPos))
|
Debug.Log("Trying to start drag at: " + screenPos);
|
||||||
StartDrag(worldPos);
|
Ray ray = mainCamera.ScreenPointToRay(screenPos);
|
||||||
|
RaycastHit hit;
|
||||||
|
|
||||||
|
if (Physics.Raycast(ray, out hit))
|
||||||
|
{
|
||||||
|
if (hit.collider == col)
|
||||||
|
{
|
||||||
|
StartDrag(hit.point);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartDrag(Vector3 worldPos)
|
void StartDrag(Vector3 hitPoint)
|
||||||
{
|
{
|
||||||
|
Debug.Log("Started dragging at: " + hitPoint);
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
rb.linearVelocity = Vector2.zero;
|
rb.isKinematic = false;
|
||||||
targetPosition = worldPos;
|
rb.linearVelocity = Vector3.zero;
|
||||||
|
_dragPlane = new Plane(-mainCamera.transform.forward, hitPoint);
|
||||||
|
targetPosition = hitPoint;
|
||||||
transform.localScale = originalScale * scaleOnDrag;
|
transform.localScale = originalScale * scaleOnDrag;
|
||||||
if (spriteRenderer != null)
|
if (Renderer != null)
|
||||||
{
|
{
|
||||||
spriteRenderer.color = dragColor;
|
Renderer.material.color = dragColor;
|
||||||
spriteRenderer.sortingOrder = 10;
|
}
|
||||||
|
}
|
||||||
|
void UpdateDrag(Vector3 screenPos)
|
||||||
|
{
|
||||||
|
Ray ray = mainCamera.ScreenPointToRay(screenPos);
|
||||||
|
float distance;
|
||||||
|
if (_dragPlane.Raycast(ray, out distance))
|
||||||
|
{
|
||||||
|
targetPosition = ray.GetPoint(distance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EndDrag()
|
void EndDrag()
|
||||||
{
|
{
|
||||||
|
Debug.Log("Ended dragging");
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
|
rb.isKinematic = true;
|
||||||
transform.localScale = originalScale;
|
transform.localScale = originalScale;
|
||||||
if (spriteRenderer != null)
|
if (Renderer != null)
|
||||||
{
|
{
|
||||||
spriteRenderer.color = normalColor;
|
Renderer.material.color = normalColor;
|
||||||
spriteRenderer.sortingOrder = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnScored()
|
public void OnScored()
|
||||||
{
|
{
|
||||||
|
Debug.Log("Object scored");
|
||||||
if (hasBeenScored) return;
|
if (hasBeenScored) return;
|
||||||
hasBeenScored = true;
|
hasBeenScored = true;
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
@@ -123,8 +153,8 @@ public class DraggableObject : MonoBehaviour
|
|||||||
elapsed += Time.deltaTime;
|
elapsed += Time.deltaTime;
|
||||||
float t = elapsed / duration;
|
float t = elapsed / duration;
|
||||||
transform.localScale = Vector3.Lerp(startScale, Vector3.zero, t);
|
transform.localScale = Vector3.Lerp(startScale, Vector3.zero, t);
|
||||||
if (spriteRenderer != null)
|
if (Renderer != null)
|
||||||
spriteRenderer.color = new Color(normalColor.r, normalColor.g, normalColor.b, 1f - t);
|
Renderer.material.color = new Color(normalColor.r, normalColor.g, normalColor.b, 1f - t);
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using UnityEngine;
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(CapsuleCollider))]
|
||||||
public class Hole : MonoBehaviour
|
public class Hole : MonoBehaviour
|
||||||
{
|
{
|
||||||
[Header("Nastavení")]
|
[Header("Nastavení")]
|
||||||
@@ -10,13 +11,9 @@ public class Hole : MonoBehaviour
|
|||||||
[Tooltip("Síla vtahování itemu k díře")]
|
[Tooltip("Síla vtahování itemu k díře")]
|
||||||
public float pullForce = 4f;
|
public float pullForce = 4f;
|
||||||
|
|
||||||
[Header("Pohyb díry (volitelné)")]
|
|
||||||
public bool hasMovement = false;
|
|
||||||
public float moveSpeed = 2f;
|
|
||||||
public Vector2 moveRange = new Vector2(1.5f, 0f);
|
|
||||||
|
|
||||||
[Header("Vizuál")]
|
[Header("Vizuál")]
|
||||||
public SpriteRenderer glowRenderer;
|
public Renderer glowRenderer;
|
||||||
|
|
||||||
private Vector3 startPosition;
|
private Vector3 startPosition;
|
||||||
private bool isGlowing = false;
|
private bool isGlowing = false;
|
||||||
@@ -35,27 +32,24 @@ public class Hole : MonoBehaviour
|
|||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
if (hasMovement)
|
|
||||||
{
|
|
||||||
float x = startPosition.x + Mathf.Sin(Time.time * moveSpeed) * moveRange.x;
|
|
||||||
float y = startPosition.y + Mathf.Cos(Time.time * moveSpeed * 0.7f) * moveRange.y;
|
|
||||||
transform.position = new Vector3(x, y, transform.position.z);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnTriggerStay2D(Collider2D other)
|
void OnTriggerStay(Collider other)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"Trigger stay with: {other.gameObject.name}");
|
||||||
DraggableObject draggable = other.GetComponent<DraggableObject>();
|
DraggableObject draggable = other.GetComponent<DraggableObject>();
|
||||||
if (draggable == null) return;
|
if (draggable == null) return;
|
||||||
|
Debug.Log($"Draggable object detected: {other.gameObject.name}");
|
||||||
float dist = Vector2.Distance(transform.position, other.transform.position);
|
float dist = Vector3.Distance(transform.position, other.transform.position);
|
||||||
|
|
||||||
|
|
||||||
Rigidbody2D rb = other.GetComponent<Rigidbody2D>();
|
Rigidbody rb = other.GetComponent<Rigidbody>();
|
||||||
if (rb != null)
|
if (rb != null)
|
||||||
{
|
{
|
||||||
Vector2 dir = ((Vector2)transform.position - rb.position).normalized;
|
Debug.Log($"Rigidbody detected: {other.gameObject.name}, distance: {dist}");
|
||||||
rb.AddForce(dir * pullForce * Time.fixedDeltaTime, ForceMode2D.Impulse);
|
Vector3 dir = (transform.position - rb.position).normalized;
|
||||||
|
rb.AddForce(dir * pullForce * Time.fixedDeltaTime, ForceMode.Impulse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -66,18 +60,21 @@ public class Hole : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnTriggerEnter2D(Collider2D other)
|
void OnTriggerEnter(Collider other)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"Collider entered: {other.gameObject.name}");
|
||||||
if (other.GetComponent<DraggableObject>() != null) SetGlow(true);
|
if (other.GetComponent<DraggableObject>() != null) SetGlow(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnTriggerExit2D(Collider2D other)
|
void OnTriggerExit(Collider other)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"Collider exited: {other.gameObject.name}");
|
||||||
if (other.GetComponent<DraggableObject>() != null) SetGlow(false);
|
if (other.GetComponent<DraggableObject>() != null) SetGlow(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetGlow(bool active)
|
void SetGlow(bool active)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"SetGlow: {active}");
|
||||||
isGlowing = active;
|
isGlowing = active;
|
||||||
if (glowRenderer == null) return;
|
if (glowRenderer == null) return;
|
||||||
glowRenderer.enabled = active;
|
glowRenderer.enabled = active;
|
||||||
@@ -89,8 +86,8 @@ public class Hole : MonoBehaviour
|
|||||||
while (isGlowing && glowRenderer != null)
|
while (isGlowing && glowRenderer != null)
|
||||||
{
|
{
|
||||||
float t = Mathf.PingPong(Time.time * 3f, 1f);
|
float t = Mathf.PingPong(Time.time * 3f, 1f);
|
||||||
Color c = glowRenderer.color;
|
Color c = glowRenderer.sharedMaterial.color;
|
||||||
glowRenderer.color = new Color(c.r, c.g, c.b, Mathf.Lerp(0.3f, 0.9f, t));
|
glowRenderer.sharedMaterial.color = new Color(c.r, c.g, c.b, Mathf.Lerp(0.3f, 0.9f, t));
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Events;
|
using UnityEngine.Events;
|
||||||
|
using GeoSus.Client;
|
||||||
|
|
||||||
public class LevelManager : MonoBehaviour
|
public class LevelManager : MonoBehaviour, ITask
|
||||||
{
|
{
|
||||||
public static LevelManager Instance;
|
public static LevelManager Instance;
|
||||||
|
|
||||||
@@ -14,11 +16,15 @@ public class LevelManager : MonoBehaviour
|
|||||||
|
|
||||||
private int scoredCount = 0;
|
private int scoredCount = 0;
|
||||||
|
|
||||||
void Awake()
|
public string TaskID { get; set; }
|
||||||
{
|
|
||||||
if (Instance == null) Instance = this;
|
public TaskType TaskType { get; set; }
|
||||||
else Destroy(gameObject);
|
public string TaskName { get; set; }
|
||||||
}
|
|
||||||
|
public Position TaskLocation { get; set; }
|
||||||
|
|
||||||
|
public bool IsCompleted { get; private set; } = false;
|
||||||
|
protected Action<ITask> OnCompleted;
|
||||||
|
|
||||||
public void RegisterItem()
|
public void RegisterItem()
|
||||||
{
|
{
|
||||||
@@ -38,4 +44,25 @@ public class LevelManager : MonoBehaviour
|
|||||||
|
|
||||||
public int GetScoredCount() => scoredCount;
|
public int GetScoredCount() => scoredCount;
|
||||||
public int GetTotalCount() => itemsToScore;
|
public int GetTotalCount() => itemsToScore;
|
||||||
|
|
||||||
|
public void Initialize(Action<ITask> onCompleted)
|
||||||
|
{
|
||||||
|
OnCompleted = onCompleted;
|
||||||
|
|
||||||
|
IsCompleted = false;
|
||||||
|
ResetCounter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExitTask(Action<ITask> onExit)
|
||||||
|
{
|
||||||
|
onExit?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Complete()
|
||||||
|
{
|
||||||
|
if (IsCompleted) return;
|
||||||
|
|
||||||
|
IsCompleted = true;
|
||||||
|
OnCompleted?.Invoke(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ using System.Collections.Generic;
|
|||||||
public class ObjectSpawner : MonoBehaviour
|
public class ObjectSpawner : MonoBehaviour
|
||||||
{
|
{
|
||||||
public static ObjectSpawner Instance;
|
public static ObjectSpawner Instance;
|
||||||
|
[SerializeField]
|
||||||
|
private List<GameObject> spawnedHoles = new List<GameObject>();
|
||||||
|
|
||||||
[Header("Prefaby")]
|
[Header("Prefaby")]
|
||||||
public GameObject[] objectPrefabs;
|
public GameObject[] objectPrefabs;
|
||||||
public GameObject holePrefab;
|
|
||||||
|
|
||||||
[Header("Počty")]
|
[Header("Počty")]
|
||||||
[Tooltip("Kolik předmětů spawnovat")]
|
[Tooltip("Kolik předmětů spawnovat")]
|
||||||
@@ -28,12 +29,9 @@ public class ObjectSpawner : MonoBehaviour
|
|||||||
public float minY = -5f;
|
public float minY = -5f;
|
||||||
public float maxY = 4f;
|
public float maxY = 4f;
|
||||||
|
|
||||||
[Header("Rodiče pro přehlednost (volitelné)")]
|
|
||||||
public Transform objectParent;
|
|
||||||
public Transform holeParent;
|
|
||||||
|
|
||||||
private List<GameObject> spawnedObjects = new List<GameObject>();
|
private List<GameObject> spawnedObjects = new List<GameObject>();
|
||||||
private List<GameObject> spawnedHoles = new List<GameObject>();
|
|
||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
@@ -57,41 +55,42 @@ public class ObjectSpawner : MonoBehaviour
|
|||||||
LevelManager.Instance.ResetCounter();
|
LevelManager.Instance.ResetCounter();
|
||||||
}
|
}
|
||||||
|
|
||||||
SpawnHoles();
|
//SpawnHoles();
|
||||||
SpawnObjects();
|
SpawnObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpawnHoles()
|
//void SpawnHoles()
|
||||||
{
|
//{
|
||||||
for (int i = 0; i < holeCount; i++)
|
// for (int i = 0; i < holeCount; i++)
|
||||||
{
|
// {
|
||||||
Vector2 pos = RandomPos(1f);
|
// Vector2 pos = RandomPos(1f);
|
||||||
GameObject hole = Instantiate(holePrefab, pos, Quaternion.identity, holeParent);
|
// GameObject hole = Instantiate(holePrefab, pos, Quaternion.identity, holeParent);
|
||||||
|
|
||||||
Hole h = hole.GetComponent<Hole>();
|
// Hole h = hole.GetComponent<Hole>();
|
||||||
if (h != null && holesMove)
|
// if (h != null && holesMove)
|
||||||
{
|
// {
|
||||||
h.hasMovement = true;
|
// h.hasMovement = true;
|
||||||
h.moveSpeed = holeMoveSpeed;
|
// h.moveSpeed = holeMoveSpeed;
|
||||||
h.moveRange = new Vector2(Random.Range(0.8f, 1.8f), 0f);
|
// h.moveRange = new Vector2(Random.Range(0.8f, 1.8f), 0f);
|
||||||
}
|
// }
|
||||||
|
|
||||||
spawnedHoles.Add(hole);
|
// spawnedHoles.Add(hole);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
void SpawnObjects()
|
public void SpawnObjects()
|
||||||
{
|
{
|
||||||
|
Debug.Log($"Spawning {objectCount} objects...");
|
||||||
for (int i = 0; i < objectCount; i++)
|
for (int i = 0; i < objectCount; i++)
|
||||||
{
|
{
|
||||||
GameObject prefab = objectPrefabs[Random.Range(0, objectPrefabs.Length)];
|
GameObject prefab = objectPrefabs[Random.Range(0, objectPrefabs.Length)];
|
||||||
Vector2 pos = RandomPos(0.5f);
|
Vector3 pos = RandomPos(0.5f);
|
||||||
GameObject obj = Instantiate(prefab, pos, Quaternion.identity, objectParent);
|
GameObject obj = Instantiate(prefab, pos, Quaternion.identity);
|
||||||
|
|
||||||
// Náhodná barva
|
// Náhodná barva
|
||||||
SpriteRenderer sr = obj.GetComponent<SpriteRenderer>();
|
Renderer sr = obj.GetComponent<Renderer>();
|
||||||
if (sr != null)
|
if (sr != null)
|
||||||
sr.color = Random.ColorHSV(0f, 1f, 0.7f, 1f, 0.9f, 1f);
|
sr.sharedMaterial.color = Random.ColorHSV(0f, 1f, 0.7f, 1f, 0.9f, 1f);
|
||||||
|
|
||||||
spawnedObjects.Add(obj);
|
spawnedObjects.Add(obj);
|
||||||
}
|
}
|
||||||
@@ -100,14 +99,17 @@ public class ObjectSpawner : MonoBehaviour
|
|||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
foreach (var o in spawnedObjects) if (o != null) Destroy(o);
|
foreach (var o in spawnedObjects) if (o != null) Destroy(o);
|
||||||
foreach (var h in spawnedHoles) if (h != null) Destroy(h);
|
//foreach (var h in spawnedHoles) if (h != null) Destroy(h);
|
||||||
spawnedObjects.Clear();
|
//spawnedObjects.Clear();
|
||||||
spawnedHoles.Clear();
|
spawnedHoles.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 RandomPos(float margin) =>
|
Vector3 RandomPos(float margin)
|
||||||
new Vector2(
|
{
|
||||||
|
return new Vector3(
|
||||||
Random.Range(minX + margin, maxX - margin),
|
Random.Range(minX + margin, maxX - margin),
|
||||||
Random.Range(minY + margin, maxY - margin)
|
Random.Range(minY + margin, maxY - margin),
|
||||||
|
0.5f
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using UnityEngine;
|
|||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
using GeoSus.Client;
|
||||||
|
|
||||||
public class DraggableKey : MonoBehaviour,
|
public class DraggableKey : MonoBehaviour,
|
||||||
IBeginDragHandler, IDragHandler, IEndDragHandler, ITask
|
IBeginDragHandler, IDragHandler, IEndDragHandler, ITask
|
||||||
@@ -27,7 +28,7 @@ public class DraggableKey : MonoBehaviour,
|
|||||||
public string TaskID { get; set; }
|
public string TaskID { get; set; }
|
||||||
public TaskType TaskType { get; set; }
|
public TaskType TaskType { get; set; }
|
||||||
public string TaskName { get; set; }
|
public string TaskName { get; set; }
|
||||||
public (double, double) TaskLocation { get; set; }
|
public Position TaskLocation { get; set; }
|
||||||
public bool IsCompleted { get; private set; }
|
public bool IsCompleted { get; private set; }
|
||||||
|
|
||||||
private Action<ITask> _onCompleted;
|
private Action<ITask> _onCompleted;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
|
using GeoSus.Client;
|
||||||
|
|
||||||
public class CableMiniGame : MonoBehaviour, ITask
|
public class CableMiniGame : MonoBehaviour, ITask
|
||||||
{
|
{
|
||||||
@@ -21,7 +22,7 @@ public class CableMiniGame : MonoBehaviour, ITask
|
|||||||
public string TaskID { get; set; }
|
public string TaskID { get; set; }
|
||||||
public TaskType TaskType { get; set; }
|
public TaskType TaskType { get; set; }
|
||||||
public string TaskName { get; set; }
|
public string TaskName { get; set; }
|
||||||
public (double, double) TaskLocation { get; set; }
|
public Position TaskLocation { get; set; }
|
||||||
public bool IsCompleted { get; private set; }
|
public bool IsCompleted { get; private set; }
|
||||||
|
|
||||||
private Action<ITask> _onCompleted;
|
private Action<ITask> _onCompleted;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
using UnityEngine.UI;
|
using GeoSus.Client;
|
||||||
|
|
||||||
public class WindController : MonoBehaviour, ITask
|
public class WindController : MonoBehaviour, ITask
|
||||||
{
|
{
|
||||||
@@ -24,7 +24,7 @@ public class WindController : MonoBehaviour, ITask
|
|||||||
public string TaskID { get; set; }
|
public string TaskID { get; set; }
|
||||||
public TaskType TaskType { get; set; }
|
public TaskType TaskType { get; set; }
|
||||||
public string TaskName { get; set; }
|
public string TaskName { get; set; }
|
||||||
public (double, double) TaskLocation { get; set; }
|
public Position TaskLocation { get; set; }
|
||||||
public bool IsCompleted { get; private set; }
|
public bool IsCompleted { get; private set; }
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ Material:
|
|||||||
- _UVSec: 0
|
- _UVSec: 0
|
||||||
- _ZWrite: 1
|
- _ZWrite: 1
|
||||||
m_Colors:
|
m_Colors:
|
||||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
- _Color: {r: 0.12311344, g: 0.96881944, b: 0.7101699, a: 1}
|
||||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
m_BuildTextureStacks: []
|
m_BuildTextureStacks: []
|
||||||
m_AllowLocking: 1
|
m_AllowLocking: 1
|
||||||
|
|||||||
161
Assets/minigame/Sphere.prefab
Normal file
161
Assets/minigame/Sphere.prefab
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &4463416604269783915
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 5999486385027004331}
|
||||||
|
- component: {fileID: 3096372749762576219}
|
||||||
|
- component: {fileID: 1253701241776310731}
|
||||||
|
- component: {fileID: 7907572649200639552}
|
||||||
|
- component: {fileID: 4926246710868963563}
|
||||||
|
- component: {fileID: 4731565560732594675}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Sphere
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &5999486385027004331
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 4463416604269783915}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: -13.138, y: -2.91, z: -0.63}
|
||||||
|
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!33 &3096372749762576219
|
||||||
|
MeshFilter:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 4463416604269783915}
|
||||||
|
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
|
--- !u!23 &1253701241776310731
|
||||||
|
MeshRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 4463416604269783915}
|
||||||
|
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: 1a424fd36745ea14682570c13681319e, 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!114 &7907572649200639552
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 4463416604269783915}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: fb5157d7cd78450439c40cd6f5afe6ac, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::DraggableObject
|
||||||
|
dragSmoothness: 15
|
||||||
|
Renderer: {fileID: 0}
|
||||||
|
normalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
dragColor: {r: 1, g: 1, b: 0.5, a: 1}
|
||||||
|
scaleOnDrag: 1.15
|
||||||
|
--- !u!54 &4926246710868963563
|
||||||
|
Rigidbody:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 4463416604269783915}
|
||||||
|
serializedVersion: 5
|
||||||
|
m_Mass: 1
|
||||||
|
m_LinearDamping: 0
|
||||||
|
m_AngularDamping: 0.05
|
||||||
|
m_CenterOfMass: {x: 0, y: 0, z: 0}
|
||||||
|
m_InertiaTensor: {x: 1, y: 1, z: 1}
|
||||||
|
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ImplicitCom: 1
|
||||||
|
m_ImplicitTensor: 1
|
||||||
|
m_UseGravity: 1
|
||||||
|
m_IsKinematic: 0
|
||||||
|
m_Interpolate: 0
|
||||||
|
m_Constraints: 0
|
||||||
|
m_CollisionDetection: 0
|
||||||
|
--- !u!136 &4731565560732594675
|
||||||
|
CapsuleCollider:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 4463416604269783915}
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_LayerOverridePriority: 0
|
||||||
|
m_IsTrigger: 0
|
||||||
|
m_ProvidesContacts: 0
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Radius: 0.5
|
||||||
|
m_Height: 1
|
||||||
|
m_Direction: 1
|
||||||
|
m_Center: {x: 0, y: 0, z: 0}
|
||||||
7
Assets/minigame/Sphere.prefab.meta
Normal file
7
Assets/minigame/Sphere.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4c53ddc4a767a214e853d6ef8e5841a1
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Reference in New Issue
Block a user