【エディター拡張】UnityEditor上で動くドット絵エディタを作ってみた

UnityEditor上でドット絵が打てたら面白いのではないかと思い、作ってみました。

※この記事で使っているUnityのバージョンは Unity5.2.1p3 です。

![image](https://qiita-image-store.s3.amazonaws.com/0/44288/971a1d86-22d4-366d-e7ff-c3064a018c68.png)

## エディター上でのクリック判定
Event.currentを見ることでマウスの位置情報やクリック情報が取得できるのでこれを利用します。

##エディター上でのドットの描画
まず、以下のコードでテクスチャを生成します

```
private static Texture2D CreateTexture(Color color)
{
  Texture2D texture = new Texture2D(1, 1);
  texture.SetPixel(0, 0, color);
  texture.Apply();
  return texture;
}
```
生成したテクスチャはGUI.DrawTextureを使うことで描画させることができます。

# ソースコード全体
``` PixelArtWindow.cs
using UnityEngine;
using UnityEditor;
using System.Collections;

public class PixelArtWindow : EditorWindow
{
  // ドット
  private int canvasScale = 16; 
  private int canvasX = 16; 
  private int canvasY = 16;
  private Pixel[,] canvas = null;
  private int gridSize = 1;

  // パレット
  private int paletteX = 8;
  private int paletteLabelY { get { return canvasY * canvasScale + 24; } }
  private int paletteColorX { get { return paletteX + 10; } }
  private int paletteColorY { get { return paletteLabelY + 16; } }
  private int paletteColorInterval = 4;
  private int palettesSizeX = 32;
  private int palettesSizeY = 32;
  private int cursorWidth = 1;
  private Rect paletteCursorIn;
  private Rect paletteCursorOut;
  private Rect paletteLabel;
  private Palette[] palettes = new Palette[]
  {
    new Palette(Palette.textureBlack),
    new Palette(Palette.textureWhite),
    new Palette(Palette.textureRed),
    new Palette(Palette.textureGreen),
    new Palette(Palette.textureBlue),
  };

  // ブラシ
  private Texture2D brushColor = null;

  [MenuItem("Window/PixelArtWindow")]
  static void Open()
  {
    EditorWindow.GetWindow<PixelArtWindow>();
  }

  void Init()
  {
    // ドット初期化
    {
      this.canvas = new Pixel[canvasX, canvasY];
      for (int x = 0; x < canvasX; x++)
      {
        for (int y = 0; y < canvasY; y++)
        {
          this.canvas[x, y] = new Pixel(Palette.textureWhite, x, y);
        }
      }
    }

    // パレット初期化
    {
      paletteLabel = new Rect(paletteX, paletteLabelY, 100, 100);
      int x = paletteColorX;
      int y = paletteColorY;
      for (int i = 0; i < palettes.Length; i++)
      {
        palettes[i].rect = new Rect(x, y, palettesSizeX, palettesSizeY);
        x += paletteColorInterval + palettesSizeX;
      }
    }

    // 一番左のパレットを選んでおく
    this.SelectPalette(0);
  }

  void Update()
  {
    Repaint();
  }

  void OnGUI()
  {
    if (this.canvas == null)
    {
      this.Init();
    }

    this.CanvasGUI();

    this.PalettesGUI();
  }

  private void CanvasGUI()
  {
    // ドット描画
    this.DrawCanvas();

    // マウスクリック処理
    var e = Event.current;
    switch (e.type)
    {
      case EventType.MouseDrag:
      case EventType.MouseDown:
        {
          var pos = e.mousePosition;
          int x = Mathf.FloorToInt(pos.x / canvasScale);
          int y = Mathf.FloorToInt(pos.y / canvasScale);

          // クリックしたドットの色を塗り替える
          if (x >= 0 && x < canvasX && y >= 0 && y < canvasY)
          {
            this.canvas[x, y].texture = brushColor;
          }
        }
        break;
    }
  }

  private void PalettesGUI()
  {
    GUI.Label(paletteLabel, "Palette");

    // 選択中のパレットをカーソルで囲む
    GUI.DrawTexture(this.paletteCursorOut, Palette.textureCursorOut);
    GUI.DrawTexture(this.paletteCursorIn, Palette.textureCursorIn);

    // パレット描画
    for (int i = 0; i < palettes.Length; i++)
    {
      GUI.DrawTexture(palettes[i].rect, palettes[i].texture);
    }

    // マウスクリック処理
    var e = Event.current;
    switch (e.type)
    {
      case EventType.MouseDown:
        for (int i = 0; i < palettes.Length; i++)
        {
          // パレットをクリックしたらブラシの色を切り替える
          var p = palettes[i]; 
          if (p.rect.Contains(e.mousePosition))
          {
            this.SelectPalette(i);
            break;
          }
        }
        break;
    }

  }

  // ブラシの色選択
  private void SelectPalette(int index)
  {
    Palette p = this.palettes[index];
    paletteCursorOut = new Rect(p.rect);
    paletteCursorOut.xMin -= cursorWidth * 2;
    paletteCursorOut.xMax += cursorWidth * 2;
    paletteCursorOut.yMin -= cursorWidth * 2;
    paletteCursorOut.yMax += cursorWidth * 2;

    paletteCursorIn = new Rect(p.rect);
    paletteCursorIn.xMin -= cursorWidth;
    paletteCursorIn.xMax += cursorWidth;
    paletteCursorIn.yMin -= cursorWidth;
    paletteCursorIn.yMax += cursorWidth;

    this.brushColor = p.texture;
  }

  // ドットの描画
  private void DrawCanvas()
  {
    GUI.DrawTexture(new Rect(0, 0, this.position.width, this.position.height), Palette.textureBG);
    for (int x = 0; x < canvasX; x++)
    {
      for (int y = 0; y < canvasY; y++)
      {
        Pixel p = this.canvas[x, y];
        var texture = p.texture;

        if (texture != null)
        {
          GUI.DrawTexture(p.GetRect(canvasScale, gridSize), p.texture);
        }
      }
    }
  }

  // ドット
  private class Pixel
  {
    public Texture texture;
    public int x;
    public int y;

    // コンストラクタ
    public Pixel(Texture texture, int x, int y)
    {
      this.texture = texture;
      this.x = x;
      this.y = y;
    }

    public Rect GetRect(int canvasScale, int gridSize)
    {
      return new Rect(this.x * canvasScale, this.y * canvasScale, canvasScale - gridSize, canvasScale - gridSize);
    }
  }

  // パレット
  private class Palette
  {
    public static readonly Texture2D textureWhite = CreateTexture(RGB(255, 255, 255));
    public static readonly Texture2D textureBlack = CreateTexture(RGB(0, 0, 0));
    public static readonly Texture2D textureRed = CreateTexture(RGB(255, 0, 0));
    public static readonly Texture2D textureGreen = CreateTexture(RGB(0, 255, 0));
    public static readonly Texture2D textureBlue = CreateTexture(RGB(0, 0, 255));
    public static readonly Texture2D textureYellow = CreateTexture(RGB(255, 50, 0));
    public static readonly Texture2D textureGray = CreateTexture(RGB(40, 40, 40));

    public static readonly Texture2D textureCursorIn = CreateTexture(RGB(0, 0, 0)); // カーソルの内側の色
    public static readonly Texture2D textureCursorOut = CreateTexture(RGB(255, 50, 10)); // カーソルの外側の色
    public static readonly Texture2D textureBG = CreateTexture(RGB(60, 60, 60)); // 背景色

    private static Color RGB(float r, float g, float b) { return new Color(r / 255.0f, g / 255.0f, b / 255.0f); }

    public Texture2D texture;
    public Rect rect;

    // コンストラクタ
    public Palette(Texture2D texture)
    {
      this.texture = texture;
    }

    private static Texture2D CreateTexture(Color color)
    {
      Texture2D texture = new Texture2D(1, 1);
      texture.SetPixel(0, 0, color);
      texture.Apply();
      return texture;
    }
  }

}

```