chore: Assets 디렉토리 구조 정리 및 네이밍 컨벤션 적용

- Assets/_Game/ 하위로 게임 에셋 통합
- External/ 패키지 벤더별 분류 (Synty, Animations, UI)
- 에셋 네이밍 컨벤션 확립 및 적용
  (Data_Skill_, Data_SkillEffect_, Prefab_, Anim_, Model_, BT_ 등)
- pre-commit hook으로 네이밍 컨벤션 자동 검사 추가
- RESTRUCTURE_CHECKLIST.md 작성

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 19:08:27 +09:00
parent 309bf5f48b
commit c265f980db
17251 changed files with 2630777 additions and 206 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77605b453f0629148b3bffb54a8ffa05
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 89b25c2a9a284f54bfbc6d5bbdb8561d
timeCreated: 1715237413

View File

@@ -0,0 +1,86 @@
// Copyright (c) 2024 Synty Studios Limited. All rights reserved.
//
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
// available at: https://syntystore.com/pages/end-user-licence-agreement
//
// For additional details, see the LICENSE.MD file bundled with this software.
#if UNITY_EDITOR
using System.Collections.Generic;
using System.Net;
using UnityEngine.Networking;
namespace Synty.SidekickCharacters.Network
{
/// <summary>
/// Manages all URLs used in the application to communicate with the Synty API.
/// </summary>
internal static class UrlManager
{
// Base URL
private const string _BASE_URL = "https://api.syntystudios.com/";
// API version we're connecting to
private const int _API_VERSION = 1;
// API URLs
private const string _API_GET_DB_UPDATE = "sidekick-database";
/// <summary>
/// Creates a new POST request built from the provided URL and form data with the api key added into the header if required.
/// </summary>
/// <param name="url">The URL to attach the post request to.</param>
/// <param name="formData">Any form data to attach to the request.</param>
/// <param name="apiKey">The api key to use, if required.</param>
/// <returns>A <c>UnityWebRequest.POST</c> with the given details.</returns>
public static UnityWebRequest CreatePostRequest(string url, List<IMultipartFormSection> formData, string apiKey = "")
{
UnityWebRequest webRequest = UnityWebRequest.Post(url, formData);
if (apiKey != "")
{
webRequest.SetRequestHeader("Authorization", "Bearer " + apiKey);
}
return webRequest;
}
/// <summary>
/// Creates a new GET request built from the provided URL with the api key added into the header if required.
/// </summary>
/// <param name="url">The URL to attach the post request to.</param>
/// <param name="apiKey">The api key to use, if required.</param>
/// <returns>A <c>UnityWebRequest.GET</c> with the given details.</returns>
public static UnityWebRequest CreateGetRequest(string url, string apiKey = "")
{
UnityWebRequest webRequest = UnityWebRequest.Get(url);
if (apiKey != "")
{
webRequest.SetRequestHeader("Authorization", "Bearer " + apiKey);
}
return webRequest;
}
/// <summary>
/// Returns the base API URL.
/// </summary>
/// <returns>The base API URL.</returns>
private static string GetBaseUrl()
{
return _BASE_URL + "api/v" + _API_VERSION + "/";
}
/// <summary>
/// Gets the Sidekick DB update URL.
/// </summary>
/// <returns>The Sidekick DB URL.</returns>
public static string GetDbUpdateUrl()
{
// TODO : do we still need this encoding?
// As this URL contains "unsafe" characters, we encode them first.
return GetBaseUrl() + WebUtility.UrlEncode(_API_GET_DB_UPDATE);
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 49e26bea33a6414183d894f7dd6f0db0
timeCreated: 1715237455

View File

@@ -0,0 +1,24 @@
{
"name": "SidekickCharacters.Editor",
"rootNamespace": "Synty.SidekickCharacters",
"references": [
"GUID:17f96cd3b93974f6493e51a2f25c1241",
"GUID:478a2357cc57436488a56e564b08d223",
"GUID:343deaaf83e0cee4ca978e7df0b80d21",
"GUID:2bafac87e7f4b9b418d9448d219b01ab",
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:20c7bd479fdd98b4a8a7e965f8984457",
"GUID:21b0c8d1703a94250bfac916590cea4f"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7dc2bd83f45c34f4aa6f44c8db8e69a9
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6e14566ac09749f3965f3b03e878bc1e
timeCreated: 1715560091

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2024 Synty Studios Limited. All rights reserved.
//
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
// available at: https://syntystore.com/pages/end-user-licence-agreement
//
// For additional details, see the LICENSE.MD file bundled with this software.
namespace Synty.SidekickCharacters
{
public class DatabaseUpdateController
{
// This class is no longer required, and will be deleted in the future.
// Included as an empty class to remove code dependencies to allow for deletion without causing project errors.
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3861dbcdb267690459396145635ae2a1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
// Copyright (c) 2024 Synty Studios Limited. All rights reserved.
//
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
// available at: https://syntystore.com/pages/end-user-licence-agreement
//
// For additional details, see the LICENSE.MD file bundled with this software.
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Synty.SidekickCharacters.UI
{
[InitializeOnLoad]
public static class MenuBootstrapController
{
private const string _AUTO_OPEN_STATE = "syntySkAutoOpenState";
private const string _PREFS_CHECK_NAME = "syntySKCheckDependencies";
private static ModularCharacterWindow _sidekickCharacterWindow;
private static bool _openWindowOnStart = true;
static MenuBootstrapController()
{
EditorApplication.update += OpenWindowOnStartup;
}
/// <summary>
/// Opens the Character Creator window when Unity starts or the plugin is added to a project.
/// </summary>
private static void OpenWindowOnStartup()
{
_openWindowOnStart = EditorPrefs.GetBool(_AUTO_OPEN_STATE, true);
EditorApplication.update -= OpenWindowOnStartup;
if (_openWindowOnStart)
{
if (!SessionState.GetBool("FirstInitDone", false))
{
ShowSidekickCharacterWindow();
SessionState.SetBool("FirstInitDone", true);
}
}
}
/// <summary>
/// Creates the Sidekick Character Creator window and adds it to the toolbar.
/// </summary>
[MenuItem("Synty/Sidekick Character Tool")]
public static void ShowSidekickCharacterWindow()
{
FindSidekickCharacterWindow();
_sidekickCharacterWindow.Show();
}
/// <summary>
/// Find the existing Sidekick Character Creator window, or create one if it doesn't exist
/// </summary>
private static void FindSidekickCharacterWindow()
{
if (_sidekickCharacterWindow != null)
{
return;
}
_sidekickCharacterWindow = EditorWindow.GetWindow<ModularCharacterWindow>("Sidekick Character Tool");
_sidekickCharacterWindow.minSize = new Vector2(600, 600);
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: afe29b6282ee4928834c03be6ab3a791
timeCreated: 1715842853

View File

@@ -0,0 +1,88 @@
// Copyright (c) 2024 Synty Studios Limited. All rights reserved.
//
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
// available at: https://syntystore.com/pages/end-user-licence-agreement
//
// For additional details, see the LICENSE.MD file bundled with this software.
using Synty.SidekickCharacters.Enums;
using System.Collections.Generic;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace Synty.SidekickCharacters.Synty.SidekickCharacters.Scripts.Editor.UI
{
public class PartTypeControls
{
public CharacterPartType PartType;
public Button ClearButton;
public Button PreviousButton;
public Button NextButton;
public Button RandomButton;
public PopupField<string> PartDropdown;
public PartTypeControls()
{
}
public PartTypeControls(CharacterPartType partType, Button clearButton, Button previousButton, Button nextButton, Button randomButton, PopupField<string> partDropdown)
{
PartType = partType;
ClearButton = clearButton;
PreviousButton = previousButton;
NextButton = nextButton;
RandomButton = randomButton;
PartDropdown = partDropdown;
}
public void UpdateControls()
{
PartDropdown.SetEnabled(PartDropdown.choices.Count > 0);
if (PartType != CharacterPartType.Wrap)
{
ClearButton.SetEnabled(PartDropdown.value != "None");
}
else
{
ClearButton.SetEnabled(false);
if (string.IsNullOrEmpty(PartDropdown.value))
{
PartDropdown.SetEnabled(false);
}
}
if (PartDropdown.choices.Count > 1)
{
PreviousButton.SetEnabled(PartDropdown.index >= 1);
NextButton.SetEnabled(PartDropdown.index < PartDropdown.choices.Count - 1);
}
else
{
PreviousButton.SetEnabled(false);
NextButton.SetEnabled(false);
}
RandomButton.SetEnabled(PartDropdown.choices.Count > 2);
}
public void UpdateDropdownValues(List<string> newValues)
{
PartDropdown.choices = newValues;
PartDropdown.MarkDirtyRepaint();
}
public void SetPartDropdownValue(string value)
{
PartDropdown.value = value;
}
public void RandomisePartDropdownValue()
{
int lowValue = PartType == CharacterPartType.Wrap ? 0 : 1;
PartDropdown.index = Random.Range(lowValue, PartDropdown.choices.Count);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9ea5b8f302104c468972a217d39a305b
timeCreated: 1750736517

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d34501b02519daf44ae2d5425d4745f3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,354 @@
// Copyright (c) 2025 Synty Studios Limited. All rights reserved.
//
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
// available at: https://syntystore.com/pages/end-user-licence-agreement
//
// For additional details, see the LICENSE.MD file bundled with this software.
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Synty.SidekickCharacters
{
public class ToolDownloader : EditorWindow
{
private const string _GIT_URL = "https://github.com/SyntyStudios/SidekicksToolRelease/releases/latest/download/Sidekicks.unitypackage";
private const string _GIT_DB_URL = "https://github.com/SyntyStudios/SidekicksToolRelease/releases/latest/download/SidekicksDatabase.unitypackage";
private const string _PACKAGE_CACHE = "Assets/DownloadCache/Sidekicks.unitypackage";
private const string _DB_PACKAGE_CACHE = "Assets/DownloadCache/SidekicksDatabase.unitypackage";
private const string _VERSION_FILE = "Assets/Synty/SidekickCharacters/Scripts/Editor/version.txt";
private const string _VERSION_TAG = "\"tag_name\":";
private const string _VERSION_KEY = "sk_current_tool_version";
private const string _SIDEKICK_TOOL_MENU_ITEM = "Synty/Sidekick Character Tool";
private string _version = "-";
private Label _latestVersion;
public void Awake()
{
BackgroundUpdateCheck();
}
public void CreateGUI()
{
VisualElement root = rootVisualElement;
root.Clear();
Button downloadButton = new Button(DownloadLatestDBVersion)
{
text = "Download and Install",
style =
{
height = 40
}
};
root.Add(downloadButton);
Label textPrompt = new Label("This tool will download and install the latest version of the Sidekicks Tool.");
Label textPrompt2 = new Label("Or you can manually download from this link:\nhttps://github.com/SyntyStudios/SidekicksToolRelease/releases/latest/");
root.Add(textPrompt);
root.Add(textPrompt2);
string version = LoadCurrentInstalledVersion();
Label currentVersion = new Label("Currently Installed: " + (string.IsNullOrEmpty(version) ? "N/A" : version))
{
style =
{
marginTop = 5
}
};
_latestVersion = new Label("Latest Version: " + _version);
root.Add(currentVersion);
root.Add(_latestVersion);
Button changeLogButton = new Button()
{
text = "Show Changelog"
};
root.Add(changeLogButton);
changeLogButton.clickable.clicked += delegate
{
Application.OpenURL("https://syntystore.com/pages/sidekicks-changelog");
};
}
public async void BackgroundUpdateCheck()
{
_version = await CheckAvailableVersion();
if (_latestVersion != null)
{
_latestVersion.text = "Latest Version: " + _version;
}
if (IsNewVersionAvailable(_version))
{
DownloaderBackgroundService.ShowToolDownloaderWindow();
}
}
private async Task<string> CheckAvailableVersion()
{
HttpClient client = new HttpClient();
string uri = "http://api.github.com/repos/SyntyStudios/SidekicksToolRelease/releases/latest";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Accept", "application/vnd.github+json");
request.Headers.Add("X-GitHub-Api-Version", "2022-11-28");
request.Headers.Add("User-Agent", "Sidekicks Download Tool");
HttpResponseMessage response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
// Read the response content as a string
string responseBody = await response.Content.ReadAsStringAsync();
return GetVersionNumber(responseBody);
}
private string GetVersionNumber(string data)
{
int index = data.IndexOf(_VERSION_TAG, StringComparison.OrdinalIgnoreCase) + _VERSION_TAG.Length;
string portion = data.Substring(index, data.Length - index);
string number = portion.Substring(0, portion.IndexOf(',')).Replace("\"", "").Replace(" ", "");
return number;
}
private bool IsNewVersionAvailable(string version)
{
string currentVersion = LoadCurrentInstalledVersion();
if (string.IsNullOrEmpty(currentVersion))
{
return true;
}
if (!currentVersion.Contains('.') || !version.Contains('.'))
{
return false;
}
string[] currentSplitVersion = currentVersion.Split('.');
string[] newSplitVersion = version.Split('.');
if (currentSplitVersion.Length != newSplitVersion.Length)
{
return false;
}
for (int i = 0; i < newSplitVersion.Length; i++)
{
if (int.TryParse(currentSplitVersion[i], out int current))
{
if (int.TryParse(newSplitVersion[i], out int newVersion))
{
if (newVersion > current)
{
return true;
}
}
}
}
return false;
}
private void SaveCurrentInstalledVersion(string version)
{
File.WriteAllText(_VERSION_FILE, version);
}
private string LoadCurrentInstalledVersion()
{
if (File.Exists(_VERSION_FILE))
{
return File.ReadAllText(_VERSION_FILE);
}
return null;
}
private void DownloadLatestDBVersion()
{
WebClient client = new WebClient();
client.DownloadFileCompleted += (sender, e) =>
{
if (e.Error == null)
{
CloseOpenToolWindow();
AssetDatabase.ImportPackage(_DB_PACKAGE_CACHE, false);
AssetDatabase.importPackageCompleted += ProceedWithInstall;
}
else
{
Debug.LogError("Error downloading file: " + e.Error.Message);
}
};
// Ensure the directory exists
string directory = Path.GetDirectoryName(_DB_PACKAGE_CACHE);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
client.DownloadFileAsync(new System.Uri(_GIT_DB_URL), _DB_PACKAGE_CACHE);
}
private async void DownloadLatestToolVersion()
{
WebClient client = new WebClient();
_version = await CheckAvailableVersion();
client.DownloadFileCompleted += (sender, e) =>
{
if (e.Error == null)
{
SaveCurrentInstalledVersion(_version);
AssetDatabase.ImportPackage(_PACKAGE_CACHE, false);
AssetDatabase.importPackageCompleted += ProceedWithInstall;
}
else
{
Debug.LogError("Error downloading file: " + e.Error.Message);
}
};
// Ensure the directory exists
string directory = Path.GetDirectoryName(_PACKAGE_CACHE);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
client.DownloadFileAsync(new System.Uri(_GIT_URL), _PACKAGE_CACHE);
}
private void ProceedWithInstall(string packageName)
{
if (packageName == "SidekicksDatabase")
{
AssetDatabase.importPackageCompleted -= ProceedWithInstall;
DownloadLatestToolVersion();
}
else if (packageName == "Sidekicks")
{
AssetDatabase.importPackageCompleted -= ProceedWithInstall;
DownloaderBackgroundService.RefreshWindow();
if (EditorUtility.DisplayDialog("Installation Finished", "Sidekick Tool installation has completed.", "Re-open Sidekicks Tool",
"Close"))
{
ReopenToolWindow();
}
}
}
private bool CloseOpenToolWindow()
{
EditorWindow[] allWindows = Resources.FindObjectsOfTypeAll<EditorWindow>();
// Filter the windows to find the one with the desired type name
EditorWindow foundWindow = allWindows.FirstOrDefault(window => window.GetType().Name == "ModularCharacterWindow");
if (foundWindow != null)
{
foundWindow.Close();
Thread.Sleep(1500);
return true;
}
return false;
}
private void ReopenToolWindow()
{
try
{
EditorApplication.ExecuteMenuItem(_SIDEKICK_TOOL_MENU_ITEM);
}
catch (Exception ex)
{
Debug.LogWarning("Sidekicks Tool menu item not found. Please verify installation.");
Debug.LogWarning("Exception details: " + ex.Message);
}
}
}
/// <summary>
/// Creates an instance of the Downloader Tool, to allow checks for new versions on editor startup.
/// </summary>
[InitializeOnLoad]
public static class DownloaderBackgroundService
{
private static ToolDownloader _instance;
static DownloaderBackgroundService()
{
EditorApplication.update += CreateToolInstance;
}
static void CreateToolInstance()
{
EditorApplication.update -= CreateToolInstance;
EditorWindow[] allWindows = Resources.FindObjectsOfTypeAll<EditorWindow>();
// Filter the windows to find the one with the desired type name
EditorWindow foundWindow = allWindows.FirstOrDefault(window => window.GetType().Name == "ToolDownloader");
if (foundWindow != null)
{
_instance = (ToolDownloader) foundWindow;
}
else
{
_instance = ScriptableObject.CreateInstance<ToolDownloader>();
}
_instance.titleContent.text = "Sidekick Tool Downloader";
_instance.minSize = new Vector2(600, 150);
}
/// <summary>
/// Refreshes the Tool Downloader window to ensure it shows the latest version. Repaint is unreliable.
/// </summary>
public static void RefreshWindow()
{
if (_instance == null)
{
CreateToolInstance();
}
_instance.Close();
if (_instance == null)
{
CreateToolInstance();
}
_instance.Show();
}
[MenuItem("Synty/Sidekick Tool Downloader")]
public static void ShowToolDownloaderWindow()
{
if (_instance == null)
{
CreateToolInstance();
}
_instance.Show();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37db7a969509bfb4e9d387dbc16a24f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1 @@
1.0.37

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 982be5b55df4bb44e88732188d2f37cb
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: