【Unity3D入门教程】Unity3D开发实战之五子棋

前言

经过前面《Unity3D入门教程》系列讲解,再加上我们自己的探索,相信大家已经掌握了Unity3D的相关知识和基本方法。本文将使用前面学到的知识,开发一款简单的五子棋程序。本文用到的东西其实不多,非常简单。在最后我们会把完整工程的源代码发布出来,以供初学者参考。先展示一下最后的运行效果吧。

1 准备工作

(1)开发环境:Win10 + Unity5.4.1

(2)图片素材准备:

黑棋子和白棋子

   

棋盘

获胜提示图片

2 开发流程

上文提到的素材可以直接下载我们给出的这些图,也可以自己制作。注意黑白棋子要做成PNG格式,以保证显示的时候棋子四个角是透明的。将用到的图片素材导入到工程当中。新建一个场景,创建一个Plane,作为MainCamera的子物体。将棋盘贴图拖动到Plane上,并且将Plane正面面向摄像机。

再创建四个sphere,作为Plane的子物体,分别命名为LeftTop、RightTop、LeftBottom、RightBottom。然后把他们的MeshRenderer勾选掉。这些球是为了计算棋子落点所设置的,所以需要把它们与棋盘的四个角点对准。

然后我们创建一个chess.cs脚本,绑定到MainCamera上。脚本中包含了所有的功能。需要绑定的一些物体如图所示。

chess.cs脚本如下:

using UnityEngine;
using System.Collections;

public class chess : MonoBehaviour {

	//四个锚点位置,用于计算棋子落点
	public GameObject LeftTop;
	public GameObject RightTop;
	public GameObject LeftBottom;
	public GameObject RightBottom;
	//主摄像机
	public Camera cam;
	//锚点在屏幕上的映射位置
	Vector3 LTPos;
	Vector3 RTPos;
	Vector3 LBPos;
	Vector3 RBPos;

	Vector3 PointPos;//当前点选的位置
	float gridWidth =1; //棋盘网格宽度
	float gridHeight=1; //棋盘网格高度
	float minGridDis;  //网格宽和高中较小的一个
	Vector2[,] chessPos; //存储棋盘上所有可以落子的位置
	int[,] chessState; //存储棋盘位置上的落子状态
	enum turn {black, white } ;
	turn chessTurn; //落子顺序
	public Texture2D white; //白棋子
	public Texture2D black; //黑棋子
	public Texture2D blackWin; //白子获胜提示图
	public Texture2D whiteWin; //黑子获胜提示图
	int winner = 0; //获胜方,1为黑子,-1为白子
	bool isPlaying = true; //是否处于对弈状态
	void Start () {
		chessPos = new Vector2[15, 15];
		chessState =new int[15,15];
		chessTurn = turn.black;

	}

	void Update () {

		//计算锚点位置
		LTPos = cam.WorldToScreenPoint(LeftTop.transform.position);
		RTPos = cam.WorldToScreenPoint(RightTop.transform.position);
		LBPos = cam.WorldToScreenPoint(LeftBottom.transform.position);
		RBPos = cam.WorldToScreenPoint(RightBottom.transform.position);
		//计算网格宽度
		gridWidth = (RTPos.x - LTPos.x) / 14;
		gridHeight = (LTPos.y - LBPos.y) / 14;
		minGridDis = gridWidth < gridHeight ? gridWidth : gridHeight;
		//计算落子点位置
		for (int i = 0; i < 15; i++)
		{
			for (int j = 0; j < 15; j++)
			{
				chessPos[i, j] = new Vector2(LBPos.x + gridWidth * i, LBPos.y + gridHeight * j);
			}
		}
		//检测鼠标输入并确定落子状态
		if (isPlaying && Input.GetMouseButtonDown(0))
		{
			PointPos = Input.mousePosition;
			for (int i = 0; i < 15; i++)
			{
				for (int j = 0; j < 15; j++)
				{   
					//找到最接近鼠标点击位置的落子点,如果空则落子
					if (Dis(PointPos, chessPos[i, j]) < minGridDis / 2 && chessState[i,j]==0)
					{
						//根据下棋顺序确定落子颜色
						chessState[i, j] = chessTurn == turn.black ? 1 : -1;
						//落子成功,更换下棋顺序
						chessTurn = chessTurn == turn.black ? turn.white : turn.black;                        
					}
				}
			}
			//调用判断函数,确定是否有获胜方
			int re = result();
			if (re == 1)
			{
				Debug.Log("黑棋胜");
				winner = 1;
				isPlaying = false;
			}
			else if(re==-1)
			{
				Debug.Log("白棋胜");
				winner = -1;
				isPlaying = false;
			}            
		}
		//按下空格重新开始游戏
		if (Input.GetKeyDown(KeyCode.Space))
		{
			for (int i = 0; i < 15; i++)
			{
				for (int j = 0; j < 15; j++)
				{
					chessState[i, j] = 0;
				}
			}
			isPlaying = true;
			chessTurn = turn.black;
			winner = 0;
		}     
	}
	//计算平面距离函数
	float Dis(Vector3 mPos, Vector2 gridPos)
	{
		return Mathf.Sqrt(Mathf.Pow(mPos.x - gridPos.x, 2)+ Mathf.Pow(mPos.y - gridPos.y, 2));
	}

	void OnGUI()
	{ 
		//绘制棋子
		for(int i=0;i<15;i++)
		{
			for (int j = 0; j < 15; j++)
			{
				if (chessState[i, j] == 1)
				{
					GUI.DrawTexture(new Rect(chessPos[i,j].x-gridWidth/2, Screen.height-chessPos[i,j].y-gridHeight/2, gridWidth,gridHeight),black);
				}
				if (chessState[i, j] == -1)
				{
					GUI.DrawTexture(new Rect(chessPos[i, j].x - gridWidth / 2, Screen.height - chessPos[i, j].y - gridHeight / 2, gridWidth, gridHeight), white);
				}               
			}
		}
		//根据获胜状态,弹出相应的胜利图片
		if (winner ==  1)
			GUI.DrawTexture(new Rect(Screen.width * 0.25f, Screen.height * 0.25f, Screen.width * 0.5f, Screen.height * 0.25f), blackWin);
		if (winner == -1)
			GUI.DrawTexture(new Rect(Screen.width * 0.25f, Screen.height * 0.25f, Screen.width * 0.5f, Screen.height * 0.25f), whiteWin);

	}
	//检测是够获胜的函数,不含黑棋禁手检测
	int result()
	{
		int flag = 0;
		//如果当前该白棋落子,标定黑棋刚刚下完一步,此时应该判断黑棋是否获胜
		if(chessTurn == turn.white)
		{
			for (int i = 0; i < 11; i++)
			{
				for (int j = 0; j < 15; j++)
				{
					if (j < 4)
					{
						//横向
						if (chessState[i, j] == 1 && chessState[i, j + 1] == 1 && chessState[i, j + 2] == 1 && chessState[i, j + 3] == 1 && chessState[i, j + 4] == 1)
						{
							flag = 1;
							return flag;
						}
						//纵向
						if (chessState[i, j] == 1 && chessState[i + 1, j] == 1 && chessState[i + 2, j] == 1 && chessState[i + 3, j] == 1 && chessState[i + 4, j] == 1)
						{
							flag = 1;
							return flag;
						}
						//右斜线
						if (chessState[i, j] == 1 && chessState[i + 1, j + 1] == 1 && chessState[i + 2, j + 2] == 1 && chessState[i + 3, j + 3] == 1 && chessState[i + 4, j + 4] == 1)
						{
							flag = 1;
							return flag;
						}
						//左斜线
						//if (chessState[i, j] == 1 && chessState[i + 1, j - 1] == 1 && chessState[i + 2, j - 2] == 1 && chessState[i + 3, j - 3] == 1 && chessState[i + 4, j - 4] == 1)
						//{
						//    flag = 1;
						//    return flag;
						//}
					}
					else if (j >= 4 && j < 11)
					{
						//横向
						if (chessState[i, j] == 1 && chessState[i, j + 1] == 1 && chessState[i, j + 2] == 1 && chessState[i, j + 3] == 1 && chessState[i, j + 4] == 1)
						{
							flag = 1;
							return flag;
						}
						//纵向
						if (chessState[i, j] == 1 && chessState[i + 1, j] == 1 && chessState[i + 2, j] == 1 && chessState[i + 3, j] == 1 && chessState[i + 4, j] == 1)
						{
							flag = 1;
							return flag;
						}
						//右斜线
						if (chessState[i, j] == 1 && chessState[i + 1, j + 1] == 1 && chessState[i + 2, j + 2] == 1 && chessState[i + 3, j + 3] == 1 && chessState[i + 4, j + 4] == 1)
						{
							flag = 1;
							return flag;
						}
						//左斜线
						if (chessState[i, j] == 1 && chessState[i + 1, j - 1] == 1 && chessState[i + 2, j - 2] == 1 && chessState[i + 3, j - 3] == 1 && chessState[i + 4, j - 4] == 1)
						{
							flag = 1;
							return flag;
						}
					}
					else
					{
						//横向
						//if (chessState[i, j] == 1 && chessState[i, j + 1] == 1 && chessState[i, j + 2] == 1 && chessState[i, j + 3] == 1 && chessState[i, j + 4] == 1)
						//{
						//    flag = 1;
						//    return flag;
						//}
						//纵向
						if (chessState[i, j] == 1 && chessState[i + 1, j] == 1 && chessState[i + 2, j] == 1 && chessState[i + 3, j] == 1 && chessState[i + 4, j] == 1)
						{
							flag = 1;
							return flag;
						}
						//右斜线
						//if (chessState[i, j] == 1 && chessState[i + 1, j + 1] == 1 && chessState[i + 2, j + 2] == 1 && chessState[i + 3, j + 3] == 1 && chessState[i + 4, j + 4] == 1)
						//{
						//    flag = 1;
						//    return flag;
						//}
						//左斜线
						if (chessState[i, j] == 1 && chessState[i + 1, j - 1] == 1 && chessState[i + 2, j - 2] == 1 && chessState[i + 3, j - 3] == 1 && chessState[i + 4, j - 4] == 1)
						{
							flag = 1;
							return flag;
						}
					}

				}
			}
			for (int i = 11; i < 15; i++) 
			{
				for (int j = 0; j < 11; j++) 
				{
					//只需要判断横向  
					if (chessState[i, j] == 1 && chessState[i, j + 1] == 1 && chessState[i, j + 2] == 1 && chessState[i, j + 3] == 1 && chessState[i, j + 4] == 1)  
					{  
						flag = 1;  
						return flag;  
					}  
				}
			}
		}
		//如果当前该黑棋落子,标定白棋刚刚下完一步,此时应该判断白棋是否获胜
		else if(chessTurn == turn.black)
		{
			for (int i = 0; i < 11; i++)
			{
				for (int j = 0; j < 15; j++)
				{
					if (j < 4)
					{
						//横向
						if (chessState[i, j] == -1 && chessState[i, j + 1] == -1 && chessState[i, j + 2] == -1 && chessState[i, j + 3] == -1 && chessState[i, j + 4] == -1)
						{
							flag = -1;
							return flag;
						}
						//纵向
						if (chessState[i, j] == -1 && chessState[i + 1, j] == -1 && chessState[i + 2, j] == -1 && chessState[i + 3, j] == -1 && chessState[i + 4, j] == -1)
						{
							flag = -1;
							return flag;
						}
						//右斜线
						if (chessState[i, j] == -1 && chessState[i + 1, j + 1] == -1 && chessState[i + 2, j + 2] == -1 && chessState[i + 3, j + 3] == -1 && chessState[i + 4, j + 4] == -1)
						{
							flag = -1;
							return flag;
						}
						//左斜线
						//if (chessState[i, j] == -1 && chessState[i + 1, j - 1] == -1 && chessState[i + 2, j - 2] == -1 && chessState[i + 3, j - 3] == -1 && chessState[i + 4, j - 4] == -1)
						//{
						//    flag = -1;
						//    return flag;
						//}
					}
					else if (j >= 4 && j < 11)
					{
						//横向
						if (chessState[i, j] == -1 && chessState[i, j + 1] == -1 && chessState[i, j + 2] == -1 && chessState[i, j + 3] == -1 && chessState[i, j + 4] ==- 1)
						{
							flag = -1;
							return flag;
						}
						//纵向
						if (chessState[i, j] == -1 && chessState[i + 1, j] == -1 && chessState[i + 2, j] == -1 && chessState[i + 3, j] == -1 && chessState[i + 4, j] == -1)
						{
							flag = -1;
							return flag;
						}
						//右斜线
						if (chessState[i, j] == -1 && chessState[i + 1, j + 1] == -1 && chessState[i + 2, j + 2] == -1 && chessState[i + 3, j + 3] == -1 && chessState[i + 4, j + 4] == -1)
						{
							flag = -1;
							return flag;
						}
						//左斜线
						if (chessState[i, j] == -1 && chessState[i + 1, j - 1] == -1 && chessState[i + 2, j - 2] == -1 && chessState[i + 3, j - 3] == -1 && chessState[i + 4, j - 4] == -1)
						{
							flag = -1;
							return flag;
						}
					}
					else
					{
						//横向
						//if (chessState[i, j] == -1 && chessState[i, j + 1] ==- 1 && chessState[i, j + 2] == -1 && chessState[i, j + 3] == -1 && chessState[i, j + 4] == -1)
						//{
						//    flag = -1;
						//    return flag;
						//}
						//纵向
						if (chessState[i, j] == -1 && chessState[i + 1, j] ==- 1 && chessState[i + 2, j] ==- 1 && chessState[i + 3, j] ==- 1 && chessState[i + 4, j] == -1)
						{
							flag = -1;
							return flag;
						}
						//右斜线
						//if (chessState[i, j] == -1 && chessState[i + 1, j + 1] == -1 && chessState[i + 2, j + 2] == -1 && chessState[i + 3, j + 3] == -1 && chessState[i + 4, j + 4] == -1)
						//{
						//    flag = -1;
						//    return flag;
						//}
						//左斜线
						if (chessState[i, j] == -1 && chessState[i + 1, j - 1] == -1 && chessState[i + 2, j - 2] == -1 && chessState[i + 3, j - 3] == -1 && chessState[i + 4, j - 4] == -1)
						{
							flag = -1;
							return flag;
						}
					}
				}
			}
			for (int i = 11; i < 15; i++) 
			{
				for (int j = 0; j < 11; j++) 
				{
					//只需要判断横向  
					if (chessState[i, j] == -1 && chessState[i, j + 1] == -1 && chessState[i, j + 2] == -1 && chessState[i, j + 3] == -1 && chessState[i, j + 4] == -1)  
					{  
						flag = -1;  
						return flag;  
					}  
				}
			}
		}       
		return flag;
	}    
}

运行效果截图:

小结

本程序实现了五子棋的基本功能,纯属娱乐而作。暂时没有加入各种UI、网络模块等。本程序经过了简单的测试,没有什么问题,如果大家在使用的时候发现有什么Bug,请联系我改正,谢谢。

*************************************************************************************

下面是工程源码下载地址:

GitHub - zzlyw/Gobang_Tutorial

<think>好的,用户想了解学习EasyX的好处,我需要先确认自己对这个库的了解。EasyX是一个针对C++的图形库,主要在Windows环境下使用,适合初学者图形编程。接下来要分析用户可能的背景,可能是刚开始学习编程,或者对图形学感兴趣,想要制作小游戏或图形界面应用。用户可能想知道学习EasyX能带来哪些实际的好处,比如提升编程技能、项目实践机会等。 首先,需要列出EasyX的主要优点。比如简单易学,适合新手,能够快速看到图形效果,这对保持学习兴趣很重要。然后是对C++的巩固,因为使用EasyX需要基本的C++知识,实践过程中能加深理解。再者,图形编程的直观性,帮助理解算法和数学概念,比如坐标系统、动画原理。另外,项目开发方面,可以用它制作小游戏或图形工具,增强实战经验。还有社区和资源的支持,国内资料丰富,容易找到教程和帮助。 需要注意用户可能的深层需求,比如是否为了未来的职业发展或学术研究。但考虑到EasyX主要在教育和小型项目中使用,可能用户更关注兴趣培养和基础巩固。另外,要提醒EasyX的局限性,比如仅限于Windows,但作为入门工具已经足够。最后总结学习的好处,鼓励用户尝试,并结合其他知识拓展能力。</think>学习 EasyX 图形库的主要好处可以从以下几个方面分析,我将逐步为你说明: 1. **快速上手图形编程** - **简单接口**:EasyX 提供类似 Turbo C 的图形函数(如 `initgraph`, `circle`, `line`),无需深入 Win32 API 即可绘制图形。 - **即时反馈**:通过几行代码即可生成窗口、绘制几何图形,例如: ```cpp #include <graphics.h> int main() { initgraph(640, 480); // 创建 640x480 窗口 circle(320, 240, 100); // 绘制圆心 (320,240)、半径 100 的圆 getch(); // 等待按键 closegraph(); // 关闭窗口 return 0; } ``` - **降低学习曲线**:适合编程新手理解图形渲染的基本原理。 2. **巩固 C/C++ 编程能力** - 通过实现动画、交互逻辑(如鼠标键盘事件处理),能练习: - 循环结构(控制帧率) - 数组/结构体(管理图形对象) - 指针与内存管理(动态图形资源) - 例如用双缓冲避免闪烁: ```cpp BeginBatchDraw(); // 开始批量绘制 cleardevice(); drawScene(); // 自定义绘制函数 FlushBatchDraw(); // 提交绘制 ``` 3. **直观理解计算机图形学概念** - **坐标系**:通过 `putpixel(x, y, color)` 理解笛卡尔坐标系与屏幕坐标差异。 - **颜色模型**:使用 `RGB(255, 0, 0)` 配置颜色,混合透明度(如 `0x7F0000FF`)。 - **几何变换**:手动实现平移、旋转公式,例如旋转点 $(x,y)$ 角度 $\theta$: $$ x' = x \cos \theta - y \sin \theta \\ y' = x \sin \theta + y \cos \theta $$ 4. **项目实践机会** - **经典案例**: - 贪吃蛇(链表管理蛇身) - 五子棋(二维数组存储棋盘状态) - 物理模拟(如抛物线运动公式 $y = v_0 t - \frac{1}{2}gt^2$) - **扩展性强**:可结合 STL 容器、多文件编程组织复杂项目。 5. **过渡到更高级框架** - 学习 EasyX 后,更容易理解: - OpenGL 的坐标系与渲染管线 - DirectX 的窗口管理概念 - 游戏引擎(如 Unity)的基础组件设计 **局限性说明**: - 主要支持 Windows 系统 - 缺少 3D 渲染等高级功能 **建议学习路径**: 1. 掌握基础图形绘制 2. 实现简单动画(如匀速运动 $s = vt$) 3. 添加用户交互(键盘控制角色移动) 4. 开发完整小游戏(如俄罗斯方块) 通过 EasyX,你可以在实践中提升调试能力、算法设计思维,并为后续学习更复杂的图形技术奠定基础。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雁回晴空

您的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值