前言
策划在游戏里的一个关卡采用一笔画类似的玩法,我在调查发现关于一笔画的玩法在网上很少。
一笔画有很多实现方法,以下这种方法是我自己的一个实现方法。
目前这个交互方式是点击每个点来连线(策划需求),而要想拖动连线的话可以使用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的其他接口来实现。
要善于分解然后考虑每个要素的特点以及实现,这个是要我们的观察力以及想法构造,不能只靠学,得自己多动手。
感谢观看到这里,祝你我在学习路上更上一层楼!!!