手把手教初学Windows程序设计的小童鞋如何用VS(05版以上)写一个像模像样的象棋游戏(原创)

首先呢,想编写这个程序的同学一定是要下过象棋的(国际的,中国的都可以),就算没下过,也应该看别人下过吧。 好吧..你也没见别人下过,那你至少应该知道象棋长什么样子吧...

首先选择自己的武器,如果你觉得自己够牛的话,那我建议你选择C语言,编译环境为turboC. 然后拥有超强的记忆力记下一大堆的API函数..然后开始动工.. 最后在花上远远多过编写代码所用时间的时间来调试自己的程序(你也就不用再看这篇日志了)

或者说,你觉得自己一般牛B,选择MFC来编写这个程序,虽然说武器是先进了一些,但是依然无法从根本上解决API函数的复杂性。

如果你想用VB6来编写类似于这样的一个Windows程序,可以是可以。但是你永远无法学习到OOP的真正价值所在。VB6不允许程序员在类与类之间建立“is a”的关系. 对于象棋这样的小型程序尚罢..



废话不说了.. 这次的例子我选择了基于.net framew 2.0以上的平台,C#语言来编写这样一个程序。 现在开始动工吧!





首先,你应该明确这是一个Windows Form程序, 而不是一个Console程序..(这一点应该都清楚吧) 所以在这个程序中会使用许多的资源,例如棋盘,棋子图形等等..所以第一步,你需要打开你的Photoshop, 开始绘制这些图片。

棋子的图片如下:








棋盘图片如下:







大家可以根据自己的不同爱好来制作自己的图片,本人一向没有什么审美能力,所以能有这样的图片已经是很尽力了..



准备工作做完后,现在开始编码:

首先打开VS2008 ,新建一个Windows Form Project, 在Projrect中添加第一个类ChessPiece。IDE会自动帮你生成一个ChessPiece.cs文件 和一个Form1.cs文件

现在删除IDE自动在ChessPiece.cs这个文件中的代码,开始自己编写代码:






using System;
using System.Drawing;



// 创建一个棋子类
class ChessPiece
{
  
   public enum Types //这是一个枚举类新,表示所有的棋子种类

   {
      KING,//国王
      QUEEN,//王后
      BISHOP,//象
      KNIGHT,//马
      ROOK,//车
      PAWN//卒
   }

   private int currentType; // 当前类型,用于后台控制当前应该绘制的图像
   private Bitmap pieceImage; // 用来储存当前图像当前图像

   // 初始化一个矩形区域,这个矩形区域就是以后棋盘的边缘,用于放置整个棋盘和棋盘上的棋子
   private Rectangle targetRectangle =
      new Rectangle( 0, 0, 75, 75 );

   // 构造函数
   public ChessPiece( int type, int xLocation,
      int yLocation, Bitmap sourceImage )
   {
      currentType = type; // 设置棋子类型
      targetRectangle.X = xLocation; // 设置X坐标
      targetRectangle.Y = yLocation; // 设置Y坐标

      // 将棋子的子图从大图资源中切割下来
      pieceImage = sourceImage.Clone(
         new Rectangle( type * 75, 0, 75, 75 ),
         System.Drawing.Imaging.PixelFormat.DontCare );
   }

   // 绘制棋子
   public void Draw( Graphics graphicsObject )
   {
      graphicsObject.DrawImage( pieceImage, targetRectangle );
   }

   // 获得当前棋子坐标
   public Rectangle GetBounds()
   {
      return targetRectangle;
   }

   // 设置当前棋子坐标
   public void SetLocation( int xLocation, int yLocation )
   {
      targetRectangle.X = xLocation;
      targetRectangle.Y = yLocation;
   }
}


好了,这个棋子类已经写完。那应该如何让这些“死”代码活灵活现的呈现在大家面前呢?

我们还需要一个前台呈现类,也就是我们常说的UI类,或者叫Interface。

现在,将刚才在PS中绘制好的图片放置在bin/Release/images目录下



将之前IDE自动生成的Form1.cs改名为ChessGame.cs (不改也可以)

删除IDE自动生成的代码,然后自己编码:

//  ChessGame.cs
//该文件实现棋盘和棋子的初始化,部署,和在窗口和面板上的绘制,移动,刷新等工作
using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;


public partial class ChessGame : Form
{
   private ArrayList chessTile = new ArrayList(); // 创建一个动态数组储存棋盘的图片
   private ArrayList chessPieces = new ArrayList(); // 创建一个动态数组储存棋子的图片
   private int selectedIndex = -1; // 所选图片的索引
   private int[ , ] board = new int[ 8, 8 ]; // 将棋盘用一个 8 * 8 的数组来表示
   private const int TILESIZE = 75; // 将每个图片快定义为 75 像素

   // 初始化构造函数
   public ChessGame()
   {
      // 对主窗口进行生成
      InitializeComponent();
   }

   // 将棋盘的4种图片快存入动态数组chessTile中并对棋子进行部署
   private void ChessGame_Load( object sender, EventArgs e )
   {
     
      chessTile.Add( Bitmap.FromFile( @"images\lightTile1.png" ) );
      chessTile.Add( Bitmap.FromFile( @"images\lightTile2.png" ) );
      chessTile.Add( Bitmap.FromFile( @"images\darkTile1.png" ) );
      chessTile.Add( Bitmap.FromFile( @"images\darkTile2.png" ) );

      ResetBoard(); //对棋盘上棋子进行部署
      Invalidate(); // 刷新主窗口
   }

   // 具体实现对棋盘的部署
   private void ResetBoard()
   {
      int current = -1;
      ChessPiece piece;
      Random random = new Random();
      bool light = false;
      int type;

      chessPieces.Clear(); // 确保此时棋子的动态数组为空

      // 将白方棋子大图放入临时变量whitePieces中
      Bitmap whitePieces =
         ( Bitmap ) Image.FromFile( @"images\whitePieces.png" );

      // 将黑方棋子大图放入临时变量blackPieces中
      Bitmap blackPieces =
         ( Bitmap ) Image.FromFile( @"images\blackPieces.png" );

      // 从上到下先部署白色棋子
      Bitmap selected = whitePieces;

      // 遍历棋盘的行
      for ( int row = 0; row <= board.GetUpperBound( 0 ); row++ )
      {
         // 当到达第 6 行时开始部署黑色棋子
         if ( row > 5 )
            selected = blackPieces;

         // 第二层遍历 遍历棋盘的列
         for ( int column = 0;
            column <= board.GetUpperBound( 1 ); column++ )
         {
            // 部署第一行和最后一行上的棋子
            if ( row == 0 || row == 7 )
            {
               switch ( column )
               {
                  case 0:
                  case 7: // 部署车
                     current = ( int ) ChessPiece.Types.ROOK;
                     break;
                  case 1:
                  case 6: // 部署马
                     current = ( int ) ChessPiece.Types.KNIGHT;
                     break;
                  case 2:
                  case 5: // 部署象
                     current = ( int ) ChessPiece.Types.BISHOP;
                     break;
                  case 3: // 部署国王
                     current = ( int ) ChessPiece.Types.KING;
                     break;
                  case 4: // 部署王后
                     current = ( int ) ChessPiece.Types.QUEEN;
                     break;
               }

               // 确定当前棋子的信息
               piece = new ChessPiece( current,
                  column * TILESIZE, row * TILESIZE, selected );

               chessPieces.Add( piece ); // 将子图存入动态数组中
            } // end if

            // 当遍历到第二行和倒数第二行
            if ( row == 1 || row == 6 )
            {
               piece = new ChessPiece(
                  ( int ) ChessPiece.Types.PAWN,
                  column * TILESIZE, row * TILESIZE, selected );
               chessPieces.Add( piece ); // 将卒的子图存入动态数组
            }

            type = random.Next( 0, 2 ); // 随即决定棋盘的格式

            if ( light ) // 设置亮棋盘
            {
               board[ row, column ] = type;
               light = false;
            }
            else // 设置暗棋盘
            {
               board[ row, column ] = type + 2;
               light = true;
            }
         }

         light = !light; // 让亮色和暗色棋盘交替出现
      }
   }

   // 在主窗口中绘制棋盘
   private void ChessGame_Paint( object sender, PaintEventArgs e )
   {
      Graphics graphicsObject = e.Graphics; // 实例化一个图形对象
      graphicsObject.TranslateTransform( 0, 28 ); // 将棋盘整体下沿一定的距离,防止菜单栏挡住棋盘

      for ( int row = 0; row <= board.GetUpperBound( 0 ); row++ )
      {
         for ( int column = 0;
            column <= board.GetUpperBound( 1 ); column++)
         {
            // 绘制棋盘
            graphicsObject.DrawImage(
               ( Image ) chessTile[ board[ row, column ] ],
               new Point( TILESIZE * column, ( TILESIZE * row ) ) );
         }
      }
   }
   // 绘制棋盘上的棋子
   private void pieceBox_Paint(
      object sender, System.Windows.Forms.PaintEventArgs e )
   {
      // 便利整个动态数组
      for ( int i = 0; i < chessPieces.Count; i++ )
         GetPiece( i ).Draw( e.Graphics );
   }
  
  //检测鼠标点击区域是否有棋子
   private int CheckBounds( Point point, int exclude )
   {
      Rectangle rectangle; // current bounding rectangle

      for ( int i = 0; i < chessPieces.Count; i++ )
      {
         // 获得鼠标区域坐标信息
         rectangle = GetPiece( i ).GetBounds();

       
         if ( rectangle.Contains( point ) && i != exclude )
            return i;
      }

      return -1;
   }



   // 鼠标点击左键产生事件
   private void pieceBox_MouseDown(
      object sender, System.Windows.Forms.MouseEventArgs e )
   {
      // 获得点击棋子的索引号
      selectedIndex = CheckBounds( new Point( e.X, e.Y ), -1 );
   }

   // 如果选中棋子,移动产生事件
   private void pieceBox_MouseMove(
      object sender, System.Windows.Forms.MouseEventArgs e )
   {
      if ( selectedIndex > -1 )
      {
         Rectangle region = new Rectangle(
            e.X - TILESIZE * 2, e.Y - TILESIZE * 2,
            TILESIZE * 4, TILESIZE * 4 );

         // 将图片中心设置为鼠标所在区域
         GetPiece( selectedIndex ).SetLocation(
            e.X - TILESIZE / 2, e.Y - TILESIZE / 2 );

         pieceBox.Invalidate( region ); // 局部刷新棋子移动后的棋盘
      }
   }

   // 将棋子放下时产生的事件
   private void pieceBox_MouseUp( object sender, MouseEventArgs e )
   {
      int remove = -1;

      // 如果棋子被选中
      if ( selectedIndex > -1 )
      {
         Point current = new Point( e.X, e.Y );
          //调整棋子放下时的位置
         Point newPoint = new Point(
            current.X - ( current.X % TILESIZE ),
            current.Y - ( current.Y % TILESIZE ) );

         // 检查该位置是否具有其他棋子
         remove = CheckBounds( current, selectedIndex );

         // 将新棋子放在新位置上
         GetPiece( selectedIndex ).SetLocation( newPoint.X, newPoint.Y );
         selectedIndex = -1; // 将棋子索引还原为-1,为下一次移动做准备

         //如果有棋子,将其移除并换上新棋子
         if ( remove > -1 )
            chessPieces.RemoveAt( remove );
      }

      pieceBox.Invalidate(); // 刷新棋盘
   }

  //获得棋子
   private ChessPiece GetPiece( int i )
   {
      return ( ChessPiece ) chessPieces[ i ];
   }

  //点击New Game重新开始游戏
   private void newGameItem_Click(
      object sender, System.EventArgs e )
   {
      ResetBoard(); // 重新部署棋盘
      Invalidate(); // 刷新主窗口
   }
}



ok!大功告成了~运行结果看看吧:









如果有兴趣,你还可以为自己的象棋添加AI,让电脑自动下棋。 不过这就不属于这个例子的范围之类了。

总之,对于那些刚刚从控制台程序转向Windwos程序的同学来说,初次这个程序可能稍微
有些困难,不过只要你能硬着头皮看下去,认真对待每一行代码,相信对你的编程能力会有提高的。

你可能感兴趣的:(游戏,windows,ide,mfc,中国移动)