/// <inheritdoc/> public override void OnRefreshToolEvent(ToolEvent e, IToolContext context) { base.OnRefreshToolEvent(e, context); // Update local point of mouse within tile system. this.localMousePoint = e.MousePointerLocalPoint; if (this.SnapAxisX.Alignment == SnapAlignment.Cells) { this.localMousePoint.x -= this.SnapAxisX.Resolve(context.TileSystem.CellSize.x) / 2f; } if (this.SnapAxisY.Alignment == SnapAlignment.Cells) { this.localMousePoint.y += this.SnapAxisY.Resolve(context.TileSystem.CellSize.y) / 2f; } // "Disable cycle function" this.allowOverpaint = this.DisableCycleFunction | Event.current.control; if (Event.current.isMouse) { var go = HandleUtility.PickGameObject(Event.current.mousePosition, true); ToolUtility.ActivePlop = (go != null) ? go.GetComponentInParent <PlopInstance>() : null; // "Interact with active system only" if (this.InteractWithActiveSystemOnly && ToolUtility.ActivePlop != null && ToolUtility.ActivePlop.Owner != context.TileSystem) { ToolUtility.ActivePlop = null; } } }
/// <inheritdoc/> public override void OnTool(ToolEvent e, IToolContext context) { switch (e.Type) { case EventType.MouseDown: ToolBase fallbackRestoreTool; Brush pickedBrush = null; if (ToolUtility.ActivePlop != null && ToolUtility.ActivePlop.Brush != null) { fallbackRestoreTool = ToolManager.Instance.Find <PlopTool>(); // Get plop at pointer. pickedBrush = ToolUtility.ActivePlop.Brush; // Pick rotation from tile also! ToolUtility.Rotation = ToolUtility.ActivePlop.PaintedRotation; } else { fallbackRestoreTool = ToolManager.DefaultPaintTool; // Get tile at pointer. var tile = context.TileSystem.GetTile(e.MousePointerTileIndex); if (tile != null) { pickedBrush = tile.brush; // Pick rotation from tile also! ToolUtility.Rotation = tile.PaintedRotation; } } // Select brush in tool window and force auto scroll. if (e.IsLeftButtonPressed) { ToolUtility.SelectedBrush = pickedBrush; ToolUtility.RevealBrush(pickedBrush); } else { ToolUtility.SelectedBrushSecondary = pickedBrush; } ToolUtility.RepaintBrushPalette(); // Switch to previous tool or the "Paint" tool. var toolManager = ToolManager.Instance; if (toolManager.PreviousTool != null && toolManager.PreviousTool != this) { toolManager.CurrentTool = toolManager.PreviousTool; } else { toolManager.CurrentTool = fallbackRestoreTool; } break; } }
/// <inheritdoc/> public override void OnSceneGUI(ToolEvent e, IToolContext context) { if (!IsEditorNearestControl) { return; } // "Hide wireframe outline" if (this.HideWireframeOutline) { bool willErase = (ToolUtility.SelectedBrush == null && ToolUtility.ActivePlop != null); bool willCycle = (!this.allowOverpaint && ToolUtility.ActivePlop != null && PlopUtility.CanPlopWithBrush(ToolUtility.SelectedBrush)); bool disableImmediatePreview = (ToolUtility.SelectedBrush != null && ToolUtility.SelectedBrush.disableImmediatePreview); if (!willErase && !willCycle && !disableImmediatePreview) { return; } } // Outline plop with wire cube! Vector3 wirePoint = (!this.allowOverpaint && ToolUtility.ActivePlop != null) ? ToolUtility.ActivePlop.PlopPoint : this.ApplySnapping(this.localMousePoint); Vector3 snapCellSize = context.TileSystem.CellSize; snapCellSize.x = this.SnapAxisX.Resolve(snapCellSize.x); snapCellSize.y = this.SnapAxisY.Resolve(snapCellSize.y); ToolHandleUtility.DrawWireBox(wirePoint, snapCellSize); }
/// <inheritdoc/> public override void OnTool(ToolEvent e, IToolContext context) { switch (e.Type) { case EventType.MouseDown: if (this.anchorSystem != context.TileSystem && (e.IsLeftButtonPressed || e.IsRightButtonPressed)) { this.anchorIndex = e.MousePointerTileIndex; this.anchorSystem = context.TileSystem; } else { // Allow another mouse button to cancel painting. this.anchorSystem = null; } break; case EventType.MouseUp: if (context.TileSystem == this.anchorSystem) { this.anchorSystem = null; this.OnPaint(e, context); } break; } }
/// <inheritdoc/> public override void OnRefreshToolEvent(ToolEvent e, IToolContext context) { if (this.IsTargetPointConstrained) { TileIndex targetIndex = e.MousePointerTileIndex; // Determine whether to constrain horizontally or vertically. int lineRowCount = Mathf.Abs(targetIndex.row - anchorIndex.row); int lineColumnCount = Mathf.Abs(targetIndex.column - anchorIndex.column); if (lineRowCount < lineColumnCount) { targetIndex.row = anchorIndex.row; } else { targetIndex.column = anchorIndex.column; } e.MousePointerTileIndex = targetIndex; } // Allow user to cancel painting by tapping escape key. if (e.Type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) { this.anchorSystem = null; Event.current.Use(); } }
/// <summary> /// Raised by <see cref="OnTool"/> to perform painting upon releasing left or right /// mouse button when a tile has been anchored on the active tile system. /// </summary> /// <param name="e">Tool event data.</param> /// <param name="context">Context that tool is being used in.</param> protected virtual void OnPaint(ToolEvent e, IToolContext context) { var tileSystem = context.TileSystem; // Lines are only painted upon releasing mouse button. // See: LineTool.OnTool implementation. try { // Avoid updating procedural meshes multiple times whilst painting tiles. // In most circumstances this would not happen anyhow, but for performance // it's better to play it safe. tileSystem.BeginProceduralEditing(); var brush = (e.WasLeftButtonPressed ? ToolUtility.SelectedBrush : ToolUtility.SelectedBrushSecondary); // Draw line from last anchor point. this.PaintLine( system: tileSystem, from: this.anchorIndex, to: e.MousePointerTileIndex, brush: brush ); } finally { tileSystem.EndProceduralEditing(); } }
/// <summary> /// Raised by <see cref="OnTool"/> to perform painting upon releasing left or right /// mouse button when a tile has been anchored on the active tile system. /// </summary> /// <param name="e">Tool event data.</param> /// <param name="context">Context that tool is being used in.</param> protected virtual void OnPaint(ToolEvent e, IToolContext context) { var tileSystem = context.TileSystem; try { // Avoid updating procedural meshes multiple times whilst painting tiles. // In most circumstances this would not happen anyhow, but for performance // it's better to play it safe. tileSystem.BeginProceduralEditing(); var brush = (e.WasLeftButtonPressed ? ToolUtility.SelectedBrush : ToolUtility.SelectedBrushSecondary); TileIndex from, to; MathUtility.GetRectangleBoundsClamp(tileSystem, this.anchorIndex, e.MousePointerTileIndex, out from, out to, this.IsTargetPointConstrained); if (from != to) { PaintingUtility.PaintRectangle(tileSystem, from, to, ToolUtility.FillCenter, this.GetPaintingArgs(brush)); } else { this.PaintPoint(tileSystem, from, brush); } } finally { tileSystem.EndProceduralEditing(); } }
/// <inheritdoc/> public override void OnRefreshToolEvent(ToolEvent e, IToolContext context) { base.OnRefreshToolEvent(e, context); if (Event.current.isMouse) { ToolUtility.ActivePlop = null; // "Can pick plops" if (this.CanPickPlops) { var go = HandleUtility.PickGameObject(Event.current.mousePosition, true); if (go != null) { ToolUtility.ActivePlop = go.GetComponentInParent <PlopInstance>(); if (ToolUtility.ActivePlop != null) { // "Interact with active system only" if (this.InteractWithActiveSystemOnly && ToolUtility.ActivePlop.Owner != context.TileSystem) { ToolUtility.ActivePlop = null; } } } } } }
/// <inheritdoc/> public override void OnRefreshToolEvent(ToolEvent e, IToolContext context) { // Allow user to cancel painting by tapping escape key. if (e.Type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) { this.anchorSystem = null; Event.current.Use(); } }
/// <inheritdoc/> public override void OnTool(ToolEvent e, IToolContext context) { switch (e.Type) { case EventType.MouseDown: this.OnPaint(e, context); break; } }
/// <inheritdoc/> public override void OnSceneGUI(ToolEvent e, IToolContext context) { if (!IsEditorNearestControl) { return; } if (this.IsLineModeActive && context.TileSystem == this.anchorSystem) { this.DrawNozzleLine(this.anchorSystem, this.anchorIndex, e.MousePointerTileIndex); } this.DrawNozzleIndicator(context.TileSystem, e.MousePointerTileIndex, ToolUtility.BrushNozzle, this.NozzleSize); }
/// <summary> /// Raised by <see cref="OnTool"/> to perform painting upon pressing left or right mouse button. /// </summary> /// <param name="e">Tool event data.</param> /// <param name="context">Context that tool is being used in.</param> protected virtual void OnPaint(ToolEvent e, IToolContext context) { var brush = e.IsLeftButtonPressed ? ToolUtility.SelectedBrush : ToolUtility.SelectedBrushSecondary; // Like with the regular paint tool, null brush is the eraser! if (brush != null && !PlopUtility.CanPlopWithBrush(brush)) { return; } if (brush == null) { if (ToolUtility.ActivePlop != null) { PlopUtility.ErasePlop(ToolUtility.ActivePlop); ToolUtility.ActivePlop = null; } } else { if (!this.allowOverpaint && ToolUtility.ActivePlop != null) { // Cycle to next variation if replacing plop with same brush. int nextVariation = ToolUtility.ActivePlop.VariationIndex; if (brush == ToolUtility.ActivePlop.Brush) { ++nextVariation; } ToolUtility.ActivePlop = PlopUtility.CyclePlop(context.TileSystem, ToolUtility.ActivePlop, brush, ToolUtility.Rotation, nextVariation); } else { var args = this.GetPaintingArgs(brush); if (args.variation == Brush.RANDOM_VARIATION) { args.variation = this.PreRandomizeVariation(brush, 0); this.RandomizeVariationShift(); } int nextVariation = args.ResolveVariation(0); var plop = PlopUtility.PaintPlop(context.TileSystem, this.ApplySnapping(this.localMousePoint), brush, ToolUtility.Rotation, nextVariation); ToolUtility.ActivePlop = plop; ToolUtility.PreviouslyPlopped = plop; } } }
/// <summary> /// Raised when handling scene view GUI events. /// </summary> /// <remarks> /// <para>The primary purpose of this method is to draw helper objects into /// scene views making it easier to interact with tile systems. Custom tool /// processing logic should be placed into one of the following methods /// where possible:</para> /// <list type="bullet"> /// <item><see cref="OnRefreshToolEvent"/></item> /// <item><see cref="OnTool"/></item> /// <item><see cref="OnToolInactive"/></item> /// </list> /// <para>This method will be invoked multiple times when handling different /// GUI events within a scene view. This method is called from the context of /// the tile system editor (see <a href="http://docs.unity3d.com/Documentation/ScriptReference/Editor.OnSceneGUI.html">Editor.OnSceneGUI</a>).</para> /// <para>Here is the default implementation of this method:</para> /// <code language="csharp"><![CDATA[ /// public override void OnSceneGUI(ToolEvent e, IToolContext context) /// { /// if (!IsEditorNearestControl) { /// return; /// } /// this.DrawNozzleIndicator(context.TileSystem, e.tileIndex, BrushNozzle.Square, 1); /// } /// ]]></code> /// </remarks> /// <param name="e">Event data.</param> /// <param name="context">Context of tool usage.</param> public virtual void OnSceneGUI(ToolEvent e, IToolContext context) { if (!IsEditorNearestControl) { return; } // Outline plop with wire cube! if (ToolUtility.ActivePlop != null) { ToolHandleUtility.DrawWireBox(ToolUtility.ActivePlop.PlopPoint, context.TileSystem.CellSize); return; } this.DrawNozzleIndicator(context.TileSystem, e.MousePointerTileIndex, BrushNozzle.Square, 1); }
/// <inheritdoc/> public override void OnTool(ToolEvent e, IToolContext context) { switch (e.Type) { case EventType.MouseDown: case EventType.MouseDrag: var brush = e.IsLeftButtonPressed ? ToolUtility.SelectedBrush : ToolUtility.SelectedBrushSecondary; int restoreMaximumFillCount = PaintingUtility.MaximumFillCount; PaintingUtility.MaximumFillCount = this.MaximumFillCount; try { PaintingUtility.FloodFill(context.TileSystem, e.MousePointerTileIndex, this.GetPaintingArgs(brush)); } finally { PaintingUtility.MaximumFillCount = restoreMaximumFillCount; } break; } }
/// <inheritdoc/> public override void OnTool(ToolEvent e, IToolContext context) { switch (e.Type) { case EventType.MouseDown: case EventType.MouseDrag: // Skip overpaint to avoid painting same cell multiple times! if (e.MousePointerTileIndex == this.lastPaintIndex) { return; } try { this.OnPaint(e, context); } finally { this.lastPaintIndex = e.MousePointerTileIndex; } break; } }
/// <inheritdoc/> public override void OnRefreshToolEvent(ToolEvent e, IToolContext context) { if (this.IsLineModeActive && this.IsTargetPointConstrained) { TileIndex targetIndex = e.MousePointerTileIndex; // Determine whether to constrain horizontally or vertically. int lineRowCount = Mathf.Abs(targetIndex.row - anchorIndex.row); int lineColumnCount = Mathf.Abs(targetIndex.column - anchorIndex.column); if (lineRowCount < lineColumnCount) { targetIndex.row = anchorIndex.row; } else { targetIndex.column = anchorIndex.column; } e.MousePointerTileIndex = targetIndex; } // Automatically switch between brush and line cursor. this.cursor = this.IsLineModeActive ? ToolCursors.Line : ToolCursors.Brush; // Is there any potential to cycle the active tile? if (ToolUtility.SelectedBrush != null && this.NozzleSize == 1 && (!e.IsLeftButtonPressed && !e.IsRightButtonPressed)) { var existingTile = context.TileSystem.GetTileOrNull(e.MousePointerTileIndex); if (existingTile != null && existingTile.brush == ToolUtility.SelectedBrush) { // Find out if tile has multiple variations. int orientation = OrientationUtility.DetermineTileOrientation(context.TileSystem, e.MousePointerTileIndex, ToolUtility.SelectedBrush, existingTile.PaintedRotation); int variationCount = ToolUtility.SelectedBrush.CountTileVariations(orientation); if (variationCount > 1) { this.cursor = ToolCursors.Cycle; } } } }
/// <inheritdoc/> public override void OnTool(ToolEvent e, IToolContext context) { switch (e.Type) { case EventType.MouseDown: // Note: Left button for next; right button for previous. int offset = 0; if (e.IsLeftButtonPressed) { offset = -1; } else if (e.IsRightButtonPressed) { offset = +1; } if (ToolUtility.ActivePlop != null) { // Variation index to use next? int nextVariation = ToolUtility.ActivePlop.VariationIndex + offset; // Cycle through plop variations. ToolUtility.ActivePlop = PlopUtility.CyclePlop(context.TileSystem, ToolUtility.ActivePlop, ToolUtility.ActivePlop.Brush, ToolUtility.ActivePlop.PaintedRotation, nextVariation); } else { // Get tile at pointer var tile = context.TileSystem.GetTile(e.MousePointerTileIndex); if (tile == null || tile.brush == null) { return; } // Variation index to use next? int nextVariation = tile.variationIndex + offset; // Cycle through tile variations. tile.brush.CycleWithSimpleRotation(context.TileSystem, e.MousePointerTileIndex, tile.PaintedRotation, nextVariation); } break; } }
/// <inheritdoc/> public override void OnToolInactive(ToolEvent e, IToolContext context) { // Clear index of previously painted tile. this.lastPaintIndex = TileIndex.invalid; }
/// <summary> /// Raised by <see cref="OnTool"/> to perform painting upon pressing left or right /// mouse button and then occurs again each time the mouse is dragged. /// </summary> /// <param name="e">Tool event data.</param> /// <param name="context">Context that tool is being used in.</param> protected virtual void OnPaint(ToolEvent e, IToolContext context) { var tileSystem = context.TileSystem; try { // Avoid updating procedural meshes multiple times whilst painting tiles. // In most circumstances this would not happen anyhow, but for performance // it's better to play it safe. tileSystem.BeginProceduralEditing(); var brush = (e.IsLeftButtonPressed ? ToolUtility.SelectedBrush : ToolUtility.SelectedBrushSecondary); if (e.Type == EventType.MouseDown) { // Get existing tile instance from target cell. var existingTile = tileSystem.GetTile(e.MousePointerTileIndex); // Determine whether new tile will be unique. bool willPaintUnique = existingTile == null || existingTile.IsGameObjectMissing || existingTile.brush == null || existingTile.brush != brush; if (this.IsLineModeActive && tileSystem == this.anchorSystem) { // Draw line from last anchor point when using shift key. this.PaintLine( system: tileSystem, from: this.anchorIndex, to: e.MousePointerTileIndex, brush: brush ); } else if (!willPaintUnique && this.NozzleSize == 1) { // Attempt to cycle tile on mouse down. // // Note: Always overpaint missing tile, however. // if (ToolUtility.Rotation != existingTile.PaintedRotation) { existingTile.brush.CycleWithSimpleRotation(tileSystem, e.MousePointerTileIndex, ToolUtility.Rotation, existingTile.variationIndex); // Cycle with rotation could affect the orientation of surrounding tiles. // Therefore it is necessary to refresh them. tileSystem.RefreshSurroundingTiles(e.MousePointerTileIndex); } else { existingTile.brush.Cycle(tileSystem, e.MousePointerTileIndex, existingTile.variationIndex + 1); } } else { // Paint single tile. this.PaintPoint( system: tileSystem, index: e.MousePointerTileIndex, brush: brush ); } } else { this.PaintLine( system: tileSystem, from: this.lastPaintIndex, to: e.MousePointerTileIndex, brush: brush ); } } finally { tileSystem.EndProceduralEditing(); this.anchorSystem = context.TileSystem; this.anchorIndex = e.MousePointerTileIndex; } }
/// <summary> /// Raised allowing tool to adjust tool event before interacting with tool or /// handling scene view GUI events. /// </summary> /// <example> /// <para>The line tool overrides this method allowing the user to constrain /// to straight horizontal/vertical lines when the shift key is held:</para> /// <code language="csharp"><![CDATA[ /// public override void FilterToolEvent(ToolEvent e, IToolContext context) /// { /// if (Event.current.shift) { /// var targetIndex = e.tileIndex; /// /// // Determine whether to constrain horizontally or vertically. /// int lineRowCount = Mathf.Abs(targetIndex.row - this.anchorIndex.row); /// int lineColumnCount = Mathf.Abs(targetIndex.column - this.anchorIndex.column); /// if (lineRowCount < lineColumnCount) { /// targetIndex.row = this.anchorIndex.row; /// } /// else { /// targetIndex.column = this.anchorIndex.column; /// } /// /// e.tileIndex = targetIndex; /// } /// } /// ]]></code> /// </example> /// <param name="e">Event data.</param> /// <param name="context">Context of tool usage.</param> public virtual void OnRefreshToolEvent(ToolEvent e, IToolContext context) { }
/// <summary> /// Raised when tool is being used. /// </summary> /// <remarks> /// <para>This event occurs when the left or right mouse button is pressed.</para> /// <para>Additional information regarding the current event can be determined using /// <c>UnityEditor.Event.current</c>.</para> /// </remarks> /// <example> /// <para>The following example demonstrates how to implement a tool which paints /// a single tile when either the left or right mouse button is clicked.</para> /// <code language="csharp"><![CDATA[ /// public override void OnTool(ToolEvent e, IToolContext context) /// { /// if (e.type == EventType.MouseDown) { /// // Switch between primary and secondary brush depending /// // upon whether left or right button was used /// var brush = e.leftButton /// ? ToolUtility.selectedBrush /// : ToolUtility.selectedBrushSecondary; /// /// // Paint or erase a single tile /// if (brush != null) { /// brush.Paint(context.TileSystem, e.tileIndex); /// } /// else { /// context.TileSystem.EraseTile(e.tileIndex); /// } /// /// context.TileSystem.RefreshSurroundingTiles(e.tileIndex); /// } /// } /// ]]></code> /// </example> /// <param name="e">Event data.</param> /// <param name="context">Context of tool usage.</param> public abstract void OnTool(ToolEvent e, IToolContext context);
/// <summary> /// Raised when tool is being used but is inactive. /// </summary> /// <remarks> /// <para>This event occurs when the left or right mouse button is <b>not</b> pressed.</para> /// <para>Additional information regarding the current event can be determined using /// <c>UnityEditor.Event.current</c>.</para> /// </remarks> /// <param name="e">Event data.</param> /// <param name="context">Context of tool usage.</param> public virtual void OnToolInactive(ToolEvent e, IToolContext context) { }
private void OnSceneGUI() { var tileSystem = this.target as TileSystem; // This is not the active tile system, bail!! if (tileSystem != ToolUtility.ActiveTileSystem) { return; } // Skip for non-instance prefabs. if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab) { return; } // Tool Hack: Just in case OnPreSceneGUI is not supported in the future. if (this.sceneControlID == -1) { this.sceneControlID = GUIUtility.GetControlID(FocusType.Passive); } // Do not update the value of `ToolBase.IsEditorNearestControl` during layout // events since Unity 4.6.0f1 seems to have introduced a bug whereby the // value of `HandleUtility.nearestControl` is temporarily incorrect. if (Event.current.type != EventType.Layout) { // Determine whether tile system editor is the nearest control to the mouse pointer. // We can use this to avoid overuling other controls in the scene view (like the // viewing angle gadget in upper-right corner). ToolBase.IsEditorNearestControl = (HandleUtility.nearestControl == this.sceneControlID || GUIUtility.hotControl == this.sceneControlID); } // Should tool event data be initialized? if (this.toolEvent == null) { this.toolEvent = new ToolEvent(); this.UpdateToolEventFromCursor(Event.current.mousePosition); } ToolManager.Instance.CheckForKeyboardShortcut(); var tool = ToolManager.Instance.CurrentTool; if (tool != null) { this.DrawStatusPanel(); } // Prevent editing if tile system is not editable! if (!tileSystem.IsEditable) { return; } EventType eventType = Event.current.GetTypeForControl(this.sceneControlID); switch (eventType) { case EventType.Ignore: case EventType.Used: return; case EventType.Layout: if (tool != null) { // Prevent regular object selection when tool is active. HandleUtility.AddDefaultControl(this.sceneControlID); } break; } // Need to record whether this is a mouse event prior to tracking mouse // input since "MouseDrag" and "MouseUp" will otherwise not be properly // detected within `DoTool` since `GUIUtility.hotControl` will have been // reassigned to a value of 0. bool isMouseEvent = (ToolBase.IsEditorNearestControl && Event.current.IsMouseForControl(this.sceneControlID)); bool wasSceneActiveControl = (GUIUtility.hotControl == this.sceneControlID); ToolBase.PreviousToolEvent = this.toolEvent; this.toolEvent.Type = eventType; this.toolEvent.WasLeftButtonPressed = this.toolEvent.IsLeftButtonPressed; this.toolEvent.WasRightButtonPressed = this.toolEvent.IsRightButtonPressed; this.UpdateCursor(); if (isMouseEvent) { this.TrackMouseInput(); } // Do not proceed when a different tool is anchored (with mouse drag)! if (tool != null && (s_AnchorTool == null || s_AnchorTool == tool)) { this.DoTool(tool, isMouseEvent, wasSceneActiveControl); } }