Compare commits
2 Commits
master
..
a00ab7e32d
| Author | SHA1 | Date | |
|---|---|---|---|
| a00ab7e32d | |||
| 0b6374511c |
@@ -1,12 +0,0 @@
|
|||||||
# Unity recommended line endings
|
|
||||||
* text=auto eol=lf
|
|
||||||
|
|
||||||
# Force binary for Unity binary assets
|
|
||||||
*.png binary
|
|
||||||
*.jpg binary
|
|
||||||
*.wav binary
|
|
||||||
*.mp3 binary
|
|
||||||
*.ogg binary
|
|
||||||
*.mp4 binary
|
|
||||||
*.fbx binary
|
|
||||||
*.asset binary
|
|
||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
# Unity
|
|
||||||
/Library/
|
|
||||||
/Temp/
|
|
||||||
/Obj/
|
|
||||||
/Build/
|
|
||||||
/Builds/
|
|
||||||
/Logs/
|
|
||||||
/UserSettings/
|
|
||||||
|
|
||||||
# IDE / Generated
|
|
||||||
*.csproj
|
|
||||||
*.csproj.user
|
|
||||||
*.slnx
|
|
||||||
*.sln
|
|
||||||
.vscode/
|
|
||||||
.vs/
|
|
||||||
|
|
||||||
# Claude Code local settings
|
|
||||||
.claude/
|
|
||||||
|
|
||||||
# Credentials — never commit
|
|
||||||
.env
|
|
||||||
/env
|
|
||||||
/cookies.txt
|
|
||||||
/Assets/StreamingAssets/nas_config.json
|
|
||||||
/Assets/StreamingAssets/nas_config.json.meta
|
|
||||||
|
|
||||||
# Local tool output
|
|
||||||
/Captures/
|
|
||||||
/tools/unity-mcp-server/node_modules/
|
|
||||||
/Assets/_Recovery/
|
|
||||||
/Assets/_Recovery.meta
|
|
||||||
|
|
||||||
# Local video sources / superseded test clips
|
|
||||||
/Assets/img/*.mkv
|
|
||||||
/Assets/img/*.mkv.meta
|
|
||||||
/Assets/img/No Copyright Neon Lights Modern Animated Loop Background - Free Footage - Motion Stock Footage.mp4
|
|
||||||
/Assets/img/No Copyright Neon Lights Modern Animated Loop Background - Free Footage - Motion Stock Footage.mp4.meta
|
|
||||||
/Assets/img/neon_background_unity.mp4
|
|
||||||
/Assets/img/neon_background_unity.mp4.meta
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
|
|
||||||
m_Name: DefaultVolumeProfile
|
|
||||||
m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.VolumeProfile
|
|
||||||
components: []
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 0767462997e881e4980faede0fe3cc8a
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 967f9dfcbece854419d004baa2dd052d
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,946 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
|
|
||||||
namespace VRBeats.EditorTools
|
|
||||||
{
|
|
||||||
[InitializeOnLoad]
|
|
||||||
internal static class UnityCodexBridgeServer
|
|
||||||
{
|
|
||||||
private const int PreferredPort = 19744;
|
|
||||||
private const int MaxPortAttempts = 5;
|
|
||||||
private const string AutoStartPrefKey = "VRBeats.CodexBridge.AutoStart";
|
|
||||||
private const int MaxLogs = 250;
|
|
||||||
|
|
||||||
private static readonly ConcurrentQueue<BridgeJob> Jobs = new ConcurrentQueue<BridgeJob>();
|
|
||||||
private static readonly List<LogEntry> Logs = new List<LogEntry>();
|
|
||||||
private static readonly object LogLock = new object();
|
|
||||||
|
|
||||||
private static TcpListener _listener;
|
|
||||||
private static Thread _serverThread;
|
|
||||||
private static bool _running;
|
|
||||||
private static int _port = PreferredPort;
|
|
||||||
private static int _logIndex;
|
|
||||||
|
|
||||||
static UnityCodexBridgeServer()
|
|
||||||
{
|
|
||||||
if (IsBackgroundEditorProcess())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Application.logMessageReceived -= OnLogMessageReceived;
|
|
||||||
Application.logMessageReceived += OnLogMessageReceived;
|
|
||||||
|
|
||||||
EditorApplication.update -= ProcessJobs;
|
|
||||||
EditorApplication.update += ProcessJobs;
|
|
||||||
|
|
||||||
EditorApplication.quitting -= StopServer;
|
|
||||||
EditorApplication.quitting += StopServer;
|
|
||||||
|
|
||||||
AssemblyReloadEvents.beforeAssemblyReload -= StopServer;
|
|
||||||
AssemblyReloadEvents.beforeAssemblyReload += StopServer;
|
|
||||||
|
|
||||||
if (EditorPrefs.GetBool(AutoStartPrefKey, true))
|
|
||||||
StartServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MenuItem("Tools/Codex Bridge/Start Server")]
|
|
||||||
private static void StartServerMenu()
|
|
||||||
{
|
|
||||||
StartServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MenuItem("Tools/Codex Bridge/Stop Server")]
|
|
||||||
private static void StopServerMenu()
|
|
||||||
{
|
|
||||||
StopServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MenuItem("Tools/Codex Bridge/Auto Start")]
|
|
||||||
private static void ToggleAutoStart()
|
|
||||||
{
|
|
||||||
bool enabled = !EditorPrefs.GetBool(AutoStartPrefKey, true);
|
|
||||||
EditorPrefs.SetBool(AutoStartPrefKey, enabled);
|
|
||||||
|
|
||||||
if (enabled)
|
|
||||||
StartServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MenuItem("Tools/Codex Bridge/Auto Start", true)]
|
|
||||||
private static bool ValidateToggleAutoStart()
|
|
||||||
{
|
|
||||||
Menu.SetChecked("Tools/Codex Bridge/Auto Start", EditorPrefs.GetBool(AutoStartPrefKey, true));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MenuItem("Tools/Codex Bridge/Capture Game View")]
|
|
||||||
private static void CaptureGameViewMenu()
|
|
||||||
{
|
|
||||||
BridgeResponse response = CaptureGameView(new Dictionary<string, string>());
|
|
||||||
Debug.Log("[CodexBridge] Capture result: " + response.Body);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MenuItem("Tools/Codex Bridge/Open Sync Calibration")]
|
|
||||||
private static void OpenSyncCalibrationMenu()
|
|
||||||
{
|
|
||||||
SyncCalibrationOverlay.Open();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void StartServer()
|
|
||||||
{
|
|
||||||
if (IsBackgroundEditorProcess())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_running)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_listener = CreateListener();
|
|
||||||
_running = true;
|
|
||||||
|
|
||||||
_serverThread = new Thread(ServerLoop)
|
|
||||||
{
|
|
||||||
IsBackground = true,
|
|
||||||
Name = "UnityCodexBridgeServer"
|
|
||||||
};
|
|
||||||
_serverThread.Start();
|
|
||||||
|
|
||||||
Debug.Log("[CodexBridge] Listening on http://127.0.0.1:" + _port);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_running = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_listener != null)
|
|
||||||
_listener.Stop();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Ignore cleanup failures after a failed bind.
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_listener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.LogWarning("[CodexBridge] Failed to start: " + ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void StopServer()
|
|
||||||
{
|
|
||||||
_running = false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_listener != null)
|
|
||||||
_listener.Stop();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Ignore shutdown races.
|
|
||||||
}
|
|
||||||
|
|
||||||
_listener = null;
|
|
||||||
|
|
||||||
if (_serverThread != null && _serverThread.IsAlive)
|
|
||||||
_serverThread.Join(200);
|
|
||||||
|
|
||||||
_serverThread = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TcpListener CreateListener()
|
|
||||||
{
|
|
||||||
Exception lastException = null;
|
|
||||||
|
|
||||||
for (int i = 0; i < MaxPortAttempts; i++)
|
|
||||||
{
|
|
||||||
int port = PreferredPort + i;
|
|
||||||
TcpListener listener = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
listener = new TcpListener(IPAddress.Loopback, port);
|
|
||||||
listener.Server.ExclusiveAddressUse = true;
|
|
||||||
listener.Start();
|
|
||||||
_port = port;
|
|
||||||
return listener;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
lastException = ex;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (listener != null)
|
|
||||||
listener.Stop();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Ignore cleanup failures while trying fallback ports.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw lastException ?? new SocketException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsBackgroundEditorProcess()
|
|
||||||
{
|
|
||||||
string commandLine = Environment.CommandLine;
|
|
||||||
|
|
||||||
return Application.isBatchMode ||
|
|
||||||
commandLine.IndexOf("AssetImportWorker", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
|
||||||
commandLine.IndexOf("-batchMode", StringComparison.OrdinalIgnoreCase) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ServerLoop()
|
|
||||||
{
|
|
||||||
while (_running)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
TcpClient client = _listener.AcceptTcpClient();
|
|
||||||
ThreadPool.QueueUserWorkItem(_ => HandleClient(client));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
if (_running)
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void HandleClient(TcpClient client)
|
|
||||||
{
|
|
||||||
using (client)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
client.ReceiveTimeout = 5000;
|
|
||||||
client.SendTimeout = 5000;
|
|
||||||
|
|
||||||
BridgeRequest request = ReadRequest(client.GetStream());
|
|
||||||
BridgeResponse response;
|
|
||||||
|
|
||||||
if (request == null)
|
|
||||||
{
|
|
||||||
response = BridgeResponse.Json(400, "{\"ok\":false,\"error\":\"invalid_request\"}");
|
|
||||||
}
|
|
||||||
else if (request.Method == "OPTIONS")
|
|
||||||
{
|
|
||||||
response = BridgeResponse.Json(204, string.Empty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BridgeJob job = new BridgeJob(request);
|
|
||||||
Jobs.Enqueue(job);
|
|
||||||
|
|
||||||
if (!job.Done.Wait(TimeSpan.FromSeconds(10)))
|
|
||||||
response = BridgeResponse.Json(504, "{\"ok\":false,\"error\":\"unity_main_thread_timeout\"}");
|
|
||||||
else
|
|
||||||
response = job.Response;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteResponse(client.GetStream(), response);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
WriteResponse(client.GetStream(),
|
|
||||||
BridgeResponse.Json(500, "{\"ok\":false,\"error\":" + JsonString(ex.Message) + "}"));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Client has already gone away.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeRequest ReadRequest(Stream stream)
|
|
||||||
{
|
|
||||||
StreamReader reader = new StreamReader(stream, Encoding.UTF8, false, 4096, true);
|
|
||||||
string requestLine = reader.ReadLine();
|
|
||||||
if (string.IsNullOrEmpty(requestLine))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
string[] requestParts = requestLine.Split(' ');
|
|
||||||
if (requestParts.Length < 2)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
int contentLength = 0;
|
|
||||||
string line;
|
|
||||||
while (!string.IsNullOrEmpty(line = reader.ReadLine()))
|
|
||||||
{
|
|
||||||
int separatorIndex = line.IndexOf(':');
|
|
||||||
if (separatorIndex <= 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
string headerName = line.Substring(0, separatorIndex).Trim();
|
|
||||||
string headerValue = line.Substring(separatorIndex + 1).Trim();
|
|
||||||
if (string.Equals(headerName, "Content-Length", StringComparison.OrdinalIgnoreCase))
|
|
||||||
int.TryParse(headerValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out contentLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
string body = string.Empty;
|
|
||||||
if (contentLength > 0)
|
|
||||||
{
|
|
||||||
char[] buffer = new char[contentLength];
|
|
||||||
int read = reader.ReadBlock(buffer, 0, contentLength);
|
|
||||||
body = new string(buffer, 0, read);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri uri = new Uri("http://127.0.0.1" + requestParts[1]);
|
|
||||||
return new BridgeRequest(requestParts[0].ToUpperInvariant(), uri.AbsolutePath, ParseQuery(uri.Query), body);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<string, string> ParseQuery(string query)
|
|
||||||
{
|
|
||||||
Dictionary<string, string> values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
if (string.IsNullOrEmpty(query))
|
|
||||||
return values;
|
|
||||||
|
|
||||||
string trimmed = query[0] == '?' ? query.Substring(1) : query;
|
|
||||||
string[] pairs = trimmed.Split('&');
|
|
||||||
|
|
||||||
foreach (string pair in pairs)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(pair))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int separatorIndex = pair.IndexOf('=');
|
|
||||||
string key = separatorIndex >= 0 ? pair.Substring(0, separatorIndex) : pair;
|
|
||||||
string value = separatorIndex >= 0 ? pair.Substring(separatorIndex + 1) : string.Empty;
|
|
||||||
values[Uri.UnescapeDataString(key)] = Uri.UnescapeDataString(value.Replace("+", " "));
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WriteResponse(Stream stream, BridgeResponse response)
|
|
||||||
{
|
|
||||||
if (response == null)
|
|
||||||
response = BridgeResponse.Json(500, "{\"ok\":false,\"error\":\"null_response\"}");
|
|
||||||
|
|
||||||
byte[] body = Encoding.UTF8.GetBytes(response.Body ?? string.Empty);
|
|
||||||
string headers =
|
|
||||||
"HTTP/1.1 " + response.StatusCode + " " + StatusText(response.StatusCode) + "\r\n" +
|
|
||||||
"Content-Type: " + response.ContentType + "; charset=utf-8\r\n" +
|
|
||||||
"Content-Length: " + body.Length.ToString(CultureInfo.InvariantCulture) + "\r\n" +
|
|
||||||
"Access-Control-Allow-Origin: http://localhost\r\n" +
|
|
||||||
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n" +
|
|
||||||
"Access-Control-Allow-Headers: Content-Type\r\n" +
|
|
||||||
"Connection: close\r\n\r\n";
|
|
||||||
|
|
||||||
byte[] headerBytes = Encoding.UTF8.GetBytes(headers);
|
|
||||||
stream.Write(headerBytes, 0, headerBytes.Length);
|
|
||||||
if (body.Length > 0)
|
|
||||||
stream.Write(body, 0, body.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string StatusText(int statusCode)
|
|
||||||
{
|
|
||||||
switch (statusCode)
|
|
||||||
{
|
|
||||||
case 200: return "OK";
|
|
||||||
case 204: return "No Content";
|
|
||||||
case 400: return "Bad Request";
|
|
||||||
case 404: return "Not Found";
|
|
||||||
case 405: return "Method Not Allowed";
|
|
||||||
case 500: return "Internal Server Error";
|
|
||||||
case 504: return "Gateway Timeout";
|
|
||||||
default: return "OK";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ProcessJobs()
|
|
||||||
{
|
|
||||||
while (Jobs.TryDequeue(out BridgeJob job))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
job.Response = Execute(job.Request);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
job.Response = BridgeResponse.Json(500,
|
|
||||||
"{\"ok\":false,\"error\":" + JsonString(ex.Message) + "}");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
job.Done.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeResponse Execute(BridgeRequest request)
|
|
||||||
{
|
|
||||||
switch (request.Path)
|
|
||||||
{
|
|
||||||
case "/health":
|
|
||||||
case "/state":
|
|
||||||
return GetHealth();
|
|
||||||
case "/capture":
|
|
||||||
return CaptureGameView(request.Query);
|
|
||||||
case "/logs":
|
|
||||||
return GetLogs(request.Query);
|
|
||||||
case "/scene/roots":
|
|
||||||
return GetSceneRoots();
|
|
||||||
case "/scene/objects":
|
|
||||||
return GetSceneObjects(request.Query);
|
|
||||||
case "/object":
|
|
||||||
return GetObjectDetails(request.Query);
|
|
||||||
case "/play":
|
|
||||||
return SetPlayState(true, false);
|
|
||||||
case "/pause":
|
|
||||||
return SetPlayState(true, true);
|
|
||||||
case "/stop":
|
|
||||||
return SetPlayState(false, false);
|
|
||||||
case "/sync/open":
|
|
||||||
SyncCalibrationOverlay.Open();
|
|
||||||
return BridgeResponse.Json(200, "{\"ok\":true,\"opened\":\"sync_calibration\"}");
|
|
||||||
case "/transform":
|
|
||||||
if (request.Method != "POST")
|
|
||||||
return BridgeResponse.Json(405, "{\"ok\":false,\"error\":\"method_not_allowed\"}");
|
|
||||||
return SetTransform(request.Body);
|
|
||||||
default:
|
|
||||||
return BridgeResponse.Json(404, "{\"ok\":false,\"error\":\"not_found\"}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeResponse GetHealth()
|
|
||||||
{
|
|
||||||
Scene scene = SceneManager.GetActiveScene();
|
|
||||||
string body =
|
|
||||||
"{\"ok\":true" +
|
|
||||||
",\"bridge\":\"unity-codex-bridge\"" +
|
|
||||||
",\"port\":" + _port.ToString(CultureInfo.InvariantCulture) +
|
|
||||||
",\"unityVersion\":" + JsonString(Application.unityVersion) +
|
|
||||||
",\"projectPath\":" + JsonString(Directory.GetCurrentDirectory()) +
|
|
||||||
",\"scene\":" + JsonString(scene.IsValid() ? scene.name : string.Empty) +
|
|
||||||
",\"isPlaying\":" + Bool(EditorApplication.isPlaying) +
|
|
||||||
",\"isPaused\":" + Bool(EditorApplication.isPaused) +
|
|
||||||
"}";
|
|
||||||
|
|
||||||
return BridgeResponse.Json(200, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeResponse CaptureGameView(Dictionary<string, string> query)
|
|
||||||
{
|
|
||||||
int width = GetInt(query, "width", 1280, 320, 4096);
|
|
||||||
int height = GetInt(query, "height", 720, 180, 4096);
|
|
||||||
string relativePath = "Captures/latest.png";
|
|
||||||
string absolutePath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), relativePath));
|
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(absolutePath));
|
|
||||||
|
|
||||||
Camera camera = ResolveCaptureCamera();
|
|
||||||
string cameraName = camera != null ? camera.name : string.Empty;
|
|
||||||
bool usedCameraRender = false;
|
|
||||||
|
|
||||||
if (camera != null)
|
|
||||||
{
|
|
||||||
RenderTexture previousTarget = camera.targetTexture;
|
|
||||||
RenderTexture previousActive = RenderTexture.active;
|
|
||||||
RenderTexture renderTexture = RenderTexture.GetTemporary(width, height, 24, RenderTextureFormat.ARGB32);
|
|
||||||
Texture2D texture = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
camera.targetTexture = renderTexture;
|
|
||||||
RenderTexture.active = renderTexture;
|
|
||||||
camera.Render();
|
|
||||||
|
|
||||||
texture = new Texture2D(width, height, TextureFormat.RGB24, false);
|
|
||||||
texture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
|
|
||||||
texture.Apply();
|
|
||||||
|
|
||||||
File.WriteAllBytes(absolutePath, texture.EncodeToPNG());
|
|
||||||
usedCameraRender = true;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
camera.targetTexture = previousTarget;
|
|
||||||
RenderTexture.active = previousActive;
|
|
||||||
RenderTexture.ReleaseTemporary(renderTexture);
|
|
||||||
if (texture != null)
|
|
||||||
UnityEngine.Object.DestroyImmediate(texture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!usedCameraRender)
|
|
||||||
ScreenCapture.CaptureScreenshot(relativePath);
|
|
||||||
|
|
||||||
string body =
|
|
||||||
"{\"ok\":true" +
|
|
||||||
",\"path\":" + JsonString(absolutePath) +
|
|
||||||
",\"relativePath\":" + JsonString(relativePath) +
|
|
||||||
",\"width\":" + width.ToString(CultureInfo.InvariantCulture) +
|
|
||||||
",\"height\":" + height.ToString(CultureInfo.InvariantCulture) +
|
|
||||||
",\"camera\":" + JsonString(cameraName) +
|
|
||||||
",\"mode\":" + JsonString(usedCameraRender ? "camera_render" : "screen_capture") +
|
|
||||||
"}";
|
|
||||||
|
|
||||||
return BridgeResponse.Json(200, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Camera ResolveCaptureCamera()
|
|
||||||
{
|
|
||||||
Camera main = Camera.main;
|
|
||||||
if (main != null && main.isActiveAndEnabled)
|
|
||||||
return main;
|
|
||||||
|
|
||||||
Camera[] cameras = UnityEngine.Object.FindObjectsByType<Camera>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
|
|
||||||
foreach (Camera camera in cameras)
|
|
||||||
{
|
|
||||||
if (camera != null && camera.isActiveAndEnabled)
|
|
||||||
return camera;
|
|
||||||
}
|
|
||||||
|
|
||||||
return main != null ? main : (cameras.Length > 0 ? cameras[0] : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeResponse GetLogs(Dictionary<string, string> query)
|
|
||||||
{
|
|
||||||
int count = GetInt(query, "count", 80, 1, MaxLogs);
|
|
||||||
List<LogEntry> snapshot;
|
|
||||||
|
|
||||||
lock (LogLock)
|
|
||||||
{
|
|
||||||
int start = Mathf.Max(0, Logs.Count - count);
|
|
||||||
snapshot = Logs.GetRange(start, Logs.Count - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.Append("{\"ok\":true,\"logs\":[");
|
|
||||||
|
|
||||||
for (int i = 0; i < snapshot.Count; i++)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
sb.Append(',');
|
|
||||||
|
|
||||||
LogEntry entry = snapshot[i];
|
|
||||||
sb.Append('{');
|
|
||||||
sb.Append("\"index\":").Append(entry.Index.ToString(CultureInfo.InvariantCulture)).Append(',');
|
|
||||||
sb.Append("\"time\":").Append(JsonString(entry.Time)).Append(',');
|
|
||||||
sb.Append("\"type\":").Append(JsonString(entry.Type)).Append(',');
|
|
||||||
sb.Append("\"message\":").Append(JsonString(entry.Message)).Append(',');
|
|
||||||
sb.Append("\"stackTrace\":").Append(JsonString(entry.StackTrace));
|
|
||||||
sb.Append('}');
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append("]}");
|
|
||||||
return BridgeResponse.Json(200, sb.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeResponse GetSceneRoots()
|
|
||||||
{
|
|
||||||
Scene scene = SceneManager.GetActiveScene();
|
|
||||||
GameObject[] roots = scene.IsValid() ? scene.GetRootGameObjects() : Array.Empty<GameObject>();
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.Append("{\"ok\":true,\"scene\":").Append(JsonString(scene.IsValid() ? scene.name : string.Empty));
|
|
||||||
sb.Append(",\"roots\":[");
|
|
||||||
|
|
||||||
for (int i = 0; i < roots.Length; i++)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
sb.Append(',');
|
|
||||||
|
|
||||||
AppendGameObjectSummary(sb, roots[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append("]}");
|
|
||||||
return BridgeResponse.Json(200, sb.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeResponse GetSceneObjects(Dictionary<string, string> query)
|
|
||||||
{
|
|
||||||
string filter = GetString(query, "query", string.Empty);
|
|
||||||
int limit = GetInt(query, "limit", 120, 1, 500);
|
|
||||||
string lowerFilter = filter.ToLowerInvariant();
|
|
||||||
|
|
||||||
Transform[] transforms = UnityEngine.Object.FindObjectsByType<Transform>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.Append("{\"ok\":true,\"objects\":[");
|
|
||||||
|
|
||||||
int written = 0;
|
|
||||||
foreach (Transform transform in transforms)
|
|
||||||
{
|
|
||||||
if (transform == null || transform.gameObject == null)
|
|
||||||
continue;
|
|
||||||
if (EditorUtility.IsPersistent(transform.gameObject))
|
|
||||||
continue;
|
|
||||||
if (!transform.gameObject.scene.IsValid())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
string path = GetHierarchyPath(transform.gameObject);
|
|
||||||
if (!string.IsNullOrEmpty(lowerFilter) &&
|
|
||||||
path.ToLowerInvariant().IndexOf(lowerFilter, StringComparison.Ordinal) < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (written > 0)
|
|
||||||
sb.Append(',');
|
|
||||||
|
|
||||||
AppendGameObjectSummary(sb, transform.gameObject);
|
|
||||||
written++;
|
|
||||||
|
|
||||||
if (written >= limit)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append("],\"count\":").Append(written.ToString(CultureInfo.InvariantCulture)).Append('}');
|
|
||||||
return BridgeResponse.Json(200, sb.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeResponse GetObjectDetails(Dictionary<string, string> query)
|
|
||||||
{
|
|
||||||
string path = GetString(query, "path", string.Empty);
|
|
||||||
GameObject go = FindGameObjectByPath(path);
|
|
||||||
|
|
||||||
if (go == null)
|
|
||||||
return BridgeResponse.Json(404, "{\"ok\":false,\"error\":\"object_not_found\",\"path\":" + JsonString(path) + "}");
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.Append("{\"ok\":true,\"object\":");
|
|
||||||
AppendGameObjectDetails(sb, go);
|
|
||||||
sb.Append('}');
|
|
||||||
|
|
||||||
return BridgeResponse.Json(200, sb.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeResponse SetPlayState(bool play, bool pause)
|
|
||||||
{
|
|
||||||
EditorApplication.isPlaying = play;
|
|
||||||
EditorApplication.isPaused = pause;
|
|
||||||
return GetHealth();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BridgeResponse SetTransform(string body)
|
|
||||||
{
|
|
||||||
string path = ExtractString(body, "path");
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
return BridgeResponse.Json(400, "{\"ok\":false,\"error\":\"missing_path\"}");
|
|
||||||
|
|
||||||
GameObject go = FindGameObjectByPath(path);
|
|
||||||
if (go == null)
|
|
||||||
return BridgeResponse.Json(404, "{\"ok\":false,\"error\":\"object_not_found\",\"path\":" + JsonString(path) + "}");
|
|
||||||
|
|
||||||
Undo.RecordObject(go.transform, "Codex Bridge Set Transform");
|
|
||||||
|
|
||||||
if (TryExtractVector3(body, "position", out Vector3 position))
|
|
||||||
go.transform.position = position;
|
|
||||||
if (TryExtractVector3(body, "localPosition", out Vector3 localPosition))
|
|
||||||
go.transform.localPosition = localPosition;
|
|
||||||
if (TryExtractVector3(body, "rotationEuler", out Vector3 rotationEuler))
|
|
||||||
go.transform.eulerAngles = rotationEuler;
|
|
||||||
if (TryExtractVector3(body, "localRotationEuler", out Vector3 localRotationEuler))
|
|
||||||
go.transform.localEulerAngles = localRotationEuler;
|
|
||||||
if (TryExtractVector3(body, "scale", out Vector3 scale))
|
|
||||||
go.transform.localScale = scale;
|
|
||||||
|
|
||||||
EditorUtility.SetDirty(go.transform);
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.Append("{\"ok\":true,\"object\":");
|
|
||||||
AppendGameObjectDetails(sb, go);
|
|
||||||
sb.Append('}');
|
|
||||||
|
|
||||||
return BridgeResponse.Json(200, sb.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AppendGameObjectSummary(StringBuilder sb, GameObject go)
|
|
||||||
{
|
|
||||||
sb.Append('{');
|
|
||||||
sb.Append("\"name\":").Append(JsonString(go.name)).Append(',');
|
|
||||||
sb.Append("\"path\":").Append(JsonString(GetHierarchyPath(go))).Append(',');
|
|
||||||
sb.Append("\"activeSelf\":").Append(Bool(go.activeSelf)).Append(',');
|
|
||||||
sb.Append("\"activeInHierarchy\":").Append(Bool(go.activeInHierarchy)).Append(',');
|
|
||||||
sb.Append("\"scene\":").Append(JsonString(go.scene.IsValid() ? go.scene.name : string.Empty)).Append(',');
|
|
||||||
sb.Append("\"position\":").Append(VectorJson(go.transform.position)).Append(',');
|
|
||||||
sb.Append("\"rotationEuler\":").Append(VectorJson(go.transform.eulerAngles)).Append(',');
|
|
||||||
sb.Append("\"scale\":").Append(VectorJson(go.transform.localScale));
|
|
||||||
sb.Append('}');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AppendGameObjectDetails(StringBuilder sb, GameObject go)
|
|
||||||
{
|
|
||||||
AppendGameObjectSummary(sb, go);
|
|
||||||
sb.Length -= 1;
|
|
||||||
|
|
||||||
Component[] components = go.GetComponents<Component>();
|
|
||||||
sb.Append(",\"components\":[");
|
|
||||||
for (int i = 0; i < components.Length; i++)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
sb.Append(',');
|
|
||||||
|
|
||||||
Component component = components[i];
|
|
||||||
sb.Append(JsonString(component != null ? component.GetType().FullName : "Missing Script"));
|
|
||||||
}
|
|
||||||
sb.Append("]}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GameObject FindGameObjectByPath(string path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (parts.Length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
Scene scene = SceneManager.GetActiveScene();
|
|
||||||
if (!scene.IsValid())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
foreach (GameObject root in scene.GetRootGameObjects())
|
|
||||||
{
|
|
||||||
if (root.name != parts[0])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Transform current = root.transform;
|
|
||||||
for (int i = 1; i < parts.Length && current != null; i++)
|
|
||||||
{
|
|
||||||
current = FindChildByName(current, parts[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current != null)
|
|
||||||
return current.gameObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Transform FindChildByName(Transform parent, string childName)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < parent.childCount; i++)
|
|
||||||
{
|
|
||||||
Transform child = parent.GetChild(i);
|
|
||||||
if (child.name == childName)
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetHierarchyPath(GameObject go)
|
|
||||||
{
|
|
||||||
Stack<string> parts = new Stack<string>();
|
|
||||||
Transform current = go.transform;
|
|
||||||
|
|
||||||
while (current != null)
|
|
||||||
{
|
|
||||||
parts.Push(current.name);
|
|
||||||
current = current.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Join("/", parts.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnLogMessageReceived(string condition, string stackTrace, LogType type)
|
|
||||||
{
|
|
||||||
lock (LogLock)
|
|
||||||
{
|
|
||||||
Logs.Add(new LogEntry
|
|
||||||
{
|
|
||||||
Index = ++_logIndex,
|
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss", CultureInfo.InvariantCulture),
|
|
||||||
Type = type.ToString(),
|
|
||||||
Message = condition ?? string.Empty,
|
|
||||||
StackTrace = stackTrace ?? string.Empty
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Logs.Count > MaxLogs)
|
|
||||||
Logs.RemoveAt(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetInt(Dictionary<string, string> query, string key, int fallback, int min, int max)
|
|
||||||
{
|
|
||||||
if (!query.TryGetValue(key, out string value) ||
|
|
||||||
!int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int parsed))
|
|
||||||
return fallback;
|
|
||||||
|
|
||||||
return Mathf.Clamp(parsed, min, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetString(Dictionary<string, string> query, string key, string fallback)
|
|
||||||
{
|
|
||||||
return query.TryGetValue(key, out string value) ? value : fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ExtractString(string json, string key)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(json))
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
Match match = Regex.Match(json,
|
|
||||||
"\"" + Regex.Escape(key) + "\"\\s*:\\s*\"((?:\\\\.|[^\"])*)\"",
|
|
||||||
RegexOptions.CultureInvariant);
|
|
||||||
|
|
||||||
return match.Success ? Regex.Unescape(match.Groups[1].Value) : string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryExtractVector3(string json, string key, out Vector3 value)
|
|
||||||
{
|
|
||||||
value = Vector3.zero;
|
|
||||||
if (string.IsNullOrEmpty(json))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Match objectMatch = Regex.Match(json,
|
|
||||||
"\"" + Regex.Escape(key) + "\"\\s*:\\s*\\{(?<body>.*?)\\}",
|
|
||||||
RegexOptions.Singleline | RegexOptions.CultureInvariant);
|
|
||||||
|
|
||||||
if (!objectMatch.Success)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
string objectBody = objectMatch.Groups["body"].Value;
|
|
||||||
if (!TryExtractFloat(objectBody, "x", out float x) ||
|
|
||||||
!TryExtractFloat(objectBody, "y", out float y) ||
|
|
||||||
!TryExtractFloat(objectBody, "z", out float z))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
value = new Vector3(x, y, z);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryExtractFloat(string json, string key, out float value)
|
|
||||||
{
|
|
||||||
value = 0f;
|
|
||||||
Match match = Regex.Match(json,
|
|
||||||
"\"" + Regex.Escape(key) + "\"\\s*:\\s*(?<num>-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?)",
|
|
||||||
RegexOptions.CultureInvariant);
|
|
||||||
|
|
||||||
return match.Success &&
|
|
||||||
float.TryParse(match.Groups["num"].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string VectorJson(Vector3 value)
|
|
||||||
{
|
|
||||||
return "{\"x\":" + FloatJson(value.x) +
|
|
||||||
",\"y\":" + FloatJson(value.y) +
|
|
||||||
",\"z\":" + FloatJson(value.z) + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FloatJson(float value)
|
|
||||||
{
|
|
||||||
if (float.IsNaN(value) || float.IsInfinity(value))
|
|
||||||
return "0";
|
|
||||||
|
|
||||||
return value.ToString("0.#####", CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string Bool(bool value)
|
|
||||||
{
|
|
||||||
return value ? "true" : "false";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string JsonString(string value)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
return "null";
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(value.Length + 2);
|
|
||||||
sb.Append('"');
|
|
||||||
foreach (char c in value)
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case '\\':
|
|
||||||
sb.Append("\\\\");
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
sb.Append("\\\"");
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
sb.Append("\\n");
|
|
||||||
break;
|
|
||||||
case '\r':
|
|
||||||
sb.Append("\\r");
|
|
||||||
break;
|
|
||||||
case '\t':
|
|
||||||
sb.Append("\\t");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (c < 32)
|
|
||||||
sb.Append("\\u").Append(((int)c).ToString("x4", CultureInfo.InvariantCulture));
|
|
||||||
else
|
|
||||||
sb.Append(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.Append('"');
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class BridgeRequest
|
|
||||||
{
|
|
||||||
public readonly string Method;
|
|
||||||
public readonly string Path;
|
|
||||||
public readonly Dictionary<string, string> Query;
|
|
||||||
public readonly string Body;
|
|
||||||
|
|
||||||
public BridgeRequest(string method, string path, Dictionary<string, string> query, string body)
|
|
||||||
{
|
|
||||||
Method = method;
|
|
||||||
Path = path;
|
|
||||||
Query = query;
|
|
||||||
Body = body ?? string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class BridgeResponse
|
|
||||||
{
|
|
||||||
public readonly int StatusCode;
|
|
||||||
public readonly string ContentType;
|
|
||||||
public readonly string Body;
|
|
||||||
|
|
||||||
private BridgeResponse(int statusCode, string contentType, string body)
|
|
||||||
{
|
|
||||||
StatusCode = statusCode;
|
|
||||||
ContentType = contentType;
|
|
||||||
Body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BridgeResponse Json(int statusCode, string body)
|
|
||||||
{
|
|
||||||
return new BridgeResponse(statusCode, "application/json", body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class BridgeJob
|
|
||||||
{
|
|
||||||
public readonly BridgeRequest Request;
|
|
||||||
public readonly ManualResetEventSlim Done = new ManualResetEventSlim(false);
|
|
||||||
public BridgeResponse Response;
|
|
||||||
|
|
||||||
public BridgeJob(BridgeRequest request)
|
|
||||||
{
|
|
||||||
Request = request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class LogEntry
|
|
||||||
{
|
|
||||||
public int Index;
|
|
||||||
public string Time;
|
|
||||||
public string Type;
|
|
||||||
public string Message;
|
|
||||||
public string StackTrace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: b3aaf23c3d6f42b5b1f68100b9b0f682
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,507 +0,0 @@
|
|||||||
using UnityEditor;
|
|
||||||
using UnityEditor.SceneManagement;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using TMPro;
|
|
||||||
using VRBeats;
|
|
||||||
using VRBeats.ScriptableEvents;
|
|
||||||
|
|
||||||
public static class VRBeatSaberSceneBuilder
|
|
||||||
{
|
|
||||||
private const string MenuScene = "Assets/VRBeatsKit/Scenes/Menu.unity";
|
|
||||||
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
// ④ Build Game Scene
|
|
||||||
// SaberStyle 복제 → Game.unity 생성
|
|
||||||
// PlayableManager 제거, SongController + 카운트다운 캔버스 추가
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
[MenuItem("Tools/VRBeatSaber/④ Build Game Scene")]
|
|
||||||
public static void BuildGameScene()
|
|
||||||
{
|
|
||||||
const string saberStylePath = "Assets/VRBeatsKit/Scenes/SaberStyle.unity";
|
|
||||||
const string gamePath = "Assets/Scenes/Game.unity";
|
|
||||||
|
|
||||||
// SaberStyle → Game.unity 복제 (이미 있으면 그냥 열기)
|
|
||||||
if (!AssetDatabase.LoadAssetAtPath<Object>(gamePath))
|
|
||||||
{
|
|
||||||
if (!AssetDatabase.CopyAsset(saberStylePath, gamePath))
|
|
||||||
{
|
|
||||||
Debug.LogError("[SceneBuilder] SaberStyle.unity 복제 실패");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
AssetDatabase.Refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
var scene = EditorSceneManager.OpenScene(gamePath, OpenSceneMode.Single);
|
|
||||||
|
|
||||||
// PlayableManager 제거 (PlayableDirector는 유지)
|
|
||||||
var pm = Object.FindFirstObjectByType<PlayableManager>();
|
|
||||||
if (pm != null)
|
|
||||||
Object.DestroyImmediate(pm);
|
|
||||||
|
|
||||||
// SongController GO 생성
|
|
||||||
var scGO = new GameObject("SongController");
|
|
||||||
var songController = scGO.AddComponent<SongController>();
|
|
||||||
|
|
||||||
var cubePrefab = AssetDatabase.LoadAssetAtPath<Spawneable>(
|
|
||||||
"Assets/VRBeatsKit/Prefabs/Spawneable/VR_BeatCube.prefab");
|
|
||||||
var onLevelComplete = AssetDatabase.LoadAssetAtPath<GameEvent>(
|
|
||||||
"Assets/VRBeatsKit/GameEvents/OnLevelComplete.asset");
|
|
||||||
|
|
||||||
// 카운트다운 캔버스 생성
|
|
||||||
var canvasGO = new GameObject("CountdownCanvas");
|
|
||||||
var canvas = canvasGO.AddComponent<Canvas>();
|
|
||||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
||||||
canvas.sortingOrder = 100;
|
|
||||||
canvasGO.AddComponent<CanvasScaler>();
|
|
||||||
canvasGO.AddComponent<GraphicRaycaster>();
|
|
||||||
|
|
||||||
var countdownGO = new GameObject("CountdownText");
|
|
||||||
countdownGO.transform.SetParent(canvasGO.transform, false);
|
|
||||||
var cRect = countdownGO.AddComponent<RectTransform>();
|
|
||||||
cRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
||||||
cRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
||||||
cRect.pivot = new Vector2(0.5f, 0.5f);
|
|
||||||
cRect.anchoredPosition = Vector2.zero;
|
|
||||||
cRect.sizeDelta = new Vector2(400f, 200f);
|
|
||||||
var cTmp = countdownGO.AddComponent<TextMeshProUGUI>();
|
|
||||||
cTmp.text = "";
|
|
||||||
cTmp.fontSize = 120f;
|
|
||||||
cTmp.color = Color.white;
|
|
||||||
cTmp.alignment = TextAlignmentOptions.Center;
|
|
||||||
cTmp.fontStyle = FontStyles.Bold;
|
|
||||||
countdownGO.SetActive(false);
|
|
||||||
|
|
||||||
// SongController 필드 연결
|
|
||||||
var scSO = new SerializedObject(songController);
|
|
||||||
scSO.FindProperty("cubePrefab") .objectReferenceValue = cubePrefab;
|
|
||||||
scSO.FindProperty("onLevelComplete") .objectReferenceValue = onLevelComplete;
|
|
||||||
scSO.FindProperty("countdownText") .objectReferenceValue = cTmp;
|
|
||||||
scSO.ApplyModifiedPropertiesWithoutUndo();
|
|
||||||
|
|
||||||
// Build Settings 에 Game.unity 추가
|
|
||||||
var scenes = EditorBuildSettings.scenes;
|
|
||||||
bool exists = System.Array.Exists(scenes, s => s.path == gamePath);
|
|
||||||
if (!exists)
|
|
||||||
{
|
|
||||||
var newList = new EditorBuildSettingsScene[scenes.Length + 1];
|
|
||||||
System.Array.Copy(scenes, newList, scenes.Length);
|
|
||||||
newList[scenes.Length] = new EditorBuildSettingsScene(gamePath, true);
|
|
||||||
EditorBuildSettings.scenes = newList;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorSceneManager.MarkSceneDirty(scene);
|
|
||||||
EditorSceneManager.SaveScene(scene);
|
|
||||||
Debug.Log("[SceneBuilder] ✓ Game.unity 생성 완료");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
// ③ Menu — Rebuild SongSelect Panel
|
|
||||||
//
|
|
||||||
// Canvas(SongSelect) size: 105.885 × 68.223
|
|
||||||
// BG child covers full canvas (stretch anchors)
|
|
||||||
// BG local coord origin = center
|
|
||||||
// X: -52.94 ~ +52.94
|
|
||||||
// Y: -34.11 ~ +34.11
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
[MenuItem("Tools/VRBeatSaber/③ Menu — Rebuild SongSelect Panel")]
|
|
||||||
public static void RebuildSongSelectPanel()
|
|
||||||
{
|
|
||||||
var scene = EditorSceneManager.OpenScene(MenuScene, OpenSceneMode.Single);
|
|
||||||
|
|
||||||
var songSelectGO = GameObject.Find("SongSelect");
|
|
||||||
if (songSelectGO == null) { Debug.LogError("[SceneBuilder] 'SongSelect' not found."); return; }
|
|
||||||
|
|
||||||
var bgTransform = songSelectGO.transform.Find("BG");
|
|
||||||
if (bgTransform == null) { Debug.LogError("[SceneBuilder] 'SongSelect/BG' not found."); return; }
|
|
||||||
|
|
||||||
// Clear BG children
|
|
||||||
for (int i = bgTransform.childCount - 1; i >= 0; i--)
|
|
||||||
Object.DestroyImmediate(bgTransform.GetChild(i).gameObject);
|
|
||||||
|
|
||||||
// Create/reuse SongSystem root GO for SongLibrary (must be root for DontDestroyOnLoad)
|
|
||||||
var sysGO = GameObject.Find("SongSystem");
|
|
||||||
if (sysGO == null) sysGO = new GameObject("SongSystem");
|
|
||||||
var oldLib = sysGO.GetComponent<SongLibrary>();
|
|
||||||
if (oldLib != null) Object.DestroyImmediate(oldLib);
|
|
||||||
var songLibrary = sysGO.AddComponent<SongLibrary>();
|
|
||||||
|
|
||||||
// Add/replace SongSelectManager + DownloadManager on SongSelect GO
|
|
||||||
var oldSSM = songSelectGO.GetComponent<SongSelectManager>();
|
|
||||||
if (oldSSM != null) Object.DestroyImmediate(oldSSM);
|
|
||||||
var oldDM = songSelectGO.GetComponent<DownloadManager>();
|
|
||||||
if (oldDM != null) Object.DestroyImmediate(oldDM);
|
|
||||||
|
|
||||||
var downloadManager = songSelectGO.AddComponent<DownloadManager>();
|
|
||||||
var songSelectManager = songSelectGO.AddComponent<SongSelectManager>();
|
|
||||||
|
|
||||||
var bg = bgTransform;
|
|
||||||
|
|
||||||
// ── Header ──────────────────────────────────────────
|
|
||||||
|
|
||||||
CreateLabel(bg, "Title", "SONG SELECT",
|
|
||||||
new Vector2(0f, 28.5f), new Vector2(100f, 9f), 8.5f,
|
|
||||||
Color.white, TextAlignmentOptions.Center);
|
|
||||||
|
|
||||||
CreateDivider(bg, "DivHeader", new Vector2(0f, 23.5f), new Vector2(104f, 0.5f));
|
|
||||||
|
|
||||||
var tabAllBtn = CreateStyledButton(bg, "TabAll", "ALL", new Vector2(-18f, 19.5f), new Vector2(30f, 7f), 5f);
|
|
||||||
var tabOwnedBtn = CreateStyledButton(bg, "TabOwned", "OWNED", new Vector2( 14f, 19.5f), new Vector2(30f, 7f), 5f);
|
|
||||||
|
|
||||||
CreateDivider(bg, "DivTabs", new Vector2(0f, 15.5f), new Vector2(104f, 0.5f));
|
|
||||||
|
|
||||||
// ── Content area: Y from 15 to -34.11, height ~49 ───
|
|
||||||
|
|
||||||
// ListPanel (left half)
|
|
||||||
var listPanelGO = new GameObject("ListPanel");
|
|
||||||
listPanelGO.transform.SetParent(bg, false);
|
|
||||||
SetRect(listPanelGO, new Vector2(-26.6f, -9.4f), new Vector2(52.7f, 49f));
|
|
||||||
|
|
||||||
// Vertical divider
|
|
||||||
CreateDivider(bg, "DivVertical", new Vector2(0.1f, -9.4f), new Vector2(0.5f, 49f));
|
|
||||||
|
|
||||||
// DetailPanel (right half, hidden until card clicked)
|
|
||||||
var detailPanelGO = new GameObject("DetailPanel");
|
|
||||||
detailPanelGO.transform.SetParent(bg, false);
|
|
||||||
SetRect(detailPanelGO, new Vector2(26.6f, -9.4f), new Vector2(52.7f, 49f));
|
|
||||||
|
|
||||||
// ── ListPanel contents ───────────────────────────────
|
|
||||||
|
|
||||||
RectTransform scrollContent;
|
|
||||||
GameObject loadingOverlay;
|
|
||||||
GameObject errorOverlay;
|
|
||||||
TMP_Text errorText;
|
|
||||||
BuildScrollList(listPanelGO.transform,
|
|
||||||
out scrollContent, out loadingOverlay, out errorOverlay, out errorText);
|
|
||||||
|
|
||||||
// ── DetailPanel contents ─────────────────────────────
|
|
||||||
|
|
||||||
var detailPanelComp = detailPanelGO.AddComponent<SongDetailPanel>();
|
|
||||||
Button btnNormal, btnHard, btnExpert, btnExpertPlus;
|
|
||||||
Button downloadBtn, deleteBtn, playBtn, closeBtn;
|
|
||||||
GameObject progressGroup;
|
|
||||||
Slider progressSlider;
|
|
||||||
TMP_Text progressText;
|
|
||||||
TMP_Text titleTmp, artistTmp, infoTmp;
|
|
||||||
BuildDetailPanelUI(detailPanelGO.transform,
|
|
||||||
out titleTmp, out artistTmp, out infoTmp,
|
|
||||||
out btnNormal, out btnHard, out btnExpert, out btnExpertPlus,
|
|
||||||
out downloadBtn, out deleteBtn, out playBtn, out closeBtn,
|
|
||||||
out progressGroup, out progressSlider, out progressText);
|
|
||||||
|
|
||||||
detailPanelGO.SetActive(false);
|
|
||||||
|
|
||||||
// ── Wire SongDetailPanel refs ────────────────────────
|
|
||||||
|
|
||||||
var dpSO = new SerializedObject(detailPanelComp);
|
|
||||||
dpSO.FindProperty("titleText") .objectReferenceValue = titleTmp;
|
|
||||||
dpSO.FindProperty("artistText") .objectReferenceValue = artistTmp;
|
|
||||||
dpSO.FindProperty("infoText") .objectReferenceValue = infoTmp;
|
|
||||||
dpSO.FindProperty("btnNormal") .objectReferenceValue = btnNormal;
|
|
||||||
dpSO.FindProperty("btnHard") .objectReferenceValue = btnHard;
|
|
||||||
dpSO.FindProperty("btnExpert") .objectReferenceValue = btnExpert;
|
|
||||||
dpSO.FindProperty("btnExpertPlus") .objectReferenceValue = btnExpertPlus;
|
|
||||||
dpSO.FindProperty("downloadButton") .objectReferenceValue = downloadBtn;
|
|
||||||
dpSO.FindProperty("deleteButton") .objectReferenceValue = deleteBtn;
|
|
||||||
dpSO.FindProperty("playButton") .objectReferenceValue = playBtn;
|
|
||||||
dpSO.FindProperty("closeButton") .objectReferenceValue = closeBtn;
|
|
||||||
dpSO.FindProperty("progressGroup") .objectReferenceValue = progressGroup;
|
|
||||||
dpSO.FindProperty("progressSlider") .objectReferenceValue = progressSlider;
|
|
||||||
dpSO.FindProperty("progressText") .objectReferenceValue = progressText;
|
|
||||||
dpSO.FindProperty("gameSceneName") .stringValue = "Game";
|
|
||||||
dpSO.ApplyModifiedPropertiesWithoutUndo();
|
|
||||||
|
|
||||||
// ── Wire SongSelectManager refs ──────────────────────
|
|
||||||
|
|
||||||
var smSO = new SerializedObject(songSelectManager);
|
|
||||||
smSO.FindProperty("tabAllBtn") .objectReferenceValue = tabAllBtn.GetComponent<Button>();
|
|
||||||
smSO.FindProperty("tabOwnedBtn") .objectReferenceValue = tabOwnedBtn.GetComponent<Button>();
|
|
||||||
smSO.FindProperty("cardContainer") .objectReferenceValue = scrollContent;
|
|
||||||
smSO.FindProperty("detailPanel") .objectReferenceValue = detailPanelComp;
|
|
||||||
smSO.FindProperty("downloadManager").objectReferenceValue = downloadManager;
|
|
||||||
smSO.FindProperty("loadingOverlay") .objectReferenceValue = loadingOverlay;
|
|
||||||
smSO.FindProperty("errorOverlay") .objectReferenceValue = errorOverlay;
|
|
||||||
smSO.FindProperty("errorText") .objectReferenceValue = errorText;
|
|
||||||
smSO.ApplyModifiedPropertiesWithoutUndo();
|
|
||||||
|
|
||||||
EditorSceneManager.MarkSceneDirty(scene);
|
|
||||||
EditorSceneManager.SaveScene(scene);
|
|
||||||
Debug.Log("[SceneBuilder] ✓ SongSelect panel rebuilt in Menu.unity");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
// ListPanel: ScrollRect + overlays
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
|
|
||||||
private static void BuildScrollList(Transform parent,
|
|
||||||
out RectTransform scrollContent,
|
|
||||||
out GameObject loadingOverlay,
|
|
||||||
out GameObject errorOverlay,
|
|
||||||
out TMP_Text errorText)
|
|
||||||
{
|
|
||||||
// ScrollRect (fills parent)
|
|
||||||
var scrollGO = new GameObject("Scroll");
|
|
||||||
scrollGO.transform.SetParent(parent, false);
|
|
||||||
StretchFull(scrollGO);
|
|
||||||
|
|
||||||
// Viewport with Mask
|
|
||||||
var vpGO = new GameObject("Viewport");
|
|
||||||
vpGO.transform.SetParent(scrollGO.transform, false);
|
|
||||||
StretchFull(vpGO);
|
|
||||||
var vpImg = vpGO.AddComponent<Image>();
|
|
||||||
vpImg.color = new Color(0f, 0f, 0f, 0.01f);
|
|
||||||
vpGO.AddComponent<Mask>().showMaskGraphic = false;
|
|
||||||
|
|
||||||
// Content with VerticalLayoutGroup + ContentSizeFitter
|
|
||||||
var contentGO = new GameObject("Content");
|
|
||||||
contentGO.transform.SetParent(vpGO.transform, false);
|
|
||||||
var contentRect = contentGO.AddComponent<RectTransform>();
|
|
||||||
contentRect.anchorMin = new Vector2(0f, 1f);
|
|
||||||
contentRect.anchorMax = new Vector2(1f, 1f);
|
|
||||||
contentRect.pivot = new Vector2(0.5f, 1f);
|
|
||||||
contentRect.anchoredPosition = Vector2.zero;
|
|
||||||
contentRect.sizeDelta = Vector2.zero;
|
|
||||||
|
|
||||||
var vlg = contentGO.AddComponent<VerticalLayoutGroup>();
|
|
||||||
vlg.spacing = 1.5f;
|
|
||||||
vlg.padding = new RectOffset(2, 2, 2, 2);
|
|
||||||
vlg.childForceExpandWidth = true;
|
|
||||||
vlg.childForceExpandHeight = false;
|
|
||||||
vlg.childControlWidth = true;
|
|
||||||
vlg.childControlHeight = true;
|
|
||||||
|
|
||||||
var csf = contentGO.AddComponent<ContentSizeFitter>();
|
|
||||||
csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
||||||
csf.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
|
||||||
|
|
||||||
// ScrollRect component
|
|
||||||
var sr = scrollGO.AddComponent<ScrollRect>();
|
|
||||||
sr.content = contentRect;
|
|
||||||
sr.viewport = vpGO.GetComponent<RectTransform>();
|
|
||||||
sr.horizontal = false;
|
|
||||||
sr.vertical = true;
|
|
||||||
sr.movementType = ScrollRect.MovementType.Clamped;
|
|
||||||
sr.scrollSensitivity = 30f;
|
|
||||||
sr.inertia = true;
|
|
||||||
sr.decelerationRate = 0.135f;
|
|
||||||
|
|
||||||
// Loading overlay
|
|
||||||
loadingOverlay = new GameObject("LoadingOverlay");
|
|
||||||
loadingOverlay.transform.SetParent(parent, false);
|
|
||||||
StretchFull(loadingOverlay);
|
|
||||||
loadingOverlay.AddComponent<Image>().color = new Color(0.10f, 0.18f, 0.22f, 0.92f);
|
|
||||||
CreateLabel(loadingOverlay.transform, "Text", "Loading...",
|
|
||||||
Vector2.zero, new Vector2(40f, 10f), 5f, Color.white, TextAlignmentOptions.Center);
|
|
||||||
|
|
||||||
// Error overlay (hidden by default)
|
|
||||||
errorOverlay = new GameObject("ErrorOverlay");
|
|
||||||
errorOverlay.transform.SetParent(parent, false);
|
|
||||||
StretchFull(errorOverlay);
|
|
||||||
errorOverlay.AddComponent<Image>().color = new Color(0.10f, 0.18f, 0.22f, 0.92f);
|
|
||||||
var errLblGO = CreateLabel(errorOverlay.transform, "ErrorText", "",
|
|
||||||
Vector2.zero, new Vector2(48f, 20f), 4.5f, new Color(1f, 0.5f, 0.5f), TextAlignmentOptions.Center);
|
|
||||||
errorText = errLblGO.GetComponent<TMP_Text>();
|
|
||||||
errorOverlay.SetActive(false);
|
|
||||||
|
|
||||||
scrollContent = contentRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
// DetailPanel UI
|
|
||||||
// Local space: 52.7 × 49 → X: ±26.35, Y: ±24.5
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
|
|
||||||
private static void BuildDetailPanelUI(Transform parent,
|
|
||||||
out TMP_Text titleTmp, out TMP_Text artistTmp, out TMP_Text infoTmp,
|
|
||||||
out Button btnNormal, out Button btnHard, out Button btnExpert, out Button btnExpertPlus,
|
|
||||||
out Button downloadBtn, out Button deleteBtn, out Button playBtn, out Button closeBtn,
|
|
||||||
out GameObject progressGroup, out Slider progressSlider, out TMP_Text progressText)
|
|
||||||
{
|
|
||||||
// Close button (top-right)
|
|
||||||
var closeBtnGO = CreateStyledButton(parent, "CloseBtn", "✕",
|
|
||||||
new Vector2(21f, 20.5f), new Vector2(8f, 7f), 5.5f);
|
|
||||||
closeBtn = closeBtnGO.GetComponent<Button>();
|
|
||||||
|
|
||||||
// Song info
|
|
||||||
var titleGO = CreateLabel(parent, "TitleText", "---",
|
|
||||||
new Vector2(-3f, 18.5f), new Vector2(38f, 8f), 6.5f,
|
|
||||||
Color.white, TextAlignmentOptions.MidlineLeft);
|
|
||||||
titleTmp = titleGO.GetComponent<TMP_Text>();
|
|
||||||
titleTmp.overflowMode = TextOverflowModes.Ellipsis;
|
|
||||||
|
|
||||||
var artistGO = CreateLabel(parent, "ArtistText", "",
|
|
||||||
new Vector2(0f, 12f), new Vector2(50f, 6f), 5f,
|
|
||||||
new Color(1f, 1f, 1f, 0.8f), TextAlignmentOptions.Center);
|
|
||||||
artistTmp = artistGO.GetComponent<TMP_Text>();
|
|
||||||
|
|
||||||
var infoGO = CreateLabel(parent, "InfoText", "",
|
|
||||||
new Vector2(0f, 7f), new Vector2(50f, 5f), 4.2f,
|
|
||||||
new Color(1f, 1f, 1f, 0.6f), TextAlignmentOptions.Center);
|
|
||||||
infoTmp = infoGO.GetComponent<TMP_Text>();
|
|
||||||
|
|
||||||
CreateDivider(parent, "Div1", new Vector2(0f, 4f), new Vector2(50f, 0.4f));
|
|
||||||
|
|
||||||
// Difficulty section
|
|
||||||
CreateLabel(parent, "LblDifficulty", "DIFFICULTY",
|
|
||||||
new Vector2(-16f, 1.5f), new Vector2(26f, 5f), 4.2f,
|
|
||||||
new Color(1f, 1f, 1f, 0.65f), TextAlignmentOptions.MidlineLeft);
|
|
||||||
|
|
||||||
var btnNormalGO = CreateStyledButton(parent, "BtnNormal", "Normal", new Vector2(-12f, -5f), new Vector2(22f, 7f), 4.5f);
|
|
||||||
var btnHardGO = CreateStyledButton(parent, "BtnHard", "Hard", new Vector2( 12f, -5f), new Vector2(22f, 7f), 4.5f);
|
|
||||||
var btnExpertGO = CreateStyledButton(parent, "BtnExpert", "Expert", new Vector2(-12f, -14f), new Vector2(22f, 7f), 4.5f);
|
|
||||||
var btnExpertPlusGO = CreateStyledButton(parent, "BtnExpertPlus", "Expert+", new Vector2( 12f, -14f), new Vector2(22f, 7f), 4.5f);
|
|
||||||
|
|
||||||
btnNormal = btnNormalGO .GetComponent<Button>();
|
|
||||||
btnHard = btnHardGO .GetComponent<Button>();
|
|
||||||
btnExpert = btnExpertGO .GetComponent<Button>();
|
|
||||||
btnExpertPlus = btnExpertPlusGO.GetComponent<Button>();
|
|
||||||
|
|
||||||
CreateDivider(parent, "Div2", new Vector2(0f, -18.5f), new Vector2(50f, 0.4f));
|
|
||||||
|
|
||||||
// Action buttons
|
|
||||||
var downloadBtnGO = CreateStyledButton(parent, "DownloadBtn", "Download",
|
|
||||||
new Vector2(-6f, -21.5f), new Vector2(34f, 7f), 5f);
|
|
||||||
var deleteBtnGO = CreateStyledButton(parent, "DeleteBtn", "Delete",
|
|
||||||
new Vector2(-6f, -21.5f), new Vector2(34f, 7f), 5f);
|
|
||||||
var playBtnGO = CreateStyledButton(parent, "PlayBtn", "Play",
|
|
||||||
new Vector2(19f, -21.5f), new Vector2(16f, 7f), 5f);
|
|
||||||
|
|
||||||
downloadBtn = downloadBtnGO.GetComponent<Button>();
|
|
||||||
deleteBtn = deleteBtnGO .GetComponent<Button>();
|
|
||||||
playBtn = playBtnGO .GetComponent<Button>();
|
|
||||||
|
|
||||||
// Make delete button red-tinted
|
|
||||||
var delImg = deleteBtnGO.GetComponent<Image>();
|
|
||||||
if (delImg != null) delImg.color = new Color(0.9f, 0.3f, 0.3f, 0.3f);
|
|
||||||
|
|
||||||
// Progress group (hidden by default)
|
|
||||||
progressGroup = new GameObject("ProgressGroup");
|
|
||||||
progressGroup.transform.SetParent(parent, false);
|
|
||||||
SetRect(progressGroup, new Vector2(0f, -21.5f), new Vector2(50f, 7f));
|
|
||||||
|
|
||||||
var pTextGO = CreateLabel(progressGroup.transform, "ProgressText", "--- 0%",
|
|
||||||
new Vector2(-13f, 0f), new Vector2(22f, 6f), 4f,
|
|
||||||
new Color(1f, 1f, 1f, 0.85f), TextAlignmentOptions.MidlineLeft);
|
|
||||||
progressText = pTextGO.GetComponent<TMP_Text>();
|
|
||||||
|
|
||||||
progressSlider = CreateSlider(progressGroup.transform, "ProgressSlider",
|
|
||||||
new Vector2(18f, 0f), new Vector2(18f, 4.5f));
|
|
||||||
|
|
||||||
progressGroup.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
// Helpers — UI factory
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
|
|
||||||
private static GameObject CreateStyledButton(Transform parent, string goName, string label,
|
|
||||||
Vector2 pos, Vector2 size, float fontSize)
|
|
||||||
{
|
|
||||||
var go = new GameObject(goName);
|
|
||||||
go.transform.SetParent(parent, false);
|
|
||||||
SetRect(go, pos, size);
|
|
||||||
|
|
||||||
var img = go.AddComponent<Image>();
|
|
||||||
img.color = new Color(1f, 1f, 1f, 0.12f);
|
|
||||||
|
|
||||||
var btn = go.AddComponent<Button>();
|
|
||||||
btn.targetGraphic = img;
|
|
||||||
var c = btn.colors;
|
|
||||||
c.normalColor = Color.white;
|
|
||||||
c.highlightedColor = new Color(0.96f, 0.96f, 0.96f, 1f);
|
|
||||||
c.pressedColor = new Color(0.78f, 0.78f, 0.78f, 1f);
|
|
||||||
c.selectedColor = new Color(0.96f, 0.96f, 0.96f, 1f);
|
|
||||||
c.fadeDuration = 0.1f;
|
|
||||||
btn.colors = c;
|
|
||||||
|
|
||||||
var textGO = new GameObject("Text");
|
|
||||||
textGO.transform.SetParent(go.transform, false);
|
|
||||||
StretchFull(textGO);
|
|
||||||
var tmp = textGO.AddComponent<TextMeshProUGUI>();
|
|
||||||
tmp.text = label;
|
|
||||||
tmp.alignment = TextAlignmentOptions.Center;
|
|
||||||
tmp.fontSize = fontSize;
|
|
||||||
tmp.color = Color.white;
|
|
||||||
|
|
||||||
return go;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GameObject CreateLabel(Transform parent, string goName, string text,
|
|
||||||
Vector2 pos, Vector2 size, float fontSize,
|
|
||||||
Color? color = null, TextAlignmentOptions align = TextAlignmentOptions.MidlineLeft)
|
|
||||||
{
|
|
||||||
var go = new GameObject(goName);
|
|
||||||
go.transform.SetParent(parent, false);
|
|
||||||
SetRect(go, pos, size);
|
|
||||||
var tmp = go.AddComponent<TextMeshProUGUI>();
|
|
||||||
tmp.text = text;
|
|
||||||
tmp.fontSize = fontSize;
|
|
||||||
tmp.color = color ?? Color.white;
|
|
||||||
tmp.alignment = align;
|
|
||||||
return go;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CreateDivider(Transform parent, string goName, Vector2 pos, Vector2 size)
|
|
||||||
{
|
|
||||||
var go = new GameObject(goName);
|
|
||||||
go.transform.SetParent(parent, false);
|
|
||||||
SetRect(go, pos, size);
|
|
||||||
var img = go.AddComponent<Image>();
|
|
||||||
img.color = new Color(1f, 1f, 1f, 0.18f);
|
|
||||||
img.raycastTarget = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Slider CreateSlider(Transform parent, string goName, Vector2 pos, Vector2 size)
|
|
||||||
{
|
|
||||||
var go = new GameObject(goName);
|
|
||||||
go.transform.SetParent(parent, false);
|
|
||||||
SetRect(go, pos, size);
|
|
||||||
|
|
||||||
var bgGO = new GameObject("Background");
|
|
||||||
bgGO.transform.SetParent(go.transform, false);
|
|
||||||
StretchFull(bgGO);
|
|
||||||
bgGO.AddComponent<Image>().color = new Color(1f, 1f, 1f, 0.15f);
|
|
||||||
|
|
||||||
var fillArea = new GameObject("Fill Area");
|
|
||||||
fillArea.transform.SetParent(go.transform, false);
|
|
||||||
StretchFull(fillArea);
|
|
||||||
|
|
||||||
var fill = new GameObject("Fill");
|
|
||||||
fill.transform.SetParent(fillArea.transform, false);
|
|
||||||
StretchFull(fill);
|
|
||||||
fill.AddComponent<Image>().color = new Color(0.3f, 0.8f, 0.3f, 0.9f);
|
|
||||||
|
|
||||||
var slider = go.AddComponent<Slider>();
|
|
||||||
slider.fillRect = fill.GetComponent<RectTransform>();
|
|
||||||
slider.minValue = 0f;
|
|
||||||
slider.maxValue = 1f;
|
|
||||||
slider.value = 0f;
|
|
||||||
slider.interactable = false;
|
|
||||||
|
|
||||||
return slider;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
// Utils
|
|
||||||
// ─────────────────────────────────────────────
|
|
||||||
|
|
||||||
private static void SetRect(GameObject go, Vector2 pos, Vector2 size)
|
|
||||||
{
|
|
||||||
var r = go.GetComponent<RectTransform>();
|
|
||||||
if (r == null) r = go.AddComponent<RectTransform>();
|
|
||||||
r.anchorMin = new Vector2(0.5f, 0.5f);
|
|
||||||
r.anchorMax = new Vector2(0.5f, 0.5f);
|
|
||||||
r.pivot = new Vector2(0.5f, 0.5f);
|
|
||||||
r.anchoredPosition = pos;
|
|
||||||
r.sizeDelta = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void StretchFull(GameObject go)
|
|
||||||
{
|
|
||||||
var r = go.GetComponent<RectTransform>();
|
|
||||||
if (r == null) r = go.AddComponent<RectTransform>();
|
|
||||||
r.anchorMin = Vector2.zero;
|
|
||||||
r.anchorMax = Vector2.one;
|
|
||||||
r.offsetMin = r.offsetMax = Vector2.zero;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 424dfcaa538598f4785c3cb3be62ec8a
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 052faaac586de48259a63d0c4782560b
|
|
||||||
ScriptedImporter:
|
|
||||||
internalIDToNameTable: []
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3}
|
|
||||||
generateWrapperCode: 0
|
|
||||||
wrapperCodePath:
|
|
||||||
wrapperClassName:
|
|
||||||
wrapperCodeNamespace:
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: fcf7219bab7fe46a1ad266029b2fee19, type: 3}
|
|
||||||
m_Name: Readme
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
icon: {fileID: 2800000, guid: 727a75301c3d24613a3ebcec4a24c2c8, type: 3}
|
|
||||||
title: URP Empty Template
|
|
||||||
sections:
|
|
||||||
- heading: Welcome to the Universal Render Pipeline
|
|
||||||
text: This template includes the settings and assets you need to start creating with the Universal Render Pipeline.
|
|
||||||
linkText:
|
|
||||||
url:
|
|
||||||
- heading: URP Documentation
|
|
||||||
text:
|
|
||||||
linkText: Read more about URP
|
|
||||||
url: https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest
|
|
||||||
- heading: Forums
|
|
||||||
text:
|
|
||||||
linkText: Get answers and support
|
|
||||||
url: https://forum.unity.com/forums/universal-render-pipeline.383/
|
|
||||||
- heading: Report bugs
|
|
||||||
text:
|
|
||||||
linkText: Submit a report
|
|
||||||
url: https://unity3d.com/unity/qa/bug-reporting
|
|
||||||
loadedLayout: 1
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8105016687592461f977c054a80ce2f2
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 0
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8e716445493484943b18d1b34e247068
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: dab0572fcafe87d4b80a838fd874234b
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 09ef50fbbdfb41e4784b699dcffd4c2b
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
[System.Serializable]
|
|
||||||
public class SongMetadata
|
|
||||||
{
|
|
||||||
public string title;
|
|
||||||
public string artist;
|
|
||||||
public float bpm;
|
|
||||||
}
|
|
||||||
|
|
||||||
[System.Serializable]
|
|
||||||
public class BeatSageInfoDat
|
|
||||||
{
|
|
||||||
public string _songName;
|
|
||||||
public string _songAuthorName;
|
|
||||||
public float _beatsPerMinute;
|
|
||||||
}
|
|
||||||
|
|
||||||
[System.Serializable]
|
|
||||||
public class BeatSageRoot
|
|
||||||
{
|
|
||||||
public string _version;
|
|
||||||
public List<BeatSageNote> _notes;
|
|
||||||
}
|
|
||||||
|
|
||||||
[System.Serializable]
|
|
||||||
public class BeatSageNote
|
|
||||||
{
|
|
||||||
public float _time;
|
|
||||||
public int _lineIndex;
|
|
||||||
public int _lineLayer;
|
|
||||||
public int _type;
|
|
||||||
public int _cutDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class BeatSageConverter
|
|
||||||
{
|
|
||||||
private static readonly bool LogConversions = false;
|
|
||||||
|
|
||||||
public static List<NoteData> Convert(string rawJson, float bpm)
|
|
||||||
{
|
|
||||||
var result = new List<NoteData>();
|
|
||||||
|
|
||||||
var root = JsonUtility.FromJson<BeatSageRoot>(rawJson);
|
|
||||||
if (root?._notes == null)
|
|
||||||
{
|
|
||||||
Debug.LogWarning("[BeatSageConverter] Parse failed or no notes.");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var note in root._notes)
|
|
||||||
{
|
|
||||||
// Only process normal notes (0=red, 1=blue); skip bombs (3) etc.
|
|
||||||
if (note._type != 0 && note._type != 1) continue;
|
|
||||||
|
|
||||||
result.Add(new NoteData
|
|
||||||
{
|
|
||||||
time = (note._time * 60f) / bpm,
|
|
||||||
position = note._lineIndex,
|
|
||||||
lineLayer = note._lineLayer,
|
|
||||||
colorType = note._type,
|
|
||||||
cutDirection = note._cutDirection,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LogConversions)
|
|
||||||
Debug.Log($"[BeatSageConverter] Converted {result.Count} notes.");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToMapJson(List<NoteData> notes)
|
|
||||||
{
|
|
||||||
return JsonUtility.ToJson(new MapData { target = notes }, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SongMetadata ParseInfoDat(string json)
|
|
||||||
{
|
|
||||||
var info = JsonUtility.FromJson<BeatSageInfoDat>(json);
|
|
||||||
if (info == null) return null;
|
|
||||||
return new SongMetadata
|
|
||||||
{
|
|
||||||
title = (info._songName ?? "").Trim(),
|
|
||||||
artist = (info._songAuthorName ?? "").Trim(),
|
|
||||||
bpm = info._beatsPerMinute,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 1eca2ce555fd76e44ab91d0aea717fad
|
|
||||||
@@ -1,320 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Networking;
|
|
||||||
|
|
||||||
public class BeatSageUploader : MonoBehaviour
|
|
||||||
{
|
|
||||||
private const string BASE_URL = "https://beatsage.com";
|
|
||||||
private const string CREATE_EP = "/beatsaber_custom_level_create";
|
|
||||||
private const string HEARTBEAT_EP = "/beatsaber_custom_level_heartbeat/{0}";
|
|
||||||
private const string DOWNLOAD_EP = "/beatsaber_custom_level_download/{0}";
|
|
||||||
|
|
||||||
private const float POLL_INTERVAL = 5f;
|
|
||||||
private const float POLL_TIMEOUT = 300f;
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, string> DiffNames = new()
|
|
||||||
{
|
|
||||||
{ "normal", "Normal" },
|
|
||||||
{ "hard", "Hard" },
|
|
||||||
{ "expert", "Expert" },
|
|
||||||
{ "expertplus", "ExpertPlus" },
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, string> DatFileNames = new()
|
|
||||||
{
|
|
||||||
{ "normal", "Normal.dat" },
|
|
||||||
{ "hard", "Hard.dat" },
|
|
||||||
{ "expert", "Expert.dat" },
|
|
||||||
{ "expertplus", "ExpertPlus.dat" },
|
|
||||||
};
|
|
||||||
|
|
||||||
public string CurrentStatus { get; private set; } = "";
|
|
||||||
public SongMetadata LastMetadata { get; private set; }
|
|
||||||
|
|
||||||
// Upload from local file path
|
|
||||||
public IEnumerator Upload(
|
|
||||||
string audioPath,
|
|
||||||
List<string> difficulties,
|
|
||||||
float bpm,
|
|
||||||
Action<float> onProgress,
|
|
||||||
Action<Dictionary<string, List<NoteData>>> onComplete,
|
|
||||||
Action<string> onError)
|
|
||||||
{
|
|
||||||
SetStatus("[1/4] Uploading audio...");
|
|
||||||
string levelId = null;
|
|
||||||
|
|
||||||
yield return CreateLevel(audioPath, difficulties, id => levelId = id, onError);
|
|
||||||
if (levelId == null) yield break;
|
|
||||||
onProgress?.Invoke(0.15f);
|
|
||||||
|
|
||||||
yield return PollAndDownload(levelId, difficulties, bpm, onProgress, onComplete, onError);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload from direct audio URL (Beat Sage downloads it server-side)
|
|
||||||
public IEnumerator UploadFromUrl(
|
|
||||||
string audioUrl,
|
|
||||||
List<string> difficulties,
|
|
||||||
float bpm,
|
|
||||||
Action<float> onProgress,
|
|
||||||
Action<Dictionary<string, List<NoteData>>> onComplete,
|
|
||||||
Action<string> onError)
|
|
||||||
{
|
|
||||||
SetStatus("[1/4] Sending URL to Beat Sage...");
|
|
||||||
string levelId = null;
|
|
||||||
|
|
||||||
yield return CreateLevelFromUrl(audioUrl, difficulties, id => levelId = id, onError);
|
|
||||||
if (levelId == null) yield break;
|
|
||||||
onProgress?.Invoke(0.15f);
|
|
||||||
|
|
||||||
yield return PollAndDownload(levelId, difficulties, bpm, onProgress, onComplete, onError);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shared poll + download + convert phase
|
|
||||||
private IEnumerator PollAndDownload(
|
|
||||||
string levelId,
|
|
||||||
List<string> difficulties,
|
|
||||||
float bpm,
|
|
||||||
Action<float> onProgress,
|
|
||||||
Action<Dictionary<string, List<NoteData>>> onComplete,
|
|
||||||
Action<string> onError)
|
|
||||||
{
|
|
||||||
SetStatus("[2/4] Generating beatmap...");
|
|
||||||
bool ready = false;
|
|
||||||
float elapsed = 0f;
|
|
||||||
|
|
||||||
while (!ready && elapsed < POLL_TIMEOUT)
|
|
||||||
{
|
|
||||||
yield return new WaitForSeconds(POLL_INTERVAL);
|
|
||||||
elapsed += POLL_INTERVAL;
|
|
||||||
|
|
||||||
bool error = false;
|
|
||||||
yield return PollHeartbeat(levelId,
|
|
||||||
status =>
|
|
||||||
{
|
|
||||||
ready = string.Equals(status, "generated", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| string.Equals(status, "done", StringComparison.OrdinalIgnoreCase);
|
|
||||||
error = string.Equals(status, "error", StringComparison.OrdinalIgnoreCase);
|
|
||||||
},
|
|
||||||
onError);
|
|
||||||
|
|
||||||
if (error) { onError?.Invoke("Beat Sage generation failed (error status)"); yield break; }
|
|
||||||
|
|
||||||
onProgress?.Invoke(0.15f + Mathf.Clamp01(elapsed / POLL_TIMEOUT) * 0.6f);
|
|
||||||
SetStatus($"[2/4] Generating... {(int)elapsed}s elapsed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ready) { onError?.Invoke("Beat Sage timeout (>5 min)"); yield break; }
|
|
||||||
|
|
||||||
SetStatus("[3/4] Downloading result...");
|
|
||||||
byte[] zipBytes = null;
|
|
||||||
yield return DownloadZip(levelId, b => zipBytes = b, onError);
|
|
||||||
if (zipBytes == null) yield break;
|
|
||||||
onProgress?.Invoke(0.9f);
|
|
||||||
|
|
||||||
SetStatus("[3/4] Converting map data...");
|
|
||||||
Dictionary<string, List<NoteData>> maps = null;
|
|
||||||
try { maps = ExtractAndConvert(zipBytes, difficulties, bpm); }
|
|
||||||
catch (Exception e) { onError?.Invoke($"Conversion failed: {e.Message}"); yield break; }
|
|
||||||
|
|
||||||
onProgress?.Invoke(1f);
|
|
||||||
SetStatus("[3/4] Conversion complete.");
|
|
||||||
onComplete?.Invoke(maps);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator CreateLevelFromUrl(string audioUrl, List<string> difficulties,
|
|
||||||
Action<string> onSuccess, Action<string> onError)
|
|
||||||
{
|
|
||||||
var mappedDiffs = new List<string>();
|
|
||||||
foreach (string d in difficulties)
|
|
||||||
if (DiffNames.TryGetValue(d, out var n)) mappedDiffs.Add(n);
|
|
||||||
|
|
||||||
if (mappedDiffs.Count == 0)
|
|
||||||
{
|
|
||||||
onError?.Invoke("No supported difficulties selected.");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var form = new List<IMultipartFormSection>
|
|
||||||
{
|
|
||||||
new MultipartFormDataSection("audio_url", audioUrl),
|
|
||||||
new MultipartFormDataSection("audio_metadata_title", " "),
|
|
||||||
new MultipartFormDataSection("audio_metadata_artist", " "),
|
|
||||||
new MultipartFormDataSection("difficulties", string.Join(",", mappedDiffs)),
|
|
||||||
new MultipartFormDataSection("modes", "Standard"),
|
|
||||||
new MultipartFormDataSection("events", "DotBlocks,Obstacles,Bombs"),
|
|
||||||
new MultipartFormDataSection("environment", "DefaultEnvironment"),
|
|
||||||
new MultipartFormDataSection("system_tag", "v2"),
|
|
||||||
};
|
|
||||||
|
|
||||||
using var req = UnityWebRequest.Post(BASE_URL + CREATE_EP, form);
|
|
||||||
req.SetRequestHeader("Accept", "*/*");
|
|
||||||
req.SetRequestHeader("User-Agent", "VRBeatSaber/1.0");
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
|
|
||||||
if (req.result != UnityWebRequest.Result.Success)
|
|
||||||
{
|
|
||||||
onError?.Invoke($"Level create (URL) failed: {req.error}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
string levelId = ParseJsonString(req.downloadHandler.text, "id");
|
|
||||||
if (string.IsNullOrEmpty(levelId))
|
|
||||||
{
|
|
||||||
onError?.Invoke($"Failed to parse levelId. Response: {req.downloadHandler.text}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
onSuccess?.Invoke(levelId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator CreateLevel(string audioPath, List<string> difficulties,
|
|
||||||
Action<string> onSuccess, Action<string> onError)
|
|
||||||
{
|
|
||||||
byte[] audioBytes = File.ReadAllBytes(audioPath);
|
|
||||||
string fileName = Path.GetFileName(audioPath);
|
|
||||||
|
|
||||||
var mappedDiffs = new List<string>();
|
|
||||||
foreach (string d in difficulties)
|
|
||||||
if (DiffNames.TryGetValue(d, out var n)) mappedDiffs.Add(n);
|
|
||||||
|
|
||||||
if (mappedDiffs.Count == 0)
|
|
||||||
{
|
|
||||||
onError?.Invoke("No supported difficulties selected (use Normal/Hard/Expert/ExpertPlus).");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var form = new List<IMultipartFormSection>
|
|
||||||
{
|
|
||||||
new MultipartFormFileSection("audio_file", audioBytes, fileName, "audio/mpeg"),
|
|
||||||
new MultipartFormDataSection("audio_metadata_title", " "),
|
|
||||||
new MultipartFormDataSection("audio_metadata_artist", " "),
|
|
||||||
new MultipartFormDataSection("difficulties", string.Join(",", mappedDiffs)),
|
|
||||||
new MultipartFormDataSection("modes", "Standard"),
|
|
||||||
new MultipartFormDataSection("events", "DotBlocks,Obstacles,Bombs"),
|
|
||||||
new MultipartFormDataSection("environment", "DefaultEnvironment"),
|
|
||||||
new MultipartFormDataSection("system_tag", "v2"),
|
|
||||||
};
|
|
||||||
|
|
||||||
using var req = UnityWebRequest.Post(BASE_URL + CREATE_EP, form);
|
|
||||||
req.SetRequestHeader("Accept", "*/*");
|
|
||||||
req.SetRequestHeader("User-Agent", "VRBeatSaber/1.0");
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
|
|
||||||
if (req.result != UnityWebRequest.Result.Success)
|
|
||||||
{
|
|
||||||
onError?.Invoke($"Level create request failed: {req.error}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
string levelId = ParseJsonString(req.downloadHandler.text, "id");
|
|
||||||
if (string.IsNullOrEmpty(levelId))
|
|
||||||
{
|
|
||||||
onError?.Invoke($"Failed to parse levelId. Response: {req.downloadHandler.text}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
onSuccess?.Invoke(levelId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator PollHeartbeat(string levelId,
|
|
||||||
Action<string> onStatus, Action<string> onError)
|
|
||||||
{
|
|
||||||
using var req = UnityWebRequest.Get(BASE_URL + string.Format(HEARTBEAT_EP, levelId));
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
|
|
||||||
if (req.result != UnityWebRequest.Result.Success)
|
|
||||||
{
|
|
||||||
onError?.Invoke($"Heartbeat check failed: {req.error}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
onStatus?.Invoke(ParseJsonString(req.downloadHandler.text, "status") ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator DownloadZip(string levelId,
|
|
||||||
Action<byte[]> onSuccess, Action<string> onError)
|
|
||||||
{
|
|
||||||
string url = BASE_URL + string.Format(DOWNLOAD_EP, levelId);
|
|
||||||
|
|
||||||
for (int attempt = 1; attempt <= 3; attempt++)
|
|
||||||
{
|
|
||||||
using var req = UnityWebRequest.Get(url);
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
|
|
||||||
if (req.result == UnityWebRequest.Result.Success)
|
|
||||||
{
|
|
||||||
onSuccess?.Invoke(req.downloadHandler.data);
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 500 오류는 Beat Sage 처리 지연일 수 있으므로 재시도
|
|
||||||
if (req.responseCode == 500 && attempt < 3)
|
|
||||||
{
|
|
||||||
SetStatus($"[3/4] Server error, retrying ({attempt}/3)...");
|
|
||||||
yield return new WaitForSeconds(5f);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
onError?.Invoke($"ZIP download failed: {req.error} (HTTP {req.responseCode})");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<string, List<NoteData>> ExtractAndConvert(
|
|
||||||
byte[] zipBytes, List<string> difficulties, float fallbackBpm)
|
|
||||||
{
|
|
||||||
var result = new Dictionary<string, List<NoteData>>();
|
|
||||||
|
|
||||||
using var ms = new MemoryStream(zipBytes);
|
|
||||||
using var archive = new ZipArchive(ms, ZipArchiveMode.Read);
|
|
||||||
|
|
||||||
// Read info.dat first to get auto-detected BPM and metadata
|
|
||||||
float bpm = fallbackBpm;
|
|
||||||
foreach (var e in archive.Entries)
|
|
||||||
{
|
|
||||||
if (!string.Equals(e.Name, "info.dat", StringComparison.OrdinalIgnoreCase)) continue;
|
|
||||||
using var r = new StreamReader(e.Open(), Encoding.UTF8);
|
|
||||||
var meta = BeatSageConverter.ParseInfoDat(r.ReadToEnd());
|
|
||||||
if (meta != null)
|
|
||||||
{
|
|
||||||
LastMetadata = meta;
|
|
||||||
if (meta.bpm > 0) bpm = meta.bpm;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string diff in difficulties)
|
|
||||||
{
|
|
||||||
if (!DatFileNames.TryGetValue(diff, out string datName)) continue;
|
|
||||||
|
|
||||||
ZipArchiveEntry entry = null;
|
|
||||||
foreach (var e in archive.Entries)
|
|
||||||
if (string.Equals(e.Name, datName, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{ entry = e; break; }
|
|
||||||
|
|
||||||
if (entry == null) { Debug.LogWarning($"[BeatSageUploader] {datName} not found — skipped."); continue; }
|
|
||||||
|
|
||||||
using var reader = new StreamReader(entry.Open(), Encoding.UTF8);
|
|
||||||
result[diff] = BeatSageConverter.Convert(reader.ReadToEnd(), bpm);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ParseJsonString(string json, string key)
|
|
||||||
{
|
|
||||||
string search = $"\"{key}\":\"";
|
|
||||||
int start = json.IndexOf(search, StringComparison.Ordinal);
|
|
||||||
if (start < 0) return null;
|
|
||||||
start += search.Length;
|
|
||||||
int end = json.IndexOf('"', start);
|
|
||||||
return end > start ? json.Substring(start, end - start) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetStatus(string msg) => CurrentStatus = msg;
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 313c2722c0b3ff845a6d014c821e3660
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
using UnityEngine.InputSystem;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
// Editor/PC-only helper — auto-injects at runtime, no need to place in scene.
|
|
||||||
// On Quest builds this entire class is stripped.
|
|
||||||
//
|
|
||||||
// Features:
|
|
||||||
// 1. Replaces TrackedDeviceGraphicRaycaster → GraphicRaycaster (enables mouse clicks)
|
|
||||||
// 2. Keeps worldCamera up to date on all World Space canvases
|
|
||||||
// 3. ESC key navigates back
|
|
||||||
public class DesktopUIMode : MonoBehaviour
|
|
||||||
{
|
|
||||||
#if !UNITY_ANDROID || UNITY_EDITOR
|
|
||||||
|
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
|
||||||
private static void AutoCreate()
|
|
||||||
{
|
|
||||||
if (FindFirstObjectByType<DesktopUIMode>() != null) return;
|
|
||||||
new GameObject("[DesktopUIMode]").AddComponent<DesktopUIMode>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly System.Collections.Generic.Dictionary<string, string> BackMap =
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
{ "SongSelect", "Menu" },
|
|
||||||
{ "SongCreator", "Menu" },
|
|
||||||
{ "MapEditorScene", "SongCreator" },
|
|
||||||
{ "Game", "SongSelect" },
|
|
||||||
};
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
if (FindObjectsByType<DesktopUIMode>(FindObjectsSortMode.None).Length > 1)
|
|
||||||
{ Destroy(gameObject); return; }
|
|
||||||
|
|
||||||
DontDestroyOnLoad(gameObject);
|
|
||||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
||||||
PatchCanvases();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy() => SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
||||||
|
|
||||||
private void OnSceneLoaded(Scene s, LoadSceneMode m) => StartCoroutine(PatchAfterFrame());
|
|
||||||
|
|
||||||
private System.Collections.IEnumerator PatchAfterFrame()
|
|
||||||
{ yield return null; PatchCanvases(); }
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
RefreshCanvasCameras();
|
|
||||||
if (Keyboard.current?.escapeKey.wasPressedThisFrame == true) GoBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PatchCanvases()
|
|
||||||
{
|
|
||||||
foreach (var canvas in FindObjectsByType<Canvas>(FindObjectsSortMode.None))
|
|
||||||
{
|
|
||||||
if (canvas.renderMode != RenderMode.WorldSpace) continue;
|
|
||||||
|
|
||||||
var tracked = canvas.GetComponent("TrackedDeviceGraphicRaycaster");
|
|
||||||
if (tracked != null)
|
|
||||||
{
|
|
||||||
DestroyImmediate(tracked);
|
|
||||||
if (canvas.GetComponent<GraphicRaycaster>() == null)
|
|
||||||
canvas.gameObject.AddComponent<GraphicRaycaster>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveDuplicateAudioListeners();
|
|
||||||
RefreshCanvasCameras();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RemoveDuplicateAudioListeners()
|
|
||||||
{
|
|
||||||
var listeners = FindObjectsByType<AudioListener>(FindObjectsSortMode.None);
|
|
||||||
if (listeners.Length <= 1) return;
|
|
||||||
|
|
||||||
AudioListener keep = null;
|
|
||||||
foreach (var al in listeners)
|
|
||||||
if (al.gameObject.scene.name != "DontDestroyOnLoad") { keep = al; break; }
|
|
||||||
keep ??= listeners[0];
|
|
||||||
|
|
||||||
foreach (var al in listeners)
|
|
||||||
if (al != keep) DestroyImmediate(al);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RefreshCanvasCameras()
|
|
||||||
{
|
|
||||||
Camera cam = Camera.main;
|
|
||||||
if (cam == null)
|
|
||||||
foreach (var c in FindObjectsByType<Camera>(FindObjectsSortMode.None))
|
|
||||||
if (c.enabled && c.gameObject.scene.name != "DontDestroyOnLoad") { cam = c; break; }
|
|
||||||
cam ??= FindFirstObjectByType<Camera>();
|
|
||||||
if (cam == null) return;
|
|
||||||
|
|
||||||
foreach (var canvas in FindObjectsByType<Canvas>(FindObjectsSortMode.None))
|
|
||||||
if (canvas.renderMode == RenderMode.WorldSpace && canvas.worldCamera != cam)
|
|
||||||
canvas.worldCamera = cam;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GoBack()
|
|
||||||
{
|
|
||||||
if (BackMap.TryGetValue(SceneManager.GetActiveScene().name, out string target))
|
|
||||||
SceneManager.LoadScene(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: c0afc29d40bc9cc4486fc0c8078d2cb7
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.IO;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Networking;
|
|
||||||
|
|
||||||
public class DownloadManager : MonoBehaviour
|
|
||||||
{
|
|
||||||
[SerializeField] private string baseUrl = "http://whdwo798.synology.me/beatsaber";
|
|
||||||
|
|
||||||
private static string CacheRoot => Path.Combine(Application.persistentDataPath, "beatsaber");
|
|
||||||
private static string LegacyCacheRoot => Path.Combine(Application.temporaryCachePath, "beatsaber");
|
|
||||||
|
|
||||||
// ── Public API ───────────────────────────────────────────
|
|
||||||
|
|
||||||
public void FetchSongsList(Action<SongsList> onSuccess, Action<string> onError = null)
|
|
||||||
{
|
|
||||||
StartCoroutine(GetText($"{baseUrl}/songs.json", json =>
|
|
||||||
{
|
|
||||||
SongsList list = JsonUtility.FromJson<SongsList>(json);
|
|
||||||
if (list == null)
|
|
||||||
onError?.Invoke("songs.json 파싱 실패");
|
|
||||||
else
|
|
||||||
onSuccess?.Invoke(list);
|
|
||||||
}, onError));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DownloadSong(SongInfo song, string difficulty,
|
|
||||||
Action<float> onProgress, Action onComplete, Action<string> onError = null)
|
|
||||||
{
|
|
||||||
StartCoroutine(DownloadSongCoroutine(song, difficulty, onProgress, onComplete, onError));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteSong(string songId)
|
|
||||||
{
|
|
||||||
string dir = SongDir(songId);
|
|
||||||
if (Directory.Exists(dir))
|
|
||||||
{
|
|
||||||
Directory.Delete(dir, recursive: true);
|
|
||||||
Debug.Log($"[DownloadManager] 삭제: {songId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
string legacyDir = LegacySongDir(songId);
|
|
||||||
if (Directory.Exists(legacyDir))
|
|
||||||
Directory.Delete(legacyDir, recursive: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteDifficulty(SongInfo song, string difficulty)
|
|
||||||
{
|
|
||||||
TryMigrateLegacySong(song.id);
|
|
||||||
|
|
||||||
string path = MapPath(song, difficulty);
|
|
||||||
if (path != null && File.Exists(path))
|
|
||||||
File.Delete(path);
|
|
||||||
|
|
||||||
string songDir = SongDir(song.id);
|
|
||||||
if (Directory.Exists(songDir) && Directory.GetFileSystemEntries(songDir).Length == 0)
|
|
||||||
Directory.Delete(songDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSongDownloaded(string songId)
|
|
||||||
{
|
|
||||||
TryMigrateLegacySong(songId);
|
|
||||||
return File.Exists(AudioPath(songId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsDifficultyDownloaded(SongInfo song, string difficulty)
|
|
||||||
{
|
|
||||||
TryMigrateLegacySong(song.id);
|
|
||||||
|
|
||||||
string path = MapPath(song, difficulty);
|
|
||||||
return path != null && File.Exists(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string AudioPath(string songId)
|
|
||||||
=> Path.Combine(SongDir(songId), $"{songId}.mp3");
|
|
||||||
|
|
||||||
public string MapPath(SongInfo song, string difficulty)
|
|
||||||
{
|
|
||||||
DifficultyInfo info = song.difficulties.Get(difficulty);
|
|
||||||
if (info == null || string.IsNullOrEmpty(info.mapFile)) return null;
|
|
||||||
string fileName = Path.GetFileName(info.mapFile);
|
|
||||||
if (string.IsNullOrEmpty(fileName)) return null;
|
|
||||||
return Path.Combine(SongDir(song.id), fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 내부 구현 ─────────────────────────────────────────────
|
|
||||||
|
|
||||||
private IEnumerator DownloadSongCoroutine(SongInfo song, string difficulty,
|
|
||||||
Action<float> onProgress, Action onComplete, Action<string> onError)
|
|
||||||
{
|
|
||||||
TryMigrateLegacySong(song.id);
|
|
||||||
|
|
||||||
string songDir = Path.GetFullPath(SongDir(song.id));
|
|
||||||
Directory.CreateDirectory(songDir);
|
|
||||||
|
|
||||||
// 1단계: 오디오 (70%)
|
|
||||||
string audioPath = Path.Combine(songDir, $"{song.id}.mp3");
|
|
||||||
if (!File.Exists(audioPath))
|
|
||||||
{
|
|
||||||
bool failed = false;
|
|
||||||
yield return DownloadFile(
|
|
||||||
$"{baseUrl}/{song.audioFile}", audioPath,
|
|
||||||
p => onProgress?.Invoke(p * 0.7f),
|
|
||||||
err => { onError?.Invoke(err); failed = true; });
|
|
||||||
if (failed) yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2단계: 맵 파일 (30%)
|
|
||||||
DifficultyInfo diffInfo = song.difficulties.Get(difficulty);
|
|
||||||
if (diffInfo == null)
|
|
||||||
{
|
|
||||||
onError?.Invoke($"난이도 '{difficulty}' 없음");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(diffInfo.mapFile))
|
|
||||||
{
|
|
||||||
onError?.Invoke($"'{difficulty}' 맵 파일 정보 없음 — Creator에서 곡을 다시 생성해주세요");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
string mapPath = MapPath(song, difficulty);
|
|
||||||
if (mapPath != null) mapPath = Path.GetFullPath(mapPath);
|
|
||||||
if (mapPath == null)
|
|
||||||
{
|
|
||||||
onError?.Invoke($"'{difficulty}' 맵 경로 계산 실패");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
if (!File.Exists(mapPath))
|
|
||||||
{
|
|
||||||
bool failed = false;
|
|
||||||
yield return DownloadFile(
|
|
||||||
$"{baseUrl}/{diffInfo.mapFile}", mapPath,
|
|
||||||
p => onProgress?.Invoke(0.7f + p * 0.3f),
|
|
||||||
err => { onError?.Invoke(err); failed = true; });
|
|
||||||
if (failed) yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
onProgress?.Invoke(1f);
|
|
||||||
onComplete?.Invoke();
|
|
||||||
Debug.Log($"[DownloadManager] 완료: {song.title} ({difficulty})");
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator DownloadFile(string url, string savePath,
|
|
||||||
Action<float> onProgress, Action<string> onError)
|
|
||||||
{
|
|
||||||
using var req = UnityWebRequest.Get(url);
|
|
||||||
req.downloadHandler = new DownloadHandlerFile(savePath);
|
|
||||||
req.SendWebRequest();
|
|
||||||
|
|
||||||
while (!req.isDone)
|
|
||||||
{
|
|
||||||
onProgress?.Invoke(req.downloadProgress);
|
|
||||||
yield return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.result != UnityWebRequest.Result.Success)
|
|
||||||
{
|
|
||||||
if (File.Exists(savePath)) File.Delete(savePath);
|
|
||||||
onError?.Invoke($"다운로드 실패: {url} — {req.error}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator GetText(string url, Action<string> onSuccess, Action<string> onError)
|
|
||||||
{
|
|
||||||
using var req = UnityWebRequest.Get(url);
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
|
|
||||||
if (req.result != UnityWebRequest.Result.Success)
|
|
||||||
onError?.Invoke($"요청 실패: {url} — {req.error}");
|
|
||||||
else
|
|
||||||
onSuccess?.Invoke(req.downloadHandler.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string SongDir(string songId)
|
|
||||||
=> Path.Combine(CacheRoot, songId);
|
|
||||||
|
|
||||||
private static string LegacySongDir(string songId)
|
|
||||||
=> Path.Combine(LegacyCacheRoot, songId);
|
|
||||||
|
|
||||||
private static void TryMigrateLegacySong(string songId)
|
|
||||||
{
|
|
||||||
string sourceDir = LegacySongDir(songId);
|
|
||||||
string targetDir = SongDir(songId);
|
|
||||||
|
|
||||||
if (Directory.Exists(targetDir) || !Directory.Exists(sourceDir))
|
|
||||||
return;
|
|
||||||
|
|
||||||
CopyDirectory(sourceDir, targetDir);
|
|
||||||
Directory.Delete(sourceDir, recursive: true);
|
|
||||||
Debug.Log($"[DownloadManager] 기존 캐시를 영구 저장소로 이동: {songId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CopyDirectory(string sourceDir, string targetDir)
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(targetDir);
|
|
||||||
|
|
||||||
foreach (string file in Directory.GetFiles(sourceDir))
|
|
||||||
{
|
|
||||||
string targetFile = Path.Combine(targetDir, Path.GetFileName(file));
|
|
||||||
File.Copy(file, targetFile, overwrite: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string dir in Directory.GetDirectories(sourceDir))
|
|
||||||
{
|
|
||||||
string targetSubDir = Path.Combine(targetDir, Path.GetFileName(dir));
|
|
||||||
CopyDirectory(dir, targetSubDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: a8efd2469f7355140ae71425ecc638e0
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Video;
|
|
||||||
|
|
||||||
public class Game360VideoBackground : MonoBehaviour
|
|
||||||
{
|
|
||||||
[SerializeField] private VideoClip videoClip;
|
|
||||||
[SerializeField] private int renderTextureSize = 2048;
|
|
||||||
[SerializeField] private bool muteVideoAudio = true;
|
|
||||||
[SerializeField, Range(0f, 360f)] private float skyboxRotationDegrees = 0f;
|
|
||||||
[SerializeField, Range(0f, 8f)] private float skyboxExposure = 1f;
|
|
||||||
|
|
||||||
private GameObject videoPlayerObject;
|
|
||||||
private Material skyboxMaterial;
|
|
||||||
private Material previousSkybox;
|
|
||||||
private RenderTexture renderTexture;
|
|
||||||
private VideoPlayer videoPlayer;
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
if (videoClip == null)
|
|
||||||
{
|
|
||||||
Debug.LogWarning("[Game360VideoBackground] videoClip is not assigned.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateSkyboxMaterial();
|
|
||||||
CreateVideoPlayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
if (videoPlayer != null)
|
|
||||||
{
|
|
||||||
videoPlayer.prepareCompleted -= OnVideoPrepared;
|
|
||||||
videoPlayer.errorReceived -= OnVideoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renderTexture != null)
|
|
||||||
{
|
|
||||||
renderTexture.Release();
|
|
||||||
Destroy(renderTexture);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderSettings.skybox = previousSkybox;
|
|
||||||
DynamicGI.UpdateEnvironment();
|
|
||||||
|
|
||||||
if (skyboxMaterial != null)
|
|
||||||
Destroy(skyboxMaterial);
|
|
||||||
|
|
||||||
if (videoPlayerObject != null)
|
|
||||||
Destroy(videoPlayerObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateSkyboxMaterial()
|
|
||||||
{
|
|
||||||
renderTexture = new RenderTexture(renderTextureSize, renderTextureSize / 2, 0, RenderTextureFormat.ARGB32)
|
|
||||||
{
|
|
||||||
name = "Game360VideoRenderTexture",
|
|
||||||
wrapMode = TextureWrapMode.Clamp,
|
|
||||||
filterMode = FilterMode.Bilinear,
|
|
||||||
};
|
|
||||||
renderTexture.Create();
|
|
||||||
|
|
||||||
previousSkybox = RenderSettings.skybox;
|
|
||||||
skyboxMaterial = new Material(ResolveSkyboxShader())
|
|
||||||
{
|
|
||||||
name = "Game360VideoMaterial",
|
|
||||||
};
|
|
||||||
skyboxMaterial.SetTexture("_MainTex", renderTexture);
|
|
||||||
skyboxMaterial.SetFloat("_ImageType", 0f);
|
|
||||||
skyboxMaterial.SetFloat("_Mapping", 0f);
|
|
||||||
skyboxMaterial.SetFloat("_Layout", 0f);
|
|
||||||
ApplySkyboxSettings();
|
|
||||||
|
|
||||||
RenderSettings.skybox = skyboxMaterial;
|
|
||||||
DynamicGI.UpdateEnvironment();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateVideoPlayer()
|
|
||||||
{
|
|
||||||
videoPlayerObject = new GameObject("[360 Video Skybox Player]");
|
|
||||||
videoPlayerObject.transform.SetParent(transform, false);
|
|
||||||
|
|
||||||
videoPlayer = videoPlayerObject.AddComponent<VideoPlayer>();
|
|
||||||
videoPlayer.playOnAwake = false;
|
|
||||||
videoPlayer.isLooping = true;
|
|
||||||
videoPlayer.waitForFirstFrame = true;
|
|
||||||
videoPlayer.renderMode = VideoRenderMode.RenderTexture;
|
|
||||||
videoPlayer.targetTexture = renderTexture;
|
|
||||||
videoPlayer.clip = videoClip;
|
|
||||||
videoPlayer.audioOutputMode = muteVideoAudio
|
|
||||||
? VideoAudioOutputMode.None
|
|
||||||
: VideoAudioOutputMode.Direct;
|
|
||||||
videoPlayer.prepareCompleted += OnVideoPrepared;
|
|
||||||
videoPlayer.errorReceived += OnVideoError;
|
|
||||||
videoPlayer.Prepare();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Shader ResolveSkyboxShader()
|
|
||||||
{
|
|
||||||
return Shader.Find("Skybox/Panoramic")
|
|
||||||
?? Shader.Find("Skybox/6 Sided")
|
|
||||||
?? Shader.Find("Standard");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnVideoPrepared(VideoPlayer source)
|
|
||||||
{
|
|
||||||
source.Play();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnValidate()
|
|
||||||
{
|
|
||||||
ApplySkyboxSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplySkyboxSettings()
|
|
||||||
{
|
|
||||||
if (skyboxMaterial == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
skyboxMaterial.SetFloat("_Exposure", skyboxExposure);
|
|
||||||
skyboxMaterial.SetFloat("_Rotation", skyboxRotationDegrees);
|
|
||||||
DynamicGI.UpdateEnvironment();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnVideoError(VideoPlayer source, string message)
|
|
||||||
{
|
|
||||||
Debug.LogWarning($"[Game360VideoBackground] VideoPlayer error: {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3e381cd99de84f67b9f83c19a032dc24
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// Static container — passes selected song/difficulty between scenes
|
|
||||||
public static class GameSession
|
|
||||||
{
|
|
||||||
public static SongInfo SelectedSong;
|
|
||||||
public static string SelectedDifficulty;
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 4794ac1142dcc254fa53e2c8d7c1512a
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public static class GlobalSyncSettings
|
|
||||||
{
|
|
||||||
private const string AudioOffsetMsKey = "VRBeats.GlobalAudioOffsetMs";
|
|
||||||
|
|
||||||
public static float AudioOffsetMs
|
|
||||||
{
|
|
||||||
get => PlayerPrefs.GetFloat(AudioOffsetMsKey, 0.0f);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
PlayerPrefs.SetFloat(AudioOffsetMsKey, Mathf.Clamp(value, -300.0f, 300.0f));
|
|
||||||
PlayerPrefs.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float AudioOffsetSeconds => AudioOffsetMs / 1000.0f;
|
|
||||||
|
|
||||||
public static void Reset()
|
|
||||||
{
|
|
||||||
PlayerPrefs.DeleteKey(AudioOffsetMsKey);
|
|
||||||
PlayerPrefs.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: a2e8c518ec2f4a03a6d820774b475ce0
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using TMPro;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
[RequireComponent(typeof(TMP_Text))]
|
|
||||||
public class MarqueeText : MonoBehaviour
|
|
||||||
{
|
|
||||||
public float speed = 14f;
|
|
||||||
public float pauseStart = 1.8f;
|
|
||||||
public float pauseEnd = 0.9f;
|
|
||||||
|
|
||||||
private TMP_Text _label;
|
|
||||||
private RectTransform _rect;
|
|
||||||
private Coroutine _scrollRoutine;
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
_label = GetComponent<TMP_Text>();
|
|
||||||
_rect = GetComponent<RectTransform>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator Start()
|
|
||||||
{
|
|
||||||
yield return null;
|
|
||||||
Refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
StopScrolling();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Refresh()
|
|
||||||
{
|
|
||||||
if (!isActiveAndEnabled || _label == null || _rect == null || transform.parent == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
StopScrolling();
|
|
||||||
SetX(0f);
|
|
||||||
|
|
||||||
_label.ForceMeshUpdate();
|
|
||||||
float textW = _label.preferredWidth;
|
|
||||||
float containerW = ((RectTransform)transform.parent).rect.width;
|
|
||||||
float dist = textW - containerW;
|
|
||||||
|
|
||||||
if (dist > 1f)
|
|
||||||
_scrollRoutine = StartCoroutine(ScrollLoop(dist));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator ScrollLoop(float dist)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
SetX(0f);
|
|
||||||
yield return new WaitForSeconds(pauseStart);
|
|
||||||
|
|
||||||
float x = 0f;
|
|
||||||
while (x > -dist)
|
|
||||||
{
|
|
||||||
x = Mathf.MoveTowards(x, -dist, speed * Time.deltaTime);
|
|
||||||
SetX(x);
|
|
||||||
yield return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return new WaitForSeconds(pauseEnd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetX(float x) =>
|
|
||||||
_rect.anchoredPosition = new Vector2(x, _rect.anchoredPosition.y);
|
|
||||||
|
|
||||||
private void StopScrolling()
|
|
||||||
{
|
|
||||||
if (_scrollRoutine == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
StopCoroutine(_scrollRoutine);
|
|
||||||
_scrollRoutine = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: aec41476b82385047a8cec63612a6698
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using TMPro;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
public class MenuSyncButtonInjector : MonoBehaviour
|
|
||||||
{
|
|
||||||
private const string MenuSceneName = "Menu";
|
|
||||||
|
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
|
||||||
private static void AutoCreate()
|
|
||||||
{
|
|
||||||
if (FindFirstObjectByType<MenuSyncButtonInjector>() != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
GameObject go = new GameObject("[MenuSyncButtonInjector]");
|
|
||||||
DontDestroyOnLoad(go);
|
|
||||||
go.AddComponent<MenuSyncButtonInjector>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
||||||
StartCoroutine(InjectAfterFrame());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
||||||
{
|
|
||||||
StartCoroutine(InjectAfterFrame());
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator InjectAfterFrame()
|
|
||||||
{
|
|
||||||
yield return null;
|
|
||||||
|
|
||||||
if (SceneManager.GetActiveScene().name != MenuSceneName)
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
if (GameObject.Find("SyncButton") != null)
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
Button sourceButton = FindButtonByText("CreateSong");
|
|
||||||
if (sourceButton == null)
|
|
||||||
sourceButton = FindButtonByText("음악만들기");
|
|
||||||
if (sourceButton == null)
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
Button syncButton = Instantiate(sourceButton, sourceButton.transform.parent);
|
|
||||||
syncButton.gameObject.name = "SyncButton";
|
|
||||||
foreach (VRBeats.LoadSceneButton loader in syncButton.GetComponents<VRBeats.LoadSceneButton>())
|
|
||||||
{
|
|
||||||
loader.enabled = false;
|
|
||||||
Destroy(loader);
|
|
||||||
}
|
|
||||||
|
|
||||||
syncButton.onClick.RemoveAllListeners();
|
|
||||||
syncButton.onClick.AddListener(SyncCalibrationOverlay.Open);
|
|
||||||
|
|
||||||
RectTransform sourceRect = sourceButton.GetComponent<RectTransform>();
|
|
||||||
RectTransform syncRect = syncButton.GetComponent<RectTransform>();
|
|
||||||
syncRect.anchoredPosition = sourceRect.anchoredPosition + new Vector2(0.0f, -22.0f);
|
|
||||||
|
|
||||||
TextMeshProUGUI tmp = syncButton.GetComponentInChildren<TextMeshProUGUI>(true);
|
|
||||||
if (tmp != null)
|
|
||||||
tmp.text = "SYNC";
|
|
||||||
|
|
||||||
Text text = syncButton.GetComponentInChildren<Text>(true);
|
|
||||||
if (text != null)
|
|
||||||
text.text = "SYNC";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Button FindButtonByText(string text)
|
|
||||||
{
|
|
||||||
foreach (Button button in FindObjectsByType<Button>(FindObjectsSortMode.None))
|
|
||||||
{
|
|
||||||
TextMeshProUGUI tmp = button.GetComponentInChildren<TextMeshProUGUI>(true);
|
|
||||||
if (tmp != null && tmp.text.Trim() == text)
|
|
||||||
return button;
|
|
||||||
|
|
||||||
Text legacyText = button.GetComponentInChildren<Text>(true);
|
|
||||||
if (legacyText != null && legacyText.text.Trim() == text)
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: efc5a20a7b4749bfb60d95ac0f0b2180
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Networking;
|
|
||||||
|
|
||||||
public class NasPublisher : MonoBehaviour
|
|
||||||
{
|
|
||||||
[Header("NAS Connection")]
|
|
||||||
[SerializeField] private string nasBaseUrl = "http://192.168.55.3:5000";
|
|
||||||
[SerializeField] private string nasAccount = "admin";
|
|
||||||
[SerializeField] private string nasRootPath = "/web/beatsaber";
|
|
||||||
|
|
||||||
[Header("Static Server URL (for reading songs.json)")]
|
|
||||||
[SerializeField] private string staticBaseUrl = "http://whdwo798.synology.me/beatsaber";
|
|
||||||
|
|
||||||
private string _sid = "";
|
|
||||||
private string _synoToken = "";
|
|
||||||
private string _password = "";
|
|
||||||
|
|
||||||
private void Awake() => LoadConfig();
|
|
||||||
|
|
||||||
private void LoadConfig()
|
|
||||||
{
|
|
||||||
string path = Path.Combine(Application.streamingAssetsPath, "nas_config.json");
|
|
||||||
if (!File.Exists(path))
|
|
||||||
{
|
|
||||||
NormalizeSettings();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var cfg = JsonUtility.FromJson<NasConfig>(File.ReadAllText(path));
|
|
||||||
if (cfg == null) return;
|
|
||||||
_password = cfg.password ?? "";
|
|
||||||
if (!string.IsNullOrWhiteSpace(cfg.host)) nasBaseUrl = cfg.host.Trim();
|
|
||||||
if (!string.IsNullOrWhiteSpace(cfg.account)) nasAccount = cfg.account.Trim();
|
|
||||||
if (!string.IsNullOrWhiteSpace(cfg.rootPath)) nasRootPath = cfg.rootPath.Trim();
|
|
||||||
if (!string.IsNullOrWhiteSpace(cfg.staticUrl)) staticBaseUrl = cfg.staticUrl.Trim();
|
|
||||||
|
|
||||||
NormalizeSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable] private class NasConfig
|
|
||||||
{
|
|
||||||
public string host = "";
|
|
||||||
public string account = "";
|
|
||||||
public string rootPath = "";
|
|
||||||
public string staticUrl = "";
|
|
||||||
public string password = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator Publish(
|
|
||||||
SongInfo song,
|
|
||||||
string audioPath,
|
|
||||||
Dictionary<string, List<NoteData>> maps,
|
|
||||||
Action<float> onProgress,
|
|
||||||
Action onComplete,
|
|
||||||
Action<string> onError)
|
|
||||||
{
|
|
||||||
NormalizeSettings();
|
|
||||||
|
|
||||||
bool failed = false;
|
|
||||||
void OnErr(string e) { onError?.Invoke(e); failed = true; }
|
|
||||||
|
|
||||||
yield return Login(OnErr);
|
|
||||||
if (string.IsNullOrEmpty(_sid)) yield break;
|
|
||||||
onProgress?.Invoke(0.1f);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(audioPath))
|
|
||||||
{
|
|
||||||
yield return UploadFile(audioPath, $"{nasRootPath}/music", $"{song.id}.mp3", OnErr);
|
|
||||||
if (failed) { yield return Logout(); yield break; }
|
|
||||||
}
|
|
||||||
onProgress?.Invoke(0.4f);
|
|
||||||
|
|
||||||
int total = maps.Count, done = 0;
|
|
||||||
foreach (var kv in maps)
|
|
||||||
{
|
|
||||||
string fileName = $"Map_{song.id}_{kv.Key}.json";
|
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(BeatSageConverter.ToMapJson(kv.Value));
|
|
||||||
AssignMapFile(song, kv.Key, fileName);
|
|
||||||
|
|
||||||
yield return UploadBytes(bytes, fileName, $"{nasRootPath}/maps", OnErr);
|
|
||||||
if (failed) { yield return Logout(); yield break; }
|
|
||||||
|
|
||||||
done++;
|
|
||||||
onProgress?.Invoke(0.4f + (float)done / total * 0.3f);
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return PatchSongsJson(song, OnErr);
|
|
||||||
if (failed) { yield return Logout(); yield break; }
|
|
||||||
onProgress?.Invoke(0.95f);
|
|
||||||
|
|
||||||
yield return Logout();
|
|
||||||
onProgress?.Invoke(1f);
|
|
||||||
onComplete?.Invoke();
|
|
||||||
Debug.Log($"[NasPublisher] Upload complete: '{song.title}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator Login(Action<string> onError)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(_password))
|
|
||||||
{
|
|
||||||
onError?.Invoke("NAS password missing. Create Assets/StreamingAssets/nas_config.json with host, account, rootPath, staticUrl, and password.");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
string url = $"{nasBaseUrl}/webapi/auth.cgi" +
|
|
||||||
$"?api=SYNO.API.Auth&version=6&method=login" +
|
|
||||||
$"&account={UnityWebRequest.EscapeURL(nasAccount)}" +
|
|
||||||
$"&passwd={UnityWebRequest.EscapeURL(_password)}" +
|
|
||||||
$"&session=FileStation&format=sid&enable_syno_token=yes";
|
|
||||||
|
|
||||||
UnityWebRequest req;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
req = UnityWebRequest.Get(url);
|
|
||||||
}
|
|
||||||
catch (UriFormatException e)
|
|
||||||
{
|
|
||||||
onError?.Invoke($"DSM login URL invalid: '{nasBaseUrl}' — {e.Message}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
string resp;
|
|
||||||
using (req)
|
|
||||||
{
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
|
|
||||||
if (req.result != UnityWebRequest.Result.Success)
|
|
||||||
{
|
|
||||||
onError?.Invoke($"DSM login failed: {req.error}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
resp = req.downloadHandler.text;
|
|
||||||
_sid = ParseJsonString(resp, "sid");
|
|
||||||
_synoToken = ParseJsonString(resp, "synotoken");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_sid))
|
|
||||||
onError?.Invoke($"DSM sid parse failed. Check NAS account/password/permissions. Response: {Shorten(resp)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator Logout()
|
|
||||||
{
|
|
||||||
NormalizeSettings();
|
|
||||||
|
|
||||||
string url = $"{nasBaseUrl}/webapi/auth.cgi" +
|
|
||||||
$"?api=SYNO.API.Auth&version=1&method=logout&session=FileStation&_sid={_sid}";
|
|
||||||
using var req = UnityWebRequest.Get(url);
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
_sid = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator UploadFile(string localPath, string nasFolder,
|
|
||||||
string fileName, Action<string> onError)
|
|
||||||
{
|
|
||||||
yield return UploadBytes(File.ReadAllBytes(localPath), fileName, nasFolder, onError);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator UploadBytes(byte[] bytes, string fileName,
|
|
||||||
string nasFolder, Action<string> onError)
|
|
||||||
{
|
|
||||||
NormalizeSettings();
|
|
||||||
|
|
||||||
string uploadUrl = $"{nasBaseUrl}/webapi/entry.cgi" +
|
|
||||||
$"?api=SYNO.FileStation.Upload&version=2&method=upload" +
|
|
||||||
$"&_sid={UnityWebRequest.EscapeURL(_sid)}";
|
|
||||||
|
|
||||||
string boundary = Guid.NewGuid().ToString("N");
|
|
||||||
const string CRLF = "\r\n";
|
|
||||||
|
|
||||||
using var body = new MemoryStream();
|
|
||||||
void WriteText(string s) { var b = Encoding.UTF8.GetBytes(s); body.Write(b, 0, b.Length); }
|
|
||||||
void WriteField(string name, string value)
|
|
||||||
{
|
|
||||||
WriteText($"--{boundary}{CRLF}");
|
|
||||||
WriteText($"Content-Disposition: form-data; name=\"{name}\"{CRLF}{CRLF}");
|
|
||||||
WriteText(value + CRLF);
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteField("path", nasFolder);
|
|
||||||
WriteField("create_parents", "true");
|
|
||||||
WriteField("overwrite", "true");
|
|
||||||
WriteText($"--{boundary}{CRLF}");
|
|
||||||
WriteText($"Content-Disposition: form-data; name=\"file\"; filename=\"{fileName}\"{CRLF}");
|
|
||||||
WriteText($"Content-Type: application/octet-stream{CRLF}{CRLF}");
|
|
||||||
body.Write(bytes, 0, bytes.Length);
|
|
||||||
WriteText(CRLF + $"--{boundary}--{CRLF}");
|
|
||||||
|
|
||||||
using var req = new UnityWebRequest(uploadUrl, "POST");
|
|
||||||
req.uploadHandler = new UploadHandlerRaw(body.ToArray());
|
|
||||||
req.downloadHandler = new DownloadHandlerBuffer();
|
|
||||||
req.SetRequestHeader("Content-Type", $"multipart/form-data; boundary={boundary}");
|
|
||||||
if (!string.IsNullOrEmpty(_synoToken))
|
|
||||||
req.SetRequestHeader("X-SYNO-TOKEN", _synoToken);
|
|
||||||
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
|
|
||||||
if (req.result != UnityWebRequest.Result.Success)
|
|
||||||
{
|
|
||||||
onError?.Invoke($"Upload failed ({fileName}): {req.error}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.downloadHandler.text.Contains("\"success\":false"))
|
|
||||||
onError?.Invoke($"Upload rejected ({fileName}): {req.downloadHandler.text}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator PatchSongsJson(SongInfo newSong, Action<string> onError)
|
|
||||||
{
|
|
||||||
NormalizeSettings();
|
|
||||||
|
|
||||||
SongsList list = null;
|
|
||||||
|
|
||||||
using (var req = UnityWebRequest.Get($"{staticBaseUrl}/songs.json"))
|
|
||||||
{
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
if (req.result == UnityWebRequest.Result.Success)
|
|
||||||
list = JsonUtility.FromJson<SongsList>(req.downloadHandler.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
list ??= new SongsList { version = "1.0", songs = new List<SongInfo>() };
|
|
||||||
|
|
||||||
int idx = list.songs.FindIndex(s => s.id == newSong.id);
|
|
||||||
if (idx >= 0) list.songs[idx] = newSong;
|
|
||||||
else list.songs.Add(newSong);
|
|
||||||
|
|
||||||
yield return UploadBytes(
|
|
||||||
Encoding.UTF8.GetBytes(JsonUtility.ToJson(list, true)),
|
|
||||||
"songs.json", nasRootPath, onError);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ParseJsonString(string json, string key)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(json) || string.IsNullOrEmpty(key))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
Match match = Regex.Match(
|
|
||||||
json,
|
|
||||||
$"\"{Regex.Escape(key)}\"\\s*:\\s*\"(?<value>(?:\\\\.|[^\"])*)\"");
|
|
||||||
|
|
||||||
return match.Success
|
|
||||||
? Regex.Unescape(match.Groups["value"].Value)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AssignMapFile(SongInfo song, string diff, string fileName)
|
|
||||||
{
|
|
||||||
var info = song.difficulties.Get(diff);
|
|
||||||
if (info != null) info.mapFile = $"maps/{fileName}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnValidate()
|
|
||||||
{
|
|
||||||
NormalizeSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NormalizeSettings()
|
|
||||||
{
|
|
||||||
nasBaseUrl = NormalizeBaseUrl(nasBaseUrl);
|
|
||||||
staticBaseUrl = NormalizeBaseUrl(staticBaseUrl);
|
|
||||||
nasAccount = nasAccount?.Trim() ?? "";
|
|
||||||
nasRootPath = NormalizeRootPath(nasRootPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeBaseUrl(string value)
|
|
||||||
{
|
|
||||||
return (value ?? "").Trim().TrimEnd('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeRootPath(string value)
|
|
||||||
{
|
|
||||||
value = (value ?? "").Trim().Replace('\\', '/');
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
return "/";
|
|
||||||
return value.StartsWith("/") ? value.TrimEnd('/') : "/" + value.TrimEnd('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string Shorten(string value, int maxLength = 240)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value) || value.Length <= maxLength)
|
|
||||||
return value ?? "";
|
|
||||||
|
|
||||||
return value.Substring(0, maxLength) + "...";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 2379e0d70040c994089638264e6e9934
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class NoteData
|
|
||||||
{
|
|
||||||
public float time;
|
|
||||||
public int position; // column 0-3
|
|
||||||
public int lineLayer; // row 0-2
|
|
||||||
public int colorType; // 0=red, 1=blue
|
|
||||||
public int cutDirection; // 0-8 (see Beat Saber spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class MapData
|
|
||||||
{
|
|
||||||
public List<NoteData> target;
|
|
||||||
public ForcedResultData forcedResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class ForcedResultData
|
|
||||||
{
|
|
||||||
public bool enabled;
|
|
||||||
public int totalNotes;
|
|
||||||
public int perfect;
|
|
||||||
public int great;
|
|
||||||
public int good;
|
|
||||||
public int miss;
|
|
||||||
public int maxCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class SongsList
|
|
||||||
{
|
|
||||||
public string version;
|
|
||||||
public List<SongInfo> songs;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class SongInfo
|
|
||||||
{
|
|
||||||
public string id;
|
|
||||||
public string title;
|
|
||||||
public string artist;
|
|
||||||
public float bpm;
|
|
||||||
public int duration;
|
|
||||||
public string audioFile;
|
|
||||||
public long audioSize;
|
|
||||||
public string coverImage;
|
|
||||||
public DifficultyMap difficulties;
|
|
||||||
public string addedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class DifficultyMap
|
|
||||||
{
|
|
||||||
public DifficultyInfo normal;
|
|
||||||
public DifficultyInfo hard;
|
|
||||||
public DifficultyInfo expert;
|
|
||||||
public DifficultyInfo expertplus;
|
|
||||||
|
|
||||||
public DifficultyInfo Get(string key) => key switch
|
|
||||||
{
|
|
||||||
"normal" => normal,
|
|
||||||
"hard" => hard,
|
|
||||||
"expert" => expert,
|
|
||||||
"expertplus" => expertplus,
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class DifficultyInfo
|
|
||||||
{
|
|
||||||
public string mapFile;
|
|
||||||
public long mapSize;
|
|
||||||
public int noteCount;
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 16355c5f50bd642439e8ce4f61be6b92
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using TMPro;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Networking;
|
|
||||||
using VRBeats;
|
|
||||||
using VRBeats.ScriptableEvents;
|
|
||||||
|
|
||||||
public class SongController : MonoBehaviour
|
|
||||||
{
|
|
||||||
[SerializeField] private Spawneable cubePrefab;
|
|
||||||
[SerializeField] private GameEvent onLevelComplete;
|
|
||||||
[SerializeField] private TMP_Text countdownText;
|
|
||||||
|
|
||||||
private const float LaneSpacing = 0.42f;
|
|
||||||
private const float LayerSpacing = 0.34f;
|
|
||||||
private const float HorizontalCenter = 1.5f;
|
|
||||||
private const float VerticalCenter = 1f;
|
|
||||||
private const float VerticalOffset = 0.22f;
|
|
||||||
|
|
||||||
private AudioManager _audio;
|
|
||||||
private ScoreManager _scoreManager;
|
|
||||||
private float _clipLength;
|
|
||||||
|
|
||||||
private static string CacheRoot =>
|
|
||||||
Path.Combine(Application.persistentDataPath, "beatsaber");
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
_audio = FindFirstObjectByType<AudioManager>();
|
|
||||||
_scoreManager = FindFirstObjectByType<ScoreManager>();
|
|
||||||
StartCoroutine(LoadAndPlay());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
if (_audio != null && _scoreManager != null && _clipLength > 0.0f)
|
|
||||||
_scoreManager.SetSongProgress(_audio.CurrentTime, _clipLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator LoadAndPlay()
|
|
||||||
{
|
|
||||||
SongInfo song = GameSession.SelectedSong;
|
|
||||||
string diff = GameSession.SelectedDifficulty;
|
|
||||||
|
|
||||||
if (song == null || string.IsNullOrEmpty(diff))
|
|
||||||
{
|
|
||||||
Debug.LogError("[SongController] No song/difficulty selected");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load audio clip from local cache
|
|
||||||
string audioPath = Path.Combine(CacheRoot, song.id, song.id + ".mp3");
|
|
||||||
AudioClip clip;
|
|
||||||
using (var req = UnityWebRequestMultimedia.GetAudioClip("file://" + audioPath, AudioType.MPEG))
|
|
||||||
{
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
if (req.result != UnityWebRequest.Result.Success)
|
|
||||||
{
|
|
||||||
Debug.LogError($"[SongController] Audio load failed: {req.error}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
clip = DownloadHandlerAudioClip.GetContent(req);
|
|
||||||
}
|
|
||||||
_clipLength = clip.length;
|
|
||||||
|
|
||||||
// Load and parse map
|
|
||||||
DifficultyInfo diffInfo = song.difficulties.Get(diff);
|
|
||||||
if (diffInfo == null)
|
|
||||||
{
|
|
||||||
Debug.LogError($"[SongController] Difficulty '{diff}' not found");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
string mapPath = Path.Combine(CacheRoot, song.id, Path.GetFileName(diffInfo.mapFile));
|
|
||||||
if (!File.Exists(mapPath))
|
|
||||||
{
|
|
||||||
Debug.LogError($"[SongController] Map file missing: {mapPath}");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
MapData map = JsonUtility.FromJson<MapData>(File.ReadAllText(mapPath));
|
|
||||||
if (map == null)
|
|
||||||
{
|
|
||||||
Debug.LogError("[SongController] Map parse failed");
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
if (map.target == null)
|
|
||||||
map.target = new List<NoteData>();
|
|
||||||
|
|
||||||
if (IsForcedResultMap(map))
|
|
||||||
{
|
|
||||||
_scoreManager?.SetTotalNotes(Mathf.Max(0, map.forcedResult.totalNotes));
|
|
||||||
|
|
||||||
yield return StartCoroutine(Countdown());
|
|
||||||
|
|
||||||
_audio.PlayClip(clip);
|
|
||||||
yield return new WaitForSeconds(Mathf.Min(Mathf.Max(0.2f, _clipLength), 0.75f));
|
|
||||||
|
|
||||||
_scoreManager?.ApplyForcedResult(
|
|
||||||
map.forcedResult.totalNotes,
|
|
||||||
map.forcedResult.perfect,
|
|
||||||
map.forcedResult.great,
|
|
||||||
map.forcedResult.good,
|
|
||||||
map.forcedResult.miss,
|
|
||||||
map.forcedResult.maxCombo);
|
|
||||||
_scoreManager?.CompleteSong();
|
|
||||||
onLevelComplete?.Invoke();
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
map.target.Sort(CompareNotes);
|
|
||||||
if (_clipLength <= 0.0f)
|
|
||||||
{
|
|
||||||
float lastNoteTime = map.target.Count > 0 ? map.target[map.target.Count - 1].time : 0.0f;
|
|
||||||
_clipLength = Mathf.Max(song.duration, lastNoteTime + 1.0f);
|
|
||||||
}
|
|
||||||
_scoreManager?.SetTotalNotes(map.target.Count);
|
|
||||||
|
|
||||||
yield return StartCoroutine(Countdown());
|
|
||||||
|
|
||||||
_audio.PlayClip(clip);
|
|
||||||
|
|
||||||
StartCoroutine(SpawnRoutine(map.target));
|
|
||||||
yield return StartCoroutine(WaitForCompletion(_clipLength, map.target));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator Countdown()
|
|
||||||
{
|
|
||||||
if (countdownText == null) yield break;
|
|
||||||
countdownText.gameObject.SetActive(true);
|
|
||||||
|
|
||||||
string[] labels = { "3", "2", "1", "GO!" };
|
|
||||||
float[] durations = { 1f, 1f, 1f, 0.6f };
|
|
||||||
|
|
||||||
for (int i = 0; i < labels.Length; i++)
|
|
||||||
{
|
|
||||||
countdownText.text = labels[i];
|
|
||||||
yield return new WaitForSeconds(durations[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
countdownText.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator SpawnRoutine(List<NoteData> notes)
|
|
||||||
{
|
|
||||||
float travelTime = VR_BeatManager.instance.GameSettings.TargetTravelTime;
|
|
||||||
|
|
||||||
foreach (NoteData note in notes)
|
|
||||||
{
|
|
||||||
float adjustedNoteTime = note.time + GlobalSyncSettings.AudioOffsetSeconds;
|
|
||||||
float spawnAt = Mathf.Max(0f, adjustedNoteTime - travelTime);
|
|
||||||
yield return new WaitUntil(() => _audio.CurrentTime >= spawnAt);
|
|
||||||
SpawnNote(note);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SpawnNote(NoteData note)
|
|
||||||
{
|
|
||||||
float x = MapLaneX(note.position);
|
|
||||||
float y = MapLayerY(note.lineLayer);
|
|
||||||
|
|
||||||
// 스폰 시점의 실제 남은 시간으로 역산 → 동시 노트가 프레임 차이 나도 같은 타이밍에 도착
|
|
||||||
float remaining = note.time + GlobalSyncSettings.AudioOffsetSeconds - _audio.CurrentTime;
|
|
||||||
float travelTime = Mathf.Max(0.05f, remaining);
|
|
||||||
|
|
||||||
var info = new SpawnEventInfo
|
|
||||||
{
|
|
||||||
position = new Vector3(x, y, 0f),
|
|
||||||
colorSide = note.colorType == 0 ? ColorSide.Left : ColorSide.Right,
|
|
||||||
hitDirection = MapCutDirection(note.cutDirection),
|
|
||||||
useSpark = false,
|
|
||||||
speed = 2f,
|
|
||||||
travelTimeOverride = travelTime,
|
|
||||||
};
|
|
||||||
|
|
||||||
VR_BeatManager.instance.Spawn(cubePrefab, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int CompareNotes(NoteData a, NoteData b)
|
|
||||||
{
|
|
||||||
int timeCompare = a.time.CompareTo(b.time);
|
|
||||||
if (timeCompare != 0)
|
|
||||||
return timeCompare;
|
|
||||||
|
|
||||||
int positionCompare = a.position.CompareTo(b.position);
|
|
||||||
if (positionCompare != 0)
|
|
||||||
return positionCompare;
|
|
||||||
|
|
||||||
return a.lineLayer.CompareTo(b.lineLayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsForcedResultMap(MapData map)
|
|
||||||
=> map?.forcedResult != null && map.forcedResult.enabled;
|
|
||||||
|
|
||||||
private static float MapLaneX(int position)
|
|
||||||
{
|
|
||||||
int lane = Mathf.Clamp(position, 0, 3);
|
|
||||||
return (lane - HorizontalCenter) * LaneSpacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float MapLayerY(int lineLayer)
|
|
||||||
{
|
|
||||||
int layer = Mathf.Clamp(lineLayer, 0, 2);
|
|
||||||
return VerticalOffset + (layer - VerticalCenter) * LayerSpacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Beat Saber cutDirection → VRBeats Direction
|
|
||||||
// BS: 0=Up 1=Down 2=Left 3=Right 4=UpperLeft 5=UpperRight 6=LowerLeft 7=LowerRight 8=Any
|
|
||||||
private static readonly Direction[] CutDirMap =
|
|
||||||
{
|
|
||||||
Direction.Up,
|
|
||||||
Direction.Down,
|
|
||||||
Direction.Left,
|
|
||||||
Direction.Right,
|
|
||||||
Direction.UpperLeft,
|
|
||||||
Direction.UpperRight,
|
|
||||||
Direction.LowerLeft,
|
|
||||||
Direction.LowerRight,
|
|
||||||
Direction.Center,
|
|
||||||
};
|
|
||||||
|
|
||||||
private static Direction MapCutDirection(int cut)
|
|
||||||
=> (cut >= 0 && cut < CutDirMap.Length) ? CutDirMap[cut] : Direction.Center;
|
|
||||||
|
|
||||||
private IEnumerator WaitForCompletion(float clipLength, List<NoteData> notes)
|
|
||||||
{
|
|
||||||
float lastNoteTime = notes.Count > 0 ? notes[notes.Count - 1].time : 0.0f;
|
|
||||||
float resultTime = Mathf.Min(clipLength, lastNoteTime + GlobalSyncSettings.AudioOffsetSeconds + 0.35f);
|
|
||||||
yield return new WaitUntil(() => _audio.CurrentTime >= resultTime);
|
|
||||||
yield return new WaitForSeconds(0.35f);
|
|
||||||
_scoreManager?.CompleteSong();
|
|
||||||
onLevelComplete?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: ca3b401467e927148b0face4c03b0062
|
|
||||||
@@ -1,424 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
using TMPro;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Networking;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
public class SongCreatorManager : MonoBehaviour
|
|
||||||
{
|
|
||||||
[Header("Audio Source")]
|
|
||||||
[SerializeField] private TMP_Dropdown audioDropdown;
|
|
||||||
[SerializeField] private Button refreshBtn;
|
|
||||||
[SerializeField] private TMP_Text inputPathHint;
|
|
||||||
|
|
||||||
[Header("Audio — Local File")]
|
|
||||||
[SerializeField] private Button filePickerBtn;
|
|
||||||
[SerializeField] private TMP_Text addStatusText;
|
|
||||||
|
|
||||||
[Header("Audio — URL")]
|
|
||||||
[SerializeField] private TMP_InputField urlInput;
|
|
||||||
[SerializeField] private Button urlDownloadBtn;
|
|
||||||
|
|
||||||
[Header("Metadata")]
|
|
||||||
[SerializeField] private TMP_InputField titleInput;
|
|
||||||
[SerializeField] private TMP_InputField artistInput;
|
|
||||||
[SerializeField] private TMP_InputField bpmInput;
|
|
||||||
|
|
||||||
[Header("Difficulty")]
|
|
||||||
[SerializeField] private Toggle toggleNormal;
|
|
||||||
[SerializeField] private Toggle toggleHard;
|
|
||||||
[SerializeField] private Toggle toggleExpert;
|
|
||||||
[SerializeField] private Toggle toggleExpertPlus;
|
|
||||||
|
|
||||||
[Header("Actions")]
|
|
||||||
[SerializeField] private Button generateButton;
|
|
||||||
[SerializeField] private Button manualEditorButton;
|
|
||||||
[SerializeField] private Button backButton;
|
|
||||||
[SerializeField] private string menuSceneName = "Menu";
|
|
||||||
|
|
||||||
[Header("Progress")]
|
|
||||||
[SerializeField] private GameObject progressGroup;
|
|
||||||
[SerializeField] private TMP_Text statusText;
|
|
||||||
[SerializeField] private Slider progressSlider;
|
|
||||||
|
|
||||||
[Header("References")]
|
|
||||||
[SerializeField] private BeatSageUploader beatSageUploader;
|
|
||||||
[SerializeField] private NasPublisher nasPublisher;
|
|
||||||
|
|
||||||
private static readonly Color NeonBg = new Color(0.05f, 0.82f, 0.95f, 0.42f);
|
|
||||||
private static readonly Color DarkButtonBg = new Color(0.09f, 0.22f, 0.27f, 0.66f);
|
|
||||||
private static readonly Color DisabledBg = new Color(0.06f, 0.12f, 0.15f, 0.48f);
|
|
||||||
private static readonly Color ButtonText = new Color(0.92f, 1.0f, 1.0f, 1.0f);
|
|
||||||
private static readonly Color MutedText = new Color(0.66f, 0.80f, 0.84f, 0.76f);
|
|
||||||
private static readonly Color NeonOutline = new Color(0.25f, 0.96f, 1.0f, 0.42f);
|
|
||||||
|
|
||||||
private static string InputPath =>
|
|
||||||
Path.Combine(Application.persistentDataPath, "input");
|
|
||||||
|
|
||||||
private readonly List<string> audioFiles = new();
|
|
||||||
private string _pendingFilePath;
|
|
||||||
|
|
||||||
private void OnValidate()
|
|
||||||
{
|
|
||||||
ApplyButtonStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
ApplyButtonStyles();
|
|
||||||
Directory.CreateDirectory(InputPath);
|
|
||||||
|
|
||||||
if (inputPathHint != null)
|
|
||||||
inputPathHint.text = $"Path: {InputPath}";
|
|
||||||
|
|
||||||
refreshBtn?.onClick.AddListener(RefreshAudioList);
|
|
||||||
generateButton?.onClick.AddListener(OnGenerateClicked);
|
|
||||||
backButton?.onClick.AddListener(() => SceneManager.LoadScene(menuSceneName));
|
|
||||||
filePickerBtn?.onClick.AddListener(OnFilePickerClicked);
|
|
||||||
urlDownloadBtn?.onClick.AddListener(OnUrlDownloadClicked);
|
|
||||||
|
|
||||||
if (progressGroup != null) progressGroup.SetActive(false);
|
|
||||||
RefreshAudioList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
if (_pendingFilePath != null)
|
|
||||||
{
|
|
||||||
CopyToInput(_pendingFilePath);
|
|
||||||
_pendingFilePath = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshAudioList()
|
|
||||||
{
|
|
||||||
audioFiles.Clear();
|
|
||||||
audioDropdown?.ClearOptions();
|
|
||||||
|
|
||||||
var options = new List<string>();
|
|
||||||
foreach (string f in Directory.GetFiles(InputPath, "*.mp3"))
|
|
||||||
{
|
|
||||||
audioFiles.Add(f);
|
|
||||||
options.Add(Path.GetFileNameWithoutExtension(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.Count == 0) options.Add("-- no .mp3 files --");
|
|
||||||
audioDropdown?.AddOptions(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGenerateClicked()
|
|
||||||
{
|
|
||||||
string directUrl = urlInput != null ? urlInput.text.Trim() : "";
|
|
||||||
bool hasUrl = !string.IsNullOrEmpty(directUrl);
|
|
||||||
bool hasFile = audioFiles.Count > 0;
|
|
||||||
|
|
||||||
if (!hasUrl && !hasFile) { SetStatus("No audio source. Add a file or enter a URL."); return; }
|
|
||||||
|
|
||||||
// BPM input is optional — Beat Sage auto-detects from audio; use as fallback only
|
|
||||||
float.TryParse(bpmInput?.text, out float bpmHint);
|
|
||||||
|
|
||||||
var diffs = new List<string> { "normal", "hard", "expert", "expertplus" };
|
|
||||||
|
|
||||||
if (hasUrl)
|
|
||||||
{
|
|
||||||
if (!Uri.TryCreate(directUrl, UriKind.Absolute, out var uri) ||
|
|
||||||
(uri.Scheme != "http" && uri.Scheme != "https"))
|
|
||||||
{ SetStatus("Invalid URL."); return; }
|
|
||||||
|
|
||||||
StartCoroutine(GenerateFlowFromUrl(uri.AbsoluteUri, bpmHint, diffs));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StartCoroutine(GenerateFlow(audioFiles[audioDropdown.value], bpmHint, diffs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator GenerateFlowFromUrl(string audioUrl, float bpm, List<string> diffs)
|
|
||||||
{
|
|
||||||
SetInteractable(false);
|
|
||||||
if (progressGroup != null) progressGroup.SetActive(true);
|
|
||||||
|
|
||||||
Dictionary<string, List<NoteData>> maps = null;
|
|
||||||
bool failed = false;
|
|
||||||
|
|
||||||
yield return beatSageUploader.UploadFromUrl(
|
|
||||||
audioUrl, diffs, bpm,
|
|
||||||
onProgress: p =>
|
|
||||||
{
|
|
||||||
if (progressSlider != null) progressSlider.value = p * 0.8f;
|
|
||||||
SetStatus($"{beatSageUploader.CurrentStatus} ({(int)(p * 80)}%)");
|
|
||||||
},
|
|
||||||
onComplete: result => maps = result,
|
|
||||||
onError: err => { SetStatus($"Error: {err}"); failed = true; });
|
|
||||||
|
|
||||||
if (failed) { SetInteractable(true); yield break; }
|
|
||||||
|
|
||||||
SongInfo song = BuildSongInfo(audioUrl, bpm, maps);
|
|
||||||
|
|
||||||
yield return nasPublisher.Publish(
|
|
||||||
song, null, maps,
|
|
||||||
onProgress: p =>
|
|
||||||
{
|
|
||||||
if (progressSlider != null) progressSlider.value = 0.8f + p * 0.2f;
|
|
||||||
SetStatus($"[4/4] Uploading to NAS... ({(int)((0.8f + p * 0.2f) * 100)}%)");
|
|
||||||
},
|
|
||||||
onComplete: () =>
|
|
||||||
{
|
|
||||||
if (progressSlider != null) progressSlider.value = 1f;
|
|
||||||
SetStatus($"Done! '{song.title}' created successfully.");
|
|
||||||
},
|
|
||||||
onError: err => { SetStatus($"NAS upload failed: {err}"); failed = true; });
|
|
||||||
|
|
||||||
SetInteractable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator GenerateFlow(string audioPath, float bpm, List<string> diffs)
|
|
||||||
{
|
|
||||||
SetInteractable(false);
|
|
||||||
if (progressGroup != null) progressGroup.SetActive(true);
|
|
||||||
|
|
||||||
Dictionary<string, List<NoteData>> maps = null;
|
|
||||||
bool failed = false;
|
|
||||||
|
|
||||||
yield return beatSageUploader.Upload(
|
|
||||||
audioPath, diffs, bpm,
|
|
||||||
onProgress: p =>
|
|
||||||
{
|
|
||||||
if (progressSlider != null) progressSlider.value = p * 0.8f;
|
|
||||||
SetStatus($"{beatSageUploader.CurrentStatus} ({(int)(p * 80)}%)");
|
|
||||||
},
|
|
||||||
onComplete: result => maps = result,
|
|
||||||
onError: err => { SetStatus($"Error: {err}"); failed = true; });
|
|
||||||
|
|
||||||
if (failed) { SetInteractable(true); yield break; }
|
|
||||||
|
|
||||||
SongInfo song = BuildSongInfo(audioPath, bpm, maps);
|
|
||||||
|
|
||||||
yield return nasPublisher.Publish(
|
|
||||||
song, audioPath, maps,
|
|
||||||
onProgress: p =>
|
|
||||||
{
|
|
||||||
if (progressSlider != null) progressSlider.value = 0.8f + p * 0.2f;
|
|
||||||
SetStatus($"[4/4] Uploading to NAS... ({(int)((0.8f + p * 0.2f) * 100)}%)");
|
|
||||||
},
|
|
||||||
onComplete: () =>
|
|
||||||
{
|
|
||||||
if (progressSlider != null) progressSlider.value = 1f;
|
|
||||||
SetStatus($"Done! '{song.title}' created successfully.");
|
|
||||||
},
|
|
||||||
onError: err => { SetStatus($"NAS upload failed: {err}"); failed = true; });
|
|
||||||
|
|
||||||
SetInteractable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SongInfo BuildSongInfo(string audioPath, float fallbackBpm,
|
|
||||||
Dictionary<string, List<NoteData>> maps)
|
|
||||||
{
|
|
||||||
// Prefer values from info.dat (auto-detected by Beat Sage); UI inputs override if non-empty
|
|
||||||
var meta = beatSageUploader != null ? beatSageUploader.LastMetadata : null;
|
|
||||||
string uiTitle = titleInput?.text.Trim() ?? "";
|
|
||||||
string uiArtist = artistInput?.text.Trim() ?? "";
|
|
||||||
float.TryParse(bpmInput?.text, out float uiBpm);
|
|
||||||
|
|
||||||
string title = !string.IsNullOrEmpty(uiTitle) ? uiTitle : (meta?.title ?? "");
|
|
||||||
string artist = !string.IsNullOrEmpty(uiArtist) ? uiArtist : (meta?.artist ?? "");
|
|
||||||
float bpm = (meta != null && meta.bpm > 0) ? meta.bpm : (uiBpm > 0 ? uiBpm : fallbackBpm);
|
|
||||||
|
|
||||||
// Fallback id from filename if title is still empty
|
|
||||||
if (string.IsNullOrEmpty(title) && !string.IsNullOrEmpty(audioPath))
|
|
||||||
title = Path.GetFileNameWithoutExtension(audioPath);
|
|
||||||
if (string.IsNullOrEmpty(title))
|
|
||||||
title = $"song_{DateTime.Now:yyyyMMdd_HHmmss}";
|
|
||||||
|
|
||||||
string id = title.ToLower().Replace(" ", "_");
|
|
||||||
|
|
||||||
var diffMap = new DifficultyMap();
|
|
||||||
foreach (var kv in maps)
|
|
||||||
{
|
|
||||||
var info = new DifficultyInfo { noteCount = kv.Value.Count };
|
|
||||||
switch (kv.Key)
|
|
||||||
{
|
|
||||||
case "normal": diffMap.normal = info; break;
|
|
||||||
case "hard": diffMap.hard = info; break;
|
|
||||||
case "expert": diffMap.expert = info; break;
|
|
||||||
case "expertplus": diffMap.expertplus = info; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SongInfo
|
|
||||||
{
|
|
||||||
id = id,
|
|
||||||
title = title,
|
|
||||||
artist = artist,
|
|
||||||
bpm = bpm,
|
|
||||||
audioFile = $"music/{id}.mp3",
|
|
||||||
difficulties = diffMap,
|
|
||||||
addedAt = DateTime.Now.ToString("yyyy-MM-dd"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetStatus(string msg) { if (statusText != null) statusText.text = msg; }
|
|
||||||
|
|
||||||
private void SetInteractable(bool value)
|
|
||||||
{
|
|
||||||
if (generateButton != null) generateButton.interactable = value;
|
|
||||||
if (audioDropdown != null) audioDropdown.interactable = value;
|
|
||||||
if (refreshBtn != null) refreshBtn.interactable = value;
|
|
||||||
if (filePickerBtn != null) filePickerBtn.interactable = value;
|
|
||||||
if (urlDownloadBtn != null) urlDownloadBtn.interactable = value;
|
|
||||||
ApplyButtonStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnFilePickerClicked()
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
string path = UnityEditor.EditorUtility.OpenFilePanel("Select audio file", "", "mp3");
|
|
||||||
if (!string.IsNullOrEmpty(path)) CopyToInput(path);
|
|
||||||
#elif UNITY_STANDALONE_WIN
|
|
||||||
var t = new Thread(() =>
|
|
||||||
{
|
|
||||||
var dlg = new System.Windows.Forms.OpenFileDialog { Filter = "MP3|*.mp3" };
|
|
||||||
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
|
||||||
_pendingFilePath = dlg.FileName;
|
|
||||||
});
|
|
||||||
t.SetApartmentState(ApartmentState.STA);
|
|
||||||
t.Start();
|
|
||||||
#else
|
|
||||||
SetAddStatus($"Copy file via ADB:\n{InputPath}");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CopyToInput(string srcPath)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string dest = Path.Combine(InputPath, Path.GetFileName(srcPath));
|
|
||||||
File.Copy(srcPath, dest, overwrite: true);
|
|
||||||
RefreshAudioList();
|
|
||||||
string nameNoExt = Path.GetFileNameWithoutExtension(srcPath);
|
|
||||||
int idx = audioFiles.FindIndex(f => Path.GetFileNameWithoutExtension(f) == nameNoExt);
|
|
||||||
if (idx >= 0 && audioDropdown != null) audioDropdown.value = idx;
|
|
||||||
SetAddStatus($"Added: {Path.GetFileName(srcPath)}");
|
|
||||||
}
|
|
||||||
catch (Exception e) { SetAddStatus($"File copy failed: {e.Message}"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUrlDownloadClicked()
|
|
||||||
{
|
|
||||||
string url = urlInput != null ? urlInput.text.Trim() : "";
|
|
||||||
if (string.IsNullOrEmpty(url)) { SetAddStatus("Please enter a URL."); return; }
|
|
||||||
|
|
||||||
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) ||
|
|
||||||
(uri.Scheme != "http" && uri.Scheme != "https"))
|
|
||||||
{
|
|
||||||
SetAddStatus($"Invalid URL: must start with http:// or https://");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Streaming service URLs cannot be downloaded directly
|
|
||||||
string host = uri.Host.ToLower();
|
|
||||||
if (host.Contains("youtube.com") || host.Contains("youtu.be") ||
|
|
||||||
host.Contains("spotify.com") || host.Contains("soundcloud.com") ||
|
|
||||||
host.Contains("music.apple.com"))
|
|
||||||
{
|
|
||||||
SetAddStatus("Streaming URLs not supported.\nUse a direct .mp3 download link or Browse File.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StartCoroutine(DownloadFromUrl(uri.AbsoluteUri));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator DownloadFromUrl(string url)
|
|
||||||
{
|
|
||||||
SetAddStatus("Downloading...");
|
|
||||||
if (urlDownloadBtn != null) urlDownloadBtn.interactable = false;
|
|
||||||
ApplyButtonStyles();
|
|
||||||
|
|
||||||
string fileName;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string uriPath = new Uri(url).AbsolutePath;
|
|
||||||
fileName = Path.GetFileName(uriPath);
|
|
||||||
if (string.IsNullOrEmpty(fileName) || !fileName.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase))
|
|
||||||
fileName = "download.mp3";
|
|
||||||
}
|
|
||||||
catch { fileName = "download.mp3"; }
|
|
||||||
|
|
||||||
string savePath = Path.GetFullPath(Path.Combine(InputPath, fileName));
|
|
||||||
|
|
||||||
using var req = UnityWebRequest.Get(url);
|
|
||||||
req.downloadHandler = new DownloadHandlerFile(savePath);
|
|
||||||
yield return req.SendWebRequest();
|
|
||||||
|
|
||||||
if (urlDownloadBtn != null) urlDownloadBtn.interactable = true;
|
|
||||||
ApplyButtonStyles();
|
|
||||||
|
|
||||||
if (req.result == UnityWebRequest.Result.Success)
|
|
||||||
{
|
|
||||||
RefreshAudioList();
|
|
||||||
int idx = audioFiles.FindIndex(
|
|
||||||
f => Path.GetFileNameWithoutExtension(f) == Path.GetFileNameWithoutExtension(fileName));
|
|
||||||
if (idx >= 0 && audioDropdown != null) audioDropdown.value = idx;
|
|
||||||
SetAddStatus($"Downloaded: {fileName}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (File.Exists(savePath)) File.Delete(savePath);
|
|
||||||
SetAddStatus($"Download failed: {req.error}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetAddStatus(string msg) { if (addStatusText != null) addStatusText.text = msg; }
|
|
||||||
|
|
||||||
private void ApplyButtonStyles()
|
|
||||||
{
|
|
||||||
ApplyCreatorButtonStyle(generateButton, true);
|
|
||||||
ApplyCreatorButtonStyle(urlDownloadBtn, true);
|
|
||||||
ApplyCreatorButtonStyle(refreshBtn, false);
|
|
||||||
ApplyCreatorButtonStyle(filePickerBtn, false);
|
|
||||||
ApplyCreatorButtonStyle(backButton, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyCreatorButtonStyle(Button btn, bool primary)
|
|
||||||
{
|
|
||||||
if (btn == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Color bg = btn.interactable ? (primary ? NeonBg : DarkButtonBg) : DisabledBg;
|
|
||||||
if (btn.targetGraphic is Image img)
|
|
||||||
{
|
|
||||||
img.color = bg;
|
|
||||||
img.raycastTarget = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var colors = btn.colors;
|
|
||||||
colors.normalColor = bg;
|
|
||||||
colors.highlightedColor = btn.interactable
|
|
||||||
? new Color(0.10f, 0.95f, 1.0f, primary ? 0.58f : 0.48f)
|
|
||||||
: DisabledBg;
|
|
||||||
colors.pressedColor = btn.interactable
|
|
||||||
? new Color(0.02f, 0.58f, 0.72f, 0.80f)
|
|
||||||
: DisabledBg;
|
|
||||||
colors.selectedColor = colors.highlightedColor;
|
|
||||||
colors.disabledColor = DisabledBg;
|
|
||||||
colors.fadeDuration = 0.08f;
|
|
||||||
btn.colors = colors;
|
|
||||||
|
|
||||||
TMP_Text label = btn.GetComponentInChildren<TMP_Text>(true);
|
|
||||||
if (label != null)
|
|
||||||
{
|
|
||||||
label.color = btn.interactable ? ButtonText : MutedText;
|
|
||||||
label.raycastTarget = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Outline outline = btn.GetComponent<Outline>() ?? btn.gameObject.AddComponent<Outline>();
|
|
||||||
outline.enabled = btn.interactable;
|
|
||||||
outline.effectColor = NeonOutline;
|
|
||||||
outline.effectDistance = new Vector2(0.0f, -0.28f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: ad9984c644076724bb5507e3e9e73ed5
|
|
||||||
@@ -1,384 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using TMPro;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
public class SongDetailPanel : MonoBehaviour
|
|
||||||
{
|
|
||||||
[Header("곡 정보")]
|
|
||||||
[SerializeField] private TMP_Text titleText;
|
|
||||||
[SerializeField] private TMP_Text artistText;
|
|
||||||
[SerializeField] private TMP_Text infoText;
|
|
||||||
|
|
||||||
[Header("난이도 버튼")]
|
|
||||||
[SerializeField] private Button btnNormal;
|
|
||||||
[SerializeField] private Button btnHard;
|
|
||||||
[SerializeField] private Button btnExpert;
|
|
||||||
[SerializeField] private Button btnExpertPlus;
|
|
||||||
|
|
||||||
[Header("액션 버튼")]
|
|
||||||
[SerializeField] private Button downloadButton;
|
|
||||||
[SerializeField] private Button deleteButton;
|
|
||||||
[SerializeField] private Button playButton;
|
|
||||||
[SerializeField] private Button closeButton;
|
|
||||||
|
|
||||||
[Header("진행률")]
|
|
||||||
[SerializeField] private GameObject progressGroup;
|
|
||||||
[SerializeField] private Slider progressSlider;
|
|
||||||
[SerializeField] private TMP_Text progressText;
|
|
||||||
|
|
||||||
[Header("씬 이름")]
|
|
||||||
[SerializeField] private string gameSceneName = "Game";
|
|
||||||
|
|
||||||
private static readonly Color NeonBg = new Color(0.05f, 0.82f, 0.95f, 0.42f);
|
|
||||||
private static readonly Color DarkButtonBg = new Color(0.09f, 0.22f, 0.27f, 0.66f);
|
|
||||||
private static readonly Color DisabledBg = new Color(0.06f, 0.12f, 0.15f, 0.48f);
|
|
||||||
private static readonly Color DangerBg = new Color(0.52f, 0.16f, 0.22f, 0.72f);
|
|
||||||
private static readonly Color ButtonText = new Color(0.92f, 1.0f, 1.0f, 1.0f);
|
|
||||||
private static readonly Color MutedText = new Color(0.66f, 0.80f, 0.84f, 0.76f);
|
|
||||||
private static readonly Color NeonOutline = new Color(0.25f, 0.96f, 1.0f, 0.42f);
|
|
||||||
|
|
||||||
private SongInfo currentSong;
|
|
||||||
private string selectedDifficulty;
|
|
||||||
private DownloadManager downloadManager;
|
|
||||||
private SongSelectManager selectManager;
|
|
||||||
private MarqueeText titleMarquee;
|
|
||||||
private MarqueeText artistMarquee;
|
|
||||||
|
|
||||||
private readonly (string key, Func<SongDetailPanel, Button> btn)[] diffSlots =
|
|
||||||
{
|
|
||||||
("normal", p => p.btnNormal),
|
|
||||||
("hard", p => p.btnHard),
|
|
||||||
("expert", p => p.btnExpert),
|
|
||||||
("expertplus", p => p.btnExpertPlus),
|
|
||||||
};
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
HideDifficultyLabel();
|
|
||||||
titleMarquee = ConfigureMarqueeText(titleText, 5.0f, 7.2f);
|
|
||||||
artistMarquee = ConfigureMarqueeText(artistText, 3.4f, 4.4f);
|
|
||||||
ConfigureOneLineText(infoText, 3.2f, 4.2f, TextAlignmentOptions.MidlineLeft);
|
|
||||||
ConfigureButtonText(btnNormal, 3.2f, 4.0f);
|
|
||||||
ConfigureButtonText(btnHard, 3.2f, 4.0f);
|
|
||||||
ConfigureButtonText(btnExpert, 3.2f, 4.0f);
|
|
||||||
ConfigureButtonText(btnExpertPlus, 3.0f, 3.8f);
|
|
||||||
ConfigureButtonText(downloadButton, 3.5f, 4.4f);
|
|
||||||
ConfigureButtonText(deleteButton, 3.5f, 4.4f);
|
|
||||||
ConfigureButtonText(playButton, 3.5f, 4.4f);
|
|
||||||
ConfigureButtonText(closeButton, 5.2f, 6.4f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Public API ───────────────────────────────────────────
|
|
||||||
|
|
||||||
public void Show(SongInfo song, DownloadManager dm, SongSelectManager sm)
|
|
||||||
{
|
|
||||||
currentSong = song;
|
|
||||||
downloadManager = dm;
|
|
||||||
selectManager = sm;
|
|
||||||
selectedDifficulty = null;
|
|
||||||
|
|
||||||
titleText.text = song.title;
|
|
||||||
artistText.text = song.artist;
|
|
||||||
infoText.text = song.duration > 0
|
|
||||||
? $"BPM {Mathf.RoundToInt(song.bpm)} {FormatDuration(song.duration)}"
|
|
||||||
: $"BPM {Mathf.RoundToInt(song.bpm)}";
|
|
||||||
|
|
||||||
titleMarquee?.Refresh();
|
|
||||||
artistMarquee?.Refresh();
|
|
||||||
RefreshUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── UI 갱신 ──────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void RefreshUI()
|
|
||||||
{
|
|
||||||
bool downloaded = SongLibrary.Instance.IsSongDownloaded(currentSong.id);
|
|
||||||
|
|
||||||
foreach (var (key, getBtn) in diffSlots)
|
|
||||||
{
|
|
||||||
Button btn = getBtn(this);
|
|
||||||
bool exists = currentSong.difficulties.Get(key) != null;
|
|
||||||
|
|
||||||
btn.interactable = downloaded && exists;
|
|
||||||
btn.onClick.RemoveAllListeners();
|
|
||||||
|
|
||||||
if (downloaded && exists)
|
|
||||||
{
|
|
||||||
string captured = key;
|
|
||||||
btn.onClick.AddListener(() => SelectDifficulty(captured));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateDiffColors();
|
|
||||||
|
|
||||||
downloadButton.gameObject.SetActive(!downloaded);
|
|
||||||
deleteButton.gameObject.SetActive(downloaded);
|
|
||||||
downloadButton.interactable = !downloaded;
|
|
||||||
deleteButton.interactable = downloaded;
|
|
||||||
playButton.interactable = downloaded && selectedDifficulty != null;
|
|
||||||
progressGroup.SetActive(false);
|
|
||||||
UpdateActionButtonStyles(downloaded);
|
|
||||||
|
|
||||||
downloadButton.onClick.RemoveAllListeners();
|
|
||||||
downloadButton.onClick.AddListener(OnDownloadClicked);
|
|
||||||
|
|
||||||
deleteButton.onClick.RemoveAllListeners();
|
|
||||||
deleteButton.onClick.AddListener(OnDeleteClicked);
|
|
||||||
|
|
||||||
playButton.onClick.RemoveAllListeners();
|
|
||||||
playButton.onClick.AddListener(OnPlayClicked);
|
|
||||||
|
|
||||||
if (closeButton != null)
|
|
||||||
{
|
|
||||||
closeButton.onClick.RemoveAllListeners();
|
|
||||||
closeButton.onClick.AddListener(() => gameObject.SetActive(false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SelectDifficulty(string difficulty)
|
|
||||||
{
|
|
||||||
selectedDifficulty = difficulty;
|
|
||||||
playButton.interactable = true;
|
|
||||||
UpdateDiffColors();
|
|
||||||
UpdateActionButtonStyles(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateDiffColors()
|
|
||||||
{
|
|
||||||
foreach (var (key, getBtn) in diffSlots)
|
|
||||||
{
|
|
||||||
Button btn = getBtn(this);
|
|
||||||
bool selected = key == selectedDifficulty;
|
|
||||||
|
|
||||||
ApplyButtonStyle(btn, selected ? NeonBg : DarkButtonBg, selected, btn.interactable, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 다운로드 ──────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void OnDownloadClicked()
|
|
||||||
{
|
|
||||||
StartCoroutine(DownloadAllCoroutine());
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator DownloadAllCoroutine()
|
|
||||||
{
|
|
||||||
var diffs = new List<string>();
|
|
||||||
foreach (var (key, _) in diffSlots)
|
|
||||||
if (currentSong.difficulties.Get(key) != null)
|
|
||||||
diffs.Add(key);
|
|
||||||
|
|
||||||
if (diffs.Count == 0) yield break;
|
|
||||||
|
|
||||||
SetInteractable(false);
|
|
||||||
progressGroup.SetActive(true);
|
|
||||||
downloadButton.gameObject.SetActive(false);
|
|
||||||
deleteButton.gameObject.SetActive(false);
|
|
||||||
playButton.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
int totalSteps = diffs.Count;
|
|
||||||
int doneSteps = 0;
|
|
||||||
bool failed = false;
|
|
||||||
|
|
||||||
foreach (string diff in diffs)
|
|
||||||
{
|
|
||||||
bool stepDone = false;
|
|
||||||
|
|
||||||
downloadManager.DownloadSong(
|
|
||||||
currentSong, diff,
|
|
||||||
onProgress: p =>
|
|
||||||
{
|
|
||||||
float overall = (doneSteps + p) / totalSteps;
|
|
||||||
progressSlider.value = overall;
|
|
||||||
progressText.text = $"{diffs[Mathf.Min(doneSteps, diffs.Count - 1)].ToUpper()} {(int)(overall * 100)}%";
|
|
||||||
},
|
|
||||||
onComplete: () =>
|
|
||||||
{
|
|
||||||
SongLibrary.Instance.MarkDownloaded(currentSong.id, diff);
|
|
||||||
doneSteps++;
|
|
||||||
stepDone = true;
|
|
||||||
},
|
|
||||||
onError: err =>
|
|
||||||
{
|
|
||||||
Debug.LogError($"[SongDetailPanel] {err}");
|
|
||||||
failed = true;
|
|
||||||
stepDone = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
yield return new WaitUntil(() => stepDone);
|
|
||||||
if (failed) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetInteractable(true);
|
|
||||||
progressGroup.SetActive(false);
|
|
||||||
playButton.gameObject.SetActive(true);
|
|
||||||
selectManager.RefreshCards();
|
|
||||||
RefreshUI();
|
|
||||||
|
|
||||||
if (!failed)
|
|
||||||
Debug.Log($"[SongDetailPanel] '{currentSong.title}' 전체 다운로드 완료");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 삭제 ─────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void OnDeleteClicked()
|
|
||||||
{
|
|
||||||
downloadManager.DeleteSong(currentSong.id);
|
|
||||||
SongLibrary.Instance.MarkSongRemoved(currentSong.id);
|
|
||||||
selectedDifficulty = null;
|
|
||||||
selectManager.RefreshCards();
|
|
||||||
RefreshUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 플레이 ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void OnPlayClicked()
|
|
||||||
{
|
|
||||||
GameSession.SelectedSong = currentSong;
|
|
||||||
GameSession.SelectedDifficulty = selectedDifficulty;
|
|
||||||
SceneManager.LoadScene(gameSceneName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 유틸 ─────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void SetInteractable(bool value)
|
|
||||||
{
|
|
||||||
downloadButton.interactable = value;
|
|
||||||
deleteButton.interactable = value;
|
|
||||||
playButton.interactable = value && selectedDifficulty != null;
|
|
||||||
foreach (var (_, getBtn) in diffSlots)
|
|
||||||
getBtn(this).interactable = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FormatDuration(int seconds)
|
|
||||||
=> $"{seconds / 60}:{seconds % 60:D2}";
|
|
||||||
|
|
||||||
private void HideDifficultyLabel()
|
|
||||||
{
|
|
||||||
Transform label = transform.Find("LblDifficulty");
|
|
||||||
if (label != null)
|
|
||||||
label.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateActionButtonStyles(bool downloaded)
|
|
||||||
{
|
|
||||||
ApplyButtonStyle(downloadButton, NeonBg, true, !downloaded, false);
|
|
||||||
ApplyButtonStyle(deleteButton, DangerBg, true, downloaded, true);
|
|
||||||
ApplyButtonStyle(playButton, NeonBg, true, playButton.interactable, false);
|
|
||||||
ApplyButtonStyle(closeButton, DarkButtonBg, false, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyButtonStyle(Button btn, Color activeBg, bool outlined, bool enabled, bool danger)
|
|
||||||
{
|
|
||||||
if (btn == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Color bg = enabled ? activeBg : DisabledBg;
|
|
||||||
if (btn.targetGraphic is Image img)
|
|
||||||
img.color = bg;
|
|
||||||
|
|
||||||
var colors = btn.colors;
|
|
||||||
colors.normalColor = bg;
|
|
||||||
colors.highlightedColor = enabled
|
|
||||||
? (danger ? new Color(0.72f, 0.23f, 0.30f, 0.86f) : new Color(0.10f, 0.95f, 1.0f, 0.58f))
|
|
||||||
: DisabledBg;
|
|
||||||
colors.pressedColor = enabled
|
|
||||||
? (danger ? new Color(0.42f, 0.10f, 0.15f, 0.92f) : new Color(0.02f, 0.58f, 0.72f, 0.80f))
|
|
||||||
: DisabledBg;
|
|
||||||
colors.selectedColor = colors.highlightedColor;
|
|
||||||
colors.disabledColor = DisabledBg;
|
|
||||||
colors.fadeDuration = 0.08f;
|
|
||||||
btn.colors = colors;
|
|
||||||
|
|
||||||
TMP_Text label = btn.GetComponentInChildren<TMP_Text>();
|
|
||||||
if (label != null)
|
|
||||||
label.color = enabled ? ButtonText : MutedText;
|
|
||||||
|
|
||||||
Outline outline = btn.GetComponent<Outline>() ?? btn.gameObject.AddComponent<Outline>();
|
|
||||||
outline.enabled = outlined && enabled;
|
|
||||||
outline.effectColor = danger ? new Color(1.0f, 0.35f, 0.42f, 0.34f) : NeonOutline;
|
|
||||||
outline.effectDistance = new Vector2(0.0f, -0.28f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MarqueeText ConfigureMarqueeText(TMP_Text text, float minSize, float maxSize)
|
|
||||||
{
|
|
||||||
if (text == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
RectTransform textRect = text.rectTransform;
|
|
||||||
Transform originalParent = textRect.parent;
|
|
||||||
int siblingIndex = textRect.GetSiblingIndex();
|
|
||||||
string maskName = $"{text.name}Mask";
|
|
||||||
Transform existingMask = originalParent != null ? originalParent.Find(maskName) : null;
|
|
||||||
RectTransform maskRect;
|
|
||||||
|
|
||||||
if (existingMask != null)
|
|
||||||
{
|
|
||||||
maskRect = existingMask as RectTransform;
|
|
||||||
if (textRect.parent != existingMask)
|
|
||||||
textRect.SetParent(existingMask, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var mask = new GameObject(maskName);
|
|
||||||
mask.transform.SetParent(originalParent, false);
|
|
||||||
mask.transform.SetSiblingIndex(siblingIndex);
|
|
||||||
maskRect = mask.AddComponent<RectTransform>();
|
|
||||||
maskRect.anchorMin = textRect.anchorMin;
|
|
||||||
maskRect.anchorMax = textRect.anchorMax;
|
|
||||||
maskRect.pivot = textRect.pivot;
|
|
||||||
maskRect.anchoredPosition = textRect.anchoredPosition;
|
|
||||||
maskRect.sizeDelta = textRect.sizeDelta;
|
|
||||||
maskRect.localRotation = textRect.localRotation;
|
|
||||||
maskRect.localScale = textRect.localScale;
|
|
||||||
mask.AddComponent<RectMask2D>();
|
|
||||||
textRect.SetParent(mask.transform, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
textRect.anchorMin = new Vector2(0f, 0f);
|
|
||||||
textRect.anchorMax = new Vector2(0f, 1f);
|
|
||||||
textRect.pivot = new Vector2(0f, 0.5f);
|
|
||||||
textRect.anchoredPosition = Vector2.zero;
|
|
||||||
textRect.localRotation = Quaternion.identity;
|
|
||||||
textRect.localScale = Vector3.one;
|
|
||||||
textRect.sizeDelta = new Vector2(260.0f, 0f);
|
|
||||||
|
|
||||||
ConfigureOneLineText(text, minSize, maxSize, TextAlignmentOptions.MidlineLeft);
|
|
||||||
text.overflowMode = TextOverflowModes.Overflow;
|
|
||||||
text.raycastTarget = false;
|
|
||||||
|
|
||||||
MarqueeText marquee = text.GetComponent<MarqueeText>() ?? text.gameObject.AddComponent<MarqueeText>();
|
|
||||||
marquee.speed = 9f;
|
|
||||||
marquee.pauseStart = 1.25f;
|
|
||||||
marquee.pauseEnd = 0.8f;
|
|
||||||
return marquee;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ConfigureButtonText(Button btn, float minSize, float maxSize)
|
|
||||||
{
|
|
||||||
if (btn == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
TMP_Text label = btn.GetComponentInChildren<TMP_Text>();
|
|
||||||
ConfigureOneLineText(label, minSize, maxSize, TextAlignmentOptions.Center);
|
|
||||||
if (label != null)
|
|
||||||
label.raycastTarget = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ConfigureOneLineText(TMP_Text text, float minSize, float maxSize, TextAlignmentOptions alignment)
|
|
||||||
{
|
|
||||||
if (text == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
text.enableAutoSizing = true;
|
|
||||||
text.fontSizeMin = minSize;
|
|
||||||
text.fontSizeMax = maxSize;
|
|
||||||
text.alignment = alignment;
|
|
||||||
text.overflowMode = TextOverflowModes.Ellipsis;
|
|
||||||
text.textWrappingMode = TextWrappingModes.NoWrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d27acdc84ca9a6241894ce7ee9f3c3fa
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
public class SongLibrary : MonoBehaviour
|
|
||||||
{
|
|
||||||
public static SongLibrary Instance { get; private set; }
|
|
||||||
|
|
||||||
private const string FileName = "song_library.json";
|
|
||||||
private static string SavePath => Path.Combine(Application.persistentDataPath, FileName);
|
|
||||||
|
|
||||||
private LibraryData _data = new LibraryData();
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
if (Instance != null) { Destroy(gameObject); return; }
|
|
||||||
Instance = this;
|
|
||||||
DontDestroyOnLoad(gameObject);
|
|
||||||
Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Public API ───────────────────────────────────────────
|
|
||||||
|
|
||||||
public void MarkDownloaded(string songId, string difficulty)
|
|
||||||
{
|
|
||||||
LibraryEntry entry = GetOrCreate(songId);
|
|
||||||
if (!entry.difficulties.Contains(difficulty))
|
|
||||||
entry.difficulties.Add(difficulty);
|
|
||||||
entry.lastAccessedAt = DateTime.UtcNow.ToString("o");
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MarkDifficultyRemoved(string songId, string difficulty)
|
|
||||||
{
|
|
||||||
LibraryEntry entry = Find(songId);
|
|
||||||
if (entry == null) return;
|
|
||||||
|
|
||||||
entry.difficulties.Remove(difficulty);
|
|
||||||
if (entry.difficulties.Count == 0)
|
|
||||||
_data.entries.Remove(entry);
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MarkSongRemoved(string songId)
|
|
||||||
{
|
|
||||||
_data.entries.RemoveAll(e => e.songId == songId);
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TouchSong(string songId)
|
|
||||||
{
|
|
||||||
LibraryEntry entry = Find(songId);
|
|
||||||
if (entry == null) return;
|
|
||||||
entry.lastAccessedAt = DateTime.UtcNow.ToString("o");
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSongDownloaded(string songId)
|
|
||||||
=> Find(songId) != null;
|
|
||||||
|
|
||||||
public bool IsDifficultyDownloaded(string songId, string difficulty)
|
|
||||||
=> Find(songId)?.difficulties.Contains(difficulty) ?? false;
|
|
||||||
|
|
||||||
public List<LibraryEntry> GetAll()
|
|
||||||
=> _data.entries;
|
|
||||||
|
|
||||||
public void ValidateWithFileSystem(DownloadManager dm, List<SongInfo> songs)
|
|
||||||
{
|
|
||||||
bool dirty = false;
|
|
||||||
foreach (SongInfo song in songs)
|
|
||||||
{
|
|
||||||
LibraryEntry entry = Find(song.id);
|
|
||||||
if (entry == null) continue;
|
|
||||||
|
|
||||||
if (!dm.IsSongDownloaded(song.id))
|
|
||||||
{
|
|
||||||
_data.entries.Remove(entry);
|
|
||||||
dirty = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.difficulties.RemoveAll(d => !dm.IsDifficultyDownloaded(song, d));
|
|
||||||
if (entry.difficulties.Count == 0)
|
|
||||||
{
|
|
||||||
_data.entries.Remove(entry);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dirty) Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 내부 구현 ─────────────────────────────────────────────
|
|
||||||
|
|
||||||
private LibraryEntry Find(string songId)
|
|
||||||
=> _data.entries.Find(e => e.songId == songId);
|
|
||||||
|
|
||||||
private LibraryEntry GetOrCreate(string songId)
|
|
||||||
{
|
|
||||||
LibraryEntry entry = Find(songId);
|
|
||||||
if (entry != null) return entry;
|
|
||||||
entry = new LibraryEntry { songId = songId };
|
|
||||||
_data.entries.Add(entry);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Load()
|
|
||||||
{
|
|
||||||
if (!File.Exists(SavePath)) return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string json = File.ReadAllText(SavePath);
|
|
||||||
_data = JsonUtility.FromJson<LibraryData>(json) ?? new LibraryData();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Debug.LogWarning($"[SongLibrary] 로드 실패, 초기화: {e.Message}");
|
|
||||||
_data = new LibraryData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Save()
|
|
||||||
{
|
|
||||||
File.WriteAllText(SavePath, JsonUtility.ToJson(_data, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class LibraryData
|
|
||||||
{
|
|
||||||
public List<LibraryEntry> entries = new List<LibraryEntry>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class LibraryEntry
|
|
||||||
{
|
|
||||||
public string songId;
|
|
||||||
public List<string> difficulties = new List<string>();
|
|
||||||
public string lastAccessedAt;
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 765cf3a9cd9c14943be42e1cee050abd
|
|
||||||
@@ -1,328 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using TMPro;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
public class SongSelectManager : MonoBehaviour
|
|
||||||
{
|
|
||||||
[SerializeField] private Button tabAllBtn;
|
|
||||||
[SerializeField] private Button tabOwnedBtn;
|
|
||||||
[SerializeField] private RectTransform cardContainer;
|
|
||||||
[SerializeField] private SongDetailPanel detailPanel;
|
|
||||||
[SerializeField] private DownloadManager downloadManager;
|
|
||||||
[SerializeField] private GameObject loadingOverlay;
|
|
||||||
[SerializeField] private GameObject errorOverlay;
|
|
||||||
[SerializeField] private TMP_Text errorText;
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly Color TabActiveBg = new Color(0.05f, 0.82f, 0.95f, 0.42f);
|
|
||||||
private static readonly Color TabInactiveBg = new Color(0.09f, 0.22f, 0.27f, 0.66f);
|
|
||||||
private static readonly Color TabActiveText = new Color(0.92f, 1.0f, 1.0f, 1.0f);
|
|
||||||
private static readonly Color TabInactiveText = new Color(0.72f, 0.86f, 0.90f, 0.82f);
|
|
||||||
private static readonly Color TabActiveOutline = new Color(0.25f, 0.96f, 1.0f, 0.55f);
|
|
||||||
|
|
||||||
private static string CachePath =>
|
|
||||||
Path.Combine(Application.persistentDataPath, "songs_cache.json");
|
|
||||||
|
|
||||||
private List<SongInfo> allSongs = new List<SongInfo>();
|
|
||||||
private bool showingOwned = false;
|
|
||||||
private TMP_FontAsset _cardFont;
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
// NanumGothic SDF를 직접 로드 — Resources 경로에 있어야 함
|
|
||||||
_cardFont = Resources.Load<TMP_FontAsset>("Fonts & Materials/NanumGothic SDF");
|
|
||||||
if (_cardFont == null)
|
|
||||||
_cardFont = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
|
|
||||||
|
|
||||||
tabAllBtn .onClick.AddListener(() => SwitchTab(false));
|
|
||||||
tabOwnedBtn.onClick.AddListener(() => SwitchTab(true));
|
|
||||||
detailPanel.gameObject.SetActive(false);
|
|
||||||
SetTabVisual(false);
|
|
||||||
FetchSongs();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SwitchTab(bool owned)
|
|
||||||
{
|
|
||||||
showingOwned = owned;
|
|
||||||
SetTabVisual(owned);
|
|
||||||
RefreshCards();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetTabVisual(bool owned)
|
|
||||||
{
|
|
||||||
ApplyTabStyle(tabAllBtn, !owned);
|
|
||||||
ApplyTabStyle(tabOwnedBtn, owned);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyTabStyle(Button btn, bool active)
|
|
||||||
{
|
|
||||||
if (btn == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Color bg = active ? TabActiveBg : TabInactiveBg;
|
|
||||||
if (btn.targetGraphic is Image img)
|
|
||||||
img.color = bg;
|
|
||||||
|
|
||||||
var colors = btn.colors;
|
|
||||||
colors.normalColor = bg;
|
|
||||||
colors.highlightedColor = active
|
|
||||||
? new Color(0.10f, 0.95f, 1.0f, 0.58f)
|
|
||||||
: new Color(0.14f, 0.34f, 0.40f, 0.72f);
|
|
||||||
colors.pressedColor = active
|
|
||||||
? new Color(0.02f, 0.58f, 0.72f, 0.72f)
|
|
||||||
: new Color(0.08f, 0.20f, 0.24f, 0.82f);
|
|
||||||
colors.selectedColor = colors.highlightedColor;
|
|
||||||
colors.disabledColor = new Color(0.05f, 0.10f, 0.12f, 0.45f);
|
|
||||||
btn.colors = colors;
|
|
||||||
|
|
||||||
TMP_Text label = btn.GetComponentInChildren<TMP_Text>();
|
|
||||||
if (label != null)
|
|
||||||
label.color = active ? TabActiveText : TabInactiveText;
|
|
||||||
|
|
||||||
Outline outline = btn.GetComponent<Outline>() ?? btn.gameObject.AddComponent<Outline>();
|
|
||||||
outline.enabled = active;
|
|
||||||
outline.effectColor = TabActiveOutline;
|
|
||||||
outline.effectDistance = new Vector2(0.0f, -0.35f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FetchSongs()
|
|
||||||
{
|
|
||||||
loadingOverlay.SetActive(true);
|
|
||||||
errorOverlay .SetActive(false);
|
|
||||||
|
|
||||||
downloadManager.FetchSongsList(
|
|
||||||
onSuccess: list =>
|
|
||||||
{
|
|
||||||
allSongs = list.songs ?? new List<SongInfo>();
|
|
||||||
AddLocalForcedRankDummies(allSongs);
|
|
||||||
SaveCache(new SongsList { version = list.version, songs = allSongs });
|
|
||||||
SongLibrary.Instance.ValidateWithFileSystem(downloadManager, allSongs);
|
|
||||||
loadingOverlay.SetActive(false);
|
|
||||||
RefreshCards();
|
|
||||||
},
|
|
||||||
onError: _ =>
|
|
||||||
{
|
|
||||||
SongsList cached = LoadCache();
|
|
||||||
loadingOverlay.SetActive(false);
|
|
||||||
if (cached != null)
|
|
||||||
{
|
|
||||||
allSongs = cached.songs;
|
|
||||||
RefreshCards();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errorOverlay.SetActive(true);
|
|
||||||
errorText.text = "Failed to connect to server\nPlease check your internet connection";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RefreshCards()
|
|
||||||
{
|
|
||||||
// DestroyImmediate to avoid deferred-destroy interfering with layout
|
|
||||||
for (int i = cardContainer.childCount - 1; i >= 0; i--)
|
|
||||||
DestroyImmediate(cardContainer.GetChild(i).gameObject);
|
|
||||||
|
|
||||||
List<SongInfo> songs = showingOwned
|
|
||||||
? allSongs.FindAll(s => SongLibrary.Instance.IsSongDownloaded(s.id))
|
|
||||||
: allSongs;
|
|
||||||
|
|
||||||
foreach (SongInfo song in songs)
|
|
||||||
SpawnCard(song);
|
|
||||||
|
|
||||||
// Order matters: layout first → card gets size → then canvas update → anchored children recalculate
|
|
||||||
LayoutRebuilder.ForceRebuildLayoutImmediate(cardContainer);
|
|
||||||
Canvas.ForceUpdateCanvases();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SpawnCard(SongInfo song)
|
|
||||||
{
|
|
||||||
bool downloaded = SongLibrary.Instance.IsSongDownloaded(song.id);
|
|
||||||
|
|
||||||
var card = new GameObject(song.title);
|
|
||||||
card.transform.SetParent(cardContainer, false);
|
|
||||||
|
|
||||||
var le = card.AddComponent<LayoutElement>();
|
|
||||||
le.preferredHeight = 13f;
|
|
||||||
le.flexibleWidth = 1f;
|
|
||||||
|
|
||||||
var bg = card.AddComponent<Image>();
|
|
||||||
bg.color = new Color(1f, 1f, 1f, 0.06f);
|
|
||||||
|
|
||||||
var btn = card.AddComponent<Button>();
|
|
||||||
btn.targetGraphic = bg;
|
|
||||||
var bc = btn.colors;
|
|
||||||
bc.normalColor = new Color(1f, 1f, 1f, 0.06f);
|
|
||||||
bc.highlightedColor = new Color(0.4f, 0.75f, 1f, 0.25f);
|
|
||||||
bc.pressedColor = new Color(0.3f, 0.60f, 0.9f, 0.45f);
|
|
||||||
bc.fadeDuration = 0.1f;
|
|
||||||
btn.colors = bc;
|
|
||||||
|
|
||||||
float textLeftInset = downloaded ? 12f : 5f;
|
|
||||||
|
|
||||||
// Title — RectMask2D 컨테이너 안에서 마퀴 스크롤
|
|
||||||
var titleMask = new GameObject("TitleMask");
|
|
||||||
titleMask.transform.SetParent(card.transform, false);
|
|
||||||
var tmr = titleMask.AddComponent<RectTransform>();
|
|
||||||
tmr.anchorMin = new Vector2(0f, 0.5f);
|
|
||||||
tmr.anchorMax = new Vector2(1f, 1f);
|
|
||||||
tmr.offsetMin = new Vector2(textLeftInset, 0f);
|
|
||||||
tmr.offsetMax = new Vector2(-3f, 0f);
|
|
||||||
titleMask.AddComponent<RectMask2D>();
|
|
||||||
|
|
||||||
var titleGO = new GameObject("Title");
|
|
||||||
titleGO.transform.SetParent(titleMask.transform, false);
|
|
||||||
var tr = titleGO.AddComponent<RectTransform>();
|
|
||||||
tr.anchorMin = new Vector2(0f, 0f);
|
|
||||||
tr.anchorMax = new Vector2(0f, 1f);
|
|
||||||
tr.pivot = new Vector2(0f, 0.5f);
|
|
||||||
tr.anchoredPosition = Vector2.zero;
|
|
||||||
tr.sizeDelta = new Vector2(500f, 0f);
|
|
||||||
var tTmp = titleGO.AddComponent<TextMeshProUGUI>();
|
|
||||||
if (_cardFont != null) tTmp.font = _cardFont;
|
|
||||||
tTmp.text = song.title;
|
|
||||||
tTmp.fontSize = 5f;
|
|
||||||
tTmp.color = Color.white;
|
|
||||||
tTmp.alignment = TextAlignmentOptions.MidlineLeft;
|
|
||||||
tTmp.overflowMode = TextOverflowModes.Overflow;
|
|
||||||
tTmp.textWrappingMode = TextWrappingModes.NoWrap;
|
|
||||||
titleGO.AddComponent<MarqueeText>();
|
|
||||||
|
|
||||||
// Artist
|
|
||||||
var artistGO = new GameObject("Artist");
|
|
||||||
artistGO.transform.SetParent(card.transform, false);
|
|
||||||
var ar = artistGO.AddComponent<RectTransform>();
|
|
||||||
ar.anchorMin = new Vector2(0f, 0.04f);
|
|
||||||
ar.anchorMax = new Vector2(1f, 0.48f);
|
|
||||||
ar.offsetMin = new Vector2(textLeftInset, 0f);
|
|
||||||
ar.offsetMax = new Vector2(-3f, 0f);
|
|
||||||
var aTmp = artistGO.AddComponent<TextMeshProUGUI>();
|
|
||||||
if (_cardFont != null) aTmp.font = _cardFont;
|
|
||||||
aTmp.text = song.artist;
|
|
||||||
aTmp.fontSize = 4f;
|
|
||||||
aTmp.enableAutoSizing = true;
|
|
||||||
aTmp.fontSizeMin = 2.8f;
|
|
||||||
aTmp.fontSizeMax = 4f;
|
|
||||||
aTmp.color = new Color(1f, 1f, 1f, 0.6f);
|
|
||||||
aTmp.alignment = TextAlignmentOptions.MidlineLeft;
|
|
||||||
aTmp.overflowMode = TextOverflowModes.Ellipsis;
|
|
||||||
aTmp.textWrappingMode = TextWrappingModes.NoWrap;
|
|
||||||
|
|
||||||
// Downloaded check mark
|
|
||||||
if (downloaded)
|
|
||||||
{
|
|
||||||
var checkGO = new GameObject("OwnedCheck");
|
|
||||||
checkGO.transform.SetParent(card.transform, false);
|
|
||||||
var cr = checkGO.AddComponent<RectTransform>();
|
|
||||||
cr.anchorMin = new Vector2(0f, 0f);
|
|
||||||
cr.anchorMax = new Vector2(0f, 1f);
|
|
||||||
cr.pivot = new Vector2(0f, 0.5f);
|
|
||||||
cr.anchoredPosition = new Vector2(3.0f, 0f);
|
|
||||||
cr.sizeDelta = new Vector2(6f, 0f);
|
|
||||||
|
|
||||||
Color checkColor = new Color(0.36f, 1.0f, 0.58f, 0.95f);
|
|
||||||
CreateCheckStroke(checkGO.transform, "ShortStroke", new Vector2(1.8f, 7.1f), new Vector2(1.5f, 0.35f), 42.0f, checkColor);
|
|
||||||
CreateCheckStroke(checkGO.transform, "LongStroke", new Vector2(3.25f, 7.85f), new Vector2(3.7f, 0.35f), -45.0f, checkColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
SongInfo captured = song;
|
|
||||||
btn.onClick.AddListener(() => OnCardClicked(captured));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCardClicked(SongInfo song)
|
|
||||||
{
|
|
||||||
detailPanel.gameObject.SetActive(true);
|
|
||||||
detailPanel.Show(song, downloadManager, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CreateCheckStroke(Transform parent, string name, Vector2 anchoredPosition,
|
|
||||||
Vector2 size, float rotationZ, Color color)
|
|
||||||
{
|
|
||||||
var stroke = new GameObject(name);
|
|
||||||
stroke.transform.SetParent(parent, false);
|
|
||||||
|
|
||||||
var rect = stroke.AddComponent<RectTransform>();
|
|
||||||
rect.anchorMin = new Vector2(0f, 0f);
|
|
||||||
rect.anchorMax = new Vector2(0f, 0f);
|
|
||||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
|
||||||
rect.anchoredPosition = anchoredPosition;
|
|
||||||
rect.sizeDelta = size;
|
|
||||||
rect.localRotation = Quaternion.Euler(0f, 0f, rotationZ);
|
|
||||||
|
|
||||||
var img = stroke.AddComponent<Image>();
|
|
||||||
img.color = color;
|
|
||||||
img.raycastTarget = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SaveCache(SongsList list)
|
|
||||||
{
|
|
||||||
try { File.WriteAllText(CachePath, JsonUtility.ToJson(list, true)); }
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SongsList LoadCache()
|
|
||||||
{
|
|
||||||
if (!File.Exists(CachePath)) return null;
|
|
||||||
try { return JsonUtility.FromJson<SongsList>(File.ReadAllText(CachePath)); }
|
|
||||||
catch { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddLocalForcedRankDummies(List<SongInfo> songs)
|
|
||||||
{
|
|
||||||
string root = Path.Combine(Application.persistentDataPath, "beatsaber");
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_m", "M", 10);
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_splus", "S+", 100);
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_s", "S", 100);
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_a", "A", 100);
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_b", "B", 100);
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_c", "C", 100);
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_d", "D", 100);
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_f", "F", 100);
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_splus_border", "S+ 98%", 100);
|
|
||||||
AddLocalForcedRankDummy(songs, root, "dummy_rank_f_zero", "F 0%", 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddLocalForcedRankDummy(List<SongInfo> songs, string root, string id, string title, int noteCount)
|
|
||||||
{
|
|
||||||
if (songs.Exists(song => song.id == id))
|
|
||||||
return;
|
|
||||||
|
|
||||||
string songDir = Path.Combine(root, id);
|
|
||||||
string audioPath = Path.Combine(songDir, $"{id}.mp3");
|
|
||||||
string mapFile = $"Map_{id}_forced.json";
|
|
||||||
string mapPath = Path.Combine(songDir, mapFile);
|
|
||||||
if (!File.Exists(audioPath) || !File.Exists(mapPath))
|
|
||||||
return;
|
|
||||||
|
|
||||||
long audioSize = new FileInfo(audioPath).Length;
|
|
||||||
long mapSize = new FileInfo(mapPath).Length;
|
|
||||||
DifficultyInfo info = new DifficultyInfo
|
|
||||||
{
|
|
||||||
mapFile = mapFile,
|
|
||||||
mapSize = mapSize,
|
|
||||||
noteCount = noteCount
|
|
||||||
};
|
|
||||||
|
|
||||||
songs.Insert(0, new SongInfo
|
|
||||||
{
|
|
||||||
id = id,
|
|
||||||
title = title,
|
|
||||||
artist = "Forced Rank Dummy",
|
|
||||||
bpm = 120.0f,
|
|
||||||
duration = 1,
|
|
||||||
audioFile = $"dummy/{id}.mp3",
|
|
||||||
audioSize = audioSize,
|
|
||||||
coverImage = "",
|
|
||||||
difficulties = new DifficultyMap
|
|
||||||
{
|
|
||||||
normal = info,
|
|
||||||
hard = info,
|
|
||||||
expert = info,
|
|
||||||
expertplus = info
|
|
||||||
},
|
|
||||||
addedAt = "2026-05-29"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 46403458ee5537142ad1e0b2ce7d3995
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using TMPro;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.InputSystem;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityEngine.XR;
|
|
||||||
using XRCommonUsages = UnityEngine.XR.CommonUsages;
|
|
||||||
using XRInputDevice = UnityEngine.XR.InputDevice;
|
|
||||||
|
|
||||||
public class SyncCalibrationOverlay : MonoBehaviour
|
|
||||||
{
|
|
||||||
private const float TickInterval = 1.0f;
|
|
||||||
private const int MaxSamples = 8;
|
|
||||||
|
|
||||||
private static Scene pendingReturnScene;
|
|
||||||
|
|
||||||
private readonly List<float> samples = new();
|
|
||||||
private readonly List<(Canvas canvas, bool enabled)> hiddenCanvases = new();
|
|
||||||
|
|
||||||
private AudioSource audioSource = null;
|
|
||||||
private AudioClip tickClip = null;
|
|
||||||
private RectTransform sweepDot = null;
|
|
||||||
private RectTransform visualPulse = null;
|
|
||||||
private TextMeshProUGUI offsetText = null;
|
|
||||||
private TextMeshProUGUI sampleText = null;
|
|
||||||
private TextMeshProUGUI guideText = null;
|
|
||||||
|
|
||||||
private float lastTickTime = 0.0f;
|
|
||||||
private float nextTickTime = 0.0f;
|
|
||||||
private float pendingVisualPulseTime = -1.0f;
|
|
||||||
private float visualPulseTimer = 0.0f;
|
|
||||||
private bool previousPrimary = false;
|
|
||||||
private bool previousSecondary = false;
|
|
||||||
private Scene returnScene;
|
|
||||||
private Scene syncScene;
|
|
||||||
private bool canvasesRestored = false;
|
|
||||||
|
|
||||||
public static void Open()
|
|
||||||
{
|
|
||||||
if (FindFirstObjectByType<SyncCalibrationOverlay>() != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pendingReturnScene = SceneManager.GetActiveScene();
|
|
||||||
Scene calibrationScene = SceneManager.CreateScene("SyncCalibration");
|
|
||||||
SceneManager.SetActiveScene(calibrationScene);
|
|
||||||
|
|
||||||
GameObject overlay = new GameObject("[SyncCalibrationOverlay]");
|
|
||||||
overlay.AddComponent<SyncCalibrationOverlay>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
syncScene = gameObject.scene;
|
|
||||||
returnScene = pendingReturnScene;
|
|
||||||
|
|
||||||
HideExistingCanvases();
|
|
||||||
BuildView();
|
|
||||||
tickClip = CreateTickClip();
|
|
||||||
audioSource = gameObject.AddComponent<AudioSource>();
|
|
||||||
audioSource.playOnAwake = false;
|
|
||||||
audioSource.spatialBlend = 0.0f;
|
|
||||||
|
|
||||||
float now = Time.unscaledTime;
|
|
||||||
lastTickTime = now;
|
|
||||||
nextTickTime = now + 0.8f;
|
|
||||||
UpdateTexts();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
RestoreCanvases();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
float now = Time.unscaledTime;
|
|
||||||
|
|
||||||
if (now >= nextTickTime)
|
|
||||||
{
|
|
||||||
lastTickTime = nextTickTime;
|
|
||||||
nextTickTime += TickInterval;
|
|
||||||
audioSource.PlayOneShot(tickClip);
|
|
||||||
pendingVisualPulseTime = lastTickTime + GlobalSyncSettings.AudioOffsetSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pendingVisualPulseTime > 0.0f && now >= pendingVisualPulseTime)
|
|
||||||
{
|
|
||||||
visualPulseTimer = 0.18f;
|
|
||||||
pendingVisualPulseTime = -1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateMetronomeVisual(now);
|
|
||||||
HandleInput(now);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleInput(float now)
|
|
||||||
{
|
|
||||||
bool primary = GetRightButton(XRCommonUsages.primaryButton) || IsKeyboardPressed(Key.Space);
|
|
||||||
bool secondary = GetRightButton(XRCommonUsages.secondaryButton) || IsKeyboardPressed(Key.Escape);
|
|
||||||
|
|
||||||
if (primary && !previousPrimary)
|
|
||||||
CaptureSample(now);
|
|
||||||
if (secondary && !previousSecondary)
|
|
||||||
Close();
|
|
||||||
|
|
||||||
previousPrimary = primary;
|
|
||||||
previousSecondary = secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CaptureSample(float now)
|
|
||||||
{
|
|
||||||
float nearestTick = Mathf.Abs(now - lastTickTime) <= Mathf.Abs(now - nextTickTime)
|
|
||||||
? lastTickTime
|
|
||||||
: nextTickTime;
|
|
||||||
|
|
||||||
float offsetMs = Mathf.Clamp((now - nearestTick) * 1000.0f, -300.0f, 300.0f);
|
|
||||||
samples.Add(offsetMs);
|
|
||||||
if (samples.Count > MaxSamples)
|
|
||||||
samples.RemoveAt(0);
|
|
||||||
|
|
||||||
float sum = 0.0f;
|
|
||||||
for (int i = 0; i < samples.Count; i++)
|
|
||||||
sum += samples[i];
|
|
||||||
|
|
||||||
GlobalSyncSettings.AudioOffsetMs = sum / samples.Count;
|
|
||||||
UpdateTexts();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AdjustOffset(float deltaMs)
|
|
||||||
{
|
|
||||||
GlobalSyncSettings.AudioOffsetMs += deltaMs;
|
|
||||||
samples.Clear();
|
|
||||||
UpdateTexts();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResetOffset()
|
|
||||||
{
|
|
||||||
GlobalSyncSettings.Reset();
|
|
||||||
samples.Clear();
|
|
||||||
UpdateTexts();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Close()
|
|
||||||
{
|
|
||||||
RestoreCanvases();
|
|
||||||
|
|
||||||
if (returnScene.IsValid() && returnScene.isLoaded)
|
|
||||||
SceneManager.SetActiveScene(returnScene);
|
|
||||||
|
|
||||||
if (syncScene.IsValid() && syncScene.isLoaded)
|
|
||||||
SceneManager.UnloadSceneAsync(syncScene);
|
|
||||||
else
|
|
||||||
Destroy(gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BuildView()
|
|
||||||
{
|
|
||||||
Camera camera = Camera.main ?? FindFirstObjectByType<Camera>();
|
|
||||||
|
|
||||||
GameObject canvasObject = new GameObject("SyncCalibrationCanvas");
|
|
||||||
canvasObject.transform.SetParent(transform, false);
|
|
||||||
Canvas canvas = canvasObject.AddComponent<Canvas>();
|
|
||||||
canvas.renderMode = RenderMode.WorldSpace;
|
|
||||||
canvas.sortingOrder = 400;
|
|
||||||
canvas.worldCamera = camera;
|
|
||||||
canvasObject.AddComponent<GraphicRaycaster>();
|
|
||||||
|
|
||||||
RectTransform canvasRect = canvasObject.GetComponent<RectTransform>();
|
|
||||||
canvasRect.sizeDelta = new Vector2(1080.0f, 660.0f);
|
|
||||||
canvasObject.transform.localScale = Vector3.one * 0.0028f;
|
|
||||||
|
|
||||||
if (camera != null)
|
|
||||||
{
|
|
||||||
Transform camTransform = camera.transform;
|
|
||||||
canvasObject.transform.position = camTransform.position + camTransform.forward * 1.9f;
|
|
||||||
canvasObject.transform.rotation = Quaternion.LookRotation(canvasObject.transform.position - camTransform.position, camTransform.up);
|
|
||||||
}
|
|
||||||
|
|
||||||
Image background = CreateImage("Panel", canvasRect, new Vector2(0, 0), new Vector2(1020, 620), new Color(0.02f, 0.06f, 0.09f, 0.92f));
|
|
||||||
background.raycastTarget = false;
|
|
||||||
|
|
||||||
TextMeshProUGUI title = CreateText("Title", canvasRect, "SYNC CALIBRATION", new Vector2(0, 260), new Vector2(920, 58), 42, Color.white, TextAlignmentOptions.Center);
|
|
||||||
title.fontStyle = FontStyles.Bold;
|
|
||||||
guideText = CreateText("Guide", canvasRect, "", new Vector2(0, 180), new Vector2(920, 92), 26, new Color(0.77f, 0.9f, 1.0f, 1.0f), TextAlignmentOptions.Center);
|
|
||||||
|
|
||||||
Image bar = CreateImage("BeatBar", canvasRect, new Vector2(0, 82), new Vector2(760, 10), new Color(0.25f, 0.85f, 1.0f, 0.28f));
|
|
||||||
bar.raycastTarget = false;
|
|
||||||
sweepDot = CreateImage("BeatDot", canvasRect, new Vector2(-380, 82), new Vector2(34, 34), new Color(0.35f, 0.95f, 1.0f, 1.0f)).rectTransform;
|
|
||||||
visualPulse = CreateImage("VisualPulse", canvasRect, new Vector2(0, 82), new Vector2(140, 140), new Color(0.35f, 0.95f, 1.0f, 0.0f)).rectTransform;
|
|
||||||
|
|
||||||
offsetText = CreateText("Offset", canvasRect, "", new Vector2(0, 0), new Vector2(720, 74), 54, Color.white, TextAlignmentOptions.Center);
|
|
||||||
offsetText.fontStyle = FontStyles.Bold;
|
|
||||||
sampleText = CreateText("Samples", canvasRect, "", new Vector2(0, -72), new Vector2(760, 44), 24, new Color(0.65f, 0.78f, 0.84f, 1.0f), TextAlignmentOptions.Center);
|
|
||||||
|
|
||||||
CreateButton(canvasRect, "-10ms", new Vector2(-300, -165), () => AdjustOffset(-10.0f));
|
|
||||||
CreateButton(canvasRect, "+10ms", new Vector2(-100, -165), () => AdjustOffset(10.0f));
|
|
||||||
CreateButton(canvasRect, "RESET", new Vector2(100, -165), ResetOffset);
|
|
||||||
CreateButton(canvasRect, "BACK", new Vector2(300, -165), Close);
|
|
||||||
|
|
||||||
CreateText("Footer", canvasRect, "A / Space: capture beat B / Esc: back", new Vector2(0, -270), new Vector2(860, 40), 22, new Color(0.58f, 0.7f, 0.75f, 1.0f), TextAlignmentOptions.Center);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateMetronomeVisual(float now)
|
|
||||||
{
|
|
||||||
float phase = Mathf.InverseLerp(lastTickTime, nextTickTime, now);
|
|
||||||
if (sweepDot != null)
|
|
||||||
sweepDot.anchoredPosition = new Vector2(Mathf.Lerp(-380.0f, 380.0f, phase), 82.0f);
|
|
||||||
|
|
||||||
if (visualPulse == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
visualPulseTimer = Mathf.Max(0.0f, visualPulseTimer - Time.unscaledDeltaTime);
|
|
||||||
float alpha = visualPulseTimer / 0.18f;
|
|
||||||
visualPulse.sizeDelta = Vector2.one * Mathf.Lerp(190.0f, 80.0f, alpha);
|
|
||||||
Image image = visualPulse.GetComponent<Image>();
|
|
||||||
if (image != null)
|
|
||||||
image.color = new Color(0.35f, 0.95f, 1.0f, alpha * 0.52f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateTexts()
|
|
||||||
{
|
|
||||||
float offset = GlobalSyncSettings.AudioOffsetMs;
|
|
||||||
if (offsetText != null)
|
|
||||||
offsetText.text = $"{offset:+0;-0;0} ms";
|
|
||||||
if (sampleText != null)
|
|
||||||
sampleText.text = $"samples {samples.Count}/{MaxSamples} global offset saved";
|
|
||||||
if (guideText != null)
|
|
||||||
guideText.text = "Tick 소리가 들리는 순간 A / Space를 누르세요.\n파란 원이 박자와 겹치면 보정이 맞습니다.";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HideExistingCanvases()
|
|
||||||
{
|
|
||||||
hiddenCanvases.Clear();
|
|
||||||
foreach (Canvas canvas in FindObjectsByType<Canvas>(FindObjectsSortMode.None))
|
|
||||||
{
|
|
||||||
hiddenCanvases.Add((canvas, canvas.enabled));
|
|
||||||
canvas.enabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RestoreCanvases()
|
|
||||||
{
|
|
||||||
if (canvasesRestored)
|
|
||||||
return;
|
|
||||||
|
|
||||||
canvasesRestored = true;
|
|
||||||
foreach ((Canvas canvas, bool enabled) in hiddenCanvases)
|
|
||||||
{
|
|
||||||
if (canvas != null)
|
|
||||||
canvas.enabled = enabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TextMeshProUGUI CreateText(string name, RectTransform parent, string value, Vector2 position, Vector2 size, int fontSize, Color color, TextAlignmentOptions alignment)
|
|
||||||
{
|
|
||||||
GameObject go = new GameObject(name);
|
|
||||||
go.transform.SetParent(parent, false);
|
|
||||||
RectTransform rect = go.AddComponent<RectTransform>();
|
|
||||||
rect.anchoredPosition = position;
|
|
||||||
rect.sizeDelta = size;
|
|
||||||
|
|
||||||
TextMeshProUGUI text = go.AddComponent<TextMeshProUGUI>();
|
|
||||||
text.text = value;
|
|
||||||
text.fontSize = fontSize;
|
|
||||||
text.color = color;
|
|
||||||
text.alignment = alignment;
|
|
||||||
text.textWrappingMode = TextWrappingModes.Normal;
|
|
||||||
text.overflowMode = TextOverflowModes.Overflow;
|
|
||||||
text.raycastTarget = false;
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Image CreateImage(string name, RectTransform parent, Vector2 position, Vector2 size, Color color)
|
|
||||||
{
|
|
||||||
GameObject go = new GameObject(name);
|
|
||||||
go.transform.SetParent(parent, false);
|
|
||||||
RectTransform rect = go.AddComponent<RectTransform>();
|
|
||||||
rect.anchoredPosition = position;
|
|
||||||
rect.sizeDelta = size;
|
|
||||||
|
|
||||||
Image image = go.AddComponent<Image>();
|
|
||||||
image.color = color;
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CreateButton(RectTransform parent, string label, Vector2 position, UnityEngine.Events.UnityAction action)
|
|
||||||
{
|
|
||||||
Image image = CreateImage(label + "Button", parent, position, new Vector2(168, 58), new Color(0.07f, 0.18f, 0.24f, 0.96f));
|
|
||||||
Button button = image.gameObject.AddComponent<Button>();
|
|
||||||
button.onClick.AddListener(action);
|
|
||||||
|
|
||||||
ColorBlock colors = button.colors;
|
|
||||||
colors.normalColor = new Color(0.07f, 0.18f, 0.24f, 0.96f);
|
|
||||||
colors.highlightedColor = new Color(0.13f, 0.38f, 0.48f, 1.0f);
|
|
||||||
colors.pressedColor = new Color(0.08f, 0.72f, 0.85f, 1.0f);
|
|
||||||
colors.selectedColor = colors.highlightedColor;
|
|
||||||
button.colors = colors;
|
|
||||||
|
|
||||||
TextMeshProUGUI labelText = CreateText(label + "Text", image.rectTransform, label, Vector2.zero, new Vector2(154, 48), 24, Color.white, TextAlignmentOptions.Center);
|
|
||||||
labelText.fontStyle = FontStyles.Bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool GetRightButton(InputFeatureUsage<bool> usage)
|
|
||||||
{
|
|
||||||
var devices = new List<XRInputDevice>();
|
|
||||||
InputDevices.GetDevicesWithCharacteristics(InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right, devices);
|
|
||||||
if (devices.Count == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
devices[0].TryGetFeatureValue(usage, out bool pressed);
|
|
||||||
return pressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsKeyboardPressed(Key key)
|
|
||||||
{
|
|
||||||
Keyboard keyboard = Keyboard.current;
|
|
||||||
return keyboard != null && keyboard[key].isPressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AudioClip CreateTickClip()
|
|
||||||
{
|
|
||||||
const int sampleRate = 48000;
|
|
||||||
const float duration = 0.055f;
|
|
||||||
int sampleCount = Mathf.CeilToInt(sampleRate * duration);
|
|
||||||
float[] data = new float[sampleCount];
|
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
|
||||||
{
|
|
||||||
float t = (float)i / sampleRate;
|
|
||||||
float envelope = Mathf.Exp(-t * 62.0f);
|
|
||||||
float high = Mathf.Sin(2.0f * Mathf.PI * 1760.0f * t);
|
|
||||||
float click = i < 80 ? 1.0f - (float)i / 80.0f : 0.0f;
|
|
||||||
data[i] = Mathf.Clamp((high * 0.75f + click * 0.35f) * envelope, -1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioClip clip = AudioClip.Create("SyncTick", sampleCount, 1, sampleRate, false);
|
|
||||||
clip.SetData(data, 0);
|
|
||||||
return clip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 90f8d18d467240d8bb84178048a5aa91
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,512 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityEngine.XR;
|
|
||||||
|
|
||||||
namespace VRBeats
|
|
||||||
{
|
|
||||||
[RequireComponent(typeof(LineRenderer))]
|
|
||||||
public class VRPointerController : MonoBehaviour
|
|
||||||
{
|
|
||||||
[SerializeField] private bool isRightHand = true;
|
|
||||||
[SerializeField] private float maxDistance = 50f;
|
|
||||||
[SerializeField] private bool debugLogging = false;
|
|
||||||
[SerializeField] private float scrollSpeed = 2.4f;
|
|
||||||
[SerializeField] private float scrollDeadZone = 0.15f;
|
|
||||||
[SerializeField] private float dragScrollSpeed = 1.25f;
|
|
||||||
[SerializeField] private float dragClickThreshold = 0.025f;
|
|
||||||
|
|
||||||
private LineRenderer _line;
|
|
||||||
private bool _prevTrigger;
|
|
||||||
private Selectable _currentHover;
|
|
||||||
private ScrollRect _dragScrollRect;
|
|
||||||
private Selectable _triggerPressSelectable;
|
|
||||||
private Vector2 _dragStartLocalPoint;
|
|
||||||
private float _dragStartNormalizedPosition;
|
|
||||||
private float _dragMaxNormalizedDelta;
|
|
||||||
|
|
||||||
private static readonly Color NormalColor = new Color(1f, 1f, 1f, 0.8f);
|
|
||||||
private static readonly Color HoverColor = new Color(0.3f, 0.8f, 1f, 1f);
|
|
||||||
|
|
||||||
private float _deviceLogTimer;
|
|
||||||
|
|
||||||
// 버튼별 이전 상태
|
|
||||||
private bool _prevGrip;
|
|
||||||
private bool _prevPrimary;
|
|
||||||
private bool _prevSecondary;
|
|
||||||
private bool _prevThumbstick;
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
_line = GetComponent<LineRenderer>();
|
|
||||||
_line.positionCount = 2;
|
|
||||||
_line.startWidth = 0.005f;
|
|
||||||
_line.endWidth = 0.001f;
|
|
||||||
_line.useWorldSpace = true;
|
|
||||||
// enabled 상태는 OnEnable/OnDisable이 관리 — Awake에서 건드리지 않음
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
// Awake 이후 reflection으로 isRightHand가 설정되므로 Start에서 로그
|
|
||||||
if (debugLogging)
|
|
||||||
Debug.Log($"[VRPointer] Start — {gameObject.name} / isRightHand={isRightHand}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEnable()
|
|
||||||
{
|
|
||||||
if (_line != null) _line.enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
if (_line != null) _line.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
// 3초마다 연결된 디바이스 목록 출력
|
|
||||||
if (debugLogging)
|
|
||||||
{
|
|
||||||
_deviceLogTimer += Time.deltaTime;
|
|
||||||
if (_deviceLogTimer >= 3f)
|
|
||||||
{
|
|
||||||
_deviceLogTimer = 0f;
|
|
||||||
LogConnectedDevices();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool trigger = GetButton(CommonUsages.triggerButton);
|
|
||||||
bool grip = GetButton(CommonUsages.gripButton);
|
|
||||||
bool primary = GetButton(CommonUsages.primaryButton);
|
|
||||||
bool secondary = GetButton(CommonUsages.secondaryButton);
|
|
||||||
bool thumbstick = GetButton(CommonUsages.primary2DAxisClick);
|
|
||||||
|
|
||||||
bool triggerDown = trigger && !_prevTrigger;
|
|
||||||
bool triggerUp = !trigger && _prevTrigger;
|
|
||||||
bool gripDown = grip && !_prevGrip;
|
|
||||||
bool primaryDown = primary && !_prevPrimary;
|
|
||||||
bool secondaryDown = secondary && !_prevSecondary;
|
|
||||||
bool thumbstickDown = thumbstick && !_prevThumbstick;
|
|
||||||
|
|
||||||
string hand = isRightHand ? "R" : "L";
|
|
||||||
if (debugLogging)
|
|
||||||
{
|
|
||||||
if (triggerDown) Debug.Log($"[VRPointer:{hand}] 검지 트리거 눌림");
|
|
||||||
if (gripDown) Debug.Log($"[VRPointer:{hand}] 그립(중지) 눌림");
|
|
||||||
if (primaryDown) Debug.Log($"[VRPointer:{hand}] {(isRightHand ? "A" : "X")} 버튼 눌림");
|
|
||||||
if (secondaryDown) Debug.Log($"[VRPointer:{hand}] {(isRightHand ? "B" : "Y")} 버튼 눌림");
|
|
||||||
if (thumbstickDown) Debug.Log($"[VRPointer:{hand}] 조이스틱 클릭 눌림");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ray ray = new Ray(transform.position, transform.forward);
|
|
||||||
float selectableHitDist = maxDistance;
|
|
||||||
float scrollHitDist = maxDistance;
|
|
||||||
|
|
||||||
Selectable hit = FindSelectableUnderRay(ray, ref selectableHitDist);
|
|
||||||
ScrollRect scrollRect = FindScrollRectUnderRay(ray, ref scrollHitDist);
|
|
||||||
float hitDist = Mathf.Min(selectableHitDist, scrollHitDist);
|
|
||||||
|
|
||||||
bool beganScrollDrag = false;
|
|
||||||
if (triggerDown)
|
|
||||||
beganScrollDrag = TryBeginScrollDrag(scrollRect, hit, ray);
|
|
||||||
|
|
||||||
if (_dragScrollRect != null && trigger)
|
|
||||||
UpdateScrollDrag(ray);
|
|
||||||
else if (_dragScrollRect == null)
|
|
||||||
HandleScroll(scrollRect);
|
|
||||||
|
|
||||||
// 호버 변화 로그
|
|
||||||
if (debugLogging && hit != _currentHover)
|
|
||||||
{
|
|
||||||
Debug.Log(hit != null
|
|
||||||
? $"[VRPointer] HOVER → {hit.gameObject.name}"
|
|
||||||
: $"[VRPointer] HOVER → (없음)");
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateHoverState(hit);
|
|
||||||
|
|
||||||
if (triggerUp && _dragScrollRect != null)
|
|
||||||
EndScrollDrag(hand, ray);
|
|
||||||
|
|
||||||
// 검지 트리거 또는 A/X 버튼으로 클릭.
|
|
||||||
// ScrollRect 위의 검지 트리거는 드래그/클릭 판별을 위해 release 시점에 처리한다.
|
|
||||||
if ((triggerDown && !beganScrollDrag) || primaryDown)
|
|
||||||
{
|
|
||||||
if (_currentHover != null)
|
|
||||||
{
|
|
||||||
string btn = triggerDown && !beganScrollDrag ? "검지 트리거" : (isRightHand ? "A" : "X");
|
|
||||||
if (debugLogging)
|
|
||||||
Debug.Log($"[VRPointer:{hand}] CLICK [{btn}] → {_currentHover.gameObject.name}");
|
|
||||||
Click(_currentHover);
|
|
||||||
}
|
|
||||||
else if (debugLogging)
|
|
||||||
{
|
|
||||||
Debug.Log($"[VRPointer:{hand}] CLICK — 레이 아래 버튼 없음 " +
|
|
||||||
$"pos={transform.position:F2} fwd={transform.forward:F2}");
|
|
||||||
DebugRaycastAttempt(new Ray(transform.position, transform.forward));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawLine(hitDist);
|
|
||||||
|
|
||||||
_prevTrigger = trigger;
|
|
||||||
_prevGrip = grip;
|
|
||||||
_prevPrimary = primary;
|
|
||||||
_prevSecondary = secondary;
|
|
||||||
_prevThumbstick = thumbstick;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LogConnectedDevices()
|
|
||||||
{
|
|
||||||
var all = new List<InputDevice>();
|
|
||||||
InputDevices.GetDevices(all);
|
|
||||||
|
|
||||||
if (all.Count == 0)
|
|
||||||
{
|
|
||||||
Debug.LogWarning($"[VRPointer] 연결된 XR 디바이스 없음 ({gameObject.name})");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var chars = InputDeviceCharacteristics.Controller |
|
|
||||||
(isRightHand ? InputDeviceCharacteristics.Right : InputDeviceCharacteristics.Left);
|
|
||||||
var matched = new List<InputDevice>();
|
|
||||||
InputDevices.GetDevicesWithCharacteristics(chars, matched);
|
|
||||||
|
|
||||||
Debug.Log($"[VRPointer] 전체 디바이스 {all.Count}개 | " +
|
|
||||||
$"{(isRightHand ? "오른손" : "왼손")} 컨트롤러 {matched.Count}개 ({gameObject.name})");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateHoverState(Selectable hit)
|
|
||||||
{
|
|
||||||
if (hit == _currentHover) return;
|
|
||||||
|
|
||||||
var es = EventSystem.current;
|
|
||||||
if (_currentHover != null)
|
|
||||||
ExecuteEvents.Execute(_currentHover.gameObject,
|
|
||||||
new PointerEventData(es), ExecuteEvents.pointerExitHandler);
|
|
||||||
|
|
||||||
_currentHover = hit;
|
|
||||||
|
|
||||||
if (_currentHover != null)
|
|
||||||
ExecuteEvents.Execute(_currentHover.gameObject,
|
|
||||||
new PointerEventData(es), ExecuteEvents.pointerEnterHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Click(Selectable sel)
|
|
||||||
{
|
|
||||||
var es = EventSystem.current;
|
|
||||||
if (es == null)
|
|
||||||
{
|
|
||||||
Debug.LogWarning("[VRPointer] EventSystem.current is null — click 실패");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var eventData = new PointerEventData(es);
|
|
||||||
ExecuteEvents.Execute(sel.gameObject, eventData, ExecuteEvents.pointerDownHandler);
|
|
||||||
ExecuteEvents.Execute(sel.gameObject, eventData, ExecuteEvents.pointerUpHandler);
|
|
||||||
ExecuteEvents.Execute(sel.gameObject, eventData, ExecuteEvents.pointerClickHandler);
|
|
||||||
|
|
||||||
var btn = sel.GetComponent<Button>();
|
|
||||||
if (btn != null) btn.onClick.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawLine(float hitDist)
|
|
||||||
{
|
|
||||||
Color c = _currentHover != null ? HoverColor : NormalColor;
|
|
||||||
_line.startColor = c;
|
|
||||||
_line.endColor = new Color(c.r, c.g, c.b, 0f);
|
|
||||||
_line.SetPosition(0, transform.position);
|
|
||||||
_line.SetPosition(1, transform.position + transform.forward * hitDist);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DebugRaycastAttempt(Ray ray)
|
|
||||||
{
|
|
||||||
var all = Selectable.allSelectablesArray;
|
|
||||||
Debug.Log($"[VRPointer:DEBUG] Selectable 총 {all.Length}개 | ray.origin={ray.origin:F2} ray.dir={ray.direction:F2}");
|
|
||||||
|
|
||||||
foreach (Selectable sel in all)
|
|
||||||
{
|
|
||||||
if (!sel.gameObject.activeInHierarchy) { Debug.Log($" SKIP(비활성) {sel.gameObject.name}"); continue; }
|
|
||||||
if (!sel.interactable) { Debug.Log($" SKIP(interactable=false) {sel.gameObject.name}"); continue; }
|
|
||||||
|
|
||||||
var rt = sel.GetComponent<RectTransform>();
|
|
||||||
if (rt == null) { Debug.Log($" SKIP(RectTransform없음) {sel.gameObject.name}"); continue; }
|
|
||||||
|
|
||||||
Vector3[] c = new Vector3[4];
|
|
||||||
rt.GetWorldCorners(c);
|
|
||||||
|
|
||||||
Vector3 normal = rt.forward;
|
|
||||||
if (Vector3.Dot(normal, ray.direction) >= 0f) normal = -normal;
|
|
||||||
|
|
||||||
Plane plane = new Plane(normal, c[0]);
|
|
||||||
bool hit = plane.Raycast(ray, out float dist);
|
|
||||||
if (!hit) { Debug.Log($" MISS(Plane.Raycast=false) {sel.gameObject.name} | rtPos={rt.position:F2} normal={normal:F2}"); continue; }
|
|
||||||
|
|
||||||
bool inRect = IsPointInRect(ray.GetPoint(dist), c);
|
|
||||||
Debug.Log($" {(inRect ? "HIT" : "MISS(InRect=false)")} {sel.gameObject.name} | dist={dist:F2} inRect={inRect}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Selectable FindSelectableUnderRay(Ray ray, ref float maxDist)
|
|
||||||
{
|
|
||||||
Selectable closest = null;
|
|
||||||
float closestDist = maxDist;
|
|
||||||
|
|
||||||
foreach (Selectable sel in Selectable.allSelectablesArray)
|
|
||||||
{
|
|
||||||
if (!sel.gameObject.activeInHierarchy || !sel.interactable) continue;
|
|
||||||
if (!IsOnEnabledCanvas(sel)) continue;
|
|
||||||
|
|
||||||
var rt = sel.GetComponent<RectTransform>();
|
|
||||||
if (rt == null) continue;
|
|
||||||
|
|
||||||
Vector3[] corners = new Vector3[4];
|
|
||||||
rt.GetWorldCorners(corners);
|
|
||||||
|
|
||||||
// Plane.Raycast은 normal 쪽(앞면)에서 레이가 출발해야 true 반환.
|
|
||||||
// rt.forward가 카메라 반대를 향할 수 있으므로 레이 원점 방향으로 노말 보정.
|
|
||||||
Vector3 normal = rt.forward;
|
|
||||||
if (Vector3.Dot(normal, ray.direction) >= 0f)
|
|
||||||
normal = -normal;
|
|
||||||
|
|
||||||
Plane plane = new Plane(normal, corners[0]);
|
|
||||||
if (!plane.Raycast(ray, out float dist)) continue;
|
|
||||||
if (dist >= closestDist || dist <= 0f) continue;
|
|
||||||
|
|
||||||
if (!IsPointInRect(ray.GetPoint(dist), corners)) continue;
|
|
||||||
|
|
||||||
closestDist = dist;
|
|
||||||
closest = sel;
|
|
||||||
}
|
|
||||||
|
|
||||||
maxDist = closestDist;
|
|
||||||
return closest;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsPointInRect(Vector3 p, Vector3[] c)
|
|
||||||
{
|
|
||||||
Vector3 toP = p - c[0];
|
|
||||||
Vector3 right = c[3] - c[0];
|
|
||||||
Vector3 up = c[1] - c[0];
|
|
||||||
|
|
||||||
float r = Vector3.Dot(toP, right) / right.sqrMagnitude;
|
|
||||||
float u = Vector3.Dot(toP, up) / up.sqrMagnitude;
|
|
||||||
return r >= 0f && r <= 1f && u >= 0f && u <= 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ScrollRect FindScrollRectUnderRay(Ray ray, ref float maxDist)
|
|
||||||
{
|
|
||||||
ScrollRect closest = null;
|
|
||||||
float closestDist = maxDist;
|
|
||||||
var all = Object.FindObjectsByType<ScrollRect>(FindObjectsSortMode.None);
|
|
||||||
|
|
||||||
foreach (ScrollRect scroll in all)
|
|
||||||
{
|
|
||||||
if (!scroll.isActiveAndEnabled) continue;
|
|
||||||
if (!IsOnEnabledCanvas(scroll)) continue;
|
|
||||||
|
|
||||||
RectTransform rt = scroll.viewport != null
|
|
||||||
? scroll.viewport
|
|
||||||
: scroll.GetComponent<RectTransform>();
|
|
||||||
if (rt == null) continue;
|
|
||||||
|
|
||||||
Vector3[] corners = new Vector3[4];
|
|
||||||
rt.GetWorldCorners(corners);
|
|
||||||
|
|
||||||
Vector3 normal = rt.forward;
|
|
||||||
if (Vector3.Dot(normal, ray.direction) >= 0f)
|
|
||||||
normal = -normal;
|
|
||||||
|
|
||||||
Plane plane = new Plane(normal, corners[0]);
|
|
||||||
if (!plane.Raycast(ray, out float dist)) continue;
|
|
||||||
if (dist >= closestDist || dist <= 0f) continue;
|
|
||||||
if (!IsPointInRect(ray.GetPoint(dist), corners)) continue;
|
|
||||||
|
|
||||||
closestDist = dist;
|
|
||||||
closest = scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closest != null)
|
|
||||||
maxDist = closestDist;
|
|
||||||
|
|
||||||
return closest;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsOnEnabledCanvas(Component component)
|
|
||||||
{
|
|
||||||
Canvas[] canvases = component.GetComponentsInParent<Canvas>(true);
|
|
||||||
if (canvases.Length == 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for (int i = 0; i < canvases.Length; i++)
|
|
||||||
{
|
|
||||||
Canvas canvas = canvases[i];
|
|
||||||
if (canvas == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!canvas.enabled || !canvas.gameObject.activeInHierarchy)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleScroll(ScrollRect scrollRect)
|
|
||||||
{
|
|
||||||
if (!CanScrollVertically(scrollRect))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Vector2 axis = GetAxis(CommonUsages.primary2DAxis);
|
|
||||||
if (Mathf.Abs(axis.y) < scrollDeadZone)
|
|
||||||
return;
|
|
||||||
|
|
||||||
scrollRect.verticalNormalizedPosition = Mathf.Clamp01(
|
|
||||||
scrollRect.verticalNormalizedPosition + axis.y * scrollSpeed * Time.deltaTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryBeginScrollDrag(ScrollRect scrollRect, Selectable pressSelectable, Ray ray)
|
|
||||||
{
|
|
||||||
if (!CanScrollVertically(scrollRect))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!TryGetScrollLocalPoint(scrollRect, ray, out Vector2 localPoint, out _))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_dragScrollRect = scrollRect;
|
|
||||||
_triggerPressSelectable = pressSelectable;
|
|
||||||
_dragStartLocalPoint = localPoint;
|
|
||||||
_dragStartNormalizedPosition = scrollRect.verticalNormalizedPosition;
|
|
||||||
_dragMaxNormalizedDelta = 0f;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateScrollDrag(Ray ray)
|
|
||||||
{
|
|
||||||
if (_dragScrollRect == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryGetScrollLocalPoint(_dragScrollRect, ray, out Vector2 localPoint, out float viewportHeight))
|
|
||||||
return;
|
|
||||||
|
|
||||||
float deltaY = localPoint.y - _dragStartLocalPoint.y;
|
|
||||||
float normalizedDelta = deltaY / viewportHeight * dragScrollSpeed;
|
|
||||||
_dragMaxNormalizedDelta = Mathf.Max(_dragMaxNormalizedDelta, Mathf.Abs(normalizedDelta));
|
|
||||||
|
|
||||||
_dragScrollRect.verticalNormalizedPosition = Mathf.Clamp01(
|
|
||||||
_dragStartNormalizedPosition - normalizedDelta);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EndScrollDrag(string hand, Ray ray)
|
|
||||||
{
|
|
||||||
bool shouldClick = _dragMaxNormalizedDelta < dragClickThreshold;
|
|
||||||
ScrollRect scrollRect = _dragScrollRect;
|
|
||||||
Selectable pressSelectable = _triggerPressSelectable;
|
|
||||||
float startNormalizedPosition = _dragStartNormalizedPosition;
|
|
||||||
|
|
||||||
ClearScrollDrag();
|
|
||||||
|
|
||||||
if (!shouldClick)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (scrollRect != null)
|
|
||||||
scrollRect.verticalNormalizedPosition = startNormalizedPosition;
|
|
||||||
|
|
||||||
if (pressSelectable != null && pressSelectable.isActiveAndEnabled && pressSelectable.interactable)
|
|
||||||
{
|
|
||||||
if (debugLogging)
|
|
||||||
Debug.Log($"[VRPointer:{hand}] CLICK [검지 트리거] → {pressSelectable.gameObject.name}");
|
|
||||||
Click(pressSelectable);
|
|
||||||
}
|
|
||||||
else if (debugLogging)
|
|
||||||
{
|
|
||||||
Debug.Log($"[VRPointer:{hand}] CLICK — 레이 아래 버튼 없음 " +
|
|
||||||
$"pos={transform.position:F2} fwd={transform.forward:F2}");
|
|
||||||
DebugRaycastAttempt(ray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearScrollDrag()
|
|
||||||
{
|
|
||||||
_dragScrollRect = null;
|
|
||||||
_triggerPressSelectable = null;
|
|
||||||
_dragStartLocalPoint = Vector2.zero;
|
|
||||||
_dragStartNormalizedPosition = 0f;
|
|
||||||
_dragMaxNormalizedDelta = 0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CanScrollVertically(ScrollRect scrollRect)
|
|
||||||
{
|
|
||||||
if (scrollRect == null || !scrollRect.vertical)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RectTransform viewport = scrollRect.viewport != null
|
|
||||||
? scrollRect.viewport
|
|
||||||
: scrollRect.GetComponent<RectTransform>();
|
|
||||||
|
|
||||||
if (viewport == null || scrollRect.content == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return scrollRect.content.rect.height > viewport.rect.height + 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetScrollLocalPoint(ScrollRect scrollRect, Ray ray, out Vector2 localPoint, out float viewportHeight)
|
|
||||||
{
|
|
||||||
localPoint = Vector2.zero;
|
|
||||||
viewportHeight = 1f;
|
|
||||||
|
|
||||||
RectTransform rt = scrollRect.viewport != null
|
|
||||||
? scrollRect.viewport
|
|
||||||
: scrollRect.GetComponent<RectTransform>();
|
|
||||||
if (rt == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Vector3[] corners = new Vector3[4];
|
|
||||||
rt.GetWorldCorners(corners);
|
|
||||||
|
|
||||||
Vector3 normal = rt.forward;
|
|
||||||
if (Vector3.Dot(normal, ray.direction) >= 0f)
|
|
||||||
normal = -normal;
|
|
||||||
|
|
||||||
Plane plane = new Plane(normal, corners[0]);
|
|
||||||
if (!plane.Raycast(ray, out float dist) || dist <= 0f)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Vector3 local = rt.InverseTransformPoint(ray.GetPoint(dist));
|
|
||||||
localPoint = new Vector2(local.x, local.y);
|
|
||||||
viewportHeight = Mathf.Max(1f, rt.rect.height);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool GetButton(InputFeatureUsage<bool> usage)
|
|
||||||
{
|
|
||||||
var chars = InputDeviceCharacteristics.Controller |
|
|
||||||
(isRightHand
|
|
||||||
? InputDeviceCharacteristics.Right
|
|
||||||
: InputDeviceCharacteristics.Left);
|
|
||||||
|
|
||||||
var devices = new List<InputDevice>();
|
|
||||||
InputDevices.GetDevicesWithCharacteristics(chars, devices);
|
|
||||||
if (devices.Count == 0) return false;
|
|
||||||
|
|
||||||
devices[0].TryGetFeatureValue(usage, out bool pressed);
|
|
||||||
return pressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector2 GetAxis(InputFeatureUsage<Vector2> usage)
|
|
||||||
{
|
|
||||||
var chars = InputDeviceCharacteristics.Controller |
|
|
||||||
(isRightHand
|
|
||||||
? InputDeviceCharacteristics.Right
|
|
||||||
: InputDeviceCharacteristics.Left);
|
|
||||||
|
|
||||||
var devices = new List<InputDevice>();
|
|
||||||
InputDevices.GetDevicesWithCharacteristics(chars, devices);
|
|
||||||
if (devices.Count == 0) return Vector2.zero;
|
|
||||||
|
|
||||||
devices[0].TryGetFeatureValue(usage, out Vector2 axis);
|
|
||||||
return axis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: eec74ab726176d74491e9be716a7a609
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
|
|
||||||
namespace VRBeats
|
|
||||||
{
|
|
||||||
// 모든 씬에서 자동 실행.
|
|
||||||
// Game 씬: VRPointerController를 비활성 상태로 추가 → VR_InteractorController가 게임오버 시 활성화.
|
|
||||||
// 나머지 씬: 바로 활성 상태로 추가.
|
|
||||||
public class VRPointerSetup : MonoBehaviour
|
|
||||||
{
|
|
||||||
private static VRPointerSetup instance;
|
|
||||||
|
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
|
||||||
private static void ResetStatics()
|
|
||||||
{
|
|
||||||
instance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
|
||||||
private static void AutoInject()
|
|
||||||
{
|
|
||||||
if (instance != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var go = new GameObject("[VRPointerSetup]");
|
|
||||||
go.AddComponent<VRPointerSetup>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
if (instance != null && instance != this)
|
|
||||||
{
|
|
||||||
Destroy(gameObject);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
instance = this;
|
|
||||||
DontDestroyOnLoad(gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEnable()
|
|
||||||
{
|
|
||||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
SetupActiveScene();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
||||||
{
|
|
||||||
SetupScene(scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetupActiveScene()
|
|
||||||
{
|
|
||||||
SetupScene(SceneManager.GetActiveScene());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetupScene(Scene scene)
|
|
||||||
{
|
|
||||||
bool isGameScene = scene.name == "Game";
|
|
||||||
SetupControllers(disabledByDefault: isGameScene);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetupControllers(bool disabledByDefault)
|
|
||||||
{
|
|
||||||
foreach (var go in FindObjectsByType<GameObject>(FindObjectsSortMode.None))
|
|
||||||
{
|
|
||||||
string name = go.name;
|
|
||||||
bool isRight = name.Contains("Right");
|
|
||||||
bool isLeft = name.Contains("Left");
|
|
||||||
|
|
||||||
if (!isRight && !isLeft) continue;
|
|
||||||
if (!name.Contains("Controller") && !name.Contains("Hand")) continue;
|
|
||||||
if (go.GetComponent<LineRenderer>() == null) continue;
|
|
||||||
|
|
||||||
DisableToolkitPointerComponents(go);
|
|
||||||
|
|
||||||
if (go.GetComponent<VRPointerController>() != null) continue;
|
|
||||||
|
|
||||||
var pointer = go.AddComponent<VRPointerController>();
|
|
||||||
|
|
||||||
var field = typeof(VRPointerController)
|
|
||||||
.GetField("isRightHand",
|
|
||||||
System.Reflection.BindingFlags.NonPublic |
|
|
||||||
System.Reflection.BindingFlags.Instance);
|
|
||||||
field?.SetValue(pointer, isRight);
|
|
||||||
|
|
||||||
// Game 씬에서는 게임오버 전까지 비활성
|
|
||||||
if (disabledByDefault)
|
|
||||||
pointer.enabled = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DisableToolkitPointerComponents(GameObject go)
|
|
||||||
{
|
|
||||||
var rayInteractor = go.GetComponent<UnityEngine.XR.Interaction.Toolkit.Interactors.XRRayInteractor>();
|
|
||||||
if (rayInteractor != null)
|
|
||||||
rayInteractor.enabled = false;
|
|
||||||
|
|
||||||
var lineVisual = go.GetComponent<UnityEngine.XR.Interaction.Toolkit.Interactors.Visuals.XRInteractorLineVisual>();
|
|
||||||
if (lineVisual != null)
|
|
||||||
lineVisual.enabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 38f89babd4e99734aac47edbc4f87aa3
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
// Automatically spawns the XR Interaction Simulator when running in the Editor or on PC.
|
|
||||||
// Add this to any persistent GameObject in the Menu scene (e.g. VR_Manager).
|
|
||||||
//
|
|
||||||
// Setup:
|
|
||||||
// 1. Attach this script to VR_Manager (or any root object) in Menu.unity
|
|
||||||
// 2. In Package Manager → XR Interaction Toolkit → Samples → import "XR Interaction Simulator"
|
|
||||||
// 3. Drag the imported prefab into the SimulatorPrefab field:
|
|
||||||
// Assets/Samples/XR Interaction Toolkit/<version>/XR Interaction Simulator/XR Interaction Simulator.prefab
|
|
||||||
//
|
|
||||||
// Controls (XR Interaction Simulator):
|
|
||||||
// Right-click drag — rotate head
|
|
||||||
// G + mouse move — move right controller
|
|
||||||
// Shift+G — left controller
|
|
||||||
// Space — trigger (UI click)
|
|
||||||
public class XRSimulatorLoader : MonoBehaviour
|
|
||||||
{
|
|
||||||
[SerializeField] private GameObject simulatorPrefab;
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
#if !UNITY_ANDROID || UNITY_EDITOR
|
|
||||||
if (simulatorPrefab != null)
|
|
||||||
Instantiate(simulatorPrefab);
|
|
||||||
else
|
|
||||||
Debug.LogWarning("[XRSimulatorLoader] simulatorPrefab is not assigned.\n" +
|
|
||||||
"Import the XR Interaction Simulator sample via Package Manager and assign the prefab.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 39a535ec9b2d18e489709431e0c25086
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 709f11a7f3c4041caa4ef136ea32d874
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,893 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &-9167874883656233139
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 5485954d14dfb9a4c8ead8edb0ded5b1, type: 3}
|
|
||||||
m_Name: LiftGammaGain
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
lift:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 1, y: 1, z: 1, w: 0}
|
|
||||||
gamma:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 1, y: 1, z: 1, w: 0}
|
|
||||||
gain:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 1, y: 1, z: 1, w: 0}
|
|
||||||
--- !u!114 &-8270506406425502121
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 70afe9e12c7a7ed47911bb608a23a8ff, type: 3}
|
|
||||||
m_Name: SplitToning
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
shadows:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
|
||||||
highlights:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
|
||||||
balance:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
--- !u!114 &-7750755424749557576
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 60f3b30c03e6ba64d9a27dc9dba8f28d, type: 3}
|
|
||||||
m_Name: OutlineVolumeComponent
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
Enabled:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
--- !u!114 &-7743500325797982168
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: ccf1aba9553839d41ae37dd52e9ebcce, type: 3}
|
|
||||||
m_Name: MotionBlur
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
mode:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
quality:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
intensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
clamp:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.05
|
|
||||||
--- !u!114 &-7274224791359825572
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 0fd9ee276a1023e439cf7a9c393195fa, type: 3}
|
|
||||||
m_Name: TestAnimationCurveVolumeComponent
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
testParameter:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Curve:
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 0.5
|
|
||||||
value: 10
|
|
||||||
inSlope: 0
|
|
||||||
outSlope: 10
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 1
|
|
||||||
value: 15
|
|
||||||
inSlope: 10
|
|
||||||
outSlope: 0
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
m_PreInfinity: 2
|
|
||||||
m_PostInfinity: 2
|
|
||||||
m_RotationOrder: 4
|
|
||||||
--- !u!114 &-6335409530604852063
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 66f335fb1ffd8684294ad653bf1c7564, type: 3}
|
|
||||||
m_Name: ColorAdjustments
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
postExposure:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
contrast:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
colorFilter:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
hueShift:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
saturation:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
--- !u!114 &-6288072647309666549
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 29fa0085f50d5e54f8144f766051a691, type: 3}
|
|
||||||
m_Name: FilmGrain
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
type:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
intensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
response:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.8
|
|
||||||
texture:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {fileID: 0}
|
|
||||||
--- !u!114 &-5520245016509672950
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 97c23e3b12dc18c42a140437e53d3951, type: 3}
|
|
||||||
m_Name: Tonemapping
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
mode:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
neutralHDRRangeReductionMode:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 2
|
|
||||||
acesPreset:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 3
|
|
||||||
hueShiftAmount:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
detectPaperWhite:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
paperWhite:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 300
|
|
||||||
detectBrightnessLimits:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
minNits:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.005
|
|
||||||
maxNits:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1000
|
|
||||||
--- !u!114 &-5139089513906902183
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 5a00a63fdd6bd2a45ab1f2d869305ffd, type: 3}
|
|
||||||
m_Name: OasisFogVolumeComponent
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
Density:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
StartDistance:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
HeightRange:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 0, y: 50}
|
|
||||||
Tint:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
SunScatteringIntensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 2
|
|
||||||
--- !u!114 &-4463884970436517307
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: fb60a22f311433c4c962b888d1393f88, type: 3}
|
|
||||||
m_Name: PaniniProjection
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
distance:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
cropToFit:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
--- !u!114 &-1410297666881709256
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 6bd486065ce11414fa40e631affc4900, type: 3}
|
|
||||||
m_Name: ProbeVolumesOptions
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
normalBias:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.33
|
|
||||||
viewBias:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
scaleBiasWithMinProbeDistance:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
samplingNoise:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.1
|
|
||||||
animateSamplingNoise:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
leakReductionMode:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
minValidDotProductValue:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.1
|
|
||||||
occlusionOnlyReflectionNormalization:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
intensityMultiplier:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
skyOcclusionIntensityMultiplier:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
--- !u!114 &-1216621516061285780
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 0b2db86121404754db890f4c8dfe81b2, type: 3}
|
|
||||||
m_Name: Bloom
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
skipIterations:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
threshold:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.9
|
|
||||||
intensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
scatter:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.7
|
|
||||||
clamp:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 65472
|
|
||||||
tint:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
highQualityFiltering:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
downscale:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
maxIterations:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 6
|
|
||||||
dirtTexture:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {fileID: 0}
|
|
||||||
dimension: 1
|
|
||||||
dirtIntensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
--- !u!114 &-1170528603972255243
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 221518ef91623a7438a71fef23660601, type: 3}
|
|
||||||
m_Name: WhiteBalance
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
temperature:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
tint:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
|
|
||||||
m_Name: DefaultVolumeProfile
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
components:
|
|
||||||
- {fileID: -9167874883656233139}
|
|
||||||
- {fileID: 1918650496244738858}
|
|
||||||
- {fileID: 853819529557874667}
|
|
||||||
- {fileID: 1052315754049611418}
|
|
||||||
- {fileID: -1170528603972255243}
|
|
||||||
- {fileID: -8270506406425502121}
|
|
||||||
- {fileID: -5520245016509672950}
|
|
||||||
- {fileID: 7173750748008157695}
|
|
||||||
- {fileID: 1666464333004379222}
|
|
||||||
- {fileID: 9001657382290151224}
|
|
||||||
- {fileID: -6335409530604852063}
|
|
||||||
- {fileID: -1216621516061285780}
|
|
||||||
- {fileID: 3959858460715838825}
|
|
||||||
- {fileID: -7743500325797982168}
|
|
||||||
- {fileID: 4644742534064026673}
|
|
||||||
- {fileID: -4463884970436517307}
|
|
||||||
- {fileID: -6288072647309666549}
|
|
||||||
- {fileID: 7518938298396184218}
|
|
||||||
- {fileID: -1410297666881709256}
|
|
||||||
- {fileID: -7750755424749557576}
|
|
||||||
- {fileID: -5139089513906902183}
|
|
||||||
--- !u!114 &853819529557874667
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 06437c1ff663d574d9447842ba0a72e4, type: 3}
|
|
||||||
m_Name: ScreenSpaceLensFlare
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
intensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
tintColor:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
bloomMip:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
firstFlareIntensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
secondaryFlareIntensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
warpedFlareIntensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
warpedFlareScale:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 1, y: 1}
|
|
||||||
samples:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
sampleDimmer:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.5
|
|
||||||
vignetteEffect:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
startingPosition:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1.25
|
|
||||||
scale:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1.5
|
|
||||||
streaksIntensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
streaksLength:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.5
|
|
||||||
streaksOrientation:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
streaksThreshold:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.25
|
|
||||||
resolution:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 4
|
|
||||||
chromaticAbberationIntensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.5
|
|
||||||
--- !u!114 &1052315754049611418
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 558a8e2b6826cf840aae193990ba9f2e, type: 3}
|
|
||||||
m_Name: ShadowsMidtonesHighlights
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
shadows:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 1, y: 1, z: 1, w: 0}
|
|
||||||
midtones:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 1, y: 1, z: 1, w: 0}
|
|
||||||
highlights:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 1, y: 1, z: 1, w: 0}
|
|
||||||
shadowsStart:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
shadowsEnd:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.3
|
|
||||||
highlightsStart:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.55
|
|
||||||
highlightsEnd:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
--- !u!114 &1666464333004379222
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 3eb4b772797da9440885e8bd939e9560, type: 3}
|
|
||||||
m_Name: ColorCurves
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
master:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value:
|
|
||||||
<length>k__BackingField: 2
|
|
||||||
m_Loop: 0
|
|
||||||
m_ZeroValue: 0
|
|
||||||
m_Range: 1
|
|
||||||
m_Curve:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Curve:
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 0
|
|
||||||
value: 0
|
|
||||||
inSlope: 1
|
|
||||||
outSlope: 1
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 1
|
|
||||||
value: 1
|
|
||||||
inSlope: 1
|
|
||||||
outSlope: 1
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
m_PreInfinity: 2
|
|
||||||
m_PostInfinity: 2
|
|
||||||
m_RotationOrder: 4
|
|
||||||
red:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value:
|
|
||||||
<length>k__BackingField: 2
|
|
||||||
m_Loop: 0
|
|
||||||
m_ZeroValue: 0
|
|
||||||
m_Range: 1
|
|
||||||
m_Curve:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Curve:
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 0
|
|
||||||
value: 0
|
|
||||||
inSlope: 1
|
|
||||||
outSlope: 1
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 1
|
|
||||||
value: 1
|
|
||||||
inSlope: 1
|
|
||||||
outSlope: 1
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
m_PreInfinity: 2
|
|
||||||
m_PostInfinity: 2
|
|
||||||
m_RotationOrder: 4
|
|
||||||
green:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value:
|
|
||||||
<length>k__BackingField: 2
|
|
||||||
m_Loop: 0
|
|
||||||
m_ZeroValue: 0
|
|
||||||
m_Range: 1
|
|
||||||
m_Curve:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Curve:
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 0
|
|
||||||
value: 0
|
|
||||||
inSlope: 1
|
|
||||||
outSlope: 1
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 1
|
|
||||||
value: 1
|
|
||||||
inSlope: 1
|
|
||||||
outSlope: 1
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
m_PreInfinity: 2
|
|
||||||
m_PostInfinity: 2
|
|
||||||
m_RotationOrder: 4
|
|
||||||
blue:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value:
|
|
||||||
<length>k__BackingField: 2
|
|
||||||
m_Loop: 0
|
|
||||||
m_ZeroValue: 0
|
|
||||||
m_Range: 1
|
|
||||||
m_Curve:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Curve:
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 0
|
|
||||||
value: 0
|
|
||||||
inSlope: 1
|
|
||||||
outSlope: 1
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
- serializedVersion: 3
|
|
||||||
time: 1
|
|
||||||
value: 1
|
|
||||||
inSlope: 1
|
|
||||||
outSlope: 1
|
|
||||||
tangentMode: 0
|
|
||||||
weightedMode: 0
|
|
||||||
inWeight: 0
|
|
||||||
outWeight: 0
|
|
||||||
m_PreInfinity: 2
|
|
||||||
m_PostInfinity: 2
|
|
||||||
m_RotationOrder: 4
|
|
||||||
hueVsHue:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value:
|
|
||||||
<length>k__BackingField: 0
|
|
||||||
m_Loop: 1
|
|
||||||
m_ZeroValue: 0.5
|
|
||||||
m_Range: 1
|
|
||||||
m_Curve:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Curve: []
|
|
||||||
m_PreInfinity: 2
|
|
||||||
m_PostInfinity: 2
|
|
||||||
m_RotationOrder: 4
|
|
||||||
hueVsSat:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value:
|
|
||||||
<length>k__BackingField: 0
|
|
||||||
m_Loop: 1
|
|
||||||
m_ZeroValue: 0.5
|
|
||||||
m_Range: 1
|
|
||||||
m_Curve:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Curve: []
|
|
||||||
m_PreInfinity: 2
|
|
||||||
m_PostInfinity: 2
|
|
||||||
m_RotationOrder: 4
|
|
||||||
satVsSat:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value:
|
|
||||||
<length>k__BackingField: 0
|
|
||||||
m_Loop: 0
|
|
||||||
m_ZeroValue: 0.5
|
|
||||||
m_Range: 1
|
|
||||||
m_Curve:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Curve: []
|
|
||||||
m_PreInfinity: 2
|
|
||||||
m_PostInfinity: 2
|
|
||||||
m_RotationOrder: 4
|
|
||||||
lumVsSat:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value:
|
|
||||||
<length>k__BackingField: 0
|
|
||||||
m_Loop: 0
|
|
||||||
m_ZeroValue: 0.5
|
|
||||||
m_Range: 1
|
|
||||||
m_Curve:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Curve: []
|
|
||||||
m_PreInfinity: 2
|
|
||||||
m_PostInfinity: 2
|
|
||||||
m_RotationOrder: 4
|
|
||||||
--- !u!114 &1918650496244738858
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: e021b4c809a781e468c2988c016ebbea, type: 3}
|
|
||||||
m_Name: ColorLookup
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
texture:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {fileID: 0}
|
|
||||||
dimension: 1
|
|
||||||
contribution:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
--- !u!114 &3959858460715838825
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: c01700fd266d6914ababb731e09af2eb, type: 3}
|
|
||||||
m_Name: DepthOfField
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
mode:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
gaussianStart:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 10
|
|
||||||
gaussianEnd:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 30
|
|
||||||
gaussianMaxRadius:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
highQualitySampling:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
focusDistance:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 10
|
|
||||||
aperture:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 5.6
|
|
||||||
focalLength:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 50
|
|
||||||
bladeCount:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 5
|
|
||||||
bladeCurvature:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
bladeRotation:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
--- !u!114 &4251301726029935498
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 74955a4b0b4243bc87231e8b59ed9140, type: 3}
|
|
||||||
m_Name: TestVolume
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
param:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 123
|
|
||||||
--- !u!114 &4644742534064026673
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 81180773991d8724ab7f2d216912b564, type: 3}
|
|
||||||
m_Name: ChromaticAberration
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
intensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
--- !u!114 &7173750748008157695
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 899c54efeace73346a0a16faa3afe726, type: 3}
|
|
||||||
m_Name: Vignette
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
color:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {r: 0, g: 0, b: 0, a: 1}
|
|
||||||
center:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 0.5, y: 0.5}
|
|
||||||
intensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
smoothness:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0.2
|
|
||||||
rounded:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
--- !u!114 &7518938298396184218
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: c5e1dc532bcb41949b58bc4f2abfbb7e, type: 3}
|
|
||||||
m_Name: LensDistortion
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
intensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
xMultiplier:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
yMultiplier:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
center:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: {x: 0.5, y: 0.5}
|
|
||||||
scale:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 1
|
|
||||||
--- !u!114 &9001657382290151224
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: cdfbdbb87d3286943a057f7791b43141, type: 3}
|
|
||||||
m_Name: ChannelMixer
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
redOutRedIn:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 100
|
|
||||||
redOutGreenIn:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
redOutBlueIn:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
greenOutRedIn:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
greenOutGreenIn:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 100
|
|
||||||
greenOutBlueIn:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
blueOutRedIn:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
blueOutGreenIn:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 0
|
|
||||||
blueOutBlueIn:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 100
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: ab09877e2e707104187f6f83e2f62510
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3}
|
|
||||||
m_Name: Mobile_RPAsset
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
k_AssetVersion: 13
|
|
||||||
k_AssetPreviousVersion: 13
|
|
||||||
m_RendererType: 1
|
|
||||||
m_RendererData: {fileID: 0}
|
|
||||||
m_RendererDataList:
|
|
||||||
- {fileID: 11400000, guid: 65bc7dbf4170f435aa868c779acfb082, type: 2}
|
|
||||||
m_DefaultRendererIndex: 0
|
|
||||||
m_RequireDepthTexture: 0
|
|
||||||
m_RequireOpaqueTexture: 0
|
|
||||||
m_OpaqueDownsampling: 0
|
|
||||||
m_SupportsTerrainHoles: 1
|
|
||||||
m_SupportsHDR: 1
|
|
||||||
m_HDRColorBufferPrecision: 0
|
|
||||||
m_MSAA: 1
|
|
||||||
m_RenderScale: 0.8
|
|
||||||
m_UpscalingFilter: 0
|
|
||||||
m_FsrOverrideSharpness: 0
|
|
||||||
m_FsrSharpness: 0.92
|
|
||||||
m_EnableLODCrossFade: 1
|
|
||||||
m_LODCrossFadeDitheringType: 1
|
|
||||||
m_ShEvalMode: 0
|
|
||||||
m_LightProbeSystem: 0
|
|
||||||
m_ProbeVolumeMemoryBudget: 1024
|
|
||||||
m_ProbeVolumeBlendingMemoryBudget: 256
|
|
||||||
m_SupportProbeVolumeGPUStreaming: 0
|
|
||||||
m_SupportProbeVolumeDiskStreaming: 0
|
|
||||||
m_SupportProbeVolumeScenarios: 0
|
|
||||||
m_SupportProbeVolumeScenarioBlending: 0
|
|
||||||
m_ProbeVolumeSHBands: 1
|
|
||||||
m_MainLightRenderingMode: 1
|
|
||||||
m_MainLightShadowsSupported: 1
|
|
||||||
m_MainLightShadowmapResolution: 1024
|
|
||||||
m_AdditionalLightsRenderingMode: 1
|
|
||||||
m_AdditionalLightsPerObjectLimit: 4
|
|
||||||
m_AdditionalLightShadowsSupported: 0
|
|
||||||
m_AdditionalLightsShadowmapResolution: 2048
|
|
||||||
m_AdditionalLightsShadowResolutionTierLow: 256
|
|
||||||
m_AdditionalLightsShadowResolutionTierMedium: 512
|
|
||||||
m_AdditionalLightsShadowResolutionTierHigh: 1024
|
|
||||||
m_ReflectionProbeBlending: 1
|
|
||||||
m_ReflectionProbeBoxProjection: 1
|
|
||||||
m_ReflectionProbeAtlas: 1
|
|
||||||
m_ShadowDistance: 50
|
|
||||||
m_ShadowCascadeCount: 1
|
|
||||||
m_Cascade2Split: 0.25
|
|
||||||
m_Cascade3Split: {x: 0.1, y: 0.3}
|
|
||||||
m_Cascade4Split: {x: 0.067, y: 0.2, z: 0.467}
|
|
||||||
m_CascadeBorder: 0.2
|
|
||||||
m_ShadowDepthBias: 1
|
|
||||||
m_ShadowNormalBias: 1
|
|
||||||
m_AnyShadowsSupported: 1
|
|
||||||
m_SoftShadowsSupported: 0
|
|
||||||
m_ConservativeEnclosingSphere: 1
|
|
||||||
m_NumIterationsEnclosingSphere: 64
|
|
||||||
m_SoftShadowQuality: 2
|
|
||||||
m_AdditionalLightsCookieResolution: 1024
|
|
||||||
m_AdditionalLightsCookieFormat: 1
|
|
||||||
m_UseSRPBatcher: 1
|
|
||||||
m_SupportsDynamicBatching: 0
|
|
||||||
m_MixedLightingSupported: 1
|
|
||||||
m_SupportsLightCookies: 1
|
|
||||||
m_SupportsLightLayers: 1
|
|
||||||
m_DebugLevel: 0
|
|
||||||
m_StoreActionsOptimization: 0
|
|
||||||
m_UseAdaptivePerformance: 1
|
|
||||||
m_ColorGradingMode: 0
|
|
||||||
m_ColorGradingLutSize: 32
|
|
||||||
m_AllowPostProcessAlphaOutput: 0
|
|
||||||
m_UseFastSRGBLinearConversion: 1
|
|
||||||
m_SupportDataDrivenLensFlare: 1
|
|
||||||
m_SupportScreenSpaceLensFlare: 1
|
|
||||||
m_GPUResidentDrawerMode: 0
|
|
||||||
m_SmallMeshScreenPercentage: 0
|
|
||||||
m_GPUResidentDrawerEnableOcclusionCullingInCameras: 0
|
|
||||||
m_ShadowType: 1
|
|
||||||
m_LocalShadowsSupported: 0
|
|
||||||
m_LocalShadowsAtlasResolution: 256
|
|
||||||
m_MaxPixelLights: 0
|
|
||||||
m_ShadowAtlasResolution: 256
|
|
||||||
m_VolumeFrameworkUpdateMode: 0
|
|
||||||
m_VolumeProfile: {fileID: 11400000, guid: 10fc4df2da32a41aaa32d77bc913491c, type: 2}
|
|
||||||
apvScenesData:
|
|
||||||
obsoleteSceneBounds:
|
|
||||||
m_Keys: []
|
|
||||||
m_Values: []
|
|
||||||
obsoleteHasProbeVolumes:
|
|
||||||
m_Keys: []
|
|
||||||
m_Values:
|
|
||||||
m_PrefilteringModeMainLightShadows: 3
|
|
||||||
m_PrefilteringModeAdditionalLight: 4
|
|
||||||
m_PrefilteringModeAdditionalLightShadows: 0
|
|
||||||
m_PrefilterXRKeywords: 1
|
|
||||||
m_PrefilteringModeForwardPlus: 1
|
|
||||||
m_PrefilteringModeDeferredRendering: 0
|
|
||||||
m_PrefilteringModeScreenSpaceOcclusion: 0
|
|
||||||
m_PrefilterDebugKeywords: 1
|
|
||||||
m_PrefilterWriteRenderingLayers: 1
|
|
||||||
m_PrefilterHDROutput: 1
|
|
||||||
m_PrefilterAlphaOutput: 0
|
|
||||||
m_PrefilterSSAODepthNormals: 1
|
|
||||||
m_PrefilterSSAOSourceDepthLow: 1
|
|
||||||
m_PrefilterSSAOSourceDepthMedium: 0
|
|
||||||
m_PrefilterSSAOSourceDepthHigh: 1
|
|
||||||
m_PrefilterSSAOInterleaved: 0
|
|
||||||
m_PrefilterSSAOBlueNoise: 1
|
|
||||||
m_PrefilterSSAOSampleCountLow: 1
|
|
||||||
m_PrefilterSSAOSampleCountMedium: 0
|
|
||||||
m_PrefilterSSAOSampleCountHigh: 1
|
|
||||||
m_PrefilterDBufferMRT1: 1
|
|
||||||
m_PrefilterDBufferMRT2: 1
|
|
||||||
m_PrefilterDBufferMRT3: 1
|
|
||||||
m_PrefilterSoftShadowsQualityLow: 1
|
|
||||||
m_PrefilterSoftShadowsQualityMedium: 1
|
|
||||||
m_PrefilterSoftShadowsQualityHigh: 1
|
|
||||||
m_PrefilterSoftShadows: 0
|
|
||||||
m_PrefilterScreenCoord: 1
|
|
||||||
m_PrefilterScreenSpaceIrradiance: 0
|
|
||||||
m_PrefilterNativeRenderPass: 1
|
|
||||||
m_PrefilterUseLegacyLightmaps: 0
|
|
||||||
m_PrefilterBicubicLightmapSampling: 0
|
|
||||||
m_PrefilterReflectionProbeRotation: 0
|
|
||||||
m_PrefilterReflectionProbeBlending: 0
|
|
||||||
m_PrefilterReflectionProbeBoxProjection: 0
|
|
||||||
m_PrefilterReflectionProbeAtlas: 0
|
|
||||||
m_ShaderVariantLogLevel: 0
|
|
||||||
m_ShadowCascades: 0
|
|
||||||
m_Textures:
|
|
||||||
blueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3}
|
|
||||||
bayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 5e6cbd92db86f4b18aec3ed561671858
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3}
|
|
||||||
m_Name: Mobile_Renderer
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
debugShaders:
|
|
||||||
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7,
|
|
||||||
type: 3}
|
|
||||||
hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}
|
|
||||||
probeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959,
|
|
||||||
type: 3}
|
|
||||||
probeVolumeResources:
|
|
||||||
probeVolumeDebugShader: {fileID: 0}
|
|
||||||
probeVolumeFragmentationDebugShader: {fileID: 0}
|
|
||||||
probeVolumeOffsetDebugShader: {fileID: 0}
|
|
||||||
probeVolumeSamplingDebugShader: {fileID: 0}
|
|
||||||
probeSamplingDebugMesh: {fileID: 0}
|
|
||||||
probeSamplingDebugTexture: {fileID: 0}
|
|
||||||
probeVolumeBlendStatesCS: {fileID: 0}
|
|
||||||
m_RendererFeatures: []
|
|
||||||
m_RendererFeatureMap:
|
|
||||||
m_UseNativeRenderPass: 1
|
|
||||||
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
|
|
||||||
m_AssetVersion: 2
|
|
||||||
m_OpaqueLayerMask:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_TransparentLayerMask:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_DefaultStencilState:
|
|
||||||
overrideStencilState: 0
|
|
||||||
stencilReference: 0
|
|
||||||
stencilCompareFunction: 8
|
|
||||||
passOperation: 2
|
|
||||||
failOperation: 0
|
|
||||||
zFailOperation: 0
|
|
||||||
m_ShadowTransparentReceive: 0
|
|
||||||
m_RenderingMode: 0
|
|
||||||
m_DepthPrimingMode: 0
|
|
||||||
m_CopyDepthMode: 0
|
|
||||||
m_AccurateGbufferNormals: 0
|
|
||||||
m_IntermediateTextureMode: 0
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 65bc7dbf4170f435aa868c779acfb082
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3}
|
|
||||||
m_Name: PC_RPAsset
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
k_AssetVersion: 13
|
|
||||||
k_AssetPreviousVersion: 13
|
|
||||||
m_RendererType: 1
|
|
||||||
m_RendererData: {fileID: 0}
|
|
||||||
m_RendererDataList:
|
|
||||||
- {fileID: 11400000, guid: f288ae1f4751b564a96ac7587541f7a2, type: 2}
|
|
||||||
m_DefaultRendererIndex: 0
|
|
||||||
m_RequireDepthTexture: 1
|
|
||||||
m_RequireOpaqueTexture: 1
|
|
||||||
m_OpaqueDownsampling: 1
|
|
||||||
m_SupportsTerrainHoles: 1
|
|
||||||
m_SupportsHDR: 1
|
|
||||||
m_HDRColorBufferPrecision: 0
|
|
||||||
m_MSAA: 1
|
|
||||||
m_RenderScale: 1
|
|
||||||
m_UpscalingFilter: 0
|
|
||||||
m_FsrOverrideSharpness: 0
|
|
||||||
m_FsrSharpness: 0.92
|
|
||||||
m_EnableLODCrossFade: 1
|
|
||||||
m_LODCrossFadeDitheringType: 1
|
|
||||||
m_ShEvalMode: 0
|
|
||||||
m_LightProbeSystem: 0
|
|
||||||
m_ProbeVolumeMemoryBudget: 1024
|
|
||||||
m_ProbeVolumeBlendingMemoryBudget: 256
|
|
||||||
m_SupportProbeVolumeGPUStreaming: 0
|
|
||||||
m_SupportProbeVolumeDiskStreaming: 0
|
|
||||||
m_SupportProbeVolumeScenarios: 0
|
|
||||||
m_SupportProbeVolumeScenarioBlending: 0
|
|
||||||
m_ProbeVolumeSHBands: 1
|
|
||||||
m_MainLightRenderingMode: 1
|
|
||||||
m_MainLightShadowsSupported: 1
|
|
||||||
m_MainLightShadowmapResolution: 2048
|
|
||||||
m_AdditionalLightsRenderingMode: 1
|
|
||||||
m_AdditionalLightsPerObjectLimit: 4
|
|
||||||
m_AdditionalLightShadowsSupported: 1
|
|
||||||
m_AdditionalLightsShadowmapResolution: 2048
|
|
||||||
m_AdditionalLightsShadowResolutionTierLow: 256
|
|
||||||
m_AdditionalLightsShadowResolutionTierMedium: 512
|
|
||||||
m_AdditionalLightsShadowResolutionTierHigh: 1024
|
|
||||||
m_ReflectionProbeBlending: 1
|
|
||||||
m_ReflectionProbeBoxProjection: 1
|
|
||||||
m_ReflectionProbeAtlas: 1
|
|
||||||
m_ShadowDistance: 50
|
|
||||||
m_ShadowCascadeCount: 4
|
|
||||||
m_Cascade2Split: 0.25
|
|
||||||
m_Cascade3Split: {x: 0.1, y: 0.3}
|
|
||||||
m_Cascade4Split: {x: 0.12299999, y: 0.2926, z: 0.53599995}
|
|
||||||
m_CascadeBorder: 0.107758604
|
|
||||||
m_ShadowDepthBias: 0.1
|
|
||||||
m_ShadowNormalBias: 0.5
|
|
||||||
m_AnyShadowsSupported: 1
|
|
||||||
m_SoftShadowsSupported: 1
|
|
||||||
m_ConservativeEnclosingSphere: 1
|
|
||||||
m_NumIterationsEnclosingSphere: 64
|
|
||||||
m_SoftShadowQuality: 3
|
|
||||||
m_AdditionalLightsCookieResolution: 2048
|
|
||||||
m_AdditionalLightsCookieFormat: 3
|
|
||||||
m_UseSRPBatcher: 1
|
|
||||||
m_SupportsDynamicBatching: 0
|
|
||||||
m_MixedLightingSupported: 1
|
|
||||||
m_SupportsLightCookies: 1
|
|
||||||
m_SupportsLightLayers: 1
|
|
||||||
m_DebugLevel: 0
|
|
||||||
m_StoreActionsOptimization: 0
|
|
||||||
m_UseAdaptivePerformance: 1
|
|
||||||
m_ColorGradingMode: 0
|
|
||||||
m_ColorGradingLutSize: 32
|
|
||||||
m_AllowPostProcessAlphaOutput: 0
|
|
||||||
m_UseFastSRGBLinearConversion: 0
|
|
||||||
m_SupportDataDrivenLensFlare: 1
|
|
||||||
m_SupportScreenSpaceLensFlare: 1
|
|
||||||
m_GPUResidentDrawerMode: 0
|
|
||||||
m_SmallMeshScreenPercentage: 0
|
|
||||||
m_GPUResidentDrawerEnableOcclusionCullingInCameras: 0
|
|
||||||
m_ShadowType: 1
|
|
||||||
m_LocalShadowsSupported: 0
|
|
||||||
m_LocalShadowsAtlasResolution: 256
|
|
||||||
m_MaxPixelLights: 0
|
|
||||||
m_ShadowAtlasResolution: 256
|
|
||||||
m_VolumeFrameworkUpdateMode: 0
|
|
||||||
m_VolumeProfile: {fileID: 11400000, guid: 10fc4df2da32a41aaa32d77bc913491c, type: 2}
|
|
||||||
apvScenesData:
|
|
||||||
obsoleteSceneBounds:
|
|
||||||
m_Keys: []
|
|
||||||
m_Values: []
|
|
||||||
obsoleteHasProbeVolumes:
|
|
||||||
m_Keys: []
|
|
||||||
m_Values:
|
|
||||||
m_PrefilteringModeMainLightShadows: 3
|
|
||||||
m_PrefilteringModeAdditionalLight: 4
|
|
||||||
m_PrefilteringModeAdditionalLightShadows: 0
|
|
||||||
m_PrefilterXRKeywords: 1
|
|
||||||
m_PrefilteringModeForwardPlus: 1
|
|
||||||
m_PrefilteringModeDeferredRendering: 0
|
|
||||||
m_PrefilteringModeScreenSpaceOcclusion: 1
|
|
||||||
m_PrefilterDebugKeywords: 1
|
|
||||||
m_PrefilterWriteRenderingLayers: 0
|
|
||||||
m_PrefilterHDROutput: 1
|
|
||||||
m_PrefilterAlphaOutput: 0
|
|
||||||
m_PrefilterSSAODepthNormals: 0
|
|
||||||
m_PrefilterSSAOSourceDepthLow: 1
|
|
||||||
m_PrefilterSSAOSourceDepthMedium: 1
|
|
||||||
m_PrefilterSSAOSourceDepthHigh: 1
|
|
||||||
m_PrefilterSSAOInterleaved: 1
|
|
||||||
m_PrefilterSSAOBlueNoise: 0
|
|
||||||
m_PrefilterSSAOSampleCountLow: 1
|
|
||||||
m_PrefilterSSAOSampleCountMedium: 0
|
|
||||||
m_PrefilterSSAOSampleCountHigh: 1
|
|
||||||
m_PrefilterDBufferMRT1: 1
|
|
||||||
m_PrefilterDBufferMRT2: 1
|
|
||||||
m_PrefilterDBufferMRT3: 0
|
|
||||||
m_PrefilterSoftShadowsQualityLow: 0
|
|
||||||
m_PrefilterSoftShadowsQualityMedium: 0
|
|
||||||
m_PrefilterSoftShadowsQualityHigh: 0
|
|
||||||
m_PrefilterSoftShadows: 0
|
|
||||||
m_PrefilterScreenCoord: 1
|
|
||||||
m_PrefilterScreenSpaceIrradiance: 0
|
|
||||||
m_PrefilterNativeRenderPass: 1
|
|
||||||
m_PrefilterUseLegacyLightmaps: 0
|
|
||||||
m_PrefilterBicubicLightmapSampling: 0
|
|
||||||
m_PrefilterReflectionProbeRotation: 0
|
|
||||||
m_PrefilterReflectionProbeBlending: 0
|
|
||||||
m_PrefilterReflectionProbeBoxProjection: 0
|
|
||||||
m_PrefilterReflectionProbeAtlas: 0
|
|
||||||
m_ShaderVariantLogLevel: 0
|
|
||||||
m_ShadowCascades: 0
|
|
||||||
m_Textures:
|
|
||||||
blueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3}
|
|
||||||
bayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 4b83569d67af61e458304325a23e5dfd
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3}
|
|
||||||
m_Name: PC_Renderer
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
debugShaders:
|
|
||||||
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7,
|
|
||||||
type: 3}
|
|
||||||
hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}
|
|
||||||
probeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959,
|
|
||||||
type: 3}
|
|
||||||
probeVolumeResources:
|
|
||||||
probeVolumeDebugShader: {fileID: 4800000, guid: e5c6678ed2aaa91408dd3df699057aae,
|
|
||||||
type: 3}
|
|
||||||
probeVolumeFragmentationDebugShader: {fileID: 4800000, guid: 03cfc4915c15d504a9ed85ecc404e607,
|
|
||||||
type: 3}
|
|
||||||
probeVolumeOffsetDebugShader: {fileID: 4800000, guid: 53a11f4ebaebf4049b3638ef78dc9664,
|
|
||||||
type: 3}
|
|
||||||
probeVolumeSamplingDebugShader: {fileID: 4800000, guid: 8f96cd657dc40064aa21efcc7e50a2e7,
|
|
||||||
type: 3}
|
|
||||||
probeSamplingDebugMesh: {fileID: -3555484719484374845, guid: 57d7c4c16e2765b47a4d2069b311bffe,
|
|
||||||
type: 3}
|
|
||||||
probeSamplingDebugTexture: {fileID: 2800000, guid: 24ec0e140fb444a44ab96ee80844e18e,
|
|
||||||
type: 3}
|
|
||||||
probeVolumeBlendStatesCS: {fileID: 7200000, guid: b9a23f869c4fd45f19c5ada54dd82176,
|
|
||||||
type: 3}
|
|
||||||
m_RendererFeatures:
|
|
||||||
- {fileID: 7833122117494664109}
|
|
||||||
m_RendererFeatureMap: ad6b866f10d7b46c
|
|
||||||
m_UseNativeRenderPass: 1
|
|
||||||
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
|
|
||||||
m_AssetVersion: 2
|
|
||||||
m_OpaqueLayerMask:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_TransparentLayerMask:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_DefaultStencilState:
|
|
||||||
overrideStencilState: 0
|
|
||||||
stencilReference: 1
|
|
||||||
stencilCompareFunction: 3
|
|
||||||
passOperation: 2
|
|
||||||
failOperation: 0
|
|
||||||
zFailOperation: 0
|
|
||||||
m_ShadowTransparentReceive: 1
|
|
||||||
m_RenderingMode: 2
|
|
||||||
m_DepthPrimingMode: 0
|
|
||||||
m_CopyDepthMode: 0
|
|
||||||
m_AccurateGbufferNormals: 0
|
|
||||||
m_IntermediateTextureMode: 0
|
|
||||||
--- !u!114 &7833122117494664109
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: f62c9c65cf3354c93be831c8bc075510, type: 3}
|
|
||||||
m_Name: ScreenSpaceAmbientOcclusion
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
m_Active: 1
|
|
||||||
m_Settings:
|
|
||||||
AOMethod: 0
|
|
||||||
Downsample: 0
|
|
||||||
AfterOpaque: 0
|
|
||||||
Source: 1
|
|
||||||
NormalSamples: 1
|
|
||||||
Intensity: 0.4
|
|
||||||
DirectLightingStrength: 0.25
|
|
||||||
Radius: 0.3
|
|
||||||
Samples: 1
|
|
||||||
BlurQuality: 0
|
|
||||||
Falloff: 100
|
|
||||||
SampleCount: -1
|
|
||||||
m_BlueNoise256Textures:
|
|
||||||
- {fileID: 2800000, guid: 36f118343fc974119bee3d09e2111500, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 4b7b083e6b6734e8bb2838b0b50a0bc8, type: 3}
|
|
||||||
- {fileID: 2800000, guid: c06cc21c692f94f5fb5206247191eeee, type: 3}
|
|
||||||
- {fileID: 2800000, guid: cb76dd40fa7654f9587f6a344f125c9a, type: 3}
|
|
||||||
- {fileID: 2800000, guid: e32226222ff144b24bf3a5a451de54bc, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 3302065f671a8450b82c9ddf07426f3a, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 56a77a3e8d64f47b6afe9e3c95cb57d5, type: 3}
|
|
||||||
m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f288ae1f4751b564a96ac7587541f7a2
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &-7893295128165547882
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 3
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 0b2db86121404754db890f4c8dfe81b2, type: 3}
|
|
||||||
m_Name: Bloom
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
active: 1
|
|
||||||
m_AdvancedMode: 0
|
|
||||||
threshold:
|
|
||||||
m_OverrideState: 0
|
|
||||||
m_Value: 1
|
|
||||||
min: 0
|
|
||||||
intensity:
|
|
||||||
m_OverrideState: 1
|
|
||||||
m_Value: 15
|
|
||||||
min: 0
|
|
||||||
scatter:
|
|
||||||
m_OverrideState: 0
|
|
||||||
m_Value: 0.7
|
|
||||||
min: 0
|
|
||||||
max: 1
|
|
||||||
clamp:
|
|
||||||
m_OverrideState: 0
|
|
||||||
m_Value: 65472
|
|
||||||
min: 0
|
|
||||||
tint:
|
|
||||||
m_OverrideState: 0
|
|
||||||
m_Value: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
hdr: 0
|
|
||||||
showAlpha: 0
|
|
||||||
showEyeDropper: 1
|
|
||||||
highQualityFiltering:
|
|
||||||
m_OverrideState: 0
|
|
||||||
m_Value: 0
|
|
||||||
dirtTexture:
|
|
||||||
m_OverrideState: 0
|
|
||||||
m_Value: {fileID: 0}
|
|
||||||
dirtIntensity:
|
|
||||||
m_OverrideState: 0
|
|
||||||
m_Value: 0
|
|
||||||
min: 0
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
|
|
||||||
m_Name: SampleSceneProfile
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
components:
|
|
||||||
- {fileID: -7893295128165547882}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 10fc4df2da32a41aaa32d77bc913491c
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 0
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/Settings/SampleSceneProfile.asset
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,433 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 2ec995e51a6e251468d2a3fd8a686257, type: 3}
|
|
||||||
m_Name: UniversalRenderPipelineGlobalSettings
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
m_ShaderStrippingSetting:
|
|
||||||
m_Version: 0
|
|
||||||
m_ExportShaderVariants: 1
|
|
||||||
m_ShaderVariantLogLevel: 0
|
|
||||||
m_StripRuntimeDebugShaders: 1
|
|
||||||
m_URPShaderStrippingSetting:
|
|
||||||
m_Version: 0
|
|
||||||
m_StripUnusedPostProcessingVariants: 1
|
|
||||||
m_StripUnusedVariants: 1
|
|
||||||
m_StripScreenCoordOverrideVariants: 1
|
|
||||||
m_ShaderVariantLogLevel: 0
|
|
||||||
m_ExportShaderVariants: 1
|
|
||||||
m_StripDebugVariants: 1
|
|
||||||
m_StripUnusedPostProcessingVariants: 1
|
|
||||||
m_StripUnusedVariants: 1
|
|
||||||
m_StripScreenCoordOverrideVariants: 1
|
|
||||||
supportRuntimeDebugDisplay: 0
|
|
||||||
m_EnableRenderGraph: 0
|
|
||||||
m_Settings:
|
|
||||||
m_SettingsList:
|
|
||||||
m_List:
|
|
||||||
- rid: 6852985685364965376
|
|
||||||
- rid: 6852985685364965377
|
|
||||||
- rid: 6852985685364965378
|
|
||||||
- rid: 6852985685364965379
|
|
||||||
- rid: 6852985685364965380
|
|
||||||
- rid: 6852985685364965381
|
|
||||||
- rid: 6852985685364965382
|
|
||||||
- rid: 6852985685364965383
|
|
||||||
- rid: 6852985685364965384
|
|
||||||
- rid: 6852985685364965385
|
|
||||||
- rid: 6852985685364965386
|
|
||||||
- rid: 6852985685364965387
|
|
||||||
- rid: 6852985685364965388
|
|
||||||
- rid: 6852985685364965389
|
|
||||||
- rid: 6852985685364965390
|
|
||||||
- rid: 6852985685364965391
|
|
||||||
- rid: 6852985685364965392
|
|
||||||
- rid: 6852985685364965393
|
|
||||||
- rid: 6852985685364965394
|
|
||||||
- rid: 8712630790384254976
|
|
||||||
- rid: 3348792947937902592
|
|
||||||
- rid: 3348792947937902593
|
|
||||||
- rid: 3348792947937902594
|
|
||||||
- rid: 3348792947937902595
|
|
||||||
- rid: 3348792947937902596
|
|
||||||
- rid: 3348792947937902597
|
|
||||||
- rid: 3348792947937902598
|
|
||||||
- rid: 3348792947937902599
|
|
||||||
- rid: 3348792947937902600
|
|
||||||
- rid: 3348792947937902601
|
|
||||||
- rid: 3348792947937902602
|
|
||||||
- rid: 3348792947937902603
|
|
||||||
- rid: 3348792947937902604
|
|
||||||
- rid: 3348792947937902605
|
|
||||||
m_RuntimeSettings:
|
|
||||||
m_List: []
|
|
||||||
m_AssetVersion: 10
|
|
||||||
m_ObsoleteDefaultVolumeProfile: {fileID: 0}
|
|
||||||
m_RenderingLayerNames:
|
|
||||||
- Light Layer default
|
|
||||||
- Light Layer 1
|
|
||||||
- Light Layer 2
|
|
||||||
- Light Layer 3
|
|
||||||
- Light Layer 4
|
|
||||||
- Light Layer 5
|
|
||||||
- Light Layer 6
|
|
||||||
- Light Layer 7
|
|
||||||
m_ValidRenderingLayers: 0
|
|
||||||
lightLayerName0: Light Layer default
|
|
||||||
lightLayerName1: Light Layer 1
|
|
||||||
lightLayerName2: Light Layer 2
|
|
||||||
lightLayerName3: Light Layer 3
|
|
||||||
lightLayerName4: Light Layer 4
|
|
||||||
lightLayerName5: Light Layer 5
|
|
||||||
lightLayerName6: Light Layer 6
|
|
||||||
lightLayerName7: Light Layer 7
|
|
||||||
apvScenesData:
|
|
||||||
obsoleteSceneBounds:
|
|
||||||
m_Keys: []
|
|
||||||
m_Values: []
|
|
||||||
obsoleteHasProbeVolumes:
|
|
||||||
m_Keys: []
|
|
||||||
m_Values:
|
|
||||||
references:
|
|
||||||
version: 2
|
|
||||||
RefIds:
|
|
||||||
- rid: 3348792947937902592
|
|
||||||
type: {class: RayTracingRenderPipelineResources, ns: UnityEngine.Rendering.UnifiedRayTracing, asm: Unity.UnifiedRayTracing.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 1
|
|
||||||
m_GeometryPoolKernels: {fileID: 7200000, guid: 98e3d58cae7210c4786f67f504c9e899, type: 3}
|
|
||||||
m_CopyBuffer: {fileID: 7200000, guid: 1b95b5dcf48d1914c9e1e7405c7660e3, type: 3}
|
|
||||||
m_CopyPositions: {fileID: 7200000, guid: 1ad53a96b58d3c3488dde4f14db1aaeb, type: 3}
|
|
||||||
m_BitHistogram: {fileID: 7200000, guid: 8670f7ce4b60cef43bed36148aa1b0a2, type: 3}
|
|
||||||
m_BlockReducePart: {fileID: 7200000, guid: 4e034cc8ea2635c4e9f063e5ddc7ea7a, type: 3}
|
|
||||||
m_BlockScan: {fileID: 7200000, guid: 4d6d5de35fa45ef4a92119397a045cc9, type: 3}
|
|
||||||
m_BuildHlbvh: {fileID: 7200000, guid: 2d70cd6be91bd7843a39a54b51c15b13, type: 3}
|
|
||||||
m_RestructureBvh: {fileID: 7200000, guid: 56641cb88dcb31a4398a4997ef7a7a8c, type: 3}
|
|
||||||
m_Scatter: {fileID: 7200000, guid: a2eaeefdac4637a44b734e85b7be9186, type: 3}
|
|
||||||
- rid: 3348792947937902593
|
|
||||||
type: {class: UniversalRenderPipelineEditorAssets, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_DefaultSettingsVolumeProfile: {fileID: 11400000, guid: eda47df5b85f4f249abf7abd73db2cb2, type: 2}
|
|
||||||
- rid: 3348792947937902594
|
|
||||||
type: {class: UniversalRenderPipelineRuntimeTerrainShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_TerrainDetailLit: {fileID: 4800000, guid: f6783ab646d374f94b199774402a5144, type: 3}
|
|
||||||
m_TerrainDetailGrassBillboard: {fileID: 4800000, guid: 29868e73b638e48ca99a19ea58c48d90, type: 3}
|
|
||||||
m_TerrainDetailGrass: {fileID: 4800000, guid: e507fdfead5ca47e8b9a768b51c291a1, type: 3}
|
|
||||||
- rid: 3348792947937902595
|
|
||||||
type: {class: URPTerrainShaderSetting, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_IncludeTerrainShaders: 1
|
|
||||||
- rid: 3348792947937902596
|
|
||||||
type: {class: PostProcessData/ShaderResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
stopNanPS: {fileID: 4800000, guid: 1121bb4e615ca3c48b214e79e841e823, type: 3}
|
|
||||||
subpixelMorphologicalAntialiasingPS: {fileID: 4800000, guid: 63eaba0ebfb82cc43bde059b4a8c65f6, type: 3}
|
|
||||||
gaussianDepthOfFieldPS: {fileID: 4800000, guid: 5e7134d6e63e0bc47a1dd2669cedb379, type: 3}
|
|
||||||
bokehDepthOfFieldPS: {fileID: 4800000, guid: 2aed67ad60045d54ba3a00c91e2d2631, type: 3}
|
|
||||||
cameraMotionBlurPS: {fileID: 4800000, guid: 1edcd131364091c46a17cbff0b1de97a, type: 3}
|
|
||||||
paniniProjectionPS: {fileID: 4800000, guid: a15b78cf8ca26ca4fb2090293153c62c, type: 3}
|
|
||||||
lutBuilderLdrPS: {fileID: 4800000, guid: 65df88701913c224d95fc554db28381a, type: 3}
|
|
||||||
lutBuilderHdrPS: {fileID: 4800000, guid: ec9fec698a3456d4fb18cf8bacb7a2bc, type: 3}
|
|
||||||
bloomPS: {fileID: 4800000, guid: 5f1864addb451f54bae8c86d230f736e, type: 3}
|
|
||||||
temporalAntialiasingPS: {fileID: 4800000, guid: 9c70c1a35ff15f340b38ea84842358bf, type: 3}
|
|
||||||
LensFlareDataDrivenPS: {fileID: 4800000, guid: 6cda457ac28612740adb23da5d39ea92, type: 3}
|
|
||||||
LensFlareScreenSpacePS: {fileID: 4800000, guid: 701880fecb344ea4c9cd0db3407ab287, type: 3}
|
|
||||||
scalingSetupPS: {fileID: 4800000, guid: e8ee25143a34b8c4388709ea947055d1, type: 3}
|
|
||||||
easuPS: {fileID: 4800000, guid: 562b7ae4f629f144aa97780546fce7c6, type: 3}
|
|
||||||
uberPostPS: {fileID: 4800000, guid: e7857e9d0c934dc4f83f270f8447b006, type: 3}
|
|
||||||
finalPostPassPS: {fileID: 4800000, guid: c49e63ed1bbcb334780a3bd19dfed403, type: 3}
|
|
||||||
m_ShaderResourcesVersion: 0
|
|
||||||
- rid: 3348792947937902597
|
|
||||||
type: {class: URPReflectionProbeSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
version: 1
|
|
||||||
useReflectionProbeRotation: 0
|
|
||||||
- rid: 3348792947937902598
|
|
||||||
type: {class: ScreenSpaceAmbientOcclusionDynamicResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_BlueNoise256Textures:
|
|
||||||
- {fileID: 2800000, guid: 36f118343fc974119bee3d09e2111500, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 4b7b083e6b6734e8bb2838b0b50a0bc8, type: 3}
|
|
||||||
- {fileID: 2800000, guid: c06cc21c692f94f5fb5206247191eeee, type: 3}
|
|
||||||
- {fileID: 2800000, guid: cb76dd40fa7654f9587f6a344f125c9a, type: 3}
|
|
||||||
- {fileID: 2800000, guid: e32226222ff144b24bf3a5a451de54bc, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 3302065f671a8450b82c9ddf07426f3a, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 56a77a3e8d64f47b6afe9e3c95cb57d5, type: 3}
|
|
||||||
m_Version: 0
|
|
||||||
- rid: 3348792947937902599
|
|
||||||
type: {class: OnTilePostProcessResource, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_UberPostShader: {fileID: 4800000, guid: fe4f13c1004a07d4ea1e30bfd0326d9e, type: 3}
|
|
||||||
- rid: 3348792947937902600
|
|
||||||
type: {class: UniversalRenderPipelineRuntimeXRResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_xrOcclusionMeshPS: {fileID: 4800000, guid: 4431b1f1f743fbf4eb310a967890cbea, type: 3}
|
|
||||||
m_xrMirrorViewPS: {fileID: 4800000, guid: d5a307c014552314b9f560906d708772, type: 3}
|
|
||||||
m_xrMotionVector: {fileID: 4800000, guid: f89aac1e4f84468418fe30e611dff395, type: 3}
|
|
||||||
- rid: 3348792947937902601
|
|
||||||
type: {class: ScreenSpaceAmbientOcclusionPersistentResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3}
|
|
||||||
m_Version: 0
|
|
||||||
- rid: 3348792947937902602
|
|
||||||
type: {class: PostProcessData/TextureResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
blueNoise16LTex:
|
|
||||||
- {fileID: 2800000, guid: 81200413a40918d4d8702e94db29911c, type: 3}
|
|
||||||
- {fileID: 2800000, guid: d50c5e07c9911a74982bddf7f3075e7b, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 1134690bf9216164dbc75050e35b7900, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 7ce2118f74614a94aa8a0cdf2e6062c3, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 2ca97df9d1801e84a8a8f2c53cb744f0, type: 3}
|
|
||||||
- {fileID: 2800000, guid: e63eef8f54aa9dc4da9a5ac094b503b5, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 39451254daebd6d40b52899c1f1c0c1b, type: 3}
|
|
||||||
- {fileID: 2800000, guid: c94ad916058dff743b0f1c969ddbe660, type: 3}
|
|
||||||
- {fileID: 2800000, guid: ed5ea7ce59ca8ec4f9f14bf470a30f35, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 071e954febf155243a6c81e48f452644, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 96aaab9cc247d0b4c98132159688c1af, type: 3}
|
|
||||||
- {fileID: 2800000, guid: fc3fa8f108657e14486697c9a84ccfc5, type: 3}
|
|
||||||
- {fileID: 2800000, guid: bfed3e498947fcb4890b7f40f54d85b9, type: 3}
|
|
||||||
- {fileID: 2800000, guid: d512512f4af60a442ab3458489412954, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 47a45908f6db0cb44a0d5e961143afec, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 4dcc0502f8586f941b5c4a66717205e8, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 9d92991794bb5864c8085468b97aa067, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 14381521ff11cb74abe3fe65401c23be, type: 3}
|
|
||||||
- {fileID: 2800000, guid: d36f0fe53425e08499a2333cf423634c, type: 3}
|
|
||||||
- {fileID: 2800000, guid: d4044ea2490d63b43aa1765f8efbf8a9, type: 3}
|
|
||||||
- {fileID: 2800000, guid: c9bd74624d8070f429e3f46d161f9204, type: 3}
|
|
||||||
- {fileID: 2800000, guid: d5c9b274310e5524ebe32a4e4da3df1f, type: 3}
|
|
||||||
- {fileID: 2800000, guid: f69770e54f2823f43badf77916acad83, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 10b6c6d22e73dea46a8ab36b6eebd629, type: 3}
|
|
||||||
- {fileID: 2800000, guid: a2ec5cbf5a9b64345ad3fab0912ddf7b, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 1c3c6d69a645b804fa232004b96b7ad3, type: 3}
|
|
||||||
- {fileID: 2800000, guid: d18a24d7b4ed50f4387993566d9d3ae2, type: 3}
|
|
||||||
- {fileID: 2800000, guid: c989e1ed85cf7154caa922fec53e6af6, type: 3}
|
|
||||||
- {fileID: 2800000, guid: ff47e5a0f105eb34883b973e51f4db62, type: 3}
|
|
||||||
- {fileID: 2800000, guid: fa042edbfc40fbd4bad0ab9d505b1223, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 896d9004736809c4fb5973b7c12eb8b9, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 179f794063d2a66478e6e726f84a65bc, type: 3}
|
|
||||||
filmGrainTex:
|
|
||||||
- {fileID: 2800000, guid: 654c582f7f8a5a14dbd7d119cbde215d, type: 3}
|
|
||||||
- {fileID: 2800000, guid: dd77ffd079630404e879388999033049, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 1097e90e1306e26439701489f391a6c0, type: 3}
|
|
||||||
- {fileID: 2800000, guid: f0b67500f7fad3b4c9f2b13e8f41ba6e, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 9930fb4528622b34687b00bbe6883de7, type: 3}
|
|
||||||
- {fileID: 2800000, guid: bd9e8c758250ef449a4b4bfaad7a2133, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 510a2f57334933e4a8dbabe4c30204e4, type: 3}
|
|
||||||
- {fileID: 2800000, guid: b4db8180660810945bf8d55ab44352ad, type: 3}
|
|
||||||
- {fileID: 2800000, guid: fd2fd78b392986e42a12df2177d3b89c, type: 3}
|
|
||||||
- {fileID: 2800000, guid: 5cdee82a77d13994f83b8fdabed7c301, type: 3}
|
|
||||||
smaaAreaTex: {fileID: 2800000, guid: d1f1048909d55cd4fa1126ab998f617e, type: 3}
|
|
||||||
smaaSearchTex: {fileID: 2800000, guid: 51eee22c2a633ef4aada830eed57c3fd, type: 3}
|
|
||||||
m_TexturesResourcesVersion: 0
|
|
||||||
- rid: 3348792947937902603
|
|
||||||
type: {class: VrsRenderPipelineRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_TextureComputeShader: {fileID: 7200000, guid: cacb30de6c40c7444bbc78cb0a81fd2a, type: 3}
|
|
||||||
m_VisualizationShader: {fileID: 4800000, guid: 620b55b8040a88d468e94abe55bed5ba, type: 3}
|
|
||||||
m_VisualizationLookupTable:
|
|
||||||
m_Data:
|
|
||||||
- {r: 0.785, g: 0.23, b: 0.2, a: 1}
|
|
||||||
- {r: 1, g: 0.8, b: 0.8, a: 1}
|
|
||||||
- {r: 0.4, g: 0.2, b: 0.2, a: 1}
|
|
||||||
- {r: 0.51, g: 0.8, b: 0.6, a: 1}
|
|
||||||
- {r: 0.6, g: 0.8, b: 1, a: 1}
|
|
||||||
- {r: 0.2, g: 0.4, b: 0.6, a: 1}
|
|
||||||
- {r: 0.8, g: 1, b: 0.8, a: 1}
|
|
||||||
- {r: 0.2, g: 0.4, b: 0.2, a: 1}
|
|
||||||
- {r: 0.125, g: 0.22, b: 0.36, a: 1}
|
|
||||||
m_ConversionLookupTable:
|
|
||||||
m_Data:
|
|
||||||
- {r: 0.785, g: 0.23, b: 0.2, a: 1}
|
|
||||||
- {r: 1, g: 0.8, b: 0.8, a: 1}
|
|
||||||
- {r: 0.4, g: 0.2, b: 0.2, a: 1}
|
|
||||||
- {r: 0.51, g: 0.8, b: 0.6, a: 1}
|
|
||||||
- {r: 0.6, g: 0.8, b: 1, a: 1}
|
|
||||||
- {r: 0.2, g: 0.4, b: 0.6, a: 1}
|
|
||||||
- {r: 0.8, g: 1, b: 0.8, a: 1}
|
|
||||||
- {r: 0.2, g: 0.4, b: 0.2, a: 1}
|
|
||||||
- {r: 0.125, g: 0.22, b: 0.36, a: 1}
|
|
||||||
- rid: 3348792947937902604
|
|
||||||
type: {class: RenderingDebuggerRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_version: 0
|
|
||||||
- rid: 3348792947937902605
|
|
||||||
type: {class: LightmapSamplingSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 1
|
|
||||||
m_UseBicubicLightmapSampling: 0
|
|
||||||
- rid: 6852985685364965376
|
|
||||||
type: {class: URPShaderStrippingSetting, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_StripUnusedPostProcessingVariants: 1
|
|
||||||
m_StripUnusedVariants: 1
|
|
||||||
m_StripScreenCoordOverrideVariants: 1
|
|
||||||
- rid: 6852985685364965377
|
|
||||||
type: {class: UniversalRenderPipelineEditorShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_AutodeskInteractive: {fileID: 4800000, guid: 0e9d5a909a1f7e84882a534d0d11e49f, type: 3}
|
|
||||||
m_AutodeskInteractiveTransparent: {fileID: 4800000, guid: 5c81372d981403744adbdda4433c9c11, type: 3}
|
|
||||||
m_AutodeskInteractiveMasked: {fileID: 4800000, guid: 80aa867ac363ac043847b06ad71604cd, type: 3}
|
|
||||||
m_DefaultSpeedTree7Shader: {fileID: 4800000, guid: 0f4122b9a743b744abe2fb6a0a88868b, type: 3}
|
|
||||||
m_DefaultSpeedTree8Shader: {fileID: -6465566751694194690, guid: 9920c1f1781549a46ba081a2a15a16ec, type: 3}
|
|
||||||
m_DefaultSpeedTree9Shader: {fileID: -6465566751694194690, guid: cbd3e1cc4ae141c42a30e33b4d666a61, type: 3}
|
|
||||||
- rid: 6852985685364965378
|
|
||||||
type: {class: UniversalRendererResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_CopyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3}
|
|
||||||
m_CameraMotionVector: {fileID: 4800000, guid: c56b7e0d4c7cb484e959caeeedae9bbf, type: 3}
|
|
||||||
m_StencilDeferredPS: {fileID: 4800000, guid: e9155b26e1bc55942a41e518703fe304, type: 3}
|
|
||||||
m_ClusterDeferred: {fileID: 4800000, guid: 222cce62363a44a380c36bf03b392608, type: 3}
|
|
||||||
m_StencilDitherMaskSeedPS: {fileID: 4800000, guid: 8c3ee818f2efa514c889881ccb2e95a2, type: 3}
|
|
||||||
m_DBufferClear: {fileID: 4800000, guid: f056d8bd2a1c7e44e9729144b4c70395, type: 3}
|
|
||||||
- rid: 6852985685364965379
|
|
||||||
type: {class: UniversalRenderPipelineDebugShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_DebugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3}
|
|
||||||
m_HdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}
|
|
||||||
m_ProbeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, type: 3}
|
|
||||||
- rid: 6852985685364965380
|
|
||||||
type: {class: UniversalRenderPipelineRuntimeShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_FallbackErrorShader: {fileID: 4800000, guid: e6e9a19c3678ded42a3bc431ebef7dbd, type: 3}
|
|
||||||
m_BlitHDROverlay: {fileID: 4800000, guid: a89bee29cffa951418fc1e2da94d1959, type: 3}
|
|
||||||
m_CoreBlitPS: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3}
|
|
||||||
m_CoreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b, type: 3}
|
|
||||||
m_SamplingPS: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3}
|
|
||||||
m_TerrainDetailLit: {fileID: 0}
|
|
||||||
m_TerrainDetailGrassBillboard: {fileID: 0}
|
|
||||||
m_TerrainDetailGrass: {fileID: 0}
|
|
||||||
- rid: 6852985685364965381
|
|
||||||
type: {class: UniversalRenderPipelineRuntimeTextures, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 1
|
|
||||||
m_BlueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3}
|
|
||||||
m_BayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3}
|
|
||||||
m_DebugFontTex: {fileID: 2800000, guid: 26a413214480ef144b2915d6ff4d0beb, type: 3}
|
|
||||||
- rid: 6852985685364965382
|
|
||||||
type: {class: Renderer2DResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_LightShader: {fileID: 4800000, guid: 3f6c848ca3d7bca4bbe846546ac701a1, type: 3}
|
|
||||||
m_ProjectedShadowShader: {fileID: 4800000, guid: ce09d4a80b88c5a4eb9768fab4f1ee00, type: 3}
|
|
||||||
m_SpriteShadowShader: {fileID: 4800000, guid: 44fc62292b65ab04eabcf310e799ccf6, type: 3}
|
|
||||||
m_SpriteUnshadowShader: {fileID: 4800000, guid: de02b375720b5c445afe83cd483bedf3, type: 3}
|
|
||||||
m_GeometryShadowShader: {fileID: 4800000, guid: 19349a0f9a7ed4c48a27445bcf92e5e1, type: 3}
|
|
||||||
m_GeometryUnshadowShader: {fileID: 4800000, guid: 77774d9009bb81447b048c907d4c6273, type: 3}
|
|
||||||
m_CopyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3}
|
|
||||||
m_DefaultLitMaterial: {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}
|
|
||||||
m_DefaultUnlitMaterial: {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}
|
|
||||||
m_DefaultMaskMaterial: {fileID: 2100000, guid: 15d0c3709176029428a0da2f8cecf0b5, type: 2}
|
|
||||||
m_DefaultMesh2DLitMaterial: {fileID: 2100000, guid: 9452ae1262a74094f8a68013fbcd1834, type: 2}
|
|
||||||
- rid: 6852985685364965383
|
|
||||||
type: {class: UniversalRenderPipelineEditorMaterials, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_DefaultMaterial: {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
|
||||||
m_DefaultParticleMaterial: {fileID: 2100000, guid: e823cd5b5d27c0f4b8256e7c12ee3e6d, type: 2}
|
|
||||||
m_DefaultLineMaterial: {fileID: 2100000, guid: e823cd5b5d27c0f4b8256e7c12ee3e6d, type: 2}
|
|
||||||
m_DefaultTerrainMaterial: {fileID: 2100000, guid: 594ea882c5a793440b60ff72d896021e, type: 2}
|
|
||||||
m_DefaultDecalMaterial: {fileID: 2100000, guid: 31d0dcc6f2dd4e4408d18036a2c93862, type: 2}
|
|
||||||
m_DefaultSpriteMaterial: {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}
|
|
||||||
- rid: 6852985685364965384
|
|
||||||
type: {class: URPDefaultVolumeProfileSettings, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_VolumeProfile: {fileID: 11400000, guid: ab09877e2e707104187f6f83e2f62510, type: 2}
|
|
||||||
- rid: 6852985685364965385
|
|
||||||
type: {class: RenderGraphSettings, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_EnableRenderCompatibilityMode: 0
|
|
||||||
- rid: 6852985685364965386
|
|
||||||
type: {class: GPUResidentDrawerResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.GPUDriven.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_InstanceDataBufferCopyKernels: {fileID: 7200000, guid: f984aeb540ded8b4fbb8a2047ab5b2e2, type: 3}
|
|
||||||
m_InstanceDataBufferUploadKernels: {fileID: 7200000, guid: 53864816eb00f2343b60e1a2c5a262ef, type: 3}
|
|
||||||
m_TransformUpdaterKernels: {fileID: 7200000, guid: 2a567b9b2733f8d47a700c3c85bed75b, type: 3}
|
|
||||||
m_WindDataUpdaterKernels: {fileID: 7200000, guid: fde76746e4fd0ed418c224f6b4084114, type: 3}
|
|
||||||
m_OccluderDepthPyramidKernels: {fileID: 7200000, guid: 08b2b5fb307b0d249860612774a987da, type: 3}
|
|
||||||
m_InstanceOcclusionCullingKernels: {fileID: 7200000, guid: f6d223acabc2f974795a5a7864b50e6c, type: 3}
|
|
||||||
m_OcclusionCullingDebugKernels: {fileID: 7200000, guid: b23e766bcf50ca4438ef186b174557df, type: 3}
|
|
||||||
m_DebugOcclusionTestPS: {fileID: 4800000, guid: d3f0849180c2d0944bc71060693df100, type: 3}
|
|
||||||
m_DebugOccluderPS: {fileID: 4800000, guid: b3c92426a88625841ab15ca6a7917248, type: 3}
|
|
||||||
- rid: 6852985685364965387
|
|
||||||
type: {class: STP/RuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_setupCS: {fileID: 7200000, guid: 33be2e9a5506b2843bdb2bdff9cad5e1, type: 3}
|
|
||||||
m_preTaaCS: {fileID: 7200000, guid: a679dba8ec4d9ce45884a270b0e22dda, type: 3}
|
|
||||||
m_taaCS: {fileID: 7200000, guid: 3923900e2b41b5e47bc25bfdcbcdc9e6, type: 3}
|
|
||||||
- rid: 6852985685364965388
|
|
||||||
type: {class: ProbeVolumeBakingResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 1
|
|
||||||
dilationShader: {fileID: 7200000, guid: 6bb382f7de370af41b775f54182e491d, type: 3}
|
|
||||||
subdivideSceneCS: {fileID: 7200000, guid: bb86f1f0af829fd45b2ebddda1245c22, type: 3}
|
|
||||||
voxelizeSceneShader: {fileID: 4800000, guid: c8b6a681c7b4e2e4785ffab093907f9e, type: 3}
|
|
||||||
traceVirtualOffsetCS: {fileID: -6772857160820960102, guid: ff2cbab5da58bf04d82c5f34037ed123, type: 3}
|
|
||||||
traceVirtualOffsetRT: {fileID: -5126288278712620388, guid: ff2cbab5da58bf04d82c5f34037ed123, type: 3}
|
|
||||||
skyOcclusionCS: {fileID: -6772857160820960102, guid: 5a2a534753fbdb44e96c3c78b5a6999d, type: 3}
|
|
||||||
skyOcclusionRT: {fileID: -5126288278712620388, guid: 5a2a534753fbdb44e96c3c78b5a6999d, type: 3}
|
|
||||||
renderingLayerCS: {fileID: -6772857160820960102, guid: 94a070d33e408384bafc1dea4a565df9, type: 3}
|
|
||||||
renderingLayerRT: {fileID: -5126288278712620388, guid: 94a070d33e408384bafc1dea4a565df9, type: 3}
|
|
||||||
- rid: 6852985685364965389
|
|
||||||
type: {class: ProbeVolumeGlobalSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 1
|
|
||||||
m_ProbeVolumeDisableStreamingAssets: 0
|
|
||||||
- rid: 6852985685364965390
|
|
||||||
type: {class: ProbeVolumeDebugResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 1
|
|
||||||
probeVolumeDebugShader: {fileID: 4800000, guid: 3b21275fd12d65f49babb5286f040f2d, type: 3}
|
|
||||||
probeVolumeFragmentationDebugShader: {fileID: 4800000, guid: 3a80877c579b9144ebdcc6d923bca303, type: 3}
|
|
||||||
probeVolumeSamplingDebugShader: {fileID: 4800000, guid: bf54e6528c79a224e96346799064c393, type: 3}
|
|
||||||
probeVolumeOffsetDebugShader: {fileID: 4800000, guid: db8bd7436dc2c5f4c92655307d198381, type: 3}
|
|
||||||
probeSamplingDebugMesh: {fileID: -3555484719484374845, guid: 20be25aac4e22ee49a7db76fb3df6de2, type: 3}
|
|
||||||
numbersDisplayTex: {fileID: 2800000, guid: 73fe53b428c5b3440b7e87ee830b608a, type: 3}
|
|
||||||
- rid: 6852985685364965391
|
|
||||||
type: {class: IncludeAdditionalRPAssets, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_version: 0
|
|
||||||
m_IncludeReferencedInScenes: 0
|
|
||||||
m_IncludeAssetsByLabel: 0
|
|
||||||
m_LabelToInclude:
|
|
||||||
- rid: 6852985685364965392
|
|
||||||
type: {class: ShaderStrippingSetting, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_ExportShaderVariants: 1
|
|
||||||
m_ShaderVariantLogLevel: 0
|
|
||||||
m_StripRuntimeDebugShaders: 1
|
|
||||||
- rid: 6852985685364965393
|
|
||||||
type: {class: ProbeVolumeRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 1
|
|
||||||
probeVolumeBlendStatesCS: {fileID: 7200000, guid: a3f7b8c99de28a94684cb1daebeccf5d, type: 3}
|
|
||||||
probeVolumeUploadDataCS: {fileID: 7200000, guid: 0951de5992461754fa73650732c4954c, type: 3}
|
|
||||||
probeVolumeUploadDataL2CS: {fileID: 7200000, guid: 6196f34ed825db14b81fb3eb0ea8d931, type: 3}
|
|
||||||
- rid: 6852985685364965394
|
|
||||||
type: {class: RenderGraphGlobalSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_version: 0
|
|
||||||
m_EnableCompilationCaching: 1
|
|
||||||
m_EnableValidityChecks: 1
|
|
||||||
- rid: 8712630790384254976
|
|
||||||
type: {class: RenderGraphUtilsResources, ns: UnityEngine.Rendering.RenderGraphModule.Util, asm: Unity.RenderPipelines.Core.Runtime}
|
|
||||||
data:
|
|
||||||
m_Version: 0
|
|
||||||
m_CoreCopyPS: {fileID: 4800000, guid: 12dc59547ea167a4ab435097dd0f9add, type: 3}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 18dc0cd2c080841dea60987a38ce93fa
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"host": "http://192.168.55.3:5000",
|
|
||||||
"publicHost": "http://whdwo798.synology.me",
|
|
||||||
"account": "",
|
|
||||||
"password": ""
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 5f6f642b36f74dc5a0f44793fa605c2e
|
|
||||||
TextScriptImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8e7e8f5a82a3a134e91c54efd2274ea9
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
Binary file not shown.
@@ -1,14 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 1b8d251f9af63b746bf2f7ffe00ebb9b
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Documentation/TextMesh Pro User Guide 2016.pdf
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 6e59c59b81ab47f9b6ec5781fa725d2c
|
|
||||||
timeCreated: 1484171296
|
|
||||||
licenseType: Pro
|
|
||||||
TextScriptImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Fonts/LiberationSans - OFL.txt
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e3265ab4bf004d28a9537516768c1c75
|
|
||||||
timeCreated: 1484171297
|
|
||||||
licenseType: Pro
|
|
||||||
TrueTypeFontImporter:
|
|
||||||
serializedVersion: 2
|
|
||||||
fontSize: 16
|
|
||||||
forceTextureCase: -2
|
|
||||||
characterSpacing: 1
|
|
||||||
characterPadding: 0
|
|
||||||
includeFontData: 1
|
|
||||||
use2xBehaviour: 0
|
|
||||||
fontNames: []
|
|
||||||
fallbackFontReferences: []
|
|
||||||
customCharacters:
|
|
||||||
fontRenderingMode: 0
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Fonts/LiberationSans.ttf
|
|
||||||
uploadId: 546658
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f6c6fe0f3c5912a43a8a6707e336d2ea
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
Binary file not shown.
@@ -1,21 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d2abafb75e12afe48bb523e2ac30b43b
|
|
||||||
TrueTypeFontImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 4
|
|
||||||
fontSize: 16
|
|
||||||
forceTextureCase: -2
|
|
||||||
characterSpacing: 0
|
|
||||||
characterPadding: 1
|
|
||||||
includeFontData: 1
|
|
||||||
fontNames:
|
|
||||||
- NanumGothic
|
|
||||||
fallbackFontReferences: []
|
|
||||||
customCharacters:
|
|
||||||
fontRenderingMode: 0
|
|
||||||
ascentCalculationMode: 1
|
|
||||||
useLegacyBoundsCalculation: 0
|
|
||||||
shouldRoundAdvanceValue: 1
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e73a58f6e2794ae7b1b7e50b7fb811b0
|
|
||||||
timeCreated: 1484172806
|
|
||||||
licenseType: Pro
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF -
|
|
||||||
Drop Shadow.mat
|
|
||||||
uploadId: 546658
|
|
||||||
File diff suppressed because one or more lines are too long
-16
@@ -1,16 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 2e498d1c8094910479dc3e1b768306a4
|
|
||||||
timeCreated: 1484171803
|
|
||||||
licenseType: Pro
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF -
|
|
||||||
Fallback.asset
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 79459efec17a4d00a321bdcc27bbc385
|
|
||||||
timeCreated: 1484172856
|
|
||||||
licenseType: Pro
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF -
|
|
||||||
Outline.mat
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8f586378b4e144a9851e7b34d9b748ee
|
|
||||||
timeCreated: 1484171803
|
|
||||||
licenseType: Pro
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF.asset
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: fade42e8bc714b018fac513c043d323b
|
|
||||||
timeCreated: 1425440388
|
|
||||||
licenseType: Store
|
|
||||||
TextScriptImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Resources/LineBreaking Following Characters.txt
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d82c1b31c7e74239bff1220585707d2b
|
|
||||||
timeCreated: 1425440388
|
|
||||||
licenseType: Store
|
|
||||||
TextScriptImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Resources/LineBreaking Leading Characters.txt
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 512a49d95c0c4332bdd98131869c23c9
|
|
||||||
folderAsset: yes
|
|
||||||
timeCreated: 1441876896
|
|
||||||
licenseType: Pro
|
|
||||||
DefaultImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,659 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!21 &2103686
|
|
||||||
Material:
|
|
||||||
serializedVersion: 6
|
|
||||||
m_ObjectHideFlags: 1
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_Name: TextMeshPro/Sprite
|
|
||||||
m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3}
|
|
||||||
m_ShaderKeywords: UNITY_UI_CLIP_RECT
|
|
||||||
m_LightmapFlags: 5
|
|
||||||
m_EnableInstancingVariants: 0
|
|
||||||
m_DoubleSidedGI: 0
|
|
||||||
m_CustomRenderQueue: -1
|
|
||||||
stringTagMap: {}
|
|
||||||
disabledShaderPasses: []
|
|
||||||
m_SavedProperties:
|
|
||||||
serializedVersion: 3
|
|
||||||
m_TexEnvs:
|
|
||||||
- _MainTex:
|
|
||||||
m_Texture: {fileID: 2800000, guid: dffef66376be4fa480fb02b19edbe903, type: 3}
|
|
||||||
m_Scale: {x: 1, y: 1}
|
|
||||||
m_Offset: {x: 0, y: 0}
|
|
||||||
m_Floats:
|
|
||||||
- _ColorMask: 15
|
|
||||||
- _CullMode: 0
|
|
||||||
- _Stencil: 0
|
|
||||||
- _StencilComp: 8
|
|
||||||
- _StencilOp: 0
|
|
||||||
- _StencilReadMask: 255
|
|
||||||
- _StencilWriteMask: 255
|
|
||||||
- _UseUIAlphaClip: 0
|
|
||||||
m_Colors:
|
|
||||||
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
|
|
||||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 84a92b25f83d49b9bc132d206b370281, type: 3}
|
|
||||||
m_Name: EmojiOne
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
hashCode: -1836805472
|
|
||||||
material: {fileID: 2103686}
|
|
||||||
materialHashCode: 0
|
|
||||||
m_Version: 1.1.0
|
|
||||||
m_FaceInfo:
|
|
||||||
m_FamilyName:
|
|
||||||
m_StyleName:
|
|
||||||
m_PointSize: 0
|
|
||||||
m_Scale: 0
|
|
||||||
m_LineHeight: 0
|
|
||||||
m_AscentLine: 0
|
|
||||||
m_CapLine: 0
|
|
||||||
m_MeanLine: 0
|
|
||||||
m_Baseline: 0
|
|
||||||
m_DescentLine: 0
|
|
||||||
m_SuperscriptOffset: 0
|
|
||||||
m_SuperscriptSize: 0
|
|
||||||
m_SubscriptOffset: 0
|
|
||||||
m_SubscriptSize: 0
|
|
||||||
m_UnderlineOffset: 0
|
|
||||||
m_UnderlineThickness: 0
|
|
||||||
m_StrikethroughOffset: 0
|
|
||||||
m_StrikethroughThickness: 0
|
|
||||||
m_TabWidth: 0
|
|
||||||
spriteSheet: {fileID: 2800000, guid: dffef66376be4fa480fb02b19edbe903, type: 3}
|
|
||||||
m_SpriteCharacterTable:
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128522
|
|
||||||
m_GlyphIndex: 0
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: Smiling face with smiling eyes
|
|
||||||
m_HashCode: -1318250903
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128523
|
|
||||||
m_GlyphIndex: 1
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f60b
|
|
||||||
m_HashCode: 57188339
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128525
|
|
||||||
m_GlyphIndex: 2
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f60d
|
|
||||||
m_HashCode: 57188341
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128526
|
|
||||||
m_GlyphIndex: 3
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f60e
|
|
||||||
m_HashCode: 57188340
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128512
|
|
||||||
m_GlyphIndex: 4
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: Grinning face
|
|
||||||
m_HashCode: -95541379
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128513
|
|
||||||
m_GlyphIndex: 5
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f601
|
|
||||||
m_HashCode: 57188256
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128514
|
|
||||||
m_GlyphIndex: 6
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: Face with tears of joy
|
|
||||||
m_HashCode: 239522663
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128515
|
|
||||||
m_GlyphIndex: 7
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f603
|
|
||||||
m_HashCode: 57188258
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128516
|
|
||||||
m_GlyphIndex: 8
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f604
|
|
||||||
m_HashCode: 57188261
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128517
|
|
||||||
m_GlyphIndex: 9
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f605
|
|
||||||
m_HashCode: 57188260
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128518
|
|
||||||
m_GlyphIndex: 10
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f606
|
|
||||||
m_HashCode: 57188263
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 128521
|
|
||||||
m_GlyphIndex: 11
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f609
|
|
||||||
m_HashCode: 57188264
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 0
|
|
||||||
m_GlyphIndex: 12
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: .notdef
|
|
||||||
m_HashCode: -600915428
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 129315
|
|
||||||
m_GlyphIndex: 13
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 1f923
|
|
||||||
m_HashCode: 57200239
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 9786
|
|
||||||
m_GlyphIndex: 14
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 263a
|
|
||||||
m_HashCode: 1748406
|
|
||||||
- m_ElementType: 2
|
|
||||||
m_Unicode: 9785
|
|
||||||
m_GlyphIndex: 15
|
|
||||||
m_Scale: 1
|
|
||||||
m_Name: 2639
|
|
||||||
m_HashCode: 1748462
|
|
||||||
m_SpriteGlyphTable:
|
|
||||||
- m_Index: 0
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 0
|
|
||||||
m_Y: 384
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 1
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 128
|
|
||||||
m_Y: 384
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 2
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 256
|
|
||||||
m_Y: 384
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 3
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 384
|
|
||||||
m_Y: 384
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 4
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 0
|
|
||||||
m_Y: 256
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 5
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 128
|
|
||||||
m_Y: 256
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 6
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 256
|
|
||||||
m_Y: 256
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 7
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 384
|
|
||||||
m_Y: 256
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 8
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 0
|
|
||||||
m_Y: 128
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 9
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 128
|
|
||||||
m_Y: 128
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 10
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 256
|
|
||||||
m_Y: 128
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 11
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 384
|
|
||||||
m_Y: 128
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 12
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 0
|
|
||||||
m_Y: 0
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 13
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 128
|
|
||||||
m_Y: 0
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 14
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 256
|
|
||||||
m_Y: 0
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- m_Index: 15
|
|
||||||
m_Metrics:
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_HorizontalBearingX: 0
|
|
||||||
m_HorizontalBearingY: 115.6
|
|
||||||
m_HorizontalAdvance: 128
|
|
||||||
m_GlyphRect:
|
|
||||||
m_X: 384
|
|
||||||
m_Y: 0
|
|
||||||
m_Width: 128
|
|
||||||
m_Height: 128
|
|
||||||
m_Scale: 1
|
|
||||||
m_AtlasIndex: 0
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
spriteInfoList:
|
|
||||||
- id: 0
|
|
||||||
x: 0
|
|
||||||
y: 384
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: Smiling face with smiling eyes
|
|
||||||
hashCode: -1318250903
|
|
||||||
unicode: 128522
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 1
|
|
||||||
x: 128
|
|
||||||
y: 384
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f60b
|
|
||||||
hashCode: 57188339
|
|
||||||
unicode: 128523
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 2
|
|
||||||
x: 256
|
|
||||||
y: 384
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f60d
|
|
||||||
hashCode: 57188341
|
|
||||||
unicode: 128525
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 3
|
|
||||||
x: 384
|
|
||||||
y: 384
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f60e
|
|
||||||
hashCode: 57188340
|
|
||||||
unicode: 128526
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 4
|
|
||||||
x: 0
|
|
||||||
y: 256
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: Grinning face
|
|
||||||
hashCode: -95541379
|
|
||||||
unicode: 128512
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 5
|
|
||||||
x: 128
|
|
||||||
y: 256
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f601
|
|
||||||
hashCode: 57188256
|
|
||||||
unicode: 128513
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 6
|
|
||||||
x: 256
|
|
||||||
y: 256
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: Face with tears of joy
|
|
||||||
hashCode: 239522663
|
|
||||||
unicode: 128514
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 7
|
|
||||||
x: 384
|
|
||||||
y: 256
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f603
|
|
||||||
hashCode: 57188258
|
|
||||||
unicode: 128515
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 8
|
|
||||||
x: 0
|
|
||||||
y: 128
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f604
|
|
||||||
hashCode: 57188261
|
|
||||||
unicode: 128516
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 9
|
|
||||||
x: 128
|
|
||||||
y: 128
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f605
|
|
||||||
hashCode: 57188260
|
|
||||||
unicode: 128517
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 10
|
|
||||||
x: 256
|
|
||||||
y: 128
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f606
|
|
||||||
hashCode: 57188263
|
|
||||||
unicode: 128518
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 11
|
|
||||||
x: 384
|
|
||||||
y: 128
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f609
|
|
||||||
hashCode: 57188264
|
|
||||||
unicode: 128521
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 12
|
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f618
|
|
||||||
hashCode: 57188168
|
|
||||||
unicode: 128536
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 13
|
|
||||||
x: 128
|
|
||||||
y: 0
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 1f923
|
|
||||||
hashCode: 57200239
|
|
||||||
unicode: 129315
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 14
|
|
||||||
x: 256
|
|
||||||
y: 0
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 263a
|
|
||||||
hashCode: 1748406
|
|
||||||
unicode: 9786
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
- id: 15
|
|
||||||
x: 384
|
|
||||||
y: 0
|
|
||||||
width: 128
|
|
||||||
height: 128
|
|
||||||
xOffset: 0
|
|
||||||
yOffset: 115.6
|
|
||||||
xAdvance: 128
|
|
||||||
scale: 1
|
|
||||||
name: 2639
|
|
||||||
hashCode: 1748462
|
|
||||||
unicode: 9785
|
|
||||||
pivot: {x: 0.5, y: 0.5}
|
|
||||||
sprite: {fileID: 0}
|
|
||||||
fallbackSpriteAssets: []
|
|
||||||
--- !u!21 &1369835458
|
|
||||||
Material:
|
|
||||||
serializedVersion: 6
|
|
||||||
m_ObjectHideFlags: 1
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_Name: TextMeshPro/Sprite
|
|
||||||
m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3}
|
|
||||||
m_ShaderKeywords:
|
|
||||||
m_LightmapFlags: 5
|
|
||||||
m_EnableInstancingVariants: 0
|
|
||||||
m_DoubleSidedGI: 0
|
|
||||||
m_CustomRenderQueue: -1
|
|
||||||
stringTagMap: {}
|
|
||||||
disabledShaderPasses: []
|
|
||||||
m_SavedProperties:
|
|
||||||
serializedVersion: 3
|
|
||||||
m_TexEnvs: []
|
|
||||||
m_Floats: []
|
|
||||||
m_Colors: []
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: c41005c129ba4d66911b75229fd70b45
|
|
||||||
timeCreated: 1480316912
|
|
||||||
licenseType: Pro
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Resources/Sprite Assets/EmojiOne.asset
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f952c082cb03451daed3ee968ac6c63e
|
|
||||||
timeCreated: 1432805430
|
|
||||||
licenseType: Store
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Resources/Style Sheets/Default Style Sheet.asset
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3f5b5dff67a942289a9defa416b206f3
|
|
||||||
timeCreated: 1436653997
|
|
||||||
licenseType: Pro
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Resources/TMP Settings.asset
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 48bb5f55d8670e349b6e614913f9d910
|
|
||||||
ShaderImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
defaultTextures: []
|
|
||||||
nonModifiableTextures: []
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Shaders/TMP_Bitmap-Custom-Atlas.shader
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 1e3b057af24249748ff873be7fafee47
|
|
||||||
ShaderImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
defaultTextures: []
|
|
||||||
nonModifiableTextures: []
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Shaders/TMP_Bitmap-Mobile.shader
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 128e987d567d4e2c824d754223b3f3b0
|
|
||||||
ShaderImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
defaultTextures: []
|
|
||||||
nonModifiableTextures: []
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Shaders/TMP_Bitmap.shader
|
|
||||||
uploadId: 546658
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: dd89cf5b9246416f84610a006f916af7
|
|
||||||
ShaderImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
defaultTextures: []
|
|
||||||
nonModifiableTextures: []
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
AssetOrigin:
|
|
||||||
serializedVersion: 1
|
|
||||||
productId: 168243
|
|
||||||
packageName: VR Beats Kit
|
|
||||||
packageVersion: 2.0
|
|
||||||
assetPath: Assets/TextMesh Pro/Shaders/TMP_SDF Overlay.shader
|
|
||||||
uploadId: 546658
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user