Unity中,画出任意形状的带有物理碰撞的线
2022-03-25 本文已影响0人
全新的饭
示意
画出带有物理碰撞的线.gif思路
滑屏时持续生成LineRenderer的点,每隔一小段时间,生成相应的2D矩形碰撞体。
具体实现
Brush提供3个方法供外界调用画线:
- 开始画线:生成LineRenderer
- 刷新线的数据:增加line的点、创建碰撞
- 结束画线:添加刚体
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();
}
}
}