【Unity】LineRendererを使ってお絵かきソフトにある機能を実装する

f:id:coffee_ryo:20180506190912p:plain

概要

現在開発中の2Dアクションゲームにてお絵かきのシステムをギミックとして取り入れたかったので実装しました。

環境

作る機能

  • 線を引く
  • 対称定規で線を引く

LineRendererについて

Unityにあるコンポーネントです。2点以上の位置情報を設定することで、点と点を繋いで線を引いてくれます。 「1つの繋がった線につきLineRendererが取り付けられたオブジェクトが1つ必要」です。

大体の処理の流れ

【マウスクリック時】

  • 線オブジェクトを生成し、線の太さや色を設定する

【Update】

  • プレイヤーのマウスの位置を取得
  • LineRendererのSetPositionメソッドを使い位置情報を登録していく

線を引く機能を実装する

スクリプティングをしてコンポーネントを作ります。

using UnityEngine;
using System.Collections.Generic;
using System.Linq;

public class FreeHand : MonoBehaviour {

    /// <summary>
    /// 描く線のコンポーネントリスト
    /// </summary>
    private List<LineRenderer> lineRendererList;

    /// <summary>
    /// 描く線のマテリアル
    /// </summary>
    public Material lineMaterial;

    /// <summary>
    /// 描く線の色
    /// </summary>
    public Color lineColor;

    /// <summary>
    /// 描く線の太さ
    /// </summary>
    [Range(0,10)] public float lineWidth;


    void Awake () {
        lineRendererList = new List<LineRenderer>();
    }

    void Update () {

        // ボタンが押された時に線オブジェクトの追加を行う
        if (Input.GetMouseButtonDown(0)) {
            this.AddLineObject();
        }

        // ボタンが押されている時、LineRendererに位置データの設定を指定していく
        if (Input.GetMouseButton(0)) {
            this.AddPositionDataToLineRendererList();
        }
    }

    /// <summary>
    /// 線オブジェクトの追加を行うメソッド
    /// </summary>
    private void AddLineObject () {

        // 追加するオブジェクトをインスタンス
        GameObject lineObject = new GameObject();

        // オブジェクトにLineRendererを取り付ける
        lineObject.AddComponent<LineRenderer>();

        // 描く線のコンポーネントリストに追加する
        lineRendererList.Add(lineObject.GetComponent<LineRenderer>());

        // 線と線をつなぐ点の数を0に初期化
        lineRendererList.Last().positionCount = 0;

        // マテリアルを初期化
        lineRendererList.Last().material = this.lineMaterial;

        // 線の色を初期化
        lineRendererList.Last().material.color = this.lineColor;

        // 線の太さを初期化
        lineRendererList.Last().startWidth = this.lineWidth;
        lineRendererList.Last().endWidth   = this.lineWidth;
    }

    /// <summary>
    /// 描く線のコンポーネントリストに位置情報を登録していく
    /// </summary>
    private void AddPositionDataToLineRendererList () {

        // 座標の変換を行いマウス位置を取得
        Vector3 screenPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.nearClipPlane + 1.0f);
        var mousePosition = Camera.main.ScreenToWorldPoint(screenPosition);

        // 線と線をつなぐ点の数を更新
        lineRendererList.Last().positionCount += 1;

        // 描く線のコンポーネントリストを更新
        lineRendererList.Last().SetPosition(lineRendererList.Last().positionCount - 1, mousePosition);
    }
}

空のゲームオブジェクトをヒエラルキー上で作って、上記のコンポーネントをアタッチします。 f:id:coffee_ryo:20180506184102p:plain

線の太さは「LineWidth」で変更できます。とりあえず0.2くらいで設定しておきます。 線の色は「LineColor」で変更できます。 線の色の変更の為に「LineMaterial」の設定が必要なので、プロジェクト上でCreate > Material、Shaderを「Unlit / Color」としてコンポーネントに取り付けます。

ヒエラルキー上のMainCameraのBackgroundを白色にして、動作させてみます。 f:id:coffee_ryo:20180506184419g:plain

対称定規で線を引く

スクリプティングをしてコンポーネントを作ります。 LineRendererの位置の設定をする際に、UnityのQuaternionを使います。

using UnityEngine;
using System.Collections.Generic;
using System.Linq;

public class SymmetricRuler : MonoBehaviour {

    /// <summary>
    /// 描く線のコンポーネントリスト
    /// </summary>
    private List<List<LineRenderer>> lineRendererList;

    /// <summary>
    /// 対称定規で1回に描く線の本数
    /// </summary>
    public int rulerLineCount;

    /// <summary>
    /// 描く線のマテリアル
    /// </summary>
    public Material lineMaterial;

    /// <summary>
    /// 描く線の色
    /// </summary>
    public Color lineColor;

    /// <summary>
    /// 描く線の太さ
    /// </summary>
    [Range(0,10)] public float lineWidth;


    void Awake () {
        lineRendererList = new List<List<LineRenderer>>();
    }

    void Update () {

        // ボタンが押された時に線オブジェクトの追加を行う(複数個作る)
        if (Input.GetMouseButtonDown(0)) {
            this.AddLineObject();
        }

        // ボタンが押されている時、LineRendererに位置データの設定を指定していく(複数のLineRendererにデータを設定)
        if (Input.GetMouseButton(0)) {
            this.AddPositionDataToLineRendererList();
        }
    }

    /// <summary>
    /// 線オブジェクトの追加を行うメソッド
    /// </summary>
    private void AddLineObject () {

        lineRendererList.Add(new List<LineRenderer>());

        for(int i = 0; i < rulerLineCount; i ++){
            // 追加するオブジェクトをインスタンス
            GameObject lineObject = new GameObject();

            // オブジェクトにLineRendererを取り付ける
            lineObject.AddComponent<LineRenderer>();

            // 描く線のコンポーネントリストに追加する
            lineRendererList.Last().Add(lineObject.GetComponent<LineRenderer>());

            // 線と線をつなぐ点の数を0に初期化
            lineRendererList.Last().Last().positionCount = 0;

            // マテリアルを初期化
            lineRendererList.Last().Last().material = this.lineMaterial;

            // 線の色を初期化
            lineRendererList.Last().Last().material.color = this.lineColor;

            // 線の太さを初期化
            lineRendererList.Last().Last().startWidth = this.lineWidth;
            lineRendererList.Last().Last().endWidth   = this.lineWidth;
        }
    }

    /// <summary>
    /// 描く線のコンポーネントリストに位置情報を登録していく
    /// </summary>
    private void AddPositionDataToLineRendererList () {

        // 座標の変換を行いマウス位置を取得
        Vector3 screenPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.nearClipPlane + 1.0f);
        var mousePosition = Camera.main.ScreenToWorldPoint(screenPosition);

        for(int i = 0; i < rulerLineCount; i ++){

            // 線と線をつなぐ点の数を更新
            lineRendererList.Last()[i].positionCount += 1;

            // 描く線のコンポーネントリストを更新
            lineRendererList.Last()[i].SetPosition(lineRendererList.Last()[i].positionCount - 1, Quaternion.Euler (0f,  0f, i * 360 / rulerLineCount) * mousePosition);
        }
    }
}

RulerLineCountを適当に8くらいに設定して、線を描いてみます。

f:id:coffee_ryo:20180506190637g:plain

その他

  • ヒエラルキー上の線オブジェクトを消せば線単位での取り消しができますので、スクリプト組めばUndo機能も実装できると思います。
  • 後から引いた線をカメラ手前に持ってくる課題や、パフォーマンス面での懸念があります。