Unity3d制作一个简单粗暴的五子棋
最终效果
效果是这样的gif动图展示
项目源码
先把源码贴这
https://download.csdn.net/download/qq_33789001/15743651
绘制棋盘
绘制构思
先定一个白色背景,然后盘由黑色的线绘制,
15*15的棋盘 就需要15条横着的线,和15条竖着的线构成。
预制两条横竖的线,那么这两条线的两边分别画7条线。
一个7次的for循环就搞定了。
还有五个点,这个就预制好改一下位置就完成。
绘制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class QiPanCreateor : MonoBehaviour
{
public GameObject LineMH,LineMV;
public GameObject Pnt1, Pnt2, Pnt3, Pnt4;
float width = 40;
void Start()
{
CreatorQiPan();
}
void CreatorQiPan() {
Pnt1.transform.localPosition = new Vector3(-width * 4, -width * 4, 0);
Pnt2.transform.localPosition = new Vector3(width * 4, -width * 4, 0);
Pnt3.transform.localPosition = new Vector3(-width * 4, width * 4, 0);
Pnt4.transform.localPosition = new Vector3(width * 4, width * 4, 0);
for (int i = 0; i < 7; i++)
{
GameObject H1 = GameObject.Instantiate<GameObject>(LineMH);
GameObject H2 = GameObject.Instantiate<GameObject>(LineMH);
GameObject V1 = GameObject.Instantiate<GameObject>(LineMV);
GameObject V2 = GameObject.Instantiate<GameObject>(LineMV);
H1.transform.SetParent(this.transform);
H1.transform.localScale = Vector3.one;
H1.transform.localPosition = new Vector3(0, width * (i + 1), 0);
H2.transform.SetParent(this.transform);
H2.transform.localScale = Vector3.one;
H2.transform.localPosition = new Vector3(0, -width * (i + 1), 0);
V1.transform.SetParent(this.transform);
V1.transform.localScale = Vector3.one;
V1.transform.localPosition = new Vector3(width * (i + 1), 0, 0);
V2.transform.SetParent(this.transform);
V2.transform.localScale = Vector3.one;
V2.transform.localPosition = new Vector3(-width * (i + 1), 0, 0);
}
}
}
效果图
绘制的效果如下图:
今天就花这小点时间弄个棋盘,后续慢慢写!
放置棋子
功能和效果
功能
主要是实现一下这些功能:按下鼠标并移动,新棋子会跟随移动;松开鼠标,放置棋子;自动修正放置的位置在格子交叉处;不可在棋盘外和已放置的位置放置;黑白棋子交替放置。

效果
制作棋子
先预制两个棋子对象,用默认的Image组件,图片设置为内置的“Knob”。
定义类和类型
棋子类型
//定义棋子类型
public enum QiType {
none = 0,
white = 1, //白
black = 2, //黑
}
这个就是简单明了,两种类型的棋子,黑白。
棋盘格类
这个类主要用于记录每个格子内的信息,便于后面进行格子相关的逻辑判断。
// 盘格类
public class QPGrid {
public bool isPlace = false; //是否放置
public QiType QiTp = QiType.none; //类型
public Vector3 Pos; //位置
public GameObject Node = null; //对象节点
}
实现功能
初始棋盘格数据
void InitQiPanArray() {
for (int i = 0; i < 15; i++)
for (int j = 0; j < 15; j++) {
QPGrid temp = new QPGrid();
temp.Pos = new Vector3((i - 7) * width, (7 - j) * width, 0); //只用计算位置,其他默认
QiPanArray[i, j] = temp;
}
}
共有15*15个格子,我们对每个进行初始化,并赋值位置数据;位置数据通过格子大小计算出,用于放棋子位置修正。
创建棋子
//创建下一棋子
void CreateNextQi() {
if (NextQiType == QiType.white)
{
GameObject go = GameObject.Instantiate<GameObject>(WhiteQi);
go.transform.SetParent(WhiteQi.transform.parent);
go.transform.localPosition = Vector3.zero;
go.transform.localScale = Vector3.zero;
NowQi = go;
}
else if (NextQiType == QiType.black)
{
GameObject go = GameObject.Instantiate<GameObject>(BlackQi);
go.transform.SetParent(BlackQi.transform.parent);
go.transform.localPosition = Vector3.zero;
go.transform.localScale = Vector3.zero;
NowQi = go;
}
}
这个是通过复制之前预制的黑白棋对象实现,根据接下来的棋子类型来复制。
实时同步棋子位置
//同步当前棋子位置
void SyncNowQiPos(float x,float y) {
if (NowQi == null)
{
CreateNextQi();
}
else
{
NowQi.transform.localScale = Vector3.one;
NowQi.transform.localPosition = new Vector3(x, y, 0);
}
}
当鼠标按下并移动时,根据鼠标位置更新棋子的位置。
放置棋子
//尝试放置当前棋子
void TryPlaceNowQi(float x, float y) {
if ((x < 7.5f * width && x > -7.5f * width) && (y < 7.5f * width && y > -7.5f * width))
{
//在棋盘范围内
int xidx = (int)Math.Round(x / width + 7, 0);
int yidx = (int)Math.Round(7 - y / width, 0);
if (!QiPanArray[xidx, yidx].isPlace)
{
QiPanArray[xidx, yidx].isPlace = true;
QiPanArray[xidx, yidx].QiTp = NextQiType;
QiPanArray[xidx, yidx].Node = NowQi;
NowQi.transform.localPosition = QiPanArray[xidx, yidx].Pos;
if (NextQiType == QiType.white)
NextQiType = QiType.black;
else
NextQiType = QiType.white;
NowQi = null;
}
else
{
NowQi.transform.localScale = Vector3.zero;
}
}
else
{
NowQi.transform.localScale = Vector3.zero;
}
}
当鼠标松开时调用该函数,会根据鼠标位置设置放置位置,会有放置不成功的情况。
现在我们的游戏数据和操作功能有了,交替放黑白棋的功能也有了,接下来就是实现游戏结束的判断逻辑了。
结束判定
这输赢的判定就是率在八个方向的连续的五颗子即为赢。
判定分析
因为这的输赢判定简单,是最后落下的一颗棋子代表的一方有可能赢,就是最后落下的子能决定胜负,只用判定以它为中心关联的条线上的棋子是否构成五颗连续同颜色即可。
上图中四条不同颜色的线代表需要判定的方向,按这个思路,就是写几个for循环的事了。
结束判定实现
public bool CheckGameOver(int posx,int posy) {
int count = 1; //连续棋子数
//上下方向检测
for (int i = posy + 1; i < 15; i++) {
if (QiPanArray[posx, posy].QiTp == QiPanArray[posx, i].QiTp) {
if (++count >= 5)
return true;
}
else
break;
}
for (int i = posy - 1; i >= 0; i--) {
if (QiPanArray[posx, posy].QiTp == QiPanArray[posx, i].QiTp)
{
if (++count >= 5)
return true;
}
else
break;
}
count = 1;
//左右方向检测
for (int i = posx + 1; i < 15; i++)
{
if (QiPanArray[posx, posy].QiTp == QiPanArray[i, posy].QiTp)
{
if (++count >= 5)
return true;
}
else
break;
}
for (int i = posx - 1; i >= 0; i--)
{
if (QiPanArray[posx, posy].QiTp == QiPanArray[i, posy].QiTp)
{
if (++count >= 5)
return true;
}
else
break;
}
count = 1;
//右上方向检测
for (int i = posx + 1, j = posy + 1; i < 15 && j < 15; i++,j++)
{
if (QiPanArray[posx, posy].QiTp == QiPanArray[i, j].QiTp)
{
if (++count >= 5)
return true;
}
else
break;
}
//左下方向检测
for (int i = posx - 1, j = posy - 1; i >= 0 && j >= 0; i--,j--)
{
if (QiPanArray[posx, posy].QiTp == QiPanArray[i, j].QiTp)
{
if (++count >= 5)
return true;
}
else
break;
}
count = 1;
//右下方向检测
for (int i = posx + 1, j = posy - 1; i < 15 && j >= 0; i++, j--)
{
if (QiPanArray[posx, posy].QiTp == QiPanArray[i, j].QiTp)
{
if (++count >= 5)
return true;
}
else
break;
}
//左上方向检测
for (int i = posx - 1, j = posy + 1; i >= 0 && j < 15; i--, j++)
{
if (QiPanArray[posx, posy].QiTp == QiPanArray[i, j].QiTp)
{
if (++count >= 5)
return true;
}
else
break;
}
return false;
}
这里的两个参数即int posx,int posy,是最后落下棋子所在的格子的下标。
完善工程
新增UI
这里新增的两个UI分别是开始UI和结束UI,都是自带的按钮组件和图片组件构成。
开始UI
结束UI
完善代码
这里主要实现几个按钮的点击功能,已经场景的切换。
点击函数
自己看代码吧:
//开始游戏
public void Play()
{
StartPanel.SetActive(false);
BackBtn.SetActive(true);
isGaming = true;
}
//重新开始
public void RePlay()
{
StartPanel.SetActive(false);
OverPanel.SetActive(false);
BackBtn.SetActive(true);
for (int i = 0; i < 15; i++)
for (int j = 0; j < 15; j++)
{
QiPanArray[i, j].isPlace = false;
QiPanArray[i, j].QiTp = QiType.none;
if (QiPanArray[i, j].Node != null)
{
Destroy(QiPanArray[i, j].Node);
QiPanArray[i, j].Node = null;
}
}
isGaming = true;
}
//返回大厅
public void BackHall()
{
isGaming = false;
StartPanel.SetActive(true);
OverPanel.SetActive(false);
for (int i = 0; i < 15; i++)
for (int j = 0; j < 15; j++) {
QiPanArray[i, j].isPlace = false;
QiPanArray[i, j].QiTp = QiType.none;
if (QiPanArray[i, j].Node != null) {
Destroy(QiPanArray[i, j].Node);
QiPanArray[i, j].Node = null;
}
}
BackBtn.SetActive(false);
}
结束操作
public void GameOver()
{
isGaming = false;
OverPanel.SetActive(true);
WinSide.text = NextQiType == QiType.white ? "白棋获胜!" : "黑棋获胜!";
}
总结
这个实现的比较快,不过只是实现了核心的玩法功能,其实可以延申开发的地方比较多,可以有兴趣写个人机对战的,还有平手的判定等这些都没有加。源码接近17KB,代码包含300多行,将就这看吧。