unity常用方法

Unity中,画出任意形状的带有物理碰撞的线

2022-03-25  本文已影响0人  全新的饭

示意

画出带有物理碰撞的线.gif

思路

滑屏时持续生成LineRenderer的点,每隔一小段时间,生成相应的2D矩形碰撞体。

具体实现

Brush提供3个方法供外界调用画线:

  1. 开始画线:生成LineRenderer
  2. 刷新线的数据:增加line的点、创建碰撞
  3. 结束画线:添加刚体
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Brush : MonoBehaviour
{
    [SerializeField]
    private float _createLineColInterval = 0.1f;
    [SerializeField]
    private float _lineWidth = 0.1f;
    [SerializeField]
    private Color _lineColor = Color.yellow;

    private LineRenderer _curLine;
    private Vector2 _previousPoint;

    private InputReceiver _inputReceiver;
    private List<LineRenderer> _lines;
    private void Start()
    {
        Init();
    }
    private void Update()
    {
        if (_inputReceiver != null)
        {
            _inputReceiver.Update();
        }
    }
    private void OnDestroy()
    {
        Destroy();
    }

    private void Init()
    {
        _inputReceiver = new InputReceiver(FindObjectOfType<MyInput>(), this, _createLineColInterval);
        _lines = new List<LineRenderer>();
    }
    private void Destroy()
    {
        if (_inputReceiver != null)
        {
            _inputReceiver?.Destroy();
            _inputReceiver = null;
        }

        _lines.Clear();
        _lines = null;
    }

    private void BeginCreateLine(Vector2 pos)
    {
        _curLine = new GameObject("Line").AddComponent<LineRenderer>();
        _curLine.material = new Material(Shader.Find("Sprites/Default")) { color = _lineColor };
        _curLine.widthMultiplier = _lineWidth;
        _curLine.useWorldSpace = false;
        _curLine.positionCount = 1;
        _curLine.SetPosition(0, pos);

        _curLine.transform.SetParent(transform);

        _previousPoint = pos;
    }
    private void RefreshLineData(Vector2 pos, bool shouldCreateCol)
    {
        if (_previousPoint != pos)
        {
            _curLine.positionCount++;
            _curLine.SetPosition(_curLine.positionCount - 1, pos);

            if (shouldCreateCol)
            {
                BoxCollider2D col = new GameObject(string.Format("Col_{0}_{1}", _curLine.positionCount - 1, _curLine.transform.childCount+1)).AddComponent<BoxCollider2D>();
                col.transform.SetParent(_curLine.transform);
                col.transform.position = (_previousPoint + pos) / 2f;
                Vector2 vec = pos - _previousPoint;
                float angle = Mathf.Atan2(vec.y, vec.x) * Mathf.Rad2Deg;
                col.transform.eulerAngles = Vector3.forward * angle;
                col.size = new Vector2(vec.magnitude, _lineWidth);
                _previousPoint = pos;
            }
        }
    }

    private void EndCreateLine(Vector2 pos)
    {
        if (_curLine.transform.childCount > 0)
        {
            _curLine.gameObject.AddComponent<Rigidbody2D>().useAutoMass = true;
            _lines.Add(_curLine);
            _curLine.gameObject.name = "Line_" + _lines.Count;
            _curLine = null;
        }
        else
        {
            Destroy(_curLine.gameObject);
        }
    }

    private class InputReceiver
    {
        private readonly float CreateLineColInterval;
        private MyInput _myInput;
        private Brush _brush;
        private float _timer;
        private bool ShouldCreateLineCol{ get { return _timer <= 0; } }

        public InputReceiver(MyInput myInput, Brush brush, float createLineColInterval)
        {
            _myInput = myInput;
            _myInput.PointerDownEvent += OnPointerDown;
            _myInput.DragEvent += OnDrag;
            _myInput.PointerUpEvent += OnPointerUp;

            _brush = brush;

            CreateLineColInterval = createLineColInterval;
            _timer = 0;
        }
        public void Destroy()
        { 
            if (_myInput != null)
            {
                _myInput.PointerDownEvent -= OnPointerDown;
                _myInput.DragEvent -= OnDrag;
                _myInput.PointerUpEvent -= OnPointerUp;
                _myInput = null;
            }
            if (_brush != null)
            {
                _brush = null;
            }
        }

        public void Update()
        {
            if (_timer > 0)
            {
                _timer -= Time.deltaTime;
            }
        }

        private void OnPointerDown(Vector2 pos)
        {
            _timer = 0;
            _brush.BeginCreateLine(ScreenToWorldPos(pos));
        }
        private void OnDrag(Vector2 pos)
        {
            _brush.RefreshLineData(ScreenToWorldPos(pos), ShouldCreateLineCol);
            if (ShouldCreateLineCol)
            {
                _timer = CreateLineColInterval;
            }
        }
        private void OnPointerUp(Vector2 pos)
        {
            _brush.EndCreateLine(ScreenToWorldPos(pos));
            _timer = 0;
        }

        private Vector2 ScreenToWorldPos(Vector2 screenPos)
        {
            return Camera.main.ScreenToWorldPoint(screenPos);
        }
    }
}

MyInput:检测单指的按下、滑动、抬起

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

/// <summary>
/// 检测单指的按下、滑动、抬起
/// </summary>
public class MyInput : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
    public event Action<Vector2> PointerDownEvent;
    public event Action<Vector2> DragEvent;
    public event Action<Vector2> PointerUpEvent;

    private int _curPointerId;
    private const int DefaultPointerId = -100;

    private void Start()
    {
        Init();
    }
    private void OnDestroy()
    {
        Destroy();
    }

    public void Init()
    {
        ResetFingerId();
    }

    public void Destroy()
    {}

    private void ResetFingerId()
    {
        _curPointerId = DefaultPointerId;
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        if (_curPointerId == DefaultPointerId)
        {
            _curPointerId = eventData.pointerId;
            PointerDownEvent?.Invoke(eventData.position);
        }
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (eventData.pointerId == _curPointerId)
        {
            DragEvent?.Invoke(eventData.position);
        }
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        if (eventData.pointerId == _curPointerId)
        {
            PointerUpEvent?.Invoke(eventData.position);
            ResetFingerId();
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读