d2c5e0c92c
Worked on the Editor's Actor creator. NPC's can now have custom colored skins and hairs.
1108 lines
49 KiB
C#
1108 lines
49 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEditorInternal;
|
|
using System;
|
|
using RPGCreationKit;
|
|
|
|
namespace RPGCreationKit
|
|
{
|
|
/// <summary>
|
|
/// Window to create races.
|
|
/// </summary>
|
|
public class RaceEditor : EditorWindow
|
|
{
|
|
bool isReady = false;
|
|
|
|
SerializedObject serializedObject;
|
|
|
|
// Navigation
|
|
public enum CurrentWindow { General, BodyData, FaceData, TextData, Factions };
|
|
CurrentWindow m_Window;
|
|
|
|
// Buttons scroll
|
|
Vector2 indexesScrollView;
|
|
Vector2 contentScrollView;
|
|
|
|
Vector2 hairWindowScrollView;
|
|
Vector2 eyeWindowScrollView;
|
|
|
|
GUIStyle guiLayoutDarkColor = new GUIStyle();
|
|
GUIStyle guiLayoutLightColor = new GUIStyle();
|
|
|
|
List<Race> allRaces = new List<Race>();
|
|
|
|
UnityEngine.Object malePrefab; // prefabs in the scene that are being edited
|
|
UnityEngine.Object femalePrefab; // prefabs in the scene that are being edited
|
|
string assetPathMale;
|
|
string assetPathFemale;
|
|
|
|
Race selectedRace; // copy of the original race object
|
|
bool hasSelectedRace = false;
|
|
int selectedRaceIndex = -1;
|
|
bool isCreatingNewRace = false;
|
|
|
|
Texture refreshButtonIcon;
|
|
|
|
public ReorderableList maleHairList;
|
|
public ReorderableList femaleHairList;
|
|
|
|
public ReorderableList eyeList;
|
|
|
|
GameObject gameObject;
|
|
Editor windowEditor;
|
|
bool gameObjectChanged = false;
|
|
|
|
bool maleSelected = true;
|
|
|
|
// Customize face
|
|
Vector2 faceBlendshapesContent;
|
|
float sliderValue;
|
|
int selectedBlendshapeIndex;
|
|
|
|
struct HairSelection
|
|
{
|
|
public int index;
|
|
public bool isMale;
|
|
|
|
public HairSelection(int _index, bool _isMale)
|
|
{
|
|
index = _index;
|
|
isMale = _isMale;
|
|
}
|
|
}
|
|
|
|
struct EyeSelection
|
|
{
|
|
public int index;
|
|
public bool isMale;
|
|
|
|
public EyeSelection(int _index, bool _isMale)
|
|
{
|
|
index = _index;
|
|
isMale = _isMale;
|
|
}
|
|
}
|
|
|
|
[MenuItem("RPG Creation Kit/Race Editor")]
|
|
public static void ShowWindow()
|
|
{
|
|
EditorWindow thisWindow = GetWindow(typeof(RaceEditor));
|
|
|
|
// Set Title
|
|
Texture icon = AssetDatabase.LoadAssetAtPath<Texture>(EditorIconsPath.RaceEditorIcon);
|
|
GUIContent titleContent = new GUIContent("Race Editor", icon);
|
|
thisWindow.titleContent = titleContent;
|
|
|
|
thisWindow.minSize = new Vector2(1265, 575);
|
|
thisWindow.maxSize = new Vector2(1265, 575);
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
guiLayoutDarkColor.normal.background = MakeTex(600, 1, new Color(5.0f, 5.0f, 5.0f, .1f));
|
|
guiLayoutLightColor.normal.background = MakeTex(600, 1, new Color(37.0f, 79.0f, 133.0f, .4f));
|
|
refreshButtonIcon = AssetDatabase.LoadAssetAtPath<Texture>(EditorIconsPath.RefreshButton);
|
|
RefreshRaceList();
|
|
isReady = true;
|
|
}
|
|
|
|
private Texture2D MakeTex(int width, int height, Color col)
|
|
{
|
|
Color[] pix = new Color[width * height];
|
|
|
|
for (int i = 0; i < pix.Length; i++)
|
|
pix[i] = col;
|
|
|
|
Texture2D result = new Texture2D(width, height);
|
|
result.SetPixels(pix);
|
|
result.Apply();
|
|
|
|
return result;
|
|
}
|
|
|
|
public void RefreshRaceList()
|
|
{
|
|
allRaces = GetAllRaces<Race>();
|
|
allRaces.Sort(SortByID);
|
|
}
|
|
|
|
|
|
private void OnSelectedRaceChanges()
|
|
{
|
|
selectedRace = allRaces[selectedRaceIndex];
|
|
|
|
// Create a new object of this race
|
|
serializedObject = new SerializedObject(allRaces[selectedRaceIndex]);
|
|
RegenerateMalePrefabs();
|
|
RegenerateFemalePrefabs();
|
|
|
|
// Restore the lists
|
|
maleHairList = new ReorderableList(serializedObject, serializedObject.FindProperty("maleHairTypes"), true, true, true, true);
|
|
femaleHairList = new ReorderableList(serializedObject, serializedObject.FindProperty("femaleHairTypes"), true, true, true, true);
|
|
|
|
eyeList = new ReorderableList(serializedObject, serializedObject.FindProperty("eyeTypes"), true, true, true, true);
|
|
}
|
|
|
|
private void RegenerateFemalePrefabs()
|
|
{
|
|
DestroyImmediate(windowEditor);
|
|
|
|
if (femalePrefab != null)
|
|
DestroyImmediate(((GameObject)femalePrefab).gameObject);
|
|
|
|
if (serializedObject.FindProperty("femaleModel").objectReferenceValue != null)
|
|
{
|
|
assetPathFemale = AssetDatabase.GetAssetPath(((BodyData)serializedObject.FindProperty("femaleModel").objectReferenceValue).gameObject);
|
|
femalePrefab = PrefabUtility.LoadPrefabContents(assetPathFemale);
|
|
femalePrefab.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
|
|
EditorUtility.SetDirty(femalePrefab);
|
|
}
|
|
gameObjectChanged = true;
|
|
}
|
|
|
|
private void RegenerateMalePrefabs()
|
|
{
|
|
DestroyImmediate(windowEditor);
|
|
|
|
// If there is an Editor instance of the body, destroy it
|
|
if (malePrefab != null)
|
|
DestroyImmediate(((GameObject)malePrefab).gameObject);
|
|
|
|
// If the Races has models, spawn them as prefabs
|
|
if (serializedObject.FindProperty("maleModel").objectReferenceValue != null)
|
|
{
|
|
assetPathMale = AssetDatabase.GetAssetPath(((BodyData)serializedObject.FindProperty("maleModel").objectReferenceValue).gameObject);
|
|
|
|
malePrefab = PrefabUtility.LoadPrefabContents(assetPathMale);
|
|
//malePrefab.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
|
|
EditorUtility.SetDirty(malePrefab);
|
|
}
|
|
|
|
gameObjectChanged = true;
|
|
}
|
|
|
|
private static int SortByID(Race r1, Race r2)
|
|
{
|
|
return r1.ID.CompareTo(r2.ID);
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
if (!isReady)
|
|
Init();
|
|
|
|
|
|
GUIStyle ButtonStyle = new GUIStyle(GUI.skin.button) { alignment = TextAnchor.MiddleCenter };
|
|
ButtonStyle.border = new RectOffset(2, 2, 2, 2);
|
|
ButtonStyle.fontSize = 16;
|
|
ButtonStyle.font = (Font)AssetDatabase.LoadAssetAtPath<Font>("Assets/RPG Creation Kit/Fonts/monofonto.ttf");
|
|
|
|
GUIStyle HeaderStyle = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleLeft };
|
|
HeaderStyle.fontSize = 14;
|
|
HeaderStyle.font = (Font)AssetDatabase.LoadAssetAtPath<Font>("Assets/RPG Creation Kit/Fonts/monofonto.ttf");
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
EditorGUILayout.BeginVertical("box", GUILayout.MaxWidth(200.0f), GUILayout.ExpandWidth(false));
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("RACES", HeaderStyle, GUILayout.MaxWidth(165.0f), GUILayout.ExpandWidth(false));
|
|
|
|
if (GUILayout.Button(new GUIContent(refreshButtonIcon, "Refreshes the Race list."), GUILayout.MaxWidth(32)))
|
|
RefreshRaceList();
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
indexesScrollView = EditorGUILayout.BeginScrollView(indexesScrollView, GUIStyle.none, GUI.skin.verticalScrollbar, GUILayout.Width(200.0f), GUILayout.Height(500.0f));
|
|
indexesScrollView.x = (indexesScrollView.x / 2);
|
|
Rect clickArea = EditorGUILayout.BeginVertical(guiLayoutDarkColor, GUILayout.Width(200.0f), GUILayout.Height(500.0f));
|
|
|
|
Event current = Event.current;
|
|
|
|
GUI.enabled = !isCreatingNewRace;
|
|
|
|
Color defaultColor = GUI.color;
|
|
|
|
// Display all current quest stages
|
|
for (int i = 0; i < allRaces.Count; i++)
|
|
{
|
|
if (selectedRaceIndex == i && hasSelectedRace)
|
|
GUI.color = Color.green;
|
|
|
|
GUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button(allRaces[i].ID, GUILayout.MaxWidth(170.0f)))
|
|
{
|
|
GUI.FocusControl(null);
|
|
selectedRaceIndex = i;
|
|
hasSelectedRace = true;
|
|
OnSelectedRaceChanges();
|
|
}
|
|
GUILayout.FlexibleSpace();
|
|
GUILayout.EndHorizontal();
|
|
|
|
GUI.color = defaultColor;
|
|
}
|
|
GUI.enabled = true;
|
|
|
|
if (!isCreatingNewRace)
|
|
{
|
|
// DROPDOWN TO CREATE NEW STAGES
|
|
if (clickArea.Contains(current.mousePosition) && current.type == EventType.ContextClick)
|
|
{
|
|
GenericMenu menu = new GenericMenu();
|
|
|
|
menu.AddDisabledItem(new GUIContent("Races"));
|
|
menu.AddSeparator("");
|
|
|
|
menu.AddItem(new GUIContent("New"), false, CreateNew);
|
|
menu.AddItem(new GUIContent("Delete Selected"), false, DeleteSelected);
|
|
|
|
menu.ShowAsContext();
|
|
|
|
current.Use();
|
|
}
|
|
|
|
void CreateNew()
|
|
{
|
|
Debug.Log("Creating new Race");
|
|
}
|
|
|
|
void DeleteSelected()
|
|
{
|
|
if(hasSelectedRace)
|
|
Debug.Log("Deleting: " + selectedRace.ID);
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.BeginVertical();
|
|
EditorGUILayout.BeginHorizontal("box");
|
|
|
|
#region NavigationMenu
|
|
// Buttons for navigation
|
|
|
|
if (m_Window == CurrentWindow.General)
|
|
GUI.color = Color.green;
|
|
else
|
|
GUI.color = defaultColor;
|
|
|
|
if (GUILayout.Button("General", GUILayout.Width(200), GUILayout.Height(20)))
|
|
{
|
|
m_Window = CurrentWindow.General;
|
|
}
|
|
|
|
if (m_Window == CurrentWindow.BodyData)
|
|
GUI.color = Color.green;
|
|
else
|
|
GUI.color = defaultColor;
|
|
|
|
if (GUILayout.Button("Body Data", GUILayout.Width(200), GUILayout.Height(20)))
|
|
{
|
|
if (m_Window != CurrentWindow.BodyData)
|
|
DestroyImmediate(windowEditor);
|
|
|
|
m_Window = CurrentWindow.BodyData;
|
|
gameObjectChanged = true;
|
|
}
|
|
|
|
if (m_Window == CurrentWindow.FaceData)
|
|
GUI.color = Color.green;
|
|
else
|
|
GUI.color = defaultColor;
|
|
|
|
if (GUILayout.Button("Face Data", GUILayout.Width(200), GUILayout.Height(20)))
|
|
{
|
|
if (m_Window != CurrentWindow.FaceData)
|
|
DestroyImmediate(windowEditor);
|
|
|
|
m_Window = CurrentWindow.FaceData;
|
|
gameObjectChanged = true;
|
|
}
|
|
|
|
if (m_Window == CurrentWindow.TextData)
|
|
GUI.color = Color.green;
|
|
else
|
|
GUI.color = defaultColor;
|
|
|
|
if (GUILayout.Button("Text Data", GUILayout.Width(200), GUILayout.Height(20)))
|
|
{
|
|
m_Window = CurrentWindow.TextData;
|
|
}
|
|
|
|
if (m_Window == CurrentWindow.Factions)
|
|
GUI.color = Color.green;
|
|
else
|
|
GUI.color = defaultColor;
|
|
|
|
if (GUILayout.Button("Factions", GUILayout.Width(200), GUILayout.Height(20)))
|
|
{
|
|
m_Window = CurrentWindow.Factions;
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
GUI.color = defaultColor;
|
|
#endregion
|
|
|
|
if (hasSelectedRace && serializedObject != null)
|
|
{
|
|
// here goes the switch window
|
|
switch (m_Window)
|
|
{
|
|
case CurrentWindow.General:
|
|
contentScrollView =
|
|
EditorGUILayout.BeginScrollView(contentScrollView, GUIStyle.none, GUI.skin.verticalScrollbar, GUILayout.Width(650.0f), GUILayout.Height(500.0f));
|
|
|
|
var raceID = serializedObject.FindProperty("ID");
|
|
var raceName = serializedObject.FindProperty("raceName");
|
|
|
|
var maleAtt = serializedObject.FindProperty("baseAttributesMale");
|
|
var femaleAtt = serializedObject.FindProperty("baseAttributesFemale");
|
|
|
|
var maleDerAtt = serializedObject.FindProperty("baseDerivedAttributesMale");
|
|
var femaleDerAtt = serializedObject.FindProperty("baseDerivedAttributesFemale");
|
|
|
|
EditorGUILayout.LabelField("BASE INFORMATION", HeaderStyle);
|
|
|
|
EditorGUILayout.BeginVertical("box");
|
|
EditorGUILayout.PropertyField(raceID, GUILayout.Width(400));
|
|
EditorGUILayout.PropertyField(raceName, GUILayout.Width(400));
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.Space(5);
|
|
|
|
EditorGUILayout.LabelField("BASE ATTRIBUTES", HeaderStyle);
|
|
EditorGUILayout.BeginVertical();
|
|
|
|
EditorGUILayout.BeginHorizontal("box");
|
|
EditorGUILayout.PropertyField(maleAtt, GUILayout.Width(200));
|
|
EditorGUILayout.PropertyField(femaleAtt, GUILayout.Width(200));
|
|
maleAtt.isExpanded = true;
|
|
femaleAtt.isExpanded = true;
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.BeginHorizontal("box");
|
|
EditorGUILayout.PropertyField(maleDerAtt, GUILayout.Width(200));
|
|
EditorGUILayout.PropertyField(femaleDerAtt, GUILayout.Width(200));
|
|
maleDerAtt.isExpanded = true;
|
|
femaleDerAtt.isExpanded = true;
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.EndVertical();
|
|
EditorGUILayout.EndScrollView();
|
|
break;
|
|
|
|
case CurrentWindow.BodyData:
|
|
#region content
|
|
contentScrollView =
|
|
EditorGUILayout.BeginScrollView(contentScrollView, GUIStyle.none, GUI.skin.verticalScrollbar, GUILayout.Width(650.0f), GUILayout.Height(500.0f));
|
|
|
|
var maleBody = serializedObject.FindProperty("maleModel");
|
|
var femaleBody = serializedObject.FindProperty("femaleModel");
|
|
|
|
var maleFPS = serializedObject.FindProperty("maleFPS");
|
|
var femaleFPS = serializedObject.FindProperty("femaleFPS");
|
|
|
|
EditorGUILayout.LabelField("BODY DATA", HeaderStyle);
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.BeginVertical("box");
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.PropertyField(maleBody, GUILayout.MaxWidth(350));
|
|
if (EditorGUI.EndChangeCheck())
|
|
RegenerateMalePrefabs();
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.PropertyField(femaleBody, GUILayout.MaxWidth(350));
|
|
if (EditorGUI.EndChangeCheck())
|
|
RegenerateFemalePrefabs();
|
|
|
|
|
|
EditorGUILayout.PropertyField(maleFPS, GUILayout.MaxWidth(350));
|
|
EditorGUILayout.PropertyField(femaleFPS, GUILayout.MaxWidth(350));
|
|
|
|
// Draw Model Preview
|
|
// Draw Model Preview
|
|
|
|
GUIStyle bgColor = new GUIStyle();
|
|
bgColor.normal.background = Texture2D.blackTexture;
|
|
|
|
if (windowEditor == null)
|
|
{
|
|
if (maleSelected)
|
|
{
|
|
if (malePrefab != null)
|
|
{
|
|
windowEditor = Editor.CreateEditor(malePrefab);
|
|
gameObjectChanged = false;
|
|
}
|
|
else
|
|
DestroyImmediate(windowEditor);
|
|
}
|
|
else if (femalePrefab != null)
|
|
{
|
|
windowEditor = Editor.CreateEditor(femalePrefab);
|
|
gameObjectChanged = false;
|
|
}else
|
|
DestroyImmediate(windowEditor);
|
|
|
|
}
|
|
|
|
if (windowEditor != null)
|
|
{
|
|
windowEditor.DrawPreview(GUILayoutUtility.GetRect(64, 355));
|
|
windowEditor.OnInteractivePreviewGUI(GUILayoutUtility.GetRect(1, 1), bgColor);
|
|
windowEditor.ReloadPreviewInstances();
|
|
windowEditor.Repaint();
|
|
windowEditor.ResetTarget();
|
|
this.Repaint();
|
|
} else
|
|
{
|
|
EditorGUILayout.Space(315);
|
|
|
|
if (maleSelected)
|
|
EditorGUILayout.HelpBox("There is no Male Model associated with this race, reference a prefab in the \"Body Data\" tab.", MessageType.Warning);
|
|
else
|
|
EditorGUILayout.HelpBox("There is no Female Model associated with this race, reference a prefab in the \"Body Data\" tab.", MessageType.Warning);
|
|
}
|
|
|
|
// Male/Female switch
|
|
EditorGUILayout.BeginHorizontal();
|
|
GUI.color = (maleSelected) ? Color.grey : defaultColor;
|
|
if (GUILayout.Button("Male"))
|
|
{
|
|
maleSelected = true;
|
|
DestroyImmediate(windowEditor);
|
|
}
|
|
|
|
GUI.color = (!maleSelected) ? Color.grey : defaultColor;
|
|
if (GUILayout.Button("Female"))
|
|
{
|
|
maleSelected = false;
|
|
DestroyImmediate(windowEditor);
|
|
}
|
|
|
|
GUI.color = defaultColor;
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
this.Repaint();
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.BeginVertical();
|
|
hairWindowScrollView =
|
|
EditorGUILayout.BeginScrollView(hairWindowScrollView, GUIStyle.none, GUI.skin.verticalScrollbar, GUILayout.MaxHeight(200.0f));
|
|
|
|
EditorGUILayout.BeginVertical("box", GUILayout.MaxHeight(200.0f));
|
|
|
|
int currentMaleIndex = serializedObject.FindProperty("defaultMaleHair").intValue;
|
|
int currentFemaleIndex = serializedObject.FindProperty("defaultFemaleHair").intValue;
|
|
|
|
if (maleSelected)
|
|
{
|
|
maleHairList.DoLayoutList();
|
|
maleHairList.drawHeaderCallback = (Rect rect) =>
|
|
{
|
|
EditorGUI.LabelField(rect, "(MALE) Hair Types", HeaderStyle);
|
|
};
|
|
|
|
maleHairList.drawElementCallback =
|
|
(Rect rect, int index, bool isActive, bool isFocused) =>
|
|
{
|
|
var element = maleHairList.serializedProperty.GetArrayElementAtIndex(index);
|
|
rect.y += 2;
|
|
|
|
EditorGUI.LabelField(rect, "Type " + index.ToString());
|
|
EditorGUI.PropertyField(
|
|
new Rect(rect.x + 60, rect.y, 150, EditorGUIUtility.singleLineHeight),
|
|
element, GUIContent.none);
|
|
};
|
|
|
|
|
|
maleHairList.onRemoveCallback = (ReorderableList list) =>
|
|
{
|
|
var element = list.serializedProperty.GetArrayElementAtIndex(list.index);
|
|
element.DeleteCommand();
|
|
element.DeleteCommand();
|
|
|
|
serializedObject.FindProperty("defaultMaleHair").intValue = -1;
|
|
currentMaleIndex = -1;
|
|
|
|
SelectHairType(new HairSelection(-1, true));
|
|
};
|
|
|
|
} else
|
|
{
|
|
femaleHairList.DoLayoutList();
|
|
femaleHairList.drawHeaderCallback = (Rect rect) =>
|
|
{
|
|
EditorGUI.LabelField(rect, "(FEMALE) Hair Types", HeaderStyle);
|
|
};
|
|
|
|
femaleHairList.drawElementCallback =
|
|
(Rect rect, int index, bool isActive, bool isFocused) =>
|
|
{
|
|
var element = femaleHairList.serializedProperty.GetArrayElementAtIndex(index);
|
|
rect.y += 2;
|
|
|
|
EditorGUI.LabelField(rect, "Type " + index.ToString());
|
|
EditorGUI.PropertyField(
|
|
new Rect(rect.x + 60, rect.y, 150, EditorGUIUtility.singleLineHeight),
|
|
element, GUIContent.none);
|
|
};
|
|
|
|
femaleHairList.onRemoveCallback = (ReorderableList list) =>
|
|
{
|
|
var element = list.serializedProperty.GetArrayElementAtIndex(list.index);
|
|
element.DeleteCommand();
|
|
element.DeleteCommand();
|
|
|
|
serializedObject.FindProperty("defaultFemaleHair").intValue = -1;
|
|
currentFemaleIndex = -1;
|
|
|
|
SelectHairType(new HairSelection(-1, false));
|
|
};
|
|
}
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
eyeWindowScrollView =
|
|
EditorGUILayout.BeginScrollView(eyeWindowScrollView, GUIStyle.none, GUI.skin.verticalScrollbar, GUILayout.MaxHeight(200.0f));
|
|
|
|
EditorGUILayout.BeginVertical("box", GUILayout.MaxHeight(200.0f));
|
|
|
|
eyeList.DoLayoutList();
|
|
eyeList.drawHeaderCallback = (Rect rect) => {
|
|
EditorGUI.LabelField(rect, "Eyes Types", HeaderStyle);
|
|
};
|
|
|
|
eyeList.drawElementCallback =
|
|
(Rect rect, int index, bool isActive, bool isFocused) => {
|
|
var element = eyeList.serializedProperty.GetArrayElementAtIndex(index);
|
|
rect.y += 2;
|
|
|
|
EditorGUI.LabelField(rect, "Type " + index.ToString());
|
|
EditorGUI.PropertyField(
|
|
new Rect(rect.x + 60, rect.y, 150, EditorGUIUtility.singleLineHeight),
|
|
element, GUIContent.none);
|
|
};
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.BeginVertical("box", GUILayout.MaxWidth(280.0f));
|
|
EditorGUILayout.LabelField("Default Hair", HeaderStyle);
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("Male:", GUILayout.MaxWidth(55.0f));
|
|
GUIContent newContent = new GUIContent();
|
|
|
|
// And update the serializedObject
|
|
currentMaleIndex = serializedObject.FindProperty("defaultMaleHair").intValue;
|
|
|
|
try
|
|
{
|
|
newContent.text = (currentMaleIndex != -1) ? maleHairList.serializedProperty.GetArrayElementAtIndex(currentMaleIndex).objectReferenceValue.name : "NONE";
|
|
}
|
|
catch
|
|
{
|
|
currentMaleIndex = -1;
|
|
newContent.text = (currentMaleIndex != -1) ? maleHairList.serializedProperty.GetArrayElementAtIndex(currentMaleIndex).objectReferenceValue.name : "NONE";
|
|
}
|
|
if (GUILayout.Button(newContent))
|
|
{
|
|
GenericMenu menu = new GenericMenu();
|
|
|
|
menu.AddDisabledItem(new GUIContent("HairTypes"));
|
|
menu.AddSeparator("");
|
|
|
|
menu.AddItem(new GUIContent("NONE"), (serializedObject.FindProperty("defaultMaleHair").intValue == -1), SelectHairType, new HairSelection(-1, true));
|
|
|
|
for (int i = 0; i < maleHairList.count; i++)
|
|
{
|
|
bool isSelected = (serializedObject.FindProperty("defaultMaleHair").intValue == i);
|
|
if(maleHairList.serializedProperty.GetArrayElementAtIndex(i).objectReferenceValue != null)
|
|
menu.AddItem(new GUIContent(maleHairList.serializedProperty.GetArrayElementAtIndex(i).objectReferenceValue.name), isSelected, SelectHairType, new HairSelection(i, true));
|
|
}
|
|
|
|
menu.ShowAsContext();
|
|
|
|
current.Use();
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("Female:", GUILayout.MaxWidth(55.0f));
|
|
newContent = new GUIContent();
|
|
|
|
currentFemaleIndex = serializedObject.FindProperty("defaultFemaleHair").intValue;
|
|
|
|
try
|
|
{
|
|
newContent.text = (currentFemaleIndex != -1) ? femaleHairList.serializedProperty.GetArrayElementAtIndex(currentFemaleIndex).objectReferenceValue.name : "NONE";
|
|
}
|
|
catch
|
|
{
|
|
currentFemaleIndex = -1;
|
|
newContent.text = (currentFemaleIndex != -1) ? femaleHairList.serializedProperty.GetArrayElementAtIndex(currentFemaleIndex).objectReferenceValue.name : "NONE";
|
|
}
|
|
if (GUILayout.Button(newContent))
|
|
{
|
|
GenericMenu menu = new GenericMenu();
|
|
|
|
menu.AddDisabledItem(new GUIContent("HairTypes"));
|
|
menu.AddSeparator("");
|
|
|
|
|
|
menu.AddItem(new GUIContent("NONE"), (serializedObject.FindProperty("defaultFemaleHair").intValue == -1), SelectHairType, new HairSelection(-1, false));
|
|
|
|
for (int i = 0; i < femaleHairList.count; i++)
|
|
{
|
|
bool isSelected = (serializedObject.FindProperty("defaultFemaleHair").intValue == i);
|
|
if(femaleHairList.serializedProperty.GetArrayElementAtIndex(i) != null)
|
|
menu.AddItem(new GUIContent(femaleHairList.serializedProperty.GetArrayElementAtIndex(i).objectReferenceValue.name), isSelected, SelectHairType, new HairSelection(i, false));
|
|
}
|
|
|
|
menu.ShowAsContext();
|
|
|
|
current.Use();
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
void SelectHairType(object hairSelection)
|
|
{
|
|
HairSelection hSel = (HairSelection)hairSelection;
|
|
serializedObject.FindProperty((hSel.isMale) ? "defaultMaleHair" : "defaultFemaleHair").intValue = hSel.index;
|
|
|
|
// Instantiate Hair in the selected body
|
|
BodyData sBodyData;
|
|
if (hSel.isMale)
|
|
{
|
|
sBodyData = (malePrefab as GameObject).GetComponent<BodyData>();
|
|
|
|
if (sBodyData.hair != null)
|
|
DestroyImmediate(sBodyData.hair.gameObject);
|
|
|
|
if (hSel.index > -1)
|
|
{
|
|
serializedObject.FindProperty("defaultMaleHair").intValue = hSel.index;
|
|
|
|
SerializedObject hairElement = new SerializedObject(maleHairList.serializedProperty.GetArrayElementAtIndex(hSel.index).objectReferenceValue); // this is Hair
|
|
UnityEngine.Object sHair = hairElement.FindProperty("mesh").objectReferenceValue;
|
|
sBodyData.hair = ((GameObject)(Instantiate(sHair, sBodyData.head.transform))).GetComponent<SkinnedMeshRenderer>();
|
|
Debug.Log(sBodyData.hair);
|
|
|
|
// Attach the hair
|
|
sBodyData.hair.transform.parent = sBodyData.head.transform;
|
|
sBodyData.hair.rootBone = sBodyData.head.rootBone;
|
|
sBodyData.hair.bones = sBodyData.head.bones;
|
|
|
|
this.Repaint();
|
|
windowEditor.Repaint();
|
|
windowEditor.ReloadPreviewInstances();
|
|
windowEditor.Repaint();
|
|
this.Repaint();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sBodyData = (femalePrefab as GameObject).GetComponent<BodyData>();
|
|
|
|
if (sBodyData.hair != null)
|
|
DestroyImmediate(sBodyData.hair.gameObject);
|
|
|
|
if (hSel.index > -1)
|
|
{
|
|
serializedObject.FindProperty("defaultFemaleHair").intValue = hSel.index;
|
|
|
|
SerializedObject hairElement = new SerializedObject(femaleHairList.serializedProperty.GetArrayElementAtIndex(hSel.index).objectReferenceValue); // this is Hair
|
|
UnityEngine.Object sHair = hairElement.FindProperty("mesh").objectReferenceValue;
|
|
sBodyData.hair = ((GameObject)(Instantiate(sHair, sBodyData.head.transform))).GetComponent<SkinnedMeshRenderer>();
|
|
Debug.Log(sBodyData.hair);
|
|
|
|
// Attach the hair
|
|
sBodyData.hair.transform.parent = sBodyData.head.transform;
|
|
sBodyData.hair.rootBone = sBodyData.head.rootBone;
|
|
sBodyData.hair.bones = sBodyData.head.bones;
|
|
|
|
this.Repaint();
|
|
windowEditor.Repaint();
|
|
windowEditor.ReloadPreviewInstances();
|
|
windowEditor.Repaint();
|
|
this.Repaint();
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.LabelField("Default Eyes", HeaderStyle);
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
int currentEyeMaleIndex = serializedObject.FindProperty("defaultMaleEye").intValue;
|
|
int currentEyeFemaleIndex = serializedObject.FindProperty("defaultFemaleEye").intValue;
|
|
|
|
EditorGUILayout.LabelField("Male:", GUILayout.MaxWidth(55.0f));
|
|
var maleEyeContent = new GUIContent();
|
|
currentEyeMaleIndex = serializedObject.FindProperty("defaultMaleEye").intValue;
|
|
|
|
try
|
|
{
|
|
maleEyeContent.text = (currentEyeMaleIndex != 0) ? eyeList.serializedProperty.GetArrayElementAtIndex(currentEyeMaleIndex).objectReferenceValue.name : "Default";
|
|
}
|
|
catch
|
|
{
|
|
currentEyeMaleIndex = 0;
|
|
maleEyeContent.text = (currentEyeMaleIndex != 0) ? eyeList.serializedProperty.GetArrayElementAtIndex(currentEyeMaleIndex).objectReferenceValue.name : "Default";
|
|
}
|
|
if (GUILayout.Button(maleEyeContent))
|
|
{
|
|
GenericMenu menu = new GenericMenu();
|
|
|
|
menu.AddDisabledItem(new GUIContent("Eyes Type"));
|
|
menu.AddSeparator("");
|
|
|
|
|
|
menu.AddItem(new GUIContent("Default"), (serializedObject.FindProperty("defaultMaleEye").intValue == 0), SelectEyeType, new EyeSelection(0, true));
|
|
|
|
for (int i = 0; i < eyeList.count; i++)
|
|
{
|
|
if (eyeList.serializedProperty.GetArrayElementAtIndex(i) != null)
|
|
menu.AddItem(new GUIContent(eyeList.serializedProperty.GetArrayElementAtIndex(i).objectReferenceValue.name), (serializedObject.FindProperty("defaultMaleEye").intValue == i), SelectEyeType, new EyeSelection(i, true));
|
|
}
|
|
|
|
menu.ShowAsContext();
|
|
|
|
current.Use();
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("Female:", GUILayout.MaxWidth(55.0f));
|
|
var femaleEyeContent = new GUIContent();
|
|
currentEyeFemaleIndex = serializedObject.FindProperty("defaultFemaleEye").intValue;
|
|
|
|
try
|
|
{
|
|
femaleEyeContent.text = (currentEyeFemaleIndex != 0) ? eyeList.serializedProperty.GetArrayElementAtIndex(currentEyeFemaleIndex).objectReferenceValue.name : "Default";
|
|
}
|
|
catch
|
|
{
|
|
currentEyeFemaleIndex = 0;
|
|
femaleEyeContent.text = (currentEyeFemaleIndex != 0) ? eyeList.serializedProperty.GetArrayElementAtIndex(currentEyeFemaleIndex).objectReferenceValue.name : "Default";
|
|
}
|
|
if (GUILayout.Button(femaleEyeContent))
|
|
{
|
|
GenericMenu menu = new GenericMenu();
|
|
|
|
menu.AddDisabledItem(new GUIContent("Eyes Type"));
|
|
menu.AddSeparator("");
|
|
|
|
|
|
menu.AddItem(new GUIContent("Default"), (serializedObject.FindProperty("defaultFemaleEye").intValue == 0), SelectEyeType, new EyeSelection(0, false));
|
|
|
|
for (int i = 0; i < eyeList.count; i++)
|
|
{
|
|
if (eyeList.serializedProperty.GetArrayElementAtIndex(i) != null)
|
|
menu.AddItem(new GUIContent(eyeList.serializedProperty.GetArrayElementAtIndex(i).objectReferenceValue.name), (serializedObject.FindProperty("defaultFemaleEye").intValue == i), SelectEyeType, new EyeSelection(i, false));
|
|
}
|
|
|
|
menu.ShowAsContext();
|
|
|
|
current.Use();
|
|
}
|
|
|
|
void SelectEyeType(object _eyeSelection)
|
|
{
|
|
EyeSelection hSel = (EyeSelection)_eyeSelection;
|
|
serializedObject.FindProperty((hSel.isMale) ? "defaultMaleEye" : "defaultFemaleEye").intValue = hSel.index;
|
|
|
|
// Instantiate Hair in the selected body
|
|
BodyData sBodyData;
|
|
if (hSel.isMale)
|
|
{
|
|
sBodyData = (malePrefab as GameObject).GetComponent<BodyData>();
|
|
|
|
serializedObject.FindProperty("defaultMaleEye").intValue = hSel.index;
|
|
|
|
SerializedObject eyeElement = new SerializedObject(eyeList.serializedProperty.GetArrayElementAtIndex(hSel.index).objectReferenceValue); // this is Hair
|
|
UnityEngine.Object sEye = eyeElement.FindProperty("eyes").objectReferenceValue;
|
|
|
|
sBodyData.eyeIndex = serializedObject.FindProperty("defaultMaleEye").intValue;
|
|
sBodyData.eyes.sharedMaterial = ((SkinnedMeshRenderer)sEye).sharedMaterial;
|
|
|
|
this.Repaint();
|
|
windowEditor.Repaint();
|
|
windowEditor.ReloadPreviewInstances();
|
|
windowEditor.Repaint();
|
|
this.Repaint();
|
|
}
|
|
else
|
|
{
|
|
sBodyData = (femalePrefab as GameObject).GetComponent<BodyData>();
|
|
|
|
serializedObject.FindProperty("defaultFemaleEye").intValue = hSel.index;
|
|
|
|
SerializedObject eyeElement = new SerializedObject(eyeList.serializedProperty.GetArrayElementAtIndex(hSel.index).objectReferenceValue); // this is Hair
|
|
UnityEngine.Object sEye = eyeElement.FindProperty("eyes").objectReferenceValue;
|
|
|
|
sBodyData.eyeIndex = serializedObject.FindProperty("defaultFemaleEye").intValue;
|
|
sBodyData.eyes.sharedMaterial = ((SkinnedMeshRenderer)sEye).sharedMaterial;
|
|
|
|
this.Repaint();
|
|
windowEditor.Repaint();
|
|
windowEditor.ReloadPreviewInstances();
|
|
windowEditor.Repaint();
|
|
this.Repaint();
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
#endregion
|
|
break;
|
|
|
|
case CurrentWindow.FaceData:
|
|
contentScrollView =
|
|
EditorGUILayout.BeginScrollView(contentScrollView, GUIStyle.none, GUI.skin.verticalScrollbar, GUILayout.Width(650.0f), GUILayout.Height(500.0f));
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
faceBlendshapesContent =
|
|
EditorGUILayout.BeginScrollView(faceBlendshapesContent, GUIStyle.none, GUI.skin.verticalScrollbar, GUILayout.Width(270.0f), GUILayout.Height(500.0f));
|
|
EditorGUILayout.BeginVertical("box");
|
|
EditorGUILayout.LabelField("FACE BLENDSHAPES", HeaderStyle);
|
|
|
|
|
|
// Get Blendshapes of the selected mesh
|
|
string blendshapeName;
|
|
GUIContent guicontent = new GUIContent();
|
|
SkinnedMeshRenderer selectedHead = null;
|
|
|
|
if (malePrefab != null && maleSelected)
|
|
selectedHead = (malePrefab as GameObject).GetComponent<BodyData>().head;
|
|
else if(femalePrefab != null && !maleSelected)
|
|
selectedHead = (femalePrefab as GameObject).GetComponent<BodyData>().head;
|
|
|
|
|
|
if (selectedHead != null)
|
|
{
|
|
// Display all blendshapes
|
|
for (int i = 0; i < selectedHead.sharedMesh.blendShapeCount; i++)
|
|
{
|
|
blendshapeName = selectedHead.sharedMesh.GetBlendShapeName(i).ToString();
|
|
|
|
guicontent.text = blendshapeName;
|
|
guicontent.tooltip = blendshapeName;
|
|
|
|
if (selectedBlendshapeIndex == i)
|
|
GUI.color = Color.gray;
|
|
|
|
if (GUILayout.Button(guicontent))
|
|
{
|
|
selectedBlendshapeIndex = i;
|
|
sliderValue = selectedHead.GetBlendShapeWeight(i);
|
|
}
|
|
|
|
GUI.color = defaultColor;
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
EditorGUILayout.BeginVertical();
|
|
bgColor = new GUIStyle();
|
|
|
|
if (selectedHead != null)
|
|
{
|
|
if (windowEditor == null || gameObjectChanged)
|
|
{
|
|
windowEditor = Editor.CreateEditor(selectedHead.gameObject);
|
|
gameObjectChanged = false;
|
|
}
|
|
}
|
|
|
|
if (windowEditor != null)
|
|
{
|
|
windowEditor.DrawPreview(GUILayoutUtility.GetRect(64, 390));
|
|
windowEditor.OnInteractivePreviewGUI(GUILayoutUtility.GetRect(1, 1), bgColor);
|
|
windowEditor.ReloadPreviewInstances();
|
|
windowEditor.Repaint();
|
|
windowEditor.ResetTarget();
|
|
this.Repaint();
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.Space(350);
|
|
|
|
if (maleSelected)
|
|
EditorGUILayout.HelpBox("There is no Male Model associated with this race, reference a prefab in the \"Body Data\" tab.", MessageType.Warning);
|
|
else
|
|
EditorGUILayout.HelpBox("There is no Female Model associated with this race, reference a prefab in the \"Body Data\" tab.", MessageType.Warning);
|
|
}
|
|
|
|
// Male/Female switch
|
|
EditorGUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
|
|
GUI.color = (maleSelected) ? Color.grey : defaultColor;
|
|
if (GUILayout.Button("Male", GUILayout.MaxWidth(100)))
|
|
{
|
|
maleSelected = true;
|
|
gameObjectChanged = true;
|
|
DestroyImmediate(windowEditor);
|
|
selectedBlendshapeIndex = 0;
|
|
}
|
|
|
|
GUI.color = (!maleSelected) ? Color.grey : defaultColor;
|
|
if (GUILayout.Button("Female", GUILayout.MaxWidth(100)))
|
|
{
|
|
maleSelected = false;
|
|
gameObjectChanged = true;
|
|
DestroyImmediate(windowEditor);
|
|
selectedBlendshapeIndex = 0;
|
|
}
|
|
|
|
GUI.color = defaultColor;
|
|
GUILayout.FlexibleSpace();
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
if (selectedHead != null)
|
|
{
|
|
HeadBlendshapesManager bsManager = selectedHead.GetComponent<HeadBlendshapesManager>();
|
|
if (bsManager != null)
|
|
bsManager.AdjustChildBlendshapes();
|
|
|
|
if (selectedHead.sharedMesh.blendShapeCount > 0)
|
|
{
|
|
sliderValue = EditorGUILayout.Slider(selectedHead.GetBlendShapeWeight(selectedBlendshapeIndex), -100, 100);
|
|
selectedHead.SetBlendShapeWeight(selectedBlendshapeIndex, sliderValue);
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
if (GUILayout.Button("Random Face"))
|
|
{
|
|
for (int i = 0; i < selectedHead.sharedMesh.blendShapeCount; i++)
|
|
selectedHead.SetBlendShapeWeight(i, UnityEngine.Random.Range(-100, 100));
|
|
}
|
|
|
|
if (GUILayout.Button("Reset Face"))
|
|
{
|
|
for (int i = 0; i < selectedHead.sharedMesh.blendShapeCount; i++)
|
|
selectedHead.SetBlendShapeWeight(i, 0);
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.HelpBox("The selected mesh has no blendshapes, therefore you cannot customize this character's face.", MessageType.Warning);
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
GUILayout.FlexibleSpace();
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
if (GUILayout.Button("SAVE", ButtonStyle))
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
// Apply prefab
|
|
|
|
if (malePrefab != null)
|
|
{
|
|
PrefabUtility.SaveAsPrefabAsset((GameObject)malePrefab, assetPathMale);
|
|
//PrefabUtility.UnloadPrefabContents((GameObject)malePrefab);
|
|
}
|
|
|
|
if (femalePrefab != null)
|
|
{
|
|
PrefabUtility.SaveAsPrefabAsset((GameObject)femalePrefab, assetPathFemale);
|
|
//PrefabUtility.UnloadPrefabContents((GameObject)femalePrefab);
|
|
}
|
|
}
|
|
|
|
if (GUILayout.Button("SAVE & CLOSE", ButtonStyle))
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
if (malePrefab != null)
|
|
{
|
|
PrefabUtility.SaveAsPrefabAsset((GameObject)malePrefab, assetPathMale);
|
|
PrefabUtility.UnloadPrefabContents((GameObject)malePrefab);
|
|
}
|
|
|
|
if (femalePrefab != null)
|
|
{
|
|
PrefabUtility.SaveAsPrefabAsset((GameObject)femalePrefab, assetPathFemale);
|
|
PrefabUtility.UnloadPrefabContents((GameObject)femalePrefab);
|
|
}
|
|
|
|
DestroyImmediate(windowEditor);
|
|
this.Close();
|
|
}
|
|
|
|
|
|
if (GUILayout.Button("CANCEL", ButtonStyle))
|
|
{
|
|
DestroyImmediate(windowEditor);
|
|
this.Close();
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
DestroyImmediate(malePrefab);
|
|
DestroyImmediate(femalePrefab);
|
|
}
|
|
|
|
public static List<T> GetAllRaces<T>() where T : Race
|
|
{
|
|
string[] guids = AssetDatabase.FindAssets("t:" + typeof(T).Name);
|
|
List<T> a = new List<T>(guids.Length);
|
|
|
|
for (int i = 0; i < guids.Length; i++)
|
|
{
|
|
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
|
a.Add(AssetDatabase.LoadAssetAtPath<T>(path));
|
|
}
|
|
|
|
return a;
|
|
}
|
|
}
|
|
} |