大概是一周前,在知乎上偶然看见一位前辈提供的意见,他认为在一个立志于进入游戏行业发展的程序员在大学期间应该多动手,亲自去完成一些项目,在遇到坑,填补坑的过程中快速的提升技术水平。想来我虽然已经入职游戏策划职业将近一年了。但是内心还是无时无刻不想自己成为一个“有技术的游戏策划”,因此将自己定位成一个萌萌的新人。开始一步一个脚印的学习之路。
之前有朋友在我的文章下面评论我,质疑我写的文章并非原创,诚然,之前关于cocos2d等其他游戏引擎的“研究”都是皮毛,很少静下心来尝试制作一款完整的项目。因此这次对于我来说,也是一个很好的机会吧。写这个系列文章,一来可以帮助自己梳理知识。二来可以帮助后来者,大家一起学习交流,三来可以让有经验的前辈帮助我指正。
我会将代码提交到我的GitHub,并将网址贴在此处。
前辈的建议有以下步骤:

  1. 开发一款五子棋游戏,AI可以在网上查找;
  2. 开发五子棋的局域网对战功能,并且分数可以保存在数据库MySQL等;
  3. 开发一款ARP游戏,实现单机部分;
  4. ARPG游戏网络化,开发服务器端;
  5. 山寨市面上比较热门的一款卡牌游戏;

而我在第一步上就有点走偏了,实际上我有自己的考虑,我想开发一款“五子连珠”的游戏,同样是二维网格类消除游戏,我认为其更能考验算法设计能力和编程基本功,因为五子棋的最大难点在于AI。而AI在这个游戏开发中又不是我们重点关注的目标。因此我做了这个决定。
五子连珠游戏,顾名思义就是当五个相同颜色的棋子在八个方向上数量大于等于5时即可消除得分。棋盘是一个9×9的网格。每回合玩家可以移动一个棋子,玩家移动之后会在棋盘上随机产生三个新的棋子(消除则不产生)。当所有网格中均有棋子时,游戏结束。棋子的种类共有五种颜色。
整个游戏在算法上有以下需要考虑的地方

  1. 随机位置产生棋子的算法;
  2. 棋子能否移动的检测;
  3. 棋子能否消除的检测;
  4. 棋子的寻路算法;

在实际的游戏开发中,实际上在进行寻路算法过程中也需要进行能否移动的检测,因此移动检测是多余的。随机位置产生棋子的算法相对简单,只需要用到随机数;棋子能否消除的算法与一般的三消游戏类似,也可以参考五子棋胜利检测的算法,我这里使用的是辐射法。寻路算法最为复杂,采用的是四方向的A星算法。

游戏的流程图如下图所示:
//此处省略,之后补充

我的思路是首先创建三个脚本分别是:

  1. GameManager.cs 游戏管理器,用于管理游戏开始、储存、读取和游戏结束等;
  2. GridController.cs 网格控制器,用于管理网格,游戏中大部分逻辑运算在这个脚本中;
  3. GridScr.cs 网格脚本,挂在网格游戏对象身上,用来记录网格的信息;

首先看GridScr.cs这个脚本的代码;

using UnityEngine;
using System.Collections;/// <summary>
/// 挂在Grid游戏对象上,用来记录信息
/// </summary>
public class GridScr : MonoBehaviour {//网格信息public int Gridx;public int Gridy;public int BianHao;//用来寻路public int gCost;public int hCost;public int fCost{get { return gCost + hCost; }}public GridScr parent;//记录颜色public int ChessmanColor;                     //0代表空//记录坐标public Vector3 pos;/// <summary>/// 设置颜色/// </summary>/// <param name="type"></param>public void SetChessmanColor(int type){ChessmanColor = type;}/// <summary>/// 获取颜色/// </summary>/// <returns></returns>public int GetChessmanColor(){return ChessmanColor;}/// <summary>/// 设置Grid对象,伪构造函数/// </summary>/// <param name="x"></param>/// <param name="y"></param>/// <param name="type"></param>public void SetGridObj(int x,int y,int type){Gridx = x;Gridy = y;pos.x = Gridx;pos.y = Gridy;ChessmanColor = type;BianHao = Gridx + Gridy * GridController._gridcontroller.MaxColNum;this.gameObject.name = "Gird" + BianHao;}//处理鼠标点击时间void OnMouseDown(){if (this.ChessmanColor > 0){GridController._gridcontroller.Selected01 = this;}if (this.ChessmanColor == 0 &&GridController._gridcontroller.Selected01 != null){GridController._gridcontroller.Selected02 = this;GridController._gridcontroller.ChessmanMoveTo();}}//设置图片对象public void SetGridSprite(int spr){Sprite sprite = GridController._gridcontroller.GridSpr[spr];this.GetComponent<SpriteRenderer> ().sprite = sprite;}
}

在这个方法中首先是记录了网格的横纵坐标以及编号和棋子颜色(这个很重要)等,之后是G、H和F这三个值是用来寻路的,之后定义了一些方法,包括设置网格的、设置和获取网格的颜色的等等。最后是一个设置网格精灵(sprite)的方法,这个方法主要是为了实现游戏中“棋盘是国际象棋效果”这一需求(只是我自己给自己设置的一个需求罢了)。

之后是GameManager.cs脚本,这个脚本我觉得现在还没有贴出来的必要,因为这个脚本现在可以说是一个空壳。

using System.Collections;
using System.Collections.Generic;public class GameManager : MonoBehaviour {public static GameManager _gameManager;void Awake(){_gameManager = this;}// Use this for initializationvoid Start () {NewGame();}// Update is called once per framevoid Update () {}//开始新游戏public void NewGame(){//新建棋盘GridController._gridcontroller.NewGridCreate();}//储存游戏public void SaveGame(){}//载入游戏public void LoadGame(){}//结束游戏public void GameOver(){Debug.Log("游戏结束");}
}

这个脚本目前的功能是实现游戏的新建、储存载入和结束的,而具体的方法操作还没有实现,因此目前没什么好说的。
最后是这篇文章的重头戏,GridController.cs网格控制器脚本。先看代码:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;public class GridController : MonoBehaviour {public static GridController _gridcontroller;//预览public GameObject[] NextGridTransform;public GameObject[] NextChessmanTransform;public GameObject NextGridObj;public GameObject NextParent;//最大行列数public int MaxRowNum = 9;public int MaxColNum = 9;//网格相关的public GameObject GridObj;public Sprite[] GridSpr;[HideInInspector]public GridScr GridScr;public GameObject[,] GridTransform;public GridScr[,] GridScrTransform;//public List<GameObject> ChessmanTransform;public GameObject GridParent;public int NewChessmanNum = 3;//棋子相关的//public GameObject ChessmanObj;public GameObject[] ChessmanObjGrop;private int GridTotal;                  //总的棋子数(网格数)private int NowChessmanNum = 0;         //目前棋子数private List<GameObject> pathObj = new List<GameObject>();[HideInInspector]public GridScr Selected01;[HideInInspector]public GridScr Selected02;[HideInInspector]public AStarFindPath asr;Route_pt[] result = null;private int[] ramNum; void Awake(){_gridcontroller = this;}/// <summary>/// 新建棋盘/// </summary>public void NewGridCreate(){GridTransform = new GameObject[MaxRowNum , MaxColNum];GridScrTransform = new GridScr[MaxRowNum, MaxColNum];//ChessmanTransform = new List<GameObject>();GridTotal = MaxRowNum * MaxColNum;for (int i = 0; i < MaxRowNum; i++) {for (int j = 0; j < MaxColNum; j++) {NewCellCreate (i, j);}}//预览网格创建NextGridGreate();//预览棋子创建NextChessmanCreate();//随机位置创建棋子DropChessman();}//新建棋盘网格void NewCellCreate(int row, int col){//实例化网格GameObject obj = Instantiate(GridObj);obj.transform.parent = GridParent.transform;obj.transform.localPosition = new Vector2(row, col);//设置脚本GridScr Scr = obj.GetComponent<GridScr>();Scr.SetGridObj(row, col, 0);//添加到二维网格GridTransform[row, col] = obj;GridScrTransform[row, col] = Scr;//设置国际象棋棋盘效果Scr.SetGridSprite((row + col) % 2);}//预览网格创建void NextGridGreate(){NextGridTransform = new GameObject[NewChessmanNum];NextChessmanTransform = new GameObject[NewChessmanNum];for (int i = 0; i < NewChessmanNum; i++){//实例化预览网格GameObject obj = Instantiate(NextGridObj);obj.transform.parent = NextParent.transform;obj.transform.localPosition = new Vector2(i, 0);NextGridTransform[i] = obj;//GridScr Scr = obj.GetComponent<GridScr>();//Scr.SetGridSprite(i % 2);//Scr.SetGridObj(i, 0, 0);}}//预览棋子的创建void NextChessmanCreate(){ramNum = new int[NewChessmanNum];for (int i = 0; i < NewChessmanNum; i++){//删除当前已有的if (NextChessmanTransform[i] != null){Destroy(NextChessmanTransform[i]);}//随机对象int ram = Random.Range(0, ChessmanObjGrop.Length - 1);ramNum[i] = ram;//ChessmanObj = ChessmanObjGrop[ram];//实例化棋子GameObject obj = Instantiate(ChessmanObjGrop[ram]);obj.transform.parent = NextGridTransform[i].transform;obj.transform.localPosition = new Vector2(0, 0);GridScr Scr = NextGridTransform[i].GetComponent<GridScr>();Scr.SetChessmanColor(ram + 1);//ChessmanScr ChessmanScr = obj.GetComponent<ChessmanScr>();NextChessmanTransform[i] = obj;}}//随机位置放置棋子public void DropChessman(){//有足够位置if (NowChessmanNum < GridTotal - NewChessmanNum){for (int i = 0; i < NewChessmanNum; i++)            //取决于游戏难度(每次产生数){int weizhi = Random.Range(0, GridTotal);if (GridScrTransform[ weizhi % MaxRowNum , weizhi / MaxColNum ].ChessmanColor == 0) //网格中没有棋子{//创建新棋子NewChessmanCreate(i, weizhi);//创建新预览棋子NextChessmanCreate();Debug.Log("不重复" + i);}else{i -= 1;                                                                     //不清楚这里会不会出错Debug.Log("重复的" + i);}}NowChessmanNum += NewChessmanNum;}else{//无法创建时,游戏结束GameManager._gameManager.GameOver();}}//创建新棋子void NewChessmanCreate(int i, int weizhi){//创建新棋子根据预览棋子,因此需要获取预览棋子的对象//实例化网格,网格位置GameObject obj = Instantiate(ChessmanObjGrop[ramNum[i]]);obj.transform.parent = GridTransform[ weizhi % MaxRowNum , weizhi / MaxColNum ].transform;obj.transform.localPosition = new Vector2(0, 0);//脚本设置GridScr scr = GridScrTransform[weizhi % MaxRowNum, weizhi / MaxColNum];//ChessmanScr Che = chessman.GetComponent<ChessmanScr>();//获取棋子颜色scr.SetChessmanColor(ramNum[i] + 1);//将网格设置为非空//ChessmanTransform[weizhi] = obj;}//移动棋子public void ChessmanMoveTo(){Debug.Log(Selected01.Gridx + "," + Selected01.Gridy);Debug.Log(Selected02.Gridx + "," + Selected02.Gridy);}}

在这个脚本文件中,

  1. 首先是脚本的实例化,没什么特别的。
  2. 之后便是新建网格,在此之前我将两个二维数组(分别用来记录网格游戏对象和网格身上挂着的脚本,将脚本拿出来存入二维数组可以提高运行效率)之后便是循环创建9×9个网格对象;在这个脚本中,我直接将创建预览网格、创建预览棋子和随机位置创建棋子写在此处,目的是不讲其他方法设置成公有。这里我再之后会进一步完善。
  3. 棋盘网格对象的实例化包括几部分,首先是实例化对象、设置父级对象和坐标等;之后是设置脚本,将信息记录在脚本中;然后将其记录在二维网格中;最后是实现国际象棋棋盘效果;
  4. 所谓预览棋子,是指预告下回合将出现的三个棋子的颜色。这里分两个方法操作,首先是创建网格,与创建棋盘网格类似,要注意这里创建的网格是没有Box Collider 2D的,目的是为了不能相应鼠标点击事件。之后就是创建三个棋子。分别在三个网格中。
  5. 随机位置创建棋子这个方法,首先应该进行一个判断,就是网格中还有足够的位置放置棋子。然后随机一个位置放置预览网格中的棋子,如果随机的位置有棋子了,那么就重新进行随机位置;最后是要将新创建的棋子个数加到当前总个数上。
  6. 移动棋子需要先进行寻路,不在这片文章内容中。但是我这里写的是输出两个位置点的坐标。方便测试我当前的逻辑是否正确。正确的话才能进行下一步,也就是寻路算法,以及沿着路径行走的方法。
  7. 此外,在创建预览棋子之前,需要先清空一下旧的预览棋子。

运行之后的游戏是这样的:

在控制台中会出现如下图的提示信息,在极少数情况下会出现“重复1”这样的提示信息,证明我们的逻辑没有错;

对了,这个黑白棋盘网格是我自己画得,棋子是随便找的,这么看起来还挺好看的。不过下一步还是会进行优化的。
游戏中的Prefab如下图,实际上我后来发现Chessman也可以用一个游戏对象,通过改变sprite来改变显示。我会在下一篇文章中做优化。

当然,还有游戏中的Hierarchy,我是这样设置的,将GameManager脚本直接放在空物体GameManager上,将GridController放在GridParent上。而GridController脚本中相关变量的赋值如下图所示:

本章总结。二维网格类三消游戏,逻辑层面实际上就是对于二维数组的操作。因此将游戏对象储存在相应的二维数组中就尤为重要,可以说是这个游戏的一个根基。只有这个根基没有问题,才能使之后的算法层面的东西实现起来没有偏差。明天我会继续进行寻路算法及消除检测方面的开发。
这篇文章写出来,希望可以和和我一样的小白交流,也更希望有热心大神批评指正(跪求人来损我,真的,太需要鞭策了!!!)
最后,祝大家中秋节快乐。

[从零开始unity3D]“五子连珠”游戏实现,网格数组的创建(1)相关推荐

  1. 五子连珠游戏使用手册

    [size=large]使用手册User Manual[/size] 本程序为一款五子连珠游戏,每移动一粒珠子,即随机出现三粒彩色珠子,当同颜色珠子连续排列超过五粒(含五粒)时,该排列成功消除并行分, ...

  2. 控制台的五子连珠游戏

    手写控制台五子连珠游戏 前言 游戏需求 框架 总体框架 main代码 补充和完善 确定游戏中的类 初始化 显示游戏帧 获取输入 更新游戏帧 游戏结束 游戏截图 最终代码(和上面有些不同 如果想cv请看 ...

  3. Cocos2d-x 小试牛刀五子连珠游戏

    Cocos2d-x小试牛刀五子连珠游戏 声明:本文游戏使用的是cocos2d-x-3.13的代码 游戏介绍 本文将介绍有Cocos编写经典游戏,五子连珠.游戏规则,有一个10*10的棋盘,里面有六种颜 ...

  4. Flex练习:写一个五子连珠游戏 Five And More

    注意:火狐和谷歌浏览器看不到效果,是因为在博客中,一些脚本无法配置,和Flex的兼容性无关 玩法:先选择棋盘中的一个棋子,然后点击你想移动到的空格,五个同样颜色在一条线上就可以消去.玩一把试试吧,个人 ...

  5. 计算机游戏五子连珠怎么出来,五子连珠游戏程序流程图

    满意答案 五子连珠介绍:玩家只需依照五子棋的基础规则进行相应怪物击杀凑成五子连珠,便能获得丰厚的通关奖励.脑力与战斗力的紧密结合,相信能带给你不一样的五子棋心得.玩家每天可以在五子连珠活动开启时间内进 ...

  6. 用C++完成五子连珠游戏

    一:项目背景 本游戏的棋盘大小是9 × 9,一共会出现6种颜色的珠子.初始状态棋盘上7个随机位置分布着7个随机颜色的珠子.通过移动珠子将同色的珠子连在一起来消除后得分.当无珠子可以移动时程序结束.让同 ...

  7. python +pygame 制作五子连珠小游戏

    python +pygame 制作五子连珠小游戏 学习python半年了,今天分享一个利用pygame制作的五子连珠游戏. 一.代码: 1.球类,ball.py """ ...

  8. 游戏更新-五子连珠-Android

    五子连珠游戏,在9×9方格中,鼠标点击小球移动,每当5个或5个以上同颜色的小球连成1行,则消去并得分,如果移动时没有得分,将随机产生三个小球,当游戏不能放3个球时游戏结束. 下载地址: http:// ...

  9. 分享:一个Java GUI五子连珠(附码云源码)~~~

    最近有时间,就整理整理自己大二时的作业(五子连珠游戏,已实现统计各色色球数.炸弹道具.最高分.重新开始等功能,这是基于网上找的一个五子连珠游戏,做了小修改)~ 该作业的码云链接:https://git ...

最新文章

  1. shutdown -s -t XXX
  2. [Java]ping或扫描端口的工具类
  3. 洛谷P1020/CODEVS1044 导弹拦截(拦截导弹)
  4. 实验18:使用@Autowired注解实现根据类型实现自动装配★
  5. 吴恩达 coursera AI 专项三第一课总结+作业答案
  6. java doc说明书_JAVADOC 常见使用方法 帮助文档
  7. 使用Androidkiller编译APK文件时出现libpng error: Not a PNG file的错误
  8. 从零开始学keras之预测房价
  9. 【转】内核通信之 Netlink 源码分析和实例分析
  10. 洛谷试炼场 没了 不见了?
  11. Markdown表情关键字大全
  12. ERROR: could not access file $libdir/postgis-2.3: No such file or director解决方法
  13. SQL Server 2000 数据库安装与配置图文教程
  14. OpenJ_Bailian - 3164 奇偶排序
  15. android播放系统音效,Android用SoundPool播放音效
  16. 蒙特卡洛方法求圆周率
  17. 依赖注入依赖注入容器
  18. Hadoop完全分布式集群总结
  19. 【STM8L】Active-Halt模式下的低功耗
  20. python二维码识别读取_python+opencv检测图片中二维码

热门文章

  1. 来自Bitly的USA.gov数据,数据分析案例
  2. 【其他】记录这次曲折的域名选择及搜索引擎seo优化的过程
  3. 串口之DCB结构体详解
  4. 《青浦区加快发展跨境电子商务实施细则(审议稿)》
  5. PDF转CAD在线怎么转换?分享个在线转换的方法
  6. 基于java+ssm+mysql的大学生考勤管理系统及智能分析系统
  7. docker 搭建frp内网穿透以及frp详细使用
  8. 区块链之开发命令行操作模块
  9. 移动OA,为企业提供更高效的办公模式
  10. 【poj-1066】判断线段相交