IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity基于LineRender划线解决方案 -> 正文阅读

[游戏开发]Unity基于LineRender划线解决方案

Unity基于LineRender划线解决方案

划线类

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mode_1;

public class LinesDrawer : MonoBehaviour
{
    public bool isDrawCmp { get { return _isDrawCmp; } }

    public float linePointsMinDistance;
    public float linePointsMinAngle;
    public float lineWidth;
    public Color lineColor;
    public float lineMaxLength;

    private Transform linePre;
    [HideInInspector]
    public Line currentLine;
    private LayerMask cantDrawOverLayer;

    private bool isLastHit;
    private Transform hitDraw;

    private bool _isDrawCmp;

    private void Start()
    {
        cantDrawOverLayer = LayerMask.GetMask("CantDrawOver");
        linePre = transform.Find("LinePre");
        hitDraw = transform.Find("HitDraw");
    }

    private void Update()
    {
        if (_isDrawCmp) return;

#if UNITY_ANDROID && !UNITY_EDITOR
        if (Input.touchCount == 1)
        {
            if (Input.GetTouch(0).phase == TouchPhase.Began)
                BeginDraw();
            if (Input.GetTouch(0).phase == TouchPhase.Moved)
                Draw();
            if (Input.GetTouch(0).phase == TouchPhase.Ended)
                EndDraw();
        }
#endif

#if UNITY_EDITOR
        if (Input.GetMouseButtonDown(0))
            BeginDraw();
        if (Input.GetMouseButton(0))
            Draw();
        if (Input.GetMouseButtonUp(0))
            EndDraw();
#endif

    }

    private void BeginDraw()
    {
        currentLine = Instantiate(linePre, transform).GetComponent<Line>();
        currentLine.gameObject.name = "Line";
        currentLine.InitData();
        currentLine.UsePhysics(false);
        currentLine.SetPointsMinDistance(linePointsMinDistance);
        currentLine.SetPiontsMinAngle(linePointsMinAngle);
        currentLine.SetLineWidth(lineWidth);
        currentLine.SetColor(lineColor);
        var pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        currentLine.AddPoint(pos);
        SoundMgr.Ins.PlayEffect(E_EffectClip.Draw, true);
    }

    private void Draw()
    {
        if (currentLine == null) return;
        if (currentLine.curLength >= lineMaxLength)
        {
            EndDraw();
            return;
        }
        var pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);

        Vector2 origin = currentLine.GetLastPoint();
        Vector2 vec = (Vector2)pos - origin;
        RaycastHit2D hit = Physics2D.CircleCast(origin, lineWidth, vec.normalized, vec.magnitude, cantDrawOverLayer);
        if (hit)
        {
            isLastHit = true;
            hitDraw.gameObject.SetActive(true);
            HitDraw(origin, pos);
        }
        else
        {
            if (isLastHit)
            {
                isLastHit = false;
                hitDraw.gameObject.SetActive(false);
            }
            currentLine.AddPoint(pos);
            EventMgr.Ins.ExecuteEvent(E_EventType.ON_LINE_CHANGE, currentLine.curLength / lineMaxLength);
        }
    }

    private void EndDraw()
    {
        SoundMgr.Ins.StopEffect(E_EffectClip.Draw);
        if (currentLine == null) return;
        if (currentLine.pointCount < 2)
        {
            hitDraw.gameObject.SetActive(false);
            Destroy(currentLine.gameObject);
            return;
        }
        _isDrawCmp = true;
        //currentLine.gameObject.layer = cantDrawOverLayerIndex;
        hitDraw.gameObject.SetActive(false);
        if (currentLine)
        {
            currentLine.UsePhysics(true);
            currentLine.AddColliders();
            //currentLine = null;
        }
        OnDrawEndEvent();
    }

    private void HitDraw(Vector2 origin, Vector2 pos)
    {
        Vector2 vecTarget = pos - origin;
        hitDraw.transform.position = vecTarget.normalized * vecTarget.magnitude / 2 + origin;

        if (vecTarget.y >= 0)
            hitDraw.transform.eulerAngles = new Vector3(0, 0, Vector2.Angle(Vector2.right, vecTarget));
        else
            hitDraw.transform.eulerAngles = new Vector3(0, 0, 360 - Vector2.Angle(Vector2.right, vecTarget));

        hitDraw.GetComponent<SpriteRenderer>().size = new Vector2(vecTarget.magnitude, lineWidth);
    }

    private void OnDrawEndEvent()
    {
        LvBase baseCtr = GetComponentInParent<LvBase>();
        if (baseCtr)
        {
            baseCtr.OnDrawEndEvent();
        }
    }
}

line类

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


public class Line : MonoBehaviour
{
    private LineRenderer lineRenderer;
    private EdgeCollider2D edgeCollider;
    private Rigidbody2D rigidBody;

    [HideInInspector] public List<Vector2> points = new List<Vector2>();
    [HideInInspector] public int pointCount = 0;
    [HideInInspector] public float curLength;

    float pointsMinAngle = 90f;
    float pointsMinDistance = 0.1f;
    float circleColliderRadius;
    bool isUsePhysics = false;

    private void Update()
    {
        if (isUsePhysics)
        {
            for (int i = 0; i < lineRenderer.positionCount; i++)
            {
                Vector3 pos = transform.TransformPoint(points[i]);
                lineRenderer.SetPosition(i, pos);
            }
        }
    }


    public void AddPoint(Vector2 newPoint)
    {
        if (pointCount == 0)
        {
            points.Add(newPoint);
            ++pointCount;
            lineRenderer.positionCount = pointCount;
            lineRenderer.SetPosition(pointCount - 1, newPoint);
        }
        else
        {
            if (Vector2.Distance(newPoint, GetLastPoint()) < pointsMinDistance)
            {
                return;
            }
            if (pointCount >= 2)
            {
                if (Vector2.Angle(newPoint, GetLastPoint()) <= pointsMinAngle)
                {
                    Vector2 lastNormal = (lineRenderer.GetPosition(pointCount - 1) - lineRenderer.GetPosition(pointCount - 2)).normalized;
                    Vector2 curNormal = ((Vector3)newPoint - lineRenderer.GetPosition(pointCount - 1)).normalized;

                    Vector2 p0 = lineRenderer.GetPosition(pointCount - 1);
                    Vector2 p1 = (Vector2)lineRenderer.GetPosition(pointCount - 1) + lastNormal * 0.01f;
                    Vector2 p2 = (Vector2)lineRenderer.GetPosition(pointCount - 1) + curNormal * 0.01f;
                    List<Vector3> vecs = new List<Vector3>();
                    vecs.Add(p0);
                    vecs.Add(p1);
                    vecs.Add(p2);
                    vecs = InitByBezier(vecs, 10);
                    for (int i = 1; i < vecs.Count; i++)
                    {
                        ++pointCount;
                        lineRenderer.positionCount = pointCount;
                        lineRenderer.SetPosition(pointCount - 1, vecs[i]);
                        points.Add(vecs[i]);
                    }
                }
            }
            curLength += Vector2.Distance(newPoint, GetLastPoint());
            points.Add(newPoint);
            ++pointCount;
            lineRenderer.positionCount = pointCount;
            lineRenderer.SetPosition(pointCount - 1, newPoint);
        }
        //if (pointCount > 1)
        //{
        //    edgeCollider.points = points.ToArray();
        //}
        //var circleCollider = gameObject.AddComponent<CircleCollider2D>();
        //circleCollider.offset = newPoint;
        //circleCollider.radius = circleColliderRadius;
    }
    public void AddColliders()
    {
        if (pointCount > 1)
        {
            edgeCollider.points = points.ToArray();
        }
        var circleCollider = gameObject.AddComponent<CircleCollider2D>();
        circleCollider.offset = points[0];
        circleCollider.radius = circleColliderRadius;

        var circleCollider2 = gameObject.AddComponent<CircleCollider2D>();
        circleCollider2.offset = points[points.Count - 1];
        circleCollider2.radius = circleColliderRadius;
    }

    public Vector2 GetLastPoint()
    {
        return lineRenderer.GetPosition(pointCount - 1);
    }

    public void InitData()
    {
        rigidBody = GetComponent<Rigidbody2D>();
        lineRenderer = GetComponent<LineRenderer>();
        edgeCollider = GetComponent<EdgeCollider2D>();
        lineRenderer.sortingOrder = 10;
        gameObject.SetActive(true);
    }

    public void UsePhysics(bool usePhysics)
    {
        isUsePhysics = usePhysics;
        rigidBody.isKinematic = !usePhysics;
    }

    public void SetPointsMinDistance(float distance)
    {
        pointsMinDistance = distance;
    }

    public void SetPiontsMinAngle(float angle)
    {
        pointsMinAngle = angle;
    }

    public void SetLineWidth(float width)
    {
        lineRenderer.startWidth = width;
        lineRenderer.endWidth = width;

        circleColliderRadius = width / 2f;
        edgeCollider.edgeRadius = circleColliderRadius / 2;
    }

    public void SetColor(Color color)
    {
        lineRenderer.startColor = color;
        lineRenderer.endColor = color;
    }

    public void SetCollisionDetection(bool isContinuous)
    {
        GetComponent<Rigidbody2D>().collisionDetectionMode = isContinuous ? CollisionDetectionMode2D.Continuous : CollisionDetectionMode2D.Discrete;
    }


    private List<Vector3> InitByBezier(List<Vector3> wayPoint, int pointCount)
    {
        List<Vector3> tempList = new List<Vector3>();
        Vector3 vecPoint = Vector3.zero;
        for (int i = 0; i <= pointCount; i++)
        {
            vecPoint = Bezier(i / (float)pointCount, wayPoint);
            tempList.Add(vecPoint);
        }
        return tempList;
    }

    private Vector3 Bezier(float t, List<Vector3> points)
    {
        if (points.Count < 2)
            return points[0];

        List<Vector3> list = new List<Vector3>();
        Vector3 p0p1 = Vector3.zero;
        for (int i = 0; i < points.Count - 1; i++)
        {
            p0p1 = (1 - t) * points[i] + t * points[i + 1];
            list.Add(p0p1);
        }
        return Bezier(t, list);
    }
}

主要实现

定义层级CantDrawOver为不可画的区域 实现思路是使用Physics2D.CircleCast 从上一个点到当前划线点的圆形路径射线检测,如果被检测到有不可划线层级  以虚线形式提示。

linerender实际使用的时候,发现如果两帧之间的点夹角小于90度的时候 如果此时转角处的点数量过于少,并且两帧之间的点间距过大。线的宽度会出现问题。所以在上一个点 以上上个点到上一个点的方向 和 当前上个点到当前点的方向  做了0.01单位的延伸 确定了两个点 然后做贝塞尔曲线补点。

关于划线碰撞器的问题 发现网上有很多解决方案 大部分是用的多边形 个人感觉多边形画出来的碰撞器还是有点棱角 最后选择了用Edge碰撞器并在首尾两个点加入两个同样宽度的圆形碰撞器(如果不加好像首尾会穿透刚体)

下面会贴Unity界面截图 不习惯打注释 代码有部分参考网上别的大佬写的 能用的就直接拿来用了。欢迎提问交流。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-11-30 15:56:52  更:2021-11-30 15:58:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/29 6:52:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码