Compare commits
13
Commits
5d2eb3aaea
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ac8f92006 | ||
|
|
78592e8d8e | ||
|
|
74d9fae6e6 | ||
|
|
9970488938 | ||
|
|
c2f4c2f808 | ||
|
|
4b2129b944 | ||
|
|
2de5a166f6 | ||
|
|
2651068b50 | ||
|
|
d62bf9e06b | ||
|
|
0a1d3c7262 | ||
|
|
f3e01462c1 | ||
|
|
87301a228f | ||
|
|
aa924c71dc |
Vendored
+3
-3
@@ -60,11 +60,11 @@
|
|||||||
"*.asset": "yaml",
|
"*.asset": "yaml",
|
||||||
"*.meta": "yaml",
|
"*.meta": "yaml",
|
||||||
"*.prefab": "yaml",
|
"*.prefab": "yaml",
|
||||||
"*.unity": "yaml",
|
"*.unity": "yaml"
|
||||||
},
|
},
|
||||||
"explorer.fileNesting.enabled": true,
|
"explorer.fileNesting.enabled": true,
|
||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"*.sln": "*.csproj",
|
"*.sln": "*.csproj"
|
||||||
},
|
},
|
||||||
"dotnet.defaultSolution": "WalkInvest_Soccer.sln"
|
"dotnet.defaultSolution": "soccar2d.sln"
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,8 @@ public class CupidConnector : MonoBehaviour
|
|||||||
//Server code
|
//Server code
|
||||||
string[] args = Environment.GetCommandLineArgs();
|
string[] args = Environment.GetCommandLineArgs();
|
||||||
int matchId = 0;
|
int matchId = 0;
|
||||||
string dedicatedSecret = "38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328";
|
string dedicatedSecret = null;
|
||||||
|
bool dedicatedSecretFromCli = false;
|
||||||
string internalApiBase = null;
|
string internalApiBase = null;
|
||||||
for (int i = 0; i < args.Length; i++)
|
for (int i = 0; i < args.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -28,12 +29,21 @@ public class CupidConnector : MonoBehaviour
|
|||||||
|| string.Equals(args[i], "-matchid", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(args[i], "-matchid", StringComparison.OrdinalIgnoreCase))
|
||||||
int.TryParse(args[i + 1], out matchId);
|
int.TryParse(args[i + 1], out matchId);
|
||||||
else if (string.Equals(args[i], "-dedicatedSecret", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(args[i], "-dedicatedSecret", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
dedicatedSecret = args[i + 1];
|
dedicatedSecret = args[i + 1];
|
||||||
|
dedicatedSecretFromCli = true;
|
||||||
|
}
|
||||||
else if (string.Equals(args[i], "-internalApiBase", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(args[i], "-internalApiBase", StringComparison.OrdinalIgnoreCase))
|
||||||
internalApiBase = args[i + 1];
|
internalApiBase = args[i + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GameManager.ConfigureDedicatedMatchReporting(matchId);
|
if (!dedicatedSecretFromCli)
|
||||||
|
{
|
||||||
|
string envSecret = Environment.GetEnvironmentVariable("DEDICATED_SERVER_SECRET");
|
||||||
|
if (!string.IsNullOrEmpty(envSecret))
|
||||||
|
dedicatedSecret = envSecret;
|
||||||
|
}
|
||||||
|
GameManager.ConfigureDedicatedMatchReporting(matchId, dedicatedSecret, internalApiBase);
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
Logger.Log("Editor mode, skipping arg check");
|
Logger.Log("Editor mode, skipping arg check");
|
||||||
Cupid.RoomPort = 7777;
|
Cupid.RoomPort = 7777;
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ public static class Cupid
|
|||||||
|
|
||||||
public static int RoomPort = -1;
|
public static int RoomPort = -1;
|
||||||
|
|
||||||
|
/// <summary>Clears auth/matchmaker session fields. Server address is left so the next login can <see cref="Init"/> again.</summary>
|
||||||
|
public static void ClearSession()
|
||||||
|
{
|
||||||
|
bearerToken = "";
|
||||||
|
RoomPort = -1;
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task Init(string _serverAddress, int _port, string _bearerToken, string _settingsPassword)
|
public static async Task Init(string _serverAddress, int _port, string _bearerToken, string _settingsPassword)
|
||||||
{
|
{
|
||||||
@@ -264,12 +270,32 @@ public class CupidSettings
|
|||||||
public int waiting_time;
|
public int waiting_time;
|
||||||
public int port_range_min;
|
public int port_range_min;
|
||||||
public int port_range_max;
|
public int port_range_max;
|
||||||
|
public CupidGameEntry[] games;
|
||||||
/// <summary>RC stake per player for the default match (<c>/settings</c>), in coin units (same as matchmaker <c>entry_fee</c>).</summary>
|
/// <summary>RC stake per player for the default match (<c>/settings</c>), in coin units (same as matchmaker <c>entry_fee</c>).</summary>
|
||||||
public int entry_fee;
|
public int entry_fee;
|
||||||
/// <summary>Optional side-bet or table minimum from matchmaker settings; same units as <see cref="entry_fee"/>.</summary>
|
/// <summary>Optional side-bet or table minimum from matchmaker settings; same units as <see cref="entry_fee"/>.</summary>
|
||||||
public int bet_fee;
|
public int bet_fee;
|
||||||
/// <summary>Winner net RC prize from settings, coin units (same as room <c>rc_prize</c>).</summary>
|
/// <summary>Winner net RC prize from settings, coin units (same as room <c>rc_prize</c>).</summary>
|
||||||
public int rc_prize;
|
public int rc_prize;
|
||||||
|
public CupidTableSettings table_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
public class CupidGameEntry
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public string exe;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
public class CupidTableSettings
|
||||||
|
{
|
||||||
|
public string entry_fee;
|
||||||
|
public string bet_fee;
|
||||||
|
/// <summary>Minimum client version (e.g. <c>0.99</c>, <c>1.21</c>). Compared to <see cref="Application.version"/>.</summary>
|
||||||
|
public string version;
|
||||||
|
public string error_msg;
|
||||||
|
public string warning_msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
|
|||||||
@@ -51,6 +51,12 @@ public class CupidLobby : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static CupidLobby instance;
|
public static CupidLobby instance;
|
||||||
|
|
||||||
|
public static void ClearUsername()
|
||||||
|
{
|
||||||
|
m_username = "";
|
||||||
|
}
|
||||||
|
|
||||||
void Awake(){
|
void Awake(){
|
||||||
instance=this;
|
instance=this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ public class Logger
|
|||||||
{
|
{
|
||||||
public static bool Enabled = true;
|
public static bool Enabled = true;
|
||||||
private static Logger m_instance = null;
|
private static Logger m_instance = null;
|
||||||
|
private static readonly object FileLock = new object();
|
||||||
|
|
||||||
private static string ApplicationDirectory
|
private static string ApplicationDirectory
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -18,6 +20,12 @@ public class Logger
|
|||||||
}
|
}
|
||||||
|
|
||||||
string path = Application.dataPath;
|
string path = Application.dataPath;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
// Never write under Assets/: the Editor importer locks those files, and
|
||||||
|
// File.AppendAllText then throws Sharing violation. Mirror treats that as a
|
||||||
|
// fatal RPC error and disconnects the client.
|
||||||
|
path = Path.GetFullPath(Path.Combine(path, ".."));
|
||||||
|
#else
|
||||||
if (Application.platform == RuntimePlatform.OSXPlayer)
|
if (Application.platform == RuntimePlatform.OSXPlayer)
|
||||||
{
|
{
|
||||||
path = Path.Combine(path, "..", "..");
|
path = Path.Combine(path, "..", "..");
|
||||||
@@ -26,6 +34,7 @@ public class Logger
|
|||||||
{
|
{
|
||||||
path = Path.Combine(path, "..");
|
path = Path.Combine(path, "..");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,14 +59,14 @@ public class Logger
|
|||||||
if(LogFilePath == null){
|
if(LogFilePath == null){
|
||||||
LogFilePath = Path.Combine(ApplicationDirectory, "Log.txt");
|
LogFilePath = Path.Combine(ApplicationDirectory, "Log.txt");
|
||||||
}
|
}
|
||||||
File.WriteAllText(LogFilePath, "Logger initiated at " + DateTime.Now + "\n\n");
|
TryWriteFile(LogFilePath, "Logger initiated at " + DateTime.Now + "\n\n", append: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void log(string message){
|
public void log(string message){
|
||||||
if(!Enabled){return;}
|
if(!Enabled){return;}
|
||||||
|
|
||||||
File.AppendAllText(LogFilePath,$"[{DateTime.Now}] {message}\n");
|
|
||||||
Debug.Log(message);
|
Debug.Log(message);
|
||||||
|
TryWriteFile(LogFilePath, $"[{DateTime.Now}] {message}\n", append: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Log(string message){
|
public static void Log(string message){
|
||||||
@@ -75,7 +84,55 @@ public class Logger
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Writable Logs folder next to match log files (creates if missing).</summary>
|
||||||
|
public static string GetLogsDirectory()
|
||||||
|
{
|
||||||
|
string dir = Path.Combine(ApplicationDirectory, "Logs");
|
||||||
|
EnsureDirectoryExists(dir);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
public static void SetFilePath(string path){
|
public static void SetFilePath(string path){
|
||||||
instance.LogFilePath= path;
|
instance.LogFilePath= path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// File I/O must never throw: callers include Mirror RPCs, and an exception there
|
||||||
|
/// disconnects the client. Sharing violations are expected if another process has
|
||||||
|
/// the file open; FileShare.ReadWrite plus a swallow keeps gameplay alive.
|
||||||
|
/// </summary>
|
||||||
|
static void TryWriteFile(string path, string contents, bool append)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path) || contents == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string dir = Path.GetDirectoryName(path);
|
||||||
|
if (!string.IsNullOrEmpty(dir))
|
||||||
|
EnsureDirectoryExists(dir);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Logger: could not create log directory: " + e.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (FileLock)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileMode mode = append ? FileMode.Append : FileMode.Create;
|
||||||
|
using (var stream = new FileStream(path, mode, FileAccess.Write, FileShare.ReadWrite))
|
||||||
|
using (var writer = new StreamWriter(stream))
|
||||||
|
{
|
||||||
|
writer.Write(contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Logger: file write failed (" + path + "): " + e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -777,6 +777,66 @@ MonoBehaviour:
|
|||||||
m_XAdvance: 0
|
m_XAdvance: 0
|
||||||
m_YAdvance: 0
|
m_YAdvance: 0
|
||||||
m_FeatureLookupFlags: 1504773
|
m_FeatureLookupFlags: 1504773
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 10
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -5.8500004
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 5
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 10
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -1.08
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 14
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 10
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -5.8500004
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 31
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 10
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -1.08
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 40
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
- m_FirstAdjustmentRecord:
|
- m_FirstAdjustmentRecord:
|
||||||
m_GlyphIndex: 12
|
m_GlyphIndex: 12
|
||||||
m_GlyphValueRecord:
|
m_GlyphValueRecord:
|
||||||
@@ -1647,6 +1707,456 @@ MonoBehaviour:
|
|||||||
m_XAdvance: 0
|
m_XAdvance: 0
|
||||||
m_YAdvance: 0
|
m_YAdvance: 0
|
||||||
m_FeatureLookupFlags: 10188005
|
m_FeatureLookupFlags: 10188005
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -2.97
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 5
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 6
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 8
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 9
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 10
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 12
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 13
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 15
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 16
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 17
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 18
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 20
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 22
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -1.26
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 23
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.99
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 30
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -2.97
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 31
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 32
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 34
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 35
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 36
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 38
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 39
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 41
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 42
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 43
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 44
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 46
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.45000002
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 48
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -1.26
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 49
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 25
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -0.99
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 56
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 1016088
|
||||||
- m_FirstAdjustmentRecord:
|
- m_FirstAdjustmentRecord:
|
||||||
m_GlyphIndex: 26
|
m_GlyphIndex: 26
|
||||||
m_GlyphValueRecord:
|
m_GlyphValueRecord:
|
||||||
@@ -3687,6 +4197,66 @@ MonoBehaviour:
|
|||||||
m_XAdvance: 0
|
m_XAdvance: 0
|
||||||
m_YAdvance: 0
|
m_YAdvance: 0
|
||||||
m_FeatureLookupFlags: 10188005
|
m_FeatureLookupFlags: 10188005
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 36
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -5.8500004
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 5
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 36
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -1.08
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 14
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 36
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -5.8500004
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 31
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 36
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -1.08
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 40
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
- m_FirstAdjustmentRecord:
|
- m_FirstAdjustmentRecord:
|
||||||
m_GlyphIndex: 38
|
m_GlyphIndex: 38
|
||||||
m_GlyphValueRecord:
|
m_GlyphValueRecord:
|
||||||
@@ -6447,6 +7017,216 @@ MonoBehaviour:
|
|||||||
m_XAdvance: 0
|
m_XAdvance: 0
|
||||||
m_YAdvance: 0
|
m_YAdvance: 0
|
||||||
m_FeatureLookupFlags: 10188005
|
m_FeatureLookupFlags: 10188005
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -6.6600003
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 5
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -4.23
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 7
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -4.23
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 11
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -5.8500004
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 14
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -3.42
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 19
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -3.42
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 21
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -3.6000001
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 23
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -6.6600003
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 31
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -4.23
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 33
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -4.23
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 37
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -5.8500004
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 40
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -3.42
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 45
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -3.42
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 47
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 55
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -3.6000001
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 49
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 11484403
|
||||||
- m_FirstAdjustmentRecord:
|
- m_FirstAdjustmentRecord:
|
||||||
m_GlyphIndex: 56
|
m_GlyphIndex: 56
|
||||||
m_GlyphValueRecord:
|
m_GlyphValueRecord:
|
||||||
|
|||||||
@@ -777,6 +777,66 @@ MonoBehaviour:
|
|||||||
m_XAdvance: 0
|
m_XAdvance: 0
|
||||||
m_YAdvance: 0
|
m_YAdvance: 0
|
||||||
m_FeatureLookupFlags: 1118827
|
m_FeatureLookupFlags: 1118827
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 10
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -5.8500004
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 5
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 10
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -1.08
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 14
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 10
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -5.8500004
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 31
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
|
- m_FirstAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 10
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: -1.08
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_SecondAdjustmentRecord:
|
||||||
|
m_GlyphIndex: 40
|
||||||
|
m_GlyphValueRecord:
|
||||||
|
m_XPlacement: 0
|
||||||
|
m_YPlacement: 0
|
||||||
|
m_XAdvance: 0
|
||||||
|
m_YAdvance: 0
|
||||||
|
m_FeatureLookupFlags: 2038298
|
||||||
- m_FirstAdjustmentRecord:
|
- m_FirstAdjustmentRecord:
|
||||||
m_GlyphIndex: 12
|
m_GlyphIndex: 12
|
||||||
m_GlyphValueRecord:
|
m_GlyphValueRecord:
|
||||||
|
|||||||
+33
-24
@@ -1,25 +1,34 @@
|
|||||||
Logger initiated at 5/16/2026 11:47:02 PM
|
Logger initiated at 8/27/2026 1:43:47 PM
|
||||||
|
|
||||||
[5/16/2026 11:47:02 PM] Cupid settings updated
|
[8/27/2026 1:43:47 PM] Cupid settings updated
|
||||||
[5/16/2026 11:47:02 PM] Cupid settings updated
|
[8/27/2026 1:43:48 PM] Cupid settings updated
|
||||||
[5/16/2026 11:47:10 PM] Starting matchmake as warlock
|
[8/27/2026 1:43:50 PM] Starting matchmake as warlock
|
||||||
[5/16/2026 11:47:13 PM] Got into a room
|
[8/27/2026 1:43:51 PM] Starting matchmake as warlock2
|
||||||
[5/16/2026 11:47:13 PM] {"ok":true,"entry_fee":21,"rc_prize":34,"for_red":{"Players":[{"Name":"warlock2","LastSeen":1778955432808,"UserId":3,"l10_wins":1,"l10_losses":3},{"Name":"warlock","LastSeen":1778955433214,"UserId":2,"l10_wins":3,"l10_losses":1}],"GameName":"soccar","Port":26614,"InitTime":1778955432808,"entry_fee":21,"match_id":32,"instance_started":true,"rc_prize":34,"your_team":"red"},"for_blue":{"Players":[{"Name":"warlock2","LastSeen":1778955432808,"UserId":3,"l10_wins":1,"l10_losses":3},{"Name":"warlock","LastSeen":1778955433214,"UserId":2,"l10_wins":3,"l10_losses":1}],"GameName":"soccar","Port":26614,"InitTime":1778955432808,"entry_fee":21,"match_id":32,"instance_started":true,"rc_prize":34,"your_team":"blue"}}
|
[8/27/2026 1:43:53 PM] Still waiting: {"ok":true,"entry_fee":21,"rc_prize":34,"for_red":{"Players":[{"Name":"warlock","LastSeen":1787818433058,"UserId":2,"l10_wins":1,"l10_losses":8}],"GameName":"soccar","Port":26357,"InitTime":1787818433058,"entry_fee":21,"match_id":595,"rc_prize":34,"your_team":"red"},"for_blue":null}
|
||||||
[5/16/2026 11:47:13 PM] Configured dedicated match reporting for match id 32 with secret 38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328 and internal api base https://kickkingsapi.playpoolstudios.com
|
[8/27/2026 1:43:53 PM] Got into a room
|
||||||
[5/16/2026 11:47:13 PM] Setting cupid to load game scene
|
[8/27/2026 1:43:53 PM] {"ok":true,"entry_fee":21,"rc_prize":34,"for_red":{"Players":[{"Name":"warlock","LastSeen":1787818433058,"UserId":2,"l10_wins":1,"l10_losses":8},{"Name":"warlock2","LastSeen":1787818433141,"UserId":3,"l10_wins":8,"l10_losses":2}],"GameName":"soccar","Port":26357,"InitTime":1787818433058,"entry_fee":21,"match_id":595,"instance_started":true,"rc_prize":34,"your_team":"red"},"for_blue":{"Players":[{"Name":"warlock","LastSeen":1787818433058,"UserId":2,"l10_wins":1,"l10_losses":8},{"Name":"warlock2","LastSeen":1787818433141,"UserId":3,"l10_wins":8,"l10_losses":2}],"GameName":"soccar","Port":26357,"InitTime":1787818433058,"entry_fee":21,"match_id":595,"instance_started":true,"rc_prize":34,"your_team":"blue"}}
|
||||||
[5/16/2026 11:47:13 PM] Loading game scene
|
[8/27/2026 1:43:53 PM] Configured dedicated match reporting for match id 595 with secret 38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328 and internal api base https://kkapi.playpoolstudios.com
|
||||||
[5/16/2026 11:47:17 PM] Starting client at $kickkingsapi.playpoolstudios.com:26614
|
[8/27/2026 1:43:53 PM] Setting cupid to load game scene
|
||||||
[5/16/2026 11:47:18 PM] Client connected
|
[8/27/2026 1:43:53 PM] Loading game scene
|
||||||
[5/16/2026 11:47:26 PM] Selected team changed from Red to Blue
|
[8/27/2026 1:43:54 PM] Got into a room
|
||||||
[5/16/2026 11:47:30 PM] Score changed from 0 to 1
|
[8/27/2026 1:43:54 PM] {"ok":true,"entry_fee":21,"rc_prize":34,"for_red":{"Players":[{"Name":"warlock","LastSeen":1787818434370,"UserId":2,"l10_wins":1,"l10_losses":8},{"Name":"warlock2","LastSeen":1787818433141,"UserId":3,"l10_wins":8,"l10_losses":2}],"GameName":"soccar","Port":26357,"InitTime":1787818433058,"entry_fee":21,"match_id":595,"instance_started":true,"rc_prize":34,"your_team":"red"},"for_blue":{"Players":[{"Name":"warlock","LastSeen":1787818434370,"UserId":2,"l10_wins":1,"l10_losses":8},{"Name":"warlock2","LastSeen":1787818433141,"UserId":3,"l10_wins":8,"l10_losses":2}],"GameName":"soccar","Port":26357,"InitTime":1787818433058,"entry_fee":21,"match_id":595,"instance_started":true,"rc_prize":34,"your_team":"blue"}}
|
||||||
[5/16/2026 11:47:33 PM] Selected team changed from Blue to Red
|
[8/27/2026 1:43:54 PM] Configured dedicated match reporting for match id 595 with secret 38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328 and internal api base https://kkapi.playpoolstudios.com
|
||||||
[5/16/2026 11:47:39 PM] Selected team changed from Red to Blue
|
[8/27/2026 1:43:54 PM] Setting cupid to load game scene
|
||||||
[5/16/2026 11:47:43 PM] Selected team changed from Blue to Red
|
[8/27/2026 1:43:54 PM] Loading game scene
|
||||||
[5/16/2026 11:47:48 PM] Selected team changed from Red to Blue
|
[8/27/2026 1:43:59 PM] Starting client at $kkapi.playpoolstudios.com:26357
|
||||||
[5/16/2026 11:47:50 PM] Score changed from 1 to 2
|
[8/27/2026 1:43:59 PM] Client connected
|
||||||
[5/16/2026 11:47:53 PM] Selected team changed from Blue to Red
|
[8/27/2026 1:43:59 PM] Dedicated join: team assigned Blue isServer=False matchId=595 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
[5/16/2026 11:47:57 PM] Selected team changed from Red to Blue
|
[8/27/2026 1:44:01 PM] Starting client at $kkapi.playpoolstudios.com:26357
|
||||||
[5/16/2026 11:48:02 PM] Score changed from 2 to 3
|
[8/27/2026 1:44:01 PM] Client connected
|
||||||
[5/16/2026 11:48:14 PM] Client disconnected
|
[8/27/2026 1:44:01 PM] Dedicated join: team assigned Red isServer=False matchId=595 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
[5/16/2026 11:48:18 PM] Cupid settings updated
|
[8/27/2026 1:44:17 PM] Selected team changed from Red to Blue
|
||||||
[5/16/2026 11:48:18 PM] Cupid settings updated
|
[8/27/2026 1:44:17 PM] Selected team changed from Red to Blue
|
||||||
|
[8/27/2026 1:44:26 PM] Selected team changed from Blue to Red
|
||||||
|
[8/27/2026 1:44:26 PM] Selected team changed from Blue to Red
|
||||||
|
[8/27/2026 1:45:01 PM] Configured dedicated match reporting for match id 596 with secret 38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328 and internal api base https://kkapi.playpoolstudios.com
|
||||||
|
[8/27/2026 1:45:01 PM] Client disconnected
|
||||||
|
[8/27/2026 1:45:01 PM] Client disconnected
|
||||||
|
[8/27/2026 1:45:05 PM] Starting client at $kkapi.playpoolstudios.com:26411
|
||||||
|
[8/27/2026 1:45:05 PM] Client connected
|
||||||
|
[8/27/2026 1:45:05 PM] Dedicated join: team assigned Red isServer=False matchId=596 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/27/2026 1:45:31 PM] Client disconnected
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d5d4b6e75e17172409ca64a1f41951a1
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4590b1d665d10c64a8174323d734b971
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[8/13/2026 7:50:10 PM] ReplayRecorder started (practice) id -1786630810 with 11 entities
|
||||||
|
[8/13/2026 7:50:10 PM] Client connected
|
||||||
|
[8/13/2026 7:50:10 PM] Dedicated join: team assigned Blue isServer=True matchId=0 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/13/2026 7:50:10 PM] Dedicated join: skipping PATCH (matchId=0, secretEmpty=False)
|
||||||
|
[8/13/2026 7:50:10 PM] Selected team changed from Red to Blue
|
||||||
|
[8/13/2026 7:50:27 PM] Match: Blue skipped turn (1/2)
|
||||||
|
[8/13/2026 7:50:27 PM] Selected team changed from Blue to Red
|
||||||
|
[8/13/2026 7:50:29 PM] Dedicated match: Blue disconnected (order count=1)
|
||||||
|
[8/13/2026 7:50:29 PM] Mirror server: client disconnected (connId=0)
|
||||||
|
[8/13/2026 7:50:29 PM] Client disconnected
|
||||||
|
[8/13/2026 7:50:29 PM] ReplayRecorder wrote F:/Projects/Unity/soccar2d/Assets\Logs\-1786630810.json (951 frames, 0 events, duration 19.02s)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 13e09da064f35f74b83f6ed19880c04d
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 084e9b7d2b988a34eb1c0aed4752ee16
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[8/13/2026 8:43:35 PM] ReplayRecorder started (practice) id -1786634015 with 11 entities
|
||||||
|
[8/13/2026 8:43:35 PM] Client connected
|
||||||
|
[8/13/2026 8:43:35 PM] Dedicated join: team assigned Blue isServer=True matchId=0 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/13/2026 8:43:35 PM] Dedicated join: skipping PATCH (matchId=0, secretEmpty=False)
|
||||||
|
[8/13/2026 8:43:35 PM] Selected team changed from Red to Blue
|
||||||
|
[8/13/2026 8:43:52 PM] Match: Blue skipped turn (1/2)
|
||||||
|
[8/13/2026 8:43:52 PM] Selected team changed from Blue to Red
|
||||||
|
[8/13/2026 8:43:55 PM] AI best puck Puck: total=3.04 (prox=0.85, between=0.03, traj=0.70, push=1.00)
|
||||||
|
[8/13/2026 8:43:55 PM] force = 70 * 0.9010412 = 63.07288
|
||||||
|
[8/13/2026 8:43:55 PM] launching puck at (0.00, -4.00) with (0.00, 252.29) force
|
||||||
|
[8/13/2026 8:43:58 PM] Selected team changed from Red to Blue
|
||||||
|
[8/13/2026 8:44:05 PM] force = 70 * 0.9955804 = 69.69063
|
||||||
|
[8/13/2026 8:44:05 PM] launching puck at (-0.77, 4.94) with (53.42, -344.33) force
|
||||||
|
[8/13/2026 8:44:08 PM] Dedicated match: Blue disconnected (order count=1)
|
||||||
|
[8/13/2026 8:44:08 PM] Mirror server: client disconnected (connId=0)
|
||||||
|
[8/13/2026 8:44:08 PM] Client disconnected
|
||||||
|
[8/13/2026 8:44:08 PM] ReplayRecorder wrote F:/Projects/Unity/soccar2d/Assets\Logs\-1786634015.json (1638 frames, 6 events, duration 32.76s)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a085b5292682b144dacde97500667411
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 48b08b67d9b52ad4c9484786cf2ec2e1
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[8/13/2026 8:49:52 PM] ReplayRecorder started (practice) id -1786634392 with 11 entities
|
||||||
|
[8/13/2026 8:49:52 PM] Client connected
|
||||||
|
[8/13/2026 8:49:53 PM] Dedicated join: team assigned Blue isServer=True matchId=0 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/13/2026 8:49:53 PM] Dedicated join: skipping PATCH (matchId=0, secretEmpty=False)
|
||||||
|
[8/13/2026 8:49:53 PM] Selected team changed from Red to Blue
|
||||||
|
[8/13/2026 8:50:09 PM] Selected team changed from Blue to Red
|
||||||
|
[8/13/2026 8:50:12 PM] AI best puck Puck: total=3.04 (prox=0.85, between=0.03, traj=0.70, push=1.00)
|
||||||
|
[8/13/2026 8:50:12 PM] force = 70 * 0.9010412 = 63.07288
|
||||||
|
[8/13/2026 8:50:12 PM] launching puck at (0.00, -4.00) with (0.00, 252.29) force
|
||||||
|
[8/13/2026 8:50:15 PM] Selected team changed from Red to Blue
|
||||||
|
[8/13/2026 8:50:33 PM] Selected team changed from Blue to Red
|
||||||
|
[8/13/2026 8:50:35 PM] AI best puck Puck: total=1.73 (prox=0.93, between=0.03, traj=0.00, push=1.00)
|
||||||
|
[8/13/2026 8:50:35 PM] force = 70 * 0.9010412 = 63.07288
|
||||||
|
[8/13/2026 8:50:35 PM] launching puck at (0.00, -4.00) with (0.00, 252.29) force
|
||||||
|
[8/13/2026 8:50:38 PM] Selected team changed from Red to Blue
|
||||||
|
[8/13/2026 8:50:53 PM] Selected team changed from Blue to Red
|
||||||
|
[8/13/2026 8:50:55 PM] AI best puck Puck: total=1.73 (prox=0.94, between=0.03, traj=0.00, push=1.00)
|
||||||
|
[8/13/2026 8:50:55 PM] force = 70 * 0.9010412 = 63.07288
|
||||||
|
[8/13/2026 8:50:55 PM] launching puck at (0.00, -4.00) with (0.00, 252.29) force
|
||||||
|
[8/13/2026 8:50:57 PM] Selected team changed from Red to Blue
|
||||||
|
[8/13/2026 8:51:00 PM] Dedicated match: Blue disconnected (order count=1)
|
||||||
|
[8/13/2026 8:51:00 PM] Mirror server: client disconnected (connId=0)
|
||||||
|
[8/13/2026 8:51:00 PM] Client disconnected
|
||||||
|
[8/13/2026 8:51:00 PM] ReplayRecorder wrote F:/Projects/Unity/soccar2d/Assets\Logs\-1786634392.json (3373 frames, 15 events, duration 67.46s)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ecdf112848437a94b8bc3ad0bb197473
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 312a453df6e098149af89b3a2094beb5
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[8/20/2026 7:19:02 PM] ReplayRecorder started (practice) id -1787233742 with 11 entities
|
||||||
|
[8/20/2026 7:19:02 PM] Client connected
|
||||||
|
[8/20/2026 7:19:03 PM] Dedicated join: team assigned Red isServer=True matchId=0 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/20/2026 7:19:03 PM] Dedicated join: skipping PATCH (matchId=0, secretEmpty=False)
|
||||||
|
[8/20/2026 7:19:19 PM] Selected team changed from Red to Blue
|
||||||
|
[8/20/2026 7:19:22 PM] AI best puck Puck (7): total=3.04 (prox=0.85, between=0.03, traj=0.70, push=1.00)
|
||||||
|
[8/20/2026 7:19:22 PM] force = 70 * 0.9010412 = 63.07288
|
||||||
|
[8/20/2026 7:19:22 PM] launching puck at (0.00, 4.00) with (0.00, -252.29) force
|
||||||
|
[8/20/2026 7:19:25 PM] Selected team changed from Blue to Red
|
||||||
|
[8/20/2026 7:19:41 PM] Selected team changed from Red to Blue
|
||||||
|
[8/20/2026 7:19:44 PM] AI best puck Puck (7): total=1.73 (prox=0.93, between=0.03, traj=0.00, push=1.00)
|
||||||
|
[8/20/2026 7:19:44 PM] force = 70 * 0.9010412 = 63.07288
|
||||||
|
[8/20/2026 7:19:44 PM] launching puck at (0.00, 4.00) with (0.00, -252.29) force
|
||||||
|
[8/20/2026 7:19:47 PM] Selected team changed from Blue to Red
|
||||||
|
[8/20/2026 7:20:02 PM] Selected team changed from Red to Blue
|
||||||
|
[8/20/2026 7:20:05 PM] AI best puck Puck (7): total=1.85 (prox=0.94, between=0.11, traj=0.00, push=1.00)
|
||||||
|
[8/20/2026 7:20:05 PM] force = 70 * 0.9010412 = 63.07288
|
||||||
|
[8/20/2026 7:20:05 PM] launching puck at (0.00, 4.00) with (0.00, -252.29) force
|
||||||
|
[8/20/2026 7:20:05 PM] Score changed from 0 to 1
|
||||||
|
[8/20/2026 7:20:05 PM] Blue goal scored, blue score is now 1
|
||||||
|
[8/20/2026 7:20:05 PM] Dedicated match: Red disconnected (order count=1)
|
||||||
|
[8/20/2026 7:20:05 PM] Mirror server: client disconnected (connId=0)
|
||||||
|
[8/20/2026 7:20:05 PM] Client disconnected
|
||||||
|
[8/20/2026 7:20:05 PM] ReplayRecorder wrote F:/Projects/Unity/soccar2d/Assets\Logs\-1787233742.json (3127 frames, 12 events, duration 62.54s)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e3ae7f36bed6c134797b7cc9a0ea5f22
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2cc4ee68f3294624c9898f706bab87a8
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[8/26/2026 10:21:50 PM] ReplayRecorder started (practice) id -1787763110 with 11 entities
|
||||||
|
[8/26/2026 10:21:50 PM] Client connected
|
||||||
|
[8/26/2026 10:21:51 PM] Dedicated join: team assigned Blue isServer=True matchId=0 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/26/2026 10:21:51 PM] Dedicated join: skipping PATCH (matchId=0, secretEmpty=False)
|
||||||
|
[8/26/2026 10:21:51 PM] Selected team changed from Red to Blue
|
||||||
|
[8/26/2026 10:21:55 PM] Match: Blue forfeited by leave — Red wins
|
||||||
|
[8/26/2026 10:21:55 PM] ReplayRecorder wrote F:/Projects/Unity/soccar2d/Assets\Logs\-1787763110.json (218 frames, 0 events, duration 4.36s)
|
||||||
|
[8/26/2026 10:21:55 PM] Dedicated match: end winner=Red red_connected=False blue_connected=False red_score=0 blue_score=0 forfeit=True
|
||||||
|
[8/26/2026 10:21:55 PM] Mirror server: client disconnected (connId=0)
|
||||||
|
[8/26/2026 10:21:55 PM] Client disconnected
|
||||||
|
[8/26/2026 10:22:00 PM] Cupid settings updated
|
||||||
|
[8/26/2026 10:22:01 PM] Cupid settings updated
|
||||||
|
[8/26/2026 10:22:03 PM] Configured dedicated match reporting for match id 0 with secret 38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328 and internal api base https://kkapi.playpoolstudios.com
|
||||||
|
[8/26/2026 10:22:10 PM] Invalid Room port
|
||||||
|
[8/26/2026 10:22:10 PM] Mirror server exception logging enabled
|
||||||
|
[8/26/2026 10:22:10 PM] Game started On localhost, tutorial mode enabled
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: eafec47de336f534da962bda68ab7dfb
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 62574c44b8b7522459dc1aebd641aa7f
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[8/26/2026 10:22:10 PM] ReplayRecorder started (practice) id -1787763130 with 11 entities
|
||||||
|
[8/26/2026 10:22:10 PM] Client connected
|
||||||
|
[8/26/2026 10:22:10 PM] Dedicated join: team assigned Blue isServer=True matchId=0 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/26/2026 10:22:10 PM] Dedicated join: skipping PATCH (matchId=0, secretEmpty=False)
|
||||||
|
[8/26/2026 10:22:10 PM] Selected team changed from Red to Blue
|
||||||
|
[8/26/2026 10:22:14 PM] Match: Blue forfeited by leave — Red wins
|
||||||
|
[8/26/2026 10:22:14 PM] ReplayRecorder wrote F:/Projects/Unity/soccar2d/Assets\Logs\-1787763130.json (198 frames, 0 events, duration 3.96s)
|
||||||
|
[8/26/2026 10:22:14 PM] Dedicated match: end winner=Red red_connected=False blue_connected=False red_score=0 blue_score=0 forfeit=True
|
||||||
|
[8/26/2026 10:22:14 PM] Mirror server: client disconnected (connId=0)
|
||||||
|
[8/26/2026 10:22:14 PM] Client disconnected
|
||||||
|
[8/26/2026 10:22:19 PM] Cupid settings updated
|
||||||
|
[8/26/2026 10:22:19 PM] Cupid settings updated
|
||||||
|
[8/26/2026 10:22:22 PM] Configured dedicated match reporting for match id 0 with secret 38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328 and internal api base https://kkapi.playpoolstudios.com
|
||||||
|
[8/26/2026 10:22:29 PM] Invalid Room port
|
||||||
|
[8/26/2026 10:22:29 PM] Mirror server exception logging enabled
|
||||||
|
[8/26/2026 10:22:29 PM] Game started On localhost, tutorial mode enabled
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6e233967e946ccc4b921379b66b22740
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5e168c91dfd19c748907045eef730143
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[8/26/2026 10:22:29 PM] ReplayRecorder started (practice) id -1787763149 with 11 entities
|
||||||
|
[8/26/2026 10:22:29 PM] Client connected
|
||||||
|
[8/26/2026 10:22:29 PM] Dedicated join: team assigned Blue isServer=True matchId=0 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/26/2026 10:22:29 PM] Dedicated join: skipping PATCH (matchId=0, secretEmpty=False)
|
||||||
|
[8/26/2026 10:22:29 PM] Selected team changed from Red to Blue
|
||||||
|
[8/26/2026 10:22:32 PM] Match: Blue forfeited by leave — Red wins
|
||||||
|
[8/26/2026 10:22:32 PM] ReplayRecorder wrote F:/Projects/Unity/soccar2d/Assets\Logs\-1787763149.json (171 frames, 0 events, duration 3.42s)
|
||||||
|
[8/26/2026 10:22:32 PM] Dedicated match: end winner=Red red_connected=False blue_connected=False red_score=0 blue_score=0 forfeit=True
|
||||||
|
[8/26/2026 10:22:32 PM] Mirror server: client disconnected (connId=0)
|
||||||
|
[8/26/2026 10:22:32 PM] Client disconnected
|
||||||
|
[8/26/2026 10:22:36 PM] Cupid settings updated
|
||||||
|
[8/26/2026 10:22:37 PM] Cupid settings updated
|
||||||
|
[8/26/2026 10:22:38 PM] Configured dedicated match reporting for match id 0 with secret 38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328 and internal api base https://kkapi.playpoolstudios.com
|
||||||
|
[8/26/2026 10:22:45 PM] Invalid Room port
|
||||||
|
[8/26/2026 10:22:45 PM] Mirror server exception logging enabled
|
||||||
|
[8/26/2026 10:22:45 PM] Game started On localhost, tutorial mode enabled
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: afd7819d04d262d42a80b619146a0acc
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f104f470c2a16344ea9b9aab39ff0f37
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[8/26/2026 10:22:45 PM] ReplayRecorder started (practice) id -1787763165 with 11 entities
|
||||||
|
[8/26/2026 10:22:45 PM] Client connected
|
||||||
|
[8/26/2026 10:22:45 PM] Dedicated join: team assigned Blue isServer=True matchId=0 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/26/2026 10:22:45 PM] Dedicated join: skipping PATCH (matchId=0, secretEmpty=False)
|
||||||
|
[8/26/2026 10:22:45 PM] Selected team changed from Red to Blue
|
||||||
|
[8/26/2026 10:22:57 PM] Match: Blue forfeited by leave — Red wins
|
||||||
|
[8/26/2026 10:22:57 PM] ReplayRecorder wrote F:/Projects/Unity/soccar2d/Assets\Logs\-1787763165.json (605 frames, 0 events, duration 12.10s)
|
||||||
|
[8/26/2026 10:22:57 PM] Dedicated match: end winner=Red red_connected=False blue_connected=False red_score=0 blue_score=0 forfeit=True
|
||||||
|
[8/26/2026 10:22:57 PM] Mirror server: client disconnected (connId=0)
|
||||||
|
[8/26/2026 10:22:57 PM] Client disconnected
|
||||||
|
[8/26/2026 10:23:02 PM] Cupid settings updated
|
||||||
|
[8/26/2026 10:23:02 PM] Cupid settings updated
|
||||||
|
[8/26/2026 10:23:05 PM] Configured dedicated match reporting for match id 0 with secret 38516e39d3406225b7c3015a66e5d25c54e9b2024c0755212122bdcea3e3a328 and internal api base https://kkapi.playpoolstudios.com
|
||||||
|
[8/26/2026 10:23:11 PM] Invalid Room port
|
||||||
|
[8/26/2026 10:23:11 PM] Mirror server exception logging enabled
|
||||||
|
[8/26/2026 10:23:11 PM] Game started On localhost, tutorial mode enabled
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b6b634e5c0a824d47b49e89290032735
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":1,"matchId":-1787763191,"isPractice":true,"fixedDeltaTime":0.019999992102384568,"duration":0.0,"entities":[{"id":0,"type":"ball","team":""},{"id":1,"type":"puck","team":"Red"},{"id":2,"type":"puck","team":"Red"},{"id":3,"type":"puck","team":"Red"},{"id":4,"type":"puck","team":"Red"},{"id":5,"type":"puck","team":"Red"},{"id":6,"type":"puck","team":"Blue"},{"id":7,"type":"puck","team":"Blue"},{"id":8,"type":"puck","team":"Blue"},{"id":9,"type":"puck","team":"Blue"},{"id":10,"type":"puck","team":"Blue"}],"frames":[],"events":[]}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d1dd94f034d5fcc48a72276d45f82c83
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[8/26/2026 10:23:11 PM] ReplayRecorder started (practice) id -1787763191 with 11 entities
|
||||||
|
[8/26/2026 10:23:11 PM] Client connected
|
||||||
|
[8/26/2026 10:23:11 PM] Dedicated join: team assigned Blue isServer=True matchId=0 secretEmpty=False redJoin=False blueJoin=False collected=False
|
||||||
|
[8/26/2026 10:23:11 PM] Dedicated join: skipping PATCH (matchId=0, secretEmpty=False)
|
||||||
|
[8/26/2026 10:23:11 PM] Dedicated match: Blue disconnected (order count=1)
|
||||||
|
[8/26/2026 10:23:11 PM] Mirror server: client disconnected (connId=0)
|
||||||
|
[8/26/2026 10:23:11 PM] Client disconnected
|
||||||
|
[8/26/2026 10:23:11 PM] ReplayRecorder wrote F:/Projects/Unity/soccar2d/Assets\Logs\-1787763191.json (0 frames, 0 events, duration 0.00s)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 13d3b05348f90644b8f236f835efa4c2
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -411,3 +411,40 @@
|
|||||||
[5/1/2026 1:13:06 AM] Active player connections: 0
|
[5/1/2026 1:13:06 AM] Active player connections: 0
|
||||||
[5/1/2026 1:13:12 AM] Server will be closed due to no players in, 40
|
[5/1/2026 1:13:12 AM] Server will be closed due to no players in, 40
|
||||||
[5/1/2026 1:13:22 AM] Server will be closed due to no players in, 30
|
[5/1/2026 1:13:22 AM] Server will be closed due to no players in, 30
|
||||||
|
[7/19/2026 12:42:42 AM] Starting server at port 7777
|
||||||
|
[7/19/2026 12:42:42 AM] Mirror server exception logging enabled
|
||||||
|
[7/19/2026 12:42:42 AM] Failed to fetch red and blue names, isServer?
|
||||||
|
[7/19/2026 12:42:42 AM] Starting auto close watchdog
|
||||||
|
[7/19/2026 12:42:42 AM] Active player connections: 0
|
||||||
|
[8/13/2026 2:36:25 PM] Starting server at port 7777
|
||||||
|
[8/13/2026 2:36:25 PM] Mirror server exception logging enabled
|
||||||
|
[8/13/2026 2:36:25 PM] Failed to fetch red and blue names, isServer?
|
||||||
|
[8/13/2026 2:36:25 PM] Starting auto close watchdog
|
||||||
|
[8/13/2026 2:36:25 PM] Active player connections: 0
|
||||||
|
[8/13/2026 3:56:45 PM] Starting server at port 7777
|
||||||
|
[8/13/2026 3:56:45 PM] Mirror server exception logging enabled
|
||||||
|
[8/13/2026 3:56:45 PM] Failed to fetch red and blue names, isServer?
|
||||||
|
[8/13/2026 3:56:45 PM] Starting auto close watchdog
|
||||||
|
[8/13/2026 3:56:45 PM] Active player connections: 0
|
||||||
|
[8/13/2026 3:56:52 PM] Server will be closed due to no players in, 60
|
||||||
|
[8/13/2026 9:06:10 PM] Starting server at port 7777
|
||||||
|
[8/13/2026 9:06:10 PM] Mirror server exception logging enabled
|
||||||
|
[8/13/2026 9:06:10 PM] Failed to fetch red and blue names, isServer?
|
||||||
|
[8/13/2026 9:06:10 PM] Starting auto close watchdog
|
||||||
|
[8/13/2026 9:06:10 PM] Active player connections: 0
|
||||||
|
[8/18/2026 2:02:56 AM] Starting server at port 7777
|
||||||
|
[8/18/2026 2:02:56 AM] Mirror server exception logging enabled
|
||||||
|
[8/18/2026 2:02:56 AM] Failed to fetch red and blue names, isServer?
|
||||||
|
[8/18/2026 2:02:56 AM] Starting auto close watchdog
|
||||||
|
[8/18/2026 2:02:56 AM] Active player connections: 0
|
||||||
|
[8/27/2026 1:12:53 PM] Starting server at port 7777
|
||||||
|
[8/27/2026 1:12:53 PM] Mirror server exception logging enabled
|
||||||
|
[8/27/2026 1:12:53 PM] Failed to fetch red and blue names, isServer?
|
||||||
|
[8/27/2026 1:12:53 PM] Starting auto close watchdog
|
||||||
|
[8/27/2026 1:12:53 PM] Active player connections: 0
|
||||||
|
[8/27/2026 1:12:59 PM] Server will be closed due to no players in, 60
|
||||||
|
[8/27/2026 1:42:51 PM] Starting server at port 7777
|
||||||
|
[8/27/2026 1:42:51 PM] Mirror server exception logging enabled
|
||||||
|
[8/27/2026 1:42:51 PM] Failed to fetch red and blue names, isServer?
|
||||||
|
[8/27/2026 1:42:51 PM] Starting auto close watchdog
|
||||||
|
[8/27/2026 1:42:51 PM] Active player connections: 0
|
||||||
|
|||||||
@@ -578,7 +578,7 @@ GameObject:
|
|||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
m_StaticEditorFlags: 0
|
m_StaticEditorFlags: 0
|
||||||
m_IsActive: 0
|
m_IsActive: 1
|
||||||
--- !u!224 &4426299922493186842
|
--- !u!224 &4426299922493186842
|
||||||
RectTransform:
|
RectTransform:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -595,7 +595,7 @@ RectTransform:
|
|||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: -12, y: -301.3}
|
m_AnchoredPosition: {x: -12, y: -289}
|
||||||
m_SizeDelta: {x: 396.3171, y: 206.1671}
|
m_SizeDelta: {x: 396.3171, y: 206.1671}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &5777954290886970586
|
--- !u!222 &5777954290886970586
|
||||||
@@ -619,7 +619,7 @@ MonoBehaviour:
|
|||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
m_Material: {fileID: 0}
|
m_Material: {fileID: 0}
|
||||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
m_Color: {r: 0.8349056, g: 0.93029827, b: 1, a: 1}
|
||||||
m_RaycastTarget: 1
|
m_RaycastTarget: 1
|
||||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||||
m_Maskable: 1
|
m_Maskable: 1
|
||||||
@@ -671,7 +671,7 @@ RectTransform:
|
|||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: 351.29, y: 619.50073}
|
m_AnchoredPosition: {x: 291.99997, y: 619.50073}
|
||||||
m_SizeDelta: {x: 356.21, y: 79.8881}
|
m_SizeDelta: {x: 356.21, y: 79.8881}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &4299910807936819090
|
--- !u!222 &4299910807936819090
|
||||||
@@ -747,7 +747,7 @@ MonoBehaviour:
|
|||||||
m_OnCullStateChanged:
|
m_OnCullStateChanged:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
m_Sprite: {fileID: -5110294184840258280, guid: b3993aed15d744d4bb390d5351cda423, type: 3}
|
m_Sprite: {fileID: 0}
|
||||||
m_Type: 0
|
m_Type: 0
|
||||||
m_PreserveAspect: 0
|
m_PreserveAspect: 0
|
||||||
m_FillCenter: 1
|
m_FillCenter: 1
|
||||||
@@ -789,9 +789,9 @@ RectTransform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 784446025213843339}
|
m_Father: {fileID: 784446025213843339}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0.5, y: 1}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 1}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: 349.8392, y: -415.53}
|
m_AnchoredPosition: {x: 349.8392, y: 714.47}
|
||||||
m_SizeDelta: {x: 287.9087, y: 53.690674}
|
m_SizeDelta: {x: 287.9087, y: 53.690674}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &4680061499564276536
|
--- !u!222 &4680061499564276536
|
||||||
@@ -928,7 +928,7 @@ RectTransform:
|
|||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: -351.29, y: 619.50073}
|
m_AnchoredPosition: {x: -297.8, y: 619.50073}
|
||||||
m_SizeDelta: {x: 356.21, y: 79.8881}
|
m_SizeDelta: {x: 356.21, y: 79.8881}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &2120176914018994436
|
--- !u!222 &2120176914018994436
|
||||||
@@ -1198,8 +1198,8 @@ MonoBehaviour:
|
|||||||
m_faceColor:
|
m_faceColor:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
rgba: 4294967295
|
rgba: 4294967295
|
||||||
m_fontSize: 49.1
|
m_fontSize: 46.1
|
||||||
m_fontSizeBase: 49.1
|
m_fontSizeBase: 46.1
|
||||||
m_fontWeight: 400
|
m_fontWeight: 400
|
||||||
m_enableAutoSizing: 0
|
m_enableAutoSizing: 0
|
||||||
m_fontSizeMin: 18
|
m_fontSizeMin: 18
|
||||||
@@ -1277,7 +1277,7 @@ RectTransform:
|
|||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: -351.29, y: 619.50073}
|
m_AnchoredPosition: {x: -297.8, y: 619.50073}
|
||||||
m_SizeDelta: {x: 356.21, y: 79.8881}
|
m_SizeDelta: {x: 356.21, y: 79.8881}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &588061042461009508
|
--- !u!222 &588061042461009508
|
||||||
@@ -1456,9 +1456,9 @@ RectTransform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 784446025213843339}
|
m_Father: {fileID: 784446025213843339}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0.5, y: 1}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 1}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: -349.83923, y: -415.53}
|
m_AnchoredPosition: {x: -349.83923, y: 714.47}
|
||||||
m_SizeDelta: {x: 287.9087, y: 53.690674}
|
m_SizeDelta: {x: 287.9087, y: 53.690674}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &4305625922513859524
|
--- !u!222 &4305625922513859524
|
||||||
@@ -1868,7 +1868,7 @@ RectTransform:
|
|||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: -0.0000059605, y: 619.1}
|
m_AnchoredPosition: {x: -0.0000059605, y: 619.1}
|
||||||
m_SizeDelta: {x: 433.0019, y: 98.6273}
|
m_SizeDelta: {x: 359.3, y: 98.6273}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &8725577243422270907
|
--- !u!222 &8725577243422270907
|
||||||
CanvasRenderer:
|
CanvasRenderer:
|
||||||
@@ -2094,7 +2094,7 @@ RectTransform:
|
|||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: -6.8129, y: 711.5879}
|
m_AnchoredPosition: {x: -6.8129, y: 711.5879}
|
||||||
m_SizeDelta: {x: 370.1125, y: 86.3439}
|
m_SizeDelta: {x: 267.8, y: 86.3439}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &2040376918808009174
|
--- !u!222 &2040376918808009174
|
||||||
CanvasRenderer:
|
CanvasRenderer:
|
||||||
@@ -2244,7 +2244,7 @@ RectTransform:
|
|||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: 351.29, y: 619.50073}
|
m_AnchoredPosition: {x: 292, y: 619.50073}
|
||||||
m_SizeDelta: {x: 356.21, y: 79.8881}
|
m_SizeDelta: {x: 356.21, y: 79.8881}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &3873575104506013387
|
--- !u!222 &3873575104506013387
|
||||||
@@ -2470,7 +2470,7 @@ MonoBehaviour:
|
|||||||
m_OnCullStateChanged:
|
m_OnCullStateChanged:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
m_Sprite: {fileID: 8390053243822926539, guid: 511a187332d4a4d4db6f55f623e1de11, type: 3}
|
m_Sprite: {fileID: 8390053243822926539, guid: 5b969ed0d82676d4890df6c74fd6f3c4, type: 3}
|
||||||
m_Type: 0
|
m_Type: 0
|
||||||
m_PreserveAspect: 0
|
m_PreserveAspect: 0
|
||||||
m_FillCenter: 1
|
m_FillCenter: 1
|
||||||
@@ -2728,7 +2728,7 @@ MonoBehaviour:
|
|||||||
m_OnCullStateChanged:
|
m_OnCullStateChanged:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
m_Sprite: {fileID: 8390053243822926539, guid: 511a187332d4a4d4db6f55f623e1de11, type: 3}
|
m_Sprite: {fileID: 8390053243822926539, guid: 5b969ed0d82676d4890df6c74fd6f3c4, type: 3}
|
||||||
m_Type: 0
|
m_Type: 0
|
||||||
m_PreserveAspect: 0
|
m_PreserveAspect: 0
|
||||||
m_FillCenter: 1
|
m_FillCenter: 1
|
||||||
@@ -3625,7 +3625,7 @@ MonoBehaviour:
|
|||||||
p2_name: {fileID: 6139878670004169448}
|
p2_name: {fileID: 6139878670004169448}
|
||||||
p1_l10: {fileID: 7658668847612786734}
|
p1_l10: {fileID: 7658668847612786734}
|
||||||
p2_l10: {fileID: 8415686484963452604}
|
p2_l10: {fileID: 8415686484963452604}
|
||||||
redIcon: {fileID: 7194123030578827194, guid: 809c8d780dc80f14e80fd92c6e42ed71, type: 3}
|
redIcon: {fileID: 7609527126711406816, guid: 532bcbd7fabd63844ac75765ae25dcb9, type: 3}
|
||||||
blueIcon: {fileID: 7609527126711406816, guid: 8d015c6e4a5ab8540814b06ae5f85980, type: 3}
|
blueIcon: {fileID: 7609527126711406816, guid: 8d015c6e4a5ab8540814b06ae5f85980, type: 3}
|
||||||
txtMiddle: {fileID: 2458004521057696282}
|
txtMiddle: {fileID: 2458004521057696282}
|
||||||
txtRcPrize: {fileID: 8330661879995437874}
|
txtRcPrize: {fileID: 8330661879995437874}
|
||||||
@@ -3953,7 +3953,7 @@ RectTransform:
|
|||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0, y: 0}
|
m_AnchorMin: {x: 0, y: 0}
|
||||||
m_AnchorMax: {x: 1, y: 1}
|
m_AnchorMax: {x: 1, y: 1}
|
||||||
m_AnchoredPosition: {x: 1.326294, y: -53.300316}
|
m_AnchoredPosition: {x: 1.326294, y: -53.300537}
|
||||||
m_SizeDelta: {x: 82.232544, y: 5.79937}
|
m_SizeDelta: {x: 82.232544, y: 5.79937}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &8345919255487113005
|
--- !u!222 &8345919255487113005
|
||||||
@@ -3984,7 +3984,7 @@ MonoBehaviour:
|
|||||||
m_OnCullStateChanged:
|
m_OnCullStateChanged:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
m_Sprite: {fileID: -7443756053782179069, guid: 0ec6ee71c3b91214284f88b794c2f1e7, type: 3}
|
m_Sprite: {fileID: -1955448553306655712, guid: b9f492da50bba6a4cae145945abc32e8, type: 3}
|
||||||
m_Type: 0
|
m_Type: 0
|
||||||
m_PreserveAspect: 0
|
m_PreserveAspect: 0
|
||||||
m_FillCenter: 1
|
m_FillCenter: 1
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ GameObject:
|
|||||||
- component: {fileID: 9033708766278989701}
|
- component: {fileID: 9033708766278989701}
|
||||||
- component: {fileID: 2872566638177417579}
|
- component: {fileID: 2872566638177417579}
|
||||||
- component: {fileID: 407622981302802273}
|
- component: {fileID: 407622981302802273}
|
||||||
- component: {fileID: 969959851011469098}
|
|
||||||
- component: {fileID: 2978779054327924816}
|
|
||||||
m_Layer: 5
|
m_Layer: 5
|
||||||
m_Name: text_emote_prefab
|
m_Name: text_emote_prefab
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -165,34 +163,6 @@ MonoBehaviour:
|
|||||||
m_OnClick:
|
m_OnClick:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
--- !u!114 &969959851011469098
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 4492919853174754762}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: d0b148fe25e99eb48b9724523833bab1, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
m_Delegates: []
|
|
||||||
--- !u!114 &2978779054327924816
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 4492919853174754762}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 4e4c65221cd965e45853c892a9facd82, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
scaleFactor: 1.1
|
|
||||||
duration: 0.1
|
|
||||||
ease: 27
|
|
||||||
--- !u!1 &4797427416459394329
|
--- !u!1 &4797427416459394329
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -225,10 +195,10 @@ RectTransform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 1018686033926137723}
|
m_Father: {fileID: 1018686033926137723}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0, y: 1}
|
m_AnchorMin: {x: 0, y: 0}
|
||||||
m_AnchorMax: {x: 0, y: 1}
|
m_AnchorMax: {x: 0, y: 0}
|
||||||
m_AnchoredPosition: {x: 78.775, y: -32.235}
|
m_AnchoredPosition: {x: 0, y: 0}
|
||||||
m_SizeDelta: {x: 137.55, y: 44.47}
|
m_SizeDelta: {x: 0, y: 0}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &101741276978323686
|
--- !u!222 &101741276978323686
|
||||||
CanvasRenderer:
|
CanvasRenderer:
|
||||||
|
|||||||
+7314
-278
File diff suppressed because it is too large
Load Diff
+4628
-43
File diff suppressed because it is too large
Load Diff
+7042
-75
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c886581a7bac8f34ba8f7995eb48db02
|
||||||
+58
-3
@@ -15,22 +15,73 @@ public class Ball : NetworkBehaviour
|
|||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
rb = GetComponent<Rigidbody2D>();
|
rb = GetComponent<Rigidbody2D>();
|
||||||
|
col = GetComponent<Collider2D>();
|
||||||
startPosition = transform.position;
|
startPosition = transform.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Collider2D col;
|
||||||
|
Coroutine coroutineReset;
|
||||||
|
bool holdingCollisionLock;
|
||||||
|
|
||||||
|
public void SetCollisionsEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (col != null)
|
||||||
|
col.enabled = enabled;
|
||||||
|
if (rb != null && !enabled)
|
||||||
|
{
|
||||||
|
rb.linearVelocity = Vector2.zero;
|
||||||
|
rb.angularVelocity = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Reset(float duration){
|
public void Reset(float duration){
|
||||||
StartCoroutine(CoroutineReset(duration));
|
if (coroutineReset != null)
|
||||||
|
{
|
||||||
|
StopCoroutine(coroutineReset);
|
||||||
|
RestoreAfterReset();
|
||||||
|
}
|
||||||
|
coroutineReset = StartCoroutine(CoroutineReset(duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator CoroutineReset(float duration){
|
IEnumerator CoroutineReset(float duration){
|
||||||
|
if (GameManager.instance != null)
|
||||||
|
{
|
||||||
|
GameManager.instance.PushResetCollisionLock();
|
||||||
|
holdingCollisionLock = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb.linearVelocity = Vector2.zero;
|
||||||
|
rb.angularVelocity = 0f;
|
||||||
|
|
||||||
float t=0;
|
float t=0;
|
||||||
Vector3 pos1 = transform.position;
|
Vector3 pos1 = transform.position;
|
||||||
Quaternion rot1 = transform.rotation;
|
Quaternion rot1 = transform.rotation;
|
||||||
while (t < duration){
|
while (t < duration){
|
||||||
t+=Time.deltaTime;
|
t+=Time.deltaTime;
|
||||||
transform.position = Vector3.Lerp(pos1, startPosition, t / duration);
|
float u = Mathf.Clamp01(t / duration);
|
||||||
transform.rotation = Quaternion.Lerp(rot1, Quaternion.identity, t/duration);
|
transform.position = Vector3.Lerp(pos1, startPosition, u);
|
||||||
|
transform.rotation = Quaternion.Lerp(rot1, Quaternion.identity, u);
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
transform.position = startPosition;
|
||||||
|
transform.rotation = Quaternion.identity;
|
||||||
|
|
||||||
|
RestoreAfterReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreAfterReset()
|
||||||
|
{
|
||||||
|
if (rb != null)
|
||||||
|
{
|
||||||
|
rb.linearVelocity = Vector2.zero;
|
||||||
|
rb.angularVelocity = 0f;
|
||||||
|
}
|
||||||
|
if (holdingCollisionLock && GameManager.instance != null)
|
||||||
|
{
|
||||||
|
GameManager.instance.PopResetCollisionLock();
|
||||||
|
holdingCollisionLock = false;
|
||||||
|
}
|
||||||
|
coroutineReset = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool touchingB,touchingL,touchingR,touchingT = false;
|
public bool touchingB,touchingL,touchingR,touchingT = false;
|
||||||
@@ -96,10 +147,14 @@ public class Ball : NetworkBehaviour
|
|||||||
|
|
||||||
if(collision.collider.CompareTag("wall")){
|
if(collision.collider.CompareTag("wall")){
|
||||||
AudioManager.instance.PlayWallHit(vol);
|
AudioManager.instance.PlayWallHit(vol);
|
||||||
|
if (isServer)
|
||||||
|
ReplayRecorder.RecordHit("ball_wall", vol, this);
|
||||||
RpcWallHitAudio(vol);
|
RpcWallHitAudio(vol);
|
||||||
}
|
}
|
||||||
else if(collision.collider.CompareTag("Puck")){
|
else if(collision.collider.CompareTag("Puck")){
|
||||||
AudioManager.instance.PlayBallHit(vol);
|
AudioManager.instance.PlayBallHit(vol);
|
||||||
|
if (isServer)
|
||||||
|
ReplayRecorder.RecordHit("ball_puck", vol, this);
|
||||||
RpcBallHitAudio(vol);
|
RpcBallHitAudio(vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public class GameCanvas : MonoBehaviour
|
|||||||
|
|
||||||
ShowWaitingForOpponentPanel();
|
ShowWaitingForOpponentPanel();
|
||||||
HideEmotesPanel();
|
HideEmotesPanel();
|
||||||
|
HideAllPostGamePanels();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnDestroy()
|
void OnDestroy()
|
||||||
@@ -59,6 +60,10 @@ public class GameCanvas : MonoBehaviour
|
|||||||
public float emojiEmoteDuration = 5f;
|
public float emojiEmoteDuration = 5f;
|
||||||
bool isShowingEmotes=> emotesPanel.gameObject.activeSelf;
|
bool isShowingEmotes=> emotesPanel.gameObject.activeSelf;
|
||||||
|
|
||||||
|
public RectTransform kickWarning;
|
||||||
|
public GameObject[] postGamePanels;
|
||||||
|
public TMP_Text txtMatchNumber;
|
||||||
|
const float PostGamePanelDuration = 3f;
|
||||||
|
|
||||||
|
|
||||||
void Start(){
|
void Start(){
|
||||||
@@ -91,6 +96,9 @@ public class GameCanvas : MonoBehaviour
|
|||||||
|
|
||||||
if (txtRcPrize != null)
|
if (txtRcPrize != null)
|
||||||
txtRcPrize.text = CoinHelper.FormatString(GameManager.MatchRcPrizeCoins) + " RC";
|
txtRcPrize.text = CoinHelper.FormatString(GameManager.MatchRcPrizeCoins) + " RC";
|
||||||
|
|
||||||
|
if (txtMatchNumber != null && GameManager.DedicatedMatchId > 0)
|
||||||
|
txtMatchNumber.text = "M" + GameManager.DedicatedMatchId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnEmoteTextPressed(string txtName){
|
void OnEmoteTextPressed(string txtName){
|
||||||
@@ -164,17 +172,36 @@ public class GameCanvas : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnLeaveGamePressed()
|
void OnLeaveGamePressed()
|
||||||
|
{
|
||||||
|
if (MessageBoxDialog.IsAvailable)
|
||||||
|
{
|
||||||
|
MessageBoxDialog.Show(
|
||||||
|
"Forfeit match?",
|
||||||
|
"Leaving will result in a loss, making opponent the winner. Are you sure?",
|
||||||
|
confirmed =>
|
||||||
|
{
|
||||||
|
if (confirmed)
|
||||||
|
ConfirmLeaveMatch();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmLeaveMatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfirmLeaveMatch()
|
||||||
{
|
{
|
||||||
if (CupidLobby.instance != null)
|
if (CupidLobby.instance != null)
|
||||||
CupidLobby.instance.Cancel();
|
CupidLobby.instance.Cancel();
|
||||||
LoginManager.ClearMatchYourTeam();
|
LoginManager.ClearMatchYourTeam();
|
||||||
|
|
||||||
if (NetworkManager.singleton != null && NetworkClient.active)
|
if (GameManager.instance != null)
|
||||||
{
|
{
|
||||||
NetManager.MarkIntentionalClientDisconnect();
|
GameManager.instance.Leave();
|
||||||
NetworkManager.singleton.StopClient();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetManager.StopNetworkingForSceneChange();
|
||||||
LevelLoadManager.LoadLevel("MainMenu");
|
LevelLoadManager.LoadLevel("MainMenu");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,4 +330,44 @@ public class GameCanvas : MonoBehaviour
|
|||||||
timeoutForOpponentPanel.SetActive(false);
|
timeoutForOpponentPanel.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void ShowKickWarning(){
|
||||||
|
kickWarning.transform.localScale = new Vector3(0, 0, 0);
|
||||||
|
kickWarning.transform.DOScale(1, 0.2f).SetEase(Ease.OutBack);
|
||||||
|
kickWarning.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HideKickWarning(){
|
||||||
|
kickWarning.transform.DOScale(0, 0.2f).SetEase(Ease.InBack).OnComplete(() => {
|
||||||
|
kickWarning.gameObject.SetActive(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator ShowRandomPostGamePanelThenHide()
|
||||||
|
{
|
||||||
|
HideAllPostGamePanels();
|
||||||
|
if (postGamePanels == null || postGamePanels.Length == 0)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
GameObject chosen = postGamePanels[Random.Range(0, postGamePanels.Length)];
|
||||||
|
if (chosen == null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
chosen.SetActive(true);
|
||||||
|
yield return new WaitForSeconds(PostGamePanelDuration);
|
||||||
|
chosen.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HideAllPostGamePanels()
|
||||||
|
{
|
||||||
|
if (postGamePanels == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < postGamePanels.Length; i++)
|
||||||
|
{
|
||||||
|
if (postGamePanels[i] != null)
|
||||||
|
postGamePanels[i].SetActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+829
-41
File diff suppressed because it is too large
Load Diff
@@ -42,6 +42,25 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
|
|
||||||
public float returnToMenuCountdown = 15f;
|
public float returnToMenuCountdown = 15f;
|
||||||
|
|
||||||
|
/// <summary>Each player needs at least 2.0 RC (20 coin units) to rematch.</summary>
|
||||||
|
const int MinimumRematchRcCoins = 20;
|
||||||
|
|
||||||
|
[Header("Bet Limit")]
|
||||||
|
public TMP_Text txtRcBalance;
|
||||||
|
public TMP_Text txtBetLimitWarning;
|
||||||
|
public CanvasGroup betLimitAdjustPanel;
|
||||||
|
//minVal: 2, max Val 500, when its 500, show ∞ on the text
|
||||||
|
public Slider betLimitSlider;
|
||||||
|
public TMP_Text txtBetLimitSliderValue;
|
||||||
|
public Button btnBetLimitAdjustAccept;
|
||||||
|
public Button btnBetLimitAdjustCancel;
|
||||||
|
|
||||||
|
const string PersonalBetLimitPrefsKey = "WalkInvest.PersonalBetLimit";
|
||||||
|
const int BetLimitMin = 8;
|
||||||
|
const int BetLimitMax = 500;
|
||||||
|
const int BetLimitUnlimited = 500;
|
||||||
|
|
||||||
|
int rematchOpponentBetLimitCoins = BetLimitUnlimited;
|
||||||
|
|
||||||
[Header("Rematch panel")]
|
[Header("Rematch panel")]
|
||||||
public CanvasGroup rematchPanel;
|
public CanvasGroup rematchPanel;
|
||||||
@@ -83,6 +102,139 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
|
|
||||||
btnConfirmBet.onClick.AddListener(OnBetAccepted);
|
btnConfirmBet.onClick.AddListener(OnBetAccepted);
|
||||||
btnCancelBet.onClick.AddListener(OnBetCancelled);
|
btnCancelBet.onClick.AddListener(OnBetCancelled);
|
||||||
|
|
||||||
|
SetupBetLimitUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupBetLimitUi()
|
||||||
|
{
|
||||||
|
if (betLimitSlider != null)
|
||||||
|
{
|
||||||
|
betLimitSlider.minValue = BetLimitMin;
|
||||||
|
betLimitSlider.maxValue = BetLimitMax;
|
||||||
|
betLimitSlider.wholeNumbers = true;
|
||||||
|
betLimitSlider.onValueChanged.AddListener(OnBetLimitSliderValueChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnBetLimitAdjustAccept != null)
|
||||||
|
btnBetLimitAdjustAccept.onClick.AddListener(OnBetLimitAdjustAccepted);
|
||||||
|
if (btnBetLimitAdjustCancel != null)
|
||||||
|
btnBetLimitAdjustCancel.onClick.AddListener(CloseBetLimitAdjuster);
|
||||||
|
|
||||||
|
var warningButton = txtBetLimitWarning != null
|
||||||
|
? txtBetLimitWarning.GetComponentInChildren<Button>()
|
||||||
|
: null;
|
||||||
|
if (warningButton != null)
|
||||||
|
warningButton.onClick.AddListener(OpenBetLimitAdjuster);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetPersonalBetLimit()
|
||||||
|
{
|
||||||
|
return PlayerPrefs.GetInt(PersonalBetLimitPrefsKey, BetLimitUnlimited);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int EffectiveBetLimitCap(int limitCoins) =>
|
||||||
|
limitCoins >= BetLimitUnlimited ? int.MaxValue : limitCoins;
|
||||||
|
|
||||||
|
int GetRematchMaxBetCoins()
|
||||||
|
{
|
||||||
|
int maxBet = Mathf.Min(
|
||||||
|
EffectiveBetLimitCap(rematchOpponentBetLimitCoins),
|
||||||
|
GetRcCoins(LevelLoadManager.myPlayer),
|
||||||
|
GetRcCoins(LevelLoadManager.opponentPlayer));
|
||||||
|
return Mathf.Max(0, maxBet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyRematchBetSliderLimits()
|
||||||
|
{
|
||||||
|
if (betSlider == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int maxBet = GetRematchMaxBetCoins();
|
||||||
|
betSlider.maxValue = maxBet;
|
||||||
|
if (maxBet <= 0)
|
||||||
|
{
|
||||||
|
betSlider.minValue = 0;
|
||||||
|
betSlider.value = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
betSlider.minValue = Mathf.Clamp(maxBet / 10f, 5, 10000);
|
||||||
|
betSlider.value = Mathf.Clamp(maxBet / 2, betSlider.minValue, maxBet);
|
||||||
|
}
|
||||||
|
OnBetSliderValueChanged(betSlider.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetPersonalBetLimit(int limitCoins)
|
||||||
|
{
|
||||||
|
limitCoins = Mathf.Clamp(limitCoins, BetLimitMin, BetLimitMax);
|
||||||
|
PlayerPrefs.SetInt(PersonalBetLimitPrefsKey, limitCoins);
|
||||||
|
PlayerPrefs.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
static string FormatBetLimitLabel(int limitCoins)
|
||||||
|
{
|
||||||
|
if (limitCoins >= BetLimitUnlimited)
|
||||||
|
return "∞";
|
||||||
|
return CoinHelper.FormatString(limitCoins) + " RC";
|
||||||
|
}
|
||||||
|
|
||||||
|
static string FormatBetLimitWarningText(int limitCoins) =>
|
||||||
|
"Max Bet : " + FormatBetLimitLabel(limitCoins);
|
||||||
|
|
||||||
|
void UpdateBetLimitWarningText()
|
||||||
|
{
|
||||||
|
if (txtBetLimitWarning != null)
|
||||||
|
txtBetLimitWarning.text = FormatBetLimitWarningText(GetPersonalBetLimit());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnBetLimitSliderValueChanged(float value)
|
||||||
|
{
|
||||||
|
if (txtBetLimitSliderValue != null)
|
||||||
|
txtBetLimitSliderValue.text = FormatBetLimitLabel(Mathf.RoundToInt(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyBetLimitVisibility()
|
||||||
|
{
|
||||||
|
bool isLoser = myTeam != winningTeam;
|
||||||
|
if (txtBetLimitWarning != null)
|
||||||
|
txtBetLimitWarning.gameObject.SetActive(isLoser);
|
||||||
|
if (!isLoser)
|
||||||
|
return;
|
||||||
|
UpdateBetLimitWarningText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenBetLimitAdjuster()
|
||||||
|
{
|
||||||
|
if (betLimitAdjustPanel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int limit = GetPersonalBetLimit();
|
||||||
|
if (betLimitSlider != null)
|
||||||
|
betLimitSlider.SetValueWithoutNotify(limit);
|
||||||
|
OnBetLimitSliderValueChanged(limit);
|
||||||
|
|
||||||
|
betLimitAdjustPanel.DOFade(1, 0.25f);
|
||||||
|
betLimitAdjustPanel.blocksRaycasts = true;
|
||||||
|
betLimitAdjustPanel.interactable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseBetLimitAdjuster()
|
||||||
|
{
|
||||||
|
if (betLimitAdjustPanel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
betLimitAdjustPanel.DOFade(0, 0.25f);
|
||||||
|
betLimitAdjustPanel.blocksRaycasts = false;
|
||||||
|
betLimitAdjustPanel.interactable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnBetLimitAdjustAccepted()
|
||||||
|
{
|
||||||
|
if (betLimitSlider != null)
|
||||||
|
SetPersonalBetLimit(Mathf.RoundToInt(betLimitSlider.value));
|
||||||
|
UpdateBetLimitWarningText();
|
||||||
|
CloseBetLimitAdjuster();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnBetAccepted(){
|
void OnBetAccepted(){
|
||||||
@@ -102,14 +254,18 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnBtnRematchClicked(){
|
void OnBtnRematchClicked(){
|
||||||
// LevelLoadManager.LoadLevel("Game");
|
if (!BothPlayersHaveMinimumRematchRc())
|
||||||
|
return;
|
||||||
|
if (txtBetLimitWarning != null)
|
||||||
|
txtBetLimitWarning.gameObject.SetActive(false);
|
||||||
NetPlayer.localPlayer.RequestRematch();
|
NetPlayer.localPlayer.RequestRematch();
|
||||||
SetRematchPendingState(true);
|
SetRematchPendingState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnRematchRequested(){
|
public void OnRematchRequested(int opponentMaxBetLimitCoins){
|
||||||
|
rematchOpponentBetLimitCoins = opponentMaxBetLimitCoins;
|
||||||
if(winningTeam == myTeam){
|
if(winningTeam == myTeam){
|
||||||
//Only i can initiate the rematch, so here we go
|
//Only the loser can initiate the rematch; winner sets the stake
|
||||||
ShowRematchPanel();
|
ShowRematchPanel();
|
||||||
}else{
|
}else{
|
||||||
rematchWarningLoser.text = "Waiting for opponent to accept";
|
rematchWarningLoser.text = "Waiting for opponent to accept";
|
||||||
@@ -129,7 +285,7 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
|
|
||||||
|
|
||||||
void OnBtnLeaveGameClicked(){
|
void OnBtnLeaveGameClicked(){
|
||||||
GameManager.instance.Leave();
|
LevelLoadManager.instance.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectOriginalPositionsAndScales(){
|
void CollectOriginalPositionsAndScales(){
|
||||||
@@ -178,6 +334,13 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
rematchPanel.alpha=0;
|
rematchPanel.alpha=0;
|
||||||
rematchPanel.blocksRaycasts = false;
|
rematchPanel.blocksRaycasts = false;
|
||||||
rematchPanel.interactable = false;
|
rematchPanel.interactable = false;
|
||||||
|
|
||||||
|
if (betLimitAdjustPanel != null)
|
||||||
|
{
|
||||||
|
betLimitAdjustPanel.alpha = 0;
|
||||||
|
betLimitAdjustPanel.blocksRaycasts = false;
|
||||||
|
betLimitAdjustPanel.interactable = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Team winningTeam;
|
Team winningTeam;
|
||||||
@@ -217,6 +380,8 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
p1_winner.gameObject.SetActive(false);
|
p1_winner.gameObject.SetActive(false);
|
||||||
p2_winner.gameObject.SetActive(true);
|
p2_winner.gameObject.SetActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplyBetLimitVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
float rcPrize = 0;
|
float rcPrize = 0;
|
||||||
@@ -246,16 +411,16 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
item.localScale = new Vector3(0, 0, 0);
|
item.localScale = new Vector3(0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(winningTeam == myTeam){
|
if (GameTutorialManager.tutorialModeEnabled)
|
||||||
|
rcPrize = 0;
|
||||||
|
else if (winningTeam == myTeam)
|
||||||
rcPrize = CoinHelper.Format(GameManager.MatchRcPrizeCoins);
|
rcPrize = CoinHelper.Format(GameManager.MatchRcPrizeCoins);
|
||||||
}
|
|
||||||
|
|
||||||
btnRematch.interactable = winningTeam != myTeam;
|
if (btnRematch != null)
|
||||||
if(winningTeam == myTeam){
|
btnRematch.gameObject.SetActive(!GameTutorialManager.tutorialModeEnabled);
|
||||||
rematchWarningLoser.text = "Only the loser can request a rematch";
|
|
||||||
}else{
|
UpdateRematchAvailability();
|
||||||
rematchWarningLoser.text = "";
|
ApplyRcBalancesToUi(null);
|
||||||
}
|
|
||||||
|
|
||||||
StartCoroutine(CoroutineShow());
|
StartCoroutine(CoroutineShow());
|
||||||
|
|
||||||
@@ -290,11 +455,16 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
|
|
||||||
yield return new WaitForSeconds(scaleTime * 0.5f);
|
yield return new WaitForSeconds(scaleTime * 0.5f);
|
||||||
|
|
||||||
//Rewards
|
// Rewards — +CC only if this client was actually awarded it (connected non-forfeit, or forfeit winner)
|
||||||
|
float ccReward = GameTutorialManager.tutorialModeEnabled || !GameManager.LocalPlayerEarnedParticipationCc
|
||||||
|
? 0f
|
||||||
|
: GameManager.ParticipationCcAmount;
|
||||||
StartCoroutine(CoroutineSetTextNumber(rcPrize, txtRewardRC, 10, "N1", "+", " RC"));
|
StartCoroutine(CoroutineSetTextNumber(rcPrize, txtRewardRC, 10, "N1", "+", " RC"));
|
||||||
StartCoroutine(CoroutineSetTextNumber(100, txtRewardCC, 100, "N0", "+", " CC"));
|
StartCoroutine(CoroutineSetTextNumber(ccReward, txtRewardCC, 100, "N0", "+", " CC"));
|
||||||
|
|
||||||
txtBetRC.text = CoinHelper.FormatString(GameManager.MatchRcPrizeCoins) + " RC";
|
txtBetRC.text = GameTutorialManager.tutorialModeEnabled
|
||||||
|
? "0 RC"
|
||||||
|
: CoinHelper.FormatString(GameManager.MatchRcPrizeCoins) + " RC";
|
||||||
|
|
||||||
yield return new WaitForSeconds(1f);
|
yield return new WaitForSeconds(1f);
|
||||||
|
|
||||||
@@ -333,17 +503,14 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GameManager.instance.Leave();
|
LevelLoadManager.instance.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void ShowRematchPanel(){
|
public void ShowRematchPanel(){
|
||||||
SetRematchPendingState(true);
|
SetRematchPendingState(true);
|
||||||
|
ApplyRematchBetSliderLimits();
|
||||||
StartCoroutine(CoroutineShowRematchPanel());
|
StartCoroutine(CoroutineShowRematchPanel());
|
||||||
|
|
||||||
betSlider.maxValue = (LevelLoadManager.myPlayer.rc < LevelLoadManager.opponentPlayer.rc) ? LevelLoadManager.myPlayer.rc : LevelLoadManager.opponentPlayer.rc;
|
|
||||||
betSlider.value = betSlider.maxValue / 2;
|
|
||||||
betSlider.minValue = Mathf.Clamp(betSlider.maxValue /10f,5,10000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator CoroutineShowRematchPanel(){
|
IEnumerator CoroutineShowRematchPanel(){
|
||||||
@@ -368,6 +535,22 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
countdownCoroutine = StartCoroutine(CoroutineCountdown());
|
countdownCoroutine = StartCoroutine(CoroutineCountdown());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnRematchFailed(string errorMessage)
|
||||||
|
{
|
||||||
|
btnRematch.interactable = true;
|
||||||
|
btnLeave.interactable = true;
|
||||||
|
if (rematchWarningLoser != null)
|
||||||
|
rematchWarningLoser.text = "Rematch failed.";
|
||||||
|
StartCoroutine(CoroutineHideRematchPanel());
|
||||||
|
|
||||||
|
if (countdownCoroutine != null)
|
||||||
|
StopCoroutine(countdownCoroutine);
|
||||||
|
countdownCoroutine = StartCoroutine(CoroutineCountdown());
|
||||||
|
|
||||||
|
string body = string.IsNullOrEmpty(errorMessage) ? "Could not start a rematch." : errorMessage;
|
||||||
|
MessageBoxDialog.Show("Rematch failed", body);
|
||||||
|
}
|
||||||
|
|
||||||
IEnumerator CoroutineHideRematchPanel(){
|
IEnumerator CoroutineHideRematchPanel(){
|
||||||
rematchPopup.DOScale(0, 0.2f).SetEase(Ease.InBack);
|
rematchPopup.DOScale(0, 0.2f).SetEase(Ease.InBack);
|
||||||
yield return new WaitForSeconds(0.2f);
|
yield return new WaitForSeconds(0.2f);
|
||||||
@@ -406,6 +589,55 @@ public class GameOverCanvas : MonoBehaviour
|
|||||||
txtMyRCBalance.text = myText;
|
txtMyRCBalance.text = myText;
|
||||||
if (txtOpponentRCBalance != null)
|
if (txtOpponentRCBalance != null)
|
||||||
txtOpponentRCBalance.text = oppText;
|
txtOpponentRCBalance.text = oppText;
|
||||||
|
if (txtRcBalance != null)
|
||||||
|
txtRcBalance.text = loadingPlaceholder ?? (my != null ? CoinHelper.FormatString((int)my.rc) + " RC" : "0.0 RC");
|
||||||
|
|
||||||
|
if (loadingPlaceholder == null)
|
||||||
|
{
|
||||||
|
UpdateRematchAvailability();
|
||||||
|
if (rematchPanel != null && rematchPanel.alpha > 0.01f)
|
||||||
|
ApplyRematchBetSliderLimits();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetRcCoins(MatchmadePlayer player) =>
|
||||||
|
player == null ? 0 : Mathf.Max(0, Mathf.FloorToInt(player.rc));
|
||||||
|
|
||||||
|
bool BothPlayersHaveMinimumRematchRc()
|
||||||
|
{
|
||||||
|
return GetRcCoins(LevelLoadManager.myPlayer) >= MinimumRematchRcCoins
|
||||||
|
&& GetRcCoins(LevelLoadManager.opponentPlayer) >= MinimumRematchRcCoins;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateRematchAvailability()
|
||||||
|
{
|
||||||
|
if (GameTutorialManager.tutorialModeEnabled)
|
||||||
|
{
|
||||||
|
btnRematch.interactable = false;
|
||||||
|
if (rematchWarningLoser != null)
|
||||||
|
rematchWarningLoser.text = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (winningTeam == myTeam)
|
||||||
|
{
|
||||||
|
btnRematch.interactable = false;
|
||||||
|
if (rematchWarningLoser != null)
|
||||||
|
rematchWarningLoser.text = "Only the loser can request a rematch";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BothPlayersHaveMinimumRematchRc())
|
||||||
|
{
|
||||||
|
btnRematch.interactable = false;
|
||||||
|
if (rematchWarningLoser != null)
|
||||||
|
rematchWarningLoser.text = "Not enough RC";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
btnRematch.interactable = true;
|
||||||
|
if (rematchWarningLoser != null)
|
||||||
|
rematchWarningLoser.text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator FetchPlayerRcAndApply(string baseUrl, MatchmadePlayer player)
|
IEnumerator FetchPlayerRcAndApply(string baseUrl, MatchmadePlayer player)
|
||||||
|
|||||||
@@ -0,0 +1,364 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using Mirror;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using DG.Tweening;
|
||||||
|
|
||||||
|
public class GameTutorialManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
public const string CpuOpponentName = "CPU";
|
||||||
|
public const string DefaultGameSceneName = "Game";
|
||||||
|
public const string CpuL10PrefsKey = "practice_cpu_l10";
|
||||||
|
const int L10Window = 10;
|
||||||
|
|
||||||
|
public static bool tutorialModeTrigger = false;
|
||||||
|
public bool isTutorial = false;
|
||||||
|
public static bool tutorialModeEnabled = false;
|
||||||
|
|
||||||
|
public AiBotController aiController;
|
||||||
|
|
||||||
|
[Header("UI")]
|
||||||
|
public CanvasGroup intro_shoot;
|
||||||
|
public CanvasGroup intro_goal;
|
||||||
|
public float introFadeDuration = 0.3f;
|
||||||
|
|
||||||
|
Button _introShootButton;
|
||||||
|
Button _introGoalButton;
|
||||||
|
bool _shootIntroDismissed;
|
||||||
|
bool _goalIntroDismissed;
|
||||||
|
bool _introVisible;
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
#if UNITY_SERVER
|
||||||
|
tutorialModeEnabled = false;
|
||||||
|
tutorialModeTrigger = false;
|
||||||
|
HideIntroImmediate(intro_shoot);
|
||||||
|
HideIntroImmediate(intro_goal);
|
||||||
|
enabled = false;
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tutorialModeEnabled = false;
|
||||||
|
if (isTutorial)
|
||||||
|
tutorialModeTrigger = true;
|
||||||
|
|
||||||
|
if (tutorialModeTrigger)
|
||||||
|
tutorialModeEnabled = true;
|
||||||
|
|
||||||
|
tutorialModeTrigger = false;
|
||||||
|
|
||||||
|
if (!tutorialModeEnabled)
|
||||||
|
{
|
||||||
|
HideIntroImmediate(intro_shoot);
|
||||||
|
HideIntroImmediate(intro_goal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnEnable()
|
||||||
|
{
|
||||||
|
GameManager.OnTeamChanged += OnTeamChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDisable()
|
||||||
|
{
|
||||||
|
GameManager.OnTeamChanged -= OnTeamChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestroy()
|
||||||
|
{
|
||||||
|
if (tutorialModeEnabled)
|
||||||
|
tutorialModeEnabled = false;
|
||||||
|
|
||||||
|
if (intro_shoot != null)
|
||||||
|
DOTween.Kill(intro_shoot);
|
||||||
|
if (intro_goal != null)
|
||||||
|
DOTween.Kill(intro_goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
if (tutorialModeEnabled)
|
||||||
|
{
|
||||||
|
NetManager.StopNetworkingForSceneChange();
|
||||||
|
|
||||||
|
NetworkManager.singleton.networkAddress = "localhost";
|
||||||
|
NetworkManager.singleton.StartHost();
|
||||||
|
|
||||||
|
WireIntroButtons();
|
||||||
|
HideIntroImmediate(intro_goal);
|
||||||
|
HideIntroImmediate(intro_shoot);
|
||||||
|
|
||||||
|
StartCoroutine(SetupAiWhenReady());
|
||||||
|
StartCoroutine(ShowShootIntroWhenReady());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WireIntroButtons()
|
||||||
|
{
|
||||||
|
if (intro_shoot != null)
|
||||||
|
_introShootButton = intro_shoot.GetComponentInChildren<Button>(true);
|
||||||
|
if (intro_goal != null)
|
||||||
|
_introGoalButton = intro_goal.GetComponentInChildren<Button>(true);
|
||||||
|
|
||||||
|
if (_introShootButton != null)
|
||||||
|
_introShootButton.onClick.AddListener(OnShootIntroDismissed);
|
||||||
|
if (_introGoalButton != null)
|
||||||
|
_introGoalButton.onClick.AddListener(OnGoalIntroDismissed);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator ShowShootIntroWhenReady()
|
||||||
|
{
|
||||||
|
while (GameManager.instance == null || !GameManager.instance.gameStarted)
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
while (NetPlayer.localPlayer == null)
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
ShowIntro(intro_shoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTeamChanged(Team team)
|
||||||
|
{
|
||||||
|
if (!tutorialModeEnabled || _introVisible)
|
||||||
|
return;
|
||||||
|
if (team != GetPlayerTeam())
|
||||||
|
return;
|
||||||
|
if (!_shootIntroDismissed || _goalIntroDismissed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ShowIntro(intro_goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnShootIntroDismissed()
|
||||||
|
{
|
||||||
|
if (_shootIntroDismissed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_shootIntroDismissed = true;
|
||||||
|
DismissIntro(intro_shoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnGoalIntroDismissed()
|
||||||
|
{
|
||||||
|
if (_goalIntroDismissed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_goalIntroDismissed = true;
|
||||||
|
DismissIntro(intro_goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowIntro(CanvasGroup group)
|
||||||
|
{
|
||||||
|
if (group == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
EnsureTutorialCanvasVisible(group);
|
||||||
|
DOTween.Kill(group);
|
||||||
|
|
||||||
|
group.gameObject.SetActive(true);
|
||||||
|
group.alpha = 1f;
|
||||||
|
group.interactable = true;
|
||||||
|
group.blocksRaycasts = true;
|
||||||
|
|
||||||
|
_introVisible = true;
|
||||||
|
if (GameManager.instance != null)
|
||||||
|
GameManager.instance.SetTurnTimerPaused(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DismissIntro(CanvasGroup group)
|
||||||
|
{
|
||||||
|
if (group == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
group.interactable = false;
|
||||||
|
group.blocksRaycasts = false;
|
||||||
|
|
||||||
|
DOTween.Kill(group);
|
||||||
|
group.DOFade(0f, introFadeDuration)
|
||||||
|
.SetEase(Ease.InOutSine)
|
||||||
|
.OnComplete(() =>
|
||||||
|
{
|
||||||
|
group.gameObject.SetActive(false);
|
||||||
|
_introVisible = false;
|
||||||
|
if (GameManager.instance != null)
|
||||||
|
GameManager.instance.SetTurnTimerPaused(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HideIntroImmediate(CanvasGroup group)
|
||||||
|
{
|
||||||
|
if (group == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DOTween.Kill(group);
|
||||||
|
group.alpha = 0f;
|
||||||
|
group.interactable = false;
|
||||||
|
group.blocksRaycasts = false;
|
||||||
|
group.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EnsureTutorialCanvasVisible(CanvasGroup group)
|
||||||
|
{
|
||||||
|
Transform root = group.transform.root;
|
||||||
|
if (root.localScale == Vector3.zero)
|
||||||
|
root.localScale = Vector3.one;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Team GetPlayerTeam()
|
||||||
|
{
|
||||||
|
if (NetPlayer.localPlayer != null)
|
||||||
|
return NetPlayer.localPlayer.myTeam;
|
||||||
|
return GameManager.MyTeam;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Call from the main menu before loading the game scene.</summary>
|
||||||
|
public static void PrepareLaunch(MatchmadePlayer myPlayer, MatchmadePlayer cpuPlayer, Team myTeam = Team.Red)
|
||||||
|
{
|
||||||
|
LoginManager.ClearMatchYourTeam();
|
||||||
|
GameManager.MyTeam = myTeam;
|
||||||
|
GameManager.MatchRcPrizeCoins = 0;
|
||||||
|
GameManager.ConfigureDedicatedMatchReporting(0);
|
||||||
|
|
||||||
|
if (myTeam == Team.Red)
|
||||||
|
{
|
||||||
|
GameManager.RedPlayer = myPlayer;
|
||||||
|
GameManager.BluePlayer = cpuPlayer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GameManager.RedPlayer = cpuPlayer;
|
||||||
|
GameManager.BluePlayer = myPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelLoadManager.myPlayer = myPlayer;
|
||||||
|
LevelLoadManager.opponentPlayer = cpuPlayer;
|
||||||
|
if (LevelLoadManager.instance != null)
|
||||||
|
LevelLoadManager.instance.SetupMatchMade(myTeam, myPlayer, cpuPlayer, "0 RC");
|
||||||
|
|
||||||
|
tutorialModeTrigger = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatchmadePlayer CreateCpuOpponent()
|
||||||
|
{
|
||||||
|
GetCpuL10(out int wins, out int losses);
|
||||||
|
return new MatchmadePlayer
|
||||||
|
{
|
||||||
|
Name = CpuOpponentName,
|
||||||
|
l10_wins = wins,
|
||||||
|
l10_losses = losses
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GetCpuL10(out int wins, out int losses)
|
||||||
|
{
|
||||||
|
CountL10(PlayerPrefs.GetString(CpuL10PrefsKey, string.Empty), out wins, out losses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ApplyCpuL10(MatchmadePlayer cpu)
|
||||||
|
{
|
||||||
|
if (cpu == null)
|
||||||
|
return;
|
||||||
|
GetCpuL10(out int wins, out int losses);
|
||||||
|
cpu.l10_wins = wins;
|
||||||
|
cpu.l10_losses = losses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Records one practice result from the CPU's perspective (rolling last 10).
|
||||||
|
/// Cleared on logout because <see cref="LoginManager.Logout"/> calls PlayerPrefs.DeleteAll.
|
||||||
|
/// </summary>
|
||||||
|
public static void RecordCpuL10Result(bool cpuWon)
|
||||||
|
{
|
||||||
|
string history = PlayerPrefs.GetString(CpuL10PrefsKey, string.Empty) ?? string.Empty;
|
||||||
|
history += cpuWon ? 'W' : 'L';
|
||||||
|
if (history.Length > L10Window)
|
||||||
|
history = history.Substring(history.Length - L10Window);
|
||||||
|
PlayerPrefs.SetString(CpuL10PrefsKey, history);
|
||||||
|
PlayerPrefs.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CountL10(string history, out int wins, out int losses)
|
||||||
|
{
|
||||||
|
wins = 0;
|
||||||
|
losses = 0;
|
||||||
|
if (string.IsNullOrEmpty(history))
|
||||||
|
return;
|
||||||
|
for (int i = 0; i < history.Length; i++)
|
||||||
|
{
|
||||||
|
if (history[i] == 'W')
|
||||||
|
wins++;
|
||||||
|
else if (history[i] == 'L')
|
||||||
|
losses++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatchmadePlayer CreateMyPlayerFromUser(UserData user)
|
||||||
|
{
|
||||||
|
return new MatchmadePlayer
|
||||||
|
{
|
||||||
|
Name = user.username,
|
||||||
|
UserId = user.id,
|
||||||
|
rc = user.rc
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator CoFetchPlayerL10(MatchmadePlayer player)
|
||||||
|
{
|
||||||
|
if (player == null || player.UserId <= 0)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
string baseUrl = (GameManager.DedicatedInternalApiBase ?? LoginManager.AuthBaseUrl ?? "").TrimEnd('/');
|
||||||
|
if (string.IsNullOrEmpty(baseUrl))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
string url = baseUrl + "/players/" + player.UserId + "/l10";
|
||||||
|
using (var req = UnityWebRequest.Get(url))
|
||||||
|
{
|
||||||
|
yield return req.SendWebRequest();
|
||||||
|
if (req.result != UnityWebRequest.Result.Success)
|
||||||
|
{
|
||||||
|
Logger.Log("Tutorial L10 fetch failed: " + req.error + " " + url);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp = JsonUtility.FromJson<PlayerL10Response>(req.downloadHandler.text);
|
||||||
|
if (resp != null && resp.ok)
|
||||||
|
{
|
||||||
|
player.l10_wins = resp.l10_wins;
|
||||||
|
player.l10_losses = resp.l10_losses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
class PlayerL10Response
|
||||||
|
{
|
||||||
|
public bool ok;
|
||||||
|
public int player_id;
|
||||||
|
public int l10_wins;
|
||||||
|
public int l10_losses;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator SetupAiWhenReady()
|
||||||
|
{
|
||||||
|
float timeout = 5f;
|
||||||
|
while (NetPlayer.localPlayer == null && timeout > 0f)
|
||||||
|
{
|
||||||
|
timeout -= Time.deltaTime;
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Team playerTeam = NetPlayer.localPlayer != null
|
||||||
|
? NetPlayer.localPlayer.myTeam
|
||||||
|
: GameManager.MyTeam;
|
||||||
|
Team opponentTeam = playerTeam == Team.Red ? Team.Blue : Team.Red;
|
||||||
|
|
||||||
|
if (GameManager.instance.SelectedTeam != playerTeam)
|
||||||
|
GameManager.instance.SwitchTeams();
|
||||||
|
|
||||||
|
aiController.Setup(opponentTeam);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 304204825fc486545a7a9bd7dd232861
|
||||||
@@ -4,9 +4,24 @@ using UnityEngine;
|
|||||||
public class Goal : NetworkBehaviour
|
public class Goal : NetworkBehaviour
|
||||||
{
|
{
|
||||||
public Team team;
|
public Team team;
|
||||||
|
|
||||||
|
public static Goal RedGoal,BlueGoal;
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
if(team == Team.Red){
|
||||||
|
RedGoal = this;
|
||||||
|
}else{
|
||||||
|
BlueGoal = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void OnTriggerEnter2D(Collider2D collision)
|
void OnTriggerEnter2D(Collider2D collision)
|
||||||
{
|
{
|
||||||
if(!isServer){return;}
|
if(!isServer){return;}
|
||||||
|
if(GameManager.instance != null && GameManager.instance.AreResetCollisionsDisabled)
|
||||||
|
return;
|
||||||
|
|
||||||
if(collision.gameObject.CompareTag("Player")){
|
if(collision.gameObject.CompareTag("Player")){
|
||||||
if(GameManager.instance == null || !GameManager.instance.CanProcessGoal){
|
if(GameManager.instance == null || !GameManager.instance.CanProcessGoal){
|
||||||
@@ -17,6 +32,8 @@ public class Goal : NetworkBehaviour
|
|||||||
Debug.Log(ball.name);
|
Debug.Log(ball.name);
|
||||||
GameManager.instance.OnGoal(team);
|
GameManager.instance.OnGoal(team);
|
||||||
}else if(collision.gameObject.CompareTag("Puck")){
|
}else if(collision.gameObject.CompareTag("Puck")){
|
||||||
|
if(GameManager.instance == null || !GameManager.instance.CanProcessGoal)
|
||||||
|
return;
|
||||||
Debug.Log("Puck goal", gameObject);
|
Debug.Log("Puck goal", gameObject);
|
||||||
Puck puck = collision.gameObject.GetComponent<Puck>();
|
Puck puck = collision.gameObject.GetComponent<Puck>();
|
||||||
Debug.Log(puck.name);
|
Debug.Log(puck.name);
|
||||||
@@ -29,6 +46,8 @@ public class Goal : NetworkBehaviour
|
|||||||
if(!isServer){return;}
|
if(!isServer){return;}
|
||||||
|
|
||||||
if(collision.gameObject.CompareTag("Puck")){
|
if(collision.gameObject.CompareTag("Puck")){
|
||||||
|
if(GameManager.instance != null && GameManager.instance.AreResetCollisionsDisabled)
|
||||||
|
return;
|
||||||
Debug.Log("Puck exit goal", gameObject);
|
Debug.Log("Puck exit goal", gameObject);
|
||||||
Puck puck = collision.gameObject.GetComponent<Puck>();
|
Puck puck = collision.gameObject.GetComponent<Puck>();
|
||||||
Debug.Log(puck.name);
|
Debug.Log(puck.name);
|
||||||
|
|||||||
+506
-28
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
@@ -13,13 +14,14 @@ using UnityEngine.UI;
|
|||||||
|
|
||||||
public class LoginManager : MonoBehaviour
|
public class LoginManager : MonoBehaviour
|
||||||
{
|
{
|
||||||
public const string AuthBaseUrl = "https://kickkingsapi.playpoolstudios.com";
|
public const string AuthBaseUrl = "https://kkapi.playpoolstudios.com";
|
||||||
const string PlayerPrefsAuthKey = "WalkInvest.AuthToken";
|
const string PlayerPrefsAuthKey = "WalkInvest.AuthToken";
|
||||||
|
|
||||||
const int MIN_USERNAME_LENGTH = 3;
|
const int MIN_USERNAME_LENGTH = 3;
|
||||||
const int MAX_USERNAME_LENGTH = 16;
|
const int MAX_USERNAME_LENGTH = 16;
|
||||||
const int MIN_PASSWORD_LENGTH = 8;
|
const int MIN_PASSWORD_LENGTH = 8;
|
||||||
const int MAX_PASSWORD_LENGTH = 20;
|
const int MAX_PASSWORD_LENGTH = 20;
|
||||||
|
const int ShortAuthMessageMaxLength = 36;
|
||||||
|
|
||||||
[Header("Login")]
|
[Header("Login")]
|
||||||
[SerializeField] private TMP_InputField usernameInputLogin;
|
[SerializeField] private TMP_InputField usernameInputLogin;
|
||||||
@@ -27,13 +29,36 @@ public class LoginManager : MonoBehaviour
|
|||||||
[SerializeField] private Button btnLogin;
|
[SerializeField] private Button btnLogin;
|
||||||
[Header("Register")]
|
[Header("Register")]
|
||||||
[SerializeField] private TMP_InputField usernameInputRegister;
|
[SerializeField] private TMP_InputField usernameInputRegister;
|
||||||
|
[SerializeField] private TMP_InputField emailInputRegister;
|
||||||
|
|
||||||
[SerializeField] private TMP_InputField passwordInputRegister;
|
[SerializeField] private TMP_InputField passwordInputRegister;
|
||||||
[SerializeField] private Button btnRegister;
|
[SerializeField] private Button btnRegister;
|
||||||
|
|
||||||
[Header("UI")]
|
[Header("Server messages (GET /table_settings, no auth)")]
|
||||||
[SerializeField] private TMP_Text error_txt;
|
[Header("Update")]
|
||||||
|
[SerializeField] private GameObject serverUpdatePanel;
|
||||||
|
[SerializeField] private Button btnServerUpdate;
|
||||||
|
[Tooltip("Opened when the user taps Update on the forced-update panel (e.g. Telegram channel).")]
|
||||||
|
public string telegramUpdateChannelUrl = "";
|
||||||
|
|
||||||
|
[Header("Error")]
|
||||||
|
[SerializeField] private GameObject errorPanel;
|
||||||
|
[SerializeField] private TMP_Text serverErrorMessageTxt;
|
||||||
|
[SerializeField] private Button btnErrorClose;
|
||||||
|
|
||||||
|
[Header("Warning")]
|
||||||
|
[SerializeField] private GameObject warningPanel;
|
||||||
|
[SerializeField] private TMP_Text serverWarningMessageTxt;
|
||||||
|
[SerializeField] private Button btnWarningClose;
|
||||||
|
|
||||||
|
const float KeepaliveIntervalSeconds = 10f;
|
||||||
|
|
||||||
bool _authInProgress;
|
bool _authInProgress;
|
||||||
|
Coroutine _keepaliveRoutine;
|
||||||
|
bool _appPaused;
|
||||||
|
bool _appFocused = true;
|
||||||
|
bool _handlingKeepaliveUnauthorized;
|
||||||
|
string _pendingAuthMessage;
|
||||||
|
|
||||||
public static string AuthToken { get; private set; }
|
public static string AuthToken { get; private set; }
|
||||||
|
|
||||||
@@ -84,27 +109,59 @@ public class LoginManager : MonoBehaviour
|
|||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
error_txt.text = "";
|
HideServerMessagePanels();
|
||||||
AuthToken = PlayerPrefs.GetString(PlayerPrefsAuthKey, "");
|
|
||||||
|
|
||||||
if(instance !=null){
|
if (instance != null && instance != this)
|
||||||
Destroy(gameObject);
|
{
|
||||||
return;
|
instance.StopKeepalive();
|
||||||
|
SceneManager.sceneLoaded -= instance.OnSceneLoaded;
|
||||||
|
Destroy(instance.gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
instance = this;
|
instance = this;
|
||||||
DontDestroyOnLoad(gameObject);
|
DontDestroyOnLoad(gameObject);
|
||||||
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||||
|
|
||||||
|
AuthToken = PlayerPrefs.GetString(PlayerPrefsAuthKey, "");
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(AuthToken))
|
if (string.IsNullOrEmpty(AuthToken))
|
||||||
ClearLocalUserProfile();
|
ClearLocalUserProfile();
|
||||||
|
#if !UNITY_EDITOR
|
||||||
|
else
|
||||||
|
StartKeepalive();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
btnLogin.onClick.AddListener(OnLogin);
|
btnLogin.onClick.AddListener(OnLogin);
|
||||||
btnRegister.onClick.AddListener(OnRegister);
|
btnRegister.onClick.AddListener(OnRegister);
|
||||||
|
if (btnServerUpdate != null)
|
||||||
|
btnServerUpdate.onClick.AddListener(OnServerUpdateClick);
|
||||||
|
if (btnErrorClose != null)
|
||||||
|
btnErrorClose.onClick.AddListener(OnServerErrorCloseClick);
|
||||||
|
|
||||||
|
SetBusy(true);
|
||||||
|
StartCoroutine(StartupSequenceCoroutine());
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator StartupSequenceCoroutine()
|
||||||
|
{
|
||||||
|
bool mayContinue = false;
|
||||||
|
yield return StartCoroutine(CoApplyTableSettingsGate(r => mayContinue = r));
|
||||||
|
|
||||||
|
if (!mayContinue)
|
||||||
|
{
|
||||||
|
// Blocked by update or error panel — keep login UI disabled, but hold the session.
|
||||||
|
if (!string.IsNullOrEmpty(AuthToken))
|
||||||
|
StartKeepalive();
|
||||||
|
SetBusy(false);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
// Dev: log in with clone-aware credentials instead of resuming a saved session.
|
// Dev: log in with clone-aware credentials instead of resuming a saved session.
|
||||||
|
StopKeepalive();
|
||||||
AuthToken = "";
|
AuthToken = "";
|
||||||
PlayerPrefs.DeleteKey(PlayerPrefsAuthKey);
|
PlayerPrefs.DeleteKey(PlayerPrefsAuthKey);
|
||||||
PlayerPrefs.Save();
|
PlayerPrefs.Save();
|
||||||
@@ -117,12 +174,15 @@ public class LoginManager : MonoBehaviour
|
|||||||
|
|
||||||
string loginJson = "{\"username\":\"" + EscapeJsonString(devUser) + "\",\"password\":\"" + EscapeJsonString(devPass) + "\"}";
|
string loginJson = "{\"username\":\"" + EscapeJsonString(devUser) + "\",\"password\":\"" + EscapeJsonString(devPass) + "\"}";
|
||||||
// StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/login", loginJson, "Signed in."));
|
// StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/login", loginJson, "Signed in."));
|
||||||
|
SetBusy(false);
|
||||||
#else
|
#else
|
||||||
if (!string.IsNullOrEmpty(AuthToken))
|
if (!string.IsNullOrEmpty(AuthToken))
|
||||||
{
|
{
|
||||||
SetBusy(true);
|
StartKeepalive();
|
||||||
StartCoroutine(ResumeSessionCoroutine());
|
yield return StartCoroutine(ResumeSessionCoroutine());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
SetBusy(false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,8 +220,7 @@ public class LoginManager : MonoBehaviour
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
InvalidateStoredSession();
|
InvalidateStoredSession();
|
||||||
if (error_txt != null)
|
ShowAuthMessage(string.IsNullOrEmpty(errMsg) ? "Session expired. Please sign in again." : errMsg);
|
||||||
error_txt.text = string.IsNullOrEmpty(errMsg) ? "Session expired. Please sign in again." : errMsg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SetBusy(false);
|
SetBusy(false);
|
||||||
@@ -176,6 +235,8 @@ public class LoginManager : MonoBehaviour
|
|||||||
/// <summary>Removes auth token from memory and disk and clears cached profile (login screen recovery).</summary>
|
/// <summary>Removes auth token from memory and disk and clears cached profile (login screen recovery).</summary>
|
||||||
static void InvalidateStoredSession()
|
static void InvalidateStoredSession()
|
||||||
{
|
{
|
||||||
|
if (instance != null)
|
||||||
|
instance.StopKeepalive();
|
||||||
AuthToken = "";
|
AuthToken = "";
|
||||||
ClearLocalUserProfile();
|
ClearLocalUserProfile();
|
||||||
ClearMatchYourTeam();
|
ClearMatchYourTeam();
|
||||||
@@ -183,6 +244,31 @@ public class LoginManager : MonoBehaviour
|
|||||||
PlayerPrefs.Save();
|
PlayerPrefs.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Clears session state and all PlayerPrefs (full logout).</summary>
|
||||||
|
public static void Logout()
|
||||||
|
{
|
||||||
|
if (instance != null)
|
||||||
|
instance.StopKeepalive();
|
||||||
|
AuthToken = "";
|
||||||
|
ClearLocalUserProfile();
|
||||||
|
ClearMatchYourTeam();
|
||||||
|
OnUserDataUpdated = null;
|
||||||
|
Cupid.ClearSession();
|
||||||
|
CupidLobby.ClearUsername();
|
||||||
|
GameManager.ClearMatchSession();
|
||||||
|
NetPlayer.RedPlayer = null;
|
||||||
|
NetPlayer.BluePlayer = null;
|
||||||
|
PlayerPrefs.DeleteAll();
|
||||||
|
PlayerPrefs.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Full logout, then reload the login scene so a fresh <see cref="LoginManager"/> can bind UI.</summary>
|
||||||
|
public static void LogoutAndReturnToLogin()
|
||||||
|
{
|
||||||
|
Logout();
|
||||||
|
SceneManager.LoadScene(0);
|
||||||
|
}
|
||||||
|
|
||||||
static void ProceedToMainMenu()
|
static void ProceedToMainMenu()
|
||||||
{
|
{
|
||||||
if (LevelLoadManager.instance != null)
|
if (LevelLoadManager.instance != null)
|
||||||
@@ -198,27 +284,62 @@ public class LoginManager : MonoBehaviour
|
|||||||
if (btnRegister != null) btnRegister.interactable = !busy;
|
if (btnRegister != null) btnRegister.interactable = !busy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShowAuthMessage(string message)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(message))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (MessageBoxDialog.instance == null)
|
||||||
|
{
|
||||||
|
_pendingAuthMessage = message;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingAuthMessage = null;
|
||||||
|
if (message.Length <= ShortAuthMessageMaxLength)
|
||||||
|
MessageBoxDialog.instance.ShowMessageBox(message);
|
||||||
|
else
|
||||||
|
MessageBoxDialog.instance.ShowMessageBox("Error", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HideAuthMessage()
|
||||||
|
{
|
||||||
|
if (MessageBoxDialog.instance != null)
|
||||||
|
MessageBoxDialog.instance.HideMessageBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_pendingAuthMessage) || MessageBoxDialog.instance == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string message = _pendingAuthMessage;
|
||||||
|
_pendingAuthMessage = null;
|
||||||
|
ShowAuthMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
void OnLogin()
|
void OnLogin()
|
||||||
{
|
{
|
||||||
if (_authInProgress) return;
|
if (_authInProgress) return;
|
||||||
|
if (_keepaliveRoutine != null && !string.IsNullOrEmpty(AuthToken)) return;
|
||||||
|
|
||||||
string username = usernameInputLogin.text;
|
string username = usernameInputLogin.text;
|
||||||
string password = passwordInputLogin.text;
|
string password = passwordInputLogin.text;
|
||||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
||||||
{
|
{
|
||||||
error_txt.text = "Please fill in all fields";
|
ShowAuthMessage("Please fill in all fields");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username.Length < MIN_USERNAME_LENGTH || username.Length > MAX_USERNAME_LENGTH)
|
if (username.Length < MIN_USERNAME_LENGTH || username.Length > MAX_USERNAME_LENGTH)
|
||||||
{
|
{
|
||||||
error_txt.text = "Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters";
|
ShowAuthMessage("Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password.Length < MIN_PASSWORD_LENGTH || password.Length > MAX_PASSWORD_LENGTH)
|
if (password.Length < MIN_PASSWORD_LENGTH || password.Length > MAX_PASSWORD_LENGTH)
|
||||||
{
|
{
|
||||||
error_txt.text = "Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters";
|
ShowAuthMessage("Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,32 +350,66 @@ public class LoginManager : MonoBehaviour
|
|||||||
void OnRegister()
|
void OnRegister()
|
||||||
{
|
{
|
||||||
if (_authInProgress) return;
|
if (_authInProgress) return;
|
||||||
|
if (_keepaliveRoutine != null && !string.IsNullOrEmpty(AuthToken)) return;
|
||||||
|
|
||||||
string username = usernameInputRegister.text;
|
string username = usernameInputRegister.text;
|
||||||
|
string email = emailInputRegister != null ? emailInputRegister.text : "";
|
||||||
string password = passwordInputRegister.text;
|
string password = passwordInputRegister.text;
|
||||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
|
||||||
{
|
{
|
||||||
error_txt.text = "Please fill in all fields";
|
ShowAuthMessage("Please fill in all fields");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
email = email.Trim().ToLowerInvariant();
|
||||||
|
if (!IsValidEmailFormat(email))
|
||||||
|
{
|
||||||
|
ShowAuthMessage("Please enter a valid email address");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username.Length < MIN_USERNAME_LENGTH || username.Length > MAX_USERNAME_LENGTH)
|
if (username.Length < MIN_USERNAME_LENGTH || username.Length > MAX_USERNAME_LENGTH)
|
||||||
{
|
{
|
||||||
error_txt.text = "Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters";
|
ShowAuthMessage("Username must be between " + MIN_USERNAME_LENGTH + " and " + MAX_USERNAME_LENGTH + " characters");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password.Length < MIN_PASSWORD_LENGTH || password.Length > MAX_PASSWORD_LENGTH)
|
if (password.Length < MIN_PASSWORD_LENGTH || password.Length > MAX_PASSWORD_LENGTH)
|
||||||
{
|
{
|
||||||
error_txt.text = "Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters";
|
ShowAuthMessage("Password must be between " + MIN_PASSWORD_LENGTH + " and " + MAX_PASSWORD_LENGTH + " characters");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server register endpoint accepts username + password only (email field is optional UI-only).
|
string json = "{\"username\":\"" + EscapeJsonString(username) + "\",\"email\":\"" + EscapeJsonString(email) + "\",\"password\":\"" + EscapeJsonString(password) + "\"}";
|
||||||
string json = "{\"username\":\"" + EscapeJsonString(username) + "\",\"password\":\"" + EscapeJsonString(password) + "\"}";
|
|
||||||
StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/register", json, "Account created."));
|
StartCoroutine(AuthPostCoroutine(AuthBaseUrl + "/auth/register", json, "Account created."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsValidEmailFormat(string email)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(email))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int at = email.IndexOf('@');
|
||||||
|
if (at <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (email.IndexOf('@', at + 1) >= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string domain = email.Substring(at + 1);
|
||||||
|
int dot = domain.LastIndexOf('.');
|
||||||
|
if (dot <= 0 || dot >= domain.Length - 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < email.Length; i++)
|
||||||
|
{
|
||||||
|
if (char.IsWhiteSpace(email[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static string EscapeJsonString(string s)
|
static string EscapeJsonString(string s)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(s)) return s;
|
if (string.IsNullOrEmpty(s)) return s;
|
||||||
@@ -264,7 +419,7 @@ public class LoginManager : MonoBehaviour
|
|||||||
IEnumerator AuthPostCoroutine(string url, string jsonBody, string successMessage)
|
IEnumerator AuthPostCoroutine(string url, string jsonBody, string successMessage)
|
||||||
{
|
{
|
||||||
SetBusy(true);
|
SetBusy(true);
|
||||||
error_txt.text = "";
|
HideAuthMessage();
|
||||||
ClearLocalUserProfile();
|
ClearLocalUserProfile();
|
||||||
|
|
||||||
using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
|
using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
|
||||||
@@ -281,14 +436,14 @@ public class LoginManager : MonoBehaviour
|
|||||||
if (request.result != UnityWebRequest.Result.Success &&
|
if (request.result != UnityWebRequest.Result.Success &&
|
||||||
request.result != UnityWebRequest.Result.ProtocolError)
|
request.result != UnityWebRequest.Result.ProtocolError)
|
||||||
{
|
{
|
||||||
error_txt.text = string.IsNullOrEmpty(request.error) ? "Network error" : request.error;
|
ShowAuthMessage("Network Error, Please try again.");
|
||||||
SetBusy(false);
|
SetBusy(false);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(text))
|
if (string.IsNullOrEmpty(text))
|
||||||
{
|
{
|
||||||
error_txt.text = "Empty response from server";
|
ShowAuthMessage("Empty response from server");
|
||||||
SetBusy(false);
|
SetBusy(false);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
@@ -300,6 +455,7 @@ public class LoginManager : MonoBehaviour
|
|||||||
AuthToken = resp.token;
|
AuthToken = resp.token;
|
||||||
PlayerPrefs.SetString(PlayerPrefsAuthKey, resp.token);
|
PlayerPrefs.SetString(PlayerPrefsAuthKey, resp.token);
|
||||||
PlayerPrefs.Save();
|
PlayerPrefs.Save();
|
||||||
|
StartKeepalive();
|
||||||
|
|
||||||
bool profileOk = false;
|
bool profileOk = false;
|
||||||
string profileErr = null;
|
string profileErr = null;
|
||||||
@@ -311,20 +467,163 @@ public class LoginManager : MonoBehaviour
|
|||||||
|
|
||||||
if (profileOk)
|
if (profileOk)
|
||||||
ProceedToMainMenu();
|
ProceedToMainMenu();
|
||||||
else if (error_txt != null)
|
else
|
||||||
error_txt.text = string.IsNullOrEmpty(profileErr)
|
ShowAuthMessage(string.IsNullOrEmpty(profileErr)
|
||||||
? "Signed in but could not load your profile. Check your connection and try again."
|
? "Signed in but could not load your profile. Check your connection and try again."
|
||||||
: profileErr;
|
: profileErr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error_txt.text = string.IsNullOrEmpty(resp.error) ? "Request failed" : resp.error;
|
ShowAuthMessage("Login failed. Please try again.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetBusy(false);
|
SetBusy(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HideServerMessagePanels()
|
||||||
|
{
|
||||||
|
if (serverUpdatePanel != null) serverUpdatePanel.SetActive(false);
|
||||||
|
if (errorPanel != null) errorPanel.SetActive(false);
|
||||||
|
if (warningPanel != null) warningPanel.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnServerUpdateClick()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(telegramUpdateChannelUrl))
|
||||||
|
Application.OpenURL(telegramUpdateChannelUrl.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnServerErrorCloseClick()
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
EditorApplication.ExitPlaymode();
|
||||||
|
#else
|
||||||
|
Application.Quit();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GET <c>/table_settings</c> (public) and applies version / error / warning gates.
|
||||||
|
/// Callback receives <c>true</c> when login may continue to the main menu.
|
||||||
|
/// </summary>
|
||||||
|
IEnumerator CoApplyTableSettingsGate(Action<bool> onComplete)
|
||||||
|
{
|
||||||
|
CupidTableSettings table = null;
|
||||||
|
yield return StartCoroutine(FetchTableSettingsCoroutine(t => table = t));
|
||||||
|
|
||||||
|
if (table == null)
|
||||||
|
{
|
||||||
|
onComplete?.Invoke(true);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsClientBelowRequiredVersion(table.version))
|
||||||
|
{
|
||||||
|
if (serverUpdatePanel != null)
|
||||||
|
serverUpdatePanel.SetActive(true);
|
||||||
|
onComplete?.Invoke(false);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(table.error_msg))
|
||||||
|
{
|
||||||
|
if (serverErrorMessageTxt != null)
|
||||||
|
serverErrorMessageTxt.text = table.error_msg.Trim();
|
||||||
|
if (errorPanel != null)
|
||||||
|
errorPanel.SetActive(true);
|
||||||
|
onComplete?.Invoke(false);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(table.warning_msg))
|
||||||
|
yield return CoWaitForWarningDismiss(table.warning_msg.Trim());
|
||||||
|
|
||||||
|
onComplete?.Invoke(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator FetchTableSettingsCoroutine(Action<CupidTableSettings> onResult)
|
||||||
|
{
|
||||||
|
string url = AuthBaseUrl + "/table_settings";
|
||||||
|
using (var request = UnityWebRequest.Get(url))
|
||||||
|
{
|
||||||
|
request.downloadHandler = new DownloadHandlerBuffer();
|
||||||
|
yield return request.SendWebRequest();
|
||||||
|
|
||||||
|
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||||
|
|
||||||
|
if (request.result != UnityWebRequest.Result.Success &&
|
||||||
|
request.result != UnityWebRequest.Result.ProtocolError)
|
||||||
|
{
|
||||||
|
onResult?.Invoke(null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.responseCode < 200 || request.responseCode >= 300 || string.IsNullOrWhiteSpace(text))
|
||||||
|
{
|
||||||
|
onResult?.Invoke(null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CupidTableSettings parsed = JsonUtility.FromJson<CupidTableSettings>(text);
|
||||||
|
onResult?.Invoke(parsed);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Log("table_settings JSON error: " + e.Message + " body=" + text);
|
||||||
|
onResult?.Invoke(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsClientBelowRequiredVersion(string requiredVersion)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(requiredVersion))
|
||||||
|
return false;
|
||||||
|
if (!TryParseVersionFloat(requiredVersion, out float required))
|
||||||
|
return false;
|
||||||
|
if (!TryParseVersionFloat(Application.version, out float client))
|
||||||
|
return false;
|
||||||
|
return client < required;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryParseVersionFloat(string version, out float value)
|
||||||
|
{
|
||||||
|
value = 0f;
|
||||||
|
if (string.IsNullOrWhiteSpace(version))
|
||||||
|
return false;
|
||||||
|
return float.TryParse(version.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator CoWaitForWarningDismiss(string message)
|
||||||
|
{
|
||||||
|
if (serverWarningMessageTxt != null)
|
||||||
|
serverWarningMessageTxt.text = message;
|
||||||
|
if (warningPanel != null)
|
||||||
|
warningPanel.SetActive(true);
|
||||||
|
|
||||||
|
bool dismissed = false;
|
||||||
|
void OnDismiss()
|
||||||
|
{
|
||||||
|
dismissed = true;
|
||||||
|
if (warningPanel != null)
|
||||||
|
warningPanel.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnWarningClose != null)
|
||||||
|
{
|
||||||
|
btnWarningClose.onClick.RemoveListener(OnDismiss);
|
||||||
|
btnWarningClose.onClick.AddListener(OnDismiss);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return new WaitUntil(() => dismissed);
|
||||||
|
|
||||||
|
if (btnWarningClose != null)
|
||||||
|
btnWarningClose.onClick.RemoveListener(OnDismiss);
|
||||||
|
}
|
||||||
|
|
||||||
static void ApplyUserData(UserData u)
|
static void ApplyUserData(UserData u)
|
||||||
{
|
{
|
||||||
_currentUser = u;
|
_currentUser = u;
|
||||||
@@ -397,6 +696,12 @@ public class LoginManager : MonoBehaviour
|
|||||||
StartCoroutine(PurchaseRcCoroutine(packId, onComplete));
|
StartCoroutine(PurchaseRcCoroutine(packId, onComplete));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>POST /auth/ping-report with <c>{"match_id":123,"ping":42}</c>. Callback (success, errorOrNull, reportOrNull).</summary>
|
||||||
|
public void ReportPing(int matchId, int ping, Action<bool, string, PingReportData> onComplete)
|
||||||
|
{
|
||||||
|
StartCoroutine(ReportPingCoroutine(matchId, ping, onComplete));
|
||||||
|
}
|
||||||
|
|
||||||
IEnumerator PurchaseRcCoroutine(string packId, Action<bool, string> onComplete)
|
IEnumerator PurchaseRcCoroutine(string packId, Action<bool, string> onComplete)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(AuthToken))
|
if (string.IsNullOrEmpty(AuthToken))
|
||||||
@@ -470,6 +775,59 @@ public class LoginManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IEnumerator ReportPingCoroutine(int matchId, int ping, Action<bool, string, PingReportData> onComplete)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(AuthToken))
|
||||||
|
{
|
||||||
|
onComplete?.Invoke(false, "Not signed in", null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string json = "{\"match_id\":" + matchId + ",\"ping\":" + ping + "}";
|
||||||
|
string url = AuthBaseUrl + "/auth/ping-report";
|
||||||
|
|
||||||
|
using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
|
||||||
|
{
|
||||||
|
byte[] bodyRaw = Encoding.UTF8.GetBytes(json);
|
||||||
|
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
|
||||||
|
request.downloadHandler = new DownloadHandlerBuffer();
|
||||||
|
request.SetRequestHeader("Content-Type", "application/json");
|
||||||
|
request.SetRequestHeader("Authorization", "Bearer " + AuthToken);
|
||||||
|
|
||||||
|
yield return request.SendWebRequest();
|
||||||
|
|
||||||
|
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||||
|
|
||||||
|
if (request.result != UnityWebRequest.Result.Success &&
|
||||||
|
request.result != UnityWebRequest.Result.ProtocolError)
|
||||||
|
{
|
||||||
|
onComplete?.Invoke(false, string.IsNullOrEmpty(request.error) ? "Network error" : request.error, null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.responseCode < 200 || request.responseCode >= 300)
|
||||||
|
{
|
||||||
|
onComplete?.Invoke(false, string.IsNullOrEmpty(text) ? "Request failed (" + request.responseCode + ")" : text, null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
{
|
||||||
|
onComplete?.Invoke(false, "Empty response from server", null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PingReportApiResponse resp = JsonUtility.FromJson<PingReportApiResponse>(text);
|
||||||
|
if (resp != null && resp.ok && resp.report != null)
|
||||||
|
onComplete?.Invoke(true, null, resp.report);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string err = resp != null && !string.IsNullOrEmpty(resp.error) ? resp.error : "Ping report failed";
|
||||||
|
onComplete?.Invoke(false, err, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IEnumerator FetchUserDataCoroutine(Action<bool, string, UserData> onComplete, bool applyToStatic)
|
IEnumerator FetchUserDataCoroutine(Action<bool, string, UserData> onComplete, bool applyToStatic)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(AuthToken))
|
if (string.IsNullOrEmpty(AuthToken))
|
||||||
@@ -522,4 +880,124 @@ public class LoginManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsAppInForeground => !_appPaused && _appFocused;
|
||||||
|
|
||||||
|
void OnApplicationPause(bool paused)
|
||||||
|
{
|
||||||
|
_appPaused = paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnApplicationFocus(bool hasFocus)
|
||||||
|
{
|
||||||
|
_appFocused = hasFocus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestroy()
|
||||||
|
{
|
||||||
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||||
|
StopKeepalive();
|
||||||
|
if (instance == this)
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartKeepalive()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(AuthToken)) return;
|
||||||
|
if (_keepaliveRoutine != null) return;
|
||||||
|
_handlingKeepaliveUnauthorized = false;
|
||||||
|
_keepaliveRoutine = StartCoroutine(KeepaliveLoopCoroutine());
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopKeepalive()
|
||||||
|
{
|
||||||
|
if (_keepaliveRoutine == null) return;
|
||||||
|
StopCoroutine(_keepaliveRoutine);
|
||||||
|
_keepaliveRoutine = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator KeepaliveLoopCoroutine()
|
||||||
|
{
|
||||||
|
while (!string.IsNullOrEmpty(AuthToken))
|
||||||
|
{
|
||||||
|
while (!IsAppInForeground && !string.IsNullOrEmpty(AuthToken))
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(AuthToken))
|
||||||
|
break;
|
||||||
|
|
||||||
|
yield return KeepalivePingCoroutine();
|
||||||
|
if (string.IsNullOrEmpty(AuthToken))
|
||||||
|
break;
|
||||||
|
|
||||||
|
float elapsed = 0f;
|
||||||
|
while (elapsed < KeepaliveIntervalSeconds && !string.IsNullOrEmpty(AuthToken))
|
||||||
|
{
|
||||||
|
if (!IsAppInForeground)
|
||||||
|
{
|
||||||
|
while (!IsAppInForeground && !string.IsNullOrEmpty(AuthToken))
|
||||||
|
yield return null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed += Time.unscaledDeltaTime;
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_keepaliveRoutine = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator KeepalivePingCoroutine()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(AuthToken))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
string url = AuthBaseUrl + "/auth/keepalive";
|
||||||
|
using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
|
||||||
|
{
|
||||||
|
request.uploadHandler = new UploadHandlerRaw(Array.Empty<byte>());
|
||||||
|
request.downloadHandler = new DownloadHandlerBuffer();
|
||||||
|
request.SetRequestHeader("Content-Type", "application/json");
|
||||||
|
request.SetRequestHeader("Authorization", "Bearer " + AuthToken);
|
||||||
|
|
||||||
|
yield return request.SendWebRequest();
|
||||||
|
|
||||||
|
if (request.responseCode == 401)
|
||||||
|
{
|
||||||
|
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||||
|
string err = "Session expired. Please sign in again.";
|
||||||
|
if (!string.IsNullOrEmpty(text))
|
||||||
|
{
|
||||||
|
AuthApiResponse resp = JsonUtility.FromJson<AuthApiResponse>(text);
|
||||||
|
if (resp != null && !string.IsNullOrEmpty(resp.error))
|
||||||
|
err = resp.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleKeepaliveUnauthorized(err);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.result != UnityWebRequest.Result.Success &&
|
||||||
|
request.result != UnityWebRequest.Result.ProtocolError)
|
||||||
|
{
|
||||||
|
Logger.Log("auth/keepalive network error: " + (string.IsNullOrEmpty(request.error) ? "Network error" : request.error));
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.responseCode < 200 || request.responseCode >= 300)
|
||||||
|
Logger.Log("auth/keepalive failed: HTTP " + request.responseCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleKeepaliveUnauthorized(string message)
|
||||||
|
{
|
||||||
|
if (_handlingKeepaliveUnauthorized) return;
|
||||||
|
_handlingKeepaliveUnauthorized = true;
|
||||||
|
|
||||||
|
SetBusy(false);
|
||||||
|
ShowAuthMessage(string.IsNullOrEmpty(message) ? "Session expired. Please sign in again." : message);
|
||||||
|
LogoutAndReturnToLogin();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,15 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
public GameObject gameModesScreen;
|
public GameObject gameModesScreen;
|
||||||
public GameObject dummyBuyScreen;
|
public GameObject dummyBuyScreen;
|
||||||
public GameObject withdrawalScreen;
|
public GameObject withdrawalScreen;
|
||||||
|
public GameObject settingsScreen;
|
||||||
|
|
||||||
[Header("Play")]
|
[Header("Play")]
|
||||||
public Button btnPlay;
|
public Button btnPlay;
|
||||||
|
public Button btnTutorial;
|
||||||
public TMP_Text rcPlayText;
|
public TMP_Text rcPlayText;
|
||||||
public TMP_Text entryFeeTxt;
|
public TMP_Text entryFeeTxt;
|
||||||
public TMP_Text rcPrizeTxt;
|
public TMP_Text rcPrizeTxt;
|
||||||
|
[SerializeField] string tutorialGameScene = GameTutorialManager.DefaultGameSceneName;
|
||||||
|
|
||||||
[Header("Dummy Buy")]
|
[Header("Dummy Buy")]
|
||||||
public Button btnBuyUsd5;
|
public Button btnBuyUsd5;
|
||||||
@@ -43,6 +47,14 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
public TMP_Text txtWithdrawStatus;
|
public TMP_Text txtWithdrawStatus;
|
||||||
private bool isCoinBuyInProgress;
|
private bool isCoinBuyInProgress;
|
||||||
|
|
||||||
|
[Header("Settings")]
|
||||||
|
public ProfileInfoFetcher profileInfoFetcher;
|
||||||
|
public TMP_Text txtUsername;
|
||||||
|
public TMP_Text txtEmail;
|
||||||
|
public TMP_Text txtAge,txtMatches,txtWinRatio;
|
||||||
|
public TMP_Text txtId;
|
||||||
|
public Button btnLogout;
|
||||||
|
|
||||||
[Header("Audio")]
|
[Header("Audio")]
|
||||||
public ToggleButton toggleSfx;
|
public ToggleButton toggleSfx;
|
||||||
public ToggleButton toggleMusic;
|
public ToggleButton toggleMusic;
|
||||||
@@ -73,6 +85,7 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
OnUserUpdated(user);
|
OnUserUpdated(user);
|
||||||
|
RefreshSettingsProfile();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -83,6 +96,8 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
RefreshSettingsProfile();
|
||||||
|
|
||||||
btnBuyUsd5.onClick.AddListener(() => OnBuy(RcPack.Usd5));
|
btnBuyUsd5.onClick.AddListener(() => OnBuy(RcPack.Usd5));
|
||||||
btnBuyUsd20.onClick.AddListener(() => OnBuy(RcPack.Usd20));
|
btnBuyUsd20.onClick.AddListener(() => OnBuy(RcPack.Usd20));
|
||||||
btnBuyUsd50.onClick.AddListener(() => OnBuy(RcPack.Usd50));
|
btnBuyUsd50.onClick.AddListener(() => OnBuy(RcPack.Usd50));
|
||||||
@@ -93,12 +108,18 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
inputCryptoAddress.onValueChanged.AddListener(OnCryptoAddressChanged);
|
inputCryptoAddress.onValueChanged.AddListener(OnCryptoAddressChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateWithdrawalStatus();
|
if (txtWithdrawStatus != null)
|
||||||
|
txtWithdrawStatus.gameObject.SetActive(false);
|
||||||
|
|
||||||
|
if (btnWithdrawal != null)
|
||||||
|
btnWithdrawal.onClick.AddListener(OnWithdrawalPressed);
|
||||||
|
|
||||||
if (btnPaste != null)
|
if (btnPaste != null)
|
||||||
btnPaste.onClick.AddListener(OnPasteFromClipboard);
|
btnPaste.onClick.AddListener(OnPasteFromClipboard);
|
||||||
|
|
||||||
btnPlay.onClick.AddListener(OnPlay);
|
btnPlay.onClick.AddListener(OnPlay);
|
||||||
|
if (btnTutorial != null && btnTutorial != btnPlay)
|
||||||
|
btnTutorial.onClick.AddListener(OnPlayTutorial);
|
||||||
|
|
||||||
toggleSfx.onToggleValueChanged.AddListener(OnToggleSfx);
|
toggleSfx.onToggleValueChanged.AddListener(OnToggleSfx);
|
||||||
toggleMusic.onToggleValueChanged.AddListener(OnToggleMusic);
|
toggleMusic.onToggleValueChanged.AddListener(OnToggleMusic);
|
||||||
@@ -106,6 +127,9 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
toggleSfx.SetIsOn(AudioManager.instance.IsSFXOn());
|
toggleSfx.SetIsOn(AudioManager.instance.IsSFXOn());
|
||||||
toggleMusic.SetIsOn(AudioManager.instance.IsMusicOn());
|
toggleMusic.SetIsOn(AudioManager.instance.IsMusicOn());
|
||||||
|
|
||||||
|
if (btnLogout != null)
|
||||||
|
btnLogout.onClick.AddListener(OnLogout);
|
||||||
|
|
||||||
if (!LevelLoadManager.BlocksMainMenuSettingsBootstrap)
|
if (!LevelLoadManager.BlocksMainMenuSettingsBootstrap)
|
||||||
StartCoroutine(CoBootstrapSettingsWithoutLoader());
|
StartCoroutine(CoBootstrapSettingsWithoutLoader());
|
||||||
}
|
}
|
||||||
@@ -114,6 +138,35 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
LoginManager.OnUserDataUpdated -= OnUserUpdated;
|
LoginManager.OnUserDataUpdated -= OnUserUpdated;
|
||||||
toggleSfx.onToggleValueChanged.RemoveListener(OnToggleSfx);
|
toggleSfx.onToggleValueChanged.RemoveListener(OnToggleSfx);
|
||||||
toggleMusic.onToggleValueChanged.RemoveListener(OnToggleMusic);
|
toggleMusic.onToggleValueChanged.RemoveListener(OnToggleMusic);
|
||||||
|
if (btnTutorial != null && btnTutorial != btnPlay)
|
||||||
|
btnTutorial.onClick.RemoveListener(OnPlayTutorial);
|
||||||
|
if (btnLogout != null)
|
||||||
|
btnLogout.onClick.RemoveListener(OnLogout);
|
||||||
|
if (btnWithdrawal != null)
|
||||||
|
btnWithdrawal.onClick.RemoveListener(OnWithdrawalPressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnLogout()
|
||||||
|
{
|
||||||
|
if (MessageBoxDialog.IsAvailable)
|
||||||
|
{
|
||||||
|
MessageBoxDialog.Show(
|
||||||
|
"Log out?",
|
||||||
|
"You will need to sign in again to play.",
|
||||||
|
confirmed =>
|
||||||
|
{
|
||||||
|
if (confirmed)
|
||||||
|
ConfirmLogout();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmLogout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfirmLogout()
|
||||||
|
{
|
||||||
|
LoginManager.LogoutAndReturnToLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnToggleSfx(bool isOn)
|
void OnToggleSfx(bool isOn)
|
||||||
@@ -134,8 +187,18 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (LoginManager.CurrentUser.rc < _matchEntryFeeCoins)
|
if (LoginManager.CurrentUser.rc < _matchEntryFeeCoins)
|
||||||
|
{
|
||||||
|
string fee = CoinHelper.FormatString(_matchEntryFeeCoins) + " RC";
|
||||||
|
if (MessageBoxDialog.IsAvailable)
|
||||||
|
{
|
||||||
|
MessageBoxDialog.Show(
|
||||||
|
"Not enough RC",
|
||||||
|
"You need " + fee + " to play. Buy more RC and try again.");
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Debug.LogError("Not enough RC to play");
|
Debug.LogError("Not enough RC to play");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (CupidLobby.instance == null)
|
if (CupidLobby.instance == null)
|
||||||
@@ -146,37 +209,82 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
CupidLobby.instance.Matchmake();
|
CupidLobby.instance.Matchmake();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnPlayTutorial()
|
||||||
|
{
|
||||||
|
if (LoginManager.CurrentUser == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Tutorial blocked: user profile not loaded.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (LevelLoadManager.instance == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Tutorial blocked: LevelLoadManager is missing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StartCoroutine(CoLaunchTutorial());
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator CoLaunchTutorial()
|
||||||
|
{
|
||||||
|
MatchmadePlayer myPlayer = GameTutorialManager.CreateMyPlayerFromUser(LoginManager.CurrentUser);
|
||||||
|
MatchmadePlayer cpuPlayer = GameTutorialManager.CreateCpuOpponent();
|
||||||
|
|
||||||
|
yield return GameTutorialManager.CoFetchPlayerL10(myPlayer);
|
||||||
|
|
||||||
|
GameTutorialManager.PrepareLaunch(myPlayer, cpuPlayer);
|
||||||
|
LevelLoadManager.LoadLevel(tutorialGameScene);
|
||||||
|
}
|
||||||
|
|
||||||
void OnCryptoAddressChanged(string value)
|
void OnCryptoAddressChanged(string value)
|
||||||
{
|
{
|
||||||
PlayerPrefs.SetString(CryptoAddressPrefsKey, value ?? string.Empty);
|
PlayerPrefs.SetString(CryptoAddressPrefsKey, value ?? string.Empty);
|
||||||
PlayerPrefs.Save();
|
PlayerPrefs.Save();
|
||||||
UpdateWithdrawalStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateWithdrawalStatus(UserData user = null)
|
void OnWithdrawalPressed()
|
||||||
{
|
{
|
||||||
if (txtWithdrawStatus == null)
|
UserData user = LoginManager.CurrentUser;
|
||||||
return;
|
if (user == null)
|
||||||
UserData u = user ?? LoginManager.CurrentUser;
|
{
|
||||||
if (u == null)
|
ShowWithdrawalMessage("Could not load your profile. Try again.", false);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (inputCryptoAddress != null && string.IsNullOrWhiteSpace(inputCryptoAddress.text))
|
if (inputCryptoAddress != null && string.IsNullOrWhiteSpace(inputCryptoAddress.text))
|
||||||
{
|
{
|
||||||
txtWithdrawStatus.text = "Enter a valid wallet address to withdraw safely.";
|
ShowWithdrawalMessage("Enter a valid wallet address to withdraw safely.", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (u.rc < MinimumWithdrawalRcCoins)
|
|
||||||
|
if (user.rc < MinimumWithdrawalRcCoins)
|
||||||
{
|
{
|
||||||
txtWithdrawStatus.text = "Your RC balance is below the minimum withdrawal amount ($20 USD / 20.0 RC).";
|
ShowWithdrawalMessage("Your RC balance is below the minimum withdrawal amount ($20 USD / 20.0 RC).", false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (u.cc < WithdrawalFeeCc)
|
|
||||||
|
if (user.cc < WithdrawalFeeCc)
|
||||||
{
|
{
|
||||||
txtWithdrawStatus.text = "Withdrawal costs 500 CC. Your CC balance is below 500 CC.";
|
ShowWithdrawalMessage("Withdrawal costs 500 CC. Your CC balance is below 500 CC.", false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
txtWithdrawStatus.text = string.Empty;
|
}
|
||||||
|
|
||||||
|
void ShowWithdrawalMessage(string message, bool basic)
|
||||||
|
{
|
||||||
|
if (MessageBoxDialog.IsAvailable)
|
||||||
|
{
|
||||||
|
if (basic)
|
||||||
|
MessageBoxDialog.ShowBasic(message);
|
||||||
|
else
|
||||||
|
MessageBoxDialog.Show("Can't withdraw", message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (txtWithdrawStatus != null)
|
||||||
|
{
|
||||||
|
txtWithdrawStatus.gameObject.SetActive(true);
|
||||||
|
txtWithdrawStatus.text = message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPasteFromClipboard()
|
void OnPasteFromClipboard()
|
||||||
@@ -229,20 +337,12 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
ccText.text = user.cc.ToString("N0") + " CC";
|
ccText.text = user.cc.ToString("N0") + " CC";
|
||||||
rcText.text = CoinHelper.FormatString(user.rc) + " RC";
|
rcText.text = CoinHelper.FormatString(user.rc) + " RC";
|
||||||
|
|
||||||
if (LoginManager.CurrentUser.rc < _matchEntryFeeCoins)
|
if (rcPlayText != null)
|
||||||
{
|
|
||||||
rcPlayText.text = "Not enough RC to play";
|
|
||||||
btnPlay.interactable = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rcPlayText.text = "";
|
rcPlayText.text = "";
|
||||||
|
if (btnPlay != null)
|
||||||
btnPlay.interactable = true;
|
btnPlay.interactable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateWithdrawalStatus(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Called by <see cref="LevelLoadManager"/> so the loading overlay stays until matchmaker settings are refreshed.</summary>
|
/// <summary>Called by <see cref="LevelLoadManager"/> so the loading overlay stays until matchmaker settings are refreshed.</summary>
|
||||||
public IEnumerator FinishLoadingScreenAndApplySettings()
|
public IEnumerator FinishLoadingScreenAndApplySettings()
|
||||||
{
|
{
|
||||||
@@ -300,37 +400,96 @@ public class MainMenuManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SetStatsPanelVisible(bool visible)
|
||||||
|
{
|
||||||
|
if (statsPanel == null)
|
||||||
|
return;
|
||||||
|
statsPanel.SetActive(visible);
|
||||||
|
Transform topPanel = statsPanel.transform.parent;
|
||||||
|
if (topPanel != null)
|
||||||
|
topPanel.gameObject.SetActive(visible);
|
||||||
|
}
|
||||||
|
|
||||||
public void ShowMainMenuScreen(){
|
public void ShowMainMenuScreen(){
|
||||||
statsPanel.SetActive(true);
|
SetStatsPanelVisible(true);
|
||||||
mainMenuScreen.SetActive(true);
|
mainMenuScreen.SetActive(true);
|
||||||
gameModesScreen.SetActive(false);
|
gameModesScreen.SetActive(false);
|
||||||
dummyBuyScreen.SetActive(false);
|
dummyBuyScreen.SetActive(false);
|
||||||
withdrawalScreen.SetActive(false);
|
withdrawalScreen.SetActive(false);
|
||||||
|
settingsScreen.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowGameModesScreen(){
|
public void ShowGameModesScreen(){
|
||||||
statsPanel.SetActive(true);
|
SetStatsPanelVisible(true);
|
||||||
mainMenuScreen.SetActive(false);
|
mainMenuScreen.SetActive(false);
|
||||||
gameModesScreen.SetActive(true);
|
gameModesScreen.SetActive(true);
|
||||||
dummyBuyScreen.SetActive(false);
|
dummyBuyScreen.SetActive(false);
|
||||||
withdrawalScreen.SetActive(false);
|
withdrawalScreen.SetActive(false);
|
||||||
|
settingsScreen.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowDummyBuyScreen(){
|
public void ShowDummyBuyScreen(){
|
||||||
statsPanel.SetActive(false);
|
SetStatsPanelVisible(false);
|
||||||
mainMenuScreen.SetActive(false);
|
mainMenuScreen.SetActive(false);
|
||||||
gameModesScreen.SetActive(false);
|
gameModesScreen.SetActive(false);
|
||||||
dummyBuyScreen.SetActive(true);
|
dummyBuyScreen.SetActive(true);
|
||||||
withdrawalScreen.SetActive(false);
|
withdrawalScreen.SetActive(false);
|
||||||
|
settingsScreen.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowWithdrawalScreen(){
|
public void ShowWithdrawalScreen(){
|
||||||
statsPanel.SetActive(false);
|
SetStatsPanelVisible(false);
|
||||||
mainMenuScreen.SetActive(false);
|
mainMenuScreen.SetActive(false);
|
||||||
gameModesScreen.SetActive(false);
|
gameModesScreen.SetActive(false);
|
||||||
dummyBuyScreen.SetActive(false);
|
dummyBuyScreen.SetActive(false);
|
||||||
withdrawalScreen.SetActive(true);
|
withdrawalScreen.SetActive(true);
|
||||||
UpdateWithdrawalStatus();
|
settingsScreen.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowSettingsScreen(){
|
||||||
|
SetStatsPanelVisible(false);
|
||||||
|
mainMenuScreen.SetActive(false);
|
||||||
|
gameModesScreen.SetActive(false);
|
||||||
|
dummyBuyScreen.SetActive(false);
|
||||||
|
withdrawalScreen.SetActive(false);
|
||||||
|
settingsScreen.SetActive(true);
|
||||||
|
RefreshSettingsProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefreshSettingsProfile()
|
||||||
|
{
|
||||||
|
if (profileInfoFetcher == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UserData user = LoginManager.CurrentUser;
|
||||||
|
if (user == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
profileInfoFetcher.GetPlayerProfile(user.id, (success, error, profile) =>
|
||||||
|
{
|
||||||
|
if (!success || profile == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Settings profile fetch failed: " + (error ?? "unknown"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ApplySettingsProfile(profile);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplySettingsProfile(PlayerProfileApiResponse profile)
|
||||||
|
{
|
||||||
|
if (txtUsername != null)
|
||||||
|
txtUsername.text = profile.username ?? "";
|
||||||
|
if (txtEmail != null)
|
||||||
|
txtEmail.text = profile.email ?? "";
|
||||||
|
if (txtId != null)
|
||||||
|
txtId.text = profile.player_id.ToString();
|
||||||
|
if (txtAge != null)
|
||||||
|
txtAge.text = profile.days_since_registration.ToString();
|
||||||
|
if (txtMatches != null)
|
||||||
|
txtMatches.text = profile.matches_played.ToString();
|
||||||
|
if (txtWinRatio != null)
|
||||||
|
txtWinRatio.text = (profile.win_ratio * 100f).ToString("0.#") + "%";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ public class NetManager : NetworkManager
|
|||||||
|
|
||||||
static bool s_IntentionalClientDisconnect;
|
static bool s_IntentionalClientDisconnect;
|
||||||
|
|
||||||
|
public static NetManager instance;
|
||||||
public override void Awake()
|
public override void Awake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
base.Awake();
|
||||||
|
instance = this;
|
||||||
Transport.active.OnClientError += OnTransportClientError;
|
Transport.active.OnClientError += OnTransportClientError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +29,35 @@ public class NetManager : NetworkManager
|
|||||||
{
|
{
|
||||||
if (Transport.active != null)
|
if (Transport.active != null)
|
||||||
Transport.active.OnClientError -= OnTransportClientError;
|
Transport.active.OnClientError -= OnTransportClientError;
|
||||||
|
|
||||||
|
if (NetworkServer.active || NetworkClient.active)
|
||||||
|
StopNetworkingForSceneChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fully tears down Mirror before loading another scene. Required for tutorial host mode;
|
||||||
|
/// <see cref="NetworkManager.StopClient"/> alone leaves the server running.
|
||||||
|
/// </summary>
|
||||||
|
public static void StopNetworkingForSceneChange()
|
||||||
|
{
|
||||||
|
MarkIntentionalClientDisconnect();
|
||||||
|
|
||||||
|
NetworkManager manager = singleton != null ? singleton : NetworkManager.singleton;
|
||||||
|
if (manager != null)
|
||||||
|
{
|
||||||
|
if (NetworkServer.active && NetworkClient.active)
|
||||||
|
manager.StopHost();
|
||||||
|
else if (NetworkClient.active)
|
||||||
|
manager.StopClient();
|
||||||
|
else if (NetworkServer.active)
|
||||||
|
manager.StopServer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkClient.active)
|
||||||
|
NetworkClient.Shutdown();
|
||||||
|
if (NetworkServer.active)
|
||||||
|
NetworkServer.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnTransportClientError(TransportError error, string reason)
|
static void OnTransportClientError(TransportError error, string reason)
|
||||||
@@ -58,6 +89,13 @@ public class NetManager : NetworkManager
|
|||||||
|
|
||||||
public override void OnServerDisconnect(NetworkConnectionToClient conn)
|
public override void OnServerDisconnect(NetworkConnectionToClient conn)
|
||||||
{
|
{
|
||||||
|
if (conn != null && conn.identity != null)
|
||||||
|
{
|
||||||
|
var netPlayer = conn.identity.GetComponent<NetPlayer>();
|
||||||
|
if (netPlayer != null && GameManager.instance != null)
|
||||||
|
GameManager.instance.OnDedicatedMatchPlayerDisconnected(netPlayer.myTeam);
|
||||||
|
}
|
||||||
|
|
||||||
if (conn != null)
|
if (conn != null)
|
||||||
Logger.Log($"Mirror server: client disconnected (connId={conn.connectionId})");
|
Logger.Log($"Mirror server: client disconnected (connId={conn.connectionId})");
|
||||||
base.OnServerDisconnect(conn);
|
base.OnServerDisconnect(conn);
|
||||||
@@ -114,6 +152,7 @@ public class NetManager : NetworkManager
|
|||||||
{
|
{
|
||||||
Application.logMessageReceived -= ForwardMirrorServerLogToLogger;
|
Application.logMessageReceived -= ForwardMirrorServerLogToLogger;
|
||||||
|
|
||||||
|
ReplayRecorder.StopAndFlush();
|
||||||
GameManager.ReportDedicatedMatchRoomClosed();
|
GameManager.ReportDedicatedMatchRoomClosed();
|
||||||
base.OnStopServer();
|
base.OnStopServer();
|
||||||
}
|
}
|
||||||
@@ -131,7 +170,10 @@ public class NetManager : NetworkManager
|
|||||||
{
|
{
|
||||||
base.OnClientDisconnect();
|
base.OnClientDisconnect();
|
||||||
Logger.Log("Client disconnected");
|
Logger.Log("Client disconnected");
|
||||||
if (ShouldReconnect())
|
// Mirror raises OnDisconnectedEvent before clearing ConnectState, so
|
||||||
|
// NetworkClient.isConnected is still true here on involuntary drops.
|
||||||
|
// Gate on policy flags only; teardown wait happens in the reconnect loop.
|
||||||
|
if (CanAttemptReconnect())
|
||||||
{
|
{
|
||||||
if (reconnectCoroutine == null)
|
if (reconnectCoroutine == null)
|
||||||
reconnectCoroutine = StartCoroutine(ReconnectLoopCoroutine());
|
reconnectCoroutine = StartCoroutine(ReconnectLoopCoroutine());
|
||||||
@@ -175,6 +217,12 @@ public class NetManager : NetworkManager
|
|||||||
SetReconnectCanvasVisible(true);
|
SetReconnectCanvasVisible(true);
|
||||||
reopenRequestedThisDisconnect = false;
|
reopenRequestedThisDisconnect = false;
|
||||||
reopenRequestInFlight = false;
|
reopenRequestInFlight = false;
|
||||||
|
|
||||||
|
// Wait until Mirror finishes OnClientDisconnectInternal (clears Connected
|
||||||
|
// then Shutdown). Without this, ShouldReconnect() fails immediately.
|
||||||
|
while (NetworkClient.isConnected || NetworkClient.active)
|
||||||
|
yield return null;
|
||||||
|
|
||||||
float reconnectStartedAt = Time.realtimeSinceStartup;
|
float reconnectStartedAt = Time.realtimeSinceStartup;
|
||||||
float initialWaitEnd = Time.realtimeSinceStartup + InitialReconnectDelaySeconds;
|
float initialWaitEnd = Time.realtimeSinceStartup + InitialReconnectDelaySeconds;
|
||||||
while (ShouldReconnect() && Time.realtimeSinceStartup < initialWaitEnd)
|
while (ShouldReconnect() && Time.realtimeSinceStartup < initialWaitEnd)
|
||||||
@@ -211,20 +259,32 @@ public class NetManager : NetworkManager
|
|||||||
SetReconnectCanvasVisible(false);
|
SetReconnectCanvasVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShouldReconnect()
|
/// <summary>
|
||||||
|
/// Policy checks that are safe during Mirror's disconnect callback
|
||||||
|
/// (when <see cref="NetworkClient.isConnected"/> may still be true).
|
||||||
|
/// </summary>
|
||||||
|
bool CanAttemptReconnect()
|
||||||
{
|
{
|
||||||
if (s_IntentionalClientDisconnect)
|
if (s_IntentionalClientDisconnect)
|
||||||
return false;
|
return false;
|
||||||
if (NetworkServer.active)
|
if (NetworkServer.active)
|
||||||
return false;
|
return false;
|
||||||
if (NetworkClient.isConnected)
|
|
||||||
return false;
|
|
||||||
if (GameManager.instance != null && GameManager.instance.isGameEnded)
|
if (GameManager.instance != null && GameManager.instance.isGameEnded)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShouldReconnect()
|
||||||
|
{
|
||||||
|
if (!CanAttemptReconnect())
|
||||||
|
return false;
|
||||||
|
if (NetworkClient.isConnected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
IEnumerator CoRequestReopenForCurrentPort()
|
IEnumerator CoRequestReopenForCurrentPort()
|
||||||
{
|
{
|
||||||
reopenRequestInFlight = true;
|
reopenRequestInFlight = true;
|
||||||
@@ -279,4 +339,5 @@ public class NetManager : NetworkManager
|
|||||||
|
|
||||||
reopenRequestInFlight = false;
|
reopenRequestInFlight = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-11
@@ -8,6 +8,7 @@ using UnityEngine.InputSystem;
|
|||||||
#endif
|
#endif
|
||||||
public class NetPlayer : NetworkBehaviour
|
public class NetPlayer : NetworkBehaviour
|
||||||
{
|
{
|
||||||
|
const float PingReportIntervalSeconds = 30f;
|
||||||
|
|
||||||
[SyncVar]
|
[SyncVar]
|
||||||
public int userId;
|
public int userId;
|
||||||
@@ -36,6 +37,9 @@ public class NetPlayer : NetworkBehaviour
|
|||||||
SetMyData(GameManager.MyTeam == Team.Red ? GameManager.RedPlayer.UserId : GameManager.BluePlayer.UserId);
|
SetMyData(GameManager.MyTeam == Team.Red ? GameManager.RedPlayer.UserId : GameManager.BluePlayer.UserId);
|
||||||
#endif
|
#endif
|
||||||
TeamSpecificEffects.instance.SetTeamSpecificEffects(myTeam);
|
TeamSpecificEffects.instance.SetTeamSpecificEffects(myTeam);
|
||||||
|
|
||||||
|
if (GameManager.instance != null)
|
||||||
|
GameManager.instance.OnLocalPlayerJoinedMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMyData(int uid){
|
void SetMyData(int uid){
|
||||||
@@ -96,6 +100,7 @@ public class NetPlayer : NetworkBehaviour
|
|||||||
bool isDragging;
|
bool isDragging;
|
||||||
Vector2 lastDragScreenPosition;
|
Vector2 lastDragScreenPosition;
|
||||||
bool serverDragActive;
|
bool serverDragActive;
|
||||||
|
Coroutine pingReportCoroutine;
|
||||||
|
|
||||||
#region EMOTES
|
#region EMOTES
|
||||||
public void SendTextEmote(string txtName){
|
public void SendTextEmote(string txtName){
|
||||||
@@ -155,10 +160,16 @@ public class NetPlayer : NetworkBehaviour
|
|||||||
if(isLocalPlayer){
|
if(isLocalPlayer){
|
||||||
localPlayer = this;
|
localPlayer = this;
|
||||||
SetupInputPanel();
|
SetupInputPanel();
|
||||||
|
pingReportCoroutine = StartCoroutine(CoReportPingPeriodically());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnDestroy(){
|
void OnDestroy(){
|
||||||
|
if (pingReportCoroutine != null)
|
||||||
|
{
|
||||||
|
StopCoroutine(pingReportCoroutine);
|
||||||
|
pingReportCoroutine = null;
|
||||||
|
}
|
||||||
if(localPlayer == this){
|
if(localPlayer == this){
|
||||||
localPlayer = null;
|
localPlayer = null;
|
||||||
}
|
}
|
||||||
@@ -419,29 +430,30 @@ public class NetPlayer : NetworkBehaviour
|
|||||||
/// Request a rematch with the opponent.
|
/// Request a rematch with the opponent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RequestRematch(){
|
public void RequestRematch(){
|
||||||
|
int maxBetLimitCoins = GameOverCanvas.GetPersonalBetLimit();
|
||||||
if(isServer){
|
if(isServer){
|
||||||
GameOverCanvas.instance.OnRematchRequested();
|
GameOverCanvas.instance.OnRematchRequested(maxBetLimitCoins);
|
||||||
RpcRequestRematch();
|
RpcRequestRematch(maxBetLimitCoins);
|
||||||
}else{
|
}else{
|
||||||
CmdRequestRematch();
|
CmdRequestRematch(maxBetLimitCoins);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command]
|
[Command]
|
||||||
void CmdRequestRematch(){
|
void CmdRequestRematch(int maxBetLimitCoins){
|
||||||
Logger.Log("Rematch requested");
|
Logger.Log("Rematch requested, max bet limit coins: " + maxBetLimitCoins);
|
||||||
|
|
||||||
if(isServerOnly){
|
if(isServerOnly){
|
||||||
//server
|
//server
|
||||||
}else{
|
}else{
|
||||||
GameOverCanvas.instance.OnRematchRequested();
|
GameOverCanvas.instance.OnRematchRequested(maxBetLimitCoins);
|
||||||
}
|
}
|
||||||
RpcRequestRematch();
|
RpcRequestRematch(maxBetLimitCoins);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ClientRpc]
|
[ClientRpc]
|
||||||
void RpcRequestRematch(){
|
void RpcRequestRematch(int maxBetLimitCoins){
|
||||||
GameOverCanvas.instance.OnRematchRequested();
|
GameOverCanvas.instance.OnRematchRequested(maxBetLimitCoins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -474,6 +486,14 @@ public class NetPlayer : NetworkBehaviour
|
|||||||
Logger.Log(matchmadeResponseString);
|
Logger.Log(matchmadeResponseString);
|
||||||
StartCoroutine(CoroutineOnRematchConfirmedServer());
|
StartCoroutine(CoroutineOnRematchConfirmedServer());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string error = string.IsNullOrEmpty(resp.ErrorMessage)
|
||||||
|
? "Could not start a rematch."
|
||||||
|
: resp.ErrorMessage;
|
||||||
|
Logger.Log("Rematch failed: " + error);
|
||||||
|
RpcRematchFailed(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator CoroutineOnRematchConfirmedServer(){
|
IEnumerator CoroutineOnRematchConfirmedServer(){
|
||||||
@@ -530,8 +550,16 @@ public class NetPlayer : NetworkBehaviour
|
|||||||
Cupid.RoomPort = branch.Port;
|
Cupid.RoomPort = branch.Port;
|
||||||
LoginManager.SetMatchYourTeam(branch.your_team);
|
LoginManager.SetMatchYourTeam(branch.your_team);
|
||||||
|
|
||||||
GameManager.instance.StopClient();
|
LevelLoadManager.instance.Leave("Game");
|
||||||
LevelLoadManager.LoadLevel("Game");
|
}
|
||||||
|
|
||||||
|
[ClientRpc]
|
||||||
|
void RpcRematchFailed(string errorMessage)
|
||||||
|
{
|
||||||
|
if (GameOverCanvas.instance != null)
|
||||||
|
GameOverCanvas.instance.OnRematchFailed(errorMessage);
|
||||||
|
else
|
||||||
|
MessageBoxDialog.Show("Rematch failed", string.IsNullOrEmpty(errorMessage) ? "Could not start a rematch." : errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnRematchCancelledServer(){
|
void OnRematchCancelledServer(){
|
||||||
@@ -552,4 +580,29 @@ public class NetPlayer : NetworkBehaviour
|
|||||||
GameOverCanvas.instance.OnRematchCancelled();
|
GameOverCanvas.instance.OnRematchCancelled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IEnumerator CoReportPingPeriodically()
|
||||||
|
{
|
||||||
|
var wait = new WaitForSecondsRealtime(PingReportIntervalSeconds);
|
||||||
|
while (isLocalPlayer)
|
||||||
|
{
|
||||||
|
yield return wait;
|
||||||
|
|
||||||
|
if (!NetworkClient.active || !NetworkClient.isConnected)
|
||||||
|
continue;
|
||||||
|
if (LoginManager.instance == null || string.IsNullOrEmpty(LoginManager.AuthToken))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int matchId = GameManager.DedicatedMatchId;
|
||||||
|
if (matchId <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int pingMs = Mathf.Max(0, Mathf.RoundToInt((float)(NetworkTime.rtt * 1000.0)));
|
||||||
|
LoginManager.instance.ReportPing(matchId, pingMs, (ok, err, report) =>
|
||||||
|
{
|
||||||
|
if (!ok && !string.IsNullOrEmpty(err))
|
||||||
|
Logger.Log("Ping report failed: " + err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
|
public class ProfileInfoFetcher : MonoBehaviour
|
||||||
|
{
|
||||||
|
/// <summary>GET /players/:playerId/profile. Callback (success, errorOrNull, profileOrNull).</summary>
|
||||||
|
public void GetPlayerProfile(int playerId, Action<bool, string, PlayerProfileApiResponse> onComplete)
|
||||||
|
{
|
||||||
|
StartCoroutine(FetchPlayerProfileCoroutine(playerId, onComplete));
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator FetchPlayerProfileCoroutine(int playerId, Action<bool, string, PlayerProfileApiResponse> onComplete)
|
||||||
|
{
|
||||||
|
string url = LoginManager.AuthBaseUrl + "/players/" + playerId + "/profile";
|
||||||
|
using (var request = UnityWebRequest.Get(url))
|
||||||
|
{
|
||||||
|
request.downloadHandler = new DownloadHandlerBuffer();
|
||||||
|
if (!string.IsNullOrEmpty(LoginManager.AuthToken))
|
||||||
|
request.SetRequestHeader("Authorization", "Bearer " + LoginManager.AuthToken);
|
||||||
|
|
||||||
|
yield return request.SendWebRequest();
|
||||||
|
|
||||||
|
string text = request.downloadHandler != null ? request.downloadHandler.text : "";
|
||||||
|
|
||||||
|
if (request.result != UnityWebRequest.Result.Success &&
|
||||||
|
request.result != UnityWebRequest.Result.ProtocolError)
|
||||||
|
{
|
||||||
|
onComplete?.Invoke(false, string.IsNullOrEmpty(request.error) ? "Network error" : request.error, null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.responseCode < 200 || request.responseCode >= 300)
|
||||||
|
{
|
||||||
|
onComplete?.Invoke(false, string.IsNullOrEmpty(text) ? "Request failed (" + request.responseCode + ")" : text, null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
{
|
||||||
|
onComplete?.Invoke(false, "Empty response from server", null);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerProfileApiResponse resp = JsonUtility.FromJson<PlayerProfileApiResponse>(text);
|
||||||
|
if (resp != null && resp.ok)
|
||||||
|
onComplete?.Invoke(true, null, resp);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string err = resp != null && !string.IsNullOrEmpty(resp.error) ? resp.error : "Request failed";
|
||||||
|
onComplete?.Invoke(false, err, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5e6e403cd7bcf0149bad56dbad78c302
|
||||||
+74
-3
@@ -6,6 +6,7 @@ using UnityEngine.EventSystems;
|
|||||||
public class Puck : NetworkBehaviour
|
public class Puck : NetworkBehaviour
|
||||||
{
|
{
|
||||||
[HideInInspector]public Rigidbody2D rb;
|
[HideInInspector]public Rigidbody2D rb;
|
||||||
|
Collider2D col;
|
||||||
[SyncVar]
|
[SyncVar]
|
||||||
public bool isSelected;
|
public bool isSelected;
|
||||||
public LineRenderer lineRenderer;
|
public LineRenderer lineRenderer;
|
||||||
@@ -30,6 +31,7 @@ public class Puck : NetworkBehaviour
|
|||||||
UpdateTeam();
|
UpdateTeam();
|
||||||
markerStartScale = selectedMarker.localScale;
|
markerStartScale = selectedMarker.localScale;
|
||||||
rb=GetComponent<Rigidbody2D>();
|
rb=GetComponent<Rigidbody2D>();
|
||||||
|
col = GetComponent<Collider2D>();
|
||||||
startPosition = transform.position;
|
startPosition = transform.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +54,8 @@ public class Puck : NetworkBehaviour
|
|||||||
GameEvents.OnSelectedPuckChanged -= OnSelectedPuckChanged;
|
GameEvents.OnSelectedPuckChanged -= OnSelectedPuckChanged;
|
||||||
GameManager.OnTeamChanged -= OnTeamChanged;
|
GameManager.OnTeamChanged -= OnTeamChanged;
|
||||||
|
|
||||||
|
RestoreAfterReset();
|
||||||
|
if (GameManager.instance != null)
|
||||||
GameManager.instance.DisposePuck(this);
|
GameManager.instance.DisposePuck(this);
|
||||||
}
|
}
|
||||||
public Vector3 lineEnd;
|
public Vector3 lineEnd;
|
||||||
@@ -135,6 +139,7 @@ public class Puck : NetworkBehaviour
|
|||||||
if (AudioManager.instance != null){
|
if (AudioManager.instance != null){
|
||||||
AudioManager.instance.PlayPuckHit(vol);
|
AudioManager.instance.PlayPuckHit(vol);
|
||||||
if(isServer){
|
if(isServer){
|
||||||
|
ReplayRecorder.RecordHit("puck_puck", vol, this);
|
||||||
RpcPuckHitAudio(vol);
|
RpcPuckHitAudio(vol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,27 +150,64 @@ public class Puck : NetworkBehaviour
|
|||||||
AudioManager.instance.PlayPuckHit(vol);
|
AudioManager.instance.PlayPuckHit(vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Coroutine coroutineReset;
|
||||||
|
bool holdingCollisionLock;
|
||||||
public void Reset(Vector3? position = null, float duration = 0.5f)
|
public void Reset(Vector3? position = null, float duration = 0.5f)
|
||||||
{
|
{
|
||||||
|
UnscheduleReset();
|
||||||
Vector3 pos = position ?? startPosition;
|
Vector3 pos = position ?? startPosition;
|
||||||
StartCoroutine(CoroutineReset(pos, duration));
|
if (coroutineReset != null)
|
||||||
|
{
|
||||||
|
StopCoroutine(coroutineReset);
|
||||||
|
RestoreAfterReset();
|
||||||
|
}
|
||||||
|
coroutineReset = StartCoroutine(CoroutineReset(pos, duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCollisionsEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (col != null)
|
||||||
|
col.enabled = enabled;
|
||||||
|
if (rb != null && !enabled)
|
||||||
|
{
|
||||||
|
rb.linearVelocity = Vector2.zero;
|
||||||
|
rb.angularVelocity = 0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Coroutine coroutineScheduleReset;
|
Coroutine coroutineScheduleReset;
|
||||||
public void ScheduleReset(Vector3 position, float duration = 0.5f){
|
public void ScheduleReset(Vector3 position, float duration = 0.5f){
|
||||||
|
UnscheduleReset();
|
||||||
coroutineScheduleReset = StartCoroutine(CoroutineScheduleReset(position, duration));
|
coroutineScheduleReset = StartCoroutine(CoroutineScheduleReset(position, duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnscheduleReset(){
|
public void UnscheduleReset(){
|
||||||
if(coroutineScheduleReset != null){
|
if(coroutineScheduleReset != null){
|
||||||
StopCoroutine(coroutineScheduleReset);
|
StopCoroutine(coroutineScheduleReset);
|
||||||
|
coroutineScheduleReset = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelInGoalReset()
|
||||||
|
{
|
||||||
|
UnscheduleReset();
|
||||||
|
if (coroutineReset != null)
|
||||||
|
{
|
||||||
|
StopCoroutine(coroutineReset);
|
||||||
|
RestoreAfterReset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator CoroutineScheduleReset(Vector3 position, float duration){
|
IEnumerator CoroutineScheduleReset(Vector3 position, float duration){
|
||||||
while(GameManager.isMoving){
|
while(GameManager.isMoving){
|
||||||
|
if (GameManager.instance != null && !GameManager.instance.CanProcessGoal)
|
||||||
|
yield break;
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GameManager.instance != null && !GameManager.instance.CanProcessGoal)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
coroutineScheduleReset = null;
|
||||||
Debug.Log("Resetting puck", gameObject);
|
Debug.Log("Resetting puck", gameObject);
|
||||||
Vector3 dir = position - transform.position;
|
Vector3 dir = position - transform.position;
|
||||||
Vector3 centerDir = Vector3.zero - transform.position;
|
Vector3 centerDir = Vector3.zero - transform.position;
|
||||||
@@ -187,15 +229,44 @@ public class Puck : NetworkBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator CoroutineReset(Vector3 pos, float duration){
|
IEnumerator CoroutineReset(Vector3 pos, float duration){
|
||||||
|
if (GameManager.instance != null)
|
||||||
|
{
|
||||||
|
GameManager.instance.PushResetCollisionLock();
|
||||||
|
holdingCollisionLock = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb.linearVelocity = Vector2.zero;
|
||||||
|
rb.angularVelocity = 0f;
|
||||||
|
|
||||||
float t=0;
|
float t=0;
|
||||||
Vector3 pos1 = transform.position;
|
Vector3 pos1 = transform.position;
|
||||||
Quaternion rot1 = transform.rotation;
|
Quaternion rot1 = transform.rotation;
|
||||||
while(t<duration){
|
while(t<duration){
|
||||||
t+=Time.deltaTime;
|
t+=Time.deltaTime;
|
||||||
transform.position = Vector3.Lerp(pos1, pos, t/duration);
|
float u = Mathf.Clamp01(t/duration);
|
||||||
transform.rotation = Quaternion.Lerp(rot1, Quaternion.identity, t/duration);
|
transform.position = Vector3.Lerp(pos1, pos, u);
|
||||||
|
transform.rotation = Quaternion.Lerp(rot1, Quaternion.identity, u);
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
transform.position = pos;
|
||||||
|
transform.rotation = Quaternion.identity;
|
||||||
|
|
||||||
|
RestoreAfterReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreAfterReset()
|
||||||
|
{
|
||||||
|
if (rb != null)
|
||||||
|
{
|
||||||
|
rb.linearVelocity = Vector2.zero;
|
||||||
|
rb.angularVelocity = 0f;
|
||||||
|
}
|
||||||
|
if (holdingCollisionLock && GameManager.instance != null)
|
||||||
|
{
|
||||||
|
GameManager.instance.PopResetCollisionLock();
|
||||||
|
holdingCollisionLock = false;
|
||||||
|
}
|
||||||
|
coroutineReset = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9cff4a867ddc05341bf42777b654509a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
# Match Replay JSON Format
|
||||||
|
|
||||||
|
Guide for implementing the admin-panel replay viewer. Files are written by the Unity dedicated server (and practice host) to disk only.
|
||||||
|
|
||||||
|
## Where files live
|
||||||
|
|
||||||
|
Written next to the match log with the **same basename**, only the extension differs:
|
||||||
|
|
||||||
|
```
|
||||||
|
{ApplicationDirectory}/Logs/{matchId}.txt ← match log
|
||||||
|
{ApplicationDirectory}/Logs/{matchId}.json ← replay
|
||||||
|
```
|
||||||
|
|
||||||
|
| Match type | `matchId` | Example pair |
|
||||||
|
|------------|-----------|--------------|
|
||||||
|
| Ranked / dedicated | Positive matchmaker id | `Logs/228.txt` + `Logs/228.json` |
|
||||||
|
| Practice / tutorial | Negative unix timestamp | `Logs/-1769539200.txt` + `Logs/-1769539200.json` |
|
||||||
|
|
||||||
|
Admin panel: given a log at `Logs/{id}.txt`, load `Logs/{id}.json` beside it.
|
||||||
|
|
||||||
|
## Top-level object
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"matchId": 228,
|
||||||
|
"isPractice": false,
|
||||||
|
"fixedDeltaTime": 0.02,
|
||||||
|
"duration": 184.32,
|
||||||
|
"entities": [ /* ... */ ],
|
||||||
|
"frames": [ /* ... */ ],
|
||||||
|
"events": [ /* ... */ ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `version` | int | Schema version. Currently `1`. |
|
||||||
|
| `matchId` | int | Match id (positive = ranked, negative = practice). |
|
||||||
|
| `isPractice` | bool | `true` for localhost practice / tutorial host. |
|
||||||
|
| `fixedDeltaTime` | float | Physics step used while recording (typically `0.02` → ~50 Hz). |
|
||||||
|
| `duration` | float | Length of the recording in seconds (`t` of last sample / end). |
|
||||||
|
| `entities` | array | Ball + pucks. Order defines pose array indices in every frame. |
|
||||||
|
| `frames` | array | Time-ordered pose samples (~every FixedUpdate). |
|
||||||
|
| `events` | array | Time-ordered discrete events (hits, goals, resets). |
|
||||||
|
|
||||||
|
Time `t` everywhere is **seconds since recording start** (match start), not wall-clock UTC.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Entities
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "id": 0, "type": "ball", "team": "" }
|
||||||
|
{ "id": 1, "type": "puck", "team": "Red" }
|
||||||
|
{ "id": 2, "type": "puck", "team": "Blue" }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Type | Values |
|
||||||
|
|-------|------|--------|
|
||||||
|
| `id` | int | Stable index `0 .. N-1`. Matches array index in each frame’s `x`/`y`/`vx`/`vy`. |
|
||||||
|
| `type` | string | `"ball"` or `"puck"` |
|
||||||
|
| `team` | string | `"Red"`, `"Blue"`, or `""` for ball |
|
||||||
|
|
||||||
|
Entity list is fixed for the whole file. **Index `i` in a frame’s pose arrays always refers to `entities[i]`.**
|
||||||
|
|
||||||
|
Typical order: ball first, then pucks sorted by team then spawn position.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Frames (motion samples)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"t": 12.40,
|
||||||
|
"x": [0.0, 1.2, -0.5],
|
||||||
|
"y": [0.1, -0.3, 2.0],
|
||||||
|
"vx": [0.0, 3.1, -1.0],
|
||||||
|
"vy": [0.2, 0.0, 0.5]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `t` | float | Sample time in seconds (monotonic, increasing). |
|
||||||
|
| `x`, `y` | float[] | World position per entity (Unity 2D world units). |
|
||||||
|
| `vx`, `vy` | float[] | Linear velocity per entity at that sample (world units / second). |
|
||||||
|
|
||||||
|
- Array length always equals `entities.length`.
|
||||||
|
- Samples are dense (~`1 / fixedDeltaTime` Hz). Do **not** treat them as render frames.
|
||||||
|
|
||||||
|
### Coordinate system
|
||||||
|
|
||||||
|
Unity 2D world space:
|
||||||
|
|
||||||
|
- Origin at field center
|
||||||
|
- `+X` right, `+Y` up (as in Unity)
|
||||||
|
- Scale matches the in-game pitch (see game `fieldSize` / art for admin canvas mapping)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "t": 12.4, "type": "hit", "kind": "ball_wall", "vol": 0.6, "id": 0, "team": "", "redScore": 0, "blueScore": 0, "kickoff": false }
|
||||||
|
{ "t": 30.1, "type": "goal", "kind": "", "vol": 0, "id": -1, "team": "Blue", "redScore": 1, "blueScore": 0, "kickoff": false }
|
||||||
|
{ "t": 32.2, "type": "reset", "kind": "", "vol": 0, "id": -1, "team": "", "redScore": 0, "blueScore": 0, "kickoff": false }
|
||||||
|
```
|
||||||
|
|
||||||
|
All event objects share the same fields (Unity `JsonUtility` shape). Unused fields are empty / zero / `-1`.
|
||||||
|
|
||||||
|
| Field | Used by | Description |
|
||||||
|
|-------|---------|-------------|
|
||||||
|
| `t` | all | Event time in seconds. |
|
||||||
|
| `type` | all | `"hit"` \| `"goal"` \| `"reset"` |
|
||||||
|
| `kind` | `hit` | `"puck_puck"` \| `"ball_puck"` \| `"ball_wall"` |
|
||||||
|
| `vol` | `hit` | SFX volume `0..1` (from relative impact speed). |
|
||||||
|
| `id` | `hit` | Entity id involved (spatial cue). `-1` if unknown. |
|
||||||
|
| `team` | `goal` | **Conceding** side / goal owner (`"Red"` or `"Blue"`). The other team scored. |
|
||||||
|
| `redScore` | `goal` | Score after this goal. |
|
||||||
|
| `blueScore` | `goal` | Score after this goal. |
|
||||||
|
| `kickoff` | `goal`, `reset` | Kickoff-goal / kickoff-style reset when `true`. |
|
||||||
|
|
||||||
|
### Event semantics
|
||||||
|
|
||||||
|
| `type` | Meaning for the viewer |
|
||||||
|
|--------|-------------------------|
|
||||||
|
| `hit` | Play collision SFX at `vol`. Optional: flash near entity `id`. |
|
||||||
|
| `goal` | Goal scored against `team`. Update scoreboard to `redScore`/`blueScore`. Play goal SFX. **Discontinuity** — do not interpolate motion across this time. |
|
||||||
|
| `reset` | Pieces are lerping / teleporting back. **Discontinuity** — snap poses; do not blend across. |
|
||||||
|
|
||||||
|
First-to-3 wins; a completed match usually ends shortly after a goal that makes a score `3`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Playback contract (required for smooth motion)
|
||||||
|
|
||||||
|
Recording is FixedUpdate (~50 Hz). Snapping happens if the viewer steps sample-by-sample without blending. Follow this:
|
||||||
|
|
||||||
|
### 1. Drive time with wall clock
|
||||||
|
|
||||||
|
```text
|
||||||
|
playbackTime += (deltaMs / 1000) * playbackSpeed
|
||||||
|
playbackTime = clamp(playbackTime, 0, duration)
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `requestAnimationFrame` (or equivalent). Do **not** advance one JSON frame per rAF tick.
|
||||||
|
|
||||||
|
### 2. Interpolate poses between samples
|
||||||
|
|
||||||
|
Binary-search `frames` for neighbors where:
|
||||||
|
|
||||||
|
```text
|
||||||
|
frames[i].t <= playbackTime < frames[i+1].t
|
||||||
|
```
|
||||||
|
|
||||||
|
Then for each entity index `e`:
|
||||||
|
|
||||||
|
**Minimum (good):** linear lerp of `x`/`y`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
u = (playbackTime - t0) / (t1 - t0)
|
||||||
|
x = lerp(x0[e], x1[e], u)
|
||||||
|
y = lerp(y0[e], y1[e], u)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Recommended (smooth under acceleration):** cubic Hermite using velocities:
|
||||||
|
|
||||||
|
```text
|
||||||
|
dt = t1 - t0
|
||||||
|
u = (playbackTime - t0) / dt
|
||||||
|
u2 = u*u
|
||||||
|
u3 = u2*u
|
||||||
|
|
||||||
|
h00 = 2*u3 - 3*u2 + 1
|
||||||
|
h10 = u3 - 2*u2 + u
|
||||||
|
h01 = -2*u3 + 3*u2
|
||||||
|
h11 = u3 - u2
|
||||||
|
|
||||||
|
x = h00*x0 + h10*dt*vx0 + h01*x1 + h11*dt*vx1
|
||||||
|
y = h00*y0 + h10*dt*vy0 + h01*y1 + h11*dt*vy1
|
||||||
|
```
|
||||||
|
|
||||||
|
(Same for each entity index.)
|
||||||
|
|
||||||
|
### 3. Snap across discontinuities
|
||||||
|
|
||||||
|
Treat every `goal` and `reset` event as a cut:
|
||||||
|
|
||||||
|
- When `playbackTime` crosses that event’s `t`, **do not** interpolate from the previous sample across the cut.
|
||||||
|
- Seek to the frame at or immediately after `t` and snap positions.
|
||||||
|
|
||||||
|
Practical approach: build a sorted list of discontinuity times from events where `type === "goal" || type === "reset"`. If `t0` and `t1` straddle a discontinuity, snap to the post-cut sample instead of lerping.
|
||||||
|
|
||||||
|
### 4. Fire events once when crossed
|
||||||
|
|
||||||
|
Keep `lastEventIndex` (or last fired `t`). When `playbackTime` advances past an event’s `t`, fire it once:
|
||||||
|
|
||||||
|
- `hit` → SFX by `kind` / `vol`
|
||||||
|
- `goal` → score UI + goal audio
|
||||||
|
- `reset` → optional reset cue (usually silent)
|
||||||
|
|
||||||
|
On seek / scrub: reset event cursor; either skip SFX or replay only from the new time forward.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Suggested viewer pipeline
|
||||||
|
|
||||||
|
```text
|
||||||
|
Load JSON
|
||||||
|
→ map entities to sprites (ball / red pucks / blue pucks)
|
||||||
|
→ sort events by t (already ordered, but verify)
|
||||||
|
→ each animation frame:
|
||||||
|
advance playbackTime
|
||||||
|
resolve poses (Hermite + discontinuity snaps)
|
||||||
|
draw entities
|
||||||
|
dispatch newly crossed events
|
||||||
|
```
|
||||||
|
|
||||||
|
### Seeking / scrubbing
|
||||||
|
|
||||||
|
1. Set `playbackTime` to the scrub value.
|
||||||
|
2. Find the nearest frame (or interpolate as above).
|
||||||
|
3. Recompute score from the last `goal` event with `t <= playbackTime` (else `0–0`).
|
||||||
|
4. Reset the event-fire cursor to that time so SFX don’t spam.
|
||||||
|
|
||||||
|
### Scoreboard without scrubbing
|
||||||
|
|
||||||
|
Start at `0–0`. On each `goal` event, set scores from that event’s `redScore` / `blueScore`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimal TypeScript types
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface ReplayFile {
|
||||||
|
version: number;
|
||||||
|
matchId: number;
|
||||||
|
isPractice: boolean;
|
||||||
|
fixedDeltaTime: number;
|
||||||
|
duration: number;
|
||||||
|
entities: ReplayEntity[];
|
||||||
|
frames: ReplayFrame[];
|
||||||
|
events: ReplayEvent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReplayEntity {
|
||||||
|
id: number;
|
||||||
|
type: "ball" | "puck" | string;
|
||||||
|
team: "Red" | "Blue" | "" | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReplayFrame {
|
||||||
|
t: number;
|
||||||
|
x: number[];
|
||||||
|
y: number[];
|
||||||
|
vx: number[];
|
||||||
|
vy: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReplayEvent {
|
||||||
|
t: number;
|
||||||
|
type: "hit" | "goal" | "reset" | string;
|
||||||
|
kind: "puck_puck" | "ball_puck" | "ball_wall" | "" | string;
|
||||||
|
vol: number;
|
||||||
|
id: number; // entity id, or -1
|
||||||
|
team: "Red" | "Blue" | "" | string; // conceding team on goal
|
||||||
|
redScore: number;
|
||||||
|
blueScore: number;
|
||||||
|
kickoff: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checklist for a correct viewer
|
||||||
|
|
||||||
|
- [ ] Time driven by real elapsed time × speed, not frame index
|
||||||
|
- [ ] Poses interpolated between samples (Hermite preferred)
|
||||||
|
- [ ] No interpolation across `goal` / `reset`
|
||||||
|
- [ ] Entity index `i` always matches `entities[i]`
|
||||||
|
- [ ] Hit SFX use `kind` + `vol`
|
||||||
|
- [ ] Goal `team` means **conceding** side
|
||||||
|
- [ ] Practice files filtered via `isPractice` or negative `matchId`
|
||||||
|
|
||||||
|
## Out of scope (not in the JSON)
|
||||||
|
|
||||||
|
- Player names / user ids (use matchmaker / match API by `matchId` for ranked games)
|
||||||
|
- Camera / UI layout
|
||||||
|
- Launch aim lines / turn timer
|
||||||
|
- Network latency or client prediction
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8cced32e9b154814b99db72a5ae58c70
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a1b2c3d4e5f60718293a4b5c6d7e8f90
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replay JSON DTOs for admin-panel playback.
|
||||||
|
/// Playback contract:
|
||||||
|
/// 1. Advance playbackTime with real time (scaled).
|
||||||
|
/// 2. Binary-search frames by t; Hermite-interpolate x/y using vx/vy between neighbors.
|
||||||
|
/// 3. On reset/goal events (discontinuities): snap to the sample at/after t — do not blend across.
|
||||||
|
/// 4. Fire SFX on hit/goal when playbackTime crosses event t.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class ReplayFile
|
||||||
|
{
|
||||||
|
public int version = 1;
|
||||||
|
public int matchId;
|
||||||
|
/// <summary>True for localhost practice / tutorial host matches (no dedicated matchmaker id).</summary>
|
||||||
|
public bool isPractice;
|
||||||
|
public float fixedDeltaTime;
|
||||||
|
public float duration;
|
||||||
|
public ReplayEntity[] entities;
|
||||||
|
public ReplayFrame[] frames;
|
||||||
|
public ReplayEvent[] events;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ReplayEntity
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
/// <summary>"ball" or "puck"</summary>
|
||||||
|
public string type;
|
||||||
|
/// <summary>"Red", "Blue", or empty for ball</summary>
|
||||||
|
public string team;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ReplayFrame
|
||||||
|
{
|
||||||
|
public float t;
|
||||||
|
public float[] x;
|
||||||
|
public float[] y;
|
||||||
|
public float[] vx;
|
||||||
|
public float[] vy;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ReplayEvent
|
||||||
|
{
|
||||||
|
public float t;
|
||||||
|
/// <summary>"hit", "goal", or "reset"</summary>
|
||||||
|
public string type;
|
||||||
|
/// <summary>For hits: "puck_puck", "ball_puck", or "ball_wall"</summary>
|
||||||
|
public string kind;
|
||||||
|
public float vol;
|
||||||
|
/// <summary>Entity id for spatial cueing; -1 when N/A</summary>
|
||||||
|
public int id = -1;
|
||||||
|
/// <summary>For goals: conceding team ("Red"/"Blue")</summary>
|
||||||
|
public string team;
|
||||||
|
public int redScore;
|
||||||
|
public int blueScore;
|
||||||
|
public bool kickoff;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b2c3d4e5f60718293a4b5c6d7e8f901a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Mirror;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Server-only match recorder. Samples poses every FixedUpdate; flushes JSON to disk on stop.
|
||||||
|
/// Admin playback must interpolate by time (Hermite using velocity) — never snap one FixedUpdate sample per render frame.
|
||||||
|
/// </summary>
|
||||||
|
public class ReplayRecorder : MonoBehaviour
|
||||||
|
{
|
||||||
|
public static ReplayRecorder Instance { get; private set; }
|
||||||
|
|
||||||
|
public static bool IsRecording => Instance != null && Instance._recording && !Instance._flushed;
|
||||||
|
|
||||||
|
bool _recording;
|
||||||
|
bool _flushed;
|
||||||
|
int _matchId;
|
||||||
|
bool _isPractice;
|
||||||
|
float _startFixedTime;
|
||||||
|
float _duration;
|
||||||
|
|
||||||
|
readonly List<ReplayEntity> _entities = new List<ReplayEntity>();
|
||||||
|
readonly List<Rigidbody2D> _bodies = new List<Rigidbody2D>();
|
||||||
|
readonly Dictionary<int, int> _instanceIdToEntityId = new Dictionary<int, int>();
|
||||||
|
readonly List<ReplayFrame> _frames = new List<ReplayFrame>();
|
||||||
|
readonly List<ReplayEvent> _events = new List<ReplayEvent>();
|
||||||
|
|
||||||
|
/// <returns>True if recording started (or already recording this session).</returns>
|
||||||
|
public static bool StartRecording(int matchId, GameManager gm)
|
||||||
|
{
|
||||||
|
if (!NetworkServer.active || gm == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Instance == null)
|
||||||
|
{
|
||||||
|
var go = new GameObject("ReplayRecorder");
|
||||||
|
DontDestroyOnLoad(go);
|
||||||
|
Instance = go.AddComponent<ReplayRecorder>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance.Begin(matchId, gm);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StopAndFlush()
|
||||||
|
{
|
||||||
|
if (Instance != null)
|
||||||
|
Instance.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RecordHit(string kind, float vol, Component source)
|
||||||
|
{
|
||||||
|
if (!IsRecording)
|
||||||
|
return;
|
||||||
|
Instance.AddHit(kind, vol, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RecordGoal(Team concedingTeam, int redScore, int blueScore, bool kickoff)
|
||||||
|
{
|
||||||
|
if (!IsRecording)
|
||||||
|
return;
|
||||||
|
Instance.AddEvent(new ReplayEvent
|
||||||
|
{
|
||||||
|
t = Instance.CurrentT(),
|
||||||
|
type = "goal",
|
||||||
|
kind = "",
|
||||||
|
vol = 0f,
|
||||||
|
id = -1,
|
||||||
|
team = concedingTeam.ToString(),
|
||||||
|
redScore = redScore,
|
||||||
|
blueScore = blueScore,
|
||||||
|
kickoff = kickoff
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RecordReset(bool kickoff)
|
||||||
|
{
|
||||||
|
if (!IsRecording)
|
||||||
|
return;
|
||||||
|
Instance.AddEvent(new ReplayEvent
|
||||||
|
{
|
||||||
|
t = Instance.CurrentT(),
|
||||||
|
type = "reset",
|
||||||
|
kind = "",
|
||||||
|
vol = 0f,
|
||||||
|
id = -1,
|
||||||
|
team = "",
|
||||||
|
redScore = 0,
|
||||||
|
blueScore = 0,
|
||||||
|
kickoff = kickoff
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetEntityId(Component source, out int entityId)
|
||||||
|
{
|
||||||
|
entityId = -1;
|
||||||
|
if (Instance == null || source == null)
|
||||||
|
return false;
|
||||||
|
return Instance._instanceIdToEntityId.TryGetValue(source.gameObject.GetInstanceID(), out entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Begin(int matchId, GameManager gm)
|
||||||
|
{
|
||||||
|
if (_recording && !_flushed)
|
||||||
|
Flush();
|
||||||
|
|
||||||
|
// Practice / tutorial host uses DedicatedMatchId 0 — mint a unique id so files don't overwrite.
|
||||||
|
_isPractice = matchId <= 0 || GameTutorialManager.tutorialModeEnabled;
|
||||||
|
if (matchId <= 0)
|
||||||
|
matchId = GeneratePracticeMatchId();
|
||||||
|
|
||||||
|
_matchId = matchId;
|
||||||
|
_recording = true;
|
||||||
|
_flushed = false;
|
||||||
|
_startFixedTime = Time.fixedTime;
|
||||||
|
_duration = 0f;
|
||||||
|
_entities.Clear();
|
||||||
|
_bodies.Clear();
|
||||||
|
_instanceIdToEntityId.Clear();
|
||||||
|
_frames.Clear();
|
||||||
|
_events.Clear();
|
||||||
|
|
||||||
|
// Keep log + replay siblings: Logs/{matchId}.txt and Logs/{matchId}.json
|
||||||
|
if (_isPractice)
|
||||||
|
Logger.SetFileName(_matchId.ToString());
|
||||||
|
|
||||||
|
BuildEntities(gm);
|
||||||
|
string mode = _isPractice ? "practice" : "match";
|
||||||
|
Logger.Log($"ReplayRecorder started ({mode}) id {_matchId} with {_entities.Count} entities");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Negative unix timestamp so practice ids never collide with positive matchmaker ids.</summary>
|
||||||
|
static int GeneratePracticeMatchId()
|
||||||
|
{
|
||||||
|
long unix = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
if (unix > int.MaxValue)
|
||||||
|
unix %= int.MaxValue;
|
||||||
|
int id = -(int)unix;
|
||||||
|
return id == 0 ? -1 : id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildEntities(GameManager gm)
|
||||||
|
{
|
||||||
|
if (gm.ball != null)
|
||||||
|
{
|
||||||
|
RegisterEntity(gm.ball.gameObject, gm.ball, "ball", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
var sorted = new List<Puck>(gm.pucks);
|
||||||
|
sorted.Sort((a, b) =>
|
||||||
|
{
|
||||||
|
int teamCmp = a.team.CompareTo(b.team);
|
||||||
|
if (teamCmp != 0)
|
||||||
|
return teamCmp;
|
||||||
|
int xCmp = a.transform.position.x.CompareTo(b.transform.position.x);
|
||||||
|
if (xCmp != 0)
|
||||||
|
return xCmp;
|
||||||
|
return a.transform.position.y.CompareTo(b.transform.position.y);
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (Puck puck in sorted)
|
||||||
|
{
|
||||||
|
if (puck == null || puck.rb == null)
|
||||||
|
continue;
|
||||||
|
RegisterEntity(puck.gameObject, puck.rb, "puck", puck.team.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterEntity(GameObject go, Rigidbody2D rb, string type, string team)
|
||||||
|
{
|
||||||
|
int id = _entities.Count;
|
||||||
|
_entities.Add(new ReplayEntity { id = id, type = type, team = team ?? "" });
|
||||||
|
_bodies.Add(rb);
|
||||||
|
_instanceIdToEntityId[go.GetInstanceID()] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CurrentT() => Time.fixedTime - _startFixedTime;
|
||||||
|
|
||||||
|
void FixedUpdate()
|
||||||
|
{
|
||||||
|
if (!_recording || _flushed || !NetworkServer.active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int n = _bodies.Count;
|
||||||
|
if (n == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var frame = new ReplayFrame
|
||||||
|
{
|
||||||
|
t = CurrentT(),
|
||||||
|
x = new float[n],
|
||||||
|
y = new float[n],
|
||||||
|
vx = new float[n],
|
||||||
|
vy = new float[n]
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
Rigidbody2D rb = _bodies[i];
|
||||||
|
if (rb == null)
|
||||||
|
continue;
|
||||||
|
Vector2 pos = rb.position;
|
||||||
|
Vector2 vel = rb.linearVelocity;
|
||||||
|
frame.x[i] = pos.x;
|
||||||
|
frame.y[i] = pos.y;
|
||||||
|
frame.vx[i] = vel.x;
|
||||||
|
frame.vy[i] = vel.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
_duration = frame.t;
|
||||||
|
_frames.Add(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddHit(string kind, float vol, Component source)
|
||||||
|
{
|
||||||
|
int entityId = -1;
|
||||||
|
if (source != null)
|
||||||
|
_instanceIdToEntityId.TryGetValue(source.gameObject.GetInstanceID(), out entityId);
|
||||||
|
|
||||||
|
AddEvent(new ReplayEvent
|
||||||
|
{
|
||||||
|
t = CurrentT(),
|
||||||
|
type = "hit",
|
||||||
|
kind = kind ?? "",
|
||||||
|
vol = vol,
|
||||||
|
id = entityId,
|
||||||
|
team = "",
|
||||||
|
redScore = 0,
|
||||||
|
blueScore = 0,
|
||||||
|
kickoff = false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddEvent(ReplayEvent ev)
|
||||||
|
{
|
||||||
|
_events.Add(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flush()
|
||||||
|
{
|
||||||
|
if (!_recording || _flushed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_flushed = true;
|
||||||
|
_recording = false;
|
||||||
|
_duration = Mathf.Max(_duration, CurrentT());
|
||||||
|
|
||||||
|
var file = new ReplayFile
|
||||||
|
{
|
||||||
|
version = 1,
|
||||||
|
matchId = _matchId,
|
||||||
|
isPractice = _isPractice,
|
||||||
|
fixedDeltaTime = Time.fixedDeltaTime,
|
||||||
|
duration = _duration,
|
||||||
|
entities = _entities.ToArray(),
|
||||||
|
frames = _frames.ToArray(),
|
||||||
|
events = _events.ToArray()
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string path = ResolveReplayFilePath();
|
||||||
|
string json = JsonUtility.ToJson(file);
|
||||||
|
File.WriteAllText(path, json);
|
||||||
|
Logger.Log($"ReplayRecorder wrote {path} ({_frames.Count} frames, {_events.Count} events, duration {_duration:F2}s)");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Log("ReplayRecorder flush failed: " + e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Same directory and basename as the match log, extension <c>.json</c>
|
||||||
|
/// (e.g. <c>Logs/228.txt</c> → <c>Logs/228.json</c>).
|
||||||
|
/// </summary>
|
||||||
|
string ResolveReplayFilePath()
|
||||||
|
{
|
||||||
|
string expectedName = _matchId.ToString();
|
||||||
|
string logPath = Logger.instance != null ? Logger.instance.LogFilePath : null;
|
||||||
|
if (!string.IsNullOrEmpty(logPath))
|
||||||
|
{
|
||||||
|
string logBase = Path.GetFileNameWithoutExtension(logPath);
|
||||||
|
if (logBase == expectedName)
|
||||||
|
return Path.ChangeExtension(logPath, ".json");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.Combine(Logger.GetLogsDirectory(), expectedName + ".json");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestroy()
|
||||||
|
{
|
||||||
|
if (Instance == this)
|
||||||
|
{
|
||||||
|
Flush();
|
||||||
|
Instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c3d4e5f60718293a4b5c6d7e8f901a2b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -56,6 +56,7 @@ public class ServerAutoClose : MonoBehaviour
|
|||||||
Logger.Log("All players left, exiting");
|
Logger.Log("All players left, exiting");
|
||||||
else
|
else
|
||||||
Logger.Log("No players joined, exiting");
|
Logger.Log("No players joined, exiting");
|
||||||
|
// ReportDedicatedMatchRoomClosed settles /winner first when escrow is open.
|
||||||
GameManager.ReportDedicatedMatchRoomClosed();
|
GameManager.ReportDedicatedMatchRoomClosed();
|
||||||
Application.Quit();
|
Application.Quit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ public class TutorialManagerMainMenu : MonoBehaviour
|
|||||||
|
|
||||||
[Header("Screen 6")]
|
[Header("Screen 6")]
|
||||||
public GameObject screen6;
|
public GameObject screen6;
|
||||||
|
public Button btnNext6;
|
||||||
|
public CardCarousel gameModeCarousel;
|
||||||
|
|
||||||
|
[Header("Screen 7")]
|
||||||
|
public GameObject screen7;
|
||||||
public Button btnFinish;
|
public Button btnFinish;
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
@@ -38,25 +43,28 @@ public class TutorialManagerMainMenu : MonoBehaviour
|
|||||||
btnNext1.onClick.AddListener(OnNext1);
|
btnNext1.onClick.AddListener(OnNext1);
|
||||||
btnSkip.onClick.AddListener(OnSkip);
|
btnSkip.onClick.AddListener(OnSkip);
|
||||||
btnNext2.onClick.AddListener(OnNext2);
|
btnNext2.onClick.AddListener(OnNext2);
|
||||||
|
btnNext6.onClick.AddListener(OnNext6);
|
||||||
|
|
||||||
btnFinish.onClick.AddListener(OnTutorialCompleted);
|
btnFinish.onClick.AddListener(OnStartTutorialMatch);
|
||||||
|
|
||||||
if(!forceShow){
|
if(!forceShow){
|
||||||
if(PlayerPrefs.HasKey(TUTORIAL_PREF_KEY)){
|
if(PlayerPrefs.HasKey(TUTORIAL_PREF_KEY)){
|
||||||
tutorialPanel.SetActive(false);
|
tutorialPanel.SetActive(false);
|
||||||
|
Debug.Log("Tutorial pref key is available, stopping;");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LoginManager.CurrentUser.rc > 0){
|
if(LoginManager.CurrentUser.rc > 0){
|
||||||
tutorialPanel.SetActive(false);
|
tutorialPanel.SetActive(false);
|
||||||
|
Debug.Log("User has rc, stopping;");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tutorialPanel.SetActive(true);
|
tutorialPanel.SetActive(true);
|
||||||
screen1.SetActive(true);
|
screen1.SetActive(true);
|
||||||
|
PlayerPrefs.SetInt(TUTORIAL_PREF_KEY, 1);
|
||||||
|
PlayerPrefs.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnNext1(){
|
void OnNext1(){
|
||||||
@@ -77,6 +85,15 @@ public class TutorialManagerMainMenu : MonoBehaviour
|
|||||||
curScreen = 3;
|
curScreen = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnNext6(){
|
||||||
|
screen6.SetActive(false);
|
||||||
|
screen7.SetActive(true);
|
||||||
|
|
||||||
|
gameModeCarousel.SetSelectedIndex(1);
|
||||||
|
|
||||||
|
curScreen = 7;
|
||||||
|
}
|
||||||
|
|
||||||
void OnNext3(){
|
void OnNext3(){
|
||||||
screen3.SetActive(false);
|
screen3.SetActive(false);
|
||||||
screen4.SetActive(true);
|
screen4.SetActive(true);
|
||||||
@@ -103,6 +120,13 @@ public class TutorialManagerMainMenu : MonoBehaviour
|
|||||||
tutorialPanel.SetActive(false);
|
tutorialPanel.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnStartTutorialMatch()
|
||||||
|
{
|
||||||
|
OnTutorialCompleted();
|
||||||
|
if (MainMenuManager.instance != null)
|
||||||
|
MainMenuManager.instance.OnPlayTutorial();
|
||||||
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
if(curScreen < 2){return;}
|
if(curScreen < 2){return;}
|
||||||
|
|||||||
@@ -0,0 +1,229 @@
|
|||||||
|
using System;
|
||||||
|
using DG.Tweening;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
public class MessageBoxDialog : MonoBehaviour
|
||||||
|
{
|
||||||
|
public const float ANIMATION_DURATION = 0.25f;
|
||||||
|
/// <summary>Above game/menu/loader canvases; below reconnect (500).</summary>
|
||||||
|
const int OverlaySortingOrder = 400;
|
||||||
|
|
||||||
|
public static MessageBoxDialog instance;
|
||||||
|
public static bool IsAvailable => instance != null;
|
||||||
|
|
||||||
|
public RectTransform dimmer;
|
||||||
|
public RectTransform basicMessageBox;
|
||||||
|
|
||||||
|
[Header("MessageBox")]
|
||||||
|
public RectTransform messageBox;
|
||||||
|
public TMP_Text txtTitle, txtMessage;
|
||||||
|
public Button btnOk, btnYes, btnNo;
|
||||||
|
|
||||||
|
Image dimmerImg;
|
||||||
|
TMP_Text basicMessageText;
|
||||||
|
Action<bool> onYesOrNo;
|
||||||
|
Sequence showHideSequence;
|
||||||
|
Canvas overlayCanvas;
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
if (instance != null && instance != this)
|
||||||
|
{
|
||||||
|
Destroy(gameObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = this;
|
||||||
|
DontDestroyOnLoad(gameObject);
|
||||||
|
|
||||||
|
overlayCanvas = GetComponent<Canvas>();
|
||||||
|
if (overlayCanvas != null)
|
||||||
|
overlayCanvas.sortingOrder = OverlaySortingOrder;
|
||||||
|
|
||||||
|
if (dimmer != null)
|
||||||
|
dimmerImg = dimmer.GetComponent<Image>();
|
||||||
|
if (basicMessageBox != null)
|
||||||
|
basicMessageText = basicMessageBox.GetComponentInChildren<TMP_Text>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnEnable()
|
||||||
|
{
|
||||||
|
if (instance != this)
|
||||||
|
return;
|
||||||
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDisable()
|
||||||
|
{
|
||||||
|
if (instance != this)
|
||||||
|
return;
|
||||||
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||||
|
{
|
||||||
|
transform.SetAsLastSibling();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowBasic(string title)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("MessageBoxDialog missing: " + title);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.ShowMessageBox(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Show(string title, string message, Action<bool> onResult = null)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("MessageBoxDialog missing: " + title);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.ShowMessageBox(title, message, onResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestroy()
|
||||||
|
{
|
||||||
|
KillTweens();
|
||||||
|
if (instance == this)
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
Button basicButton = basicMessageBox != null ? basicMessageBox.GetComponent<Button>() : null;
|
||||||
|
if (basicButton != null)
|
||||||
|
basicButton.onClick.AddListener(HideMessageBox);
|
||||||
|
|
||||||
|
if (btnOk != null)
|
||||||
|
btnOk.onClick.AddListener(HideMessageBox);
|
||||||
|
if (btnYes != null)
|
||||||
|
btnYes.onClick.AddListener(() => OnQuestionDialog(true));
|
||||||
|
if (btnNo != null)
|
||||||
|
btnNo.onClick.AddListener(() => OnQuestionDialog(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnQuestionDialog(bool val)
|
||||||
|
{
|
||||||
|
Action<bool> callback = onYesOrNo;
|
||||||
|
onYesOrNo = null;
|
||||||
|
HideMessageBox();
|
||||||
|
callback?.Invoke(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowMessageBox(string title)
|
||||||
|
{
|
||||||
|
PrepareShow();
|
||||||
|
HideBoxImmediate(messageBox);
|
||||||
|
|
||||||
|
if (basicMessageText != null)
|
||||||
|
basicMessageText.text = title;
|
||||||
|
|
||||||
|
AnimateIn(basicMessageBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowMessageBox(string title, string message, Action<bool> onResult = null)
|
||||||
|
{
|
||||||
|
PrepareShow();
|
||||||
|
HideBoxImmediate(basicMessageBox);
|
||||||
|
|
||||||
|
txtTitle.text = title;
|
||||||
|
txtMessage.text = message;
|
||||||
|
|
||||||
|
bool isQuestion = onResult != null;
|
||||||
|
btnNo.gameObject.SetActive(isQuestion);
|
||||||
|
btnYes.gameObject.SetActive(isQuestion);
|
||||||
|
btnOk.gameObject.SetActive(!isQuestion);
|
||||||
|
|
||||||
|
onYesOrNo = onResult;
|
||||||
|
AnimateIn(messageBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HideMessageBox()
|
||||||
|
{
|
||||||
|
bool basicActive = basicMessageBox != null && basicMessageBox.gameObject.activeSelf;
|
||||||
|
bool fullActive = messageBox != null && messageBox.gameObject.activeSelf;
|
||||||
|
if (!basicActive && !fullActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (basicActive && fullActive)
|
||||||
|
HideBoxImmediate(basicMessageBox);
|
||||||
|
|
||||||
|
AnimateOut(basicActive && !fullActive ? basicMessageBox : messageBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareShow()
|
||||||
|
{
|
||||||
|
KillTweens();
|
||||||
|
onYesOrNo = null;
|
||||||
|
transform.SetAsLastSibling();
|
||||||
|
|
||||||
|
dimmer.gameObject.SetActive(true);
|
||||||
|
if (dimmerImg != null)
|
||||||
|
{
|
||||||
|
Color c = dimmerImg.color;
|
||||||
|
dimmerImg.color = new Color(c.r, c.g, c.b, 0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HideBoxImmediate(RectTransform box)
|
||||||
|
{
|
||||||
|
if (box == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
box.DOKill();
|
||||||
|
box.localScale = Vector3.zero;
|
||||||
|
box.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimateIn(RectTransform box)
|
||||||
|
{
|
||||||
|
box.localScale = Vector3.zero;
|
||||||
|
box.gameObject.SetActive(true);
|
||||||
|
|
||||||
|
showHideSequence = DOTween.Sequence().SetUpdate(true);
|
||||||
|
if (dimmerImg != null)
|
||||||
|
showHideSequence.Join(dimmerImg.DOFade(0.8f, ANIMATION_DURATION));
|
||||||
|
showHideSequence.Join(box.DOScale(1f, ANIMATION_DURATION).SetEase(Ease.OutBack));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimateOut(RectTransform box)
|
||||||
|
{
|
||||||
|
KillTweens();
|
||||||
|
|
||||||
|
RectTransform targetBox = box;
|
||||||
|
showHideSequence = DOTween.Sequence().SetUpdate(true);
|
||||||
|
if (dimmerImg != null)
|
||||||
|
showHideSequence.Join(dimmerImg.DOFade(0f, ANIMATION_DURATION));
|
||||||
|
showHideSequence.Join(targetBox.DOScale(0f, ANIMATION_DURATION).SetEase(Ease.InBack));
|
||||||
|
showHideSequence.OnComplete(() =>
|
||||||
|
{
|
||||||
|
if (targetBox != null)
|
||||||
|
targetBox.gameObject.SetActive(false);
|
||||||
|
if (dimmer != null)
|
||||||
|
dimmer.gameObject.SetActive(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void KillTweens()
|
||||||
|
{
|
||||||
|
if (showHideSequence != null && showHideSequence.IsActive())
|
||||||
|
showHideSequence.Kill();
|
||||||
|
showHideSequence = null;
|
||||||
|
|
||||||
|
if (dimmerImg != null)
|
||||||
|
dimmerImg.DOKill();
|
||||||
|
if (basicMessageBox != null)
|
||||||
|
basicMessageBox.DOKill();
|
||||||
|
if (messageBox != null)
|
||||||
|
messageBox.DOKill();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c50f3cfa5a1c65b4c8b0a011d602717a
|
||||||
@@ -4,6 +4,7 @@ using DG.Tweening;
|
|||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
|
using Mirror;
|
||||||
public class LevelLoadManager : MonoBehaviour
|
public class LevelLoadManager : MonoBehaviour
|
||||||
{
|
{
|
||||||
public static LevelLoadManager instance;
|
public static LevelLoadManager instance;
|
||||||
@@ -87,6 +88,7 @@ public class LevelLoadManager : MonoBehaviour
|
|||||||
|
|
||||||
|
|
||||||
public void Show(){
|
public void Show(){
|
||||||
|
if(isShowing){return;} //already is showing
|
||||||
canvasGroup.blocksRaycasts=true;
|
canvasGroup.blocksRaycasts=true;
|
||||||
canvasGroup.interactable=true;
|
canvasGroup.interactable=true;
|
||||||
contentImage.fillOrigin = 0;
|
contentImage.fillOrigin = 0;
|
||||||
@@ -180,4 +182,26 @@ public class LevelLoadManager : MonoBehaviour
|
|||||||
p2_l10.text = $"L10 {opponentPlayer.l10_wins}-{opponentPlayer.l10_losses}";
|
p2_l10.text = $"L10 {opponentPlayer.l10_wins}-{opponentPlayer.l10_losses}";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Leave(string levelName = "MainMenu"){
|
||||||
|
NetManager.StopNetworkingForSceneChange();
|
||||||
|
|
||||||
|
StartCoroutine(CoroutineWaitTillDisconnection(levelName));
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator CoroutineWaitTillDisconnection(string levelName){
|
||||||
|
Show(); //Load the UI until the level is loaded
|
||||||
|
const float timeoutSeconds = 5f;
|
||||||
|
float elapsed = 0f;
|
||||||
|
while ((NetworkClient.isConnected || NetworkClient.active || NetworkServer.active) && elapsed < timeoutSeconds)
|
||||||
|
{
|
||||||
|
elapsed += Time.unscaledDeltaTime;
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return new WaitForSeconds(0.25f);
|
||||||
|
|
||||||
|
LoadLevel(levelName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ public class UserData
|
|||||||
{
|
{
|
||||||
public int id;
|
public int id;
|
||||||
public string username;
|
public string username;
|
||||||
|
public string email;
|
||||||
public int cc;
|
public int cc;
|
||||||
public int rc;
|
public int rc;
|
||||||
public string created_at;
|
public string created_at;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class PingReportApiResponse
|
||||||
|
{
|
||||||
|
public bool ok;
|
||||||
|
public string error;
|
||||||
|
public PingReportData report;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 76e4e69960816204d8a01d0e534e0bf4
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class PingReportData
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
public int user_id;
|
||||||
|
public int match_id;
|
||||||
|
public int ping;
|
||||||
|
public string ip_address;
|
||||||
|
public string created_at;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b3f6089d3b18cb34482d7de0cd8bca51
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class PlayerProfileApiResponse
|
||||||
|
{
|
||||||
|
public bool ok;
|
||||||
|
public string error;
|
||||||
|
public int player_id;
|
||||||
|
public string username;
|
||||||
|
public string email;
|
||||||
|
public int days_since_registration;
|
||||||
|
public int matches_played;
|
||||||
|
public float win_ratio;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: acfc3a6cd73c2234081774fcdc39082a
|
||||||
@@ -0,0 +1,456 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using DG.Tweening;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Events;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(EventTrigger))]
|
||||||
|
public class CardCarousel : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] RectTransform[] items;
|
||||||
|
[SerializeField] float spacing = 40f;
|
||||||
|
[SerializeField] float swipeThreshold = 60f;
|
||||||
|
[SerializeField] float snapDuration = 0.25f;
|
||||||
|
[SerializeField] Ease snapEase = Ease.OutCubic;
|
||||||
|
[Tooltip("Horizontal half-width of the center tap zone, in local units of this RectTransform.")]
|
||||||
|
[SerializeField] float centerTapHalfWidth = 260f;
|
||||||
|
[SerializeField] bool looped = true;
|
||||||
|
[SerializeField] int selectedIndex;
|
||||||
|
[Header("Focus Animation")]
|
||||||
|
[SerializeField] float activeScale = 1.05f;
|
||||||
|
[SerializeField] float inactiveScale = 0.88f;
|
||||||
|
[SerializeField] float activePullDown = 35f;
|
||||||
|
|
||||||
|
public UnityEvent<int> onSelected = new UnityEvent<int>();
|
||||||
|
|
||||||
|
EventTrigger eventTrigger;
|
||||||
|
RectTransform rectTransform;
|
||||||
|
RectTransform dragSpace;
|
||||||
|
RectTransform duplicateClone;
|
||||||
|
readonly Dictionary<RectTransform, Vector3> baseScales = new();
|
||||||
|
|
||||||
|
float slotStep;
|
||||||
|
float focusX;
|
||||||
|
float focusY;
|
||||||
|
float focusPosition;
|
||||||
|
float dragStartPosition;
|
||||||
|
float pointerDownLocalX;
|
||||||
|
bool isDragging;
|
||||||
|
bool isAnimating;
|
||||||
|
Tween snapTween;
|
||||||
|
|
||||||
|
public int SelectedIndex => selectedIndex;
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
rectTransform = (RectTransform)transform;
|
||||||
|
eventTrigger = GetComponent<EventTrigger>();
|
||||||
|
RegisterTrigger(EventTriggerType.PointerDown, OnPointerDown);
|
||||||
|
RegisterTrigger(EventTriggerType.Drag, OnDrag);
|
||||||
|
RegisterTrigger(EventTriggerType.PointerUp, OnPointerUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
if (items == null || items.Length < 2)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"{nameof(CardCarousel)} on {name} needs at least 2 items.", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedIndex = Mathf.Clamp(selectedIndex, 0, items.Length - 1);
|
||||||
|
dragSpace = items[0].parent as RectTransform;
|
||||||
|
slotStep = items[0].rect.width + spacing;
|
||||||
|
focusX = items[0].anchoredPosition.x;
|
||||||
|
focusY = items[0].anchoredPosition.y;
|
||||||
|
|
||||||
|
duplicateClone = CreateClone(items[0], "_carouselClone_Dup");
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
CacheBaseScale(item);
|
||||||
|
|
||||||
|
focusPosition = selectedIndex;
|
||||||
|
ApplyLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestroy()
|
||||||
|
{
|
||||||
|
snapTween?.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterTrigger(EventTriggerType type, Action<BaseEventData> handler)
|
||||||
|
{
|
||||||
|
var entry = new EventTrigger.Entry { eventID = type };
|
||||||
|
entry.callback.AddListener(handler.Invoke);
|
||||||
|
eventTrigger.triggers.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
RectTransform CreateClone(RectTransform source, string suffix)
|
||||||
|
{
|
||||||
|
var clone = Instantiate(source, source.parent);
|
||||||
|
clone.name = source.name + suffix;
|
||||||
|
DisableInteractions(clone);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DisableInteractions(RectTransform target)
|
||||||
|
{
|
||||||
|
foreach (var graphic in target.GetComponentsInChildren<Graphic>(true))
|
||||||
|
graphic.raycastTarget = false;
|
||||||
|
|
||||||
|
foreach (var button in target.GetComponentsInChildren<Button>(true))
|
||||||
|
button.interactable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPointerDown(BaseEventData eventData)
|
||||||
|
{
|
||||||
|
if (!TryGetPointer(eventData, out var pointer) || isAnimating || items == null || items.Length < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
snapTween?.Kill();
|
||||||
|
isAnimating = false;
|
||||||
|
isDragging = false;
|
||||||
|
dragStartPosition = focusPosition;
|
||||||
|
pointerDownLocalX = GetLocalDragX(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDrag(BaseEventData eventData)
|
||||||
|
{
|
||||||
|
if (!TryGetPointer(eventData, out var pointer) || items == null || items.Length < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isDragging = true;
|
||||||
|
float delta = GetLocalDragX(pointer) - pointerDownLocalX;
|
||||||
|
focusPosition = dragStartPosition - delta / slotStep;
|
||||||
|
if (!looped)
|
||||||
|
focusPosition = Mathf.Clamp(focusPosition, 0f, items.Length - 1);
|
||||||
|
|
||||||
|
ApplyLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPointerUp(BaseEventData eventData)
|
||||||
|
{
|
||||||
|
if (!TryGetPointer(eventData, out var pointer) || isAnimating || items == null || items.Length < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float pointerDelta = GetLocalDragX(pointer) - pointerDownLocalX;
|
||||||
|
float dragDelta = focusPosition - dragStartPosition;
|
||||||
|
bool passedSwipeThreshold = Mathf.Abs(pointerDelta) >= swipeThreshold
|
||||||
|
|| Mathf.Abs(dragDelta * slotStep) >= swipeThreshold;
|
||||||
|
|
||||||
|
if (!passedSwipeThreshold)
|
||||||
|
{
|
||||||
|
if (TryGetTapZone(pointer, out int tapDirection))
|
||||||
|
{
|
||||||
|
if (tapDirection == 0)
|
||||||
|
SelectCurrent();
|
||||||
|
else if (CanMoveBy(tapDirection))
|
||||||
|
AnimateToDirection(tapDirection);
|
||||||
|
else
|
||||||
|
SnapTo(selectedIndex, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SnapTo(selectedIndex, null);
|
||||||
|
|
||||||
|
isDragging = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDragging = false;
|
||||||
|
|
||||||
|
int direction = dragDelta > 0f ? 1 : -1;
|
||||||
|
if (Mathf.Abs(focusPosition - selectedIndex) > 0.5f)
|
||||||
|
{
|
||||||
|
int nearest = Mathf.RoundToInt(focusPosition);
|
||||||
|
if (!looped)
|
||||||
|
nearest = Mathf.Clamp(nearest, 0, items.Length - 1);
|
||||||
|
if (nearest != selectedIndex && CanMoveBy(nearest > selectedIndex ? 1 : -1))
|
||||||
|
{
|
||||||
|
AnimateTo(nearest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanMoveBy(direction))
|
||||||
|
{
|
||||||
|
SnapTo(selectedIndex, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimateToDirection(direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetLocalDragX(PointerEventData pointer)
|
||||||
|
{
|
||||||
|
if (dragSpace == null)
|
||||||
|
return pointer.position.x;
|
||||||
|
|
||||||
|
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||||
|
dragSpace, pointer.position, pointer.pressEventCamera, out var local);
|
||||||
|
return local.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryGetTapZone(PointerEventData pointer, out int direction)
|
||||||
|
{
|
||||||
|
direction = 0;
|
||||||
|
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||||
|
rectTransform, pointer.position, pointer.pressEventCamera, out var local))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Mathf.Abs(local.x) <= centerTapHalfWidth)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
direction = local.x < 0f ? -1 : 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectCurrent()
|
||||||
|
{
|
||||||
|
if (Mathf.Approximately(focusPosition, selectedIndex))
|
||||||
|
onSelected.Invoke(selectedIndex);
|
||||||
|
else
|
||||||
|
SnapTo(selectedIndex, () => onSelected.Invoke(selectedIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimateToDirection(int direction)
|
||||||
|
{
|
||||||
|
int newIndex = GetAdjacentIndex(direction, items.Length);
|
||||||
|
if (newIndex < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AnimateTo(newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimateTo(int newIndex)
|
||||||
|
{
|
||||||
|
isAnimating = true;
|
||||||
|
|
||||||
|
snapTween?.Kill();
|
||||||
|
snapTween = DOTween.To(
|
||||||
|
() => focusPosition,
|
||||||
|
value =>
|
||||||
|
{
|
||||||
|
focusPosition = value;
|
||||||
|
ApplyLayout();
|
||||||
|
},
|
||||||
|
newIndex,
|
||||||
|
snapDuration)
|
||||||
|
.SetEase(snapEase)
|
||||||
|
.OnComplete(() => CompleteSlide(newIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompleteSlide(int newIndex)
|
||||||
|
{
|
||||||
|
selectedIndex = newIndex;
|
||||||
|
focusPosition = newIndex;
|
||||||
|
isAnimating = false;
|
||||||
|
ApplyLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SnapTo(float targetPosition, Action onComplete)
|
||||||
|
{
|
||||||
|
isAnimating = true;
|
||||||
|
snapTween?.Kill();
|
||||||
|
snapTween = DOTween.To(
|
||||||
|
() => focusPosition,
|
||||||
|
value =>
|
||||||
|
{
|
||||||
|
focusPosition = value;
|
||||||
|
ApplyLayout();
|
||||||
|
},
|
||||||
|
targetPosition,
|
||||||
|
snapDuration)
|
||||||
|
.SetEase(snapEase)
|
||||||
|
.OnComplete(() =>
|
||||||
|
{
|
||||||
|
isAnimating = false;
|
||||||
|
onComplete?.Invoke();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetItemOffset(int itemIndex)
|
||||||
|
{
|
||||||
|
return (itemIndex - focusPosition) * slotStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsItemVisible(float offset)
|
||||||
|
{
|
||||||
|
return Mathf.Abs(offset) <= slotStep * 1.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyLayout()
|
||||||
|
{
|
||||||
|
int count = items.Length;
|
||||||
|
bool useDuplicate = looped && count == 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
float offset = GetItemOffset(i);
|
||||||
|
if (IsItemVisible(offset))
|
||||||
|
{
|
||||||
|
SetItemTransform(items[i], offset);
|
||||||
|
items[i].gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
items[i].gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useDuplicate && Mathf.Approximately(focusPosition, Mathf.Round(focusPosition)))
|
||||||
|
{
|
||||||
|
int restIndex = Mathf.RoundToInt(focusPosition);
|
||||||
|
int otherIndex = 1 - restIndex;
|
||||||
|
float otherOffset = GetItemOffset(otherIndex);
|
||||||
|
float duplicateOffset = -otherOffset;
|
||||||
|
|
||||||
|
if (IsItemVisible(duplicateOffset))
|
||||||
|
{
|
||||||
|
SyncDuplicateClone(items[otherIndex]);
|
||||||
|
SetItemTransform(duplicateClone, duplicateOffset);
|
||||||
|
duplicateClone.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
else if (duplicateClone != null)
|
||||||
|
duplicateClone.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
else if (duplicateClone != null)
|
||||||
|
{
|
||||||
|
duplicateClone.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int topIndex = 0;
|
||||||
|
float bestFocus = float.MaxValue;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (!items[i].gameObject.activeSelf)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float focus = GetFocusT(GetItemOffset(i));
|
||||||
|
if (focus < bestFocus)
|
||||||
|
{
|
||||||
|
bestFocus = focus;
|
||||||
|
topIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items[topIndex].SetAsLastSibling();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncDuplicateClone(RectTransform source)
|
||||||
|
{
|
||||||
|
string expectedName = source.name + "_carouselClone_Dup";
|
||||||
|
if (duplicateClone == null || duplicateClone.name != expectedName)
|
||||||
|
{
|
||||||
|
var parent = source.parent;
|
||||||
|
int sibling = duplicateClone != null ? duplicateClone.GetSiblingIndex() : source.GetSiblingIndex();
|
||||||
|
if (duplicateClone != null)
|
||||||
|
{
|
||||||
|
baseScales.Remove(duplicateClone);
|
||||||
|
Destroy(duplicateClone.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicateClone = CreateClone(source, "_carouselClone_Dup");
|
||||||
|
duplicateClone.SetParent(parent, false);
|
||||||
|
duplicateClone.SetSiblingIndex(sibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheBaseScale(duplicateClone, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CacheBaseScale(RectTransform item, RectTransform source = null)
|
||||||
|
{
|
||||||
|
if (baseScales.ContainsKey(item))
|
||||||
|
return;
|
||||||
|
|
||||||
|
baseScales[item] = source != null ? source.localScale : item.localScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetItemTransform(RectTransform item, float xOffset)
|
||||||
|
{
|
||||||
|
CacheBaseScale(item);
|
||||||
|
|
||||||
|
float focusT = GetFocusT(xOffset);
|
||||||
|
float scale = Mathf.Lerp(activeScale, inactiveScale, focusT);
|
||||||
|
item.localScale = baseScales[item] * scale;
|
||||||
|
|
||||||
|
var pos = item.anchoredPosition;
|
||||||
|
pos.x = focusX + xOffset;
|
||||||
|
pos.y = focusY - Mathf.Lerp(activePullDown, 0f, focusT);
|
||||||
|
item.anchoredPosition = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetFocusT(float xOffset)
|
||||||
|
{
|
||||||
|
if (slotStep <= 0f)
|
||||||
|
return 0f;
|
||||||
|
|
||||||
|
float t = Mathf.Clamp01(Mathf.Abs(xOffset) / slotStep);
|
||||||
|
return t * t * (3f - 2f * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int WrapIndex(int index, int count)
|
||||||
|
{
|
||||||
|
if (count <= 0)
|
||||||
|
return 0;
|
||||||
|
return ((index % count) + count) % count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetAdjacentIndex(int direction, int count)
|
||||||
|
{
|
||||||
|
int index = selectedIndex + direction;
|
||||||
|
if (looped)
|
||||||
|
return WrapIndex(index, count);
|
||||||
|
return index >= 0 && index < count ? index : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanMoveBy(int direction)
|
||||||
|
{
|
||||||
|
if (items == null || items.Length == 0)
|
||||||
|
return false;
|
||||||
|
return GetAdjacentIndex(direction, items.Length) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NormalizeIndex(int index)
|
||||||
|
{
|
||||||
|
if (items == null || items.Length == 0)
|
||||||
|
return 0;
|
||||||
|
return looped ? WrapIndex(index, items.Length) : Mathf.Clamp(index, 0, items.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryGetPointer(BaseEventData eventData, out PointerEventData pointer)
|
||||||
|
{
|
||||||
|
pointer = eventData as PointerEventData;
|
||||||
|
return pointer != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSelectedIndex(int index, bool animate = false)
|
||||||
|
{
|
||||||
|
if (items == null || items.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int target = NormalizeIndex(index);
|
||||||
|
if (target == selectedIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!animate || !isActiveAndEnabled)
|
||||||
|
{
|
||||||
|
snapTween?.Kill();
|
||||||
|
isAnimating = false;
|
||||||
|
selectedIndex = target;
|
||||||
|
focusPosition = target;
|
||||||
|
ApplyLayout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int direction = target > selectedIndex ? 1 : -1;
|
||||||
|
if (!CanMoveBy(direction) || Mathf.Abs(target - selectedIndex) != 1)
|
||||||
|
{
|
||||||
|
selectedIndex = target;
|
||||||
|
focusPosition = target;
|
||||||
|
ApplyLayout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimateToDirection(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a4951ec6b41c5d14cb8f5a8803dfa0c2
|
||||||
@@ -3,9 +3,9 @@ using UnityEngine.Rendering;
|
|||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inverted stencil mask (see materialForRendering). For raycasts, assign
|
/// Inverted stencil mask (see materialForRendering). Raycast options live on
|
||||||
/// <see cref="raycastPassThroughRects"/> to rects that match each visual "hole"
|
/// <see cref="CutoutMaskUISettings"/> on the same GameObject (Unity's Image
|
||||||
/// — clicks there won’t hit this graphic and pass to UI behind.
|
/// inspector does not show fields on Image subclasses).
|
||||||
///
|
///
|
||||||
/// Parent <see cref="Mask"/> also implements <see cref="ICanvasRaycastFilter"/> and only
|
/// Parent <see cref="Mask"/> also implements <see cref="ICanvasRaycastFilter"/> and only
|
||||||
/// allows points inside its RectTransform. A fullscreen child under a small mask would
|
/// allows points inside its RectTransform. A fullscreen child under a small mask would
|
||||||
@@ -15,13 +15,10 @@ using UnityEngine.UI;
|
|||||||
/// ignoreMasks) so hit testing matches the fullscreen Image; rendering still uses stencil
|
/// ignoreMasks) so hit testing matches the fullscreen Image; rendering still uses stencil
|
||||||
/// because <see cref="MaskableGraphic.maskable"/> stays true.
|
/// because <see cref="MaskableGraphic.maskable"/> stays true.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[RequireComponent(typeof(CutoutMaskUISettings))]
|
||||||
public class CutoutMaskUI : Image
|
public class CutoutMaskUI : Image
|
||||||
{
|
{
|
||||||
[Tooltip("Rects matching cutout/hole areas. Clicks inside are not consumed by this Image. Leave empty to use the parent Mask’s RectTransform.")]
|
CutoutMaskUISettings settings;
|
||||||
[SerializeField] RectTransform[] raycastPassThroughRects;
|
|
||||||
|
|
||||||
[Tooltip("The Image on the same object as Mask is usually the cutout shape and still receives raycasts. When this is on, that graphic’s Raycast Target is turned off while this component is enabled so the hole reaches UI behind the overlay.")]
|
|
||||||
[SerializeField] bool disableRaycastOnParentMaskGraphic = true;
|
|
||||||
|
|
||||||
Mask cachedParentMask;
|
Mask cachedParentMask;
|
||||||
Graphic cachedMaskGraphic;
|
Graphic cachedMaskGraphic;
|
||||||
@@ -33,8 +30,10 @@ public class CutoutMaskUI : Image
|
|||||||
return Raycast(sp, eventCamera, ignoreMasks: true);
|
return Raycast(sp, eventCamera, ignoreMasks: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Material materialForRendering{
|
public override Material materialForRendering
|
||||||
get{
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
Material material = new Material(base.materialForRendering);
|
Material material = new Material(base.materialForRendering);
|
||||||
material.SetInt("_StencilComp", (int)CompareFunction.NotEqual);
|
material.SetInt("_StencilComp", (int)CompareFunction.NotEqual);
|
||||||
return material;
|
return material;
|
||||||
@@ -43,8 +42,10 @@ public class CutoutMaskUI : Image
|
|||||||
|
|
||||||
protected override void OnEnable()
|
protected override void OnEnable()
|
||||||
{
|
{
|
||||||
|
settings = GetComponent<CutoutMaskUISettings>();
|
||||||
base.OnEnable();
|
base.OnEnable();
|
||||||
if (!disableRaycastOnParentMaskGraphic)
|
|
||||||
|
if (settings == null || !settings.disableRaycastOnParentMaskGraphic)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cachedParentMask = GetComponentInParent<Mask>();
|
cachedParentMask = GetComponentInParent<Mask>();
|
||||||
@@ -76,18 +77,19 @@ public class CutoutMaskUI : Image
|
|||||||
public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
|
public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
|
||||||
{
|
{
|
||||||
if (IsInsidePassThroughHole(screenPoint, eventCamera))
|
if (IsInsidePassThroughHole(screenPoint, eventCamera))
|
||||||
return false;
|
return settings != null && settings.blockRaycastThroughCutout;
|
||||||
|
|
||||||
return base.IsRaycastLocationValid(screenPoint, eventCamera);
|
return base.IsRaycastLocationValid(screenPoint, eventCamera);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInsidePassThroughHole(Vector2 screenPoint, Camera eventCamera)
|
bool IsInsidePassThroughHole(Vector2 screenPoint, Camera eventCamera)
|
||||||
{
|
{
|
||||||
if (raycastPassThroughRects != null)
|
RectTransform[] passThroughRects = settings != null ? settings.raycastPassThroughRects : null;
|
||||||
|
if (passThroughRects != null)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < raycastPassThroughRects.Length; i++)
|
for (int i = 0; i < passThroughRects.Length; i++)
|
||||||
{
|
{
|
||||||
RectTransform rect = raycastPassThroughRects[i];
|
RectTransform rect = passThroughRects[i];
|
||||||
if (rect != null && RectTransformUtility.RectangleContainsScreenPoint(rect, screenPoint, eventCamera))
|
if (rect != null && RectTransformUtility.RectangleContainsScreenPoint(rect, screenPoint, eventCamera))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
[RequireComponent(typeof(CutoutMaskUI))]
|
||||||
|
public class CutoutMaskUISettings : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Tooltip("Rects matching cutout/hole areas. Clicks inside are not consumed by this Image. Leave empty to use the parent Mask's RectTransform.")]
|
||||||
|
public RectTransform[] raycastPassThroughRects;
|
||||||
|
|
||||||
|
[Tooltip("The Image on the same object as Mask is usually the cutout shape and still receives raycasts. When this is on, that graphic's Raycast Target is turned off while this component is enabled so the hole reaches UI behind the overlay.")]
|
||||||
|
public bool disableRaycastOnParentMaskGraphic = true;
|
||||||
|
|
||||||
|
[Tooltip("When enabled, clicks inside cutout/hole areas are consumed by this Image instead of passing through to UI behind.")]
|
||||||
|
public bool blockRaycastThroughCutout;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 29b95265513fc314995c59cb90745214
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 05aa9e67ef3520047a8c20a6f96165d8
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -19,6 +19,7 @@ public class FrameAnimator : MonoBehaviour
|
|||||||
bool m_previewPlaying = false;
|
bool m_previewPlaying = false;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
double m_lastEditorTime = 0d;
|
double m_lastEditorTime = 0d;
|
||||||
|
bool m_editorHooked;
|
||||||
#endif
|
#endif
|
||||||
Image m_img;
|
Image m_img;
|
||||||
Image img {
|
Image img {
|
||||||
@@ -42,19 +43,19 @@ public class FrameAnimator : MonoBehaviour
|
|||||||
void OnEnable()
|
void OnEnable()
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
if (!Application.isPlaying)
|
// Never hook editor updates during play-mode enter/exit — QueuePlayerLoopUpdate
|
||||||
{
|
// + isPlaying serialized true can infinite-reload Temp/__Backupscenes.
|
||||||
m_lastEditorTime = EditorApplication.timeSinceStartup;
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||||
EditorApplication.update -= OnEditorUpdate;
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||||
EditorApplication.update += OnEditorUpdate;
|
TryHookEditorUpdate();
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnDisable()
|
void OnDisable()
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
EditorApplication.update -= OnEditorUpdate;
|
UnhookEditorUpdate();
|
||||||
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void Start()
|
void Start()
|
||||||
@@ -80,23 +81,71 @@ public class FrameAnimator : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
void OnEditorUpdate()
|
void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||||
{
|
{
|
||||||
if (Application.isPlaying || this == null || !isActiveAndEnabled)
|
if (state == PlayModeStateChange.ExitingEditMode ||
|
||||||
|
state == PlayModeStateChange.EnteredPlayMode ||
|
||||||
|
state == PlayModeStateChange.ExitingPlayMode)
|
||||||
{
|
{
|
||||||
|
m_previewPlaying = false;
|
||||||
|
UnhookEditorUpdate();
|
||||||
|
}
|
||||||
|
else if (state == PlayModeStateChange.EnteredEditMode)
|
||||||
|
{
|
||||||
|
TryHookEditorUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TryHookEditorUpdate()
|
||||||
|
{
|
||||||
|
if (Application.isPlaying || EditorApplication.isPlayingOrWillChangePlaymode)
|
||||||
|
{
|
||||||
|
UnhookEditorUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_editorHooked)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_lastEditorTime = EditorApplication.timeSinceStartup;
|
||||||
|
EditorApplication.update += OnEditorUpdate;
|
||||||
|
m_editorHooked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnhookEditorUpdate()
|
||||||
|
{
|
||||||
|
if (!m_editorHooked)
|
||||||
|
return;
|
||||||
|
EditorApplication.update -= OnEditorUpdate;
|
||||||
|
m_editorHooked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnEditorUpdate()
|
||||||
|
{
|
||||||
|
if (Application.isPlaying ||
|
||||||
|
EditorApplication.isPlayingOrWillChangePlaymode ||
|
||||||
|
this == null ||
|
||||||
|
!isActiveAndEnabled)
|
||||||
|
{
|
||||||
|
UnhookEditorUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edit-mode animation is preview-only. Ignoring serialized isPlaying prevents
|
||||||
|
// permanent editor loops when isPlaying was left true in the scene.
|
||||||
|
if (!m_previewPlaying)
|
||||||
|
return;
|
||||||
|
|
||||||
double now = EditorApplication.timeSinceStartup;
|
double now = EditorApplication.timeSinceStartup;
|
||||||
float dt = (float)(now - m_lastEditorTime);
|
float dt = (float)(now - m_lastEditorTime);
|
||||||
m_lastEditorTime = now;
|
m_lastEditorTime = now;
|
||||||
|
|
||||||
if (dt <= 0f)
|
if (dt <= 0f)
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
|
int frameBefore = currentFrame;
|
||||||
TickAnimation(dt);
|
TickAnimation(dt);
|
||||||
|
if (currentFrame != frameBefore || m_previewPlaying)
|
||||||
EditorApplication.QueuePlayerLoopUpdate();
|
EditorApplication.QueuePlayerLoopUpdate();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -108,7 +157,9 @@ public class FrameAnimator : MonoBehaviour
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldAnimate = m_previewPlaying || isPlaying;
|
bool shouldAnimate = Application.isPlaying
|
||||||
|
? (m_previewPlaying || isPlaying)
|
||||||
|
: m_previewPlaying;
|
||||||
if (!shouldAnimate)
|
if (!shouldAnimate)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
%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
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_AssetVersion: 1
|
||||||
|
m_BuildTarget: 13
|
||||||
|
m_Subtarget: 0
|
||||||
|
m_PlatformId: b9b35072a6f44c2e863f17467ea3dc13
|
||||||
|
m_PlatformBuildProfile:
|
||||||
|
rid: 1400252592429989960
|
||||||
|
m_OverrideGlobalSceneList: 1
|
||||||
|
m_Scenes:
|
||||||
|
- m_enabled: 1
|
||||||
|
m_path: Assets/Scenes/Intro.unity
|
||||||
|
- m_enabled: 1
|
||||||
|
m_path: Assets/Scenes/MainMenu.unity
|
||||||
|
- m_enabled: 1
|
||||||
|
m_path: Assets/Scenes/Game.unity
|
||||||
|
m_ScriptingDefines: []
|
||||||
|
m_PlayerSettingsYaml:
|
||||||
|
m_Settings: []
|
||||||
|
references:
|
||||||
|
version: 2
|
||||||
|
RefIds:
|
||||||
|
- rid: 1400252592429989960
|
||||||
|
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_BuildSubtarget: 0
|
||||||
|
m_BuildSystem: 1
|
||||||
|
m_ExportAsGoogleAndroidProject: 0
|
||||||
|
m_DebugSymbolLevel: 1
|
||||||
|
m_DebugSymbolFormat: 5
|
||||||
|
m_CurrentDeploymentTargetId: __builtin__target_default
|
||||||
|
m_BuildType: 2
|
||||||
|
m_BuildAppBundle: 0
|
||||||
|
m_IPAddressToConnect:
|
||||||
|
m_SymlinkSources: 0
|
||||||
@@ -1,863 +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: "New Android\u2122 Profile"
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
m_AssetVersion: 1
|
|
||||||
m_BuildTarget: 13
|
|
||||||
m_Subtarget: 0
|
|
||||||
m_PlatformId: b9b35072a6f44c2e863f17467ea3dc13
|
|
||||||
m_PlatformBuildProfile:
|
|
||||||
rid: 1400252592429989960
|
|
||||||
m_OverrideGlobalSceneList: 1
|
|
||||||
m_Scenes:
|
|
||||||
- m_enabled: 1
|
|
||||||
m_path: Assets/Scenes/MainMenu.unity
|
|
||||||
- m_enabled: 1
|
|
||||||
m_path: Assets/Scenes/Proto.unity
|
|
||||||
m_ScriptingDefines: []
|
|
||||||
m_PlayerSettingsYaml:
|
|
||||||
m_Settings:
|
|
||||||
- line: '| PlayerSettings:'
|
|
||||||
- line: '| m_ObjectHideFlags: 0'
|
|
||||||
- line: '| serializedVersion: 28'
|
|
||||||
- line: '| productGUID: 73659953e96de4447bb9eeb64c3da355'
|
|
||||||
- line: '| AndroidProfiler: 0'
|
|
||||||
- line: '| AndroidFilterTouchesWhenObscured: 0'
|
|
||||||
- line: '| AndroidEnableSustainedPerformanceMode: 0'
|
|
||||||
- line: '| defaultScreenOrientation: 4'
|
|
||||||
- line: '| targetDevice: 2'
|
|
||||||
- line: '| useOnDemandResources: 0'
|
|
||||||
- line: '| accelerometerFrequency: 60'
|
|
||||||
- line: '| companyName: Xperience'
|
|
||||||
- line: '| productName: WalkInvest_Soccer'
|
|
||||||
- line: '| defaultCursor: {instanceID: 0}'
|
|
||||||
- line: '| cursorHotspot: {x: 0, y: 0}'
|
|
||||||
- line: '| m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b:
|
|
||||||
0.1254902, a: 1}'
|
|
||||||
- line: '| m_ShowUnitySplashScreen: 1'
|
|
||||||
- line: '| m_ShowUnitySplashLogo: 1'
|
|
||||||
- line: '| m_SplashScreenOverlayOpacity: 1'
|
|
||||||
- line: '| m_SplashScreenAnimation: 1'
|
|
||||||
- line: '| m_SplashScreenLogoStyle: 1'
|
|
||||||
- line: '| m_SplashScreenDrawMode: 0'
|
|
||||||
- line: '| m_SplashScreenBackgroundAnimationZoom: 1'
|
|
||||||
- line: '| m_SplashScreenLogoAnimationZoom: 1'
|
|
||||||
- line: '| m_SplashScreenBackgroundLandscapeAspect: 1'
|
|
||||||
- line: '| m_SplashScreenBackgroundPortraitAspect: 1'
|
|
||||||
- line: '| m_SplashScreenBackgroundLandscapeUvs:'
|
|
||||||
- line: '| serializedVersion: 2'
|
|
||||||
- line: '| x: 0'
|
|
||||||
- line: '| y: 0'
|
|
||||||
- line: '| width: 1'
|
|
||||||
- line: '| height: 1'
|
|
||||||
- line: '| m_SplashScreenBackgroundPortraitUvs:'
|
|
||||||
- line: '| serializedVersion: 2'
|
|
||||||
- line: '| x: 0'
|
|
||||||
- line: '| y: 0'
|
|
||||||
- line: '| width: 1'
|
|
||||||
- line: '| height: 1'
|
|
||||||
- line: '| m_SplashScreenLogos: []'
|
|
||||||
- line: '| m_VirtualRealitySplashScreen: {instanceID: 0}'
|
|
||||||
- line: '| m_HolographicTrackingLossScreen: {instanceID: 0}'
|
|
||||||
- line: '| defaultScreenWidth: 1920'
|
|
||||||
- line: '| defaultScreenHeight: 1080'
|
|
||||||
- line: '| defaultScreenWidthWeb: 960'
|
|
||||||
- line: '| defaultScreenHeightWeb: 600'
|
|
||||||
- line: '| m_StereoRenderingPath: 0'
|
|
||||||
- line: '| m_ActiveColorSpace: 1'
|
|
||||||
- line: '| unsupportedMSAAFallback: 0'
|
|
||||||
- line: '| m_SpriteBatchMaxVertexCount: 65535'
|
|
||||||
- line: '| m_SpriteBatchVertexThreshold: 300'
|
|
||||||
- line: '| m_MTRendering: 1'
|
|
||||||
- line: '| mipStripping: 0'
|
|
||||||
- line: '| numberOfMipsStripped: 0'
|
|
||||||
- line: '| numberOfMipsStrippedPerMipmapLimitGroup: {}'
|
|
||||||
- line: '| m_StackTraceTypes: 010000000100000001000000010000000100000001000000'
|
|
||||||
- line: '| iosShowActivityIndicatorOnLoading: -1'
|
|
||||||
- line: '| androidShowActivityIndicatorOnLoading: -1'
|
|
||||||
- line: '| iosUseCustomAppBackgroundBehavior: 0'
|
|
||||||
- line: '| allowedAutorotateToPortrait: 1'
|
|
||||||
- line: '| allowedAutorotateToPortraitUpsideDown: 1'
|
|
||||||
- line: '| allowedAutorotateToLandscapeRight: 0'
|
|
||||||
- line: '| allowedAutorotateToLandscapeLeft: 0'
|
|
||||||
- line: '| useOSAutorotation: 1'
|
|
||||||
- line: '| use32BitDisplayBuffer: 1'
|
|
||||||
- line: '| preserveFramebufferAlpha: 0'
|
|
||||||
- line: '| disableDepthAndStencilBuffers: 0'
|
|
||||||
- line: '| androidStartInFullscreen: 1'
|
|
||||||
- line: '| androidRenderOutsideSafeArea: 1'
|
|
||||||
- line: '| androidUseSwappy: 1'
|
|
||||||
- line: '| androidBlitType: 0'
|
|
||||||
- line: '| androidResizeableActivity: 1'
|
|
||||||
- line: '| androidDefaultWindowWidth: 1920'
|
|
||||||
- line: '| androidDefaultWindowHeight: 1080'
|
|
||||||
- line: '| androidMinimumWindowWidth: 400'
|
|
||||||
- line: '| androidMinimumWindowHeight: 300'
|
|
||||||
- line: '| androidFullscreenMode: 1'
|
|
||||||
- line: '| androidAutoRotationBehavior: 1'
|
|
||||||
- line: '| androidPredictiveBackSupport: 0'
|
|
||||||
- line: '| androidApplicationEntry: 2'
|
|
||||||
- line: '| defaultIsNativeResolution: 1'
|
|
||||||
- line: '| macRetinaSupport: 1'
|
|
||||||
- line: '| runInBackground: 0'
|
|
||||||
- line: '| muteOtherAudioSources: 0'
|
|
||||||
- line: '| Prepare IOS For Recording: 0'
|
|
||||||
- line: '| Force IOS Speakers When Recording: 0'
|
|
||||||
- line: '| audioSpatialExperience: 0'
|
|
||||||
- line: '| deferSystemGesturesMode: 0'
|
|
||||||
- line: '| hideHomeButton: 0'
|
|
||||||
- line: '| submitAnalytics: 1'
|
|
||||||
- line: '| usePlayerLog: 1'
|
|
||||||
- line: '| dedicatedServerOptimizations: 1'
|
|
||||||
- line: '| bakeCollisionMeshes: 0'
|
|
||||||
- line: '| forceSingleInstance: 0'
|
|
||||||
- line: '| useFlipModelSwapchain: 1'
|
|
||||||
- line: '| resizableWindow: 0'
|
|
||||||
- line: '| useMacAppStoreValidation: 0'
|
|
||||||
- line: '| macAppStoreCategory: public.app-category.games'
|
|
||||||
- line: '| gpuSkinning: 0'
|
|
||||||
- line: '| meshDeformation: 0'
|
|
||||||
- line: '| xboxPIXTextureCapture: 0'
|
|
||||||
- line: '| xboxEnableAvatar: 0'
|
|
||||||
- line: '| xboxEnableKinect: 0'
|
|
||||||
- line: '| xboxEnableKinectAutoTracking: 0'
|
|
||||||
- line: '| xboxEnableFitness: 0'
|
|
||||||
- line: '| visibleInBackground: 1'
|
|
||||||
- line: '| allowFullscreenSwitch: 1'
|
|
||||||
- line: '| fullscreenMode: 1'
|
|
||||||
- line: '| xboxSpeechDB: 0'
|
|
||||||
- line: '| xboxEnableHeadOrientation: 0'
|
|
||||||
- line: '| xboxEnableGuest: 0'
|
|
||||||
- line: '| xboxEnablePIXSampling: 0'
|
|
||||||
- line: '| metalFramebufferOnly: 0'
|
|
||||||
- line: '| xboxOneResolution: 0'
|
|
||||||
- line: '| xboxOneSResolution: 0'
|
|
||||||
- line: '| xboxOneXResolution: 3'
|
|
||||||
- line: '| xboxOneMonoLoggingLevel: 0'
|
|
||||||
- line: '| xboxOneLoggingLevel: 1'
|
|
||||||
- line: '| xboxOneDisableEsram: 0'
|
|
||||||
- line: '| xboxOneEnableTypeOptimization: 0'
|
|
||||||
- line: '| xboxOnePresentImmediateThreshold: 0'
|
|
||||||
- line: '| switchQueueCommandMemory: 1048576'
|
|
||||||
- line: '| switchQueueControlMemory: 16384'
|
|
||||||
- line: '| switchQueueComputeMemory: 262144'
|
|
||||||
- line: '| switchNVNShaderPoolsGranularity: 33554432'
|
|
||||||
- line: '| switchNVNDefaultPoolsGranularity: 16777216'
|
|
||||||
- line: '| switchNVNOtherPoolsGranularity: 16777216'
|
|
||||||
- line: '| switchGpuScratchPoolGranularity: 2097152'
|
|
||||||
- line: '| switchAllowGpuScratchShrinking: 0'
|
|
||||||
- line: '| switchNVNMaxPublicTextureIDCount: 0'
|
|
||||||
- line: '| switchNVNMaxPublicSamplerIDCount: 0'
|
|
||||||
- line: '| switchMaxWorkerMultiple: 8'
|
|
||||||
- line: '| switchNVNGraphicsFirmwareMemory: 32'
|
|
||||||
- line: '| vulkanNumSwapchainBuffers: 3'
|
|
||||||
- line: '| vulkanEnableSetSRGBWrite: 0'
|
|
||||||
- line: '| vulkanEnablePreTransform: 0'
|
|
||||||
- line: '| vulkanEnableLateAcquireNextImage: 0'
|
|
||||||
- line: '| vulkanEnableCommandBufferRecycling: 1'
|
|
||||||
- line: '| loadStoreDebugModeEnabled: 0'
|
|
||||||
- line: '| visionOSBundleVersion: 1.0'
|
|
||||||
- line: '| tvOSBundleVersion: 1.0'
|
|
||||||
- line: '| bundleVersion: 0.4'
|
|
||||||
- line: '| preloadedAssets: []'
|
|
||||||
- line: '| metroInputSource: 0'
|
|
||||||
- line: '| wsaTransparentSwapchain: 0'
|
|
||||||
- line: '| m_HolographicPauseOnTrackingLoss: 1'
|
|
||||||
- line: '| xboxOneDisableKinectGpuReservation: 1'
|
|
||||||
- line: '| xboxOneEnable7thCore: 1'
|
|
||||||
- line: '| vrSettings:'
|
|
||||||
- line: '| enable360StereoCapture: 0'
|
|
||||||
- line: '| isWsaHolographicRemotingEnabled: 0'
|
|
||||||
- line: '| enableFrameTimingStats: 0'
|
|
||||||
- line: '| enableOpenGLProfilerGPURecorders: 1'
|
|
||||||
- line: '| allowHDRDisplaySupport: 0'
|
|
||||||
- line: '| useHDRDisplay: 0'
|
|
||||||
- line: '| hdrBitDepth: 0'
|
|
||||||
- line: '| m_ColorGamuts: 00000000'
|
|
||||||
- line: '| targetPixelDensity: 30'
|
|
||||||
- line: '| resolutionScalingMode: 0'
|
|
||||||
- line: '| resetResolutionOnWindowResize: 0'
|
|
||||||
- line: '| androidSupportedAspectRatio: 1'
|
|
||||||
- line: '| androidMaxAspectRatio: 2.4'
|
|
||||||
- line: '| androidMinAspectRatio: 1'
|
|
||||||
- line: '| applicationIdentifier:'
|
|
||||||
- line: '| Standalone: com.DefaultCompany.2D-URP'
|
|
||||||
- line: '| buildNumber:'
|
|
||||||
- line: '| Standalone: 0'
|
|
||||||
- line: '| VisionOS: 0'
|
|
||||||
- line: '| iPhone: 0'
|
|
||||||
- line: '| tvOS: 0'
|
|
||||||
- line: '| overrideDefaultApplicationIdentifier: 1'
|
|
||||||
- line: '| AndroidBundleVersionCode: 1'
|
|
||||||
- line: '| AndroidMinSdkVersion: 23'
|
|
||||||
- line: '| AndroidTargetSdkVersion: 0'
|
|
||||||
- line: '| AndroidPreferredInstallLocation: 1'
|
|
||||||
- line: '| aotOptions: '
|
|
||||||
- line: '| stripEngineCode: 1'
|
|
||||||
- line: '| iPhoneStrippingLevel: 0'
|
|
||||||
- line: '| iPhoneScriptCallOptimization: 0'
|
|
||||||
- line: '| ForceInternetPermission: 0'
|
|
||||||
- line: '| ForceSDCardPermission: 0'
|
|
||||||
- line: '| CreateWallpaper: 0'
|
|
||||||
- line: '| androidSplitApplicationBinary: 0'
|
|
||||||
- line: '| keepLoadedShadersAlive: 0'
|
|
||||||
- line: '| StripUnusedMeshComponents: 0'
|
|
||||||
- line: '| strictShaderVariantMatching: 0'
|
|
||||||
- line: '| VertexChannelCompressionMask: 4054'
|
|
||||||
- line: '| iPhoneSdkVersion: 988'
|
|
||||||
- line: '| iOSSimulatorArchitecture: 0'
|
|
||||||
- line: '| iOSTargetOSVersionString: 13.0'
|
|
||||||
- line: '| tvOSSdkVersion: 0'
|
|
||||||
- line: '| tvOSSimulatorArchitecture: 0'
|
|
||||||
- line: '| tvOSRequireExtendedGameController: 0'
|
|
||||||
- line: '| tvOSTargetOSVersionString: 13.0'
|
|
||||||
- line: '| VisionOSSdkVersion: 0'
|
|
||||||
- line: '| VisionOSTargetOSVersionString: 1.0'
|
|
||||||
- line: '| uIPrerenderedIcon: 0'
|
|
||||||
- line: '| uIRequiresPersistentWiFi: 0'
|
|
||||||
- line: '| uIRequiresFullScreen: 1'
|
|
||||||
- line: '| uIStatusBarHidden: 1'
|
|
||||||
- line: '| uIExitOnSuspend: 0'
|
|
||||||
- line: '| uIStatusBarStyle: 0'
|
|
||||||
- line: '| appleTVSplashScreen: {instanceID: 0}'
|
|
||||||
- line: '| appleTVSplashScreen2x: {instanceID: 0}'
|
|
||||||
- line: '| tvOSSmallIconLayers: []'
|
|
||||||
- line: '| tvOSSmallIconLayers2x: []'
|
|
||||||
- line: '| tvOSLargeIconLayers: []'
|
|
||||||
- line: '| tvOSLargeIconLayers2x: []'
|
|
||||||
- line: '| tvOSTopShelfImageLayers: []'
|
|
||||||
- line: '| tvOSTopShelfImageLayers2x: []'
|
|
||||||
- line: '| tvOSTopShelfImageWideLayers: []'
|
|
||||||
- line: '| tvOSTopShelfImageWideLayers2x: []'
|
|
||||||
- line: '| iOSLaunchScreenType: 0'
|
|
||||||
- line: '| iOSLaunchScreenPortrait: {instanceID: 0}'
|
|
||||||
- line: '| iOSLaunchScreenLandscape: {instanceID: 0}'
|
|
||||||
- line: '| iOSLaunchScreenBackgroundColor:'
|
|
||||||
- line: '| serializedVersion: 2'
|
|
||||||
- line: '| rgba: 0'
|
|
||||||
- line: '| iOSLaunchScreenFillPct: 100'
|
|
||||||
- line: '| iOSLaunchScreenSize: 100'
|
|
||||||
- line: '| iOSLaunchScreeniPadType: 0'
|
|
||||||
- line: '| iOSLaunchScreeniPadImage: {instanceID: 0}'
|
|
||||||
- line: '| iOSLaunchScreeniPadBackgroundColor:'
|
|
||||||
- line: '| serializedVersion: 2'
|
|
||||||
- line: '| rgba: 0'
|
|
||||||
- line: '| iOSLaunchScreeniPadFillPct: 100'
|
|
||||||
- line: '| iOSLaunchScreeniPadSize: 100'
|
|
||||||
- line: '| iOSLaunchScreenCustomStoryboardPath: '
|
|
||||||
- line: '| iOSLaunchScreeniPadCustomStoryboardPath: '
|
|
||||||
- line: '| iOSDeviceRequirements: []'
|
|
||||||
- line: '| iOSURLSchemes: []'
|
|
||||||
- line: '| macOSURLSchemes: []'
|
|
||||||
- line: '| iOSBackgroundModes: 0'
|
|
||||||
- line: '| iOSMetalForceHardShadows: 0'
|
|
||||||
- line: '| metalEditorSupport: 1'
|
|
||||||
- line: '| metalAPIValidation: 1'
|
|
||||||
- line: '| metalCompileShaderBinary: 0'
|
|
||||||
- line: '| iOSRenderExtraFrameOnPause: 0'
|
|
||||||
- line: '| iosCopyPluginsCodeInsteadOfSymlink: 0'
|
|
||||||
- line: '| appleDeveloperTeamID: '
|
|
||||||
- line: '| iOSManualSigningProvisioningProfileID: '
|
|
||||||
- line: '| tvOSManualSigningProvisioningProfileID: '
|
|
||||||
- line: '| VisionOSManualSigningProvisioningProfileID: '
|
|
||||||
- line: '| iOSManualSigningProvisioningProfileType: 0'
|
|
||||||
- line: '| tvOSManualSigningProvisioningProfileType: 0'
|
|
||||||
- line: '| VisionOSManualSigningProvisioningProfileType: 0'
|
|
||||||
- line: '| appleEnableAutomaticSigning: 0'
|
|
||||||
- line: '| iOSRequireARKit: 0'
|
|
||||||
- line: '| iOSAutomaticallyDetectAndAddCapabilities: 1'
|
|
||||||
- line: '| appleEnableProMotion: 0'
|
|
||||||
- line: '| shaderPrecisionModel: 0'
|
|
||||||
- line: '| clonedFromGUID: c19f32bac17ee4170b3bf8a6a0333fb9'
|
|
||||||
- line: '| templatePackageId: com.unity.template.universal-2d@5.1.0'
|
|
||||||
- line: '| templateDefaultScene: Assets/Scenes/SampleScene.unity'
|
|
||||||
- line: '| useCustomMainManifest: 0'
|
|
||||||
- line: '| useCustomLauncherManifest: 0'
|
|
||||||
- line: '| useCustomMainGradleTemplate: 0'
|
|
||||||
- line: '| useCustomLauncherGradleManifest: 0'
|
|
||||||
- line: '| useCustomBaseGradleTemplate: 0'
|
|
||||||
- line: '| useCustomGradlePropertiesTemplate: 0'
|
|
||||||
- line: '| useCustomGradleSettingsTemplate: 0'
|
|
||||||
- line: '| useCustomProguardFile: 0'
|
|
||||||
- line: '| AndroidTargetArchitectures: 2'
|
|
||||||
- line: '| AndroidSplashScreenScale: 0'
|
|
||||||
- line: '| androidSplashScreen: {instanceID: 0}'
|
|
||||||
- line: '| AndroidKeystoreName: '
|
|
||||||
- line: '| AndroidKeyaliasName: '
|
|
||||||
- line: '| AndroidEnableArmv9SecurityFeatures: 0'
|
|
||||||
- line: '| AndroidEnableArm64MTE: 0'
|
|
||||||
- line: '| AndroidBuildApkPerCpuArchitecture: 0'
|
|
||||||
- line: '| AndroidTVCompatibility: 0'
|
|
||||||
- line: '| AndroidIsGame: 1'
|
|
||||||
- line: '| androidAppCategory: 3'
|
|
||||||
- line: '| useAndroidAppCategory: 1'
|
|
||||||
- line: '| androidAppCategoryOther: '
|
|
||||||
- line: '| AndroidEnableTango: 0'
|
|
||||||
- line: '| androidEnableBanner: 1'
|
|
||||||
- line: '| androidUseLowAccuracyLocation: 0'
|
|
||||||
- line: '| androidUseCustomKeystore: 0'
|
|
||||||
- line: '| m_AndroidBanners:'
|
|
||||||
- line: '| - width: 320'
|
|
||||||
- line: '| height: 180'
|
|
||||||
- line: '| banner: {instanceID: 0}'
|
|
||||||
- line: '| androidGamepadSupportLevel: 0'
|
|
||||||
- line: '| AndroidMinifyRelease: 0'
|
|
||||||
- line: '| AndroidMinifyDebug: 0'
|
|
||||||
- line: '| AndroidValidateAppBundleSize: 1'
|
|
||||||
- line: '| AndroidAppBundleSizeToValidate: 150'
|
|
||||||
- line: '| AndroidReportGooglePlayAppDependencies: 1'
|
|
||||||
- line: '| androidSymbolsSizeThreshold: 800'
|
|
||||||
- line: '| m_BuildTargetIcons: []'
|
|
||||||
- line: '| m_BuildTargetPlatformIcons:'
|
|
||||||
- line: '| - m_BuildTarget: Android'
|
|
||||||
- line: '| m_Icons:'
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 432'
|
|
||||||
- line: '| m_Height: 432'
|
|
||||||
- line: '| m_Kind: 2'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 324'
|
|
||||||
- line: '| m_Height: 324'
|
|
||||||
- line: '| m_Kind: 2'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 216'
|
|
||||||
- line: '| m_Height: 216'
|
|
||||||
- line: '| m_Kind: 2'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 162'
|
|
||||||
- line: '| m_Height: 162'
|
|
||||||
- line: '| m_Kind: 2'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 108'
|
|
||||||
- line: '| m_Height: 108'
|
|
||||||
- line: '| m_Kind: 2'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 81'
|
|
||||||
- line: '| m_Height: 81'
|
|
||||||
- line: '| m_Kind: 2'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 192'
|
|
||||||
- line: '| m_Height: 192'
|
|
||||||
- line: '| m_Kind: 1'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 144'
|
|
||||||
- line: '| m_Height: 144'
|
|
||||||
- line: '| m_Kind: 1'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 96'
|
|
||||||
- line: '| m_Height: 96'
|
|
||||||
- line: '| m_Kind: 1'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 72'
|
|
||||||
- line: '| m_Height: 72'
|
|
||||||
- line: '| m_Kind: 1'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 48'
|
|
||||||
- line: '| m_Height: 48'
|
|
||||||
- line: '| m_Kind: 1'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 36'
|
|
||||||
- line: '| m_Height: 36'
|
|
||||||
- line: '| m_Kind: 1'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 192'
|
|
||||||
- line: '| m_Height: 192'
|
|
||||||
- line: '| m_Kind: 0'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 144'
|
|
||||||
- line: '| m_Height: 144'
|
|
||||||
- line: '| m_Kind: 0'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 96'
|
|
||||||
- line: '| m_Height: 96'
|
|
||||||
- line: '| m_Kind: 0'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 72'
|
|
||||||
- line: '| m_Height: 72'
|
|
||||||
- line: '| m_Kind: 0'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 48'
|
|
||||||
- line: '| m_Height: 48'
|
|
||||||
- line: '| m_Kind: 0'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| - m_Textures: []'
|
|
||||||
- line: '| m_Width: 36'
|
|
||||||
- line: '| m_Height: 36'
|
|
||||||
- line: '| m_Kind: 0'
|
|
||||||
- line: '| m_SubKind: '
|
|
||||||
- line: '| m_BuildTargetBatching: []'
|
|
||||||
- line: '| m_BuildTargetShaderSettings: []'
|
|
||||||
- line: '| m_BuildTargetGraphicsJobs: []'
|
|
||||||
- line: '| m_BuildTargetGraphicsJobMode: []'
|
|
||||||
- line: '| m_BuildTargetGraphicsAPIs: []'
|
|
||||||
- line: '| m_BuildTargetVRSettings: []'
|
|
||||||
- line: '| m_DefaultShaderChunkSizeInMB: 16'
|
|
||||||
- line: '| m_DefaultShaderChunkCount: 0'
|
|
||||||
- line: '| openGLRequireES31: 0'
|
|
||||||
- line: '| openGLRequireES31AEP: 0'
|
|
||||||
- line: '| openGLRequireES32: 0'
|
|
||||||
- line: '| m_TemplateCustomTags: {}'
|
|
||||||
- line: '| mobileMTRendering:'
|
|
||||||
- line: '| Android: 1'
|
|
||||||
- line: '| iPhone: 1'
|
|
||||||
- line: '| tvOS: 1'
|
|
||||||
- line: '| m_BuildTargetGroupLightmapEncodingQuality: []'
|
|
||||||
- line: '| m_BuildTargetGroupHDRCubemapEncodingQuality: []'
|
|
||||||
- line: '| m_BuildTargetGroupLightmapSettings: []'
|
|
||||||
- line: '| m_BuildTargetGroupLoadStoreDebugModeSettings: []'
|
|
||||||
- line: '| m_BuildTargetNormalMapEncoding: []'
|
|
||||||
- line: '| m_BuildTargetDefaultTextureCompressionFormat:'
|
|
||||||
- line: '| - serializedVersion: 3'
|
|
||||||
- line: '| m_BuildTarget: Android'
|
|
||||||
- line: '| m_Formats: 01000000'
|
|
||||||
- line: '| playModeTestRunnerEnabled: 0'
|
|
||||||
- line: '| runPlayModeTestAsEditModeTest: 0'
|
|
||||||
- line: '| actionOnDotNetUnhandledException: 1'
|
|
||||||
- line: '| editorGfxJobOverride: 1'
|
|
||||||
- line: '| enableInternalProfiler: 0'
|
|
||||||
- line: '| logObjCUncaughtExceptions: 1'
|
|
||||||
- line: '| enableCrashReportAPI: 0'
|
|
||||||
- line: '| cameraUsageDescription: '
|
|
||||||
- line: '| locationUsageDescription: '
|
|
||||||
- line: '| microphoneUsageDescription: '
|
|
||||||
- line: '| bluetoothUsageDescription: '
|
|
||||||
- line: '| macOSTargetOSVersion: 11.0'
|
|
||||||
- line: '| switchNMETAOverride: '
|
|
||||||
- line: '| switchNetLibKey: '
|
|
||||||
- line: '| switchSocketMemoryPoolSize: 6144'
|
|
||||||
- line: '| switchSocketAllocatorPoolSize: 128'
|
|
||||||
- line: '| switchSocketConcurrencyLimit: 14'
|
|
||||||
- line: '| switchScreenResolutionBehavior: 2'
|
|
||||||
- line: '| switchUseCPUProfiler: 0'
|
|
||||||
- line: '| switchEnableFileSystemTrace: 0'
|
|
||||||
- line: '| switchLTOSetting: 0'
|
|
||||||
- line: '| switchApplicationID: 0x01004b9000490000'
|
|
||||||
- line: '| switchNSODependencies: '
|
|
||||||
- line: '| switchCompilerFlags: '
|
|
||||||
- line: '| switchTitleNames_0: '
|
|
||||||
- line: '| switchTitleNames_1: '
|
|
||||||
- line: '| switchTitleNames_2: '
|
|
||||||
- line: '| switchTitleNames_3: '
|
|
||||||
- line: '| switchTitleNames_4: '
|
|
||||||
- line: '| switchTitleNames_5: '
|
|
||||||
- line: '| switchTitleNames_6: '
|
|
||||||
- line: '| switchTitleNames_7: '
|
|
||||||
- line: '| switchTitleNames_8: '
|
|
||||||
- line: '| switchTitleNames_9: '
|
|
||||||
- line: '| switchTitleNames_10: '
|
|
||||||
- line: '| switchTitleNames_11: '
|
|
||||||
- line: '| switchTitleNames_12: '
|
|
||||||
- line: '| switchTitleNames_13: '
|
|
||||||
- line: '| switchTitleNames_14: '
|
|
||||||
- line: '| switchTitleNames_15: '
|
|
||||||
- line: '| switchPublisherNames_0: '
|
|
||||||
- line: '| switchPublisherNames_1: '
|
|
||||||
- line: '| switchPublisherNames_2: '
|
|
||||||
- line: '| switchPublisherNames_3: '
|
|
||||||
- line: '| switchPublisherNames_4: '
|
|
||||||
- line: '| switchPublisherNames_5: '
|
|
||||||
- line: '| switchPublisherNames_6: '
|
|
||||||
- line: '| switchPublisherNames_7: '
|
|
||||||
- line: '| switchPublisherNames_8: '
|
|
||||||
- line: '| switchPublisherNames_9: '
|
|
||||||
- line: '| switchPublisherNames_10: '
|
|
||||||
- line: '| switchPublisherNames_11: '
|
|
||||||
- line: '| switchPublisherNames_12: '
|
|
||||||
- line: '| switchPublisherNames_13: '
|
|
||||||
- line: '| switchPublisherNames_14: '
|
|
||||||
- line: '| switchPublisherNames_15: '
|
|
||||||
- line: '| switchIcons_0: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_1: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_2: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_3: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_4: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_5: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_6: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_7: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_8: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_9: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_10: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_11: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_12: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_13: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_14: {instanceID: 0}'
|
|
||||||
- line: '| switchIcons_15: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_0: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_1: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_2: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_3: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_4: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_5: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_6: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_7: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_8: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_9: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_10: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_11: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_12: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_13: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_14: {instanceID: 0}'
|
|
||||||
- line: '| switchSmallIcons_15: {instanceID: 0}'
|
|
||||||
- line: '| switchManualHTML: '
|
|
||||||
- line: '| switchAccessibleURLs: '
|
|
||||||
- line: '| switchLegalInformation: '
|
|
||||||
- line: '| switchMainThreadStackSize: 1048576'
|
|
||||||
- line: '| switchPresenceGroupId: '
|
|
||||||
- line: '| switchLogoHandling: 0'
|
|
||||||
- line: '| switchReleaseVersion: 0'
|
|
||||||
- line: '| switchDisplayVersion: 1.0.0'
|
|
||||||
- line: '| switchStartupUserAccount: 0'
|
|
||||||
- line: '| switchSupportedLanguagesMask: 0'
|
|
||||||
- line: '| switchLogoType: 0'
|
|
||||||
- line: '| switchApplicationErrorCodeCategory: '
|
|
||||||
- line: '| switchUserAccountSaveDataSize: 0'
|
|
||||||
- line: '| switchUserAccountSaveDataJournalSize: 0'
|
|
||||||
- line: '| switchApplicationAttribute: 0'
|
|
||||||
- line: '| switchCardSpecSize: -1'
|
|
||||||
- line: '| switchCardSpecClock: -1'
|
|
||||||
- line: '| switchRatingsMask: 0'
|
|
||||||
- line: '| switchRatingsInt_0: 0'
|
|
||||||
- line: '| switchRatingsInt_1: 0'
|
|
||||||
- line: '| switchRatingsInt_2: 0'
|
|
||||||
- line: '| switchRatingsInt_3: 0'
|
|
||||||
- line: '| switchRatingsInt_4: 0'
|
|
||||||
- line: '| switchRatingsInt_5: 0'
|
|
||||||
- line: '| switchRatingsInt_6: 0'
|
|
||||||
- line: '| switchRatingsInt_7: 0'
|
|
||||||
- line: '| switchRatingsInt_8: 0'
|
|
||||||
- line: '| switchRatingsInt_9: 0'
|
|
||||||
- line: '| switchRatingsInt_10: 0'
|
|
||||||
- line: '| switchRatingsInt_11: 0'
|
|
||||||
- line: '| switchRatingsInt_12: 0'
|
|
||||||
- line: '| switchLocalCommunicationIds_0: '
|
|
||||||
- line: '| switchLocalCommunicationIds_1: '
|
|
||||||
- line: '| switchLocalCommunicationIds_2: '
|
|
||||||
- line: '| switchLocalCommunicationIds_3: '
|
|
||||||
- line: '| switchLocalCommunicationIds_4: '
|
|
||||||
- line: '| switchLocalCommunicationIds_5: '
|
|
||||||
- line: '| switchLocalCommunicationIds_6: '
|
|
||||||
- line: '| switchLocalCommunicationIds_7: '
|
|
||||||
- line: '| switchParentalControl: 0'
|
|
||||||
- line: '| switchAllowsScreenshot: 1'
|
|
||||||
- line: '| switchAllowsVideoCapturing: 1'
|
|
||||||
- line: '| switchAllowsRuntimeAddOnContentInstall: 0'
|
|
||||||
- line: '| switchDataLossConfirmation: 0'
|
|
||||||
- line: '| switchUserAccountLockEnabled: 0'
|
|
||||||
- line: '| switchSystemResourceMemory: 16777216'
|
|
||||||
- line: '| switchSupportedNpadStyles: 22'
|
|
||||||
- line: '| switchNativeFsCacheSize: 32'
|
|
||||||
- line: '| switchIsHoldTypeHorizontal: 0'
|
|
||||||
- line: '| switchSupportedNpadCount: 8'
|
|
||||||
- line: '| switchEnableTouchScreen: 1'
|
|
||||||
- line: '| switchSocketConfigEnabled: 0'
|
|
||||||
- line: '| switchTcpInitialSendBufferSize: 32'
|
|
||||||
- line: '| switchTcpInitialReceiveBufferSize: 64'
|
|
||||||
- line: '| switchTcpAutoSendBufferSizeMax: 256'
|
|
||||||
- line: '| switchTcpAutoReceiveBufferSizeMax: 256'
|
|
||||||
- line: '| switchUdpSendBufferSize: 9'
|
|
||||||
- line: '| switchUdpReceiveBufferSize: 42'
|
|
||||||
- line: '| switchSocketBufferEfficiency: 4'
|
|
||||||
- line: '| switchSocketInitializeEnabled: 1'
|
|
||||||
- line: '| switchNetworkInterfaceManagerInitializeEnabled: 1'
|
|
||||||
- line: '| switchDisableHTCSPlayerConnection: 0'
|
|
||||||
- line: '| switchUseNewStyleFilepaths: 0'
|
|
||||||
- line: '| switchUseLegacyFmodPriorities: 0'
|
|
||||||
- line: '| switchUseMicroSleepForYield: 1'
|
|
||||||
- line: '| switchEnableRamDiskSupport: 0'
|
|
||||||
- line: '| switchMicroSleepForYieldTime: 25'
|
|
||||||
- line: '| switchRamDiskSpaceSize: 12'
|
|
||||||
- line: '| switchUpgradedPlayerSettingsToNMETA: 0'
|
|
||||||
- line: '| ps4NPAgeRating: 12'
|
|
||||||
- line: '| ps4NPTitleSecret: '
|
|
||||||
- line: '| ps4NPTrophyPackPath: '
|
|
||||||
- line: '| ps4ParentalLevel: 11'
|
|
||||||
- line: '| ps4ContentID: ED1633-NPXX51362_00-0000000000000000'
|
|
||||||
- line: '| ps4Category: 0'
|
|
||||||
- line: '| ps4MasterVersion: 01.00'
|
|
||||||
- line: '| ps4AppVersion: 01.00'
|
|
||||||
- line: '| ps4AppType: 0'
|
|
||||||
- line: '| ps4ParamSfxPath: '
|
|
||||||
- line: '| ps4VideoOutPixelFormat: 0'
|
|
||||||
- line: '| ps4VideoOutInitialWidth: 1920'
|
|
||||||
- line: '| ps4VideoOutBaseModeInitialWidth: 1920'
|
|
||||||
- line: '| ps4VideoOutReprojectionRate: 60'
|
|
||||||
- line: '| ps4PronunciationXMLPath: '
|
|
||||||
- line: '| ps4PronunciationSIGPath: '
|
|
||||||
- line: '| ps4BackgroundImagePath: '
|
|
||||||
- line: '| ps4StartupImagePath: '
|
|
||||||
- line: '| ps4StartupImagesFolder: '
|
|
||||||
- line: '| ps4IconImagesFolder: '
|
|
||||||
- line: '| ps4SaveDataImagePath: '
|
|
||||||
- line: '| ps4SdkOverride: '
|
|
||||||
- line: '| ps4BGMPath: '
|
|
||||||
- line: '| ps4ShareFilePath: '
|
|
||||||
- line: '| ps4ShareOverlayImagePath: '
|
|
||||||
- line: '| ps4PrivacyGuardImagePath: '
|
|
||||||
- line: '| ps4ExtraSceSysFile: '
|
|
||||||
- line: '| ps4NPtitleDatPath: '
|
|
||||||
- line: '| ps4RemotePlayKeyAssignment: -1'
|
|
||||||
- line: '| ps4RemotePlayKeyMappingDir: '
|
|
||||||
- line: '| ps4PlayTogetherPlayerCount: 0'
|
|
||||||
- line: '| ps4EnterButtonAssignment: 2'
|
|
||||||
- line: '| ps4ApplicationParam1: 0'
|
|
||||||
- line: '| ps4ApplicationParam2: 0'
|
|
||||||
- line: '| ps4ApplicationParam3: 0'
|
|
||||||
- line: '| ps4ApplicationParam4: 0'
|
|
||||||
- line: '| ps4DownloadDataSize: 0'
|
|
||||||
- line: '| ps4GarlicHeapSize: 2048'
|
|
||||||
- line: '| ps4ProGarlicHeapSize: 2560'
|
|
||||||
- line: '| playerPrefsMaxSize: 32768'
|
|
||||||
- line: '| ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ'
|
|
||||||
- line: '| ps4pnSessions: 1'
|
|
||||||
- line: '| ps4pnPresence: 1'
|
|
||||||
- line: '| ps4pnFriends: 1'
|
|
||||||
- line: '| ps4pnGameCustomData: 1'
|
|
||||||
- line: '| playerPrefsSupport: 0'
|
|
||||||
- line: '| enableApplicationExit: 0'
|
|
||||||
- line: '| resetTempFolder: 1'
|
|
||||||
- line: '| restrictedAudioUsageRights: 0'
|
|
||||||
- line: '| ps4UseResolutionFallback: 0'
|
|
||||||
- line: '| ps4ReprojectionSupport: 0'
|
|
||||||
- line: '| ps4UseAudio3dBackend: 0'
|
|
||||||
- line: '| ps4UseLowGarlicFragmentationMode: 1'
|
|
||||||
- line: '| ps4SocialScreenEnabled: 0'
|
|
||||||
- line: '| ps4ScriptOptimizationLevel: 2'
|
|
||||||
- line: '| ps4Audio3dVirtualSpeakerCount: 14'
|
|
||||||
- line: '| ps4attribCpuUsage: 0'
|
|
||||||
- line: '| ps4PatchPkgPath: '
|
|
||||||
- line: '| ps4PatchLatestPkgPath: '
|
|
||||||
- line: '| ps4PatchChangeinfoPath: '
|
|
||||||
- line: '| ps4PatchDayOne: 0'
|
|
||||||
- line: '| ps4attribUserManagement: 0'
|
|
||||||
- line: '| ps4attribMoveSupport: 0'
|
|
||||||
- line: '| ps4attrib3DSupport: 0'
|
|
||||||
- line: '| ps4attribShareSupport: 0'
|
|
||||||
- line: '| ps4attribExclusiveVR: 0'
|
|
||||||
- line: '| ps4disableAutoHideSplash: 0'
|
|
||||||
- line: '| ps4videoRecordingFeaturesUsed: 0'
|
|
||||||
- line: '| ps4contentSearchFeaturesUsed: 0'
|
|
||||||
- line: '| ps4CompatibilityPS5: 0'
|
|
||||||
- line: '| ps4AllowPS5Detection: 0'
|
|
||||||
- line: '| ps4GPU800MHz: 1'
|
|
||||||
- line: '| ps4attribEyeToEyeDistanceSettingVR: 0'
|
|
||||||
- line: '| ps4IncludedModules: []'
|
|
||||||
- line: '| ps4attribVROutputEnabled: 0'
|
|
||||||
- line: '| monoEnv: '
|
|
||||||
- line: '| splashScreenBackgroundSourceLandscape: {instanceID: 0}'
|
|
||||||
- line: '| splashScreenBackgroundSourcePortrait: {instanceID: 0}'
|
|
||||||
- line: '| blurSplashScreenBackground: 1'
|
|
||||||
- line: '| spritePackerPolicy: '
|
|
||||||
- line: '| webGLMemorySize: 32'
|
|
||||||
- line: '| webGLExceptionSupport: 1'
|
|
||||||
- line: '| webGLNameFilesAsHashes: 0'
|
|
||||||
- line: '| webGLShowDiagnostics: 0'
|
|
||||||
- line: '| webGLDataCaching: 1'
|
|
||||||
- line: '| webGLDebugSymbols: 0'
|
|
||||||
- line: '| webGLEmscriptenArgs: '
|
|
||||||
- line: '| webGLModulesDirectory: '
|
|
||||||
- line: '| webGLTemplate: APPLICATION:Default'
|
|
||||||
- line: '| webGLAnalyzeBuildSize: 0'
|
|
||||||
- line: '| webGLUseEmbeddedResources: 0'
|
|
||||||
- line: '| webGLCompressionFormat: 0'
|
|
||||||
- line: '| webGLWasmArithmeticExceptions: 0'
|
|
||||||
- line: '| webGLLinkerTarget: 1'
|
|
||||||
- line: '| webGLThreadsSupport: 0'
|
|
||||||
- line: '| webGLDecompressionFallback: 0'
|
|
||||||
- line: '| webGLInitialMemorySize: 32'
|
|
||||||
- line: '| webGLMaximumMemorySize: 2048'
|
|
||||||
- line: '| webGLMemoryGrowthMode: 2'
|
|
||||||
- line: '| webGLMemoryLinearGrowthStep: 16'
|
|
||||||
- line: '| webGLMemoryGeometricGrowthStep: 0.2'
|
|
||||||
- line: '| webGLMemoryGeometricGrowthCap: 96'
|
|
||||||
- line: '| webGLEnableWebGPU: 0'
|
|
||||||
- line: '| webGLPowerPreference: 2'
|
|
||||||
- line: '| webGLWebAssemblyTable: 0'
|
|
||||||
- line: '| webGLWebAssemblyBigInt: 0'
|
|
||||||
- line: '| webGLCloseOnQuit: 0'
|
|
||||||
- line: '| webWasm2023: 0'
|
|
||||||
- line: '| scriptingDefineSymbols:'
|
|
||||||
- line: '| Android: MIRROR;MIRROR_89_OR_NEWER;MIRROR_90_OR_NEWER;MIRROR_93_OR_NEWER;MIRROR_96_OR_NEWER;EDGEGAP_PLUGIN_SERVERS;DOTWEEN'
|
|
||||||
- line: '| EmbeddedLinux: DOTWEEN'
|
|
||||||
- line: '| GameCoreScarlett: DOTWEEN'
|
|
||||||
- line: '| GameCoreXboxOne: DOTWEEN'
|
|
||||||
- line: '| Kepler: DOTWEEN'
|
|
||||||
- line: '| LinuxHeadlessSimulation: DOTWEEN'
|
|
||||||
- line: '| Nintendo Switch: DOTWEEN'
|
|
||||||
- line: '| Nintendo Switch 2: DOTWEEN'
|
|
||||||
- line: '| PS4: DOTWEEN'
|
|
||||||
- line: '| PS5: DOTWEEN'
|
|
||||||
- line: '| QNX: DOTWEEN'
|
|
||||||
- line: '| Standalone: DOTWEEN'
|
|
||||||
- line: '| VisionOS: DOTWEEN'
|
|
||||||
- line: '| WebGL: DOTWEEN'
|
|
||||||
- line: '| Windows Store Apps: DOTWEEN'
|
|
||||||
- line: '| XboxOne: DOTWEEN'
|
|
||||||
- line: '| iPhone: DOTWEEN'
|
|
||||||
- line: '| tvOS: DOTWEEN'
|
|
||||||
- line: '| additionalCompilerArguments: {}'
|
|
||||||
- line: '| platformArchitecture: {}'
|
|
||||||
- line: '| scriptingBackend:'
|
|
||||||
- line: '| Android: 1'
|
|
||||||
- line: '| il2cppCompilerConfiguration: {}'
|
|
||||||
- line: '| il2cppCodeGeneration: {}'
|
|
||||||
- line: '| il2cppStacktraceInformation: {}'
|
|
||||||
- line: '| managedStrippingLevel: {}'
|
|
||||||
- line: '| incrementalIl2cppBuild: {}'
|
|
||||||
- line: '| suppressCommonWarnings: 1'
|
|
||||||
- line: '| allowUnsafeCode: 0'
|
|
||||||
- line: '| useDeterministicCompilation: 1'
|
|
||||||
- line: '| additionalIl2CppArgs: '
|
|
||||||
- line: '| scriptingRuntimeVersion: 1'
|
|
||||||
- line: '| gcIncremental: 1'
|
|
||||||
- line: '| gcWBarrierValidation: 0'
|
|
||||||
- line: '| apiCompatibilityLevelPerPlatform: {}'
|
|
||||||
- line: '| editorAssembliesCompatibilityLevel: 1'
|
|
||||||
- line: '| m_RenderingPath: 1'
|
|
||||||
- line: '| m_MobileRenderingPath: 1'
|
|
||||||
- line: '| metroPackageName: WalkInvest_Soccer'
|
|
||||||
- line: '| metroPackageVersion: '
|
|
||||||
- line: '| metroCertificatePath: '
|
|
||||||
- line: '| metroCertificatePassword: '
|
|
||||||
- line: '| metroCertificateSubject: '
|
|
||||||
- line: '| metroCertificateIssuer: '
|
|
||||||
- line: '| metroCertificateNotAfter: 0000000000000000'
|
|
||||||
- line: '| metroApplicationDescription: WalkInvest_Soccer'
|
|
||||||
- line: '| wsaImages: {}'
|
|
||||||
- line: '| metroTileShortName: '
|
|
||||||
- line: '| metroTileShowName: 0'
|
|
||||||
- line: '| metroMediumTileShowName: 0'
|
|
||||||
- line: '| metroLargeTileShowName: 0'
|
|
||||||
- line: '| metroWideTileShowName: 0'
|
|
||||||
- line: '| metroSupportStreamingInstall: 0'
|
|
||||||
- line: '| metroLastRequiredScene: 0'
|
|
||||||
- line: '| metroDefaultTileSize: 1'
|
|
||||||
- line: '| metroTileForegroundText: 2'
|
|
||||||
- line: '| metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628,
|
|
||||||
a: 0}'
|
|
||||||
- line: '| metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902,
|
|
||||||
b: 0.21568628, a: 1}'
|
|
||||||
- line: '| metroSplashScreenUseBackgroundColor: 0'
|
|
||||||
- line: '| syncCapabilities: 0'
|
|
||||||
- line: '| platformCapabilities: {}'
|
|
||||||
- line: '| metroTargetDeviceFamilies: {}'
|
|
||||||
- line: '| metroFTAName: '
|
|
||||||
- line: '| metroFTAFileTypes: []'
|
|
||||||
- line: '| metroProtocolName: '
|
|
||||||
- line: '| vcxProjDefaultLanguage: '
|
|
||||||
- line: '| XboxOneProductId: '
|
|
||||||
- line: '| XboxOneUpdateKey: '
|
|
||||||
- line: '| XboxOneSandboxId: '
|
|
||||||
- line: '| XboxOneContentId: '
|
|
||||||
- line: '| XboxOneTitleId: '
|
|
||||||
- line: '| XboxOneSCId: '
|
|
||||||
- line: '| XboxOneGameOsOverridePath: '
|
|
||||||
- line: '| XboxOnePackagingOverridePath: '
|
|
||||||
- line: '| XboxOneAppManifestOverridePath: '
|
|
||||||
- line: '| XboxOneVersion: 1.0.0.0'
|
|
||||||
- line: '| XboxOnePackageEncryption: 0'
|
|
||||||
- line: '| XboxOnePackageUpdateGranularity: 2'
|
|
||||||
- line: '| XboxOneDescription: '
|
|
||||||
- line: '| XboxOneLanguage:'
|
|
||||||
- line: '| - enus'
|
|
||||||
- line: '| XboxOneCapability: []'
|
|
||||||
- line: '| XboxOneGameRating: {}'
|
|
||||||
- line: '| XboxOneIsContentPackage: 0'
|
|
||||||
- line: '| XboxOneEnhancedXboxCompatibilityMode: 0'
|
|
||||||
- line: '| XboxOneEnableGPUVariability: 1'
|
|
||||||
- line: '| XboxOneSockets: {}'
|
|
||||||
- line: '| XboxOneSplashScreen: {instanceID: 0}'
|
|
||||||
- line: '| XboxOneAllowedProductIds: []'
|
|
||||||
- line: '| XboxOnePersistentLocalStorageSize: 0'
|
|
||||||
- line: '| XboxOneXTitleMemory: 8'
|
|
||||||
- line: '| XboxOneOverrideIdentityName: '
|
|
||||||
- line: '| XboxOneOverrideIdentityPublisher: '
|
|
||||||
- line: '| vrEditorSettings: {}'
|
|
||||||
- line: '| cloudServicesEnabled: {}'
|
|
||||||
- line: '| luminIcon:'
|
|
||||||
- line: '| m_Name: '
|
|
||||||
- line: '| m_ModelFolderPath: '
|
|
||||||
- line: '| m_PortalFolderPath: '
|
|
||||||
- line: '| luminCert:'
|
|
||||||
- line: '| m_CertPath: '
|
|
||||||
- line: '| m_SignPackage: 1'
|
|
||||||
- line: '| luminIsChannelApp: 0'
|
|
||||||
- line: '| luminVersion:'
|
|
||||||
- line: '| m_VersionCode: 1'
|
|
||||||
- line: '| m_VersionName: '
|
|
||||||
- line: '| hmiPlayerDataPath: '
|
|
||||||
- line: '| hmiForceSRGBBlit: 1'
|
|
||||||
- line: '| embeddedLinuxEnableGamepadInput: 0'
|
|
||||||
- line: '| hmiCpuConfiguration: '
|
|
||||||
- line: '| hmiLogStartupTiming: 0'
|
|
||||||
- line: '| qnxGraphicConfPath: '
|
|
||||||
- line: '| apiCompatibilityLevel: 6'
|
|
||||||
- line: '| captureStartupLogs: {}'
|
|
||||||
- line: '| activeInputHandler: 2'
|
|
||||||
- line: '| windowsGamepadBackendHint: 0'
|
|
||||||
- line: '| cloudProjectId: 267a39bf-cb72-4f17-bb8e-775bffe1a6a1'
|
|
||||||
- line: '| framebufferDepthMemorylessMode: 0'
|
|
||||||
- line: '| qualitySettingsNames: []'
|
|
||||||
- line: '| projectName: WalkInvest_Soccer'
|
|
||||||
- line: '| organizationId: sewmina7'
|
|
||||||
- line: '| cloudEnabled: 0'
|
|
||||||
- line: '| legacyClampBlendShapeWeights: 0'
|
|
||||||
- line: '| hmiLoadingImage: {instanceID: 0}'
|
|
||||||
- line: '| platformRequiresReadableAssets: 0'
|
|
||||||
- line: '| virtualTexturingSupportEnabled: 0'
|
|
||||||
- line: '| insecureHttpOption: 2'
|
|
||||||
- line: '| androidVulkanDenyFilterList: []'
|
|
||||||
- line: '| androidVulkanAllowFilterList: []'
|
|
||||||
- line: '| '
|
|
||||||
references:
|
|
||||||
version: 2
|
|
||||||
RefIds:
|
|
||||||
- rid: 1400252592429989960
|
|
||||||
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_BuildSubtarget: 0
|
|
||||||
m_BuildSystem: 1
|
|
||||||
m_ExportAsGoogleAndroidProject: 0
|
|
||||||
m_DebugSymbolLevel: 1
|
|
||||||
m_DebugSymbolFormat: 5
|
|
||||||
m_CurrentDeploymentTargetId: __builtin__target_default
|
|
||||||
m_BuildType: 2
|
|
||||||
m_BuildAppBundle: 0
|
|
||||||
m_IPAddressToConnect:
|
|
||||||
m_SymlinkSources: 0
|
|
||||||
+1
-1
@@ -10,7 +10,7 @@ MonoBehaviour:
|
|||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
m_EditorHideFlags: 0
|
m_EditorHideFlags: 0
|
||||||
m_Script: {fileID: 15003, guid: 0000000000000000e000000000000000, type: 0}
|
m_Script: {fileID: 15003, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
m_Name: New Linux Server Profile 1
|
m_Name: Server
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
m_AssetVersion: 1
|
m_AssetVersion: 1
|
||||||
m_BuildTarget: 24
|
m_BuildTarget: 24
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user