Unity 一笔画的玩法实现


前言

策划在游戏里的一个关卡采用一笔画类似的玩法,我在调查发现关于一笔画的玩法在网上很少。
一笔画有很多实现方法,以下这种方法是我自己的一个实现方法。
目前这个交互方式是点击每个点来连线(策划需求),而要想拖动连线的话可以使用IDragHandler的其他接口来实现。


一、分解一笔画玩法

玩法:通过存在的 “线” 通道把所有的 “点” 都连起来

首先,结构是多个点和线相连的几何图;
“线”:只能通过一次的通道。
“点”:是各通道的连接处,也是通关的要素之一。

举例图:
一笔画举例

注意点:
1.在连到的最新“点”判断是否存在没连过的 “线”;
2.连过的“点”虽然可以连,但是是只会经过 “线”通道;
3.每次连一个“点”要判断是否所有的“点”和 “线”都被连接;
4.会使用到 LineRenderer 组件。

二、代码

1.“端口”:Port(每两个点相同端口连接起来就是“线”)

每个点都有一或多个端口,这个端口只能跟别的有相同端口号的相连

	/// <summary>
    /// 端口属性
    /// </summary>
    [Serializable]
    public class Port
    {
        public int portNum; //端口号
        public bool isConnected; //是否被连接
    }
    

2.“点”:Point

含有端口类
变量:
1.暗亮点素材是Ui层为了标明显示出点是否被连接过
2.ports管理该点的所有端口
3.samePort 存储两点间的相同端口号

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

public class Point : MonoBehaviour, IPointerDownHandler
{
    [Header("暗点素材")] public Sprite closeSprite;
    [Header("亮点素材")] public Sprite openSprite;

    public List<Port> ports; //存储点中所有端口数据
    
    private int samePort; //存储两点间的相同端口号

    /// <summary>
    /// 端口属性
    /// </summary>
    [Serializable]
    public class Port
    {
        public int portNum; //端口号
        public bool isConnected; //是否被连接
    }

    /// <summary> 重置点数据 </summary>
    public void ResetPort()
    {
        DimThePoint();
        samePort = 0;
        foreach (var port in ports)
        {
            port.isConnected = false;
        }
    }

    /// <summary>
    /// 每个点被点击的时候
    /// </summary>
    /// <param name="eventData"></param>
    public void OnPointerDown(PointerEventData eventData)
    {
        Debug.Log("OnPointerDown: " + gameObject.name);
        //先判断是否可以为mainPoint并点亮point
        if (transform.parent.gameObject.GetComponent<OneStrokeDraw>().mainPoint == null)
        {
            var oneStrokeDraw = transform.parent.gameObject.GetComponent<OneStrokeDraw>();
            oneStrokeDraw.mainPoint = this.gameObject;
            LightenThePoint();
            
            //划线操作
            oneStrokeDraw.LengthOfLineRenderer++;
            oneStrokeDraw.line.SetVertexCount(oneStrokeDraw.LengthOfLineRenderer);
            oneStrokeDraw.line.SetPosition(0, gameObject.transform.localPosition);
            
        }
        else if (transform.parent.gameObject.GetComponent<OneStrokeDraw>().mainPoint != this.gameObject)
        {
            var mainPoint = transform.parent.gameObject.GetComponent<OneStrokeDraw>().mainPoint;

            Debug.Log(eventData.pointerEnter.gameObject.name);
            if (CanTwoPointsBeConnected(
                mainPoint.GetComponent<Point>(),
                eventData.pointerEnter.gameObject.GetComponent<Point>(), out samePort))
            {
                mainPoint.GetComponent<Point>()
                    .ChangePortIsConnected(samePort);
                eventData.pointerEnter.gameObject.GetComponent<Point>().ChangePortIsConnected(samePort);
                
                Debug.Log(mainPoint.name + " 与 " +
                          eventData.pointerEnter.gameObject.name + " 相连");
                var oneStrokeDraw = transform.parent.gameObject.GetComponent<OneStrokeDraw>();
                
                oneStrokeDraw.nowNumInIsConnected += 2;//记录已连接的端口数
                //传递主Point换下一个点继续判断
                oneStrokeDraw.mainPoint = eventData.pointerEnter.gameObject;
                eventData.pointerEnter.gameObject.GetComponent<Point>().LightenThePoint();

                //划线操作
                //绘制两点间划线
                //设置线段的端点数
                oneStrokeDraw.LengthOfLineRenderer++;
                oneStrokeDraw.line.SetVertexCount(oneStrokeDraw.LengthOfLineRenderer);
                int drawPosition = oneStrokeDraw.LengthOfLineRenderer - 1;
                oneStrokeDraw.line.SetPosition(drawPosition, gameObject.transform.localPosition);
                
                //通关的判断
                if (oneStrokeDraw.CheckAllPointsIsConnected())
                {
                    //TODO 通关处理
                    Debug.Log("成功");
                }
            }
        }
    }

    /// <summary> 检查相连的两个点是否可以连接 </summary>
    public bool CanTwoPointsBeConnected(Point point1, Point point2, out int samePort)
    {
        foreach (var port1 in point1.ports)
        {
            //若两个点存在相同端口号
            if (point2.ports.Exists(port => port.portNum == port1.portNum))
            {
                if (!port1.isConnected) //并且还没被连接(唯一性,一般相同端口号相同状态)
                {
                    samePort = port1.portNum;
                    return true; //则这个端口可以连接
                }
            }
        }

        //不能相连的情况处理
        samePort = 0;
        return false;
    }

    /// <summary> 使该点变亮点 </summary>
    public void LightenThePoint()
    {
        this.GetComponent<Image>().sprite = openSprite;
    }

    /// <summary> 使该点变暗点 </summary>
    public void DimThePoint()
    {
        this.GetComponent<Image>().sprite = closeSprite;
    }

    /// <summary>
    /// 连接点中的端口
    /// </summary>
    /// <param name="portNum">端口号</param>
    public void ChangePortIsConnected(int portNum)
    {
        var portHash = ports.Find(port => port.portNum == portNum);
        portHash.isConnected = true;
    }

}

3.OneStrokeDraw(一笔画:点线管理器)

管理点线及总和的变量,组件LineRenderer,并判断通关条件以及重置方法
变量
1.points 存储所有点数据
2.line 挂载LineRenderer组件
3.isConnectedInSum 端口总数
4.LengthOfLineRenderer 端点个数 (LineRenderer划线用)
5.mainPoint 主节点(当前连到的,要判断的点)
6.nowNumInIsConnected 当前已连接端口数

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

public class OneStrokeDraw : MonoBehaviour
{
    public List<Point> points; //存储所有点数据
    public LineRenderer line;
    private int isConnectedInSum; //端口总数

    private LengthOfLineRenderer = 1;
    private GameObject mainPoint;
    private int nowNumInIsConnected; //当前已连接端口数

    private void Start()
    {
        isConnectedInSum = CountIsConnectedInSum(); //记录端口总数
    }


    /// <summary> 计算端口总数 </summary>
    private int CountIsConnectedInSum()
    {
        int isConnectedNum = 0;
        foreach (var point in points)
        {
            isConnectedNum += point.ports.Count;
        }

        return isConnectedNum;
    }

    /// <summary> 检查所有点是否都完成连接 (每次连接的时候都会判断) </summary>
    public bool CheckAllPointsIsConnected()
    {
        return isConnectedInSum == nowNumInIsConnected; //遍历每个点耗性能,这是优化的方案
    }

    /// <summary> UI层:重置所有点数据 </summary>
    public void ResetAllPoint()
    {
        foreach (var point in points)
        {
            point.ResetPort();
        }

        LengthOfLineRenderer = 0;
        line.SetVertexCount(LengthOfLineRenderer);
        //设置线段的端点数
        // line.SetPosition(0, new Vector3(0, 0, 0));
        mainPoint = null;
        nowNumInIsConnected = 0;
        //取消所有划线
    }
}

总结

以上就是所有要讲的内容,本文仅仅介绍了一笔画的大致实现,目前这个交互方式是点击每个点来连线,而要想拖动连线的话可以使用IDragHandler的其他接口来实现。
要善于分解然后考虑每个要素的特点以及实现,这个是要我们的观察力以及想法构造,不能只靠学,得自己多动手。
感谢观看到这里,祝你我在学习路上更上一层楼!!!

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值