Exemplo n.º 1
0
        private void DrawProceduralField(TilesetBrush brush, Tileset tileset)
        {
            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Procedural"),
                       TileLang.Text("Allows individual atlas brushes to override property of tileset.")
                       )) {
                // Need to make width of "Procedural" popup shorter.
                GUILayout.BeginHorizontal(GUILayout.Width(200));
                InheritYesNo newProcedural = (InheritYesNo)EditorGUILayout.EnumPopup(content, brush.procedural);
                if (newProcedural != brush.procedural)
                {
                    brush.procedural = newProcedural;

                    if (!brush.IsProcedural)
                    {
                        // Ensure that required procedural mesh exists!
                        if (BrushUtility.EnsureTilesetMeshExists(tileset, brush.tileIndex))
                        {
                            AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(tileset.tileMeshAsset));
                        }
                    }
                }

                Rect position = GUILayoutUtility.GetLastRect();
                GUI.Label(new Rect(position.x + position.width, position.y, 100, position.height), "= " + (brush.IsProcedural ? TileLang.Text("Procedural") : TileLang.Text("Non-Procedural")), EditorStyles.miniLabel);

                GUILayout.EndHorizontal();

                ExtraEditorGUI.TrailingTip(content);
            }
        }
Exemplo n.º 2
0
        /// <inheritdoc/>
        public override void OnExtendedPropertiesGUI()
        {
            var emptyBrush = Brush as EmptyBrush;

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Always Add Container"),
                       TileLang.Text("Add tile container object even when not needed by brush.")
                       )) {
                emptyBrush.alwaysAddContainer = EditorGUILayout.ToggleLeft(content, emptyBrush.alwaysAddContainer);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Add Collider"),
                       TileLang.Text("Automatically adds box collider to painted tile.")
                       )) {
                emptyBrush.addCollider = EditorGUILayout.ToggleLeft(content, emptyBrush.addCollider);
                if (emptyBrush.addCollider)
                {
                    ++EditorGUI.indentLevel;
                    emptyBrush.colliderType = (ColliderType)EditorGUILayout.EnumPopup(emptyBrush.colliderType);
                    --EditorGUI.indentLevel;
                }
                ExtraEditorGUI.TrailingTip(content);
            }
        }
        private void OnSection_RuntimeOptions()
        {
            GUILayout.Space(3);

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Erase Empty Chunks"),
                       TileLang.Text("Hints that empty chunks should be erased when they become empty at runtime.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyHintEraseEmptyChunks, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Apply Basic Stripping"),
                       TileLang.Text("Applies a basic degree of stripping at runtime upon awakening.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyApplyRuntimeStripping, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            ExtraEditorGUI.SeparatorLight();

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Update Procedural at Start"),
                       TileLang.Text("Automatically updates procedural meshes at runtime upon awakening.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyUpdateProceduralAtStart, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Mark Procedural Dynamic"),
                       TileLang.Text("Helps to improve performance when procedural tiles are updated frequently at runtime. Unset if only updated at start of level.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyMarkProceduralDynamic, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Add Procedural Normals"),
                       TileLang.Text("Adds normals to procedural meshes.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyAddProceduralNormals, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            ExtraEditorGUI.SeparatorLight();

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Procedural Sorting Layer"),
                       TileLang.Text("Sorting layer for procedural tileset meshes.")
                       )) {
                RotorzEditorGUI.SortingLayerField(this.propertySortingLayerID, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Procedural Order in Layer"),
                       TileLang.Text("Order in sorting layer.")
                       )) {
                EditorGUILayout.PropertyField(this.propertySortingOrder, content);
                ExtraEditorGUI.TrailingTip(content);
            }
        }
        private void OnSection_BuildOptions()
        {
            float initialLabelWidth = EditorGUIUtility.labelWidth;

            GUILayout.Space(3);

            using (var content = ControlContent.Basic(
                       TileLang.ParticularText("Property", "Combine Method")
                       )) {
                EditorGUILayout.PropertyField(this.propertyCombineMethod, content);
            }

            if (!this.propertyCombineMethod.hasMultipleDifferentValues)
            {
                if ((BuildCombineMethod)this.propertyCombineMethod.intValue == BuildCombineMethod.CustomChunkInTiles)
                {
                    GUILayout.BeginHorizontal();
                    ++EditorGUI.indentLevel;
                    {
                        EditorGUIUtility.labelWidth = 65;

                        EditorGUILayout.PrefixLabel(TileLang.ParticularText("Property", "Height"));
                        EditorGUI.BeginChangeCheck();
                        EditorGUILayout.PropertyField(this.propertyCombineChunkHeight, GUIContent.none);
                        if (EditorGUI.EndChangeCheck())
                        {
                            this.propertyCombineChunkHeight.intValue = Mathf.Max(1, this.propertyCombineChunkHeight.intValue);
                        }

                        EditorGUILayout.PrefixLabel(TileLang.ParticularText("Property", "Width"));
                        EditorGUI.BeginChangeCheck();
                        EditorGUILayout.PropertyField(this.propertyCombineChunkWidth, GUIContent.none);
                        if (EditorGUI.EndChangeCheck())
                        {
                            this.propertyCombineChunkWidth.intValue = Mathf.Max(1, this.propertyCombineChunkWidth.intValue);
                        }

                        EditorGUIUtility.labelWidth = initialLabelWidth;
                    }
                    --EditorGUI.indentLevel;
                    GUILayout.EndHorizontal();
                }

                if ((BuildCombineMethod)this.propertyCombineMethod.intValue != BuildCombineMethod.None)
                {
                    ++EditorGUI.indentLevel;
                    {
                        this.propertyCombineIntoSubmeshes.boolValue = EditorGUILayout.ToggleLeft(TileLang.ParticularText("Property", "Combine into submeshes"), this.propertyCombineIntoSubmeshes.boolValue);
                        if (ControlContent.TrailingTipsVisible)
                        {
                            ExtraEditorGUI.TrailingTip(TileLang.Text("Determines whether to use submeshes, or an individual mesh for each material."));
                        }
                    }
                    --EditorGUI.indentLevel;

                    RotorzEditorGUI.InfoBox(TileLang.Text("Avoid generation of meshes with vertices in excess of 64k."), MessageType.Warning);
                }
            }

            EditorGUILayout.Space();

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Vertex Snap Threshold"),
                       TileLang.Text("Increase threshold to snap vertices that are more widely spread.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyVertexSnapThreshold, content);
                if (!this.propertyVertexSnapThreshold.hasMultipleDifferentValues)
                {
                    if (this.propertyVertexSnapThreshold.floatValue == 0f)
                    {
                        EditorGUILayout.HelpBox(TileLang.Text("No snapping occurs when threshold is 0."), MessageType.Warning, true);
                    }
                }
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Static Snapping"),
                       TileLang.Text("Applies vertex snapping to static tiles to avoid tiny gaps due to numerical inaccuracies. Vertex snapping is always applied to 'smooth' tiles.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyStaticVertexSnapping, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.Basic(
                       TileLang.ParticularText("Property", "Generate Lightmap UVs")
                       )) {
                EditorGUILayout.PropertyField(this.propertyGenerateSecondUVs, content);
                if (this.propertyGenerateSecondUVs.boolValue)
                {
                    ++EditorGUI.indentLevel;

                    s_ToggleBuildOptions_AdvancedUV2 = EditorGUILayout.Foldout(s_ToggleBuildOptions_AdvancedUV2, TileLang.ParticularText("Section", "Advanced"));
                    if (s_ToggleBuildOptions_AdvancedUV2)
                    {
                        float hardAngle  = this.propertySecondUVsHardAngle.floatValue;
                        float packMargin = this.propertySecondUVsPackMargin.floatValue * 1024f;
                        float angleError = this.propertySecondUVsAngleError.floatValue * 100f;
                        float areaError  = this.propertySecondUVsAreaError.floatValue * 100f;

                        using (var content2 = ControlContent.WithTrailableTip(
                                   TileLang.ParticularText("Property", "Hard Angle"),
                                   TileLang.Text("Angle between neighbor triangles that will generate seam.")
                                   )) {
                            EditorGUI.BeginChangeCheck();
                            EditorGUI.showMixedValue = this.propertySecondUVsHardAngle.hasMultipleDifferentValues;
                            hardAngle = EditorGUILayout.Slider(content, hardAngle, 0f, 180f);
                            if (EditorGUI.EndChangeCheck())
                            {
                                this.propertySecondUVsHardAngle.floatValue = Mathf.Ceil(hardAngle);
                            }
                            ExtraEditorGUI.TrailingTip(content2);
                        }

                        using (var content2 = ControlContent.WithTrailableTip(
                                   TileLang.ParticularText("Property", "Pack Margin"),
                                   TileLang.Text("Measured in pixels, assuming mesh will cover an entire 1024x1024 lightmap.")
                                   )) {
                            EditorGUI.BeginChangeCheck();
                            EditorGUI.showMixedValue = this.propertySecondUVsPackMargin.hasMultipleDifferentValues;
                            packMargin = EditorGUILayout.Slider(content, packMargin, 1f, 64f);
                            if (EditorGUI.EndChangeCheck())
                            {
                                this.propertySecondUVsPackMargin.floatValue = Mathf.Ceil(packMargin) / 1024f;
                            }
                            ExtraEditorGUI.TrailingTip(content2);
                        }

                        using (var content2 = ControlContent.WithTrailableTip(
                                   TileLang.ParticularText("Property", "Angle Error"),
                                   TileLang.Text("Measured in percents. Angle error measures deviation of UV angles from geometry angles. Area error measure deviation of UV triangles area from geometry triangles if they were uniformly scaled.")
                                   )) {
                            EditorGUI.BeginChangeCheck();
                            EditorGUI.showMixedValue = this.propertySecondUVsAngleError.hasMultipleDifferentValues;
                            angleError = EditorGUILayout.Slider(content, angleError, 1f, 75f);
                            if (EditorGUI.EndChangeCheck())
                            {
                                this.propertySecondUVsAngleError.floatValue = Mathf.Ceil(angleError) / 100f;
                            }
                            ExtraEditorGUI.TrailingTip(content2);
                        }

                        using (var content2 = ControlContent.Basic(
                                   TileLang.ParticularText("Property", "Area Error")
                                   )) {
                            EditorGUI.BeginChangeCheck();
                            EditorGUI.showMixedValue = this.propertySecondUVsAreaError.hasMultipleDifferentValues;
                            areaError = EditorGUILayout.Slider(content2, areaError, 1f, 75f);
                            if (EditorGUI.EndChangeCheck())
                            {
                                this.propertySecondUVsAreaError.floatValue = Mathf.Ceil(areaError) / 100f;
                            }
                        }

                        EditorGUI.showMixedValue = false;
                    }

                    --EditorGUI.indentLevel;
                }
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Pre-generate Procedural"),
                       TileLang.Text("Increases size of scene but allows brushes to be stripped from builds.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyPregenerateProcedural, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            RotorzEditorGUI.InfoBox(TileLang.Text("Stripping capabilities are reduced when procedural tiles are present but are not pre-generated since they are otherwise generated at runtime."), MessageType.Info);
            GUILayout.Space(5);

            using (var content = ControlContent.Basic(
                       TileLang.ParticularText("Property", "Reduce Box Colliders"),
                       TileLang.Text("Reduces count of box colliders by coalescing adjacent colliders.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyReduceColliders, content);
            }
        }
        private void OnSection_TileSystem()
        {
            float initialLabelWidth = EditorGUIUtility.labelWidth;

            BeginMultiPartField(TileLang.ParticularText("Property", "Grid Size (in tiles)"));
            {
                EditorGUIUtility.labelWidth = 65;

                EditorGUILayout.PrefixLabel(TileLang.ParticularText("Property", "Rows"));
                EditorGUI.BeginChangeCheck();
                EditorGUILayout.PropertyField(this.propertyRows, GUIContent.none);
                if (EditorGUI.EndChangeCheck())
                {
                    this.propertyRows.intValue = Mathf.Max(1, this.propertyRows.intValue);
                }

                EditorGUILayout.PrefixLabel(TileLang.ParticularText("Property", "Columns"));
                EditorGUI.BeginChangeCheck();
                EditorGUILayout.PropertyField(this.propertyColumns, GUIContent.none);
                if (EditorGUI.EndChangeCheck())
                {
                    this.propertyColumns.intValue = Mathf.Max(1, this.propertyColumns.intValue);
                }

                EditorGUIUtility.labelWidth = initialLabelWidth;
            }
            EndMultiPartField();

            BeginMultiPartField(TileLang.ParticularText("Property", "Chunk Size (in tiles)"));
            {
                EditorGUIUtility.labelWidth = 65;

                EditorGUILayout.PrefixLabel(TileLang.ParticularText("Property", "Height"));
                EditorGUI.BeginChangeCheck();
                EditorGUILayout.PropertyField(this.propertyChunkHeight, GUIContent.none);
                if (EditorGUI.EndChangeCheck())
                {
                    this.propertyChunkHeight.intValue = Mathf.Max(1, this.propertyChunkHeight.intValue);
                }

                EditorGUILayout.PrefixLabel(TileLang.ParticularText("Property", "Width"));
                EditorGUI.BeginChangeCheck();
                EditorGUILayout.PropertyField(this.propertyChunkWidth, GUIContent.none);
                if (EditorGUI.EndChangeCheck())
                {
                    this.propertyChunkWidth.intValue = Mathf.Max(1, this.propertyChunkWidth.intValue);
                }

                EditorGUIUtility.labelWidth = initialLabelWidth;
            }
            EndMultiPartField();

            if (!this.propertyChunkHeight.hasMultipleDifferentValues && !this.propertyChunkWidth.hasMultipleDifferentValues)
            {
                if (this.propertyChunkHeight.intValue * this.propertyChunkWidth.intValue > 10000)
                {
                    RotorzEditorGUI.InfoBox(TileLang.Text("Do not exceed an area of 100x100 tiles per chunk when using procedural tilesets."), MessageType.Warning);
                }
            }

            if (ControlContent.TrailingTipsVisible)
            {
                ExtraEditorGUI.TrailingTip(TileLang.Text("Number of tiles that contribute to a chunk."));
            }

            ExtraEditorGUI.SeparatorLight();

            BeginMultiPartField(TileLang.ParticularText("Property", "Cell Size"));
            {
                EditorGUI.showMixedValue = this.propertyTileWidth.hasMultipleDifferentValues || this.propertyTileHeight.hasMultipleDifferentValues || this.propertyTileDepth.hasMultipleDifferentValues;

                Vector3 cellSize = new Vector3(this.propertyTileWidth.floatValue, this.propertyTileHeight.floatValue, this.propertyTileDepth.floatValue);
                EditorGUI.BeginChangeCheck();
                cellSize = EditorGUILayout.Vector3Field(GUIContent.none, cellSize);
                if (EditorGUI.EndChangeCheck())
                {
                    this.propertyTileWidth.floatValue  = Mathf.Max(0.0001f, cellSize.x);
                    this.propertyTileHeight.floatValue = Mathf.Max(0.0001f, cellSize.y);
                    this.propertyTileDepth.floatValue  = Mathf.Max(0.0001f, cellSize.z);
                }

                EditorGUI.showMixedValue = false;
            }
            EndMultiPartField();
            if (ControlContent.TrailingTipsVisible)
            {
                ExtraEditorGUI.TrailingTip(TileLang.Text("Span of an individual tile."));
            }

            GUILayout.Space(10);

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Tiles Facing"),
                       TileLang.Text("Direction that tiles will face when painted. 'Sideways' is good for platform and 2D games. 'Upwards' is good for top-down.")
                       )) {
                EditorGUI.BeginChangeCheck();

                EditorGUILayout.PropertyField(this.propertyTilesFacing, content);
                ExtraEditorGUI.TrailingTip(content);

                if (EditorGUI.EndChangeCheck() && this.propertyAutoAdjustDirection.boolValue)
                {
                    switch ((TileFacing)this.propertyTilesFacing.intValue)
                    {
                    case TileFacing.Sideways:
                        this.propertyDirection.intValue = (int)WorldDirection.Forward;
                        break;

                    case TileFacing.Upwards:
                        this.propertyDirection.intValue = (int)WorldDirection.Up;
                        break;
                    }
                }
            }

            GUILayout.Space(3);

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Initial Direction"),
                       TileLang.Text("Initial direction of tile system upon creation. If in doubt assume default and rotate afterwards.")
                       )) {
                EditorGUI.BeginChangeCheck();
                EditorGUILayout.PropertyField(this.propertyDirection, content);
                ExtraEditorGUI.TrailingTip(content);

                if (EditorGUI.EndChangeCheck())
                {
                    this.propertyAutoAdjustDirection.boolValue = false;
                }
            }

            GUILayout.Space(5);
        }
Exemplo n.º 6
0
        private void DrawRuntimeOptionsSection()
        {
            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Erase Empty Chunks"),
                       TileLang.Text("Hints that empty chunks should be erased when they become empty at runtime.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyHintEraseEmptyChunks, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Apply Basic Stripping"),
                       TileLang.Text("Applies a basic degree of stripping at runtime upon awakening.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyApplyRuntimeStripping, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            ExtraEditorGUI.SeparatorLight();

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Update Procedural at Start"),
                       TileLang.Text("Automatically updates procedural meshes at runtime upon awakening.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyUpdateProceduralAtStart, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Mark Procedural Dynamic"),
                       TileLang.Text("Helps to improve performance when procedural tiles are updated frequently at runtime. Unset if only updated at start of level.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyMarkProceduralDynamic, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Add Procedural Normals"),
                       TileLang.Text("Adds normals to procedural meshes.")
                       )) {
                EditorGUILayout.PropertyField(this.propertyAddProceduralNormals, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            ExtraEditorGUI.SeparatorLight();

            EditorGUI.BeginChangeCheck();

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Procedural Sorting Layer"),
                       TileLang.Text("Sorting layer for procedural tileset meshes.")
                       )) {
                RotorzEditorGUI.SortingLayerField(this.propertySortingLayerID, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Procedural Order in Layer"),
                       TileLang.Text("Order in sorting layer.")
                       )) {
                EditorGUILayout.PropertyField(this.propertySortingOrder, content);
                ExtraEditorGUI.TrailingTip(content);
            }

            if (EditorGUI.EndChangeCheck())
            {
                int sortingLayerID = this.propertySortingLayerID.intValue;
                int sortingOrder   = this.propertySortingOrder.intValue;

                // Update existing procedurally generated tileset meshes immediately.
                foreach (var target in this.targets)
                {
                    ((TileSystem)target).ApplySortingPropertiesToExistingProceduralMeshes(sortingLayerID, sortingOrder);
                }
            }
        }
Exemplo n.º 7
0
        /// <inheritdoc/>
        public override void OnExtendedPropertiesGUI()
        {
            bool autoInitCollider;

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Add Collider"),
                       TileLang.Text("Automatically adds box collider to painted tile.")
                       )) {
                EditorGUI.BeginChangeCheck();
                this.TilesetBrush.addCollider = EditorGUILayout.ToggleLeft(content, this.TilesetBrush.addCollider);
                autoInitCollider = (EditorGUI.EndChangeCheck() && this.TilesetBrush.addCollider);
                if (this.TilesetBrush.addCollider)
                {
                    ++EditorGUI.indentLevel;
                    this.TilesetBrush.colliderType = (ColliderType)EditorGUILayout.EnumPopup(this.TilesetBrush.colliderType);
                    --EditorGUI.indentLevel;
                }
                ExtraEditorGUI.TrailingTip(content);
            }

            if (autoInitCollider)
            {
                this.TilesetBrush.colliderType = BrushUtility.AutomaticColliderType;
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Always Add Container"),
                       TileLang.Text("Add tile container object even when not needed by brush.")
                       )) {
                if (this.TilesetBrush.IsProcedural)
                {
                    this.TilesetBrush.alwaysAddContainer = EditorGUILayout.ToggleLeft(content, this.TilesetBrush.alwaysAddContainer);
                    ExtraEditorGUI.TrailingTip(content);
                }
            }

            if (this.brushAttachPrefabTick)
            {
                ExtraEditorGUI.SeparatorLight();
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Attach Prefab"),
                       TileLang.Text("Additional game objects can be painted by attaching a prefab.")
                       )) {
                bool newAttachPrefabTick = EditorGUILayout.ToggleLeft(content, this.brushAttachPrefabTick);
                if (!newAttachPrefabTick)
                {
                    ExtraEditorGUI.TrailingTip(content);
                }

                // Has state of prefab tick changed?
                if (newAttachPrefabTick != this.brushAttachPrefabTick)
                {
                    this.brushAttachPrefabTick = newAttachPrefabTick;
                    // Should attachment be cleared?
                    if (!this.brushAttachPrefabTick)
                    {
                        this.TilesetBrush.attachPrefab = null;
                    }
                }

                if (this.brushAttachPrefabTick)
                {
                    ++EditorGUI.indentLevel;

                    this.TilesetBrush.attachPrefab = EditorGUILayout.ObjectField(this.TilesetBrush.attachPrefab, typeof(GameObject), false) as GameObject;
                    GUILayout.Space(2);
                    this.OnExtendedGUI_ScaleMode();

                    --EditorGUI.indentLevel;
                }
            }
        }
        /// <inheritdoc/>
        public override void OnExtendedPropertiesGUI()
        {
            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Always Add Container"),
                       TileLang.Text("Add tile container object even when not needed by brush.")
                       )) {
                if (this.AutotileBrush.IsProcedural)
                {
                    this.AutotileBrush.alwaysAddContainer = EditorGUILayout.ToggleLeft(content, this.AutotileBrush.alwaysAddContainer);
                    ExtraEditorGUI.TrailingTip(content);

                    if (this.brushAttachPrefabTick)
                    {
                        ExtraEditorGUI.SeparatorLight();
                    }
                }
            }

            using (var content = ControlContent.WithTrailableTip(
                       TileLang.ParticularText("Property", "Attach Prefab"),
                       TileLang.Text("Additional game objects can be painted by attaching a prefab.")
                       )) {
                bool newAttachPrefabTick = EditorGUILayout.ToggleLeft(content, this.brushAttachPrefabTick);
                if (!newAttachPrefabTick)
                {
                    ExtraEditorGUI.TrailingTip(content);
                }

                // Has state of prefab tick changed?
                if (newAttachPrefabTick != this.brushAttachPrefabTick)
                {
                    this.brushAttachPrefabTick = newAttachPrefabTick;
                    // Should attachment be cleared?
                    if (!this.brushAttachPrefabTick)
                    {
                        TilesetBrush.attachPrefab = null;
                    }
                }
            }

            if (this.brushAttachPrefabTick)
            {
                ++EditorGUI.indentLevel;

                this.TilesetBrush.attachPrefab = EditorGUILayout.ObjectField(TilesetBrush.attachPrefab, typeof(GameObject), false) as GameObject;
                GUILayout.Space(3);
                this.OnExtendedGUI_ScaleMode();

                --EditorGUI.indentLevel;
            }

            ExtraEditorGUI.SeparatorLight(marginBottom: 2);

            float restoreLabelWidth = EditorGUIUtility.labelWidth;
            float restoreFieldWidth = EditorGUIUtility.fieldWidth;

            bool autoInitCollider = false;

            EditorGUIUtility.labelWidth = 70;
            EditorGUIUtility.fieldWidth = 1;

            GUILayout.BeginHorizontal();
            {
                GUILayout.BeginVertical();
                {
                    RotorzEditorGUI.MiniFieldDescription(TileLang.Text("Edge Tiles"));

                    using (var content = ControlContent.Basic(
                               TileLang.ParticularText("Flaggable", "Solid Flag"),
                               TileLang.Text("Solid flag can be used to assist with user defined collision detection or pathfinding.")
                               )) {
                        this.AutotileBrush.SolidFlag = EditorGUILayout.ToggleLeft(content, this.AutotileBrush.SolidFlag);
                    }

                    using (var content = ControlContent.Basic(
                               TileLang.ParticularText("Property", "Add Collider"),
                               TileLang.Text("Automatically adds box collider to painted tile.")
                               )) {
                        EditorGUI.BeginChangeCheck();
                        this.AutotileBrush.addCollider = EditorGUILayout.ToggleLeft(content, this.AutotileBrush.addCollider);
                        autoInitCollider |= (EditorGUI.EndChangeCheck() && this.AutotileBrush.addCollider);
                    }
                }
                GUILayout.EndVertical();

                GUILayout.BeginVertical();
                {
                    RotorzEditorGUI.MiniFieldDescription(TileLang.Text("Inner Tiles"));

                    using (var content = ControlContent.Basic(
                               TileLang.ParticularText("Flaggable", "Solid Flag"),
                               TileLang.Text("Solid flag can be used to assist with user defined collision detection or pathfinding.")
                               )) {
                        this.AutotileBrush.InnerSolidFlag = EditorGUILayout.ToggleLeft(content, this.AutotileBrush.InnerSolidFlag);
                    }

                    using (var content = ControlContent.Basic(
                               TileLang.ParticularText("Property", "Add Collider"),
                               TileLang.Text("Automatically adds box collider to painted tile.")
                               )) {
                        EditorGUI.BeginChangeCheck();
                        this.AutotileBrush.addInnerCollider = EditorGUILayout.ToggleLeft(content, this.AutotileBrush.addInnerCollider);
                        autoInitCollider |= (EditorGUI.EndChangeCheck() && this.AutotileBrush.addInnerCollider);
                    }
                }
                GUILayout.EndVertical();
            }
            GUILayout.EndHorizontal();

            if (autoInitCollider)
            {
                this.AutotileBrush.colliderType = BrushUtility.AutomaticColliderType;
            }

            if (this.AutotileBrush.addCollider || this.AutotileBrush.addInnerCollider)
            {
                ++EditorGUI.indentLevel;
                this.AutotileBrush.colliderType = (ColliderType)EditorGUILayout.EnumPopup(this.AutotileBrush.colliderType);
                --EditorGUI.indentLevel;
            }

            EditorGUIUtility.labelWidth = restoreLabelWidth;
            EditorGUIUtility.fieldWidth = restoreFieldWidth;

            if (ControlContent.TrailingTipsVisible)
            {
                ExtraEditorGUI.TrailingTip(TileLang.Text("Edge and Inner 'solid' flag can be used for custom collision detection. Avoid inner colliders where possible."));
            }
        }