chore: 외부 에셋 권한 및 줄바꿈 재기록 반영
- Assets/External 하위 샘플 및 서드파티 에셋 파일의 실행 비트 변경을 별도 커밋으로 분리 - PolygonGeneric, SidekickCharacters, Synty 도구 자산 전반의 줄바꿈 및 재직렬화 차이를 그대로 보존 - 프로젝트 고유 로직 변경과 분리해 이후 히스토리에서 외부 에셋 노이즈 범위를 식별하기 쉽게 정리
This commit is contained in:
280
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RandomisedLayerWeight.cs
vendored
Normal file → Executable file
280
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RandomisedLayerWeight.cs
vendored
Normal file → Executable 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RandomisedLayerWeight.cs.meta
vendored
Normal file → Executable file
0
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RandomisedLayerWeight.cs.meta
vendored
Normal file → Executable file
446
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimeColorDemo.cs
vendored
Normal file → Executable file
446
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimeColorDemo.cs
vendored
Normal file → Executable 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimeColorDemo.cs.meta
vendored
Normal file → Executable file
0
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimeColorDemo.cs.meta
vendored
Normal file → Executable file
760
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimePartsDemo.cs
vendored
Normal file → Executable file
760
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimePartsDemo.cs
vendored
Normal file → Executable 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimePartsDemo.cs.meta
vendored
Normal file → Executable file
0
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimePartsDemo.cs.meta
vendored
Normal file → Executable file
650
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimePresetDemo.cs
vendored
Normal file → Executable file
650
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimePresetDemo.cs
vendored
Normal file → Executable 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimePresetDemo.cs.meta
vendored
Normal file → Executable file
0
Assets/External/Models/SidekickCharacters/_Demos/Scripts/RuntimePresetDemo.cs.meta
vendored
Normal file → Executable file
Reference in New Issue
Block a user