实现功能:4*4的背包格子,有游戏时间和步数显示,游戏开始打乱图片顺序,鼠标拖拽一个图片和另一个图片交换,完成拼图,游戏结束不能拖拽,时间暂停
1.准备素材;
找一张这样400*400的图片,命名img,在unity中改变它的Texture Type为Sprite2D,Sprite Mode改成Multiple,点击Sprite Editor,设置切割的图片大小100*100,应用就可以了,自动把我们的图片切割成了16张小图片
还有一张背景图
2.在场景中创建Panel,把Canvas的名字改成Knapsack,标签也改成Knapsack,Panvel的名字改成ImageList,给其添加一个自动布局组件Grid Layout Group,设置一下CellSize都是100,Spacing都是1,按住Ctrl把锚点设成Center,宽和高都是403,给其添加脚本ImageListManager。
3.在ImageList下创建Image,改名为Cell,标签改成Cell,sprite图片是背景图片,它的大小是100*100,;在Cell下再创建一个Image,大小也是100*100,改名为Food,标签改成Food,添加一个Canvas Group组件用于射线检测,把Cell拖成预设体,场景中删除,把预设体拖给ImageListManager,再给其添加一个脚本FoodController。
4.时间和步数的显示其实很简单了,在一个Panel上添加两个Text,分别把时间参数和步数参数传给这两个Text中的text。
ImageList上挂载的脚本如下:
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
public class ImageListManager : MonoBehaviour
{
//获取所有的图片
public List<Sprite> sprites;
//所有的格子
List<Transform> cells;
//预设体
public GameObject prefab;
//单例
public static ImageListManager Instance;
public int steps;
public float timer;
public Text StepText;
public Text TimerText;
public bool isGameOver;
void Awake()
{
Instance = this;
}
//将图片打乱
void RandomSprites()
{
int num;
Sprite spriteTemp;
for (int i = 0; i < sprites.Count; i++)
{
//从后面随机抽一张图片放到i的位置上
num = Random.Range(i, sprites.Count);
spriteTemp = sprites[i];
sprites[i] = sprites[num];
sprites[num] = spriteTemp;
}
}
//初始化游戏
void Init()
{
cells = new List<Transform>();
//每张图片实例化一个格子,格子用来存放图片
for (int i = 0; i < sprites.Count; i++)
{
Transform cell = ((GameObject)Instantiate(prefab)).transform;
cell.GetChild(0).GetComponent<Image>().sprite = sprites[i];
cell.SetParent(transform);
cell.name = "img_" + i.ToString();
cells.Add(cell);
}
timer = 0;
steps = 0;
isGameOver = false;
}
public bool IsOK()
{
for (int i = 0; i < cells.Count; i++)
{
string name = cells[i].GetChild(0).GetComponent<Image>().sprite.name;
if (cells[i].name != name)
{
return false;
}
}
return true;
}
void Start()
{
RandomSprites();
Init();
print(IsOK());
}
void Update()
{
if (!isGameOver)
{
timer += Time.deltaTime;
}
TimerText.text = "timer: " + ((int)timer).ToString();
StepText.text = "steps: " + steps.ToString();
}
}
Food上挂载的脚本如下:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class FoodController : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
//1.
RectTransform foodRF;
RectTransform knapsackRF;
RectTransform cellRF; //表示物品当前的父物体
//2.拖拽中的新位置
Vector3 foodNewPosition;
//3.原来的位置
Vector3 foodOldPosition;
//4.
CanvasGroup cg;
//5.
bool isDrag;
void Awake()
{
foodRF = GetComponent<RectTransform>();
knapsackRF = GameObject.FindWithTag("Knapsack").GetComponent<RectTransform>();
cg = GetComponent<CanvasGroup>();
}
//开始拖拽
public void OnBeginDrag(PointerEventData eventData)
{
if (!ImageListManager.Instance.isGameOver)
{
//每次开始拖拽前记录它的原始父物体
cellRF = foodRF.parent as RectTransform;
//每次开始拖拽后改变它的父物体为knapsack
foodRF.SetParent(knapsackRF);
//开始拖拽之后,不能被射线检测到
cg.blocksRaycasts = false;
//开始拖拽
isDrag = true;
}
}
//拖拽中
public void OnDrag(PointerEventData eventData)
{
if (isDrag)
{
RectTransformUtility.ScreenPointToWorldPointInRectangle(foodRF, Input.mousePosition, eventData.pressEventCamera, out foodNewPosition);
foodRF.position = foodNewPosition;
}
}
//结束拖拽
public void OnEndDrag(PointerEventData eventData)
{
if (isDrag)
{
GameObject enter = eventData.pointerEnter;
//如果拖拽到空的地方
if (enter == null)
{
//cellRF指的是原来的父物体
SetParent(foodRF, cellRF);
}
else
{
switch (enter.tag)
{
case "Cell":
SetParent(foodRF, enter.transform);
break;
case "Food":
SetParent(foodRF, enter.transform.parent);
SetParent(enter.transform, cellRF);
break;
default:
SetParent(foodRF, cellRF);
break;
}
ImageListManager.Instance.steps++;
if (ImageListManager.Instance.IsOK())
{
ImageListManager.Instance.isGameOver = true;
print("步数" + ImageListManager.Instance.steps);
}
}
//结束拖拽之后,能被射线检测到
cg.blocksRaycasts = true;
//结束拖拽
isDrag = false;
}
}
//建立父子关系的方法
void SetParent(Transform son, Transform parent)
{
son.SetParent(parent);
son.localPosition = Vector3.zero;
}
}