前几天接了一个拼图项目刚好现在写完了,拿出来分享,拼图不难,我也是看了一个官方案例写的,因为当我们写图片跟随鼠标的时候,鼠标已经有一个图片了,这个图片会遮挡射线,然后就无法判断当前拼图块在哪里,话不多说,上菜
1、新建总控脚本LevelManager
public enum State
{
None,
mousedown,
mousemove,
mouseup,
}
public PuzzleDivision PuzzleDivision;
public State state;
private void Awake()
{
ins = this;
Random.InitState((int)System.DateTime.Now.Ticks);
}
void Start()
{
state = State.None;
}
2、新建PuzzleDivision脚本,用于初始化拼图块的克隆和选区
public class PuzzleDivision : MonoBehaviour
{
public int size = 3;
public List<Grid1> grids = new List<Grid1>();
public Grid gridPrefab;
public Grid1 gridPrefab1;
public void Init(Transform tra)
{
for (int i = 1; i <= size; i++)
{
for (int j = 1; j <= size; j++)
{
if ((i - 1) * size + j > grids.Count)
{
Grid1 grids2 = Instantiate(gridPrefab1, gridLayoutGroup.transform);
grids2.transform.name = "Start" + i;
grids2.id = i;
grids2.size = size;
grids.Add(grids2);
Grid grid = Instantiate(gridPrefab, tra);
LevelManager.ins.grids.Add(grid);
}
else
{
grids[i].gameObject.SetActive(true);
LevelManager.ins.grids[i].gameObject.SetActive(true);
}
grids[(i - 1) * size + j - 1].SetInf(this, size, new Vector2(i, j), Texture2D);
LevelManager.ins.grids[(i - 1) * size + j - 1].SetInf(this, size, new Vector2(i, j), Texture2D);
}
}
if (grids.Count > size * size)
{
for (int i = size * size; i < grids.Count; i++)
{
grids[i].gameObject.SetActive(false);
}
}
for (int i = 0; i < grids.Count; i++)
{
grids[i].transform.GetComponent<RawImage>().color = new Color32(255, 255, 255, 27);
}
if (LevelManager.ins.grids.Count > size * size)
{
for (int i = size * size; i < LevelManager.ins.grids.Count; i++)
{
LevelManager.ins.grids[i].gameObject.SetActive(false);
}
}
}
}
分割图片只有RawImage才能使用,所以这里用的是RawImage,并且注意Grid1是上半部分的图
Grid是下半部分,也就是可以互动的图,如图
下面是Grid的脚本:
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using UnityEngine.EventSystems;
using System;
public class Grid: MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
Action Action;
private PuzzleDivision game;
private RawImage image;
private Image bj;
public Vector3 movepos;
public float rotesize;
public int id;
public int size;
public bool cheng = false;
Vector3 pos;
private void Awake()
{
Input.multiTouchEnabled = false;
image = GetComponent<RawImage>();
bj = GetComponent<Image>();
}
/// <summary>
/// 设置当前Grid的信息
/// </summary>
/// <param name="size">n*n</param>
/// <param name="id">Grid的ID</param>
public void SetInf(PuzzleDivision game, int size, Vector2 id, Texture2D sp)
{
image.texture = sp;
this.game = game;
float width = 1f / size;
gameObject.name = $"Grid_{id.x}_{id.y}";
image.uvRect = new Rect(
(id.x - 1) * width,
(id.y - 1) * width,
width,
width
);
switch (size)
{
case 3:
transform.GetComponent<RectTransform>().sizeDelta = new Vector2(394, 395);
break;
case 4:
transform.GetComponent<RectTransform>().sizeDelta = new Vector2(298, 299);
break;
default:
break;
}
}
public void OnBeginDrag(PointerEventData eventData)//开始拖拽
{
LevelManager.ins.name = transform.name;
transform.GetComponent<CanvasGroup>().blocksRaycasts = false;
eventData.pointerDrag.GetComponent<RectTransform>().transform.DORotate(new Vector3(0,0,0),0.01f);
LevelManager.ins.state = LevelManager.State.mousedown;
}
public void OnDrag(PointerEventData eventData)//拖拽中
{
//throw new System.NotImplementedException();
transform.position = Input.mousePosition;
eventData.pointerDrag.transform.GetComponent<RawImage>().transform.SetAsLastSibling();
LevelManager.ins.state = LevelManager.State.mousemove;
}
public void OnEndDrag(PointerEventData eventData)//停止拖拽
{
//throw new System.NotImplementedException();
transform.GetComponent<CanvasGroup>().blocksRaycasts = true;
Debug.Log(eventData?.pointerDrag.name);//当前物体
//Debug.Log(eventData.pointerEnter.name);//鼠标抬起后的物体
if (eventData.pointerEnter!=null)
{
if (eventData.pointerEnter.name == eventData.pointerDrag.name)
{
switch (size)
{
case 3:
eventData.pointerDrag.transform.GetComponent<RectTransform>().sizeDelta = new Vector2(394, 395);
AudioManager.ins.audios(3);
break;
case 4:
eventData.pointerDrag.transform.GetComponent<RectTransform>().sizeDelta = new Vector2(298, 299);
AudioManager.ins.audios(3);
break;
default:
break;
}
pos = eventData.pointerEnter.transform.position;
if (Input.GetMouseButtonUp(0))
{
eventData.pointerDrag.transform.position = eventData.pointerEnter.transform.position;
if (Vector3.Distance(pos, transform.position) <= 0.01)
{
cheng = true;
transform.GetComponent<Grid>().enabled = false;
Debug.LogError("当前手指数量:" + Input.touchCount);
}
}
if (Input.touchCount<=1&&Input.touchCount>0)
{
eventData.pointerDrag.transform.position = eventData.pointerEnter.transform.position;
if (Vector3.Distance(pos, transform.position) <= 0.01)
{
cheng = true;
transform.GetComponent<Grid>().enabled = false;
Debug.LogError("当前手指数量:"+Input.touchCount);
}
}
LevelManager.ins.sum += 1;
}
else
{
eventData?.pointerDrag.GetComponent<RectTransform>().transform.DOLocalMove(movepos, 0.25f);
eventData?.pointerDrag.GetComponent<RectTransform>().transform.DORotate(new Vector3(0, 0, rotesize), 0.1f);
}
}
LevelManager.ins.state = LevelManager.State.mouseup;
}
private void Update()
{
if (Input.touchCount>0)
{
if (Input.GetTouch(0).phase == TouchPhase.Ended)
{
LevelManager.ins.state = LevelManager.State.mouseup;
}
}
Action = LevelManager.ins.state switch
{
LevelManager.State.None => new Action(Nonetest),
LevelManager.State.mousedown => new Action(mousedowntest),
LevelManager.State.mousemove => new Action(mousemovetest),
LevelManager.State.mouseup => new Action(mouseuptest),
_ => throw new ArgumentOutOfRangeException(nameof(LevelManager.state))
};
Action.Invoke();
}
void mouseuptest()
{
Debug.Log("输出鼠标抬起回调");
if (!cheng)
{
transform.DOLocalMove(movepos, 0.25f);
}
LevelManager.ins.state = LevelManager.State.None;
}
}
因为是webGL的项目,面对的bug解决方案也不同,代码可能有点乱,大家将就看看,webGL不支持禁用多点触控,所以在这里用了比较复杂的判定,不然直接一句话的事,而且策划说在松手状态下,拼图块要回到原位,并且要旋转到原来的位置,我当时听到这个真是mmp了(小声bb一句,狗策划)
在Grid中size是设置拼图分割的块数,输入3,就分割成3*3的,输入4就是4*4,输入多少都可以,你输入一百也没事(其实我也不知道有没有事)
SetInf()是分割方法
OnBeginDrag()是开始拖拽方法,到这步有聪明的小明就要问了,那我这个拼图块也要占用射线,我应该怎么判定拼图块是否到对应的位置了呢,问的好,用transform.GetComponent<CanvasGroup>().blocksRaycasts = false;
解决,这个是用于禁用当前的射线交互的,简单点说,射线可以直接穿过当前拼图块,这样就可以用来检测了,这个是官方的做法,我记得哪个插件里有示例,我也是根据那个示例来的,这个:
UI Samples
OnDrag()是拖拽中的方法
OnEndDrag()是停止拖拽的方法
我们是拼图游戏,所有图块都是在松手后判定,所以写在OnEndDrag()中的东西会多一点,在OnEndDrag()中可以写你自己的判定逻辑,当然,也可以在其他方法里写这个就不多赘述,Update()中主要是判定屏幕中是否有手指,至于这个switch则是C#的新语法糖
注意!switch语法糖有时候在unity中可能会报错,因为mono对C#的支持不太好,还没更新到最新的语法糖
Action = LevelManager.ins.state switch
{
LevelManager.State.None => new Action(Nonetest),
LevelManager.State.mousedown => new Action(mousedowntest),
LevelManager.State.mousemove => new Action(mousemovetest),
LevelManager.State.mouseup => new Action(mouseuptest),
_ => throw new ArgumentOutOfRangeException(nameof(LevelManager.state))
};
至于mouseuptest()则是松手后拼图块的归位
Grid1脚本如下:
using DG.Tweening;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class Grid1 : MonoBehaviour, IDropHandler, IPointerEnterHandler, IPointerExitHandler
{
private PuzzleDivision game;
private RawImage image;
[SerializeField] Image bj;
public int id;
public int size;
Tween tween;
private void Awake()
{
image = GetComponent<RawImage>();
}
/// <summary>
/// 设置当前Grid1的信息
/// </summary>
/// <param name="size">n*n</param>
/// <param name="id">Grid��ID</param>
public void SetInf(PuzzleDivision game, int size, Vector2 id,Texture2D sp)
{
image.texture = sp;
this.game = game;
float width = 1f / size;
gameObject.name = $"Grid_{id.x}_{id.y}";
image.uvRect = new Rect(
(id.x - 1) * width,
(id.y - 1) * width,
width,
width
);
switch (size)
{
case 3:
bj.transform.GetComponent<RectTransform>().sizeDelta = new Vector2(394 - 10, 395 - 10);
break;
case 4:
bj.transform.GetComponent<RectTransform>().sizeDelta = new Vector2(298 - 10, 299 - 10);
break;
default:
break;
}
}
private void Update()
{
switch (LevelManager.ins.state)
{
case LevelManager.State.None:
HIde();
break;
case LevelManager.State.mousedown:
break;
case LevelManager.State.mousemove:
break;
case LevelManager.State.mouseup:
break;
default:
break;
}
}
public void Show()
{
bj.DOFade(1, 0.01f);
//bj.gameObject.SetActive(true);
tween = bj.DOFade(0, 0.25f).SetLoops(-1, LoopType.Yoyo);
AudioManager.ins.audios(2);
}
public void HIde()
{
tween.Kill();
bj.DOFade(1, 0.01f);
}
public void OnDrop(PointerEventData eventData)//鼠标抬起
{
Debug.Log("鼠标抬起");
HIde();
}
public void OnPointerEnter(PointerEventData eventData)//鼠标移入
{
Debug.Log("鼠标移入");
if (LevelManager.ins.state == LevelManager.State.mousemove)
{
Show();
}
}
public void OnPointerExit(PointerEventData eventData)//鼠标移出
{
//throw new System.NotImplementedException();
if (LevelManager.ins.state == LevelManager.State.mousemove)
{
HIde();
}
Debug.Log("鼠标移出");
}
}
Grid1就简单多了,Grid1主要用于上半部分的对比图展示,所以要判定鼠标是否移入、移出这种,这里就不做赘述,我们接着往下走
将Grid1和Grid做成预设,面板上是这样的:
我这个套了个Image,是因为策划说要增加虚线,表示当前拖拽状态,我也是无语了文章来源:https://www.toymoban.com/news/detail-480501.html
下面给你们看看示例 文章来源地址https://www.toymoban.com/news/detail-480501.html
到了这里,关于用unity写一个2D类的拼图游戏的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!