创建一个继承自 GridBrushBase
(或 GridBrushBase
的任何有用的子类,如 GridBrush
)的新类。覆盖新画笔类所需的任何方法。以下是您通常会覆盖的方法
Paint
允许画笔将项目添加到目标网格。Erase
允许画笔从目标网格中删除项目。FloodFill
允许画笔将项目填充到目标网格。Rotate
旋转画笔中设置的项目。Flip
翻转画笔中设置的项目。使用 ScriptableObject.CreateInstance<(Your Brush Class>()
创建新类的实例。您可以将此新实例转换为编辑器中的资源,以便通过调用 AssetDatabase.CreateAsset()
重复使用它。
您还可以为您的画笔制作自定义编辑器。这与可脚本化对象的自定义编辑器的工作方式相同。以下是创建自定义编辑器时您可能想要覆盖的主要方法
OnPaintInspectorGUI
以在画笔被选中时在调色板中显示一个检查器一个 Unity 窗口,显示有关当前选定的游戏对象、资源或项目设置的信息,允许您检查和编辑值。 更多信息OnPaintSceneGUI
以在 SceneView
上绘制时添加其他行为。validTargets
以获得画笔可以与其交互的目标的自定义列表。此目标列表在调色板窗口中显示为下拉列表。创建后,可脚本化的画笔将列在调色板窗口中的“画笔”下拉菜单中。默认情况下,可脚本化画笔脚本的实例会被实例化并存储在项目 的 Library 文件夹中。对画笔属性的任何修改都将存储在该实例中。如果您希望拥有多个具有不同属性的画笔副本,则可以在项目中将画笔实例化为资源。这些画笔资源在画笔下拉菜单中单独列出。
您可以将 CustomGridBrush
属性添加到可脚本化的画笔类中。这允许您配置画笔在调色板窗口中的行为。CustomGridBrush
属性具有以下属性
HideAssetInstances
- 将此设置为 true 以隐藏调色板窗口中创建的所有画笔资源副本。当您只想在瓦片调色板窗口的画笔下拉菜单中显示默认实例时,这很有用。HideDefaultInstances
- 将此设置为 true 以隐藏调色板窗口中画笔的默认实例。当您只想在瓦片调色板窗口的画笔下拉菜单中显示创建的资源时,这很有用。DefaultBrush
- 将此设置为 true 以将画笔的默认实例设置为项目中的默认画笔。这使此画笔成为项目启动时默认选定的画笔。注意:仅将一个可脚本化的画笔设置为默认画笔。设置多个默认画笔可能会导致可脚本化的画笔行为不正确。DefaultName
- 设置一个名称,使画笔下拉菜单使用设置的名称作为画笔的名称,而不是画笔类的名称。如果您希望可脚本化的画笔类仅使用某些工具,则可以向您的类添加 BrushTools
属性,其中包含兼容的 TilemapEditorTools
类型的列表。这确保了您的可脚本化的画笔仅在瓦片调色板工具栏Unity 编辑器顶部的按钮和基本控件行,允许您以各种方式与编辑器交互(例如,缩放、平移)。 更多信息
参见 术语表中的这些特定工具下激活。
LineBrush
提供了一种轻松地在瓦片地图一个游戏对象,允许您使用瓦片和网格覆盖快速创建 2D 关卡。 更多信息
参见 术语表上绘制瓦片线的能力,方法是指定起点和终点。LineBrush 的 Paint 方法被覆盖,以允许用户在 Paint 模式下通过第一次鼠标点击指定线的开始,并在 Paint 模式下通过第二次鼠标点击绘制线条。OnPaintSceneGUI
方法被覆盖以生成要在第一次和第二次鼠标点击之间绘制的线的预览。以下是用创建画笔的脚本。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
namespace UnityEditor.Tilemaps
{
[CustomGridBrush(true, false, false, "Line Brush")]
public class LineBrush : GridBrush {
public bool lineStartActive = false;
public Vector3Int lineStart = Vector3Int.zero;
public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
{
if (lineStartActive)
{
Vector2Int startPos = new Vector2Int(lineStart.x, lineStart.y);
Vector2Int endPos = new Vector2Int(position.x, position.y);
if (startPos == endPos)
base.Paint(grid, brushTarget, position);
else
{
foreach (var point in GetPointsOnLine(startPos, endPos))
{
Vector3Int paintPos = new Vector3Int(point.x, point.y, position.z);
base.Paint(grid, brushTarget, paintPos);
}
}
lineStartActive = false;
}
else
{
lineStart = position;
lineStartActive = true;
}
}
[MenuItem("Assets/Create/Line Brush")]
public static void CreateBrush()
{
string path = EditorUtility.SaveFilePanelInProject("Save Line Brush", "New Line Brush", "Asset", "Save Line Brush", "Assets");
if (path == "")
return;
AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<LineBrush>(), path);
}
// http://ericw.ca/notes/bresenhams-line-algorithm-in-csharp.html
public static IEnumerable<Vector2Int> GetPointsOnLine(Vector2Int p1, Vector2Int p2)
{
int x0 = p1.x;
int y0 = p1.y;
int x1 = p2.x;
int y1 = p2.y;
bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
if (steep)
{
int t;
t = x0; // swap x0 and y0
x0 = y0;
y0 = t;
t = x1; // swap x1 and y1
x1 = y1;
y1 = t;
}
if (x0 > x1)
{
int t;
t = x0; // swap x0 and x1
x0 = x1;
x1 = t;
t = y0; // swap y0 and y1
y0 = y1;
y1 = t;
}
int dx = x1 - x0;
int dy = Math.Abs(y1 - y0);
int error = dx / 2;
int ystep = (y0 < y1) ? 1 : -1;
int y = y0;
for (int x = x0; x <= x1; x++)
{
yield return new Vector2Int((steep ? y : x), (steep ? x : y));
error = error - dy;
if (error < 0)
{
y += ystep;
error += dx;
}
}
yield break;
}
}
[CustomEditor(typeof(LineBrush))]
public class LineBrushEditor : GridBrushEditor
{
private LineBrush lineBrush { get { return target as LineBrush; } }
public override void OnPaintSceneGUI(GridLayout grid, GameObject brushTarget, BoundsInt position, GridBrushBase.Tool tool, bool executing)
{
base.OnPaintSceneGUI(grid, brushTarget, position, tool, executing);
if (lineBrush.lineStartActive)
{
Tilemap tilemap = brushTarget.GetComponent<Tilemap>();
if (tilemap != null)
tilemap.ClearAllEditorPreviewTiles();
// Draw preview tiles for tilemap
Vector2Int startPos = new Vector2Int(lineBrush.lineStart.x, lineBrush.lineStart.y);
Vector2Int endPos = new Vector2Int(position.x, position.y);
if (startPos == endPos)
PaintPreview(grid, brushTarget, position.min);
else
{
foreach (var point in LineBrush.GetPointsOnLine(startPos, endPos))
{
Vector3Int paintPos = new Vector3Int(point.x, point.y, position.z);
PaintPreview(grid, brushTarget, paintPos);
}
}
if (Event.current.type == EventType.Repaint)
{
var min = lineBrush.lineStart;
var max = lineBrush.lineStart + position.size;
// Draws a box on the picked starting position
GL.PushMatrix();
GL.MultMatrix(GUI.matrix);
GL.Begin(GL.LINES);
Handles.color = Color.blue;
Handles.DrawLine(new Vector3(min.x, min.y, min.z), new Vector3(max.x, min.y, min.z));
Handles.DrawLine(new Vector3(max.x, min.y, min.z), new Vector3(max.x, max.y, min.z));
Handles.DrawLine(new Vector3(max.x, max.y, min.z), new Vector3(min.x, max.y, min.z));
Handles.DrawLine(new Vector3(min.x, max.y, min.z), new Vector3(min.x, min.y, min.z));
GL.End();
GL.PopMatrix();
}
}
}
}
}