c#飞行棋

这是一个很经典的题目。

明白规则

我们首先要了解这个游戏到底是怎么玩的,首先会有不同图形构成的地图,到达不同的图形代表发生不同的事件,我们通过掷骰子选择往前走几步或者往后退几步,比如走到幸运转盘的时候,用户可选择方案:交换位置或者是让对方退回原点,走到地雷就往回退六个格,暂停就不进不退,让对方走,如果走到时空隧道,前进十个图形。按照这样的规则,谁先走到最后一个格谁就获胜。

这个游戏最重要的是画地图的逻辑和行走的逻辑。

画图逻辑

首先我们来梳理一下画地图的逻辑:
当我们看见这个地图的时候我们的反应是什么?我们可能会想,为什么这个地图是弯折的?它原来是不是直着的一百个图形?图形的排布有没有规律呢?我们能否采取一些行动让相同的图形放在一起呢?我们要带着问题去看视频更有利于我们学习逻辑。

首先我们不考虑这个地图整体形状,我们观察到这个地图是由100个图形构成的。于是我们定义了一个长度为100的数组来放置这些图形。

  public static int[] Map = new int[100];//声明一个长度为100的数组用来画地图

然后我们就初始写了一个方法初始化地图

public static void InitMap()
        {
            //初始化地图
            //我用0表示普通,显示给用户就是 □
            //....1...幸运轮盘,显示组用户就 ◎
            //....2...地雷,显示给用户就是 ☆
            //....3...暂停,显示给用户就是 ▲
            //....4...时空隧道,显示组用户就 卐
            int[] luckyturn = { 6, 23, 40, 55, 69, 83 };//幸运轮盘◎
            int[] landMine = { 5, 13, 17, 33, 38, 50, 64, 80, 94 };//地雷☆
            int[] pause = { 9, 27, 60, 93 };//暂停▲
            int[] timeTunnel = { 20, 25, 45, 63, 72, 88, 90 };//时空隧道卐
            //把数组中下标为6, 23, 40, 55, 69, 83的地方的值改为1
	     for (int i = 0; i < luckyturn.Length; i++)
            {
                //int temp = luckyturn[i];
                Map[luckyturn[i]] = 1;
            }
             for (int i = 0; i < landMine.Length; i++)
            {
                Map[landMine[i]] = 2;
            }
            for (int i = 0; i < pause.Length; i++)
            {
                Map[pause[i]] = 3;
            }
            for (int i = 0; i < timeTunnel.Length; i++)
            {
                Map[timeTunnel[i]] = 4;
            }
       }



我们把不同位置的相同图形放在了一起,然后给这些相同的图形放在一个相同的索引中。这样就更加便于管理。
因为涉及要画地图我们就要考虑到玩家的问题我们是不是要考虑玩家是不是在地图上呢?
于是我们声明了一个数组用来存放玩家A和玩家B

public static int[] PlayerPos = new int[2] { 0, 0 };//声明一个数组用来存玩家A和玩家B

我们现在就要把我们刚才放在一起的位置数字用图形表示出来了

public static string DrawStringMap(int pos)
        {
        	string temp = "";
        	#region 画第一行的逻辑
                //如果玩家A和玩家B在一起并且在地图上就画<>
                if (PlayerPos[0] == PlayerPos[1] && PlayerPos[0] == pos)
                {
                Console.ForegroundColor = ConsoleColor.Yellow;
                temp = "<>";
                }
                else if (PlayerPos[0] == pos)//如果玩家A在地图上就画A
           	 {
                Console.ForegroundColor = ConsoleColor.Yellow;
                temp = "A";
            	}
        	 else if (PlayerPos[1] == pos)//如果玩家B在地图上就画B
           	 {
                Console.ForegroundColor = ConsoleColor.Yellow;
                temp = "B";
            	}
            	else   //如果玩家A和玩家B不在一起也不在这个坐标上就画该显示的地图图标
            	{
	            		switch (Map[pos])
	              		  {
	              		  	 case 0:
	              		  	 	 Console.ForegroundColor = ConsoleColor.Gray;
                       			 	temp = "□";
                       				 break;
                       			 case 1:
                        			Console.ForegroundColor = ConsoleColor.Red;
                       				 temp = "◎";
                        			 break;
                        		 case 2:
                       				 Console.ForegroundColor = ConsoleColor.Blue;
                      				  temp = "☆";
                       				 break;
                        		case 3:
			                        Console.ForegroundColor = ConsoleColor.Green;
			                        temp = "▲";
			                        break;
					case 4:
                        			Console.ForegroundColor = ConsoleColor.Magenta;
                        			temp = "卐";
                       				 break;
				 }//end switch
               }//end else
		 return temp;
            #endregion
	  }

上面这些行为我们都是只关注了图形的各自的形状,现在我们就要宏观来看了,我们的地图是拐弯的,当我们作为一个人的时候可以很清楚知道地图可以像一个绳子一样进行弯折,但是电脑如何知道呢?这里我们就要动点小手脚,当我们画完第一行,该画列的时候我们完全可以看作前面全是空格的一行,那这样还跟第一行有区别吗?
首先我们要用for先画出来第一行,只有画出来第一行以后我们才能在这个基础上进行思考,这就是我们进行i+1学习时的i,而我们学习第一列或者第二行的时候只需要在这个基础上做出一点点改变。比如当我们画第二行的时候只需要把第一行for倒序是输出(即forr),改变初始值和终值即可。第二列又和第一列一样(甚至不需要填补前边的空格,只需要换行)当我们做完每行每列以后,我们可以把相同画法的行抽象出来,因为我们发现第一行和第三行的画法只有for的初始和结束值是不一样的,剩下都是一样的。于是就有了下面的方法。

public static void DrawMapLeftToRight(int left, int right)
{
for (int i = left; i <= right; i++)
{
                Console.Write(DrawStringMap(i));//调用上边画图的方法
}
}     

有了上述的方法我们就可以简写第一行还有第三行的代码,剩下部分代码大家自行作参考。

 public static void DrawMap() 
{	 //第一行 
	 DrawMapLeftToRight(0, 29); 
	 //第一竖列 
 	for (int i = 30; i <= 34; i++) 	
 	{ 
 		for (int j = 0; j <= 28; j++) 	
 		{   
 			Console.Write("  ");//强调这里是Write而不是WriteLine 
 	        }       
 	Console.WriteLine(DrawStringMap(i));//调用画图的方法
 	 } 
 	//第二横行                  
 	  for (int i = 64; i >= 35; i--)              
         {
         	Console.Write(DrawStringMap(i));
          }
          //第二竖行
          for (int i = 65; i <= 69; i++)
            {	
            	 Console.WriteLine(DrawStringMap(i));
            }
          //第三横行
          DrawMapLeftToRight(70, 99);
	 Console.WriteLine();
	 }

下面我们来关注输入姓名部分代码的书写
首先我们应该定义一个数组来放置两个用户的姓名

 public static string[] PlayerNames = new string[2];

然后根据我们的逻辑写出输入用户名字部分的代码

	    Console.WriteLine("请输入玩家A的姓名");
            PlayerNames[0] = Console.ReadLine();
             while (PlayerNames[0]=="")
            {
            	Console.WriteLine("玩家A的姓名不能为空,请重新输入");
                PlayerNames[0] = Console.ReadLine();
	     }
	     Console.WriteLine("请输入玩家B的姓名");
             PlayerNames[1] = Console.ReadLine();
             while (PlayerNames[1] == PlayerNames[0] || PlayerNames[1] == "")
            {
              if (PlayerNames[1] == PlayerNames[0])
                {
                  Console.WriteLine("玩家B的姓名和玩家A的姓名[{0}]不能相同相同",PlayerNames[0]);
                }
              else
                {  
                 Console.WriteLine("玩家B的姓名为空,请重新输入");
                }
                PlayerNames[1] = Console.ReadLine();

然后我们开始写掷骰子的代
这里我们就又要强调游戏规则,如果忘了可以回去看看。

public static void RowTouZi(int playerPos)
        {
            Random r = new Random();
            int num = r.Next(1,7);
             string msg = "";  //存放要显示的字符
            Console.WriteLine("{0}按任意键开始掷骰子", PlayerNames[playerPos]);
            ConsoleKeyInfo coninfo = Console.ReadKey(true);
            Console.WriteLine("{0}掷出了{1}", PlayerNames[playerPos], num);
            Console.WriteLine("{0}按任意键开始行动.....", PlayerNames[playerPos]);
            Console.ReadKey(true);
            PlayerPos[playerPos] += num;
            CheckPos();//这是写的一个方法防止超出范围
            if (PlayerPos[playerPos] == PlayerPos[1 - playerPos])
            {
            	 msg=string.Format("玩家{0}踩到了玩家{1},玩家{2}退6格", PlayerNames[playerPos], PlayerNames[1 - playerPos], PlayerNames[1 - playerPos]);
                PlayerPos[1 - playerPos] -= 6;
                CheckPos();
            }
             else
            {
            	switch (Map[PlayerPos[playerPos]])
                {
                	 case 0: msg="行动完了"; break;
                         case 1:
                        msg = string.Format("{0}走到了幸运轮盘,请选择 1----交换位置,2----轰炸对方",             		PlayerNames[playerPos]);
                        int number = ReadInt(msg, 1, 2);//这里是写的一个方法让用户只能输入1或者2
                        if (number == 1)
                        {
				int temp = 0;
                            temp = PlayerPos[playerPos];
                            PlayerPos[playerPos] = PlayerPos[1 - playerPos];
                            PlayerPos[1 - playerPos] = temp;
                            msg=string.Format("玩家{0}选择了与玩家{1}交换位置", PlayerNames[playerPos], PlayerNames[1 - playerPos]);

			}
			 else
			 {
				 PlayerPos[1 - playerPos] = 0;
                            msg=string.Format("玩家{0}选择轰炸玩家{1}", PlayerNames[playerPos], PlayerNames[1 - playerPos]);

			 }
		           break;

			 case 2:
			 //踩到地雷了
                        msg="恭喜你,能踩到地雷,百年不遇,退6格";
                        PlayerPos[playerPos] -= 6;
                        CheckPos();
                        break;

			 case 3:
                        msg="踩到暂停了";
                        flag[playerPos] = true;
                        break;

			 case 4:
                        msg="恭喜你,这个猥琐家伙竟然穿越了10步";
                        PlayerPos[playerPos] += 10;
                        CheckPos();
                        break;

		}
           }
           Console.Clear();
            DrawMap();
            Console.WriteLine(msg);
        }



下面我们来梳理一下上面代码出现的一些陌生的方法下面
下面这个方法是防止出界报错的。

public static void CheckPos()
        {
        	if (PlayerPos[0]>99)
            {
                PlayerPos[0] = 99;
            }
		if (PlayerPos[1]>99)
            {
                PlayerPos[1] = 99;
            }
               if (PlayerPos[0]<0)
            {
                PlayerPos[0] = 0;
            }
             if (PlayerPos[1]<0)
            {
                PlayerPos[1] = 0;
            }
}

还有下面这个方法,根据传过来的数字返回一个数字(主要用于判断踩到幸运转盘的时候用户的选择)

public static int ReadInt(string msg, int min, int max)
        {
        	while (true)
            {
            	 try
                {
                	Console.WriteLine(msg);
                    int number = Convert.ToInt32(Console.ReadLine());
                    if (number >= min && number <= max)
                    {
                    	return number;
                    }
                    else
                    {
                    	Console.WriteLine("你的输入不合法!只能输入{0}到{1}之间的数字!", min, max);
                        continue;
                    }
                 }
                catch
                {
                	 Console.WriteLine("输入有误,请重新输入!");
                }
                }
                    	

写到这里我们的大部分的问题都能解决了,但是我们发现我们还是不能实现用户交替投掷骰子。那现在我们就要再声明一个数组来实现用户交替投掷骰子的需求。

 public static bool[] flag = new bool[] { false,false};
while (PlayerPos[0]<=99 && PlayerPos[1]<=99)
{
//玩家A掷骰子
	if (flag[0]==false)
        {
              RowTouZi(0);
        }
        else
         {
              flag[0] = false;
         }
         if (PlayerPos[0]==99)
          {
                    Console.WriteLine("恭喜玩家A胜利了");
                    break;
          }        
//玩家B掷骰子
 	if (flag[1]==false)
         {
            RowTouZi(1);
         }
         else
         {
		 flag[1] = false;
	  }
	

          if (PlayerPos[1]==99)
           {
              Console.WriteLine("恭喜玩家B胜利了");
               break;
           }


                Console.WriteLine("行动完毕...");
         }

Console.ReadKey();

}

上面就是我的一点见解,欢迎斧正!

实现飞行棋游戏是一个有趣的项目,可以锻炼编程和游戏设计能力。以下是一些技术点和它们的意义,可供你在C#中实现飞行棋游戏时参考:

  1. 图形界面设计:
    使用C#的Windows Forms或WPF等技术创建游戏的图形界面。这涉及到游戏棋盘、棋子、控制按钮等的布局和显示。

  2. 游戏逻辑设计:
    定义游戏的规则、玩家操作流程、棋子移动规则等。确保游戏逻辑合理且易于理解。

  3. 随机数生成:
    飞行棋中通常有骰子掷出的随机数,这可以通过C#的随机数生成器来实现。确保随机数生成具有足够的随机性。

  4. 状态管理:
    跟踪游戏的状态,例如玩家的位置、得分、是否可以进行操作等。合理的状态管理可以确保游戏逻辑的正确性。

  5. 玩家交互:
    处理玩家的点击事件、键盘输入等,使玩家能够掷骰子、选择移动的棋子等操作。

  6. 碰撞检测:
    检测棋子在棋盘上的位置,判断是否与其他棋子或特殊格子(如障碍、加速等)发生碰撞。这对游戏的玩法和策略有很大影响。

  7. 动画效果:
    为了增加游戏的趣味性,可以考虑为棋子移动、骰子掷出等操作添加动画效果,提升用户体验。

  8. 多人模式和单人模式:
    考虑实现多人对战模式和单人模式,让游戏更加灵活和有趣。

  9. 数据持久化:
    如果需要保存游戏进度、玩家成绩等信息,可以使用文件操作或数据库来实现数据的持久化。

  10. 测试和调试:
    编写测试用例来验证游戏的各种情况,以确保游戏逻辑的正确性。同时,利用调试工具进行问题排查和修复。

意义在于,通过实现飞行棋游戏,你可以练习图形界面设计、游戏逻辑编写、用户交互设计等多方面的技能。这个项目还可以锻炼解决问题的能力和创造力,同时为你展示如何将编程知识应用于实际项目中。最终,你将获得一个可供自己和他人玩耍的游戏作品。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

balance…

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

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

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

打赏作者

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

抵扣说明:

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

余额充值