You're welcome, @DanielKW.
DK UMA was a cool extension on top of UMA 2.0 -- I don't want to come off as entirely dismissive of it. At the time, your only alternative was to code your own framework for a system of interchangeable clothing, armor pieces, etc.
UMA is a very complex system, and layering another complex system on top of it produced a lot of headaches (in my experience). When DCS was included as a native part of UMA 2.5, it was clearly the better option, as it was simpler and fully integrated with the core UMA package. There are things that DK does out of the box that DCS doesn't, but I'd rather go with what is likely the more stable solution and extend it.
I think that DK could be re-worked as an extension of DCS (i.e. building off WardrobeRecipes instead of using its own separate slots & overlays), and I'd be quite interested in checking that out. But that's a pretty major rewrite of much of the system.
Anyway, here's the component I mentioned:
using UnityEngine;
using UMA.CharacterSystem;
namespace ORKIntegrationLibrary.UMA_DCS
{
public class DynamicEquipmentItem : MonoBehaviour
{
[SerializeField]
private string wardrobeSlot;
[SerializeField]
private UMAWardrobeRecipe _maleItemRecipe;
[SerializeField]
private UMAWardrobeRecipe _femaleItemRecipe;
private bool _isAdded = false;
private bool _isRemoved = false;
private DynamicCharacterAvatar _avatar;
private bool _useFemaleRecipe;
void Start()
{
_avatar = GetComponentInParent<DynamicCharacterAvatar>();
if (_avatar == null)
{
Debug.Log("DynamicEquipmentItem: Could not get DynamicCharacterAvatar component!");
return;
}
_useFemaleRecipe = _avatar.activeRace.name.ToUpper().Contains("FEMALE");
EquipItem();
}
private void EquipItem()
{
if (_useFemaleRecipe)
{
if (_femaleItemRecipe == null) { return; }
_avatar.SetSlot(_femaleItemRecipe);
}
else
{
if (_maleItemRecipe == null) { return; }
//Debug.Log("DynamicEquipmentItem: Equipping " + _maleItemRecipe.DisplayValue);
_avatar.SetSlot(_maleItemRecipe);
}
_isAdded = true;
_avatar.BuildCharacter(true);
}
private void OnDisable()
{
if (_avatar != null)
{
_avatar.ClearSlot(wardrobeSlot);
_avatar.BuildCharacter(true);
}
_isRemoved = true;
}
}
}
For clothing/armor items that are handled using UMA, I simply create an empty GameObject and add this component to it, then use that to create the Viewer Prefab. Just specify the name of the wardrobe slot, and then names of the male and female wardrobe recipes, and that's it.
I also create an empty GameObject as a child of the UMA character prefab (called "Equipment Setup" or something like that), and I place all of the Equipment Viewers for such items on that. No sense in fiddling around placing them on the corresponding bones of the skeleton when the items won't actually be mounted to the bones anyway.
The Start() method is called as soon as the prefab is instantiated, and that adds the item to the Avatar and rebuilds it.
If you're using ORK Equipment Viewers for weapons (say if you're not using an advanced character controller and its weapon placement system), then you can either add them via a script or you can get the UMA 2.7 Beta from Github and use the new Bone Builder feature to create the skeleton at design-time and set up the viewers as you normally would.
If you want to do it via script, you can adapt mine if you like:
using UnityEngine;
using System.Collections;
using UMA;
using ORKFramework.Behaviours;
namespace ORKIntegrationLibrary.UMA_DCS
{
public class ORKDynamicAvatarSetup : MonoBehaviour
{
[System.Serializable]
public class EquipmentViewerPrefabItem
{
public GameObject viewerPrefab;
public Vector3 positionOffset;
public Vector3 rotationOffset;
}
public bool createSheathedViewers = false;
static int headHash;
static int rightHandHash;
static int hipsHash;
static bool hashesFound = false;
[Header("Weapon Viewer Prefab")]
[SerializeField]
private EquipmentViewerPrefabItem _rightHandSlot;
[Header("Sheath Viewer Prefab")]
[SerializeField]
private EquipmentViewerPrefabItem _rightHandSheath;
public void SetupAvatar(UMAData umaData)
{
SetupEquipmentViewers(umaData);
}
private void SetupEquipmentViewers(UMAData umaData)
{
if (!hashesFound)
{
rightHandHash = UMAUtils.StringToHash("RightHand");
hipsHash = UMAUtils.StringToHash("Hips");
headHash = UMAUtils.StringToHash("Head");
hashesFound = true;
}
GameObject head = umaData.GetBoneGameObject(headHash);
GameObject rightHand = umaData.GetBoneGameObject(rightHandHash);
GameObject hips = umaData.GetBoneGameObject(hipsHash);
//if (head.GetComponent<EquipmentViewer>() == null)
//{
// var viewer = head.AddComponent<EquipmentViewer>();
// viewer.partID = ORKEquipmentPart.HELMET;
//}
//var torsoViewer = hips.AddComponent<EquipmentViewer>();
//torsoViewer.partID = ORKEquipmentPart.TORSO;
//var legsViewer = hips.AddComponent<EquipmentViewer>();
//legsViewer.partID = ORKEquipmentPart.LEGS;
//var feetViewer = hips.AddComponent<EquipmentViewer>();
//feetViewer.partID = ORKEquipmentPart.FEET;
if (_rightHandSlot != null)
{
Debug.Log("adding Right Hand slot");
var rightHandItems = Instantiate(_rightHandSlot.viewerPrefab, rightHand.transform.position, Quaternion.identity) as GameObject;
if (rightHandItems != null)
{
rightHandItems.transform.parent = rightHand.transform;
rightHandItems.transform.localPosition = _rightHandSlot.positionOffset;
rightHandItems.transform.localRotation = Quaternion.Euler(_rightHandSlot.rotationOffset);
rightHandItems.transform.localScale = Vector3.one;
}
}
else
{
Debug.Log("EquipmentViewersSlotScript: no prefab set for Right Hand Slot");
}
if (_rightHandSheath != null && createSheathedViewers)
{
var rightHandItems = Instantiate(_rightHandSheath.viewerPrefab, hips.transform.position, Quaternion.identity) as GameObject;
if (rightHandItems != null)
{
rightHandItems.transform.parent = hips.transform;
rightHandItems.transform.localPosition = _rightHandSheath.positionOffset;
rightHandItems.transform.localRotation = Quaternion.Euler(_rightHandSheath.rotationOffset);
rightHandItems.transform.localScale = Vector3.one;
}
}
}
}
}
The nested EquipmentViewerPrefabItem serializable class lets you specify the local position and rotation offset values for that particular viewer; the viewerPrefab here is just an empty GameObject with an ORK EquipmentViewer component on it, with the EquipPartID and visibility settings configured as appropriate.
The commented out part shows how you could add the EquipmentViewers for UMA slot items. The EquipPartIDs there use generated constants; if you look in the Tutorials section here, there's a post of mine with a script to generate these values from your ORK project file. Or you could just use integer ids.
You call this script (SetupAvatar method) from the CharacterCreated event on the Dynamic Character Avatar.