【游戏开发实战】TapTap物理画线游戏,教你使用Unity实现2D物理画线功能,看到我为你画的彩虹了吗

一、前言

嗨,大家好,我是新发,我又来科普了,相信很多人玩过物理画线小游戏,比如下面这样子:
在这里插入图片描述
使用Unity如何实现物理画线功能呢?今天就来教大家。

最后的实现效果如下:
在这里插入图片描述
本工程已上传到GitHub,感兴趣的同学可自行下载学习。
GitHub地址:https://github.com/linxinfa/UnityPhysicsDrawLine
在这里插入图片描述

二、思考

物理画线的核心就是:物理+画线。
物理:
想要有物理特性,最简单的做法就是挂碰撞体(Collider)和刚体(Rigidbody)组件。
画线:
可以使用LineRenderer组件来实现画线功能。

三、验证我们的思考

1、创建物体挂组件

创建一个空物体,重命名为Line,挂上EdgeCollider2DRigidbody2DLineRenderer组件。
在这里插入图片描述

2、设置组件参数

设置一下LineRenderer组件的参数。
Position:坐标点;
Width:线宽度;
Color:线颜色(支持渐变);
Corner Vertices:拐弯处的顶点数量(让拐弯圆滑一点);
End Cap Vertices:线段头尾的顶点数量(让线段头尾圆滑一点);
Use World Space:是否使用世界坐标(不要勾选);
Materias:材质球。
在这里插入图片描述

设置一下EdgeCollider2D组件的参数。
Edge Radius:边界碰撞体的半径。
Points:边界碰撞体的坐标点(要与LineRenderer的点一致)。
在这里插入图片描述

边界碰撞体设置完之后效果如下:
在这里插入图片描述
最后是Rigidbody2D组件的参数。主要是Gravity Scale:重力缩放值;这个值越大,物体受到的重力越大,掉落的加速度就越大。
加粗样式

3、运行测试

为了模拟掉落到地上的效果,我们加个地面。
在这里插入图片描述
运行测试效果如下:
在这里插入图片描述

4、结论

理论存在,实践验证成功。那么,接下来就是如何使用代码来实现鼠标画线了,关键的点就是把鼠标的坐标设置为线的点。

四、撸起袖子写代码

两个脚本,一个Line.cs负责线段的绘制,一个LinesDrawer.cs负责检测鼠标和生成线段与点。

1、Line.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 线脚本
/// </summary>
public class Line : MonoBehaviour
{
    public LineRenderer lineRenderer;
    public EdgeCollider2D edgeCollider;
    public Rigidbody2D rigidBody;

    /// <summary>
    /// 点数组
    /// </summary>
    [HideInInspector] public List<Vector2> points = new List<Vector2>();
    [HideInInspector] public int pointCount = 0;

    /// <summary>
    /// 画线过程中点与点的最小距离
    /// </summary>
    float pointsMinDistance = 0.1f;

    float circleColliderRadius;

    /// <summary>
    /// 添加点
    /// </summary>
    /// <param name="newPoint"></param>
    public void AddPoint(Vector2 newPoint)
    {
        if (pointCount >= 1 && Vector2.Distance(newPoint, GetLastPoint()) < pointsMinDistance)
            return;

        points.Add(newPoint);
        ++pointCount;

        // 添加圆形碰撞
        var circleCollider = this.gameObject.AddComponent<CircleCollider2D>();
        circleCollider.offset = newPoint;
        circleCollider.radius = circleColliderRadius;


        // Line Renderer
        lineRenderer.positionCount = pointCount;
        lineRenderer.SetPosition(pointCount - 1, newPoint);


        // 边界碰撞体的点
        if (pointCount > 1)
            edgeCollider.points = points.ToArray();
    }


    /// <summary>
    /// 获取最后一个点
    /// </summary>
    /// <returns></returns>
    public Vector2 GetLastPoint()
    {
        return lineRenderer.GetPosition(pointCount - 1);
    }

    /// <summary>
    /// 是否启用物理特性
    /// </summary>
    public void UsePhysics(bool usePhysics)
    {
        rigidBody.isKinematic = !usePhysics;
    }

    /// <summary>
    /// 设置线颜色
    /// </summary>
    /// <param name="colorGradient"></param>
    public void SetLineColor(Gradient colorGradient)
    {
        lineRenderer.colorGradient = colorGradient;
    }

    /// <summary>
    /// 设置画线的点与点之间的最小距离
    /// </summary>
    /// <param name="distance"></param>
    public void SetPointsMinDistance(float distance)
    {
        pointsMinDistance = distance;
    }

    /// <summary>
    /// 设置线宽度
    /// </summary>
    /// <param name="width"></param>
    public void SetLineWidth(float width)
    {
        lineRenderer.startWidth = width;
        lineRenderer.endWidth = width;

        circleColliderRadius = width / 2f;
        edgeCollider.edgeRadius = circleColliderRadius;
    }
}
2、LinesDrawer.cs
using UnityEngine;

/// <summary>
/// 画线控制器
/// </summary>
public class LinesDrawer : MonoBehaviour
{
    public GameObject linePrefab;

    public LayerMask cantDrawOverLayer;
    int cantDrawOverLayerIndex;

    [Space(30)]
    public Gradient lineColor;
    public float linePointsMinDistance;
    public float lineWidth;

    Line currentLine;
    Camera cam;

    private void Start()
    {
        cam = Camera.main;
        cantDrawOverLayerIndex = LayerMask.NameToLayer("CantDrawOver");
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
            BeginDraw();
        if (null != currentLine)
            Draw();
        if (Input.GetMouseButtonUp(0))
            EndDraw();
    }

    // 画线逻辑-----------------------------------------------------------------------

    // 开始画线
    void BeginDraw()
    {
        // 实例化线预设
        currentLine = Instantiate(linePrefab, this.transform).GetComponent<Line>();
        // 设置参数
        currentLine.UsePhysics(false);
        currentLine.SetLineColor(lineColor);
        currentLine.SetPointsMinDistance(linePointsMinDistance);
        currentLine.SetLineWidth(lineWidth);


    }

    // 画线进行中
    void Draw()
    {
        var pos = cam.ScreenToWorldPoint(Input.mousePosition);
        // 防止线与线之间交叉
        RaycastHit2D hit = Physics2D.CircleCast(pos, lineWidth / 3f, Vector2.zero, 1f, cantDrawOverLayer);
        if (hit)
            EndDraw();
        else
            currentLine.AddPoint(pos);
    }

    // 画线结束
    void EndDraw()
    {
        if (null == currentLine) return;
        if (currentLine.pointCount < 2)
        {
            Destroy(currentLine.gameObject);
        }
        else
        {
            currentLine.gameObject.layer = cantDrawOverLayerIndex;
            currentLine.UsePhysics(true);
            currentLine = null;
        }
    }
}

五、场景

将原来的Line保存为预设,并挂上Line脚本,赋值对应的变量。
在这里插入图片描述
添加一个LayerCantDrawOver,目的是防止画线的时候线与线交叉(也可以防止线与其他被标记为CantDrawOver层的物体交叉)。
在这里插入图片描述
在场景中创建一个空物体,重命名为LineDrawer,并挂上LineDrawer脚本,赋值对应的参数。
在这里插入图片描述

六、最终运行效果

在这里插入图片描述

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林新发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值