方块类型 BlockType
枚举(墙、O字型、I字型、S字型、Z字型、L字形、J字形、T字形)
public enum BlockType
{
Wall,
O,
I,
S,
Z,
L,
J,
T
}
绘制地图 MapManager
地图分为
固定墙壁:始终不动
动态墙壁:可以消除的墙壁
功能分为:
绘制地图(绘制左墙、右墙、底墙)
添加动态墙壁(什么时候添加动态墙布)
消除某行
难点1:某行满了,怎么消除
做一个line数组,数组的大小等于方块的高度。
line[0]表示第1行的数量,如果数量等于宽度-2,则说明这一行满了
满了之后,消除这一行,这一行上面的动态砖块下移,同时line数组也要进行偏移。
可能出现两行或者3行同时满,那么我们可以使用递归进行。
using System;
using System.Collections.Generic;
using UnityEngine;
public class MapManager : MonoSingleton<MapManager>
{
[SerializeField] Transform wallParent;
public int Width;
public int Height;
public Vector2Int originPos = new Vector2Int(-30, -5);
private List<Transform> staticWalls = new List<Transform>();
public List<Transform> dynamicWalls = new List<Transform>();
int[] lines;
private void Start()
{
lines = new int[Height-1];
DrawMaps();
}
private void DrawMaps()
{
// 1. 绘制左墙
for(int i = 0; i < Height - 1; i++)
{
GenerateWall(originPos.x, originPos.y + 1 + i);
}
// 2. 绘制右墙
for(int i = 0; i < Height - 1; i++)
{
GenerateWall(originPos.x + Width - 1, originPos.y + 1 + i);
}
// 3. 绘制底墙
for(int i = 0; i < Width; i++)
{
GenerateWall(originPos.x + i, originPos.y);
}
}
private void GenerateWall(int x, int y)
{
Transform walltf = ResourcesManager.Instance.GenerateBlock(BlockType.Wall,x,y,wallParent);
staticWalls.Add(walltf);
}
public void AddWalls(List<Transform> walls)
{
for(int i = 0; i < walls.Count; i++)
{
int y = (int)walls[i].position.y;
if(RelativeOriginPosY(y) >= lines.Length)
{
print("Game Over");
Time.timeScale = 0f;
return;
}
ResourcesManager.Instance.SetColor(BlockType.Wall, walls[i]);
dynamicWalls.Add(walls[i]);
SetLine(y);
}
CheckClear();
}
private void CheckClear()
{
for(int i = 0; i < lines.Length; i++)
{
if (lines[i] == Width - 2)
{
ClearLine(i);
// 整体下移
FallOne(i);
// line下移
LineFall(i);
CheckClear();
break;
}
}
}
private void LineFall(int y)
{
for(int i = y +1 ; i < lines.Length; i++)
{
lines[i-1] = lines[i];
}
}
private void FallOne(int relativeY)
{
for(int i = 0; i< dynamicWalls.Count; i++)
{
int x = (int)dynamicWalls[i].position.x;
int y = (int)dynamicWalls[i].position.y;
if(RelativeOriginPosY(y) > relativeY)
{
dynamicWalls[i].position = new Vector3(x, --y);
}
}
}
private void ClearLine(int y)
{
List<Transform> delLine = new List<Transform>();
for(int i = 0; i < dynamicWalls.Count; i++)
{
if (RelativeOriginPosY((int)(dynamicWalls[i].position.y)) == y)
{
delLine.Add(dynamicWalls[i]);
}
}
for(int i = 0; i < delLine.Count; i++)
{
dynamicWalls.Remove(delLine[i]);
Destroy(delLine[i].gameObject);
}
}
private void SetLine(int y)
{
int relativeY = RelativeOriginPosY(y);
lines[relativeY]++;
}
private int RelativeOriginPosY(int y)
{
return y + Mathf.Abs(originPos.y) - 1;
}
}
方块信息BlockInfo
存储各种方块各种形态的信息
using System.Collections.Generic;
using UnityEngine;
public class BlockInfo
{
List<Vector2Int[]> list = new List<Vector2Int[]>();
public BlockInfo(BlockType blockType)
{
switch (blockType)
{
case BlockType.O:
// 只有一种状态
list.Add(new Vector2Int[]
{
new Vector2Int(1, 0),
new Vector2Int(0, -1),
new Vector2Int(1, -1),
new Vector2Int(0, 0)
});
break;
case BlockType.I:
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 0),
new Vector2Int(1, 0),
new Vector2Int(2, 0),
new Vector2Int(0, 0)
});
list.Add(new Vector2Int[]
{
new Vector2Int(0, 1),
new Vector2Int(0, -1),
new Vector2Int(0, -2),
new Vector2Int(0, 0)
});
break;
case BlockType.Z:
list.Add(new Vector2Int[]
{
new Vector2Int(-1,1),
new Vector2Int(0, 1),
new Vector2Int(1, 0),
new Vector2Int(0, 0)
});
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 0),
new Vector2Int(0, 1),
new Vector2Int(-1, -1),
new Vector2Int(0, 0)
});
break;
case BlockType.S:
list.Add(new Vector2Int[]
{
new Vector2Int(-1,0),
new Vector2Int(0, 1),
new Vector2Int(1, 1),
new Vector2Int(0, 0)
});
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 0),
new Vector2Int(-1, 1),
new Vector2Int(0, -1),
new Vector2Int(0, 0)
});
break;
case BlockType.T:
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 1),
new Vector2Int(0, 1),
new Vector2Int(1, 1),
new Vector2Int(0, 0)
});
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 0),
new Vector2Int(0, 1),
new Vector2Int(0, -1),
new Vector2Int(0, 0)
});
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 0),
new Vector2Int(1, 0),
new Vector2Int(0, 1),
new Vector2Int(0, 0)
});
list.Add(new Vector2Int[]
{
new Vector2Int(0, 1),
new Vector2Int(0, -1),
new Vector2Int(1, 0),
new Vector2Int(0, 0)
});
break;
case BlockType.L:
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 1),
new Vector2Int(-1, 0),
new Vector2Int(0, 1),
new Vector2Int(1, 1)
});
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 1),
new Vector2Int(0, 0),
new Vector2Int(0, 1),
new Vector2Int(0, -1)
});
list.Add(new Vector2Int[]
{
new Vector2Int(1, 1),
new Vector2Int(-1, 0),
new Vector2Int(1, 0),
new Vector2Int(0, 0)
});
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 0),
new Vector2Int(-1, 1),
new Vector2Int(-1, -1),
new Vector2Int(0, -1)
});
break;
case BlockType.J:
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 1),
new Vector2Int(0, 1),
new Vector2Int(1, 1),
new Vector2Int(1, 0)
});
list.Add(new Vector2Int[]
{
new Vector2Int(0, 0),
new Vector2Int(0, 1),
new Vector2Int(0, -1),
new Vector2Int(-1, -1)
});
list.Add(new Vector2Int[]
{
new Vector2Int(0, 0),
new Vector2Int(-1, 0),
new Vector2Int(1, 0),
new Vector2Int(-1, 1)
});
list.Add(new Vector2Int[]
{
new Vector2Int(-1, 0),
new Vector2Int(-1, 1),
new Vector2Int(-1, -1),
new Vector2Int(0, 1)
});
break;
default:
break;
}
}
public Vector2Int[] this[int index]
{
get {
if (index < 0) return list[0];
else if(index >= list.Count) return list[list.Count - 1];
else return list[index];
}
}
public int Count { get { return list.Count; }}
}
BlockWorker类
功能有:
随机创建一个方块(7种类型)
方块变形
方块变形前,判断是否能够变形
方块左右移动
方块左右移动前,判断是否能够移动
方块向下移动
方块向下移动前,判断是否能够移动
using System.Collections.Generic;
using UnityEngine;
public class BlockWorker : MonoBehaviour
{
Dictionary<BlockType, BlockInfo> blocksDic = new Dictionary<BlockType, BlockInfo>();
List<Transform> blocks = new List<Transform>();
Vector2Int birthPos;
BlockInfo currentBlock;
int currentBlockIndex = 0;
float nextUpdate;
[SerializeField] float fallSpeed;
BlockType currentBlockType = BlockType.O;
// 基准点
Vector2Int originPos = Vector2Int.zero;
private void Start()
{
SetBirthPos();
InitDic();
RandomBlock();
}
private void SetBirthPos()
{
MapManager maps = MapManager.Instance;
int x = maps.originPos.x + maps.Width / 2;
int y = maps.originPos.y + maps.Height + 2;
birthPos = new Vector2Int(x, y);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (CanChageShape(MapManager.Instance))
{
ChangeShape();
}
}
if(Input.GetKeyDown(KeyCode.LeftArrow) ||
Input.GetKeyDown(KeyCode.A))
{
if (CanMoveLR(Vector2Int.left, MapManager.Instance))
{
Move(Vector2Int.left);
}
}else if (Input.GetKeyDown(KeyCode.RightArrow) ||
Input.GetKeyDown(KeyCode.D))
{
if (CanMoveLR(Vector2Int.right, MapManager.Instance))
{
Move(Vector2Int.right);
}
}
}
private void FixedUpdate()
{
if (Time.time < nextUpdate) return;
if(CanFallMove(MapManager.Instance))
Move(Vector2Int.down);
nextUpdate = Time.time + (1 / fallSpeed);
}
private void Move(Vector2Int direction)
{
originPos += direction;
for(int i = 0; i <blocks.Count; i++)
{
int x = (int)blocks[i].position.x + direction.x;
int y = (int)blocks[i].position.y + direction.y;
blocks[i].position = new Vector2(x, y);
}
}
private bool CanMoveLR(Vector2Int direction, MapManager maps)
{
// 检测墙
for (int i = 0; i < blocks.Count; i++)
{
int x = (int)blocks[i].position.x + direction.x;
int y = (int)blocks[i].position.y + direction.y;
if(x <= maps.originPos.x || x >= maps.originPos.x + maps.Width - 1
|| y <= maps.originPos.y)
{
return false;
}
for(int j = 0; j < maps.dynamicWalls.Count; j++)
{
if (maps.dynamicWalls[j].position.x == x &&
maps.dynamicWalls[j].position.y == y)
{
return false;
}
}
}
return true;
}
private bool CanFallMove(MapManager maps)
{
for (int i = 0; i < blocks.Count; i++)
{
int x = (int)blocks[i].position.x + Vector2Int.down.x;
int y = (int)blocks[i].position.y + Vector2Int.down.y;
if (y <= maps.originPos.y)
{
maps.AddWalls(blocks);
RandomBlock();
return false;
}
for (int j = 0; j < maps.dynamicWalls.Count; j++)
{
if (maps.dynamicWalls[j].position.x == x &&
maps.dynamicWalls[j].position.y == y)
{
maps.AddWalls(blocks);
RandomBlock();
return false;
}
}
}
return true;
}
private void InitDic()
{
blocksDic.Add(BlockType.I, new BlockInfo(BlockType.I));
blocksDic.Add(BlockType.O, new BlockInfo(BlockType.O));
blocksDic.Add(BlockType.S, new BlockInfo(BlockType.S));
blocksDic.Add(BlockType.L, new BlockInfo(BlockType.L));
blocksDic.Add(BlockType.T, new BlockInfo(BlockType.T));
blocksDic.Add(BlockType.Z, new BlockInfo(BlockType.Z));
blocksDic.Add(BlockType.J, new BlockInfo(BlockType.J));
}
private void RandomBlock()
{
blocks.Clear();
currentBlockType = (BlockType)Random.Range(1, 8);
currentBlock = blocksDic[currentBlockType];
originPos = birthPos;
for (int i = 0; i < currentBlock[currentBlockIndex].Length; i++) {
int x = currentBlock[currentBlockIndex][i].x + originPos.x;
int y = currentBlock[currentBlockIndex][i].y + originPos.y;
blocks.Add(GenerateBlock(x, y));
}
}
private void ChangeShape()
{
currentBlockIndex = (currentBlockIndex + 1) % currentBlock.Count;
for (int i = 0; i < currentBlock[currentBlockIndex].Length; i++)
{
int x = currentBlock[currentBlockIndex][i].x + originPos.x;
int y = currentBlock[currentBlockIndex][i].y + originPos.y;
blocks[i].position = new Vector2(x, y);
}
}
private bool CanChageShape(MapManager maps)
{
int tempIndex = (currentBlockIndex + 1) % currentBlock.Count;
for (int i = 0; i < currentBlock[tempIndex].Length; i++)
{
int x = currentBlock[tempIndex][i].x + originPos.x;
int y = currentBlock[tempIndex][i].y + originPos.y;
if (x <= maps.originPos.x || x >= maps.originPos.x + maps.Width - 1
|| y <= maps.originPos.y)
{
return false;
}
for (int j = 0; j < maps.dynamicWalls.Count; j++)
{
if (maps.dynamicWalls[j].position.x == x &&
maps.dynamicWalls[j].position.y == y)
{
return false;
}
}
}
return true;
}
private Transform GenerateBlock(int x, int y)
{
return ResourcesManager.Instance.GenerateBlock(currentBlockType, x, y);
}
}
ResourcesManager
实例化生成对象
using UnityEngine;
public class ResourcesManager:MonoSingleton<ResourcesManager>
{
private const string RES_PATH = "Square";
public Transform GenerateBlock(BlockType blockType, int x, int y, Transform parent = null)
{
Transform blockgo = Resources.Load<Transform>(RES_PATH);
Transform block = Instantiate(blockgo, parent);
SetColor(blockType, block);
block.localPosition = new Vector2(x, y);
return block;
}
public void SetColor(BlockType blockType, Transform block)
{
switch (blockType)
{
case BlockType.Wall:
block.GetComponent<SpriteRenderer>().color = Color.red;
break;
case BlockType.O:
block.GetComponent<SpriteRenderer>().color = Color.white;
break;
case BlockType.I:
block.GetComponent<SpriteRenderer>().color = Color.yellow;
break;
case BlockType.S:
block.GetComponent<SpriteRenderer>().color = Color.cyan;
break;
case BlockType.Z:
block.GetComponent<SpriteRenderer>().color = Color.magenta;
break;
case BlockType.L:
block.GetComponent<SpriteRenderer>().color = Color.gray;
break;
case BlockType.J:
block.GetComponent<SpriteRenderer>().color = Color.grey;
break;
case BlockType.T:
block.GetComponent<SpriteRenderer>().color = Color.yellow;
break;
default:
break;
}
}
}
MonoSingleton类
using UnityEngine;
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
private static T m_Instance = null;
// 在设计阶段 写脚本 没有挂载物体上 希望脚本 单例模式
public static T instance
{
get
{
if (m_Instance == null)
{
// 从场景中查找
m_Instance = GameObject.FindObjectOfType(typeof(T)) as T;
if (m_Instance == null)
{
// 1. 直接创建一个游戏对象
// 2. 为这个游戏对象添加脚本组件
m_Instance = new GameObject("Singleton of " + typeof(T).ToString(), typeof(T)).GetComponent<T>();
// 初始化
m_Instance.Init();
}
}
return m_Instance;
}
}
// 设计阶段,写脚本,挂在物体上
// 项目运行时【系统会帮助我们将脚本类实例化为对象(new 脚本)】
// 项目运行时 在Awake时,在场景中找到唯一实例,记录在m_Instance中
private void Awake()
{
if (m_Instance == null)
{
m_Instance = this as T;
}
}
// 提供初始化的一种选择,可以使用Init、Start都可以
public virtual void Init() { }
// 当程序退出做清理工作! 单例模式 = null
private void OnApplicationQuit()
{
m_Instance = null;
}
}
评论区