PiMuseum-游戏开发入门级教程-中国象棋-Chapter-2

  • 导语
  • 中国象棋-数据建模
    • 棋子数据模型
      • 棋子-马
    • 棋盘数据操作
  • 源码链接

导语

在上一篇系列文章中国象棋 - Chapter 1中阐述了象棋映射到数据模型的抽象分析,在本篇章中,我们将继续聊聊马棋子的实现,并一起分析 ChessHelper类(象棋全局操作类) 的功能实现,整个游戏规则在代码层面上实现后就可以去搭建传说中的引擎桥梁,我们加快脚步,走起。

中国象棋-数据建模

棋子数据模型

棋子-马

我认为象棋中最具艺术的棋子就是马了,马走日,是坐标中稍微有迷惑性的一个变换,同时马还有蹩脚限制,可以在象棋下棋过程中造成很多局势,分析一下,马走日的走法映射数据模型条件即是:马棋子横向移动距离2,纵向为1 或者 横向移动距离1,纵向为2,除了落子点不能为同色棋子外,我们还需要去判断马是否蹩脚,那么如何判断是都蹩脚呢,我们能根据落点坐标和棋子本身坐标来计算出似的该次棋子移动的坐标值,如果这个坐标值上存在棋子,则蹩脚,我们先判断马是横向走日还是纵向走日,如果是横向走日,则憋足位置X轴坐标相同,Y轴坐标取马棋子和目标位置的Y轴中点值,如果是纵向走日,则憋足位置Y轴坐标相同,X轴坐标取马棋子和目标位置的X轴中点值,得出憋足坐标后,根据坐标查询该棋盘是否存在棋子,若有棋子则蹩脚,不满足下棋条件,马棋子的实现类如下:

class HorseChessman(chessType: ChessType, position: Position) : Chessman(chessType, position) {override fun chessmanRule(nextPosition: Position): Boolean {//马走日,横向移动距离2,纵向为1 或者 横向移动距离1,纵向为2return ((Math.abs(nextPosition.column -position.column) == 1 && Math.abs(nextPosition.row -position.row) == 2)|| (Math.abs(nextPosition.column -position.column) == 2 && Math.abs(nextPosition.row -position.row) == 1))}override fun chessboardRule(chessboardInfo: Array<Array<Chessman?>>, nextPosition: Position): Boolean {ChessLogic.isExistChessman(chessboardInfo,nextPosition)?.let { chessman->if (chessman.chessType == this@HorseChessman.chessType) return false//同色棋子不能被吃}//先判断是行向蹩脚还是列向蹩脚if (Math.abs(nextPosition.row - position.row) == 2) {//行蹩脚val queryRow = (nextPosition.row + position.row)/2ChessLogic.isExistChessman(chessboardInfo, Position(queryRow, position.column))?.let {return false}} else {//列蹩脚val queryColumn = (nextPosition.column + position.column)/2ChessLogic.isExistChessman(chessboardInfo, Position(position.row, queryColumn))?.let {return false}}return true}override fun chessmanName(): String {return "马"}
}

其他棋子的实现方式也都大同小异,都是在下棋操作前判断2个约束条件,在自我的走法和棋盘对该次移动的约束下仍然不能阻止本次棋子移动,则产生单次下棋行为,至此,一个象棋游戏的实体元素就已经完成,接下来,我们需要把下棋操作,输赢判断的方法在ChessHelper 中完善。

棋盘数据操作

除了上一篇文章中在ChessHelper中已存在棋盘坐标信息,我们还需要新增:当前回合标识,当前提起的棋子,当前的操作状态(自由态,提子态),一个关于棋盘操作的观察者(主要用来通知引擎做对应操作的渲染),因此,我们在ChessHelper中增加对应的成员变量:

object ChessHelper {//十行九列一共90个坐标点 ,这里为 10 * 11 表示 左下角的第一个点并非从 (0,0) 开始,而是从(1,1) 开始const val ColumnCapacity : Int = 10const val RowCapacity : Int = 11//棋盘上的二维坐标集信息,对应的索引取值是空值或者是棋子对象private var chessboardInfo : Array<Array<Chessman?>>= Array(RowCapacity ){Array<Chessman?>(ColumnCapacity) { null } }//己方视角下操作者 Typevar myRoleType = ChessType.Red//当前哪方回合private var turnFlag = ChessType.Red//当前选择棋子private var pickedChessman : Chessman? = null//当前操作状态var operationStatus : OperationStatus = OperationStatus.ChessFreedom//棋盘操观察者var observer : OperateObserver? = null...}

除了增加上述所需的信息,之后我们需要增加一些数据处理的方法,例如查询棋盘上棋子信息,提起棋子,放下棋子,棋盘载入棋子信息等等,这些方法无非是一些对标识值的改变及一些信息的传递,我们主要来分析一下下棋落子方法 moveChessman(nextPosition : Position) : MoveResult ,参数为将要落子点的坐标,返回一个执行下棋的结果,先上代码:

fun moveChessman(nextPosition : Position) : MoveResult {pickedChessman?.let { pickedChessman ->if (pickedChessman.position.column == nextPosition.column && pickedChessman.position.row == nextPosition.row) {observer?.onDropBack(pickedChessman)return@moveChessman MoveResult.DesIsSelf}//检测是否符合下棋规则,包括棋子约束和棋盘约束if (pickedChessman.chessmanRule(nextPosition)&& pickedChessman.chessboardRule(queryChessboardInfo(),nextPosition)) {//删掉落点处棋子ChessLogic.isExistChessman(queryChessboardInfo(),nextPosition)?.let { removeChessman->queryChessboardInfo()[removeChessman.position.row][removeChessman.position.column] = nullobserver?.onRemoveChessman(removeChessman)if (removeChessman is GeneralChessman) return@moveChessman MoveResult.GameOver}/*** 主要逻辑步骤:*///置空旧坐标对 picked chess的引用queryChessboardInfo()[pickedChessman.position.row][pickedChessman.position.column] = nullobserver?.onMoveChessman(pickedChessman.position.row,pickedChessman.position.column)//更新被选择棋子的坐标信息pickedChessman.updateChessmanPosition(nextPosition.row,nextPosition.column)//新的落点处坐标数组中对应索引指向选择棋子对象queryChessboardInfo()[nextPosition.row][nextPosition.column] = pickedChessman//切换回合turnFlag = if (turnFlag == ChessType.Red) ChessType.Blackelse ChessType.Redreturn@moveChessman MoveResult.Success}return@moveChessman MoveResult.UnSupportChessRule}return MoveResult.NoPickedChessMan}

下棋方法主要有这么几个步骤:
1.首先判断是否有存在被提起的棋子(提子态),否则中断逻辑。
2.存在被提起的棋子,看下棋的目的坐标跟当前提起棋子坐标是否一致,如果一直则表示放下棋子,取消提起状态。
3.判断棋子的自我约束 chessmanRule 和棋盘约束 chessboardRule,若都通过,则移除目的坐标可能存在的棋子,更新提起棋子的坐标信息,如果移除的棋子是 将/帅 棋子,则游戏结束。
4.若游戏没有结束,则置空当前被提起棋子的引用,棋盘恢复到自由状态,最后切换回合。

(上述下棋步骤还缺少一个游戏规则的判定,你找出来了吗,我们下一篇再聊)

如此一来,整个象棋的数据模型已经初步代码实现,你可以以使用源码中 model 包下的类,并写一个终端输入类,通过传入每次想要下棋的目标的坐标,然后调用ChessHelper中的方法,可以初步在控制台上打印每次操作以后的棋盘信息返回操作失败的原因

对具象的游戏规则抽象后,并通过代码转换成数据模型,可以说象棋游戏已经完成一半,当然,该例只是一个过程化的游戏数据模型的设计,方便理解加深一个游戏规则抽象为数据模型的印象,在代码层面上还可以进行大量的优化,比如把棋盘信息的二维数组转换成一位数组提高查询效率,比如可以把ChessHelper继续拆分成棋盘类和操作类,降低元素类和操作类的耦合,比如可以设计最优走法功能等等,记住:代码永远没有自己认为的最优状态,它只有不断优化状态。在下一篇中,我们将聊聊游戏引擎并如何利用游戏引擎帮我们创造一个完整的象棋游戏。

源码链接

中国象棋 数据抽象模块在该项目 : Source Code
core/src/com/pimuseum/game/chinesechess/model 路径下

PiMuseum-游戏开发入门级教程-中国象棋-Chapter-2相关推荐

  1. PiMuseum-游戏开发入门级教程-中国象棋-Chapter-3

    PiMuseum-游戏开发入门级教程-中国象棋-Chapter-3 导语 游戏引擎 游戏引擎概念及其组件 光影效果 动画组件 物理系统 渲染系统 通信模块 初始化游戏引擎 选择游戏引擎 接入游戏引擎 ...

  2. PiMuseum-游戏开发入门级教程-中国象棋-Chapter-1

    PiMuseum-游戏开发入门级教程-中国象棋-Chapter-1 导语 游戏本质 中国象棋-数据建模 棋盘数据模型 棋子数据模型 棋子-車 棋子-兵/卒 源码链接 导语 <头号玩家>的问 ...

  3. cocos2d-x游戏开发系列教程-中国象棋02-main函数和欢迎页面

    之前两个博客讲述了象棋的规格和工程文件之后,我们继续深入的从代码开始学习cocos2dx 首先从程序入口main函数开始 main函数 int APIENTRY _tWinMain(HINSTANCE ...

  4. cocos2d x游戏开发系列教程 中国象棋01 工程文件概述

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 上一篇博 ...

  5. Unity 创建2D平台游戏开发学习教程

    了解如何使用C#在Unity中创建您的第一款2D平台游戏 你会学到什么 使用Unity创建2D奥运会 使用可脚本化的对象和单一模式 使用良好的编程实践 创造武器和射弹 使用可脚本化的对象和委托模式创建 ...

  6. UE4蓝图无代码编程游戏开发技能学习教程

    在虚幻引擎4中创建.设计和开发自己的游戏,无需编码 你会学到什么 虚幻引擎4中使用蓝图的游戏开发(无代码编程) 使用行业标准方法的游戏设计 使用Maya进行三维设计 在本课程中创建您的第一个游戏 Ga ...

  7. C#和Unity编码和游戏开发学习教程

    MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz,2 Ch 语言:英语+中英文字幕(根据原英文字幕机译更准确) |时长:110节课(26小时25分钟)|大小解压后:18.6 ...

  8. Unity2D游戏开发基础教程1.2项目、资源和场景

    Unity2D游戏开发基础教程1.2项目.资源和场景 如果使用Unity制作游戏,就一定会接触到项目(Project.资源(Asset)和场景(Scene).本节将依次介绍它们. 1.2.1  项目 ...

  9. Unity2D游戏开发基础教程1.2 项目、资源和场景

    Unity2D游戏开发基础教程1.2 项目.资源和场景 如果使用Unity制作游戏,就一定会接触到项目(Project.资源(Asset)和场景(Scene).本节将依次介绍它们. 1.2.1  项目 ...

最新文章

  1. 使用C++ ostringstream来格式化字符串输出
  2. HTTP长连接和短连接以及推送服务原理(转)
  3. 计算机组成原理——程序查询方式、程序中断方式
  4. ajax修改按钮的html值,表格行的按钮AJAX后,怎么修改表格当前行的值
  5. ppt2010基础操作笔记
  6. 支持蓝牙的模拟器_PM 2032电池模拟器展会现场演示
  7. Javascript是最好的编程语言吗?
  8. 手机h5像素_h5移动端设备像素比dpr介绍
  9. httpwatch9.1 安装包
  10. 介绍一款 微信客服平台系统
  11. 南京社保详解(绝对有用)
  12. 对微信公众号JS安全域名的理解
  13. 【非洲秃鹫优化算法】基于非洲秃鹫优化算法求解多目标优化问题MOAVOA含Matlab源码
  14. 使用BP网络逼近函数-matlab
  15. 文本分割之垂直投影法基于OpenCV(python)的实现
  16. 实现简单的直播互动功能,直播软件源码是如何做的
  17. Unity编辑器知识
  18. Ubuntu 安装中文支持
  19. FZU Problem 2238 Daxia Wzc's problem(Lucas定理求组合数)
  20. 直播搭建主播pk,如何实现无缝切换?

热门文章

  1. 番茄助手Visual Assist X提示中文注释错误
  2. PCL库导入点云模型以及常见问题分析
  3. PS解决要求96和8之间的整数。已插入最接近的数值的问题
  4. 医学影像学和计算机,医学影像学中CR与DR有什么区别?【全】
  5. HypersonicSQLDB学习日记(一)
  6. 下载spotify音乐_如何在Amazon Echo上播放Spotify音乐
  7. LeetCode 91. 解码方法 Java/Python
  8. 从录屏的视频文件中提取PPT页面
  9. 微信小程序获取用户头像和昵称
  10. Adobe Reader\Acrobat打印设置中的“自动旋转并居中”功能有个大大的bug