用unity自己制作连连看小教程
周末终于有了属于自己的时间,我花了不少时间完善了之前做的连连看游戏,顺便写个小教程分享给大家。
题外话:北京的冬天实在太冷了,冻得我手脚冰凉。
之前上传的工程文件和exe包现已更新,增加了画线功能,并且修改了边缘不能拐点的问题。
连通的类型
连连看游戏中,两个相同图片的连通类型主要有以下三种:
- 直线型:分为横向和竖向两种情况。
- 一个拐点:当两个点的x坐标和y坐标都不相同时,检测是否可以通过一个拐点连接。
- 两个拐点:从一个点开始,横向和竖向找出所有能直线连接的点,用这些点和另一个点做一个拐点的检测。
直线型连接
直线型连接分为横向和竖向两种情况:
- 横向连接:若A、B两点的x坐标相同,且图片类型相同,从A点开始到B点进行检测,如果A、B两点之间没有其他图片,则销毁A、B两个图片。
- 竖向连接:竖向连接与横向连接类似。
一个拐点连接
当A、B两点的x坐标和y坐标都不相同时,开始检测一个拐点是否可以连接。通过A、B两点计算出C、D两点,然后分别检测AC、BC、AD、BD是否可以通过直线型连接到一起。若可以,则A、B两点可以通过A > C, C > B连接到一起。
两个拐点连接
对于A、B两点,从A点开始,横向和竖向把所有和A能直线连接的点找出来,用这些点和B点做一个拐点的检测。例如,A点下边的某个点可以通过绿色的那个点以一个拐点的方式和B点连接起来。
以上就是连连看游戏的核心内容。接下来,我将以注释的形式详细介绍各个脚本的作用。
脚本介绍
GameManager.cs
游戏的核心代码,负责产生图片、判断是否可以销毁等操作。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GameManager : MonoBehaviour
{
public DrawLine drawLine; // 画线
public GameObject tilePrefab; // tile的预制
public List<Tile> tiles; // 开始实例化的时候存放tile
public List<Tile> _tiles; // 存放随机摆放的tile
public List<Tile> tilesEdge; // 为了边界处可以有拐点,把棋盘四周的tile放在这里,这里的tile看不到
public int x, y; // 棋牌的大小,两个数必须是偶数
private Tile tileA;
private Tile tileB;
private bool destroy;
private Vector3 mousePos;
private enum stepType // 控制游戏的状态
{
one,
two,
three
}
private stepType _stepType;
void Start ()
{
this.gameObject.transform.position = Vector3.zero;
Spawn ();
_stepType = stepType.one;
}
private void Spawn () // 实例化tile
{
float num = (x * y - (2 * x + 2 * y - 4)) * 0.5f;
for (int i = 0; i < num; i ++) {
int idTex = Random.Range (20, 45);
GameObject obj = Instantiate (tilePrefab) as GameObject;
GameObject obj2 = Instantiate (tilePrefab) as GameObject;
Tile tile = obj.GetComponent<Tile> ();
Tile tile2 = obj2.GetComponent<Tile> ();
tile.Init (idTex);
tile2.Init (idTex);
tiles.Add (tile);
tiles.Add (tile2);
}
for (int i = 0; i < ((2 * x + 2 * y) - 4); i++) { // 实例化边缘的tile
GameObject obj = Instantiate (tilePrefab) as GameObject;
obj.name = "edage";
Tile tile = obj.GetComponent<Tile> ();
tilesEdge.Add (tile);
}
CreatTile ();
for (int i = 0; i < _tiles.Count; i++) {
_tiles [i].transform.name = i.ToString ();
_tiles [i].id = i;
}
this.transform.position = new Vector3 (-(x / 2.0f - 0.5f), -(y / 2.0f - 0.5f), 0);
}
private void CreatTile () // 随机摆放tile,如果是边缘的就放在边缘位置
{
float _y = 0.0f;
for (int i = 0; i < y; i ++) {
float _x = 0.0f;
for (int j = 0; j < x; j ++) {
if (i == 0 || j == 0 || i == y - 1 || j == x - 1) {
tilesEdge [0].transform.position = new Vector3 (_x, _y, 0);
tilesEdge [0].pos = new Vector2 (_x, _y);
tilesEdge [0].transform.rotation = new Quaternion (0, 0, 180, 0);
tilesEdge [0].transform.parent = this.gameObject.transform;
_tiles.Add (tilesEdge [0]);
tilesEdge [0].transform.localScale = Vector3.zero;
tilesEdge [0].type = false;
tilesEdge.RemoveAt (0);
} else {
int id = Mathf.FloorToInt (Random.Range (0, tiles.Count));
tiles [id].transform.position = new Vector3 (_x, _y, 0);
tiles [id].pos = new Vector2 (_x, _y);
tiles [id].transform.rotation = new Quaternion (0, 0, 180, 0);
tiles [id].transform.parent = this.gameObject.transform;
_tiles.Add (tiles [id]);
tiles.RemoveAt (id);
}
_x += 1;
}
_y += 1;
}
}
private void SelectTile () // 开始选择图片,通过射线方式选中,如果tileA和tileB不相同,则tileA等于tileB开始下一个检测
{
Ray ray = Camera.main.ScreenPointToRay (mousePos);
RaycastHit hit;
int mask = 1 << 8;
if (Physics.Raycast (ray, out hit, mask)) {
if (tileA == null) {
tileA = hit.transform.GetComponent<Tile> ();
tileA.SetTileTexture (1);
} else {
tileB = hit.transform.GetComponent<Tile> ();
tileB.SetTileTexture (1);
Compare (tileA, tileB);
if (tileA == null || tileB == null) {
// print ("a and b is null");
}
}
}
}
private void Compare (Tile tile1, Tile tile2) // 比较两个点是否可以连接到一起
{
// same card
_stepType = stepType.one;
drawLine.waypoints.Add (tile1.transform); // 第一个选择的tile是画线的起始位置
drawLine.transform.position = tile1.transform.position;
destroy = false;
print ("compare");
if (tile1.pos.x == tile2.pos.x && tile1.pos.y == tile2.pos.y) { // 如果选中的是同一个图片返回
tileA.SetTileTexture (0);
tileA = tileB;
tileB = null;
return;
} else if (tile1.pos.x == tile2.pos.x) { // 如果两点的x相等,竖向检测
print ("check y");
destroy = CheckY (tile1, tile2);
if (destroy)
drawLine.waypoints.Add (tile2.transform);
} else if (tile1.pos.y == tile2.pos.y) { // 如果两点的y相等,横向检测
print ("check x");
destroy = CheckX (tile1, tile2);
if (destroy)
drawLine.waypoints.Add (tile2.transform);
}
if (!destroy) { // 不符合直线连接方式的开始进行一个拐点的检测
_stepType = stepType.two;
destroy = CheckTwoStep (tile1, tile2);
print ("check two step");
if (!destroy) { // 不符合直线和一个拐点检测的开始进行两个拐点的检测
_stepType = stepType.three;
destroy = CheckThreeStep (tile1, tile2);
print ("check three:" + destroy);
print ("tile1.idTex:" + tile1.idTex + "tile1.idTex:" + tile1.idTex);
}
}
if (destroy) { // 如果符合销毁条件销毁图片,并开始画线
tile1.transform.localScale = Vector3.zero;
tile2.transform.localScale = Vector3.zero;
tile1.type = false;
tile2.type = false;
tileA = null;
tileB = null;
drawLine.MoveToWaypoint ();
} else { // 不符合的话,清除画线中的路径
drawLine.ClearPath ();
tileA.SetTileTexture (0);
tileA = tileB;
tileB = null;
return;
}
}
// one step横向检测
private bool CheckX (Tile a, Tile b)
{
bool compare = true;
int _min, _max;
if (a.idTex == b.idTex) {
CompareID (a, b, out _min, out _max);
_min += 1;
if (_min == _max)
return true;
for (int i = _min; i < _max; i++) {
if (_tiles [i].type == true) {
compare = false;
break;
}
}
return compare;
} else
return false;
}
// 竖向检测
private bool CheckY (Tile a, Tile b)
{
bool compare = true;
int _min, _max;
if (a.idTex == b.idTex) {
CompareID (a, b, out _min, out _max);
_min += x;
if (_min == _max)
return true;
for (int i = _min; i < _max; i += x) {
if (_tiles [i].type == true) {
compare = false;
break;
}
}
return compare;
} else
return false;
}
// two step一个拐点的检测
private bool CheckTwoStep (Tile a, Tile b)
{
if (a.pos.x == b.pos.x || a.pos.y == b.pos.y)
return false;
int id1 = (int)(a.pos.y * x + b.pos.x);
if (_tiles [id1].type == false) {
_tiles [id1].idTex = a.idTex;
if (CheckY (_tiles [id1], b)) {
if (CheckX (a, _tiles [id1])) {
if (_stepType == stepType.two) {
drawLine.waypoints.Add (_tiles [id1].transform);
drawLine.waypoints.Add (b.transform);
} else if (_stepType == stepType.three) {
drawLine.waypoints.Add (_tiles [id1].transform);
print ("=====================:" + 1);
}
return true;
}
}
}
int id2 = (int)(b.pos.y * x + a.pos.x);
if (_tiles [id2].type == false) {
_tiles [id2].idTex = b.idTex;
if (CheckY (a, _tiles [id2])) {
if (CheckX (b, _tiles [id2])) {
if (_stepType == stepType.two) {
drawLine.waypoints.Add (_tiles [id2].transform);
drawLine.waypoints.Add (b.transform);
} else if (_stepType == stepType.three) {
drawLine.waypoints.Add (_tiles [id2].transform);
print ("=====================:" + 2);
}
return true;
}
}
}
return false;
}
// three step两个拐点的检测
private bool CheckThreeStep (Tile a, Tile b)
{
print ("a:" + a.idTex + "b:" + b.idTex);
bool returnValue = false;
print ("returnValue:" + returnValue);
List<Tile> _comparrPointsB;
ComparePoints (b, out _comparrPointsB); // 返回b点可以横竖直线相连的点
for (int i = 0; i < _comparrPointsB.Count; i++) {
returnValue = CheckTwoStep (a, _comparrPointsB [i]);
if (returnValue) {
drawLine.waypoints.Add (_comparrPointsB [i].transform);
drawLine.waypoints.Add (b.transform);
return returnValue;
}
}
if (!returnValue) {
List<Tile> _comparrPointsA;
ComparePoints (a, out _comparrPointsA);
print (a.name);
print (b.name);
for (int i = 0; i < _comparrPointsA.Count; i++) {
print ("--------------" + b.idTex);
returnValue = CheckTwoStep (b, _comparrPointsA [i]);
if (returnValue) {
drawLine.waypoints.Add (_comparrPointsA [i].transform);
drawLine.waypoints.Add (b.transform);
return returnValue;
}
}
}
return returnValue;
}
// 两个拐点的时候返回可以与a横竖直线相连的点
private void ComparePoints (Tile a, out List<Tile> comparePoints)
{
print ("a.idtex" + a.idTex);
comparePoints = new List<Tile> ();
comparePoints.Clear ();
for (int i = (int)a.pos.y - 1; i > -1; i--) {
int id = (int)(i * x + a.pos.x);
if (_tiles [id].type == false) {
comparePoints.Add (_tiles [id]);
_tiles [id].idTex = a.idTex;
print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
} else
break;
}
for (int i = (int)a.pos.y + 1; i < y; i++) {
int id = (int)(i * x + a.pos.x);
if (_tiles [id].type == false) {
comparePoints.Add (_tiles [id]);
_tiles [id].idTex = a.idTex;
print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
} else
break;
}
for (int i = (int)a.pos.x - 1; i > -1; i--) {
int id = (int)(a.pos.y * x + i);
if (_tiles [id].type == false) {
comparePoints.Add (_tiles [id]);
_tiles [id].idTex = a.idTex;
print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
} else
break;
}
for (int i = (int)a.pos.x + 1; i < x; i++) {
int id = (int)(a.pos.y * x + i);
if (_tiles [id].type == false) {
comparePoints.Add (_tiles [id]);
_tiles [id].idTex = a.idTex;
print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
} else
break;
}
}
private void CompareID (Tile a, Tile b, out int min, out int max)
{
if (a.id < b.id) {
min = a.id;
max = b.id;
} else {
min = b.id;
max = a.id;
}
}
Vector2 TexSize ()
{
Vector2 size = new Vector2 (1 / x, 1 / y);
return size;
}
Vector2 TexOffset (int _idTex)
{
int a = (int)(_idTex / x);
int b = (int)(_idTex % x);
Vector2 offset = new Vector2 (b / x, (y - 1 - a) / y);
return offset;
}
void Update ()
{
if (Input.GetMouseButtonUp (0)) {
mousePos = Input.mousePosition;
SelectTile ();
}
}
private void ClearTiles (List<Tile> tiles)
{
tiles.Clear ();
}
}
DrawLine.cs
画线脚本,使用了iTween。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class DrawLine : MonoBehaviour
{
public List<Transform> waypoints = new List<Transform> ();
public float rate = 1;
private int currentWaypoint = 1;
public void MoveToWaypoint ()
{
print ("public void MoveToWaypoint (): " + waypoints.Count);
StartCoroutine ("move");
}
public void ClearPath ()
{
waypoints.Clear ();
print ("path.Clear ();");
}
IEnumerator move ()
{
for (int i = 0; i < waypoints.Count; i++) {
iTween.MoveTo (this.gameObject, waypoints [i].position, rate);
print ("now id:" + i);
yield return new WaitForSeconds(rate);
}
waypoints.Clear ();
}
}
Tile.cs
using UnityEngine;
using System.Collections;
public class Tile : MonoBehaviour
{
public int id;
public int idTex; // 通过这个判断两个图片是否相同
public Vector2 pos;
public bool type = true; // 控制图片的状态,当销毁的时候为false,其他判断的时候可以通过该点
public float x, y;
public Texture texA, texB; // 鼠标选中的时候可以换贴图
public GameObject mask; // 鼠标选中的时候上边显示的框框
public void Init (int _idTex)
{
idTex = _idTex;
Vector2 offset = TexOffset (_idTex);
this.renderer.material.SetTextureOffset ("_MainTex", offset);
this.renderer.material.SetTextureScale ("_MainTex", new Vector2 (0.2f, 0.1f));
}
// 设置tile显示的贴图和大小
public void SetTileTexture (int i)
{
if (i == 0) {
this.renderer.material.mainTexture = texA;
mask.transform.localScale = Vector3.zero;
}
if (i == 1) {
this.renderer.material.mainTexture = texB;
mask.transform.localScale = new Vector3 (0.1380835f, 0.1380835f, 0.1380835f);
}
}
// 这个就是裁剪一张大图,变成一个个小的,贴到tile上
Vector2 TexOffset (int _idTex)
{
int a = (int)(_idTex / x);
int b = (int)(_idTex % x);
Vector2 offset = new Vector2 (b / x, (y - 1 - a) / y);
return offset;
}
Vector2 TexSize ()
{
Vector2 size = new Vector2 (1 / x, 1 / y);
return size;
}
}
Menu.cs
添加了两个按钮,可以重新开始游戏。
using UnityEngine;
using System.Collections;
public class Menu : MonoBehaviour
{
public GameManager gameManager;
private GameManager _gameManger;
private bool start = true;
void OnGUI ()
{
if (start) {
if (GUI.Button (new Rect (10, 10, 100, 50), "start")) {
start = false;
_gameManger = Instantiate (gameManager) as GameManager;
}
}
if (GUI.Button (new Rect (10, 70, 100, 50), "restart")) {
if (_gameManger != null) {
Destroy (_gameManger.gameObject);
print ("Destroy(_gameManger.gameObject);");
}
_gameManger = Instantiate (gameManager) as GameManager;
}
}
}
以上就是使用Unity制作连连看游戏的详细教程,希望对大家有所帮助。