chore: 외부 에셋 권한 및 줄바꿈 재기록 반영

- Assets/External 하위 샘플 및 서드파티 에셋 파일의 실행 비트 변경을 별도 커밋으로 분리
- PolygonGeneric, SidekickCharacters, Synty 도구 자산 전반의 줄바꿈 및 재직렬화 차이를 그대로 보존
- 프로젝트 고유 로직 변경과 분리해 이후 히스토리에서 외부 에셋 노이즈 범위를 식별하기 쉽게 정리
This commit is contained in:
2026-04-06 14:04:09 +09:00
parent c8edf838fd
commit cf103baf57
140 changed files with 15090 additions and 14829 deletions

View File

@@ -8,16 +8,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: FantasyKnights_01
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -164,6 +168,7 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &1358846705396397422
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -176,7 +181,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10
--- !u!114 &7967226135815873431
MonoBehaviour:
m_ObjectHideFlags: 11

View File

@@ -8,16 +8,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: FantasyKnights_02
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -164,6 +168,7 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &7993738256033606102
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -189,4 +194,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10

View File

@@ -8,16 +8,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: FantasyKnights_03
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -164,6 +168,7 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &5122993062315569858
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -176,7 +181,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10
--- !u!114 &8780454250115516571
MonoBehaviour:
m_ObjectHideFlags: 11

View File

@@ -21,16 +21,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: FantasyKnights_04
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -177,6 +181,7 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &6809264838136373543
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -189,4 +194,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10

View File

@@ -25,7 +25,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
@@ -34,16 +34,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: FantasyKnights_05
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -190,3 +194,4 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@@ -8,16 +8,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: FantasyKnights_06
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -164,6 +168,7 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &1513622437145775386
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -176,7 +181,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10
--- !u!114 &5488310361178818670
MonoBehaviour:
m_ObjectHideFlags: 11

View File

@@ -21,16 +21,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: HumanSpecies_01
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -177,6 +181,7 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &153752790335324610
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -189,4 +194,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10

View File

@@ -21,16 +21,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: HumanSpecies_02
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -148,6 +152,7 @@ Material:
- _DirtAmount: 0.306
- _DstBlend: 0
- _EnvironmentReflections: 1
- _EyelinerAmount: 0
- _FeminieAmount: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
@@ -176,6 +181,7 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &1793314631237679544
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -188,4 +194,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10

View File

@@ -21,16 +21,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: HumanSpecies_03
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -177,6 +181,7 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &7804425326570313428
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -189,4 +194,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10

View File

@@ -21,16 +21,20 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: HumanSpecies_04
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322,
type: 3}
m_Shader: {fileID: -6465566751694194690, guid: db628544640279b41a4a7aa5d75c0322, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_InvalidKeywords:
- _EMISSION
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -177,6 +181,7 @@ Material:
- _Grey: {r: 0.59748423, g: 0.59748423, b: 0.59748423, a: 0}
- _SkinColor: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &6958986610217973014
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -189,4 +194,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 5
version: 10

View File

View File

@@ -1,29 +1,29 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SQLite-net Official Portable Library")]
[assembly: AssemblyDescription("Light weight library providing easy SQLite database storage")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Krueger Systems, Inc.")]
[assembly: AssemblyProduct("SQLite-net")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SQLite-net Official Portable Library")]
[assembly: AssemblyDescription("Light weight library providing easy SQLite database storage")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Krueger Systems, Inc.")]
[assembly: AssemblyProduct("SQLite-net")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +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.
}
}
// 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

@@ -1,396 +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_NAME = "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)
{
string versionFilePath = GetVersionFilePath();
if (string.IsNullOrEmpty(versionFilePath))
{
Debug.LogWarning("Unable to resolve Sidekick version file path.");
return;
}
File.WriteAllText(versionFilePath, version);
}
private string LoadCurrentInstalledVersion()
{
string versionFilePath = GetVersionFilePath();
if (string.IsNullOrEmpty(versionFilePath))
{
return null;
}
if (File.Exists(versionFilePath))
{
return File.ReadAllText(versionFilePath);
}
return null;
}
private string GetVersionFilePath()
{
MonoScript currentScript = MonoScript.FromScriptableObject(this);
if (currentScript == null)
{
return null;
}
string scriptPath = AssetDatabase.GetAssetPath(currentScript);
if (string.IsNullOrEmpty(scriptPath))
{
return null;
}
string utilityDirectory = Path.GetDirectoryName(scriptPath);
if (string.IsNullOrEmpty(utilityDirectory))
{
return null;
}
string editorDirectory = Path.GetDirectoryName(utilityDirectory);
if (string.IsNullOrEmpty(editorDirectory))
{
return null;
}
return Path.GetFullPath(Path.Combine(editorDirectory, _VERSION_FILE_NAME));
}
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();
}
}
}
// 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/External/Models/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

@@ -1 +1 @@
1.0.37
1.0.38

View File

@@ -230,42 +230,6 @@ namespace Synty.SidekickCharacters.API
await runtime.PopulatePresetLibrary();
}
private static List<string> GetSidekickPartFiles()
{
string packageRootPath = DatabaseManager.GetPackageRootAbsolutePath();
if (!string.IsNullOrEmpty(packageRootPath))
{
string meshesRootPath = Path.Combine(packageRootPath, "Resources", "Meshes");
if (Directory.Exists(meshesRootPath))
{
return Directory
.GetFiles(meshesRootPath, "SK_*_*_*_*_*.fbx", SearchOption.AllDirectories)
.Select(ToAssetPath)
.ToList();
}
}
return Directory.GetFiles("Assets", "SK_*_*_*_*_*.fbx", SearchOption.AllDirectories).ToList();
}
private static string ToAssetPath(string fullPath)
{
if (string.IsNullOrWhiteSpace(fullPath))
{
return fullPath;
}
string normalizedFullPath = fullPath.Replace('\\', '/');
string normalizedAssetsPath = Application.dataPath.Replace('\\', '/');
if (!normalizedFullPath.StartsWith(normalizedAssetsPath, StringComparison.OrdinalIgnoreCase))
{
return normalizedFullPath;
}
return "Assets" + normalizedFullPath.Substring(normalizedAssetsPath.Length);
}
/// <summary>
/// Takes all the parts selected in the window, and combines them into a single model in the scene.
/// </summary>
@@ -548,7 +512,7 @@ namespace Synty.SidekickCharacters.API
_partOutfitToggleMap = new Dictionary<string, bool>();
_partCount = 0;
List<string> files = GetSidekickPartFiles();
List<string> files = Directory.GetFiles("Assets", "SK_*_*_*_*_*.fbx", SearchOption.AllDirectories).ToList();
foreach (CharacterPartType partType in Enum.GetValues(typeof(CharacterPartType)))
{
@@ -646,7 +610,7 @@ namespace Synty.SidekickCharacters.API
_speciesDictionary = new Dictionary<string, SidekickSpecies>();
_partCount = 0;
List<string> files = GetSidekickPartFiles();
List<string> files = Directory.GetFiles("Assets", "SK_*_*_*_*_*.fbx", SearchOption.AllDirectories).ToList();
Dictionary<string, string> filesOnDisk = new Dictionary<string, string>();
foreach (string file in files)
{

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,347 +1,347 @@
// 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 SQLite;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace Synty.SidekickCharacters.Database.DTO
{
[Table("sk_color_row")]
public class SidekickColorRow
{
private SidekickColorSet _colorSet;
private SidekickColorProperty _colorProperty;
private Color? _niceColor;
private Color? _niceMetallic;
private Color? _niceSmoothness;
private Color? _niceReflection;
private Color? _niceEmission;
private Color? _niceOpacity;
[PrimaryKey, AutoIncrement, Column("id")]
public int ID { get; set; }
[Column("ptr_color_set")]
public int PtrColorSet { get; set; }
[Column("ptr_color_property")]
public int PtrColorProperty { get; set; }
[Column("color")]
public string MainColor { get; set; }
[Column("metallic")]
public string Metallic { get; set; }
[Column("smoothness")]
public string Smoothness { get; set; }
[Column("reflection")]
public string Reflection { get; set; }
[Column("emission")]
public string Emission { get; set; }
[Column("opacity")]
public string Opacity { get; set; }
[Ignore]
public SidekickColorSet ColorSet
{
get => _colorSet;
set
{
_colorSet = value;
PtrColorSet = value.ID;
}
}
[Ignore]
public SidekickColorProperty ColorProperty
{
get => _colorProperty;
set
{
_colorProperty = value;
PtrColorProperty = value.ID;
}
}
[Ignore]
public Color NiceColor
{
get
{
_niceColor ??= ColorUtility.TryParseHtmlString("#" + MainColor, out Color color) ? color : Color.white;
return (Color) _niceColor;
}
set
{
_niceColor = value;
MainColor = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceMetallic
{
get
{
_niceMetallic ??= ColorUtility.TryParseHtmlString("#" + Metallic, out Color color) ? color : Color.white;
return (Color) _niceMetallic;
}
set
{
_niceMetallic = value;
Metallic = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceSmoothness
{
get
{
_niceSmoothness ??= ColorUtility.TryParseHtmlString("#" + Smoothness, out Color color) ? color : Color.white;
return (Color) _niceSmoothness;
}
set
{
_niceSmoothness = value;
Smoothness = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceReflection
{
get
{
_niceReflection ??= ColorUtility.TryParseHtmlString("#" + Reflection, out Color color) ? color : Color.white;
return (Color) _niceReflection;
}
set
{
_niceReflection = value;
Reflection = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceEmission
{
get
{
_niceEmission ??= ColorUtility.TryParseHtmlString("#" + Emission, out Color color) ? color : Color.white;
return (Color) _niceEmission;
}
set
{
_niceEmission = value;
Emission = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceOpacity
{
get
{
_niceOpacity ??= ColorUtility.TryParseHtmlString("#" + Opacity, out Color color) ? color : Color.white;
return (Color) _niceOpacity;
}
set
{
_niceOpacity = value;
Opacity = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public bool IsLocked { get; set; }
[Ignore]
public Image ButtonImage { get; set; }
/// <summary>
/// Gets a list of all the Color Rows in the database.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <returns>A list of all Color Rows in the database.</returns>
public static List<SidekickColorRow> GetAll(DatabaseManager dbManager)
{
List<SidekickColorRow> rows = dbManager.GetCurrentDbConnection().Table<SidekickColorRow>().ToList();
foreach (SidekickColorRow row in rows)
{
Decorate(dbManager, row);
}
return rows;
}
/// <summary>
/// Gets a specific Color Row by its database ID.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="id">The id of the required Color Row.</param>
/// <returns>The specific Color Row if it exists; otherwise null.</returns>
public static SidekickColorRow GetByID(DatabaseManager dbManager, int id)
{
SidekickColorRow row = dbManager.GetCurrentDbConnection().Find<SidekickColorRow>(id);
Decorate(dbManager, row);
return row;
}
/// <summary>
/// Gets a list of all the Color Rows in the database that have the matching Property.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="property">The property to get all the color rows for.</param>
/// <returns>A list of all color rows in the database for the given property.</returns>
public static List<SidekickColorRow> GetAllByProperty(DatabaseManager dbManager, SidekickColorProperty property)
{
List<SidekickColorRow> rows = dbManager.GetCurrentDbConnection().Table<SidekickColorRow>()
.Where(row => row.PtrColorProperty == property.ID)
.ToList();
foreach (SidekickColorRow row in rows)
{
row.ColorProperty = property;
Decorate(dbManager, row);
}
return rows;
}
/// <summary>
/// Gets a list of all the Color Rows in the database that have the matching Set and Property.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="set">The color set to get the color rows for.</param>
/// <param name="property">The property to get all the color rows for.</param>
/// <returns>A list of all color rows in the database for the given set and property.</returns>
public static List<SidekickColorRow> GetAllBySetAndProperty(DatabaseManager dbManager, SidekickColorSet set, SidekickColorProperty property)
{
List<SidekickColorRow> rows = dbManager.GetCurrentDbConnection().Table<SidekickColorRow>()
.Where(row => row.PtrColorSet == set.ID && row.PtrColorProperty == property.ID)
.ToList();
foreach (SidekickColorRow row in rows)
{
row.ColorSet = set;
row.ColorProperty = property;
Decorate(dbManager, row);
}
return rows;
}
/// <summary>
/// Gets a list of all the Color Rows in the database that have the matching Set.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="set">The color set to get the color rows for.</param>
/// <returns>A list of all color rows in the database for the given set.</returns>
public static List<SidekickColorRow> GetAllBySet(DatabaseManager dbManager, SidekickColorSet set)
{
List<SidekickColorRow> rows = dbManager.GetCurrentDbConnection().Table<SidekickColorRow>()
.Where(row => row.PtrColorSet == set.ID)
.ToList();
foreach (SidekickColorRow row in rows)
{
row.ColorSet = set;
Decorate(dbManager, row);
}
return rows;
}
/// <summary>
/// Creates a SidekickColorRow from a SidekickColorPresetRow.
/// </summary>
/// <param name="row">The SidekickColorPresetRow to convert.</param>
/// <returns>A SidekickColorRow created from a SidekickColorPresetRow.</returns>
public static SidekickColorRow CreateFromPresetColorRow(SidekickColorPresetRow row)
{
SidekickColorRow newRow = new SidekickColorRow()
{
MainColor = row.MainColor,
Emission = row.Emission,
Metallic = row.Metallic,
Opacity = row.Opacity,
Reflection = row.Reflection,
Smoothness = row.Smoothness,
ColorProperty = row.ColorProperty
};
return newRow;
}
/// <summary>
/// Ensures that the given row has its nice DTO class properties set
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="row">The color row to decorate</param>
/// <returns>A color row with all DTO class properties set</returns>
private static void Decorate(DatabaseManager dbManager, SidekickColorRow row)
{
// don't need PtrColorProperty check as should always be >= 0; if it's not, we have bad data and want the error
row.ColorProperty ??= SidekickColorProperty.GetByID(dbManager, row.PtrColorProperty);
if (row.ColorSet == null && row.PtrColorSet >= 0)
{
row.ColorSet = SidekickColorSet.GetByID(dbManager, row.PtrColorSet);
}
}
/// <summary>
/// Delete this color row from the database.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
public void Delete(DatabaseManager dbManager)
{
int deletedCount = dbManager.GetCurrentDbConnection().Delete<SidekickColorRow>(ID);
if (deletedCount == 0)
{
throw new Exception($"Could not delete color set with ID '{ID}'");
}
}
/// <summary>
/// Inserts, or updates the values in the database, depending on this object has been saved before or not.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
public void Save(DatabaseManager dbManager)
{
if (ID < 0)
{
SaveToDB(dbManager);
}
else
{
UpdateDB(dbManager);
}
}
/// <summary>
/// Saves this Color Set to the database with the current values.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
private void SaveToDB(DatabaseManager dbManager)
{
SQLiteConnection connection = dbManager.GetCurrentDbConnection();
int insertCount = connection.Insert(this);
if (insertCount == 0)
{
throw new Exception("Unable to save current color row");
}
// in theory this could return a different ID, but in practice it's highly unlikely
ID = (int) SQLite3.LastInsertRowid(connection.Handle);
}
/// <summary>
/// Updates this Color Set in the database with the current values.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
private void UpdateDB(DatabaseManager dbManager)
{
int updatedCount = dbManager.GetCurrentDbConnection().Update(this);
if (updatedCount == 0)
{
throw new Exception($"Could not update color row with ID '{ID}'");
}
}
}
}
// 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 SQLite;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace Synty.SidekickCharacters.Database.DTO
{
[Table("sk_color_row")]
public class SidekickColorRow
{
private SidekickColorSet _colorSet;
private SidekickColorProperty _colorProperty;
private Color? _niceColor;
private Color? _niceMetallic;
private Color? _niceSmoothness;
private Color? _niceReflection;
private Color? _niceEmission;
private Color? _niceOpacity;
[PrimaryKey, AutoIncrement, Column("id")]
public int ID { get; set; }
[Column("ptr_color_set")]
public int PtrColorSet { get; set; }
[Column("ptr_color_property")]
public int PtrColorProperty { get; set; }
[Column("color")]
public string MainColor { get; set; }
[Column("metallic")]
public string Metallic { get; set; }
[Column("smoothness")]
public string Smoothness { get; set; }
[Column("reflection")]
public string Reflection { get; set; }
[Column("emission")]
public string Emission { get; set; }
[Column("opacity")]
public string Opacity { get; set; }
[Ignore]
public SidekickColorSet ColorSet
{
get => _colorSet;
set
{
_colorSet = value;
PtrColorSet = value.ID;
}
}
[Ignore]
public SidekickColorProperty ColorProperty
{
get => _colorProperty;
set
{
_colorProperty = value;
PtrColorProperty = value.ID;
}
}
[Ignore]
public Color NiceColor
{
get
{
_niceColor ??= ColorUtility.TryParseHtmlString("#" + MainColor, out Color color) ? color : Color.white;
return (Color) _niceColor;
}
set
{
_niceColor = value;
MainColor = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceMetallic
{
get
{
_niceMetallic ??= ColorUtility.TryParseHtmlString("#" + Metallic, out Color color) ? color : Color.white;
return (Color) _niceMetallic;
}
set
{
_niceMetallic = value;
Metallic = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceSmoothness
{
get
{
_niceSmoothness ??= ColorUtility.TryParseHtmlString("#" + Smoothness, out Color color) ? color : Color.white;
return (Color) _niceSmoothness;
}
set
{
_niceSmoothness = value;
Smoothness = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceReflection
{
get
{
_niceReflection ??= ColorUtility.TryParseHtmlString("#" + Reflection, out Color color) ? color : Color.white;
return (Color) _niceReflection;
}
set
{
_niceReflection = value;
Reflection = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceEmission
{
get
{
_niceEmission ??= ColorUtility.TryParseHtmlString("#" + Emission, out Color color) ? color : Color.white;
return (Color) _niceEmission;
}
set
{
_niceEmission = value;
Emission = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public Color NiceOpacity
{
get
{
_niceOpacity ??= ColorUtility.TryParseHtmlString("#" + Opacity, out Color color) ? color : Color.white;
return (Color) _niceOpacity;
}
set
{
_niceOpacity = value;
Opacity = ColorUtility.ToHtmlStringRGB(value);
}
}
[Ignore]
public bool IsLocked { get; set; }
[Ignore]
public Image ButtonImage { get; set; }
/// <summary>
/// Gets a list of all the Color Rows in the database.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <returns>A list of all Color Rows in the database.</returns>
public static List<SidekickColorRow> GetAll(DatabaseManager dbManager)
{
List<SidekickColorRow> rows = dbManager.GetCurrentDbConnection().Table<SidekickColorRow>().ToList();
foreach (SidekickColorRow row in rows)
{
Decorate(dbManager, row);
}
return rows;
}
/// <summary>
/// Gets a specific Color Row by its database ID.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="id">The id of the required Color Row.</param>
/// <returns>The specific Color Row if it exists; otherwise null.</returns>
public static SidekickColorRow GetByID(DatabaseManager dbManager, int id)
{
SidekickColorRow row = dbManager.GetCurrentDbConnection().Find<SidekickColorRow>(id);
Decorate(dbManager, row);
return row;
}
/// <summary>
/// Gets a list of all the Color Rows in the database that have the matching Property.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="property">The property to get all the color rows for.</param>
/// <returns>A list of all color rows in the database for the given property.</returns>
public static List<SidekickColorRow> GetAllByProperty(DatabaseManager dbManager, SidekickColorProperty property)
{
List<SidekickColorRow> rows = dbManager.GetCurrentDbConnection().Table<SidekickColorRow>()
.Where(row => row.PtrColorProperty == property.ID)
.ToList();
foreach (SidekickColorRow row in rows)
{
row.ColorProperty = property;
Decorate(dbManager, row);
}
return rows;
}
/// <summary>
/// Gets a list of all the Color Rows in the database that have the matching Set and Property.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="set">The color set to get the color rows for.</param>
/// <param name="property">The property to get all the color rows for.</param>
/// <returns>A list of all color rows in the database for the given set and property.</returns>
public static List<SidekickColorRow> GetAllBySetAndProperty(DatabaseManager dbManager, SidekickColorSet set, SidekickColorProperty property)
{
List<SidekickColorRow> rows = dbManager.GetCurrentDbConnection().Table<SidekickColorRow>()
.Where(row => row.PtrColorSet == set.ID && row.PtrColorProperty == property.ID)
.ToList();
foreach (SidekickColorRow row in rows)
{
row.ColorSet = set;
row.ColorProperty = property;
Decorate(dbManager, row);
}
return rows;
}
/// <summary>
/// Gets a list of all the Color Rows in the database that have the matching Set.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="set">The color set to get the color rows for.</param>
/// <returns>A list of all color rows in the database for the given set.</returns>
public static List<SidekickColorRow> GetAllBySet(DatabaseManager dbManager, SidekickColorSet set)
{
List<SidekickColorRow> rows = dbManager.GetCurrentDbConnection().Table<SidekickColorRow>()
.Where(row => row.PtrColorSet == set.ID)
.ToList();
foreach (SidekickColorRow row in rows)
{
row.ColorSet = set;
Decorate(dbManager, row);
}
return rows;
}
/// <summary>
/// Creates a SidekickColorRow from a SidekickColorPresetRow.
/// </summary>
/// <param name="row">The SidekickColorPresetRow to convert.</param>
/// <returns>A SidekickColorRow created from a SidekickColorPresetRow.</returns>
public static SidekickColorRow CreateFromPresetColorRow(SidekickColorPresetRow row)
{
SidekickColorRow newRow = new SidekickColorRow()
{
MainColor = row.MainColor,
Emission = row.Emission,
Metallic = row.Metallic,
Opacity = row.Opacity,
Reflection = row.Reflection,
Smoothness = row.Smoothness,
ColorProperty = row.ColorProperty
};
return newRow;
}
/// <summary>
/// Ensures that the given row has its nice DTO class properties set
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="row">The color row to decorate</param>
/// <returns>A color row with all DTO class properties set</returns>
private static void Decorate(DatabaseManager dbManager, SidekickColorRow row)
{
// don't need PtrColorProperty check as should always be >= 0; if it's not, we have bad data and want the error
row.ColorProperty ??= SidekickColorProperty.GetByID(dbManager, row.PtrColorProperty);
if (row.ColorSet == null && row.PtrColorSet >= 0)
{
row.ColorSet = SidekickColorSet.GetByID(dbManager, row.PtrColorSet);
}
}
/// <summary>
/// Delete this color row from the database.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
public void Delete(DatabaseManager dbManager)
{
int deletedCount = dbManager.GetCurrentDbConnection().Delete<SidekickColorRow>(ID);
if (deletedCount == 0)
{
throw new Exception($"Could not delete color set with ID '{ID}'");
}
}
/// <summary>
/// Inserts, or updates the values in the database, depending on this object has been saved before or not.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
public void Save(DatabaseManager dbManager)
{
if (ID < 0)
{
SaveToDB(dbManager);
}
else
{
UpdateDB(dbManager);
}
}
/// <summary>
/// Saves this Color Set to the database with the current values.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
private void SaveToDB(DatabaseManager dbManager)
{
SQLiteConnection connection = dbManager.GetCurrentDbConnection();
int insertCount = connection.Insert(this);
if (insertCount == 0)
{
throw new Exception("Unable to save current color row");
}
// in theory this could return a different ID, but in practice it's highly unlikely
ID = (int) SQLite3.LastInsertRowid(connection.Handle);
}
/// <summary>
/// Updates this Color Set in the database with the current values.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
private void UpdateDB(DatabaseManager dbManager)
{
int updatedCount = dbManager.GetCurrentDbConnection().Update(this);
if (updatedCount == 0)
{
throw new Exception($"Could not update color row with ID '{ID}'");
}
}
}
}

View File

@@ -154,18 +154,6 @@ namespace Synty.SidekickCharacters.Database.DTO
/// <returns>A color set with all DTO class properties set</returns>
private static void Decorate(DatabaseManager dbManager, SidekickColorSet set)
{
if (set == null)
{
return;
}
set.SourceColorPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceColorPath);
set.SourceMetallicPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceMetallicPath);
set.SourceSmoothnessPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceSmoothnessPath);
set.SourceReflectionPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceReflectionPath);
set.SourceEmissionPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceEmissionPath);
set.SourceOpacityPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceOpacityPath);
if (set.Species == null && set.PtrSpecies >= 0)
{
set.Species = SidekickSpecies.GetByID(dbManager, set.PtrSpecies);

View File

@@ -222,8 +222,6 @@ namespace Synty.SidekickCharacters.Database.DTO
{
if (part != null)
{
part.Location = DatabaseManager.NormalizeLegacyAssetPath(part.Location);
if (part.Species == null && part.PtrSpecies >= 0)
{
part.Species = SidekickSpecies.GetByID(dbManager, part.PtrSpecies);

View File

@@ -1,285 +1,285 @@
// 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 SQLite;
using Synty.SidekickCharacters.Enums;
using Synty.SidekickCharacters.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Synty.SidekickCharacters.Database.DTO
{
[Table("sk_part_preset")]
public class SidekickPartPreset
{
private SidekickSpecies _species;
[PrimaryKey]
[AutoIncrement]
[Column("id")]
public int ID { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("part_group")]
public PartGroup PartGroup { get; set; }
[Column("ptr_species")]
public int PtrSpecies { get; set; }
[Column("outfit")]
public string Outfit { get; set; }
[Ignore]
public SidekickSpecies Species
{
get => _species;
set
{
_species = value;
PtrSpecies = value.ID;
}
}
/// <summary>
/// Gets a specific Preset by its database ID.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="id">The id of the required Preset.</param>
/// <returns>The specific Preset if it exists; otherwise null.</returns>
public static SidekickPartPreset GetByID(DatabaseManager dbManager, int id)
{
SidekickPartPreset partPreset = dbManager.GetCurrentDbConnection().Find<SidekickPartPreset>(id);
Decorate(dbManager, partPreset);
return partPreset;
}
/// <summary>
/// Gets a list of all the Presets in the database.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <returns>A list of all presets in the database.</returns>
public static List<SidekickPartPreset> GetAll(DatabaseManager dbManager)
{
List<SidekickPartPreset> partPresets = dbManager.GetCurrentDbConnection().Table<SidekickPartPreset>().ToList();
foreach (SidekickPartPreset partPreset in partPresets)
{
Decorate(dbManager, partPreset);
}
return partPresets;
}
/// <summary>
/// Gets a list of all the Part Presets in the database that have the matching species.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="species">The species to get all the part presets for.</param>
/// <returns>A list of all part presets in the database for the given species.</returns>
public static List<SidekickPartPreset> GetAllBySpecies(DatabaseManager dbManager, SidekickSpecies species)
{
List<SidekickPartPreset> partPresets = dbManager.GetCurrentDbConnection().Table<SidekickPartPreset>()
.Where(partPreset => partPreset.PtrSpecies == species.ID)
.ToList();
foreach (SidekickPartPreset partPreset in partPresets)
{
partPreset.Species = species;
}
return partPresets;
}
/// <summary>
/// Gets a Part Presets in the database with the matching name if one exists.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="name">The name of the preset to retrieve.</param>
/// <returns>Returns a Part Presets in the database with the matching name if one exists; otherwise null.</returns>
public static SidekickPartPreset GetByName(DatabaseManager dbManager, string name)
{
SidekickPartPreset partPreset = dbManager.GetCurrentDbConnection()
.Table<SidekickPartPreset>()
.FirstOrDefault(partPreset => partPreset.Name == name);
if (partPreset != null)
{
Decorate(dbManager, partPreset);
}
return partPreset;
}
/// <summary>
/// Gets a list of all the Part Presets in the database that have the matching species and part group.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="partGroup">The part group to filter search by.</param>
/// <returns>A list of all part presets in the database for the given species and part group.</returns>
public static List<SidekickPartPreset> GetAllByGroup(DatabaseManager dbManager, PartGroup partGroup, bool excludeMissingParts = true)
{
List<SidekickPartPreset> partPresets = dbManager.GetCurrentDbConnection().Table<SidekickPartPreset>()
.Where(partPreset => partPreset.PartGroup == partGroup)
.ToList();
foreach (SidekickPartPreset partPreset in partPresets)
{
Decorate(dbManager, partPreset);
}
if (excludeMissingParts)
{
List<SidekickPartPreset> toRemove = new List<SidekickPartPreset>();
foreach (SidekickPartPreset partPreset in partPresets)
{
if (!partPreset.HasAllPartsAvailable(dbManager))
{
toRemove.Add(partPreset);
}
}
partPresets.RemoveAll(preset => toRemove.Contains(preset));
}
return partPresets;
}
/// <summary>
/// Gets a list of all the Part Presets in the database that have the matching species and part group.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="species">The species to get all the part presets for.</param>
/// <param name="partGroup">The part group to filter search by.</param>
/// <returns>A list of all part presets in the database for the given species and part group.</returns>
public static List<SidekickPartPreset> GetAllBySpeciesAndGroup(DatabaseManager dbManager, SidekickSpecies species, PartGroup partGroup)
{
List<SidekickPartPreset> partPresets = dbManager.GetCurrentDbConnection().Table<SidekickPartPreset>()
.Where(partPreset => partPreset.PtrSpecies == species.ID && partPreset.PartGroup == partGroup)
.ToList();
foreach (SidekickPartPreset partPreset in partPresets)
{
partPreset.Species = species;
}
return partPresets;
}
/// <summary>
/// Ensures that the given set has its nice DTO class properties set
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="partPreset">The color set to decorate</param>
/// <returns>A color set with all DTO class properties set</returns>
private static void Decorate(DatabaseManager dbManager, SidekickPartPreset partPreset)
{
if (partPreset.Species == null && partPreset.PtrSpecies >= 0)
{
partPreset.Species = SidekickSpecies.GetByID(dbManager, partPreset.PtrSpecies);
}
}
/// <summary>
/// Updates or Inserts this item in the Database.
/// </summary>
/// <param name="dbManager">The database manager to use.</param>
public int Save(DatabaseManager dbManager)
{
if (ID < 0)
{
dbManager.GetCurrentDbConnection().Insert(this);
// in theory this could return a different ID, but in practice it's highly unlikely
ID = (int) SQLite3.LastInsertRowid(dbManager.GetCurrentDbConnection().Handle);
}
dbManager.GetCurrentDbConnection().Update(this);
return ID;
}
/// <summary>
/// Deletes this item from the database
/// </summary>
/// <param name="dbManager">The database manager to use.</param>
public void Delete(DatabaseManager dbManager)
{
foreach (SidekickPartPresetRow row in SidekickPartPresetRow.GetAllByPreset(dbManager, this))
{
row.Delete(dbManager);
}
foreach (SidekickPresetFilterRow row in SidekickPresetFilterRow.GetAllForPreset(dbManager, this))
{
row.Delete(dbManager);
}
SidekickPartPresetImage image = SidekickPartPresetImage.GetByPresetAndPartGroup(dbManager, this, PartGroup);
image?.Delete(dbManager);
dbManager.GetCurrentDbConnection().Delete<SidekickPartPreset>(ID);
}
/// <summary>
/// Determines if all the parts associated with this preset are valid (has a file in the project).
/// </summary>
/// <param name="dbManager">The database manager to use.</param>
/// <returns>True if all parts are valid; otherwise False.</returns>
public bool HasAllPartsAvailable(DatabaseManager dbManager)
{
List<SidekickPartPresetRow> allRows = SidekickPartPresetRow.GetAllByPreset(dbManager, this);
return allRows.Count > 0 && allRows.All(row => row.HasValidPart());
}
/// <summary>
/// Determines if all the parts associated with this preset are valid (has a file in the project).
/// </summary>
/// <param name="dbManager">The database manager to use.</param>
/// <returns>True if all parts are valid; otherwise False.</returns>
public bool HasOnlyBasePartsAndAllAvailable(DatabaseManager dbManager)
{
List<SidekickPartPresetRow> allRows = SidekickPartPresetRow.GetAllByPreset(dbManager, this);
return allRows.Count > 0 && allRows.All(row => row.HasValidPart() && (row.Part == null || PartUtils.IsBaseSpeciesPart(row.PartName)));
}
/// <summary>
/// Checks the equality of this preset to the given preset.
/// </summary>
/// <param name="other">The preset to check equality with.</param>
/// <returns>True if the presets are equal, otherwise false.</returns>
protected bool Equals(SidekickPartPreset other)
{
return ID == other.ID
&& Name == other.Name
&& PartGroup == other.PartGroup
&& PtrSpecies == other.PtrSpecies;
}
/// <inheritdoc cref="Equals"/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != this.GetType())
{
return false;
}
return Equals((SidekickPartPreset) obj);
}
/// <inheritdoc cref="GetHashCode"/>
public override int GetHashCode()
{
return HashCode.Combine(ID, Name, (int) PartGroup, PtrSpecies);
}
}
}
// 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 SQLite;
using Synty.SidekickCharacters.Enums;
using Synty.SidekickCharacters.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Synty.SidekickCharacters.Database.DTO
{
[Table("sk_part_preset")]
public class SidekickPartPreset
{
private SidekickSpecies _species;
[PrimaryKey]
[AutoIncrement]
[Column("id")]
public int ID { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("part_group")]
public PartGroup PartGroup { get; set; }
[Column("ptr_species")]
public int PtrSpecies { get; set; }
[Column("outfit")]
public string Outfit { get; set; }
[Ignore]
public SidekickSpecies Species
{
get => _species;
set
{
_species = value;
PtrSpecies = value.ID;
}
}
/// <summary>
/// Gets a specific Preset by its database ID.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="id">The id of the required Preset.</param>
/// <returns>The specific Preset if it exists; otherwise null.</returns>
public static SidekickPartPreset GetByID(DatabaseManager dbManager, int id)
{
SidekickPartPreset partPreset = dbManager.GetCurrentDbConnection().Find<SidekickPartPreset>(id);
Decorate(dbManager, partPreset);
return partPreset;
}
/// <summary>
/// Gets a list of all the Presets in the database.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <returns>A list of all presets in the database.</returns>
public static List<SidekickPartPreset> GetAll(DatabaseManager dbManager)
{
List<SidekickPartPreset> partPresets = dbManager.GetCurrentDbConnection().Table<SidekickPartPreset>().ToList();
foreach (SidekickPartPreset partPreset in partPresets)
{
Decorate(dbManager, partPreset);
}
return partPresets;
}
/// <summary>
/// Gets a list of all the Part Presets in the database that have the matching species.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="species">The species to get all the part presets for.</param>
/// <returns>A list of all part presets in the database for the given species.</returns>
public static List<SidekickPartPreset> GetAllBySpecies(DatabaseManager dbManager, SidekickSpecies species)
{
List<SidekickPartPreset> partPresets = dbManager.GetCurrentDbConnection().Table<SidekickPartPreset>()
.Where(partPreset => partPreset.PtrSpecies == species.ID)
.ToList();
foreach (SidekickPartPreset partPreset in partPresets)
{
partPreset.Species = species;
}
return partPresets;
}
/// <summary>
/// Gets a Part Presets in the database with the matching name if one exists.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="name">The name of the preset to retrieve.</param>
/// <returns>Returns a Part Presets in the database with the matching name if one exists; otherwise null.</returns>
public static SidekickPartPreset GetByName(DatabaseManager dbManager, string name)
{
SidekickPartPreset partPreset = dbManager.GetCurrentDbConnection()
.Table<SidekickPartPreset>()
.FirstOrDefault(partPreset => partPreset.Name == name);
if (partPreset != null)
{
Decorate(dbManager, partPreset);
}
return partPreset;
}
/// <summary>
/// Gets a list of all the Part Presets in the database that have the matching species and part group.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="partGroup">The part group to filter search by.</param>
/// <returns>A list of all part presets in the database for the given species and part group.</returns>
public static List<SidekickPartPreset> GetAllByGroup(DatabaseManager dbManager, PartGroup partGroup, bool excludeMissingParts = true)
{
List<SidekickPartPreset> partPresets = dbManager.GetCurrentDbConnection().Table<SidekickPartPreset>()
.Where(partPreset => partPreset.PartGroup == partGroup)
.ToList();
foreach (SidekickPartPreset partPreset in partPresets)
{
Decorate(dbManager, partPreset);
}
if (excludeMissingParts)
{
List<SidekickPartPreset> toRemove = new List<SidekickPartPreset>();
foreach (SidekickPartPreset partPreset in partPresets)
{
if (!partPreset.HasAllPartsAvailable(dbManager))
{
toRemove.Add(partPreset);
}
}
partPresets.RemoveAll(preset => toRemove.Contains(preset));
}
return partPresets;
}
/// <summary>
/// Gets a list of all the Part Presets in the database that have the matching species and part group.
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="species">The species to get all the part presets for.</param>
/// <param name="partGroup">The part group to filter search by.</param>
/// <returns>A list of all part presets in the database for the given species and part group.</returns>
public static List<SidekickPartPreset> GetAllBySpeciesAndGroup(DatabaseManager dbManager, SidekickSpecies species, PartGroup partGroup)
{
List<SidekickPartPreset> partPresets = dbManager.GetCurrentDbConnection().Table<SidekickPartPreset>()
.Where(partPreset => partPreset.PtrSpecies == species.ID && partPreset.PartGroup == partGroup)
.ToList();
foreach (SidekickPartPreset partPreset in partPresets)
{
partPreset.Species = species;
}
return partPresets;
}
/// <summary>
/// Ensures that the given set has its nice DTO class properties set
/// </summary>
/// <param name="dbManager">The Database Manager to use.</param>
/// <param name="partPreset">The color set to decorate</param>
/// <returns>A color set with all DTO class properties set</returns>
private static void Decorate(DatabaseManager dbManager, SidekickPartPreset partPreset)
{
if (partPreset.Species == null && partPreset.PtrSpecies >= 0)
{
partPreset.Species = SidekickSpecies.GetByID(dbManager, partPreset.PtrSpecies);
}
}
/// <summary>
/// Updates or Inserts this item in the Database.
/// </summary>
/// <param name="dbManager">The database manager to use.</param>
public int Save(DatabaseManager dbManager)
{
if (ID < 0)
{
dbManager.GetCurrentDbConnection().Insert(this);
// in theory this could return a different ID, but in practice it's highly unlikely
ID = (int) SQLite3.LastInsertRowid(dbManager.GetCurrentDbConnection().Handle);
}
dbManager.GetCurrentDbConnection().Update(this);
return ID;
}
/// <summary>
/// Deletes this item from the database
/// </summary>
/// <param name="dbManager">The database manager to use.</param>
public void Delete(DatabaseManager dbManager)
{
foreach (SidekickPartPresetRow row in SidekickPartPresetRow.GetAllByPreset(dbManager, this))
{
row.Delete(dbManager);
}
foreach (SidekickPresetFilterRow row in SidekickPresetFilterRow.GetAllForPreset(dbManager, this))
{
row.Delete(dbManager);
}
SidekickPartPresetImage image = SidekickPartPresetImage.GetByPresetAndPartGroup(dbManager, this, PartGroup);
image?.Delete(dbManager);
dbManager.GetCurrentDbConnection().Delete<SidekickPartPreset>(ID);
}
/// <summary>
/// Determines if all the parts associated with this preset are valid (has a file in the project).
/// </summary>
/// <param name="dbManager">The database manager to use.</param>
/// <returns>True if all parts are valid; otherwise False.</returns>
public bool HasAllPartsAvailable(DatabaseManager dbManager)
{
List<SidekickPartPresetRow> allRows = SidekickPartPresetRow.GetAllByPreset(dbManager, this);
return allRows.Count > 0 && allRows.All(row => row.HasValidPart());
}
/// <summary>
/// Determines if all the parts associated with this preset are valid (has a file in the project).
/// </summary>
/// <param name="dbManager">The database manager to use.</param>
/// <returns>True if all parts are valid; otherwise False.</returns>
public bool HasOnlyBasePartsAndAllAvailable(DatabaseManager dbManager)
{
List<SidekickPartPresetRow> allRows = SidekickPartPresetRow.GetAllByPreset(dbManager, this);
return allRows.Count > 0 && allRows.All(row => row.HasValidPart() && (row.Part == null || PartUtils.IsBaseSpeciesPart(row.PartName)));
}
/// <summary>
/// Checks the equality of this preset to the given preset.
/// </summary>
/// <param name="other">The preset to check equality with.</param>
/// <returns>True if the presets are equal, otherwise false.</returns>
protected bool Equals(SidekickPartPreset other)
{
return ID == other.ID
&& Name == other.Name
&& PartGroup == other.PartGroup
&& PtrSpecies == other.PtrSpecies;
}
/// <inheritdoc cref="Equals"/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != this.GetType())
{
return false;
}
return Equals((SidekickPartPreset) obj);
}
/// <inheritdoc cref="GetHashCode"/>
public override int GetHashCode()
{
return HashCode.Combine(ID, Name, (int) PartGroup, PtrSpecies);
}
}
}

View File

@@ -12,9 +12,7 @@
using SQLite;
using Synty.SidekickCharacters.Database.DTO;
using System;
using System.IO;
using System.Linq;
using UnityEngine;
namespace Synty.SidekickCharacters.Database
{
@@ -23,18 +21,11 @@ namespace Synty.SidekickCharacters.Database
/// </summary>
public class DatabaseManager
{
private const string _DATABASE_FILE_NAME = "Side_Kick_Data.db";
private const string _LEGACY_PACKAGE_ROOT = "Assets/Synty/SidekickCharacters";
private const string _LEGACY_TOOLS_PACKAGE_ROOT = "Assets/Synty/Tools/SidekickCharacters";
private const string _WORKING_DATABASE_DIRECTORY = "SidekickCharacters";
private static readonly string _DATABASE_PATH = "Assets/External/Models/SidekickCharacters/Database/Side_Kick_Data.db";
private readonly string _CURRENT_VERSION = "1.0.2";
private static SQLiteConnection _connection;
private static int _connectionHash;
private static string _databasePath;
private static string _seedDatabasePath;
private static string _packageRootAbsolutePath;
private static string _packageRootAssetPath;
/// <summary>
/// Gets the DB connection with the given connection details.
@@ -48,13 +39,7 @@ namespace Synty.SidekickCharacters.Database
{
if (_connection == null)
{
string databasePath = GetDatabasePath();
if (string.IsNullOrEmpty(databasePath))
{
throw new FileNotFoundException("Unable to locate Sidekick database file in this Unity project.");
}
_connection = new SQLiteConnection(databasePath, true);
_connection = new SQLiteConnection(_DATABASE_PATH, true);
}
else
{
@@ -115,12 +100,12 @@ namespace Synty.SidekickCharacters.Database
{
Species = new SidekickSpecies { ID = -1, Name = "None" },
Name = "Default",
SourceColorPath = GetPackageAssetPath("Resources", "Textures", "T_ColorMap.png"),
SourceMetallicPath = GetPackageAssetPath("Resources", "Textures", "T_MetallicMap.png"),
SourceSmoothnessPath = GetPackageAssetPath("Resources", "Textures", "T_SmoothnessMap.png"),
SourceReflectionPath = GetPackageAssetPath("Resources", "Textures", "T_ReflectionMap.png"),
SourceEmissionPath = GetPackageAssetPath("Resources", "Textures", "T_EmissionMap.png"),
SourceOpacityPath = GetPackageAssetPath("Resources", "Textures", "T_OpacityMap.png"),
SourceColorPath = "Assets/External/Models/SidekickCharacters/Resources/Textures/T_ColorMap.png",
SourceMetallicPath = "Assets/External/Models/SidekickCharacters/Resources/Textures/T_MetallicMap.png",
SourceSmoothnessPath = "Assets/External/Models/SidekickCharacters/Resources/Textures/T_SmoothnessMap.png",
SourceReflectionPath = "Assets/External/Models/SidekickCharacters/Resources/Textures/T_ReflectionMap.png",
SourceEmissionPath = "Assets/External/Models/SidekickCharacters/Resources/Textures/T_EmissionMap.png",
SourceOpacityPath = "Assets/External/Models/SidekickCharacters/Resources/Textures/T_OpacityMap.png",
};
newSet.Save(this);
@@ -221,216 +206,5 @@ namespace Synty.SidekickCharacters.Database
{
return new Version(GetCurrentDbConnection()?.Table<SidekickDBVersion>().FirstOrDefault().SemanticVersion ?? "0.0.1ea");
}
/// <summary>
/// Gets the absolute filesystem path to the Sidekick database file.
/// </summary>
/// <returns>The absolute path to the DB file, if found.</returns>
public static string GetDatabasePath()
{
if (!string.IsNullOrEmpty(_databasePath) && File.Exists(_databasePath))
{
return _databasePath;
}
string seedDatabasePath = GetSeedDatabasePath();
if (string.IsNullOrEmpty(seedDatabasePath))
{
return null;
}
string projectRoot = Directory.GetParent(Application.dataPath)?.FullName;
if (string.IsNullOrEmpty(projectRoot))
{
return null;
}
string workingDirectory = Path.Combine(projectRoot, "Library", _WORKING_DATABASE_DIRECTORY);
Directory.CreateDirectory(workingDirectory);
_databasePath = Path.Combine(workingDirectory, _DATABASE_FILE_NAME);
if (!File.Exists(_databasePath))
{
File.Copy(seedDatabasePath, _databasePath, false);
}
_databasePath = Path.GetFullPath(_databasePath);
CleanupLegacyDatabaseArtifacts(seedDatabasePath);
return _databasePath;
}
/// <summary>
/// Gets the absolute filesystem path to the Sidekick package root.
/// </summary>
/// <returns>The absolute path to the package root, if found.</returns>
public static string GetPackageRootAbsolutePath()
{
if (!string.IsNullOrEmpty(_packageRootAbsolutePath) && Directory.Exists(_packageRootAbsolutePath))
{
return _packageRootAbsolutePath;
}
string seedDatabasePath = GetSeedDatabasePath();
if (string.IsNullOrEmpty(seedDatabasePath))
{
return null;
}
string databaseDirectory = Path.GetDirectoryName(seedDatabasePath);
if (string.IsNullOrEmpty(databaseDirectory))
{
return null;
}
_packageRootAbsolutePath = Path.GetDirectoryName(databaseDirectory);
if (!string.IsNullOrEmpty(_packageRootAbsolutePath))
{
_packageRootAbsolutePath = Path.GetFullPath(_packageRootAbsolutePath);
}
return _packageRootAbsolutePath;
}
/// <summary>
/// Gets the project-relative asset path to the Sidekick package root.
/// </summary>
/// <returns>The asset path to the package root, if found.</returns>
public static string GetPackageRootAssetPath()
{
if (!string.IsNullOrEmpty(_packageRootAssetPath))
{
return _packageRootAssetPath;
}
string packageRootAbsolutePath = GetPackageRootAbsolutePath();
if (string.IsNullOrEmpty(packageRootAbsolutePath))
{
return null;
}
_packageRootAssetPath = ToAssetPath(packageRootAbsolutePath);
return _packageRootAssetPath;
}
/// <summary>
/// Builds a project-relative asset path under the Sidekick package root.
/// </summary>
/// <param name="relativeSegments">Path segments under the package root.</param>
/// <returns>The combined asset path, if the package root could be found.</returns>
public static string GetPackageAssetPath(params string[] relativeSegments)
{
string packageRootAssetPath = GetPackageRootAssetPath();
if (string.IsNullOrEmpty(packageRootAssetPath))
{
return null;
}
string combinedPath = packageRootAssetPath;
foreach (string segment in relativeSegments)
{
combinedPath = Path.Combine(combinedPath, segment);
}
return NormalizePathSeparators(combinedPath);
}
/// <summary>
/// Normalizes legacy Sidekick asset paths so moved packages still load correctly.
/// </summary>
/// <param name="assetPath">The asset path to normalize.</param>
/// <returns>The remapped path if it pointed at an old root; otherwise the original path.</returns>
public static string NormalizeLegacyAssetPath(string assetPath)
{
if (string.IsNullOrWhiteSpace(assetPath))
{
return assetPath;
}
string normalizedPath = NormalizePathSeparators(assetPath);
string packageRootAssetPath = GetPackageRootAssetPath();
if (string.IsNullOrEmpty(packageRootAssetPath))
{
return normalizedPath;
}
if (normalizedPath.StartsWith(_LEGACY_TOOLS_PACKAGE_ROOT, StringComparison.OrdinalIgnoreCase))
{
return packageRootAssetPath + normalizedPath.Substring(_LEGACY_TOOLS_PACKAGE_ROOT.Length);
}
if (normalizedPath.StartsWith(_LEGACY_PACKAGE_ROOT, StringComparison.OrdinalIgnoreCase))
{
return packageRootAssetPath + normalizedPath.Substring(_LEGACY_PACKAGE_ROOT.Length);
}
return normalizedPath;
}
private static string ToAssetPath(string fullPath)
{
string normalizedFullPath = NormalizePathSeparators(Path.GetFullPath(fullPath));
string normalizedAssetsPath = NormalizePathSeparators(Path.GetFullPath(Application.dataPath));
if (!normalizedFullPath.StartsWith(normalizedAssetsPath, StringComparison.OrdinalIgnoreCase))
{
return normalizedFullPath;
}
return "Assets" + normalizedFullPath.Substring(normalizedAssetsPath.Length);
}
private static string NormalizePathSeparators(string path)
{
return path?.Replace('\\', '/');
}
private static string GetSeedDatabasePath()
{
if (!string.IsNullOrEmpty(_seedDatabasePath) && File.Exists(_seedDatabasePath))
{
return _seedDatabasePath;
}
string[] databaseFiles = Directory.GetFiles(Application.dataPath, _DATABASE_FILE_NAME, SearchOption.AllDirectories);
_seedDatabasePath = databaseFiles
.OrderBy(path => path.Contains($"{Path.DirectorySeparatorChar}SidekickCharacters{Path.DirectorySeparatorChar}Database{Path.DirectorySeparatorChar}") ? 0 : 1)
.FirstOrDefault();
if (!string.IsNullOrEmpty(_seedDatabasePath))
{
_seedDatabasePath = Path.GetFullPath(_seedDatabasePath);
}
return _seedDatabasePath;
}
private static void CleanupLegacyDatabaseArtifacts(string seedDatabasePath)
{
if (string.IsNullOrEmpty(seedDatabasePath))
{
return;
}
DeleteIfExists(seedDatabasePath + "-journal");
DeleteIfExists(seedDatabasePath + "-journal.meta");
DeleteIfExists(seedDatabasePath + "-wal");
DeleteIfExists(seedDatabasePath + "-wal.meta");
DeleteIfExists(seedDatabasePath + "-shm");
DeleteIfExists(seedDatabasePath + "-shm.meta");
}
private static void DeleteIfExists(string path)
{
try
{
if (File.Exists(path))
{
File.Delete(path);
}
}
catch
{
}
}
}
}

View File

@@ -1,204 +1,204 @@
// 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.API;
using Synty.SidekickCharacters.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Synty.SidekickCharacters.Filters
{
public class FilterGroup
{
public SidekickRuntime Runtime;
public FilterCombineType CombineType;
private Dictionary<CharacterPartType, List<string>> _filteredParts;
private List<FilterItem> _filterItems = new List<FilterItem>();
private List<FilterGroup> _subGroups = new List<FilterGroup>();
/// <summary>
/// Adds a sub group of filters to this group.
/// </summary>
/// <param name="subGroup">The group to add as a sub group.</param>
public void AddFilterSubGroup(FilterGroup subGroup)
{
if (_subGroups.Count < 1 || _subGroups[0].CombineType == subGroup.CombineType)
{
_subGroups.Add(subGroup);
}
else
{
Debug.LogWarning("Unable to add sub group as sub groups cannot have different combine types.");
}
}
/// <summary>
/// Adds a filter item to this group.
/// </summary>
/// <param name="filterItem">The filter item to add.</param>
public void AddFilterItem(FilterItem filterItem)
{
if (_filterItems.Count < 1 || _filterItems[0].CombineType == filterItem.CombineType)
{
_filterItems.Add(filterItem);
}
else
{
Debug.LogWarning("Unable to add filter as filters cannot have different combine types.");
}
}
/// <summary>
/// Removes the given filter item from this group, if it exists.
/// </summary>
/// <param name="filterItem">The filter item to remove.</param>
public void RemoveFilterItem(FilterItem filterItem)
{
_filterItems.RemoveAll(fi => fi.Filter == filterItem.Filter);
}
/// <summary>
/// Resets the part dictionaries for all filter items.
/// </summary>
public void ResetFiltersForSpeciesChange()
{
foreach (FilterItem item in _filterItems)
{
item.ResetPartsForSpeciesChange();
}
}
/// <summary>
/// Gets a list of all parts that the filters within this group evaluate to.
/// </summary>
/// <returns>The list of all filtered parts</returns>
public Dictionary<CharacterPartType, List<string>> GetFilteredParts()
{
_filteredParts = new Dictionary<CharacterPartType, List<string>>();
// If there are no filter items, it means that we aren't filtering any parts out. Return all parts.
if (_filterItems.Count < 1)
{
return Runtime.MappedBasePartDictionary[Runtime.CurrentSpecies];
}
if (_filterItems.Count > 1)
{
FilterCombineType filterCombineType = _filterItems[1].CombineType;
if (_filterItems[0].CombineType != FilterCombineType.Or)
{
_filteredParts = Runtime.MappedPartList;
}
foreach (FilterItem item in _filterItems)
{
Dictionary<CharacterPartType, List<string>> newFilteredParts = item.GetFilteredParts();
List<string> toRemove = new List<string>();
foreach (KeyValuePair<CharacterPartType, List<string>> entry in newFilteredParts)
{
switch (filterCombineType)
{
case FilterCombineType.And:
toRemove = new List<string>();
foreach (string part in _filteredParts[entry.Key])
{
if (!entry.Value.Contains(part))
{
toRemove.Add(part);
}
}
_filteredParts[entry.Key].RemoveAll(part => toRemove.Contains(part));
break;
case FilterCombineType.Or:
HashSet<string> uniqueList = entry.Value.ToHashSet();
HashSet<string> existingList = _filteredParts.TryGetValue(entry.Key, out List<string> filteredList) ? filteredList.ToHashSet() : new HashSet<string>();
existingList.UnionWith(uniqueList);
_filteredParts[entry.Key] = existingList.ToList();
break;
case FilterCombineType.Not:
toRemove = new List<string>();
foreach (string part in _filteredParts[entry.Key])
{
if (entry.Value.Contains(part))
{
toRemove.Add(part);
}
}
_filteredParts[entry.Key].RemoveAll(part => toRemove.Contains(part));
break;
}
}
}
}
else
{
_filteredParts = _filterItems[0].GetFilteredParts();
}
foreach (FilterGroup group in _subGroups)
{
FilterCombineType filterCombineType = group.CombineType;
Dictionary<CharacterPartType, List<string>> newFilteredParts = group.GetFilteredParts();
List<string> toRemove = new List<string>();
foreach (KeyValuePair<CharacterPartType, List<string>> entry in newFilteredParts)
{
switch (filterCombineType)
{
case FilterCombineType.And:
toRemove = new List<string>();
foreach (string part in _filteredParts[entry.Key])
{
if (!entry.Value.Contains(part))
{
toRemove.Add(part);
}
}
_filteredParts[entry.Key].RemoveAll(part => toRemove.Contains(part));
break;
case FilterCombineType.Or:
HashSet<string> uniqueList = entry.Value.ToHashSet();
HashSet<string> existingList = _filteredParts[entry.Key].ToHashSet();
existingList.UnionWith(uniqueList);
_filteredParts[entry.Key] = existingList.ToList();
break;
case FilterCombineType.Not:
toRemove = new List<string>();
foreach (string part in _filteredParts[entry.Key])
{
if (entry.Value.Contains(part))
{
toRemove.Add(part);
}
}
_filteredParts[entry.Key].RemoveAll(part => toRemove.Contains(part));
break;
}
}
}
return _filteredParts;
}
}
}
// 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.API;
using Synty.SidekickCharacters.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Synty.SidekickCharacters.Filters
{
public class FilterGroup
{
public SidekickRuntime Runtime;
public FilterCombineType CombineType;
private Dictionary<CharacterPartType, List<string>> _filteredParts;
private List<FilterItem> _filterItems = new List<FilterItem>();
private List<FilterGroup> _subGroups = new List<FilterGroup>();
/// <summary>
/// Adds a sub group of filters to this group.
/// </summary>
/// <param name="subGroup">The group to add as a sub group.</param>
public void AddFilterSubGroup(FilterGroup subGroup)
{
if (_subGroups.Count < 1 || _subGroups[0].CombineType == subGroup.CombineType)
{
_subGroups.Add(subGroup);
}
else
{
Debug.LogWarning("Unable to add sub group as sub groups cannot have different combine types.");
}
}
/// <summary>
/// Adds a filter item to this group.
/// </summary>
/// <param name="filterItem">The filter item to add.</param>
public void AddFilterItem(FilterItem filterItem)
{
if (_filterItems.Count < 1 || _filterItems[0].CombineType == filterItem.CombineType)
{
_filterItems.Add(filterItem);
}
else
{
Debug.LogWarning("Unable to add filter as filters cannot have different combine types.");
}
}
/// <summary>
/// Removes the given filter item from this group, if it exists.
/// </summary>
/// <param name="filterItem">The filter item to remove.</param>
public void RemoveFilterItem(FilterItem filterItem)
{
_filterItems.RemoveAll(fi => fi.Filter == filterItem.Filter);
}
/// <summary>
/// Resets the part dictionaries for all filter items.
/// </summary>
public void ResetFiltersForSpeciesChange()
{
foreach (FilterItem item in _filterItems)
{
item.ResetPartsForSpeciesChange();
}
}
/// <summary>
/// Gets a list of all parts that the filters within this group evaluate to.
/// </summary>
/// <returns>The list of all filtered parts</returns>
public Dictionary<CharacterPartType, List<string>> GetFilteredParts()
{
_filteredParts = new Dictionary<CharacterPartType, List<string>>();
// If there are no filter items, it means that we aren't filtering any parts out. Return all parts.
if (_filterItems.Count < 1)
{
return Runtime.MappedBasePartDictionary[Runtime.CurrentSpecies];
}
if (_filterItems.Count > 1)
{
FilterCombineType filterCombineType = _filterItems[1].CombineType;
if (_filterItems[0].CombineType != FilterCombineType.Or)
{
_filteredParts = Runtime.MappedPartList;
}
foreach (FilterItem item in _filterItems)
{
Dictionary<CharacterPartType, List<string>> newFilteredParts = item.GetFilteredParts();
List<string> toRemove = new List<string>();
foreach (KeyValuePair<CharacterPartType, List<string>> entry in newFilteredParts)
{
switch (filterCombineType)
{
case FilterCombineType.And:
toRemove = new List<string>();
foreach (string part in _filteredParts[entry.Key])
{
if (!entry.Value.Contains(part))
{
toRemove.Add(part);
}
}
_filteredParts[entry.Key].RemoveAll(part => toRemove.Contains(part));
break;
case FilterCombineType.Or:
HashSet<string> uniqueList = entry.Value.ToHashSet();
HashSet<string> existingList = _filteredParts.TryGetValue(entry.Key, out List<string> filteredList) ? filteredList.ToHashSet() : new HashSet<string>();
existingList.UnionWith(uniqueList);
_filteredParts[entry.Key] = existingList.ToList();
break;
case FilterCombineType.Not:
toRemove = new List<string>();
foreach (string part in _filteredParts[entry.Key])
{
if (entry.Value.Contains(part))
{
toRemove.Add(part);
}
}
_filteredParts[entry.Key].RemoveAll(part => toRemove.Contains(part));
break;
}
}
}
}
else
{
_filteredParts = _filterItems[0].GetFilteredParts();
}
foreach (FilterGroup group in _subGroups)
{
FilterCombineType filterCombineType = group.CombineType;
Dictionary<CharacterPartType, List<string>> newFilteredParts = group.GetFilteredParts();
List<string> toRemove = new List<string>();
foreach (KeyValuePair<CharacterPartType, List<string>> entry in newFilteredParts)
{
switch (filterCombineType)
{
case FilterCombineType.And:
toRemove = new List<string>();
foreach (string part in _filteredParts[entry.Key])
{
if (!entry.Value.Contains(part))
{
toRemove.Add(part);
}
}
_filteredParts[entry.Key].RemoveAll(part => toRemove.Contains(part));
break;
case FilterCombineType.Or:
HashSet<string> uniqueList = entry.Value.ToHashSet();
HashSet<string> existingList = _filteredParts[entry.Key].ToHashSet();
existingList.UnionWith(uniqueList);
_filteredParts[entry.Key] = existingList.ToList();
break;
case FilterCombineType.Not:
toRemove = new List<string>();
foreach (string part in _filteredParts[entry.Key])
{
if (entry.Value.Contains(part))
{
toRemove.Add(part);
}
}
_filteredParts[entry.Key].RemoveAll(part => toRemove.Contains(part));
break;
}
}
}
return _filteredParts;
}
}
}

View File

@@ -1,59 +1,59 @@
// 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.API;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using System;
using System.Collections.Generic;
namespace Synty.SidekickCharacters.Filters
{
public class FilterItem
{
public SidekickRuntime Runtime;
public SidekickPartFilter Filter;
public FilterCombineType CombineType;
private Dictionary<CharacterPartType, List<string>> _filteredParts = new Dictionary<CharacterPartType, List<string>>();
public FilterItem(SidekickRuntime runtime, SidekickPartFilter filter, FilterCombineType combineType)
{
Runtime = runtime;
Filter = filter;
CombineType = combineType;
}
/// <summary>
/// Resets the part dictionary when the species is changed.
/// </summary>
public void ResetPartsForSpeciesChange()
{
_filteredParts = new Dictionary<CharacterPartType, List<string>>();
}
/// <summary>
/// Gets a list of all the parts for this filter item.
/// </summary>
/// <returns>A list of all parts for this filter item.</returns>
public Dictionary<CharacterPartType, List<string>> GetFilteredParts()
{
if (_filteredParts == null || _filteredParts.Count < 1)
{
_filteredParts = new Dictionary<CharacterPartType, List<string>>();
foreach (CharacterPartType type in Enum.GetValues(typeof(CharacterPartType)))
{
List<string> parts = SidekickPartFilterRow.GetAllPartNamesForFilterSpeciesAndType(Runtime.DBManager, Filter, Runtime.CurrentSpecies, type);
_filteredParts[type] = parts;
}
}
return _filteredParts;
}
}
}
// 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.API;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using System;
using System.Collections.Generic;
namespace Synty.SidekickCharacters.Filters
{
public class FilterItem
{
public SidekickRuntime Runtime;
public SidekickPartFilter Filter;
public FilterCombineType CombineType;
private Dictionary<CharacterPartType, List<string>> _filteredParts = new Dictionary<CharacterPartType, List<string>>();
public FilterItem(SidekickRuntime runtime, SidekickPartFilter filter, FilterCombineType combineType)
{
Runtime = runtime;
Filter = filter;
CombineType = combineType;
}
/// <summary>
/// Resets the part dictionary when the species is changed.
/// </summary>
public void ResetPartsForSpeciesChange()
{
_filteredParts = new Dictionary<CharacterPartType, List<string>>();
}
/// <summary>
/// Gets a list of all the parts for this filter item.
/// </summary>
/// <returns>A list of all parts for this filter item.</returns>
public Dictionary<CharacterPartType, List<string>> GetFilteredParts()
{
if (_filteredParts == null || _filteredParts.Count < 1)
{
_filteredParts = new Dictionary<CharacterPartType, List<string>>();
foreach (CharacterPartType type in Enum.GetValues(typeof(CharacterPartType)))
{
List<string> parts = SidekickPartFilterRow.GetAllPartNamesForFilterSpeciesAndType(Runtime.DBManager, Filter, Runtime.CurrentSpecies, type);
_filteredParts[type] = parts;
}
}
return _filteredParts;
}
}
}

View File

View File

@@ -1,36 +1,36 @@
// 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.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Synty.SidekickCharacters.Filters
{
public class ParsedPart
{
public string Species;
public string Outfit1;
public string Outfit2;
public string PartArea;
public string Filename;
public string Name;
public ParsedPart(string partName, string name)
{
Species = partName.Substring(partName.LastIndexOf('_') + 1, 2);
Outfit1 = partName.Substring(3, 4);
Outfit2 = partName.Substring(8, 4);
PartArea = partName.Substring(18, 4);
Filename = partName;
Name = name;
}
}
}
// 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.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Synty.SidekickCharacters.Filters
{
public class ParsedPart
{
public string Species;
public string Outfit1;
public string Outfit2;
public string PartArea;
public string Filename;
public string Name;
public ParsedPart(string partName, string name)
{
Species = partName.Substring(partName.LastIndexOf('_') + 1, 2);
Outfit1 = partName.Substring(3, 4);
Outfit2 = partName.Substring(8, 4);
PartArea = partName.Substring(18, 4);
Filename = partName;
Name = name;
}
}
}

View File

@@ -1,3 +1,3 @@
fileFormatVersion: 2
guid: 88015ac5c0e044149a859e8143155d4d
fileFormatVersion: 2
guid: 88015ac5c0e044149a859e8143155d4d
timeCreated: 1741050319

View File

@@ -1,45 +1,45 @@
// 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.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using System.Collections.Generic;
using System.Linq;
namespace Synty.SidekickCharacters.Filters
{
public class PresetFilterItem
{
public DatabaseManager DbManager;
public SidekickPresetFilter Filter;
public FilterCombineType CombineType;
private List<SidekickPartPreset> _filteredPresets;
public PresetFilterItem(DatabaseManager dbManager, SidekickPresetFilter filter, FilterCombineType combineType)
{
DbManager = dbManager;
Filter = filter;
CombineType = combineType;
}
/// <summary>
/// Gets a list of all the presets for this filter item.
/// </summary>
/// <returns>A list of all presets for this filter item.</returns>
public List<SidekickPartPreset> GetFilteredPresets()
{
if (_filteredPresets == null || _filteredPresets.Count < 1)
{
_filteredPresets = SidekickPresetFilterRow.GetAllForFilter(DbManager, Filter).Select(row => row.Preset).ToList();
}
return _filteredPresets;
}
}
}
// 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.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using System.Collections.Generic;
using System.Linq;
namespace Synty.SidekickCharacters.Filters
{
public class PresetFilterItem
{
public DatabaseManager DbManager;
public SidekickPresetFilter Filter;
public FilterCombineType CombineType;
private List<SidekickPartPreset> _filteredPresets;
public PresetFilterItem(DatabaseManager dbManager, SidekickPresetFilter filter, FilterCombineType combineType)
{
DbManager = dbManager;
Filter = filter;
CombineType = combineType;
}
/// <summary>
/// Gets a list of all the presets for this filter item.
/// </summary>
/// <returns>A list of all presets for this filter item.</returns>
public List<SidekickPartPreset> GetFilteredPresets()
{
if (_filteredPresets == null || _filteredPresets.Count < 1)
{
_filteredPresets = SidekickPresetFilterRow.GetAllForFilter(DbManager, Filter).Select(row => row.Preset).ToList();
}
return _filteredPresets;
}
}
}

View File

@@ -1,3 +1,3 @@
fileFormatVersion: 2
guid: b852b386e6bc4e6eab4140b0d792d22e
fileFormatVersion: 2
guid: b852b386e6bc4e6eab4140b0d792d22e
timeCreated: 1747794343

View File

@@ -1,70 +1,70 @@
// Copyright (c) 2025 Synty Studios Limited. All rights reserved.
//
// Use of this software is subject to the terms and conditions of the End User Licence Agreement (EULA)
// of the store at which you purchased this asset.
//
// Synty assets are available at:
// https://www.syntystore.com
// https://assetstore.unity.com/publishers/5217
// https://www.fab.com/sellers/Synty%20Studios
//
// Sample scripts are included only as examples and are not intended as production-ready.
using UnityEngine;
using UnityEngine.EventSystems;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
// compile errors?
using UnityEngine.InputSystem.UI;
// compile errors?
// if you are getting a compile error here you likely need to import the Input System package (com.unity.inputsystem) in the package manager or change the input setting in player settings back to 'Input Manager (Old)'
#endif
namespace Synty.SidekickCharacters.Utils
{
/// <summary>
/// Sample script that helps automatically select the correct input event module depending on your project's settings.
/// </summary>
[ExecuteAlways]
[RequireComponent(typeof(EventSystem))]
public class SampleAutoInputModule : MonoBehaviour
{
void OnEnable()
{
UpdateInputModule();
}
void UpdateInputModule()
{
#if ENABLE_INPUT_SYSTEM
// New Input System only
if (GetComponent<InputSystemUIInputModule>() == null)
{
// Remove any existing modules
foreach (var module in GetComponents<BaseInputModule>())
{
DestroyImmediate(module);
}
gameObject.AddComponent<InputSystemUIInputModule>();
if(!Application.isPlaying) Debug.Log("Added InputSystemUIInputModule (new input system)");
}
#elif ENABLE_LEGACY_INPUT_MANAGER
// Old Input Manager only
if (GetComponent<StandaloneInputModule>() == null)
{
// Remove any existing modules
foreach (var module in GetComponents<BaseInputModule>())
{
DestroyImmediate(module);
}
gameObject.AddComponent<StandaloneInputModule>();
if(!Application.isPlaying) Debug.Log("Added StandaloneInputModule (old input manager)");
}
#else
Debug.LogWarning("No input system enabled in project settings.");
#endif
}
}
}
// Copyright (c) 2025 Synty Studios Limited. All rights reserved.
//
// Use of this software is subject to the terms and conditions of the End User Licence Agreement (EULA)
// of the store at which you purchased this asset.
//
// Synty assets are available at:
// https://www.syntystore.com
// https://assetstore.unity.com/publishers/5217
// https://www.fab.com/sellers/Synty%20Studios
//
// Sample scripts are included only as examples and are not intended as production-ready.
using UnityEngine;
using UnityEngine.EventSystems;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
// compile errors?
using UnityEngine.InputSystem.UI;
// compile errors?
// if you are getting a compile error here you likely need to import the Input System package (com.unity.inputsystem) in the package manager or change the input setting in player settings back to 'Input Manager (Old)'
#endif
namespace Synty.SidekickCharacters.Utils
{
/// <summary>
/// Sample script that helps automatically select the correct input event module depending on your project's settings.
/// </summary>
[ExecuteAlways]
[RequireComponent(typeof(EventSystem))]
public class SampleAutoInputModule : MonoBehaviour
{
void OnEnable()
{
UpdateInputModule();
}
void UpdateInputModule()
{
#if ENABLE_INPUT_SYSTEM
// New Input System only
if (GetComponent<InputSystemUIInputModule>() == null)
{
// Remove any existing modules
foreach (var module in GetComponents<BaseInputModule>())
{
DestroyImmediate(module);
}
gameObject.AddComponent<InputSystemUIInputModule>();
if(!Application.isPlaying) Debug.Log("Added InputSystemUIInputModule (new input system)");
}
#elif ENABLE_LEGACY_INPUT_MANAGER
// Old Input Manager only
if (GetComponent<StandaloneInputModule>() == null)
{
// Remove any existing modules
foreach (var module in GetComponents<BaseInputModule>())
{
DestroyImmediate(module);
}
gameObject.AddComponent<StandaloneInputModule>();
if(!Application.isPlaying) Debug.Log("Added StandaloneInputModule (old input manager)");
}
#else
Debug.LogWarning("No input system enabled in project settings.");
#endif
}
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,140 +1,140 @@
using UnityEngine;
using UnityEngine.UI;
public class RandomisedLayerWeight : MonoBehaviour
{
[SerializeField] private Animator animator;
[Header("Random Variance Settings")]
[SerializeField, Range(0f, 1f)] private float maxRandomVariance = 0.5f;
[SerializeField] private Slider randomVarianceSlider;
[SerializeField] private Text maxRandomVarianceText; // UI Text to display maxRandomVariance
[Header("Layer Weight Settings")] // Changed header name to "Layer Weight Settings"
[SerializeField, Range(0f, 1f)] private float layerWeight = 1f; // Changed variable name to layerWeight
[SerializeField] private Slider layerWeightSlider; // Changed slider name to layerWeightSlider
[SerializeField] private Text layerWeightText; // Changed UI Text name to layerWeightText
[Header("Transition Settings")]
[SerializeField] private string layerName = "Emotion_Additive"; // Adjust this layer name in the Inspector
[SerializeField] private float averageTransitionTime = 0.4f;
[SerializeField] private float transitionVariationAmount = 0.3f;
[Header("Hold Settings")]
[SerializeField] private float averageHoldTime = 1.0f;
[SerializeField] private float holdVariationAmount = 1.0f;
private int layerIndex;
private float currentWeight = 0f;
private float targetWeight = 0f;
private float transitionTimer = 0f;
private float holdTimer = 0f;
void Start()
{
if (animator == null)
{
Debug.LogError("Animator component is not assigned.");
enabled = false;
return;
}
layerIndex = animator.GetLayerIndex(layerName);
if (layerIndex == -1)
{
Debug.LogError($"Layer '{layerName}' not found in the Animator.");
enabled = false;
return;
}
// Initialize sliders and UI Texts
if (randomVarianceSlider != null)
{
randomVarianceSlider.value = maxRandomVariance;
randomVarianceSlider.onValueChanged.AddListener(SetMaxRandomVariance);
}
if (layerWeightSlider != null) // Changed to layerWeightSlider
{
layerWeightSlider.value = layerWeight; // Changed to layerWeight
layerWeightSlider.onValueChanged.AddListener(SetLayerWeight); // Changed to SetLayerWeight
}
// Initialize UI Texts
if (maxRandomVarianceText != null)
{
maxRandomVarianceText.text = $"Random Variance: {maxRandomVariance:P0}";
}
if (layerWeightText != null) // Changed to layerWeightText
{
layerWeightText.text = $"Layer Weight: {layerWeight:P0}"; // Changed to Layer Weight
}
// Initialize the weight to a random value between 0 and 1
currentWeight = Random.value;
animator.SetLayerWeight(layerIndex, currentWeight * layerWeight); // Changed to layerWeight
// Start the initial transition
StartTransition();
}
void Update()
{
// Update timers
transitionTimer -= Time.deltaTime;
holdTimer -= Time.deltaTime;
// Check if it's time to transition to a new weight
if (transitionTimer <= 0f)
{
StartTransition();
}
// Otherwise, check if it's time to hold the current weight
else if (holdTimer <= 0f)
{
holdTimer = GenerateHoldTime();
}
// Smoothly adjust the weight towards the target weight
currentWeight = Mathf.Lerp(currentWeight, targetWeight, Time.deltaTime / averageTransitionTime);
animator.SetLayerWeight(layerIndex, currentWeight * layerWeight); // Changed to layerWeight
}
private void StartTransition()
{
// Set a new target weight
targetWeight = Random.Range(Mathf.Max(0f, 1f - maxRandomVariance), 1f) * layerWeight; // Changed to layerWeight
transitionTimer = GenerateTransitionTime();
}
private float GenerateTransitionTime()
{
float variation = Random.Range(-transitionVariationAmount, transitionVariationAmount);
return Mathf.Max(0f, averageTransitionTime + variation);
}
private float GenerateHoldTime()
{
float variation = Random.Range(-holdVariationAmount, holdVariationAmount);
return Mathf.Max(0f, averageHoldTime + variation);
}
private void SetMaxRandomVariance(float value)
{
maxRandomVariance = value;
if (maxRandomVarianceText != null)
{
maxRandomVarianceText.text = $"Random Variance: {maxRandomVariance:P0}";
}
}
private void SetLayerWeight(float value) // Changed method name to SetLayerWeight
{
layerWeight = value; // Changed to layerWeight
if (layerWeightText != null) // Changed to layerWeightText
{
layerWeightText.text = $"Layer Weight: {layerWeight:P0}"; // Changed to Layer Weight
}
}
}
using UnityEngine;
using UnityEngine.UI;
public class RandomisedLayerWeight : MonoBehaviour
{
[SerializeField] private Animator animator;
[Header("Random Variance Settings")]
[SerializeField, Range(0f, 1f)] private float maxRandomVariance = 0.5f;
[SerializeField] private Slider randomVarianceSlider;
[SerializeField] private Text maxRandomVarianceText; // UI Text to display maxRandomVariance
[Header("Layer Weight Settings")] // Changed header name to "Layer Weight Settings"
[SerializeField, Range(0f, 1f)] private float layerWeight = 1f; // Changed variable name to layerWeight
[SerializeField] private Slider layerWeightSlider; // Changed slider name to layerWeightSlider
[SerializeField] private Text layerWeightText; // Changed UI Text name to layerWeightText
[Header("Transition Settings")]
[SerializeField] private string layerName = "Emotion_Additive"; // Adjust this layer name in the Inspector
[SerializeField] private float averageTransitionTime = 0.4f;
[SerializeField] private float transitionVariationAmount = 0.3f;
[Header("Hold Settings")]
[SerializeField] private float averageHoldTime = 1.0f;
[SerializeField] private float holdVariationAmount = 1.0f;
private int layerIndex;
private float currentWeight = 0f;
private float targetWeight = 0f;
private float transitionTimer = 0f;
private float holdTimer = 0f;
void Start()
{
if (animator == null)
{
Debug.LogError("Animator component is not assigned.");
enabled = false;
return;
}
layerIndex = animator.GetLayerIndex(layerName);
if (layerIndex == -1)
{
Debug.LogError($"Layer '{layerName}' not found in the Animator.");
enabled = false;
return;
}
// Initialize sliders and UI Texts
if (randomVarianceSlider != null)
{
randomVarianceSlider.value = maxRandomVariance;
randomVarianceSlider.onValueChanged.AddListener(SetMaxRandomVariance);
}
if (layerWeightSlider != null) // Changed to layerWeightSlider
{
layerWeightSlider.value = layerWeight; // Changed to layerWeight
layerWeightSlider.onValueChanged.AddListener(SetLayerWeight); // Changed to SetLayerWeight
}
// Initialize UI Texts
if (maxRandomVarianceText != null)
{
maxRandomVarianceText.text = $"Random Variance: {maxRandomVariance:P0}";
}
if (layerWeightText != null) // Changed to layerWeightText
{
layerWeightText.text = $"Layer Weight: {layerWeight:P0}"; // Changed to Layer Weight
}
// Initialize the weight to a random value between 0 and 1
currentWeight = Random.value;
animator.SetLayerWeight(layerIndex, currentWeight * layerWeight); // Changed to layerWeight
// Start the initial transition
StartTransition();
}
void Update()
{
// Update timers
transitionTimer -= Time.deltaTime;
holdTimer -= Time.deltaTime;
// Check if it's time to transition to a new weight
if (transitionTimer <= 0f)
{
StartTransition();
}
// Otherwise, check if it's time to hold the current weight
else if (holdTimer <= 0f)
{
holdTimer = GenerateHoldTime();
}
// Smoothly adjust the weight towards the target weight
currentWeight = Mathf.Lerp(currentWeight, targetWeight, Time.deltaTime / averageTransitionTime);
animator.SetLayerWeight(layerIndex, currentWeight * layerWeight); // Changed to layerWeight
}
private void StartTransition()
{
// Set a new target weight
targetWeight = Random.Range(Mathf.Max(0f, 1f - maxRandomVariance), 1f) * layerWeight; // Changed to layerWeight
transitionTimer = GenerateTransitionTime();
}
private float GenerateTransitionTime()
{
float variation = Random.Range(-transitionVariationAmount, transitionVariationAmount);
return Mathf.Max(0f, averageTransitionTime + variation);
}
private float GenerateHoldTime()
{
float variation = Random.Range(-holdVariationAmount, holdVariationAmount);
return Mathf.Max(0f, averageHoldTime + variation);
}
private void SetMaxRandomVariance(float value)
{
maxRandomVariance = value;
if (maxRandomVarianceText != null)
{
maxRandomVarianceText.text = $"Random Variance: {maxRandomVariance:P0}";
}
}
private void SetLayerWeight(float value) // Changed method name to SetLayerWeight
{
layerWeight = value; // Changed to layerWeight
if (layerWeightText != null) // Changed to layerWeightText
{
layerWeightText.text = $"Layer Weight: {layerWeight:P0}"; // Changed to Layer Weight
}
}
}

View File

@@ -1,223 +1,223 @@
using Synty.SidekickCharacters.API;
using Synty.SidekickCharacters.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using Synty.SidekickCharacters.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;
namespace Synty.SidekickCharacters.Demo
{
/// <summary>
/// An example script to show how to interact with the Sidekick API in regards to colors at runtime.
/// </summary>
public class RuntimeColorDemo : MonoBehaviour
{
private readonly string _OUTPUT_MODEL_NAME = "Sidekick Character";
private Dictionary<string, SidekickPartPreset> _availableHeadPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private Dictionary<string, SidekickPartPreset> _availableUpperBodyPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private Dictionary<string, SidekickPartPreset> _availableLowerBodyPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private List<SidekickBodyShapePreset> _availableBodyShapes = new List<SidekickBodyShapePreset>();
private List<SidekickColorPreset> _availableColorPresets = new List<SidekickColorPreset>();
private int _currentHeadPresetIndex = 0;
private int _currentUpperBodyPresetIndex = 0;
private int _currentLowerBodyPresetIndex = 0;
private int _currentBodyShapePresetIndex = 0;
private int _currentColorPresetIndex = 0;
private DatabaseManager _dbManager;
private SidekickRuntime _sidekickRuntime;
private Dictionary<CharacterPartType, Dictionary<string, SidekickPart>> _partLibrary;
public TextMeshProUGUI _loadingText;
/// <inheritdoc cref="Start"/>
void Start()
{
_dbManager = new DatabaseManager();
GameObject model = Resources.Load<GameObject>("Meshes/SK_BaseModel");
Material material = Resources.Load<Material>("Materials/M_BaseMaterial");
_sidekickRuntime = new SidekickRuntime(model, material, null, _dbManager);
SidekickRuntime.PopulateToolData(_sidekickRuntime);
_partLibrary = _sidekickRuntime.MappedPartDictionary;
foreach (PartGroup partGroup in Enum.GetValues(typeof(PartGroup)))
{
// only filter head part presets by species
List<SidekickPartPreset> presets = SidekickPartPreset.GetAllByGroup(_dbManager, partGroup);
List<string> presetNames = new List<string>();
if (presets.Count < 1)
{
Debug.LogWarning("No parts found for " + partGroup + ". Please add at least 1 Sidekicks content pack.");
continue;
}
foreach (SidekickPartPreset preset in presets)
{
switch (partGroup)
{
case PartGroup.Head:
_availableHeadPresetDictionary.Add(preset.Name, preset);
break;
case PartGroup.UpperBody:
_availableUpperBodyPresetDictionary.Add(preset.Name, preset);
break;
case PartGroup.LowerBody:
_availableLowerBodyPresetDictionary.Add(preset.Name, preset);
break;
}
presetNames.Add(preset.Name);
}
}
_availableBodyShapes = SidekickBodyShapePreset.GetAll(_dbManager);
// An example of how to retrieve color presets from the database. To retrieve presets for other areas of the material, use the ColorGroup
// enum to retrieve other presets.
_availableColorPresets = SidekickColorPreset.GetAllByColorGroup(_dbManager, ColorGroup.Outfits);
_currentHeadPresetIndex = Random.Range(0, _availableHeadPresetDictionary.Count - 1);
_currentUpperBodyPresetIndex = Random.Range(0, _availableUpperBodyPresetDictionary.Count - 1);
_currentLowerBodyPresetIndex = Random.Range(0, _availableLowerBodyPresetDictionary.Count - 1);
_currentBodyShapePresetIndex = Random.Range(0, _availableBodyShapes.Count - 1);
_currentColorPresetIndex = Random.Range(0, _availableColorPresets.Count - 1);
_loadingText.enabled = false;
UpdateModel();
}
/// <summary>
/// Processes the change of the skin color on the character.
/// </summary>
/// <param name="image">The image tile that contains the color to change to.</param>
public void ProcessSkinColorChange(Image image)
{
ColorType colorType = ColorType.MainColor;
List<SidekickColorProperty> allProperties = SidekickColorProperty.GetAll(_dbManager);
List<SidekickColorProperty> selectedProperties = allProperties.FindAll(scp => scp.Name.ToLower().Contains("skin"));
foreach (SidekickColorProperty property in selectedProperties)
{
SidekickColorRow row = new SidekickColorRow()
{
ColorProperty = property,
MainColor = ColorUtility.ToHtmlStringRGB(image.color),
};
_sidekickRuntime.UpdateColor(colorType, row);
}
}
/// <summary>
/// Processes the change of the outfit color on the character.
/// </summary>
/// <param name="image">The image tile that contains the color to change to.</param>
public void ProcessOutfitColorChange(Image image)
{
ColorType colorType = ColorType.MainColor;
List<SidekickColorProperty> allProperties = SidekickColorProperty.GetAll(_dbManager);
List<SidekickColorProperty> selectedProperties = allProperties.FindAll(scp => scp.Name.ToLower().Contains("outfit"));
foreach (SidekickColorProperty property in selectedProperties)
{
SidekickColorRow row = new SidekickColorRow()
{
ColorProperty = property,
MainColor = ColorUtility.ToHtmlStringRGB(image.color),
};
_sidekickRuntime.UpdateColor(colorType, row);
}
}
/// <summary>
/// Updates the created character model.
/// </summary>
private void UpdateModel()
{
// If there aren't enough presets, stop trying to update the model.
if (_availableHeadPresetDictionary.Values.Count < 1
|| _availableUpperBodyPresetDictionary.Values.Count < 1
|| _availableLowerBodyPresetDictionary.Values.Count < 1)
{
return;
}
// Create and populate the list of parts to use from the parts list and the selected colors.
List<SidekickPartPreset> presets = new List<SidekickPartPreset>()
{
_availableHeadPresetDictionary.Values.ToArray()[_currentHeadPresetIndex],
_availableUpperBodyPresetDictionary.Values.ToArray()[_currentUpperBodyPresetIndex],
_availableLowerBodyPresetDictionary.Values.ToArray()[_currentLowerBodyPresetIndex]
};
List<SkinnedMeshRenderer> partsToUse = new List<SkinnedMeshRenderer>();
foreach (SidekickPartPreset preset in presets)
{
List<SidekickPartPresetRow> rows = SidekickPartPresetRow.GetAllByPreset(_dbManager, preset);
foreach (SidekickPartPresetRow row in rows)
{
if (!string.IsNullOrEmpty(row.PartName))
{
CharacterPartType type = Enum.Parse<CharacterPartType>(CharacterPartTypeUtils.GetTypeNameFromShortcode(row.PartType));
Dictionary<string, SidekickPart> partLocationDictionary = _partLibrary[type];
GameObject selectedPart = partLocationDictionary[row.PartName].GetPartModel();
SkinnedMeshRenderer selectedMesh = selectedPart.GetComponentInChildren<SkinnedMeshRenderer>();
partsToUse.Add(selectedMesh);
}
}
}
SidekickBodyShapePreset bodyPreset = _availableBodyShapes[_currentBodyShapePresetIndex];
_sidekickRuntime.BodyTypeBlendValue = bodyPreset.BodyType;
_sidekickRuntime.BodySizeHeavyBlendValue = bodyPreset.BodySize > 0 ? bodyPreset.BodySize : 0;
_sidekickRuntime.BodySizeSkinnyBlendValue = bodyPreset.BodySize < 0 ? -bodyPreset.BodySize : 0;
_sidekickRuntime.MusclesBlendValue = bodyPreset.Musculature;
List<SidekickColorPresetRow> colorRows = SidekickColorPresetRow.GetAllByPreset(_dbManager, _availableColorPresets[_currentColorPresetIndex]);
foreach (SidekickColorPresetRow row in colorRows)
{
SidekickColorRow colorRow = SidekickColorRow.CreateFromPresetColorRow(row);
foreach (ColorType property in Enum.GetValues(typeof(ColorType)))
{
_sidekickRuntime.UpdateColor(property, colorRow);
}
}
// Check for an existing copy of the model, if it exists, delete it so that we don't end up with duplicates.
GameObject character = GameObject.Find(_OUTPUT_MODEL_NAME);
if (character != null)
{
Destroy(character);
}
// Create a new character using the selected parts using the Sidekicks API.
character = _sidekickRuntime.CreateCharacter(_OUTPUT_MODEL_NAME, partsToUse, false, true);
}
/// <summary>
/// Gets a resource path for using with Resources.Load() from a full path.
/// </summary>
/// <param name="fullPath">The full path to get the resource path from.</param>
/// <returns>The resource path.</returns>
private string GetResourcePath(string fullPath)
{
string directory = Path.GetDirectoryName(fullPath);
int startIndex = directory.IndexOf("Resources") + 10;
directory = directory.Substring(startIndex, directory.Length - startIndex);
return Path.Combine(directory, Path.GetFileNameWithoutExtension(fullPath));
}
}
}
using Synty.SidekickCharacters.API;
using Synty.SidekickCharacters.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using Synty.SidekickCharacters.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;
namespace Synty.SidekickCharacters.Demo
{
/// <summary>
/// An example script to show how to interact with the Sidekick API in regards to colors at runtime.
/// </summary>
public class RuntimeColorDemo : MonoBehaviour
{
private readonly string _OUTPUT_MODEL_NAME = "Sidekick Character";
private Dictionary<string, SidekickPartPreset> _availableHeadPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private Dictionary<string, SidekickPartPreset> _availableUpperBodyPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private Dictionary<string, SidekickPartPreset> _availableLowerBodyPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private List<SidekickBodyShapePreset> _availableBodyShapes = new List<SidekickBodyShapePreset>();
private List<SidekickColorPreset> _availableColorPresets = new List<SidekickColorPreset>();
private int _currentHeadPresetIndex = 0;
private int _currentUpperBodyPresetIndex = 0;
private int _currentLowerBodyPresetIndex = 0;
private int _currentBodyShapePresetIndex = 0;
private int _currentColorPresetIndex = 0;
private DatabaseManager _dbManager;
private SidekickRuntime _sidekickRuntime;
private Dictionary<CharacterPartType, Dictionary<string, SidekickPart>> _partLibrary;
public TextMeshProUGUI _loadingText;
/// <inheritdoc cref="Start"/>
void Start()
{
_dbManager = new DatabaseManager();
GameObject model = Resources.Load<GameObject>("Meshes/SK_BaseModel");
Material material = Resources.Load<Material>("Materials/M_BaseMaterial");
_sidekickRuntime = new SidekickRuntime(model, material, null, _dbManager);
SidekickRuntime.PopulateToolData(_sidekickRuntime);
_partLibrary = _sidekickRuntime.MappedPartDictionary;
foreach (PartGroup partGroup in Enum.GetValues(typeof(PartGroup)))
{
// only filter head part presets by species
List<SidekickPartPreset> presets = SidekickPartPreset.GetAllByGroup(_dbManager, partGroup);
List<string> presetNames = new List<string>();
if (presets.Count < 1)
{
Debug.LogWarning("No parts found for " + partGroup + ". Please add at least 1 Sidekicks content pack.");
continue;
}
foreach (SidekickPartPreset preset in presets)
{
switch (partGroup)
{
case PartGroup.Head:
_availableHeadPresetDictionary.Add(preset.Name, preset);
break;
case PartGroup.UpperBody:
_availableUpperBodyPresetDictionary.Add(preset.Name, preset);
break;
case PartGroup.LowerBody:
_availableLowerBodyPresetDictionary.Add(preset.Name, preset);
break;
}
presetNames.Add(preset.Name);
}
}
_availableBodyShapes = SidekickBodyShapePreset.GetAll(_dbManager);
// An example of how to retrieve color presets from the database. To retrieve presets for other areas of the material, use the ColorGroup
// enum to retrieve other presets.
_availableColorPresets = SidekickColorPreset.GetAllByColorGroup(_dbManager, ColorGroup.Outfits);
_currentHeadPresetIndex = Random.Range(0, _availableHeadPresetDictionary.Count - 1);
_currentUpperBodyPresetIndex = Random.Range(0, _availableUpperBodyPresetDictionary.Count - 1);
_currentLowerBodyPresetIndex = Random.Range(0, _availableLowerBodyPresetDictionary.Count - 1);
_currentBodyShapePresetIndex = Random.Range(0, _availableBodyShapes.Count - 1);
_currentColorPresetIndex = Random.Range(0, _availableColorPresets.Count - 1);
_loadingText.enabled = false;
UpdateModel();
}
/// <summary>
/// Processes the change of the skin color on the character.
/// </summary>
/// <param name="image">The image tile that contains the color to change to.</param>
public void ProcessSkinColorChange(Image image)
{
ColorType colorType = ColorType.MainColor;
List<SidekickColorProperty> allProperties = SidekickColorProperty.GetAll(_dbManager);
List<SidekickColorProperty> selectedProperties = allProperties.FindAll(scp => scp.Name.ToLower().Contains("skin"));
foreach (SidekickColorProperty property in selectedProperties)
{
SidekickColorRow row = new SidekickColorRow()
{
ColorProperty = property,
MainColor = ColorUtility.ToHtmlStringRGB(image.color),
};
_sidekickRuntime.UpdateColor(colorType, row);
}
}
/// <summary>
/// Processes the change of the outfit color on the character.
/// </summary>
/// <param name="image">The image tile that contains the color to change to.</param>
public void ProcessOutfitColorChange(Image image)
{
ColorType colorType = ColorType.MainColor;
List<SidekickColorProperty> allProperties = SidekickColorProperty.GetAll(_dbManager);
List<SidekickColorProperty> selectedProperties = allProperties.FindAll(scp => scp.Name.ToLower().Contains("outfit"));
foreach (SidekickColorProperty property in selectedProperties)
{
SidekickColorRow row = new SidekickColorRow()
{
ColorProperty = property,
MainColor = ColorUtility.ToHtmlStringRGB(image.color),
};
_sidekickRuntime.UpdateColor(colorType, row);
}
}
/// <summary>
/// Updates the created character model.
/// </summary>
private void UpdateModel()
{
// If there aren't enough presets, stop trying to update the model.
if (_availableHeadPresetDictionary.Values.Count < 1
|| _availableUpperBodyPresetDictionary.Values.Count < 1
|| _availableLowerBodyPresetDictionary.Values.Count < 1)
{
return;
}
// Create and populate the list of parts to use from the parts list and the selected colors.
List<SidekickPartPreset> presets = new List<SidekickPartPreset>()
{
_availableHeadPresetDictionary.Values.ToArray()[_currentHeadPresetIndex],
_availableUpperBodyPresetDictionary.Values.ToArray()[_currentUpperBodyPresetIndex],
_availableLowerBodyPresetDictionary.Values.ToArray()[_currentLowerBodyPresetIndex]
};
List<SkinnedMeshRenderer> partsToUse = new List<SkinnedMeshRenderer>();
foreach (SidekickPartPreset preset in presets)
{
List<SidekickPartPresetRow> rows = SidekickPartPresetRow.GetAllByPreset(_dbManager, preset);
foreach (SidekickPartPresetRow row in rows)
{
if (!string.IsNullOrEmpty(row.PartName))
{
CharacterPartType type = Enum.Parse<CharacterPartType>(CharacterPartTypeUtils.GetTypeNameFromShortcode(row.PartType));
Dictionary<string, SidekickPart> partLocationDictionary = _partLibrary[type];
GameObject selectedPart = partLocationDictionary[row.PartName].GetPartModel();
SkinnedMeshRenderer selectedMesh = selectedPart.GetComponentInChildren<SkinnedMeshRenderer>();
partsToUse.Add(selectedMesh);
}
}
}
SidekickBodyShapePreset bodyPreset = _availableBodyShapes[_currentBodyShapePresetIndex];
_sidekickRuntime.BodyTypeBlendValue = bodyPreset.BodyType;
_sidekickRuntime.BodySizeHeavyBlendValue = bodyPreset.BodySize > 0 ? bodyPreset.BodySize : 0;
_sidekickRuntime.BodySizeSkinnyBlendValue = bodyPreset.BodySize < 0 ? -bodyPreset.BodySize : 0;
_sidekickRuntime.MusclesBlendValue = bodyPreset.Musculature;
List<SidekickColorPresetRow> colorRows = SidekickColorPresetRow.GetAllByPreset(_dbManager, _availableColorPresets[_currentColorPresetIndex]);
foreach (SidekickColorPresetRow row in colorRows)
{
SidekickColorRow colorRow = SidekickColorRow.CreateFromPresetColorRow(row);
foreach (ColorType property in Enum.GetValues(typeof(ColorType)))
{
_sidekickRuntime.UpdateColor(property, colorRow);
}
}
// Check for an existing copy of the model, if it exists, delete it so that we don't end up with duplicates.
GameObject character = GameObject.Find(_OUTPUT_MODEL_NAME);
if (character != null)
{
Destroy(character);
}
// Create a new character using the selected parts using the Sidekicks API.
character = _sidekickRuntime.CreateCharacter(_OUTPUT_MODEL_NAME, partsToUse, false, true);
}
/// <summary>
/// Gets a resource path for using with Resources.Load() from a full path.
/// </summary>
/// <param name="fullPath">The full path to get the resource path from.</param>
/// <returns>The resource path.</returns>
private string GetResourcePath(string fullPath)
{
string directory = Path.GetDirectoryName(fullPath);
int startIndex = directory.IndexOf("Resources") + 10;
directory = directory.Substring(startIndex, directory.Length - startIndex);
return Path.Combine(directory, Path.GetFileNameWithoutExtension(fullPath));
}
}
}

View File

View File

@@ -1,380 +1,380 @@
using Synty.SidekickCharacters.API;
using Synty.SidekickCharacters.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using Synty.SidekickCharacters.Utils;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;
namespace Synty.SidekickCharacters.Demo
{
/// <summary>
/// An example script to show how to interact with the Sidekick API in regards to parts at runtime.
/// </summary>
public class RuntimePartsDemo : MonoBehaviour
{
private readonly string _OUTPUT_MODEL_NAME = "Sidekick Character";
Dictionary<CharacterPartType, int> _partIndexDictionary = new Dictionary<CharacterPartType, int>();
Dictionary<CharacterPartType, Dictionary<string, SidekickPart>> _availablePartDictionary = new Dictionary<CharacterPartType, Dictionary<string, SidekickPart>>();
private DatabaseManager _dbManager;
private SidekickRuntime _sidekickRuntime;
private Dictionary<CharacterPartType, Dictionary<string, SidekickPart>> _partLibrary;
public TextMeshProUGUI _loadingText;
/// <inheritdoc cref="Start"/>
void Start()
{
// Create a new instance of the database manager to access database content.
_dbManager = new DatabaseManager();
// Load the base model and material required to create an instance of the Sidekick Runtime API.
GameObject model = Resources.Load<GameObject>("Meshes/SK_BaseModel");
Material material = Resources.Load<Material>("Materials/M_BaseMaterial");
_sidekickRuntime = new SidekickRuntime(model, material, null, _dbManager);
// Populate the parts list for easy access.
SidekickRuntime.PopulateToolData(_sidekickRuntime);
_partLibrary = _sidekickRuntime.MappedPartDictionary;
// For this example we are only interested in Upper Body parts, so we filter the list of all parts to only get the ones we want.
List<CharacterPartType> upperBodyParts = PartGroup.UpperBody.GetPartTypes();
foreach (CharacterPartType type in upperBodyParts)
{
_availablePartDictionary.Add(type, _partLibrary[type]);
_partIndexDictionary.Add(type, Random.Range(0, _availablePartDictionary[type].Count - 1));
}
_loadingText.enabled = false;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Torso part.
/// </summary>
public void ForwardTorso()
{
int index = _partIndexDictionary[CharacterPartType.Torso];
index++;
if (index >= _availablePartDictionary[CharacterPartType.Torso].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.Torso] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Torso part.
/// </summary>
public void BackwardTorso()
{
int index = _partIndexDictionary[CharacterPartType.Torso];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.Torso].Count - 1;
}
_partIndexDictionary[CharacterPartType.Torso] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the ArmUpperLeft part.
/// </summary>
public void ForwardUpperArmLeft()
{
int index = _partIndexDictionary[CharacterPartType.ArmUpperLeft];
index++;
if (index >= _availablePartDictionary[CharacterPartType.ArmUpperLeft].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.ArmUpperLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the ArmUpperLeft part.
/// </summary>
public void BackwardUpperArmLeft()
{
int index = _partIndexDictionary[CharacterPartType.ArmUpperLeft];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.ArmUpperLeft].Count - 1;
}
_partIndexDictionary[CharacterPartType.ArmUpperLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the ArmUpperRight part.
/// </summary>
public void ForwardUpperArmRight()
{
int index = _partIndexDictionary[CharacterPartType.ArmUpperRight];
index++;
if (index >= _availablePartDictionary[CharacterPartType.ArmUpperRight].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.ArmUpperRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the ArmUpperRight part.
/// </summary>
public void BackwardUpperArmRight()
{
int index = _partIndexDictionary[CharacterPartType.ArmUpperRight];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.ArmUpperRight].Count - 1;
}
_partIndexDictionary[CharacterPartType.ArmUpperRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the ArmLowerLeft part.
/// </summary>
public void ForwardLowerArmLeft()
{
int index = _partIndexDictionary[CharacterPartType.ArmLowerLeft];
index++;
if (index >= _availablePartDictionary[CharacterPartType.ArmLowerLeft].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.ArmLowerLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the ArmLowerLeft part.
/// </summary>
public void BackwardLowerArmLeft()
{
int index = _partIndexDictionary[CharacterPartType.ArmLowerLeft];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.ArmLowerLeft].Count - 1;
}
_partIndexDictionary[CharacterPartType.ArmLowerLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the ArmLowerRight part.
/// </summary>
public void ForwardLowerArmRight()
{
int index = _partIndexDictionary[CharacterPartType.ArmLowerRight];
index++;
if (index >= _availablePartDictionary[CharacterPartType.ArmLowerRight].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.ArmLowerRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the ArmLowerRight part.
/// </summary>
public void BackwardLowerArmRight()
{
int index = _partIndexDictionary[CharacterPartType.ArmLowerRight];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.ArmLowerRight].Count - 1;
}
_partIndexDictionary[CharacterPartType.ArmLowerRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the HandLeft part.
/// </summary>
public void ForwardHandLeft()
{
int index = _partIndexDictionary[CharacterPartType.HandLeft];
index++;
if (index >= _availablePartDictionary[CharacterPartType.HandLeft].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.HandLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the HandLeft part.
/// </summary>
public void BackwardHandLeft()
{
int index = _partIndexDictionary[CharacterPartType.HandLeft];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.HandLeft].Count - 1;
}
_partIndexDictionary[CharacterPartType.HandLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the HandRight part.
/// </summary>
public void ForwardHandRight()
{
int index = _partIndexDictionary[CharacterPartType.HandRight];
index++;
if (index >= _availablePartDictionary[CharacterPartType.HandRight].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.HandRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the HandRight part.
/// </summary>
public void BackwardHandRight()
{
int index = _partIndexDictionary[CharacterPartType.HandRight];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.HandRight].Count - 1;
}
_partIndexDictionary[CharacterPartType.HandRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the AttachmentBack part.
/// </summary>
public void ForwardBackAttachment()
{
int index = _partIndexDictionary[CharacterPartType.AttachmentBack];
index++;
if (index >= _availablePartDictionary[CharacterPartType.AttachmentBack].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.AttachmentBack] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the AttachmentBack part.
/// </summary>
public void BackwardBackAttachment()
{
int index = _partIndexDictionary[CharacterPartType.AttachmentBack];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.AttachmentBack].Count - 1;
}
_partIndexDictionary[CharacterPartType.AttachmentBack] = index;
UpdateModel();
}
/// <summary>
/// Updates the body size blends based on the slider values.
/// </summary>
/// <param name="slider">The UI slider to get the values from.</param>
public void UpdateBodySize(Slider slider)
{
// If the slider is greater than 0, then we update the Heavy blend and zero the Skinny.
if (slider.value > 0)
{
_sidekickRuntime.BodySizeHeavyBlendValue = slider.value;
_sidekickRuntime.BodySizeSkinnyBlendValue = 0;
}
// If the slider is 0 or below, we zero the Heavy blend, then we update the Skinny blend.
else
{
_sidekickRuntime.BodySizeHeavyBlendValue = 0;
_sidekickRuntime.BodySizeSkinnyBlendValue = -slider.value;
}
UpdateModel();
}
/// <summary>
/// Updates the created character model.
/// </summary>
private void UpdateModel()
{
// Create and populate the list of parts to use from the parts list.
List<SkinnedMeshRenderer> partsToUse = new List<SkinnedMeshRenderer>();
foreach (KeyValuePair<CharacterPartType, Dictionary<string, SidekickPart>> entry in _availablePartDictionary)
{
int index = _partIndexDictionary[entry.Key];
List<SidekickPart> parts = entry.Value.Values.ToList();
GameObject partContainer = null;
if (parts.Count > 0 && index < parts.Count)
{
if (index > parts.Count)
{
index = parts.Count - 1;
}
partContainer = parts[index].GetPartModel();
}
if (partContainer != null)
{
partsToUse.Add(partContainer.GetComponentInChildren<SkinnedMeshRenderer>());
}
}
// Check for an existing copy of the model, if it exists, delete it so that we don't end up with duplicates.
GameObject character = GameObject.Find(_OUTPUT_MODEL_NAME);
if (character != null)
{
Destroy(character);
}
// Create a new character using the selected parts using the Sidekicks API.
character = _sidekickRuntime.CreateCharacter(_OUTPUT_MODEL_NAME, partsToUse, false, true);
}
}
}
using Synty.SidekickCharacters.API;
using Synty.SidekickCharacters.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using Synty.SidekickCharacters.Utils;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;
namespace Synty.SidekickCharacters.Demo
{
/// <summary>
/// An example script to show how to interact with the Sidekick API in regards to parts at runtime.
/// </summary>
public class RuntimePartsDemo : MonoBehaviour
{
private readonly string _OUTPUT_MODEL_NAME = "Sidekick Character";
Dictionary<CharacterPartType, int> _partIndexDictionary = new Dictionary<CharacterPartType, int>();
Dictionary<CharacterPartType, Dictionary<string, SidekickPart>> _availablePartDictionary = new Dictionary<CharacterPartType, Dictionary<string, SidekickPart>>();
private DatabaseManager _dbManager;
private SidekickRuntime _sidekickRuntime;
private Dictionary<CharacterPartType, Dictionary<string, SidekickPart>> _partLibrary;
public TextMeshProUGUI _loadingText;
/// <inheritdoc cref="Start"/>
void Start()
{
// Create a new instance of the database manager to access database content.
_dbManager = new DatabaseManager();
// Load the base model and material required to create an instance of the Sidekick Runtime API.
GameObject model = Resources.Load<GameObject>("Meshes/SK_BaseModel");
Material material = Resources.Load<Material>("Materials/M_BaseMaterial");
_sidekickRuntime = new SidekickRuntime(model, material, null, _dbManager);
// Populate the parts list for easy access.
SidekickRuntime.PopulateToolData(_sidekickRuntime);
_partLibrary = _sidekickRuntime.MappedPartDictionary;
// For this example we are only interested in Upper Body parts, so we filter the list of all parts to only get the ones we want.
List<CharacterPartType> upperBodyParts = PartGroup.UpperBody.GetPartTypes();
foreach (CharacterPartType type in upperBodyParts)
{
_availablePartDictionary.Add(type, _partLibrary[type]);
_partIndexDictionary.Add(type, Random.Range(0, _availablePartDictionary[type].Count - 1));
}
_loadingText.enabled = false;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Torso part.
/// </summary>
public void ForwardTorso()
{
int index = _partIndexDictionary[CharacterPartType.Torso];
index++;
if (index >= _availablePartDictionary[CharacterPartType.Torso].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.Torso] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Torso part.
/// </summary>
public void BackwardTorso()
{
int index = _partIndexDictionary[CharacterPartType.Torso];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.Torso].Count - 1;
}
_partIndexDictionary[CharacterPartType.Torso] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the ArmUpperLeft part.
/// </summary>
public void ForwardUpperArmLeft()
{
int index = _partIndexDictionary[CharacterPartType.ArmUpperLeft];
index++;
if (index >= _availablePartDictionary[CharacterPartType.ArmUpperLeft].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.ArmUpperLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the ArmUpperLeft part.
/// </summary>
public void BackwardUpperArmLeft()
{
int index = _partIndexDictionary[CharacterPartType.ArmUpperLeft];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.ArmUpperLeft].Count - 1;
}
_partIndexDictionary[CharacterPartType.ArmUpperLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the ArmUpperRight part.
/// </summary>
public void ForwardUpperArmRight()
{
int index = _partIndexDictionary[CharacterPartType.ArmUpperRight];
index++;
if (index >= _availablePartDictionary[CharacterPartType.ArmUpperRight].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.ArmUpperRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the ArmUpperRight part.
/// </summary>
public void BackwardUpperArmRight()
{
int index = _partIndexDictionary[CharacterPartType.ArmUpperRight];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.ArmUpperRight].Count - 1;
}
_partIndexDictionary[CharacterPartType.ArmUpperRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the ArmLowerLeft part.
/// </summary>
public void ForwardLowerArmLeft()
{
int index = _partIndexDictionary[CharacterPartType.ArmLowerLeft];
index++;
if (index >= _availablePartDictionary[CharacterPartType.ArmLowerLeft].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.ArmLowerLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the ArmLowerLeft part.
/// </summary>
public void BackwardLowerArmLeft()
{
int index = _partIndexDictionary[CharacterPartType.ArmLowerLeft];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.ArmLowerLeft].Count - 1;
}
_partIndexDictionary[CharacterPartType.ArmLowerLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the ArmLowerRight part.
/// </summary>
public void ForwardLowerArmRight()
{
int index = _partIndexDictionary[CharacterPartType.ArmLowerRight];
index++;
if (index >= _availablePartDictionary[CharacterPartType.ArmLowerRight].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.ArmLowerRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the ArmLowerRight part.
/// </summary>
public void BackwardLowerArmRight()
{
int index = _partIndexDictionary[CharacterPartType.ArmLowerRight];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.ArmLowerRight].Count - 1;
}
_partIndexDictionary[CharacterPartType.ArmLowerRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the HandLeft part.
/// </summary>
public void ForwardHandLeft()
{
int index = _partIndexDictionary[CharacterPartType.HandLeft];
index++;
if (index >= _availablePartDictionary[CharacterPartType.HandLeft].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.HandLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the HandLeft part.
/// </summary>
public void BackwardHandLeft()
{
int index = _partIndexDictionary[CharacterPartType.HandLeft];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.HandLeft].Count - 1;
}
_partIndexDictionary[CharacterPartType.HandLeft] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the HandRight part.
/// </summary>
public void ForwardHandRight()
{
int index = _partIndexDictionary[CharacterPartType.HandRight];
index++;
if (index >= _availablePartDictionary[CharacterPartType.HandRight].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.HandRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the HandRight part.
/// </summary>
public void BackwardHandRight()
{
int index = _partIndexDictionary[CharacterPartType.HandRight];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.HandRight].Count - 1;
}
_partIndexDictionary[CharacterPartType.HandRight] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the AttachmentBack part.
/// </summary>
public void ForwardBackAttachment()
{
int index = _partIndexDictionary[CharacterPartType.AttachmentBack];
index++;
if (index >= _availablePartDictionary[CharacterPartType.AttachmentBack].Count)
{
index = 0;
}
_partIndexDictionary[CharacterPartType.AttachmentBack] = index;
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the AttachmentBack part.
/// </summary>
public void BackwardBackAttachment()
{
int index = _partIndexDictionary[CharacterPartType.AttachmentBack];
index--;
if (index < 0)
{
index = _availablePartDictionary[CharacterPartType.AttachmentBack].Count - 1;
}
_partIndexDictionary[CharacterPartType.AttachmentBack] = index;
UpdateModel();
}
/// <summary>
/// Updates the body size blends based on the slider values.
/// </summary>
/// <param name="slider">The UI slider to get the values from.</param>
public void UpdateBodySize(Slider slider)
{
// If the slider is greater than 0, then we update the Heavy blend and zero the Skinny.
if (slider.value > 0)
{
_sidekickRuntime.BodySizeHeavyBlendValue = slider.value;
_sidekickRuntime.BodySizeSkinnyBlendValue = 0;
}
// If the slider is 0 or below, we zero the Heavy blend, then we update the Skinny blend.
else
{
_sidekickRuntime.BodySizeHeavyBlendValue = 0;
_sidekickRuntime.BodySizeSkinnyBlendValue = -slider.value;
}
UpdateModel();
}
/// <summary>
/// Updates the created character model.
/// </summary>
private void UpdateModel()
{
// Create and populate the list of parts to use from the parts list.
List<SkinnedMeshRenderer> partsToUse = new List<SkinnedMeshRenderer>();
foreach (KeyValuePair<CharacterPartType, Dictionary<string, SidekickPart>> entry in _availablePartDictionary)
{
int index = _partIndexDictionary[entry.Key];
List<SidekickPart> parts = entry.Value.Values.ToList();
GameObject partContainer = null;
if (parts.Count > 0 && index < parts.Count)
{
if (index > parts.Count)
{
index = parts.Count - 1;
}
partContainer = parts[index].GetPartModel();
}
if (partContainer != null)
{
partsToUse.Add(partContainer.GetComponentInChildren<SkinnedMeshRenderer>());
}
}
// Check for an existing copy of the model, if it exists, delete it so that we don't end up with duplicates.
GameObject character = GameObject.Find(_OUTPUT_MODEL_NAME);
if (character != null)
{
Destroy(character);
}
// Create a new character using the selected parts using the Sidekicks API.
character = _sidekickRuntime.CreateCharacter(_OUTPUT_MODEL_NAME, partsToUse, false, true);
}
}
}

View File

View File

@@ -1,325 +1,325 @@
using Synty.SidekickCharacters.API;
using Synty.SidekickCharacters.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using Synty.SidekickCharacters.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using TMPro;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Synty.SidekickCharacters.Demo
{
/// <summary>
/// An example script to show how to interact with the Sidekick API in regards to presets at runtime.
/// </summary>
public class RuntimePresetDemo : MonoBehaviour
{
private readonly string _OUTPUT_MODEL_NAME = "Sidekick Character";
private Dictionary<string, SidekickPartPreset> _availableHeadPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private Dictionary<string, SidekickPartPreset> _availableUpperBodyPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private Dictionary<string, SidekickPartPreset> _availableLowerBodyPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private List<SidekickBodyShapePreset> _availableBodyShapes = new List<SidekickBodyShapePreset>();
private List<SidekickColorPreset> _availableColorPresets = new List<SidekickColorPreset>();
private int _currentHeadPresetIndex = 0;
private int _currentUpperBodyPresetIndex = 0;
private int _currentLowerBodyPresetIndex = 0;
private int _currentBodyShapePresetIndex = 0;
private int _currentColorPresetIndex = 0;
private DatabaseManager _dbManager;
private SidekickRuntime _sidekickRuntime;
private Dictionary<CharacterPartType, Dictionary<string, SidekickPart>> _partLibrary;
public TextMeshProUGUI _loadingText;
/// <inheritdoc cref="Start"/>
void Start()
{
_dbManager = new DatabaseManager();
GameObject model = Resources.Load<GameObject>("Meshes/SK_BaseModel");
Material material = Resources.Load<Material>("Materials/M_BaseMaterial");
_sidekickRuntime = new SidekickRuntime(model, material, null, _dbManager);
SidekickRuntime.PopulateToolData(_sidekickRuntime);
_partLibrary = _sidekickRuntime.MappedPartDictionary;
foreach (PartGroup partGroup in Enum.GetValues(typeof(PartGroup)))
{
// only filter head part presets by species
List<SidekickPartPreset> presets = SidekickPartPreset.GetAllByGroup(_dbManager, partGroup);
List<string> presetNames = new List<string>();
if (presets.Count < 1)
{
Debug.LogWarning("No parts found for " + partGroup + ". Please add at least 1 Sidekicks content pack.");
continue;
}
foreach (SidekickPartPreset preset in presets)
{
if (preset.HasAllPartsAvailable(_dbManager))
{
switch (partGroup)
{
case PartGroup.Head:
_availableHeadPresetDictionary.Add(preset.Name, preset);
break;
case PartGroup.UpperBody:
_availableUpperBodyPresetDictionary.Add(preset.Name, preset);
break;
case PartGroup.LowerBody:
_availableLowerBodyPresetDictionary.Add(preset.Name, preset);
break;
}
presetNames.Add(preset.Name);
}
}
}
_availableBodyShapes = SidekickBodyShapePreset.GetAll(_dbManager);
// An example of how to retrieve color presets from the database. To retrieve presets for other areas of the material, use the ColorGroup
// enum to retrieve other presets.
_availableColorPresets = SidekickColorPreset.GetAllByColorGroup(_dbManager, ColorGroup.Outfits);
_currentHeadPresetIndex = Random.Range(0, _availableHeadPresetDictionary.Count - 1);
_currentUpperBodyPresetIndex = Random.Range(0, _availableUpperBodyPresetDictionary.Count - 1);
_currentLowerBodyPresetIndex = Random.Range(0, _availableLowerBodyPresetDictionary.Count - 1);
_currentBodyShapePresetIndex = Random.Range(0, _availableBodyShapes.Count - 1);
_currentColorPresetIndex = Random.Range(0, _availableColorPresets.Count - 1);
_loadingText.enabled = false;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Head Preset.
/// </summary>
public void ForwardHeadPreset()
{
_currentHeadPresetIndex++;
if (_currentHeadPresetIndex >= _availableHeadPresetDictionary.Count)
{
_currentHeadPresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Head Preset.
/// </summary>
public void BackwardHeadPreset()
{
_currentHeadPresetIndex--;
if (_currentHeadPresetIndex < 0)
{
_currentHeadPresetIndex = _availableHeadPresetDictionary.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Upper Body Preset.
/// </summary>
public void ForwardUpperBodyPreset()
{
_currentUpperBodyPresetIndex++;
if (_currentUpperBodyPresetIndex >= _availableUpperBodyPresetDictionary.Count)
{
_currentUpperBodyPresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Upper Body Preset.
/// </summary>
public void BackwardUpperBodyPreset()
{
_currentUpperBodyPresetIndex--;
if (_currentUpperBodyPresetIndex < 0)
{
_currentUpperBodyPresetIndex = _availableUpperBodyPresetDictionary.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Lower Body Preset.
/// </summary>
public void ForwardLowerBodyPreset()
{
_currentLowerBodyPresetIndex++;
if (_currentLowerBodyPresetIndex >= _availableLowerBodyPresetDictionary.Count)
{
_currentLowerBodyPresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Lower Body Preset.
/// </summary>
public void BackwardLowerBodyPreset()
{
_currentLowerBodyPresetIndex--;
if (_currentLowerBodyPresetIndex < 0)
{
_currentLowerBodyPresetIndex = _availableLowerBodyPresetDictionary.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Body Shape Preset.
/// </summary>
public void ForwardBodyShapePreset()
{
_currentBodyShapePresetIndex++;
if (_currentBodyShapePresetIndex >= _availableBodyShapes.Count)
{
_currentBodyShapePresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Body Shape Preset.
/// </summary>
public void BackwardBodyShapePreset()
{
_currentBodyShapePresetIndex--;
if (_currentBodyShapePresetIndex < 0)
{
_currentBodyShapePresetIndex = _availableBodyShapes.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Color Preset.
/// </summary>
public void ForwardColorPreset()
{
_currentColorPresetIndex++;
if (_currentColorPresetIndex >= _availableColorPresets.Count)
{
_currentColorPresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Color Preset.
/// </summary>
public void BackwardColorPreset()
{
_currentColorPresetIndex--;
if (_currentColorPresetIndex < 0)
{
_currentColorPresetIndex = _availableColorPresets.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Updates the created character model.
/// </summary>
private void UpdateModel()
{
// If there aren't enough presets, stop trying to update the model.
if (_availableHeadPresetDictionary.Values.Count < 1
|| _availableUpperBodyPresetDictionary.Values.Count < 1
|| _availableLowerBodyPresetDictionary.Values.Count < 1)
{
return;
}
// Create and populate the list of parts to use from the parts list, and the selected presets.
List<SidekickPartPreset> presets = new List<SidekickPartPreset>()
{
_availableHeadPresetDictionary.Values.ToArray()[_currentHeadPresetIndex],
_availableUpperBodyPresetDictionary.Values.ToArray()[_currentUpperBodyPresetIndex],
_availableLowerBodyPresetDictionary.Values.ToArray()[_currentLowerBodyPresetIndex]
};
List<SkinnedMeshRenderer> partsToUse = new List<SkinnedMeshRenderer>();
foreach (SidekickPartPreset preset in presets)
{
List<SidekickPartPresetRow> rows = SidekickPartPresetRow.GetAllByPreset(_dbManager, preset);
foreach (SidekickPartPresetRow row in rows)
{
if (!string.IsNullOrEmpty(row.PartName))
{
CharacterPartType type = Enum.Parse<CharacterPartType>(CharacterPartTypeUtils.GetTypeNameFromShortcode(row.PartType));
Dictionary<string, SidekickPart> partLocationDictionary = _partLibrary[type];
GameObject selectedPart = partLocationDictionary[row.PartName].GetPartModel();
SkinnedMeshRenderer selectedMesh = selectedPart.GetComponentInChildren<SkinnedMeshRenderer>();
partsToUse.Add(selectedMesh);
}
}
}
SidekickBodyShapePreset bodyPreset = _availableBodyShapes[_currentBodyShapePresetIndex];
_sidekickRuntime.BodyTypeBlendValue = bodyPreset.BodyType;
_sidekickRuntime.BodySizeHeavyBlendValue = bodyPreset.BodySize > 0 ? bodyPreset.BodySize : 0;
_sidekickRuntime.BodySizeSkinnyBlendValue = bodyPreset.BodySize < 0 ? -bodyPreset.BodySize : 0;
_sidekickRuntime.MusclesBlendValue = bodyPreset.Musculature;
List<SidekickColorPresetRow> colorRows = SidekickColorPresetRow.GetAllByPreset(_dbManager, _availableColorPresets[_currentColorPresetIndex]);
foreach (SidekickColorPresetRow row in colorRows)
{
SidekickColorRow colorRow = SidekickColorRow.CreateFromPresetColorRow(row);
foreach (ColorType property in Enum.GetValues(typeof(ColorType)))
{
_sidekickRuntime.UpdateColor(property, colorRow);
}
}
// Check for an existing copy of the model, if it exists, delete it so that we don't end up with duplicates.
GameObject character = GameObject.Find(_OUTPUT_MODEL_NAME);
if (character != null)
{
Destroy(character);
}
// Create a new character using the selected parts using the Sidekicks API.
character = _sidekickRuntime.CreateCharacter(_OUTPUT_MODEL_NAME, partsToUse, false, true);
}
/// <summary>
/// Gets a resource path for using with Resources.Load() from a full path.
/// </summary>
/// <param name="fullPath">The full path to get the resource path from.</param>
/// <returns>The resource path.</returns>
private string GetResourcePath(string fullPath)
{
string directory = Path.GetDirectoryName(fullPath);
int startIndex = directory.IndexOf("Resources") + 10;
directory = directory.Substring(startIndex, directory.Length - startIndex);
return Path.Combine(directory, Path.GetFileNameWithoutExtension(fullPath));
}
}
}
using Synty.SidekickCharacters.API;
using Synty.SidekickCharacters.Database;
using Synty.SidekickCharacters.Database.DTO;
using Synty.SidekickCharacters.Enums;
using Synty.SidekickCharacters.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using TMPro;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Synty.SidekickCharacters.Demo
{
/// <summary>
/// An example script to show how to interact with the Sidekick API in regards to presets at runtime.
/// </summary>
public class RuntimePresetDemo : MonoBehaviour
{
private readonly string _OUTPUT_MODEL_NAME = "Sidekick Character";
private Dictionary<string, SidekickPartPreset> _availableHeadPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private Dictionary<string, SidekickPartPreset> _availableUpperBodyPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private Dictionary<string, SidekickPartPreset> _availableLowerBodyPresetDictionary = new Dictionary<string, SidekickPartPreset>();
private List<SidekickBodyShapePreset> _availableBodyShapes = new List<SidekickBodyShapePreset>();
private List<SidekickColorPreset> _availableColorPresets = new List<SidekickColorPreset>();
private int _currentHeadPresetIndex = 0;
private int _currentUpperBodyPresetIndex = 0;
private int _currentLowerBodyPresetIndex = 0;
private int _currentBodyShapePresetIndex = 0;
private int _currentColorPresetIndex = 0;
private DatabaseManager _dbManager;
private SidekickRuntime _sidekickRuntime;
private Dictionary<CharacterPartType, Dictionary<string, SidekickPart>> _partLibrary;
public TextMeshProUGUI _loadingText;
/// <inheritdoc cref="Start"/>
void Start()
{
_dbManager = new DatabaseManager();
GameObject model = Resources.Load<GameObject>("Meshes/SK_BaseModel");
Material material = Resources.Load<Material>("Materials/M_BaseMaterial");
_sidekickRuntime = new SidekickRuntime(model, material, null, _dbManager);
SidekickRuntime.PopulateToolData(_sidekickRuntime);
_partLibrary = _sidekickRuntime.MappedPartDictionary;
foreach (PartGroup partGroup in Enum.GetValues(typeof(PartGroup)))
{
// only filter head part presets by species
List<SidekickPartPreset> presets = SidekickPartPreset.GetAllByGroup(_dbManager, partGroup);
List<string> presetNames = new List<string>();
if (presets.Count < 1)
{
Debug.LogWarning("No parts found for " + partGroup + ". Please add at least 1 Sidekicks content pack.");
continue;
}
foreach (SidekickPartPreset preset in presets)
{
if (preset.HasAllPartsAvailable(_dbManager))
{
switch (partGroup)
{
case PartGroup.Head:
_availableHeadPresetDictionary.Add(preset.Name, preset);
break;
case PartGroup.UpperBody:
_availableUpperBodyPresetDictionary.Add(preset.Name, preset);
break;
case PartGroup.LowerBody:
_availableLowerBodyPresetDictionary.Add(preset.Name, preset);
break;
}
presetNames.Add(preset.Name);
}
}
}
_availableBodyShapes = SidekickBodyShapePreset.GetAll(_dbManager);
// An example of how to retrieve color presets from the database. To retrieve presets for other areas of the material, use the ColorGroup
// enum to retrieve other presets.
_availableColorPresets = SidekickColorPreset.GetAllByColorGroup(_dbManager, ColorGroup.Outfits);
_currentHeadPresetIndex = Random.Range(0, _availableHeadPresetDictionary.Count - 1);
_currentUpperBodyPresetIndex = Random.Range(0, _availableUpperBodyPresetDictionary.Count - 1);
_currentLowerBodyPresetIndex = Random.Range(0, _availableLowerBodyPresetDictionary.Count - 1);
_currentBodyShapePresetIndex = Random.Range(0, _availableBodyShapes.Count - 1);
_currentColorPresetIndex = Random.Range(0, _availableColorPresets.Count - 1);
_loadingText.enabled = false;
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Head Preset.
/// </summary>
public void ForwardHeadPreset()
{
_currentHeadPresetIndex++;
if (_currentHeadPresetIndex >= _availableHeadPresetDictionary.Count)
{
_currentHeadPresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Head Preset.
/// </summary>
public void BackwardHeadPreset()
{
_currentHeadPresetIndex--;
if (_currentHeadPresetIndex < 0)
{
_currentHeadPresetIndex = _availableHeadPresetDictionary.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Upper Body Preset.
/// </summary>
public void ForwardUpperBodyPreset()
{
_currentUpperBodyPresetIndex++;
if (_currentUpperBodyPresetIndex >= _availableUpperBodyPresetDictionary.Count)
{
_currentUpperBodyPresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Upper Body Preset.
/// </summary>
public void BackwardUpperBodyPreset()
{
_currentUpperBodyPresetIndex--;
if (_currentUpperBodyPresetIndex < 0)
{
_currentUpperBodyPresetIndex = _availableUpperBodyPresetDictionary.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Lower Body Preset.
/// </summary>
public void ForwardLowerBodyPreset()
{
_currentLowerBodyPresetIndex++;
if (_currentLowerBodyPresetIndex >= _availableLowerBodyPresetDictionary.Count)
{
_currentLowerBodyPresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Lower Body Preset.
/// </summary>
public void BackwardLowerBodyPreset()
{
_currentLowerBodyPresetIndex--;
if (_currentLowerBodyPresetIndex < 0)
{
_currentLowerBodyPresetIndex = _availableLowerBodyPresetDictionary.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Body Shape Preset.
/// </summary>
public void ForwardBodyShapePreset()
{
_currentBodyShapePresetIndex++;
if (_currentBodyShapePresetIndex >= _availableBodyShapes.Count)
{
_currentBodyShapePresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Body Shape Preset.
/// </summary>
public void BackwardBodyShapePreset()
{
_currentBodyShapePresetIndex--;
if (_currentBodyShapePresetIndex < 0)
{
_currentBodyShapePresetIndex = _availableBodyShapes.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the forward button for the Color Preset.
/// </summary>
public void ForwardColorPreset()
{
_currentColorPresetIndex++;
if (_currentColorPresetIndex >= _availableColorPresets.Count)
{
_currentColorPresetIndex = 0;
}
UpdateModel();
}
/// <summary>
/// Handles the click on the backward button for the Color Preset.
/// </summary>
public void BackwardColorPreset()
{
_currentColorPresetIndex--;
if (_currentColorPresetIndex < 0)
{
_currentColorPresetIndex = _availableColorPresets.Count - 1;
}
UpdateModel();
}
/// <summary>
/// Updates the created character model.
/// </summary>
private void UpdateModel()
{
// If there aren't enough presets, stop trying to update the model.
if (_availableHeadPresetDictionary.Values.Count < 1
|| _availableUpperBodyPresetDictionary.Values.Count < 1
|| _availableLowerBodyPresetDictionary.Values.Count < 1)
{
return;
}
// Create and populate the list of parts to use from the parts list, and the selected presets.
List<SidekickPartPreset> presets = new List<SidekickPartPreset>()
{
_availableHeadPresetDictionary.Values.ToArray()[_currentHeadPresetIndex],
_availableUpperBodyPresetDictionary.Values.ToArray()[_currentUpperBodyPresetIndex],
_availableLowerBodyPresetDictionary.Values.ToArray()[_currentLowerBodyPresetIndex]
};
List<SkinnedMeshRenderer> partsToUse = new List<SkinnedMeshRenderer>();
foreach (SidekickPartPreset preset in presets)
{
List<SidekickPartPresetRow> rows = SidekickPartPresetRow.GetAllByPreset(_dbManager, preset);
foreach (SidekickPartPresetRow row in rows)
{
if (!string.IsNullOrEmpty(row.PartName))
{
CharacterPartType type = Enum.Parse<CharacterPartType>(CharacterPartTypeUtils.GetTypeNameFromShortcode(row.PartType));
Dictionary<string, SidekickPart> partLocationDictionary = _partLibrary[type];
GameObject selectedPart = partLocationDictionary[row.PartName].GetPartModel();
SkinnedMeshRenderer selectedMesh = selectedPart.GetComponentInChildren<SkinnedMeshRenderer>();
partsToUse.Add(selectedMesh);
}
}
}
SidekickBodyShapePreset bodyPreset = _availableBodyShapes[_currentBodyShapePresetIndex];
_sidekickRuntime.BodyTypeBlendValue = bodyPreset.BodyType;
_sidekickRuntime.BodySizeHeavyBlendValue = bodyPreset.BodySize > 0 ? bodyPreset.BodySize : 0;
_sidekickRuntime.BodySizeSkinnyBlendValue = bodyPreset.BodySize < 0 ? -bodyPreset.BodySize : 0;
_sidekickRuntime.MusclesBlendValue = bodyPreset.Musculature;
List<SidekickColorPresetRow> colorRows = SidekickColorPresetRow.GetAllByPreset(_dbManager, _availableColorPresets[_currentColorPresetIndex]);
foreach (SidekickColorPresetRow row in colorRows)
{
SidekickColorRow colorRow = SidekickColorRow.CreateFromPresetColorRow(row);
foreach (ColorType property in Enum.GetValues(typeof(ColorType)))
{
_sidekickRuntime.UpdateColor(property, colorRow);
}
}
// Check for an existing copy of the model, if it exists, delete it so that we don't end up with duplicates.
GameObject character = GameObject.Find(_OUTPUT_MODEL_NAME);
if (character != null)
{
Destroy(character);
}
// Create a new character using the selected parts using the Sidekicks API.
character = _sidekickRuntime.CreateCharacter(_OUTPUT_MODEL_NAME, partsToUse, false, true);
}
/// <summary>
/// Gets a resource path for using with Resources.Load() from a full path.
/// </summary>
/// <param name="fullPath">The full path to get the resource path from.</param>
/// <returns>The resource path.</returns>
private string GetResourcePath(string fullPath)
{
string directory = Path.GetDirectoryName(fullPath);
int startIndex = directory.IndexOf("Resources") + 10;
directory = directory.Substring(startIndex, directory.Length - startIndex);
return Path.Combine(directory, Path.GetFileNameWithoutExtension(fullPath));
}
}
}

View File