/// <summary> /// Checks to see if neighbors around a grid position match the rules neighbor conditional check. /// </summary> /// <param name="gridPosition">The column and row to check.</param> /// <param name="layer">The layer to check.</param> /// <param name="ignoreCenter">If true the center will be ignored.</param> /// <param name="rule">The rule that will be used to compare neighbors.</param> /// <param name="getPrefabCallback">A callback that returns a prefab at a given grid location.</param> /// <returns>True if the rule neighbors match the prefabs on the grid.</returns> public static bool CheckNeighborsPassed(Point gridPosition, int layer, bool ignoreCenter, DrawRuleModel rule, Func<int, int, int, GameObject> getPrefabCallback) { if (getPrefabCallback == null) { throw new ArgumentNullException("getPrefabCallback"); } // check if neighbors match the rule var indexes = new[] { new Point(-1, 1), new Point(0, 1), new Point(1, 1), new Point(-1, 0), new Point(0, 0), new Point(1, 0), new Point(-1, -1), new Point(0, -1), new Point(1, -1) }; var passed = true; // check for each layer for (var i = 0; i < indexes.Length; i++) { // ignore index 4 if (i == 4 && ignoreCenter) { continue; } // get position var position = gridPosition + indexes[i]; // check if neighbor specified and if there is a prefab at the neighbor index passed = rule.Neighbors[i] == (getPrefabCallback(position.X, position.Y, layer) != null); // if did not pass the test exit the loop if (!passed) { break; } } return passed; }
/// <summary> /// Creates a clone of the object. /// </summary> /// <returns>Returns a clone of the object.</returns> public DrawRuleModel Clone() { var rule = new DrawRuleModel(); rule.AllowOriginal = this.AllowOriginal; rule.Description = this.Description; rule.Enabled = this.Enabled; rule.Name = this.Name; rule.NeighborsLowerEnabled = this.NeighborsLowerEnabled; rule.NeighborsUpperEnabled = this.NeighborsUpperEnabled; rule.Prefab = this.Prefab; rule.neighbors = this.neighbors; rule.neighborsLower = this.neighborsLower; rule.neighborsUpper = this.neighborsUpper; foreach (var alternate in this.Alternates) { rule.Alternates.Add(alternate.Clone()); } return(rule); }
/// <summary> /// Responsible for drawing rule name, description, asset path, enabled state etc. /// </summary> /// <param name="rule">The rule to draw the information for.</param> private void DrawRuleData(DrawRuleModel rule) { if (rule == null) { return; } GUILayout.BeginVertical(); // top wrapper // draws a header for the neighbor grids GUILayout.BeginHorizontal(GUI.skin.box); GUILayout.FlexibleSpace(); GUILayout.Label("Rule information"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginVertical(); GUILayout.BeginHorizontal(); // top check boxes GUILayout.FlexibleSpace(); // provide a enabled check box and determine if its state changed and if so set IsDirty GUI.SetNextControlName("chkEnabled"); var enabledState = GUILayout.Toggle(rule.Enabled, "Enabled", GUILayout.ExpandWidth(false)); if (enabledState != rule.Enabled) { rule.Enabled = enabledState; this.IsDirty = true; } // put some spacing between controls GUILayout.Space(16); // provide check box indicating whether or not the prefab being drawn is also allowed to be considered for drawing GUI.SetNextControlName("chkAllowOriginal"); enabledState = GUILayout.Toggle(rule.AllowOriginal, "Allow Original", GUILayout.ExpandWidth(false)); // if its state changed set IsDirty if (enabledState != rule.AllowOriginal) { rule.AllowOriginal = enabledState; this.IsDirty = true; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); // top check boxes // show path to asset var sourcePrefab = Helpers.GetSourcePrefab(rule.Prefab); sourcePrefab = string.IsNullOrEmpty(sourcePrefab) ? string.Empty : sourcePrefab; GUILayout.BeginHorizontal(); GUILayout.Label("Asset Path:", GUILayout.ExpandWidth(false)); GUI.SetNextControlName("txtAssetPath"); GUILayout.TextArea(sourcePrefab); EditorGUIUtility.LookLikeControls(); GUILayout.EndHorizontal(); // show category controls GUILayout.BeginHorizontal(); // category // provide button for toggling category display style GUI.SetNextControlName("chkCategory"); var categoryPopup = GUILayout.Toggle(this.showCategoryPopup, "Category", GUI.skin.button, GUILayout.ExpandWidth(false)); if (categoryPopup != this.showCategoryPopup) { this.showCategoryPopup = categoryPopup; // try to find index var index = Array.IndexOf(this.ruleCategories, rule.Category, 0); if (index != -1) { this.selectedCategoryIndex = index; } } // get list of categories if (this.showCategoryPopup) { GUI.SetNextControlName("CategoryPopup"); var index = EditorGUILayout.Popup(this.selectedCategoryIndex, this.ruleCategories); // detect category selection change and if so update rule category if (index != this.selectedCategoryIndex) { this.selectedCategoryIndex = index; rule.Category = this.ruleCategories[index]; this.IsDirty = true; } } else { GUI.SetNextControlName("txtCategory"); var value = GUILayout.TextField(rule.Category ?? string.Empty); if (value != rule.Category) { rule.Category = value; this.IsDirty = true; } } GUILayout.EndHorizontal(); // category // show rule name controls GUILayout.BeginHorizontal(); GUILayout.Label("Rule Name:", GUILayout.ExpandWidth(false)); GUI.SetNextControlName("txtRuleName"); var ruleName = GUILayout.TextField(rule.Name ?? string.Empty); GUILayout.EndHorizontal(); // if its state changed set IsDirty if (ruleName != rule.Name) { rule.Name = ruleName; this.IsDirty = true; } // provide controls to set the rule description and allow for scrolling GUILayout.Label("Description:"); this.descriptionScroll = GUILayout.BeginScrollView(this.descriptionScroll, false, false); GUI.SetNextControlName("txtDescription"); var description = GUILayout.TextArea(rule.Description ?? string.Empty); GUILayout.EndScrollView(); // if its state changed set IsDirty if (description != rule.Description) { rule.Description = description; this.IsDirty = true; } GUILayout.EndVertical(); // top wrapper }
/// <summary> /// Draws the neighbor grids for the upper, lower and same layer neighbors. /// </summary> /// <param name="rule">The source rule to get the neighbor info from.</param> private void DrawNeighborGrids(DrawRuleModel rule) { GUILayout.BeginVertical(); // draws a header for the neighbor grids GUILayout.BeginHorizontal(GUI.skin.box); GUILayout.FlexibleSpace(); GUILayout.Label("Surrounding neighbors"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(GUILayout.MinHeight(125)); GUILayout.FlexibleSpace(); // setup default content for the upper and lower neighbor grids var emptyContent = new[] { GUIContent.none, GUIContent.none, GUIContent.none, GUIContent.none, GUIContent.none, GUIContent.none, GUIContent.none, GUIContent.none, GUIContent.none }; GUILayout.BeginVertical(GUILayout.MinWidth(128)); // provides a check box to indicate whether this rule takes into account the prefabs on the layer above GUI.SetNextControlName("chkUpperNeighbors"); var enabled = GUILayout.Toggle(rule.NeighborsUpperEnabled, "Upper Neighbors"); // determine if the state changed and if so set IsDirty if (enabled != rule.NeighborsUpperEnabled) { rule.NeighborsUpperEnabled = enabled; this.IsDirty = true; } // draw the upper neighbor grid rule.NeighborsUpper = this.DrawGrid(rule.NeighborsUpper, emptyContent); GUILayout.EndVertical(); // put some spacing between grids GUILayout.Space(8); GUILayout.BeginVertical(GUILayout.MinWidth(128)); GUILayout.Label("Neighbors", GUILayout.MinHeight(GUI.skin.toggle.CalcHeight(GUIContent.none, 100))); // setup neighbor grid content with center grid indicating the prefab that is drawn var image = AssetPreview.GetMiniTypeThumbnail(typeof(GameObject)); var content = new[] { GUIContent.none, GUIContent.none, GUIContent.none, GUIContent.none, new GUIContent(image), GUIContent.none, GUIContent.none, GUIContent.none, GUIContent.none }; // draw neighbor grid rule.Neighbors = this.DrawGrid(rule.Neighbors, content); rule.Neighbors[4] = false; GUILayout.EndVertical(); // put some spacing between grids GUILayout.Space(8); // provides a check box to indicate whether this rule takes into account the prefabs on the layer below GUILayout.BeginVertical(GUILayout.MinWidth(128)); GUI.SetNextControlName("chkLowerNeighbors"); enabled = GUILayout.Toggle(rule.NeighborsLowerEnabled, "Lower Neighbors"); // determine if the state changed and if so set IsDirty if (enabled != rule.NeighborsLowerEnabled) { rule.NeighborsLowerEnabled = enabled; this.IsDirty = true; } // draw lower neighbor grid rule.NeighborsLower = this.DrawGrid(rule.NeighborsLower, emptyContent); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); }
/// <summary> /// Draws the alternate prefab list for a rule. /// </summary> /// <param name="rule">The source rule whose alternate prefabs will be displayed in the list.</param> private void DrawAlternatePrefabsList(DrawRuleModel rule) { GUILayout.BeginVertical(); // top encapsulation // draw the header GUILayout.BeginHorizontal(GUI.skin.box); GUILayout.FlexibleSpace(); GUILayout.Label("Alternate prefabs to draw"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); // draw alternate list controls this.DrawAlternateListHeaderControls(rule); // provide some spacing between the filter controls and the alternate prefab list GUILayout.Space(4); // draw alternate prefab list this.alternatePrefabListScroll = GUILayout.BeginScrollView(this.alternatePrefabListScroll, false, false, GUILayout.ExpandWidth(true)); // scroll view // calculate the number of columns to show var columns = (int)(this.lastRect.width / 154) - 1; columns = columns < 2 ? 2 : columns; GUILayout.BeginVertical(); // items in the list wrapper GUILayout.BeginHorizontal(); // rows var index = 0; var currentColumn = 0; while (index < rule.Alternates.Count) { // get reference to alternate prefab var alternate = rule.Alternates[index]; // ensure alternate is not null and if it is remove it and continue if (alternate == null) { rule.Alternates.RemoveAt(index); continue; } // ensure that the reference is not null and that the name of the alternate prefab contains // portions of the filter text if specified if (string.IsNullOrEmpty(this.alternateListFilter) || (!string.IsNullOrEmpty(alternate.Name) && alternate.Name.ToLower().Contains(this.alternateListFilter))) { // draw the alternate item this.DrawAlternateItem(alternate); // determine if a different alternate prefab was selected if (alternate.IsChecked && this.selectedAlternateIndex != index) { this.selectedAlternateIndex = index; } } // check to see if we need to move to next row currentColumn++; if (currentColumn > columns) { currentColumn = 0; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); // end row GUILayout.BeginHorizontal(); // start a new row } // move to next item index++; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); // rows GUILayout.EndVertical(); // items in the list wrapper GUILayout.EndScrollView(); // scroll view // calculate the layout width that we have to work with if (Event.current.type == EventType.Repaint) { this.lastRect = GUILayoutUtility.GetLastRect(); } GUILayout.EndVertical(); // top encapsulation }
/// <summary> /// Draw header controls for the alternate list. /// </summary> /// <param name="rule">A reference to the currently selected rule.</param> private void DrawAlternateListHeaderControls(DrawRuleModel rule) { // Draw controls for the alternate prefab list GUILayout.BeginHorizontal(); // control header bar var selectedPrefab = EditorGUILayout.ObjectField(this.alternatePrefab, typeof(GameObject), true, GUILayout.ExpandWidth(false)) as GameObject; if (selectedPrefab != this.alternatePrefab) { this.alternatePrefab = selectedPrefab; if (this.autoAddAlternate) { this.AddAlternatePrefab(rule); } } // provides a add button to add the currently selected alternate prefab to the list if (GUILayout.Button("Add")) { this.AddAlternatePrefab(rule); } // provide a check box indicating whether or not alternate prefabs are added to the list simply by // selecting them in the asset selection window this.autoAddAlternate = GUILayout.Toggle(this.autoAddAlternate, "Automatically add"); GUILayout.FlexibleSpace(); // provides a button for generating a preview of the selected alternate prefab in the off // chance unity is unable to generate a preview for us if (GUILayout.Button("Generate Preview")) { // not yet implemented! EditorUtility.DisplayDialog("Information", "Not implemented yet!", "Close"); } // provides a remove button for removing alternate prefabs from the list if (GUILayout.Button("Remove")) { this.RemoveAlternatePrefab(rule); } GUILayout.EndHorizontal(); // control header bar // provide some spacing between the upper and lower controls GUILayout.Space(4); // draw a filter controls for filtering the list of displayed alternate prefabs GUILayout.BeginHorizontal(); // filtering controls GUILayout.FlexibleSpace(); // draw filter label and text field GUILayout.Label("Filter", GUILayout.ExpandWidth(false)); this.alternateListFilter = GUILayout.TextField(this.alternateListFilter ?? string.Empty, GUILayout.MinWidth(128)); // provides a clear button to clear the filter if (GUILayout.Button("X", GUILayout.ExpandWidth(false))) { this.alternateListFilter = string.Empty; } // put some spacing between controls GUILayout.Space(8); // provide a none button to check all alternates if (GUILayout.Button("All")) { foreach (var alt in rule.Alternates) { alt.IsChecked = true; } } // put some spacing between controls GUILayout.Space(2); // provide a none button to uncheck all alternates if (GUILayout.Button("None")) { foreach (var alt in rule.Alternates) { alt.IsChecked = false; } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); // filtering controls }
/// <summary> /// Adds a alternate prefab for the selected rule. /// </summary> /// <param name="rule">A reference to the rule where the alternate prefab will be added to.</param> private void AddAlternatePrefab(DrawRuleModel rule) { // do not allow null references to be added if (this.alternatePrefab == null) { return; } // if alternate prefab selection is not null and not already added then add it if (this.alternatePrefab != null) { rule.Alternates.Add(new PrefabListItem { Prefab = this.alternatePrefab }); } // ensure that there is a alternate prefab selected this.selectedAlternateIndex = rule.Alternates.Count == 1 ? 0 : this.selectedAlternateIndex; // ensure the alternate prefab list is scrolled to view the added rule at the bottom of the list this.alternatePrefabListScroll.y = float.MaxValue; // we added a alternate prefab so record the rule change this.IsDirty = true; }
/// <summary> /// Removes the currently selected alternate prefab. /// </summary> /// <param name="rule">The rule whose alternate prefab will be removed.</param> private void RemoveAlternatePrefab(DrawRuleModel rule) { if (rule.Alternates == null) { return; } var index = 0; while (index < rule.Alternates.Count) { if (rule.Alternates[index].IsChecked) { rule.Alternates.RemoveAt(index); // the rules set has changed so set IsDirty this.IsDirty = true; continue; } index++; } }
/// <summary> /// Draws a rule item in the rule list. /// </summary> /// <param name="rule">A reference to the rule to be drawn.</param> /// <param name="index">The index of the rule being drawn.</param> /// <returns>Returns true if the rule has been checked.</returns> private bool DrawRuleListItem(DrawRuleModel rule, int index) { // setup button style to use var buttonStyle = new GUIStyle(GUI.skin.button) { alignment = TextAnchor.UpperCenter, imagePosition = ImagePosition.ImageAbove }; // get asset preview var assetPreview = rule.Prefab == null ? null : AssetPreview.GetAssetPreview(rule.Prefab); assetPreview = assetPreview == null ? AssetPreview.GetMiniTypeThumbnail(typeof(GameObject)) : assetPreview; // draw asset preview GUILayout.BeginVertical(GUI.skin.box); // entry list item // if there is something to show then show it if (!string.IsNullOrEmpty(rule.Name) || rule.Prefab != this.prefab) { GUILayout.BeginHorizontal(); // top name and update button // show the rule name above GUILayout.Label(rule.Name); // provide a button to update the rule prefab to the currently selected prefab if (rule.Prefab != null && GUILayout.Button("S", GUILayout.ExpandWidth(false))) { Selection.objects = new Object[] { rule.Prefab }; EditorGUIUtility.PingObject(rule.Prefab); } // provide a button to update the rule prefab to the currently selected prefab if (Helpers.GetSourcePrefab(rule.Prefab) != Helpers.GetSourcePrefab(this.prefab) && GUILayout.Button("U", GUILayout.ExpandWidth(false))) { rule.Prefab = this.prefab; this.IsDirty = true; } GUILayout.EndHorizontal(); // top name and update button } // draw the actual toggle button for the entry var isChecked = GUILayout.Toggle(this.selectedRuleIndex == index, new GUIContent(rule.Prefab == null ? string.Empty : rule.Prefab.name, assetPreview), buttonStyle); GUILayout.EndVertical(); // entry list item return isChecked; }
/// <summary> /// Creates a clone of the object. /// </summary> /// <returns>Returns a clone of the object.</returns> public DrawRuleModel Clone() { var rule = new DrawRuleModel(); rule.AllowOriginal = this.AllowOriginal; rule.Description = this.Description; rule.Enabled = this.Enabled; rule.Name = this.Name; rule.NeighborsLowerEnabled = this.NeighborsLowerEnabled; rule.NeighborsUpperEnabled = this.NeighborsUpperEnabled; rule.Prefab = this.Prefab; rule.neighbors = this.neighbors; rule.neighborsLower = this.neighborsLower; rule.neighborsUpper = this.neighborsUpper; foreach (var alternate in this.Alternates) { rule.Alternates.Add(alternate.Clone()); } return rule; }