using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; namespace TBTK { public class UnitEditorWindow : TBEditorWindow { [MenuItem ("Tools/TBTK/UnitEditor", false, 10)] static void OpenUnitEditor () { Init(); } private static UnitEditorWindow window; public static void Init (int prefabID=-1) { window = (UnitEditorWindow)EditorWindow.GetWindow(typeof (UnitEditorWindow), false, "UnitEditor"); window.minSize=new Vector2(570, 300); TBE.Init(); window.InitLabel(); //if(prefabID>=0) window.selectID=UnitDB.GetPrefabIndex(prefabID); //window.SelectItem(window.selectID); if(prefabID>=0){ window.selectID=UnitDB.GetPrefabIndex(prefabID); window.newSelectID=window.selectID; window.newSelectDelay=1; } useDropDownMenuForHierarchy=PlayerPrefs.GetInt("UseDropDownForHierarchyObj", 0)==0 ? true : false; window._SelectItem(); } private static bool useDropDownMenuForHierarchy=true; private static string[] aiTypeLabel; private static string[] aiTypeTooltip; private static string[] movePassTypeLabel; private static string[] movePassUnitTypeTooltip; public void InitLabel(){ int enumLength = Enum.GetValues(typeof(AI._AIBehaviour)).Length; aiTypeLabel=new string[enumLength]; aiTypeTooltip=new string[enumLength]; for(int i=0; i unitList=UnitDB.GetList(); Undo.RecordObject(this, "window"); Undo.RecordObject(UnitDB.GetDB(), "abilityDB"); if(GUI.Button(new Rect(Math.Max(260, window.position.width-120), 5, 100, 25), "Save")){ GUI.FocusControl(null); TBE.SetDirty(); } TBE.Label(300, 7, 250, 17, "Use DropDown Menu:", "Enable to use drop down menu for assigning transform within prefab hierarchy. This is for this editor window only"); useDropDownMenuForHierarchy=EditorGUI.Toggle(new Rect(430, 7, 25, 25), useDropDownMenuForHierarchy); //if(GUI.Button(new Rect(Math.Max(260, window.position.width-300), 5, 100, 25), "Set")){ // for(int i=0; i>")) minimiseList=false; } else{ if(GUI.Button(new Rect(startX, startY-20, 30, 18), "<<")) minimiseList=true; } Vector2 v2=DrawUnitList(startX, startY, unitList); startX=v2.x+25; if(unitList.Count==0) return; if(selectID>=unitList.Count) return; if(newSelectDelay>0){ newSelectDelay-=1; GUI.FocusControl(null); if(newSelectDelay==0) _SelectItem(); else Repaint(); } Rect visibleRect=new Rect(startX, startY, window.position.width-startX, window.position.height-startY); Rect contentRect=new Rect(startX, startY, contentWidth, contentHeight); scrollPos = GUI.BeginScrollView(visibleRect, scrollPos, contentRect); spaceX+=10; width-=10; EditorGUI.BeginChangeCheck(); v2=DrawUnitConfigurator(startX, startY, unitList[selectID]); contentWidth=v2.x-startX; contentHeight=v2.y-55; if(EditorGUI.EndChangeCheck()){ #if UNITY_2018_3_OR_NEWER //GameObject unitObj=PrefabUtility.LoadPrefabContents(AssetDatabase.GetAssetPath(unitList[selectID].gameObject)); //Unit selectedUnit=unitObj.GetComponent(); //selectedUnit=unitList[selectID]; //GameObject obj=PrefabUtility.SavePrefabAsset(selectedUnit.gameObject); string assetPath = AssetDatabase.GetAssetPath(unitList[selectID].gameObject); GameObject unitObj=PrefabUtility.LoadPrefabContents(assetPath); Unit selectedUnit=unitObj.GetComponent(); EditorUtility.CopySerialized(unitList[selectID], selectedUnit); PrefabUtility.SaveAsPrefabAsset(unitObj, assetPath); PrefabUtility.UnloadPrefabContents(unitObj); #endif } spaceX-=10; width+=10; GUI.EndScrollView(); PlayerPrefs.SetInt("UseDropDownForHierarchyObj", (useDropDownMenuForHierarchy ? 0 : 1)); if(GUI.changed) TBE.SetDirty(); } private bool foldStats=true; private bool foldSetting=true; private bool foldShootPoint=true; private bool foldAbNEff=true; private bool foldVisualEffect=true; private bool foldAnimNAudio=true; private bool foldAttack=true; private bool foldAttackMelee=true; private bool foldCounter=true; private bool foldOverwatch=true; private int objIdx; private Vector2 DrawUnitConfigurator(float startX, float startY, Unit unit){ float maxX=startX; startY=TBE.DrawBasicInfo(startX, startY, unit); //startY+=spaceY; TBE.Label(startX, startY, width, height, "Value:", "Just an arbitary number use for squad selection screen in demo"); unit.value=EditorGUI.IntField(new Rect(startX+spaceX-10, startY, widthS, height), unit.value); startY+=10+spaceY; foldSetting=EditorGUI.Foldout(new Rect(startX, startY, spaceX, height), foldSetting, "Settings", TBE.foldoutS); if(foldSetting){ startX+=10; int aiType=(int)unit.aiBehaviour; contL=TBE.SetupContL(aiTypeLabel, aiTypeTooltip); TBE.Label(startX, startY+=spaceY, width, height, "AI Behaviour:", "The behaviour to use when deployed as AI unit"); aiType = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), new GUIContent(""), aiType, contL); unit.aiBehaviour=(AI._AIBehaviour)aiType; TBE.Label(startX, startY+=spaceY, width, height, "Require Trigger:", "If checked, the unit will start in 'Passive' AI behaviour until it spotted a hostile unit"); if(unit.aiBehaviour==AI._AIBehaviour.passive) TBE.Label(startX+spaceX, startY, widthS, height, "n/a"); else unit.requireTrigger=EditorGUI.Toggle(new Rect(startX+spaceX, startY, widthS, height), unit.requireTrigger); startY+=10; if(!useDropDownMenuForHierarchy){ TBE.Label(startX, startY+=spaceY, width, height, "Target Point:", "The 'center' point of the unit. Any aiming/attack towards this unit will be aim at this point"); unit.targetPoint=(Transform)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), unit.targetPoint, typeof(Transform), true); } else{ objIdx=GetIndexFromHierarchy(unit.targetPoint, objHierarchyList); TBE.Label(startX, startY+=spaceY, width, height, "Target Point:", "The 'center' point of the unit. Any aiming/attack towards this unit will be aim at this point"); objIdx = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), objIdx, objHierarchylabel); unit.targetPoint = objHierarchyList[objIdx]; } TBE.Label(startX, startY+=spaceY, width, height, "Hit Radius:", "The 'size' of the unit, any shoot-object aimed towards this unit will be considered hit once it reach this radius value"); unit.radius=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), unit.radius); //unit.radius=EditorGUI.FloatField(new Rect(startX+spaceX, startY, widthS, height), unit.radius); startY+=5; TBE.Label(startX, startY+=spaceY, width, height, "Move Speed:", "Determine how fast a unit move on the grid. Doesn't really affect gameplay"); unit.moveSpeed=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), unit.moveSpeed); startY+=10; GUIStyle styleSO=unit.soRange==null ? TBE.conflictS : null; TBE.Label(startX, startY+=spaceY, width, height, "Shoot Object Range:", "The shoot-object to fire at each attack. Must be a prefab contain a 'ShootObject' component", styleSO); unit.soRange=(ShootObject)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), unit.soRange, typeof(ShootObject), true); TBE.Label(startX, startY+=spaceY, width, height, "Shoot Object Melee:", "Optional shoot-object to fire at each melee attack. If unassigned, ShootObjectRange will be used instead. Must be a prefab contain a 'ShootObject' component"); if(!unit.hasMeleeAttack) TBE.Label(startX+spaceX, startY, width, height, "-"); else unit.soMelee=(ShootObject)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), unit.soMelee, typeof(ShootObject), true); startY+=10; cont=new GUIContent("ShootPoint:", "OPTIONAL - The transform which indicate the position where the shootObject will be fired from\nEach shootPoint assigned will fire a shootObject instance in each attack\nIf left empty, the unit transform itself will be use as the shootPoint\nThe orientation of the shootPoint matter as they dictate the orientation of the shootObject starting orientation.\n"); foldShootPoint=EditorGUI.Foldout(new Rect(startX, startY+=spaceY, spaceX, height), foldShootPoint, cont); int shootPointCount=unit.shootPointList.Count; shootPointCount=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), shootPointCount); if(shootPointCount!=unit.shootPointList.Count){ while(unit.shootPointList.CountshootPointCount) unit.shootPointList.RemoveAt(unit.shootPointList.Count-1); } if(foldShootPoint){ for(int i=0; i=1 ? Color.white : Color.grey; item.attackRangeMin=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.attackRangeMin); GUI.color=Color.white; item.attackRange=EditorGUI.DelayedFloatField(new Rect(startX+spaceX+widthS, startY, widthS, height), item.attackRange); if(unit.hasMeleeAttack){ TBE.Label(startX, startY+=spaceY, width, height, "AttackRange Melee:", ""); unit.statsMelee.attackRange=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), unit.statsMelee.attackRange); unit.statsMelee.attackRange=Mathf.Min(unit.statsMelee.attackRange, item.attackRange); } TBE.Label(startX, startY+=spaceY, width, height, "Sight:", "The visibility range of the unit when fog-of-war is enabled\n\nAlso used as the hostile detection range for AI unit in 'Passive' mode"); item.sight=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.sight); startY+=spaceY*0.5f; TBE.Label(startX, startY+=spaceY, width, height, "Move Limit:", "The maximum number of moving the unit can do in a single turn"); item.moveLimit=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.moveLimit); TBE.Label(startX, startY+=spaceY, width, height, "Attack Limit:", "The maximum number of attacking the unit can do in a single turn"); item.attackLimit=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.attackLimit); TBE.Label(startX, startY+=spaceY, width, height, "Counter Limit:", "The maximum number of counter-attacking the unit can do in a single turn"); item.counterLimit=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.counterLimit); TBE.Label(startX, startY+=spaceY, width, height, "Ability Limit:", "The maximum number of ability the unit can use in a single turn"); item.abilityLimit=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.abilityLimit); startX-=10; } startY+=spaceY*0.5f; foldAbNEff=EditorGUI.Foldout(new Rect(startX, startY+=spaceY, spaceX, height), foldAbNEff, "Abilities & Effects", TBE.foldoutS); if(foldAbNEff){ startX+=10; TBE.Label(startX, startY+=spaceY, width, height, "Effect On Attack:", "The effect to be applied to the target when the unit attacks"); for(int i=0; i0 ? spaceY : 0), width, height, "-"); int effIdx=EffectDB.GetPrefabIndex(unit.attackEffectIDList[i]); effIdx = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), effIdx, EffectDB.label); int prefabID=EffectDB.GetItemID(effIdx); if(!unit.attackEffectIDList.Contains(prefabID)) unit.attackEffectIDList[i]=prefabID; if(GUI.Button(new Rect(startX+spaceX+width+3, startY, height, height), "-")){ unit.attackEffectIDList.RemoveAt(i); } } if(unit.attackEffectIDList.Count0 ? spaceY : 0 ; newIdx = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), newIdx, EffectDB.label); if(newIdx>=0){ int newPID=EffectDB.GetItemID(newIdx); if(!unit.attackEffectIDList.Contains(newPID)) unit.attackEffectIDList.Add(newPID); } ResetColor(); } startY+=spaceY; TBE.Label(startX, startY+=spaceY, width, height, "Abilities:", "The ability possesed by the unit"); for(int i=0; i0 ? spaceY : 0), width, height, "-"); int abIdx=AbilityUDB.GetPrefabIndex(unit.abilityIDList[i]); abIdx = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), abIdx, AbilityUDB.label); int prefabID=AbilityUDB.GetItemID(abIdx); if(!unit.abilityIDList.Contains(prefabID)) unit.abilityIDList[i]=prefabID; if(GUI.Button(new Rect(startX+spaceX+width+3, startY, height, height), "-")) unit.abilityIDList.RemoveAt(i); } if(unit.abilityIDList.Count0 ? spaceY : 0 ; newIdx = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), newIdx, AbilityUDB.label); if(newIdx>=0){ int newPID=AbilityUDB.GetItemID(newIdx); if(!unit.abilityIDList.Contains(newPID)) unit.abilityIDList.Add(newPID); } ResetColor(); } startY+=spaceY; TBE.Label(startX, startY+=spaceY, width, height, "Immuned Effects:", "The effects the unit is immuned to"); for(int i=0; i0 ? spaceY : 0), width, height, "-"); int effIdx=EffectDB.GetPrefabIndex(unit.immuneEffectList[i]); effIdx = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), effIdx, EffectDB.label); int prefabID=EffectDB.GetItemID(effIdx); if(!unit.attackEffectIDList.Contains(prefabID)) unit.attackEffectIDList[i]=prefabID; if(GUI.Button(new Rect(startX+spaceX+width+3, startY, height, height), "-")){ unit.immuneEffectList.RemoveAt(i); } } if(unit.immuneEffectList.Count0 ? spaceY : 0 ; newIdx = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), newIdx, EffectDB.label); if(newIdx>=0){ int newPID=EffectDB.GetItemID(newIdx); if(!unit.immuneEffectList.Contains(newPID)) unit.immuneEffectList.Add(newPID); } ResetColor(); } startX-=10; } startY+=spaceY*0.5f; foldVisualEffect=EditorGUI.Foldout(new Rect(startX, startY+=spaceY, spaceX, height), foldVisualEffect, "Visual Effect ", TBE.foldoutS); if(foldVisualEffect){ startX+=10; string txt="OPTIONAL: The effect object to spawn on the target when an attack successful hit the target (it won't spawn if the attack missed)"; startY=DrawVisualObject(startX, startY+=spaceY, unit.effectAttackHit, "Effect On Attack Hit:", txt); txt="OPTIONAL: The effect object to spawn on the target when a melee attack successful hit the target (it won't spawn if the attack missed)"; startY=DrawVisualObject(startX, startY+=spaceY+8, unit.effectAttackHitMelee, "Effect On Melee Hit:", txt); txt="OPTIONAL: The effect object to spawn when the item is spawned on the grid"; startY=DrawVisualObject(startX, startY+=spaceY+8, unit.effectOnDestroyed, "Effect On Destroyed:", txt); startX-=10; } startY+=spaceY*0.5f; foldAnimNAudio=EditorGUI.Foldout(new Rect(startX, startY+=spaceY, spaceX, height), foldAnimNAudio, "Animation and Audio ", TBE.foldoutS); if(foldAnimNAudio){ startX+=10; TBE.Label(startX, startY+=spaceY, width, height, "Animation:", "", TBE.headerS); TBE.Label(startX, startY+=spaceY, width, height, " - Animator Obj:", "The transform object which contain the Animator component"); objIdx = GetIndexFromHierarchy(unit.animatorT, objHierarchyList); objIdx = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), objIdx, objHierarchylabel); unit.animatorT = objHierarchyList[objIdx]; TBE.Label(startX, startY+=spaceY, width, height, " - Idle:", ""); unit.clipIdle=(AnimationClip)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), unit.clipIdle, typeof(AnimationClip), true); TBE.Label(startX, startY+=spaceY, width, height, " - Move:", ""); unit.clipMove=(AnimationClip)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), unit.clipMove, typeof(AnimationClip), true); TBE.Label(startX, startY+=spaceY, width, height, " - Hit:", ""); unit.clipHit=(AnimationClip)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), unit.clipHit, typeof(AnimationClip), true); TBE.Label(startX, startY+=spaceY, width, height, " - Destroyed:", ""); unit.clipDestroyed=(AnimationClip)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), unit.clipDestroyed, typeof(AnimationClip), true); widthS-=5; startY+=spaceY*0.5f; TBE.Label(startX, startY+=spaceY, width, height, " - Attack Range:", ""); unit.clipAttackRange=(AnimationClip)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width-widthS, height), unit.clipAttackRange, typeof(AnimationClip), true); if(unit.clipAttackRange==null) GUI.color=Color.grey; unit.animAttackDelayRange=EditorGUI.DelayedFloatField(new Rect(startX+spaceX+(width-widthS), startY, widthS, height), unit.animAttackDelayRange); GUI.color=Color.white; //~ TBE.Label(startX, startY+=spaceY, width, height, " - Delay:", "The delay in second after the animation is played before the shoot-object are fired\nThis is for synchronizing the attack sequence to the animation"); //~ if(unit.clipAttackRange==null) TBE.Label(startX+spaceX, startY, width, height, "-", ""); //~ else unit.animAttackDelayRange=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), unit.animAttackDelayRange); TBE.Label(startX, startY+=spaceY, width, height, " - Attack Melee:", ""); unit.clipAttackMelee=(AnimationClip)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width-widthS, height), unit.clipAttackMelee, typeof(AnimationClip), true); if(unit.clipAttackMelee==null) GUI.color=Color.grey; unit.animAttackDelayMelee=EditorGUI.DelayedFloatField(new Rect(startX+spaceX+(width-widthS), startY, widthS, height), unit.animAttackDelayMelee); GUI.color=Color.white; //~ TBE.Label(startX, startY+=spaceY, width, height, " - Delay:", "The delay in second after the animation is played before the target is hit\nThis is for synchronizing the attack sequence to the animation"); //~ if(unit.clipAttackMelee==null) TBE.Label(startX+spaceX, startY, width, height, "-", ""); //~ else unit.animAttackDelayMelee=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), unit.animAttackDelayMelee); startY+=spaceY*0.5f; int count=6; while(unit.clipAbilityList.Countcount) unit.clipAbilityList.RemoveAt(unit.clipAbilityList.Count-1); while(unit.animAbilityDelayList.Countcount) unit.animAbilityDelayList.RemoveAt(unit.animAbilityDelayList.Count-1); for(int i=0; i unitList){ List list=new List(); for(int i=0; i(); #endif UnitDB.GetList().Add(item); UnitDB.UpdateLabel(); return UnitDB.GetList().Count-1; } protected override void DeleteItem(){ UnitDB.ResetItemPID(deleteID); UnitDB.GetList().RemoveAt(deleteID); UnitDB.UpdateLabel(); } protected override void SelectItem(){ } private void _SelectItem(){ selectID=newSelectID; if(UnitDB.GetList().Count<=0) return; selectID=Mathf.Clamp(selectID, 0, UnitDB.GetList().Count-1); UpdateObjHierarchyList(UnitDB.GetList()[selectID].transform); Repaint(); } protected override void ShiftItemUp(){ if(selectID>0) ShiftItem(-1); } protected override void ShiftItemDown(){ if(selectID