修改脚本

This commit is contained in:
2026-03-22 09:33:56 +08:00
parent 1e1fec02cd
commit ba5da38dde
127 changed files with 31514 additions and 22327 deletions

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
@@ -46,8 +46,12 @@ namespace CC
//Async loading
private Coroutine activeCoroutine;
//Store character LOD size for hair/apparel bounds
//Store character LOD for hair/apparel bounds
private float mainLODSize;
private LODGroup mainLODGroup;
//Main mesh bones
private Dictionary<string, Transform> mainMeshBoneMap;
#region Initialize script
@@ -69,8 +73,6 @@ namespace CC
CC_UI_Manager.instance.onDrag += OnPartDragged;
}
mainLODSize = GetComponent<LODGroup>() == null ? 1.5f : GetComponentInChildren<LODGroup>().size;
Initialize();
}
@@ -115,11 +117,17 @@ namespace CC
//Initializes this script - run on Start by default but you can run it whenever, see InstantiateCharacter for example
public void Initialize()
{
//Delete cosmetic prefabs
foreach (var toDelete in GetComponentsInChildren<DeleteOnStart>())
{
Destroy(toDelete.gameObject);
}
//Get main mesh LOD size and bone hierarchy
mainLODGroup = GetComponent<LODGroup>();
mainLODSize = mainLODGroup == null ? 1.5f : mainLODGroup.size;
if (MainMesh != null) mainMeshBoneMap = MainMesh.rootBone.GetComponentsInChildren<Transform>(true).ToDictionary(t => t.name, t => t);
foreach (var mesh in GetComponentsInChildren<SkinnedMeshRenderer>())
{
//Add a blendshape manager script to every mesh
@@ -234,57 +242,6 @@ namespace CC
}
}
public void SaveToPrefab()
{
#if UNITY_EDITOR
//Clone character data
string characterDataJSON = JsonUtility.ToJson(StoredCharacterData, true);
var characterDataCopy = JsonUtility.FromJson<CC_CharacterData>(characterDataJSON);
//Load original prefab to duplicate
var ogPrefab = Resources.Load(StoredCharacterData.CharacterPrefab);
if (ogPrefab == null) throw new System.Exception("Prefab not assigned in character data");
var newPrefab = (GameObject)PrefabUtility.InstantiatePrefab(ogPrefab);
//Delete character in JSON file
if (File.Exists(SavePath))
{
//Load CC_SaveData from JSON file
string jsonLoad = File.ReadAllText(SavePath);
var CC_SaveData = JsonUtility.FromJson<CC_SaveData>(jsonLoad);
int index = CC_SaveData.SavedCharacters.FindIndex(t => t.CharacterName == StoredCharacterData.CharacterName);
if (index != -1)
{
CC_SaveData.SavedCharacters.RemoveAt(index);
string jsonSave = JsonUtility.ToJson(CC_SaveData, true);
File.WriteAllText(SavePath, jsonSave);
}
}
//Update name and prefab
string prefabSuffix = "_" + CharacterName;
characterDataCopy.CharacterName = CharacterName;
//Create new prefab
string prefabPath = AssetDatabase.GetAssetPath(ogPrefab);
string newPath = prefabPath.Replace(".prefab", prefabSuffix + ".prefab");
newPrefab.GetComponent<CharacterCustomization>().CharacterName = CharacterName;
newPrefab.GetComponent<CharacterCustomization>().Autoload = true;
PrefabUtility.SaveAsPrefabAsset(newPrefab, newPath);
//Overwrite or add new preset
int presetIndex = Presets.Presets.FindIndex(t => t.CharacterName == characterDataCopy.CharacterName);
if (presetIndex != -1)
{
Presets.Presets[presetIndex] = characterDataCopy;
}
else Presets.Presets.Add(characterDataCopy);
DestroyImmediate(newPrefab);
#endif
}
public void SaveToPreset(string presetName)
{
#if UNITY_EDITOR
@@ -533,14 +490,18 @@ namespace CC
public void setHair(int selection, int slot)
{
if (slot >= HairTables.Count) Debug.LogError("Tried to set hair from non-existing hair table");
if (slot >= HairTables.Count) Debug.LogError("Tried to set hair from non-existing hair table, index: " + slot);
if (HairTables[slot].Hairstyles.Count > selection)
{
scrObj_Hair.Hairstyle HairData = HairTables[slot].Hairstyles[selection];
//Destroy active GameObject
if (HairObjects[slot] != null) Destroy(HairObjects[slot]);
if (HairObjects[slot] != null)
{
removeFromLODGroup(HairObjects[slot].GetComponentsInChildren<Renderer>());
Destroy(HairObjects[slot]);
}
//Set mesh if valid
if (HairTables[slot].Hairstyles[selection].Mesh != null)
@@ -560,40 +521,8 @@ namespace CC
}
}
//Add CopyPose script
if (HairData.AddCopyPoseScript)
{
HairObject.AddComponent<CopyPose>();
}
//Otherwise assume hierarchy is the same
else
{
foreach (var mesh in HairObject.GetComponentsInChildren<SkinnedMeshRenderer>())
{
var mainMeshTransforms = MainMesh.rootBone.GetComponentsInChildren<Transform>();
var mainMeshBoneMap = mainMeshTransforms.ToDictionary(t => t.name, t => t);
var mainMeshBones = new Transform[mesh.bones.Length];
var oldMeshRoot = mesh.rootBone;
//Map old bones to new bones
for (var i = 0; i < mesh.bones.Length; i++)
{
if (mesh.bones[i] == null) continue;
mainMeshBoneMap.TryGetValue(mesh.bones[i].name, out mainMeshBones[i]);
}
//Clean up old root and reassign properties
Destroy(oldMeshRoot.gameObject);
mesh.bones = mainMeshBones;
mesh.rootBone = MainMesh.rootBone;
mesh.localBounds = MainMesh.localBounds;
}
//Recalculate bounds
var lodGroup = HairObject.GetComponentInChildren<LODGroup>();
if (lodGroup != null) { lodGroup.RecalculateBounds(); lodGroup.size = mainLODSize; }
}
//Copy character bones to hair mesh
copyBones(HairObject, HairData.AddCopyPoseScript);
}
//Set shadow map
@@ -640,7 +569,7 @@ namespace CC
{
if (slot >= ApparelTables.Count)
{
Debug.LogError("Tried to set apparel from non-existing apparel table");
Debug.LogError("Tried to set apparel from non-existing apparel table: " + slot);
return;
}
@@ -712,7 +641,11 @@ namespace CC
#endregion Handle conflicting slots
//Destroy active GameObject
if (ApparelObjects[slot] != null) Destroy(ApparelObjects[slot]);
if (ApparelObjects[slot] != null)
{
removeFromLODGroup(ApparelObjects[slot].GetComponentsInChildren<Renderer>());
Destroy(ApparelObjects[slot]);
}
//Set mesh if valid
if (ApparelTables[slot].Items[selection].Mesh != null)
@@ -757,40 +690,8 @@ namespace CC
mesh.materials = baseMaterials;
}
//Add CopyPose script
if (ApparelData.AddCopyPoseScript)
{
ApparelObject.AddComponent<CopyPose>();
}
//Otherwise assume hierarchy is the same
else
{
foreach (var mesh in ApparelObject.GetComponentsInChildren<SkinnedMeshRenderer>())
{
var mainMeshTransforms = MainMesh.rootBone.GetComponentsInChildren<Transform>();
var mainMeshBoneMap = mainMeshTransforms.ToDictionary(t => t.name, t => t);
var mainMeshBones = new Transform[mesh.bones.Length];
var oldMeshRoot = mesh.rootBone;
//Map old bones to new bones
for (var i = 0; i < mesh.bones.Length; i++)
{
if (mesh.bones[i] == null) continue;
mainMeshBoneMap.TryGetValue(mesh.bones[i].name, out mainMeshBones[i]);
}
//Clean up old root and reassign properties
Destroy(oldMeshRoot.gameObject);
mesh.bones = mainMeshBones;
mesh.rootBone = MainMesh.rootBone;
mesh.localBounds = MainMesh.localBounds;
}
//Recalculate bounds
var lodGroup = ApparelObject.GetComponentInChildren<LODGroup>();
if (lodGroup != null) { lodGroup.RecalculateBounds(); lodGroup.size = mainLODSize; }
}
//Copy character bones to apparel mesh
copyBones(ApparelObject, ApparelData.AddCopyPoseScript);
}
//Set foot offset
@@ -855,6 +756,101 @@ namespace CC
}
}
private void copyBones(GameObject GO, bool addCopyPoseScript)
{
var meshes = GO.GetComponentsInChildren<SkinnedMeshRenderer>();
//Add CopyPose script or reassign bones
if (addCopyPoseScript)
{
GO.AddComponent<CopyPose>().ParseBones(mainMeshBoneMap);
}
else
{
foreach (var mesh in meshes)
{
var mainMeshBones = new Transform[mesh.bones.Length];
var oldMeshRoot = mesh.rootBone;
for (var i = 0; i < mesh.bones.Length; i++)
mainMeshBoneMap.TryGetValue(mesh.bones[i].name, out mainMeshBones[i]);
Destroy(oldMeshRoot.gameObject);
mesh.bones = mainMeshBones;
mesh.rootBone = MainMesh.rootBone;
}
}
//Recalculate bounds
foreach (var mesh in meshes)
mesh.localBounds = MainMesh.localBounds;
//Merge LODs
var childLODGroup = GO.GetComponentInChildren<LODGroup>();
if (mainLODGroup != null && childLODGroup != null)
{
var mainLODs = mainLODGroup.GetLODs();
var childLODs = childLODGroup.GetLODs();
//Temporarily store renderers before destroying child LODGroup
var childRenderersPerLOD = childLODs.Select(lod => lod.renderers).ToList();
//Destroy the child LODGroup
DestroyImmediate(childLODGroup);
//Determine the maximum LOD count
int maxLodCount = Mathf.Max(mainLODs.Length, childRenderersPerLOD.Count);
LOD[] mergedLODs = new LOD[maxLodCount];
for (int i = 0; i < maxLodCount; i++)
{
Renderer[] combinedRenderers;
float screenRelativeHeight;
if (i < mainLODs.Length && i < childRenderersPerLOD.Count)
{
//Both have this LOD level - merge renderers, keep main's distance
combinedRenderers = mainLODs[i].renderers.Where(r => r).Concat(childRenderersPerLOD[i]).ToArray();
screenRelativeHeight = mainLODs[i].screenRelativeTransitionHeight;
}
else if (i < mainLODs.Length)
{
//Only main has this LOD level
combinedRenderers = mainLODs[i].renderers.Where(r => r).ToArray();
screenRelativeHeight = mainLODs[i].screenRelativeTransitionHeight;
}
else
{
//Only child has this LOD level - use child's distance
combinedRenderers = childRenderersPerLOD[i];
screenRelativeHeight = childLODs[i].screenRelativeTransitionHeight;
}
mergedLODs[i] = new LOD(screenRelativeHeight, combinedRenderers);
}
//Apply and update
mainLODGroup.SetLODs(mergedLODs);
}
}
private void removeFromLODGroup(IEnumerable<Renderer> renderersToRemove)
{
if (mainLODGroup == null || renderersToRemove == null) return;
//Get current LODs
var lods = mainLODGroup.GetLODs();
//Remove renderers
for (int i = 0; i < lods.Length; i++)
{
lods[i].renderers = lods[i].renderers.Except(renderersToRemove).ToArray();
}
mainLODGroup.SetLODs(lods);
}
public void randomizeAll()
{
if (Randomizer == null) return;
@@ -906,6 +902,7 @@ namespace CC
public List<Material> getRelevantMaterials(int materialIndex, string meshTag)
{
//Get meshes by tag if desired, otherwise get all meshes
IEnumerable<Renderer> meshes = string.IsNullOrEmpty(meshTag)
? gameObject.GetComponentsInChildren<Renderer>()
: getMeshByTag(meshTag);
@@ -924,7 +921,7 @@ namespace CC
}
else
{
//Add all materials
//Otherwise add all materials
materials.AddRange(mesh.materials);
}
}
@@ -940,14 +937,17 @@ namespace CC
//Set texture property
public void setTextureProperty(CC_Property p, bool save = false, Texture2D t = null)
{
if (t != null) p.stringValue = t.name;
//Get relevant materials and set texture
foreach (var material in getRelevantMaterials(p.materialIndex, p.meshTag))
{
if (material.HasProperty(p.propertyName)) material.SetTexture(p.propertyName, (t != null) ? t : Resources.Load<Texture2D>(p.stringValue));
}
if (save) saveProperty(ref StoredCharacterData.TextureProperties, p);
if (save)
{
if (t != null) p.stringValue = t.name; //Store string value if saving
saveProperty(ref StoredCharacterData.TextureProperties, p);
}
}
//Set float property
@@ -976,6 +976,7 @@ namespace CC
public bool findProperty(List<CC_Property> properties, CC_Property p, out CC_Property pOut, out int index)
{
//Properties are "equal" if property name, material index and mesh tag are identical
int i = properties.FindIndex(t => t.propertyName == p.propertyName && t.materialIndex == p.materialIndex && t.meshTag == p.meshTag);
if (i >= 0)
{

View File

@@ -6,43 +6,31 @@ namespace CC
{
public class CopyPose : MonoBehaviour
{
private Transform[] SourceHierarchy;
private Transform[] TargetHierarchy;
public List<Transform> SourceBones = new List<Transform>();
public List<Transform> TargetBones = new List<Transform>();
private void Start()
public void ParseBones(Dictionary<string, Transform> mainMeshBoneMap)
{
//Get meshes
var mainScript = GetComponentInParent<CharacterCustomization>();
var sourceMesh = mainScript.MainMesh;
var targetMeshes = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();
var targetHierarchy = GetRootBone(GetComponentInChildren<SkinnedMeshRenderer>().rootBone).GetComponentsInChildren<Transform>();
//Copy bounds from character
var LODGroup = GetComponentInChildren<LODGroup>();
var mainLODGroup = mainScript.GetComponent<LODGroup>();
if (LODGroup != null) { LODGroup.RecalculateBounds(); LODGroup.size = (mainLODGroup == null) ? 1.5f : mainLODGroup.size; }
var targetBonesDict = targetHierarchy.ToDictionary(t => t.name, t => t);
foreach (var mesh in targetMeshes)
foreach (var (boneName, sourceBone) in mainMeshBoneMap)
{
mesh.localBounds = sourceMesh.localBounds;
if (!targetBonesDict.TryGetValue(boneName, out var targetBone)) continue;
//Re-parent & reset local transform
targetBone.SetParent(sourceBone);
targetBone.localPosition = Vector3.zero;
targetBone.localRotation = Quaternion.identity;
targetBone.localScale = Vector3.one;
if (!TargetBones.Contains(targetBone)) TargetBones.Add(targetBone);
}
//Get bone hierarchies
SourceHierarchy = sourceMesh.rootBone.GetComponentsInChildren<Transform>();
TargetHierarchy = GetRootBone(targetMeshes[0].rootBone).GetComponentsInChildren<Transform>();
var targetBonesDict = TargetHierarchy.ToDictionary(t => t.name, t => t);
//Only copy bones that are found in both hierarchies, also ensures order is the same
foreach (Transform child in SourceHierarchy)
//Rename bones to avoid duplicate object names
foreach (var bone in targetHierarchy)
{
//Check if a bone with the same name exists in the target hierarchy using the dictionary
if (targetBonesDict.TryGetValue(child.name, out var targetBone))
{
SourceBones.Add(child);
TargetBones.Add(targetBone);
}
bone.name += "_" + GetInstanceID();
}
}
@@ -52,14 +40,16 @@ namespace CC
return GetRootBone(bone.parent);
}
private void LateUpdate()
private void OnDestroy()
{
//Copy bone transform
for (int i = 0; i < SourceBones.Count; i++)
if (TargetBones == null) return;
foreach (Transform t in TargetBones)
{
TargetBones[i].localPosition = SourceBones[i].localPosition;
TargetBones[i].localRotation = SourceBones[i].localRotation;
TargetBones[i].localScale = SourceBones[i].localScale;
if (t != null)
{
Destroy(t.gameObject);
}
}
}
}

View File

@@ -6,6 +6,7 @@ namespace CC
public class HeadColliders : MonoBehaviour
{
public List<ColliderSetup> colliders = new List<ColliderSetup>();
public List<GameObject> colliderObjects = new List<GameObject>();
public void createColliders()
{
@@ -23,8 +24,8 @@ namespace CC
private void CreateColliderObject(Vector3 localPosition, string label)
{
GameObject colliderObject = new GameObject(label);
//colliderObject.layer = LayerMask.NameToLayer("UI");
GameObject colliderObject = new(label);
colliderObjects.Add(colliderObject);
colliderObject.transform.SetParent(transform);

View File

@@ -7,7 +7,6 @@ namespace CC
public class PhysicsManager : MonoBehaviour
{
public Animator animator;
public CapsuleCollider capsule;
private Rigidbody[] rigidBodies;
private Collider[] colliders;
private ModifyBone[] modifyBones;
@@ -20,7 +19,6 @@ namespace CC
private void Awake()
{
if (animator == null) animator = gameObject.GetComponent<Animator>();
if (capsule == null) capsule = gameObject.GetComponent<CapsuleCollider>();
rigidBodies = gameObject.GetComponentsInChildren<Rigidbody>();
colliders = gameObject.GetComponentsInChildren<Collider>();
modifyBones = gameObject.GetComponentsInChildren<ModifyBone>();
@@ -36,7 +34,6 @@ namespace CC
//Disable colliders if not in customization mode
foreach (var item in colliders)
{
if (item == capsule) continue; //Ignore capsule
item.enabled = customizing;
}
}
@@ -61,9 +58,6 @@ namespace CC
item.enabled = true;
}
//Disable capsule when customizing
if (capsule != null) capsule.enabled = false;
customizing = true;
}
@@ -85,9 +79,6 @@ namespace CC
if (ragdolling) yield return new WaitForFixedUpdate();
//Disable capsule when ragdolling or customizing
if (capsule != null) capsule.enabled = !ragdolling;
//Enable physics
foreach (var item in rigidBodies)
{