提交捏脸插件

This commit is contained in:
2025-09-18 00:07:48 +08:00
parent cde46100ff
commit aba294e510
1676 changed files with 295391 additions and 4 deletions

View File

@@ -0,0 +1,109 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Linq;
namespace CC
{
public class Apparel_Menu : MonoBehaviour, ICustomizerUI
{
public GameObject ButtonPrefab;
public GameObject Container;
private CharacterCustomization customizer;
public bool useIcons = true;
public Sprite defaultIcon;
public GameObject CategoryPrefab;
public GameObject CategoryContainer;
private List<scrObj_Apparel.MenuCategory> menuCategories;
public SmoothScroll CategoryScroll;
public void InitializeUIElement(CharacterCustomization customizerScript, CC_UI_Util parentUI)
{
customizer = customizerScript;
RefreshUIElement();
}
public void createApparelButtons(scrObj_Apparel.MenuCategory menuCategory)
{
foreach (Transform child in Container.transform)
{
Destroy(child.gameObject);
}
for (int i = 0; i < customizer.ApparelTables.Count; i++)
{
var items = customizer.ApparelTables[i].Items.Where(item => item.MenuCategory == menuCategory);
foreach (var item in items)
{
for (int j = 0; j < item.Materials.Count; j++)
{
createButton(item.Name, i, j, item.Materials[j].Icon);
}
if (item.Materials.Count == 0)
{
createButton(item.Name, i, 0, null);
}
}
}
}
private void createCategoryButtons()
{
//Safely destroy existing category buttons
var children = new List<GameObject>();
foreach (Transform child in CategoryContainer.transform) children.Add(child.gameObject);
foreach (var go in children)
{
go.transform.SetParent(null);
Destroy(go);
}
//Try get tab manager
var tabManager = CategoryContainer.GetComponentInParent<Tab_Manager>();
//Get menu categories in order
menuCategories = customizer.ApparelTables.SelectMany(table => table.Items).Select(item => item.MenuCategory).Distinct().ToList();
//Create category button per menu category
for (int i = 0; i < menuCategories.Count; i++)
{
scrObj_Apparel.MenuCategory cat = menuCategories[i];
GameObject categoryButton = Instantiate(CategoryPrefab, CategoryContainer.transform).gameObject;
categoryButton.GetComponentInChildren<TextMeshProUGUI>().text = menuCategories[i].ToString();
var button = categoryButton.GetComponentInChildren<Button>();
button.onClick.RemoveAllListeners();
button.onClick.AddListener(() => { createApparelButtons(cat); });
}
if (tabManager != null) tabManager.assignTabs();
}
private void createButton(string text, int slot, int material, Sprite sprite)
{
string name = text;
int matIndex = material;
int apparelSlot = slot;
GameObject Button = Instantiate(ButtonPrefab, Container.transform).gameObject;
Button.GetComponentInChildren<Button>().onClick.AddListener(() => { customizer.setApparelByName(name, apparelSlot, matIndex); });
if (useIcons) Button.GetComponentInChildren<Image>().sprite = sprite == null ? defaultIcon : sprite;
else Button.GetComponentInChildren<TextMeshProUGUI>().text = text;
}
public void RefreshUIElement()
{
createCategoryButtons();
createApparelButtons(menuCategories[0]);
if (CategoryScroll != null) CategoryScroll.resetScroll(true);
}
}
}

View File

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

View File

@@ -0,0 +1,158 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
namespace CC
{
[DefaultExecutionOrder(200)]
public class CC_UI_Manager : MonoBehaviour
{
public static CC_UI_Manager instance;
public delegate void OnHover(string partHovered);
public event OnHover onHover;
public delegate void OnDrag(string partX, string partY, float deltaX, float deltaY, bool first, bool last);
public event OnDrag onDrag;
private bool Dragging;
private string hoveredPart = "";
private string partX, partY = "";
private float multX, multY = 1f;
public float mouseDeltaScale = 0.01f;
private Vector3 mousePos;
private Canvas canvas;
private void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
}
}
[Tooltip("The parent object of your customizable characters")]
public GameObject CharacterParent;
private int currentCharacter;
public List<AudioClip> UISounds = new List<AudioClip>();
public void Start()
{
SetActiveCharacter(0);
}
private void Update()
{
bool first = !Dragging && Input.GetMouseButton(0);
bool last = Dragging && !Input.GetMouseButton(0);
//Set shape on first drag
if (first)
{
partX = ""; partY = "";
multX = hoveredPart.Contains("_r") ? -1 : 1; multY = -1f;
if (hoveredPart.Contains("spine_05")) { partX = "BodyCustomization_ShoulderWidth"; partY = "BodyCustomization_TorsoHeight"; }
else if (hoveredPart.Contains("spine")) { partX = "BodyCustomization_WaistSize"; partY = ""; }
else if (hoveredPart.Contains("pelvis")) { partX = "BodyCustomization_HipWidth"; partY = ""; }
else if (hoveredPart.Contains("lowerarm")) { partX = "BodyCustomization_LowerArmScale"; partY = ""; }
else if (hoveredPart.Contains("upperarm")) { partX = "BodyCustomization_UpperArmScale"; partY = ""; }
else if (hoveredPart.Contains("thigh")) { partX = "BodyCustomization_ThighScale"; partY = ""; }
else if (hoveredPart.Contains("calf")) { partX = "BodyCustomization_CalfScale"; partY = ""; }
else if (hoveredPart.Contains("head")) { partX = "BodyCustomization_HeadSize"; partY = "BodyCustomization_NeckLength"; }
else if (hoveredPart.Contains("neck")) { partX = "BodyCustomization_NeckScale"; partY = "BodyCustomization_NeckLength"; }
else if (hoveredPart.Contains("collider_nose")) { partX = "mod_nose_size"; partY = "mod_nose_height"; multY = 1; }
else if (hoveredPart.Contains("collider_mouth")) { partX = "mod_mouth_size"; partY = "mod_mouth_height"; multY = 1; }
else if (hoveredPart.Contains("collider_cheekbones")) { partX = "mod_cheekbone_size"; partY = ""; multX *= -1; }
else if (hoveredPart.Contains("collider_cheeks")) { partX = "mod_cheeks_size"; partY = ""; multX *= -1; }
else if (hoveredPart.Contains("collider_jaw")) { partX = "mod_jaw_width"; partY = "mod_jaw_height"; multX *= -1; }
else if (hoveredPart.Contains("collider_chin")) { partX = ""; partY = "mod_chin_size"; }
else if (hoveredPart.Contains("collider_eye")) { partX = "mod_eyes_narrow"; partY = "mod_eyes_height"; multY = 1; }
else if (hoveredPart.Contains("collider_brow")) { partX = ""; partY = "mod_brow_height"; }
}
Dragging = Input.GetMouseButton(0);
if (Dragging && getCanvas() != null)
{
Vector3 mouseDelta = (Input.mousePosition - mousePos) * mouseDeltaScale / canvas.scaleFactor;
onDrag?.Invoke(partX, partY, mouseDelta.x * multX, mouseDelta.y * multY, first, last);
}
mousePos = Input.mousePosition;
}
private Canvas getCanvas()
{
if (canvas != null) return canvas;
else
{
canvas = GetComponentInChildren<Canvas>();
return canvas;
}
}
private void LateUpdate()
{
if (Dragging) return;
onHover?.Invoke(hoveredPart);
Physics.SyncTransforms();
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit) && !EventSystem.current.IsPointerOverGameObject())
{
hoveredPart = hit.collider.name;
}
else hoveredPart = "";
}
public void playUIAudio(int Index)
{
var audioSource = gameObject.GetComponent<AudioSource>();
if (audioSource && UISounds.Count > Index) audioSource.clip = UISounds[Index]; audioSource.Play();
}
public void SetActiveCharacter(int i)
{
if (CharacterParent == null) return;
currentCharacter = i;
for (int j = 0; j < CharacterParent.transform.childCount; j++)
{
var character = CharacterParent.transform.GetChild(j).gameObject;
//Reset active character and disable it
if (character.activeSelf && i != j)
{
var script = character.GetComponentInChildren<CharacterCustomization>();
script.LoadFromPreset(script.CharacterName);
character.SetActive(false);
}
//Enable selected character (its UI is automatically activated)
else if (i == j) character.SetActive(true);
}
}
public void characterNext()
{
SetActiveCharacter(currentCharacter == CharacterParent.transform.childCount - 1 ? 0 : currentCharacter + 1);
}
public void characterPrev()
{
SetActiveCharacter(currentCharacter == 0 ? CharacterParent.transform.childCount - 1 : currentCharacter - 1);
}
}
}

View File

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

View File

@@ -0,0 +1,83 @@
using UnityEngine;
namespace CC
{
public class CC_UI_Util : MonoBehaviour
{
private CharacterCustomization customizer;
//Initialize all child UI elements
public void Initialize(CharacterCustomization customizerScript)
{
customizer = customizerScript;
var interfaces = gameObject.GetComponentsInChildren<ICustomizerUI>(true);
foreach (var element in interfaces)
{
element.InitializeUIElement(customizerScript, this);
}
}
//Refresh UI elements, for example after loading a different preset
public void refreshUI()
{
var interfaces = gameObject.GetComponentsInChildren<ICustomizerUI>(true);
foreach (var element in interfaces)
{
element.RefreshUIElement();
}
}
public void characterNext()
{
CC_UI_Manager.instance.characterNext();
}
public void characterPrev()
{
CC_UI_Manager.instance.characterPrev();
}
public void saveToPreset(string name)
{
customizer.SaveToPreset(name);
}
public void saveToJSON(string name)
{
customizer.SaveToJSON(name);
}
public void loadCharacter()
{
customizer.TryLoadCharacter();
refreshUI();
}
public void setCharacterName(string newName)
{
customizer.setCharacterName(newName);
}
public void setCharacterPreset(string preset)
{
}
public void randomizeCharacter()
{
customizer.randomizeAll();
}
public void randomizeOutfit()
{
customizer.setRandomOutfit();
}
public void randomizeCharacterAndOutfit()
{
customizer.randomizeCharacterAndOutfit();
}
}
}

View File

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

View File

@@ -0,0 +1,43 @@
using UnityEngine;
namespace CC
{
public class Cursor_Manager : MonoBehaviour
{
public static Cursor_Manager instance;
public Texture2D cursorTexture;
private Vector2 hotSpot = new Vector2(0, 0);
private void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
}
if (cursorTexture != null)
{
hotSpot = new Vector2(cursorTexture.width / 2, cursorTexture.height / 2);
setDefaultCursor();
}
}
public void setCursor(Texture2D texture)
{
if (cursorTexture != null)
{
Cursor.SetCursor(texture, hotSpot, CursorMode.Auto);
}
}
public void setDefaultCursor()
{
setCursor(cursorTexture);
}
}
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace CC
{
public interface ICustomizerUI
{
void InitializeUIElement(CharacterCustomization customizerScript, CC_UI_Util parentUI);
void RefreshUIElement();
}
}

View File

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

View File

@@ -0,0 +1,37 @@
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace CC
{
public class InputFieldHandler : MonoBehaviour, ICustomizerUI
{
private TMPro.TMP_InputField inputField;
private CC_UI_Util script;
private void Awake()
{
inputField = GetComponent<TMPro.TMP_InputField>();
}
public void InitializeUIElement(CharacterCustomization customizerScript, CC_UI_Util parentUI)
{
script = parentUI;
}
public void RefreshUIElement()
{
}
public void InvokeMethod(string methodName)
{
// Get the method from CC_UI_Util that matches the name
var method = typeof(CC_UI_Util).GetMethod(methodName);
if (method != null && method.GetParameters().Length == 1 &&
method.GetParameters()[0].ParameterType == typeof(string))
{
method.Invoke(script, new object[] { inputField.text });
}
}
}
}

View File

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

View File

@@ -0,0 +1,120 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace CC
{
public class Option_Color_Picker : MonoBehaviour, ICustomizerUI
{
private CharacterCustomization customizer;
private CC_UI_Util parentUI;
public CC_Property Property;
public bool useOpacity;
public string DisplayOption = "Option";
public GameObject hsvSliders;
private Image[] imgs;
public Image pickerIcon;
private float h; private float s; private float v; private float a = 1f;
#if UNITY_EDITOR
private void OnValidate()
{
UnityEditor.EditorApplication.delayCall += OnValidateCallback;
}
private void OnValidateCallback()
{
if (this == null || Application.isPlaying)
{
UnityEditor.EditorApplication.delayCall -= OnValidateCallback;
return;
}
GetComponentInChildren<TMPro.TMP_Text>().text = DisplayOption;
gameObject.name = "ColorPicker_" + DisplayOption;
}
#endif
public void InitializeUIElement(CharacterCustomization customizerScript, CC_UI_Util ParentUI)
{
customizer = customizerScript;
parentUI = ParentUI;
RefreshUIElement();
}
public void RefreshUIElement()
{
//Get saved value
if (customizer.findProperty(customizer.StoredCharacterData.ColorProperties, Property, out Property, out int savedIndex))
{
pickerIcon.color = new Color(Property.colorValue.r, Property.colorValue.g, Property.colorValue.b, 1);
}
}
public void toggleSliders()
{
Color.RGBToHSV(Property.colorValue, out h, out s, out v);
a = Property.colorValue.a;
var sliderObj = Instantiate(hsvSliders, parentUI.transform);
//Remove on click
var eventTrigger = sliderObj.GetComponentInChildren<EventTrigger>();
var entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerDown;
entry.callback = new EventTrigger.TriggerEvent();
entry.callback.AddListener(a => Destroy(sliderObj));
eventTrigger.triggers.Add(entry);
//Put slider box on this transform
var sliderContainer = sliderObj.transform.GetChild(1);
sliderContainer.position = transform.position;
var sliders = sliderObj.GetComponentsInChildren<Slider>();
imgs = sliderObj.GetComponentsInChildren<Image>();
sliders[0].SetValueWithoutNotify(h);
sliders[1].SetValueWithoutNotify(s);
sliders[2].SetValueWithoutNotify(v);
sliders[3].SetValueWithoutNotify(a);
sliders[0].onValueChanged.AddListener(f => { h = f; setColor(); });
sliders[1].onValueChanged.AddListener(f => { s = f; setColor(); });
sliders[2].onValueChanged.AddListener(f => { v = f; setColor(); });
sliders[3].onValueChanged.AddListener(f => { a = f; setColor(); });
foreach (var img in imgs)
{
if (!img.raycastTarget) img.color = Color.HSVToRGB(h, 1, 1);
}
if (!useOpacity)
{
Destroy(sliders[3].transform.parent.gameObject);
}
LayoutRebuilder.ForceRebuildLayoutImmediate(sliderContainer.GetComponent<RectTransform>());
}
public void setColor()
{
Property.colorValue = Color.HSVToRGB(h, s, v);
Property.colorValue.a = a;
customizer.setColorProperty(Property, true);
pickerIcon.color = new Color(Property.colorValue.r, Property.colorValue.g, Property.colorValue.b, 1);
foreach (var img in imgs)
{
if (!img.raycastTarget) img.color = Color.HSVToRGB(h, 1, 1);
}
}
}
}

View File

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

View File

@@ -0,0 +1,240 @@
using System.Collections.Generic;
using UnityEngine;
using TMPro;
namespace CC
{
public class Option_Picker : MonoBehaviour, ICustomizerUI
{
private CharacterCustomization customizer;
public enum Type
{ Blendshape, Texture, Hair, Color, Stencil };
public Type CustomizationType;
public CC_Property Property;
public List<CC_Property> Options = new List<CC_Property>();
public int Slot = 0;
public TextMeshProUGUI PropertyText;
public TextMeshProUGUI OptionText;
public string DisplayOption;
private int navIndex = 0;
private int optionsCount = 0;
public bool valueFromIndex = true;
public bool valueFromString = false;
public bool blendshapeUseOptionValue;
public List<CC_Stencil> stencilOptions = new List<CC_Stencil>();
public void InitializeUIElement(CharacterCustomization customizerScript, CC_UI_Util ParentUI)
{
customizer = customizerScript;
RefreshUIElement();
foreach (var item in stencilOptions)
{
item.basePropertyName = Property.propertyName;
item.materialIndex = Property.materialIndex;
item.meshTag = Property.meshTag;
}
if (!blendshapeUseOptionValue && CustomizationType == Type.Blendshape) return;
foreach (var item in Options)
{
item.propertyName = Property.propertyName;
item.materialIndex = Property.materialIndex;
item.meshTag = Property.meshTag;
}
}
public void RefreshUIElement()
{
switch (CustomizationType)
{
case Type.Blendshape:
{
optionsCount = Options.Count;
updateOptionText();
for (int i = 0; i < Options.Count; i++)
{
CC_Property prop;
if (customizer.findProperty(customizer.StoredCharacterData.Blendshapes, Options[i], out prop, out int savedIndex))
{
if (prop.floatValue != 0)
{
navIndex = i;
updateOptionText();
break;
}
}
}
break;
}
case Type.Texture:
{
optionsCount = Options.Count;
if (customizer.findProperty(customizer.StoredCharacterData.TextureProperties, Property, out Property, out int savedIndex))
{
navIndex = Options.FindIndex(t => t.stringValue == Property.stringValue);
}
else navIndex = 0;
updateOptionText();
break;
}
case Type.Stencil:
{
optionsCount = stencilOptions.Count;
if (customizer.findProperty(customizer.StoredCharacterData.TextureProperties, Property, out Property, out int savedIndex))
{
navIndex = stencilOptions.FindIndex(t => t.texture.name == Property.stringValue);
}
else navIndex = 0;
updateOptionText();
break;
}
case Type.Color:
{
optionsCount = Options.Count;
if (customizer.findProperty(customizer.StoredCharacterData.ColorProperties, Property, out Property, out int savedIndex))
{
navIndex = Options.FindIndex(t => colorMatch(t.colorValue, Property.colorValue));
if (navIndex == -1) navIndex = 0;
}
else navIndex = 0;
updateOptionText();
break;
}
case Type.Hair:
{
if (customizer.HairTables.Count <= Slot)
{
Destroy(gameObject);
return;
}
optionsCount = customizer.HairTables[Slot].Hairstyles.Count;
navIndex = customizer.HairTables[Slot].Hairstyles.FindIndex(t => t.Name == customizer.StoredCharacterData.HairNames[Slot]);
if (navIndex == -1) navIndex = 0;
updateOptionText();
break;
}
}
}
private bool colorMatch(Color a, Color b, float tolerance = 0.1f)
{
return Mathf.Abs(a.r - b.r) < tolerance &&
Mathf.Abs(a.g - b.g) < tolerance &&
Mathf.Abs(a.b - b.b) < tolerance;
}
#if UNITY_EDITOR
private void OnValidate()
{
UnityEditor.EditorApplication.delayCall += OnValidateCallback;
}
private void OnValidateCallback()
{
if (this == null || Application.isPlaying)
{
UnityEditor.EditorApplication.delayCall -= OnValidateCallback;
return;
}
PropertyText.text = DisplayOption;
if (DisplayOption != "") gameObject.name = "Picker_" + DisplayOption;
}
#endif
public void updateOptionText()
{
if (valueFromIndex) { OptionText.gameObject.SetActive(true); OptionText.SetText((navIndex + 1) + "/" + optionsCount); }
if (valueFromString) { OptionText.gameObject.SetActive(true); OptionText.SetText(Options[navIndex].stringValue); }
}
public void setOption(int i)
{
navIndex = i;
updateOptionText();
switch (CustomizationType)
{
case Type.Blendshape:
{
if (blendshapeUseOptionValue)
{
customizer.setBlendshapeByName(Property.propertyName, Options[i].floatValue);
break;
}
foreach (var p in Options)
{
customizer.setBlendshapeByName(name, 0);
}
customizer.setBlendshapeByName(Options[i].propertyName, 1);
break;
}
case Type.Hair:
{
customizer.setHair(i, Slot);
break;
}
case Type.Texture:
{
var pNames = Options[i].propertyName.Split(",");
var pValues = Options[i].stringValue.Split(",");
var pTags = Options[i].meshTag.Split(",");
for (int j = 0; j < pNames.Length; j++)
{
string value = j < pValues.Length ? pValues[j] : string.Empty;
string tag = j < pTags.Length ? pTags[j] : string.Empty;
var prop2 = new CC_Property() { propertyName = pNames[j], stringValue = value, materialIndex = Property.materialIndex, meshTag = tag };
customizer.setTextureProperty(prop2, true);
}
break;
}
case Type.Stencil:
var stencil = stencilOptions[i];
var textureProp = new CC_Property() { propertyName = stencil.basePropertyName, materialIndex = stencil.materialIndex, meshTag = stencil.meshTag };
customizer.setTextureProperty(textureProp, true, stencil.texture);
var offsetX = new CC_Property() { propertyName = stencil.basePropertyName + "_Offset_X", floatValue = stencil.offsetX, materialIndex = stencil.materialIndex, meshTag = stencil.meshTag };
customizer.setFloatProperty(offsetX, true);
var offsetY = new CC_Property() { propertyName = stencil.basePropertyName + "_Offset_Y", floatValue = stencil.offsetY, materialIndex = stencil.materialIndex, meshTag = stencil.meshTag };
customizer.setFloatProperty(offsetY, true);
var scaleX = new CC_Property() { propertyName = stencil.basePropertyName + "_Scale_X", floatValue = stencil.scaleX, materialIndex = stencil.materialIndex, meshTag = stencil.meshTag };
customizer.setFloatProperty(scaleX, true);
var scaleY = new CC_Property() { propertyName = stencil.basePropertyName + "_Scale_Y", floatValue = stencil.scaleY, materialIndex = stencil.materialIndex, meshTag = stencil.meshTag };
customizer.setFloatProperty(scaleY, true);
var rotProp = new CC_Property() { propertyName = stencil.basePropertyName + "_Rotation", floatValue = stencil.rotation, materialIndex = stencil.materialIndex, meshTag = stencil.meshTag };
customizer.setFloatProperty(rotProp, true);
var tintableProp = new CC_Property() { propertyName = stencil.basePropertyName + "_Tintable", floatValue = stencil.tintable ? 1 : 0, materialIndex = stencil.materialIndex, meshTag = stencil.meshTag };
customizer.setFloatProperty(tintableProp, true);
break;
case Type.Color:
{
customizer.setColorProperty(Options[i], true);
break;
}
}
}
public void navLeft()
{
setOption(navIndex == 0 ? optionsCount - 1 : navIndex - 1);
}
public void navRight()
{
setOption(navIndex == optionsCount - 1 ? 0 : navIndex + 1);
}
}
}

View File

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

View File

@@ -0,0 +1,97 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
namespace CC
{
public class Option_Proportional_Sliders : MonoBehaviour, ICustomizerUI
{
private CharacterCustomization customizer;
private CC_UI_Util parentUI;
public List<CC_Property> Properties = new List<CC_Property>();
public bool RemoveText = true;
public bool Vertical = true;
public GameObject SliderObject;
public Transform SliderContainer;
private List<Slider> sliders = new List<Slider>();
private List<Option_Slider> sliderScripts = new List<Option_Slider>();
private float sliderSum;
public void InitializeUIElement(CharacterCustomization customizerScript, CC_UI_Util ParentUI)
{
customizer = customizerScript;
parentUI = ParentUI;
foreach (var slider in sliderScripts)
{
Destroy(slider.gameObject);
}
sliderScripts.Clear();
sliders.Clear();
for (int i = 0; i < Properties.Count; i++)
{
//Create sliders, assign to reference and add delegate
var sliderObj = Instantiate(SliderObject, SliderContainer);
Option_Slider sliderScript = sliderObj.AddComponent<Option_Slider>();
if (RemoveText) sliderObj.GetComponentInChildren<TMP_Text>().gameObject.SetActive(false);
sliderScripts.Add(sliderScript);
sliderScript.Property = Properties[i];
sliderScript.CustomizationType = Option_Slider.Type.Blendshape;
sliderScript.InitializeUIElement(customizerScript, ParentUI);
Slider slider = sliderScript.GetComponentInChildren<Slider>();
if (Vertical) slider.SetDirection(Slider.Direction.BottomToTop, true);
sliders.Add(slider);
slider.onValueChanged.AddListener(delegate { checkExcess(slider); });
}
LayoutRebuilder.ForceRebuildLayoutImmediate(transform.parent.GetComponent<RectTransform>());
}
public void RefreshUIElement()
{
foreach (var slider in sliderScripts)
{
slider.RefreshUIElement();
}
}
public void checkExcess(Slider mainSlider)
{
sliderSum = 0;
foreach (Slider slider in sliders)
{
sliderSum = slider.value + sliderSum;
}
if (sliderSum > 1)
{
for (int i = 0; i < sliders.Count; i++)
{
if (mainSlider != sliders[i])
{
distributeExcess(sliderSum - mainSlider.value, sliderSum - 1, i);
}
}
}
}
public void distributeExcess(float sum, float excess, int index)
{
sliders[index].SetValueWithoutNotify(sliders[index].value - (sliders[index].value / sum * excess));
sliderScripts[index].setProperty(sliders[index].value);
}
}
}

View File

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

View File

@@ -0,0 +1,129 @@
using UnityEngine;
using UnityEngine.UI;
namespace CC
{
public class Option_Slider : MonoBehaviour, ICustomizerUI
{
public enum Type
{ Blendshape, Scalar, ModifyType };
public Type CustomizationType;
public CC_Property Property;
public Vector2 Range = new Vector2(0, 1);
public float DefaultValue = 0f;
public string DisplayOption = "Option";
public bool UsesDragCustomization;
public float dragMultiplier = 1f;
private Slider slider;
private CharacterCustomization customizer;
private CC_UI_Util parentUI;
#if UNITY_EDITOR
private void OnValidate()
{
UnityEditor.EditorApplication.delayCall += OnValidateCallback;
}
private void OnValidateCallback()
{
if (this == null || Application.isPlaying)
{
UnityEditor.EditorApplication.delayCall -= OnValidateCallback;
return;
}
slider = GetComponentInChildren<Slider>();
slider.minValue = Range.x;
slider.maxValue = Range.y;
slider.SetValueWithoutNotify(DefaultValue);
GetComponentInChildren<TMPro.TMP_Text>(true).text = DisplayOption;
gameObject.name = "Slider_" + DisplayOption;
}
#endif
public void InitializeUIElement(CharacterCustomization customizerScript, CC_UI_Util ParentUI)
{
slider = GetComponentInChildren<Slider>();
customizer = customizerScript;
parentUI = ParentUI;
if (UsesDragCustomization) CC_UI_Manager.instance.onDrag += onBodyCustomization;
slider.onValueChanged.AddListener(setProperty);
RefreshUIElement();
}
private void onBodyCustomization(string partX, string partY, float deltaX, float deltaY, bool first, bool last)
{
if (partX == Property.propertyName)
{
slider.value += deltaX * dragMultiplier;
}
if (partY == Property.propertyName)
{
slider.value += deltaY * dragMultiplier;
}
}
public void RefreshUIElement()
{
//Get saved value
switch (CustomizationType)
{
case Type.Blendshape:
case Type.ModifyType:
{
if (customizer.findProperty(customizer.StoredCharacterData.Blendshapes, Property, out Property, out int savedIndex))
{
slider.SetValueWithoutNotify(Property.floatValue);
}
else
{
slider.SetValueWithoutNotify(DefaultValue);
}
break;
}
case Type.Scalar:
{
if (customizer.findProperty(customizer.StoredCharacterData.FloatProperties, Property, out Property, out int savedIndex))
{
slider.SetValueWithoutNotify(Property.floatValue);
}
else
{
slider.SetValueWithoutNotify(DefaultValue);
}
break;
}
}
}
public void setProperty(float value)
{
Property.floatValue = value;
switch (CustomizationType)
{
case Type.Blendshape:
case Type.ModifyType:
{
customizer.setBlendshapeByName(Property.propertyName, value);
break;
}
case Type.Scalar:
{
customizer.setFloatProperty(Property, true);
break;
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,56 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace CC
{
public class Option_Tint_Buttons : MonoBehaviour, ICustomizerUI
{
public CC_Property property;
public List<Color> tints = new List<Color>();
public GameObject buttonPrefab;
public UnityEvent customEvent;
private CharacterCustomization customizer;
private void Start()
{
var buttonDefault = GetComponentInChildren<Button>();
if (buttonDefault != null) buttonDefault.onClick.AddListener(delegate
{
setProperty(new Color(0, 0, 0, 0)); customEvent.Invoke();
});
for (int i = 0; i < tints.Count; i++)
{
int index = i;
var button = Instantiate(buttonPrefab, transform);
button.GetComponentInChildren<Button>().onClick.AddListener(delegate { setProperty(tints[index]); customEvent.Invoke(); });
button.GetComponentInChildren<Image>().color = tints[i];
var backgroundImg = button.AddComponent<Image>();
backgroundImg.color = new Color(0.5f, 0.5f, 0.5f);
}
LayoutRebuilder.ForceRebuildLayoutImmediate(transform.GetComponent<RectTransform>());
}
private void setProperty(Color color)
{
property.colorValue = color;
customizer.setColorProperty(property, true);
}
public void InitializeUIElement(CharacterCustomization customizerScript, CC_UI_Util parentUI)
{
customizer = customizerScript;
}
public void RefreshUIElement()
{
}
}
}

View File

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

View File

@@ -0,0 +1,20 @@
using UnityEngine;
using UnityEngine.EventSystems;
namespace CC
{
public class SetCursor : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
public Texture2D cursorTexture;
public void OnPointerEnter(PointerEventData eventData)
{
Cursor_Manager.instance.setCursor(cursorTexture);
}
public void OnPointerExit(PointerEventData eventData)
{
Cursor_Manager.instance.setDefaultCursor();
}
}
}

View File

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

View File

@@ -0,0 +1,106 @@
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System.Collections;
namespace CC
{
public class SmoothScroll : ScrollRect
{
public bool SmoothScrolling { get; set; } = true;
public float SmoothScrollTime { get; set; } = 0.2f;
private Coroutine smoothScrollCoroutine;
public override void OnScroll(PointerEventData data)
{
if (!IsActive()) return;
if (SmoothScrolling)
{
// Stop any ongoing smooth scroll
if (smoothScrollCoroutine != null)
StopCoroutine(smoothScrollCoroutine);
Vector2 positionBefore = normalizedPosition;
base.OnScroll(data);
Vector2 positionAfter = normalizedPosition;
normalizedPosition = positionBefore;
smoothScrollCoroutine = StartCoroutine(SmoothScrollToPosition(positionAfter));
}
else
{
base.OnScroll(data);
}
}
public void SetScrollTarget(Vector2 targetPosition)
{
if (SmoothScrolling)
{
if (smoothScrollCoroutine != null)
StopCoroutine(smoothScrollCoroutine);
Vector2 positionBefore = normalizedPosition;
normalizedPosition = targetPosition;
Vector2 positionAfter = normalizedPosition;
normalizedPosition = positionBefore;
smoothScrollCoroutine = StartCoroutine(SmoothScrollToPosition(positionAfter));
}
else
{
normalizedPosition = targetPosition;
}
}
public void resetScroll(bool instant = false)
{
var smoothScroll = SmoothScrolling;
if (instant) SmoothScrolling = false;
StopAllCoroutines();
SetScrollTarget(new Vector2(0, 1));
LayoutRebuilder.ForceRebuildLayoutImmediate(content.GetComponent<RectTransform>());
if (instant) SmoothScrolling = smoothScroll;
}
public void ScrollToContent(RectTransform targetContent)
{
// Ensure targetContent is a child of the content area
if (targetContent == null || !targetContent.IsChildOf(content))
{
Debug.LogWarning("Target content is not a child of the scroll rect content.");
return;
}
// Calculate the target normalized position
Vector2 targetLocalPos = content.InverseTransformPoint(targetContent.position);
Vector2 contentSize = content.rect.size;
Vector2 viewportSize = viewport.rect.size;
Vector2 normalizedPos = new Vector2(
Mathf.Clamp01((targetLocalPos.x - viewportSize.x / 2) / (contentSize.x - viewportSize.x)),
Mathf.Clamp01((targetLocalPos.y - viewportSize.y / 2) / (contentSize.y - viewportSize.y))
);
SetScrollTarget(normalizedPos);
}
private IEnumerator SmoothScrollToPosition(Vector2 targetPosition)
{
float elapsedTime = 0f;
Vector2 startPosition = normalizedPosition;
while (elapsedTime < SmoothScrollTime)
{
elapsedTime += Time.unscaledDeltaTime;
normalizedPosition = Vector2.Lerp(startPosition, targetPosition, elapsedTime / SmoothScrollTime);
yield return null;
}
normalizedPosition = targetPosition;
smoothScrollCoroutine = null;
}
}
}

View File

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

View File

@@ -0,0 +1,68 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace CC
{
public class Tab_Manager : MonoBehaviour
{
[Header("Button Active Colors")]
public ColorBlock TabColorActive;
[Header("Button Inactive Colors")]
public ColorBlock TabColorInactive;
public GameObject TabParent;
private List<GameObject> tabs = new List<GameObject>();
private List<GameObject> tabMenus = new List<GameObject>();
private SmoothScroll scrollRect;
public SmoothScroll targetScroll;
private void Start()
{
assignTabs();
}
public void assignTabs()
{
tabs.Clear();
tabMenus.Clear();
for (int i = 0; i < transform.childCount; i++)
{
var tab = transform.GetChild(i).gameObject;
var index = i;
tabs.Add(tab);
tab.GetComponentInChildren<Button>().onClick.AddListener(() => switchTab(index));
}
if (TabParent != null) foreach (Transform child in TabParent.transform)
{
tabMenus.Add(child.gameObject);
}
scrollRect = GetComponentInParent<SmoothScroll>();
switchTab(0);
if (scrollRect != null) scrollRect.resetScroll(true);
}
public void switchTab(int tab)
{
for (int i = 0; i < tabs.Count; i++)
{
//Set tab color
tabs[i].GetComponentInChildren<Button>().colors = tab == i ? TabColorActive : TabColorInactive;
//Set tab active state
if (tabMenus.Count > i) tabMenus[i].SetActive(tab == i);
}
if (scrollRect != null) scrollRect.ScrollToContent(tabs[tab].GetComponent<RectTransform>());
if (TabParent != null) LayoutRebuilder.ForceRebuildLayoutImmediate(TabParent.GetComponent<RectTransform>());
if (targetScroll != null) targetScroll.resetScroll();
}
}
}

View File

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

View File

@@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace CC
{
public class ClickAudio : MonoBehaviour
{
public int sound;
private void Start()
{
var buttons = GetComponentsInChildren<Button>();
foreach (var button in buttons)
{
button.onClick.AddListener(() => CC_UI_Manager.instance.playUIAudio(sound));
}
}
}
}

View File

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