admin管理员组

文章数量:815885

C#游戏编程:《控制台小游戏系列》之《七、键盘熟练工实例》

一、游戏分析

    相信大家对金山打字通、打字精灵等打字软件非常熟悉,想当年刚接触计算机时还靠它们的打字游戏练习打字基本功。这章的游戏DEMO就是一个打字小游戏,当然,它很简单,只是A~Z字母的打字练习小游戏。游戏的设计也不复杂,游戏开始时先初始化下落字母的个数和每次下落字母的概率,随着时间的进行,字母会从界面上方逐渐下落到界面底下,在这过程中你需要敲击键盘上与界面下落的字母相对应的键位,如果键位的字母与下落的字母一致,则记录打字正确数量,否则不记录,无论如何,敲击一次键盘记录一下敲击键盘数,当字母下落到界面底下时,字母停止下落,该字母被视为无效,相应地,也记录一次敲击数。正确率、敲击数等信息会在界面上显示,游戏还有倒计时,时间一到则被视为游戏结束。

二、游戏实现

   先定义一个字母表,字母表的实现为:   ///LetterTable类实现 [csharp]  view plain copy print ?
  1. using System;  
  2.   
  3. namespace Typing  
  4. {  
  5.     internal sealed class LetterTable  
  6.     {  
  7.         private static String[] LETTERS_TABLE =  
  8.         {  
  9.              "A", "B", "C", "D", "E", "F", "G",  
  10.              "H", "I", "J", "K", "L", "M", "N",  
  11.              "O", "P", "Q", "R", "S", "T", "U",   
  12.              "V", "W", "X", "Y", "Z"   
  13.         };  
  14.   
  15.         /// <summary>  
  16.         /// 获取字母表长度  
  17.         /// </summary>  
  18.         /// <returns></returns>  
  19.         public static Int32 getLength()  
  20.         {  
  21.             return LETTERS_TABLE.Length;  
  22.         }  
  23.   
  24.         /// <summary>  
  25.         /// 根据索引获取字母  
  26.         /// </summary>  
  27.         /// <param name="index"></param>  
  28.         /// <returns></returns>  
  29.         public static String getLetter(Int32 index)  
  30.         {  
  31.             return LETTERS_TABLE[index];  
  32.         }  
  33.   
  34.         /// <summary>  
  35.         /// 根据字母获取字母码  
  36.         /// </summary>  
  37.         /// <param name="letter"></param>  
  38.         /// <returns></returns>  
  39.         public static Int32 getLetterCode(String letter)  
  40.         {  
  41.             Int32 code = 65;  
  42.   
  43.             for (Int32 i = 0; i < LETTERS_TABLE.Length; i++)  
  44.             {  
  45.                 if (getLetter(i).Equals(letter))  
  46.                 {  
  47.                     code = 65 + i;  
  48.                 }  
  49.             }  
  50.             return code;  
  51.         }  
  52.     }  
  53. }  

   ///Letter类实现 [csharp]  view plain copy print ?
  1. using System;  
  2. using CEngine;  
  3. using CGraphics;  
  4.   
  5. namespace Typing  
  6. {  
  7.     internal class Letter  
  8.     {  
  9.         /// <summary>  
  10.         /// 字母  
  11.         /// </summary>  
  12.         private String m_letter;  
  13.         /// <summary>  
  14.         /// 字母码  
  15.         /// </summary>  
  16.         private Int32 m_code;  
  17.         /// <summary>  
  18.         /// 字母背景色  
  19.         /// </summary>  
  20.         private ConsoleColor m_backcolor;  
  21.         /// <summary>  
  22.         /// 字母前景色  
  23.         /// </summary>  
  24.         private ConsoleColor m_fontcolor;  
  25.         /// <summary>  
  26.         /// 字母活动情况  
  27.         /// </summary>  
  28.         private Boolean m_bAlive;  
  29.         /// <summary>  
  30.         /// 字母窗体位置  
  31.         /// </summary>  
  32.         private CPoint m_position;  
  33.         /// <summary>  
  34.         /// 随机数  
  35.         /// </summary>  
  36.         private static Random m_random = new Random();  
  37.   
  38.         /// <summary>  
  39.         /// 构造函数  
  40.         /// </summary>  
  41.         public Letter()  
  42.         {  
  43.   
  44.         }  
  45.   
  46.         public String getLetter()  
  47.         {  
  48.             return this.m_letter;  
  49.         }  
  50.   
  51.         public void setLetter(String letter)  
  52.         {  
  53.             this.m_letter = letter;  
  54.         }  
  55.   
  56.         public Int32 getCode()  
  57.         {  
  58.             return this.m_code;  
  59.         }  
  60.   
  61.         public void setCode(Int32 code)  
  62.         {  
  63.             this.m_code = code;  
  64.         }  
  65.   
  66.         public Char getChar()  
  67.         {  
  68.             return (Char)this.m_code;  
  69.         }  
  70.   
  71.         public ConsoleColor getBackcolor()  
  72.         {  
  73.             return this.m_backcolor;  
  74.         }  
  75.   
  76.         public ConsoleColor getFontcolor()  
  77.         {  
  78.             return this.m_fontcolor;  
  79.         }  
  80.   
  81.         public void setBackcolor(ConsoleColor color)  
  82.         {  
  83.             this.m_backcolor = color;  
  84.         }  
  85.   
  86.         public void setFontcolor(ConsoleColor color)  
  87.         {  
  88.             this.m_fontcolor = color;  
  89.         }  
  90.   
  91.         public Boolean getAlive()  
  92.         {  
  93.             return m_bAlive;  
  94.         }  
  95.   
  96.         public void setAlive(Boolean bAlive)  
  97.         {  
  98.             this.m_bAlive = bAlive;  
  99.         }  
  100.   
  101.         public CPoint getPosition()  
  102.         {  
  103.             return this.m_position;  
  104.         }  
  105.   
  106.         public void setPosition(CPoint point)  
  107.         {  
  108.             this.m_position = point;  
  109.         }  
  110.   
  111.         public void setPosition(Int32 x, Int32 y)  
  112.         {  
  113.             this.m_position.setX(x);  
  114.             this.m_position.setY(y);  
  115.         }  
  116.   
  117.         /// <summary>  
  118.         /// 随机得到一个字母  
  119.         /// </summary>  
  120.         public void newLetter()  
  121.         {  
  122.             setLetter(LetterTable.getLetter(m_random.Next(0, 26)));  
  123.             setCode(LetterTable.getLetterCode(getLetter()));  
  124.             setBackcolor((ConsoleColor)m_random.Next(0, 16));  
  125.             setFontcolor((ConsoleColor)(15 - (Int32)getBackcolor()));  
  126.             setPosition(m_random.Next(1, 30), -1);  
  127.             setAlive(false);  
  128.         }  
  129.   
  130.         /// <summary>  
  131.         /// 绘制字母  
  132.         /// </summary>  
  133.         /// <param name="draw"></param>  
  134.         public void draw(CDraw draw)  
  135.         {  
  136.             if (m_bAlive)  
  137.             {  
  138.                 draw.setBackcolor(this.m_backcolor);  
  139.                 draw.drawText(m_letter, m_position.getX(), m_position.getY(), 1, 1, this.m_fontcolor);  
  140.             }  
  141.             draw.setDrawSymbol(CSymbol.DEFAULT);  
  142.             draw.fillRect(m_position.getX(), m_position.getY() - 1, 1, 1, ConsoleColor.Black);  
  143.         }  
  144.     }  
  145. }  
   定义一个字母管理类,用于管理所有下落的字母和配置字母的下落个数和每次下落字母的数量,字母管理类定义为: ///LetterManager类实现 [csharp]  view plain copy print ?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using CGraphics;  
  4.   
  5. namespace Typing  
  6. {  
  7.     internal sealed class LetterManager  
  8.     {  
  9.         private List<Letter> m_letters;  
  10.         /// <summary>  
  11.         /// 下落字母的个数  
  12.         /// </summary>  
  13.         private Int32 m_fallNum;  
  14.         private Random m_random;  
  15.         private Int32 m_fallRate;  
  16.   
  17.         public LetterManager(Int32 num, Int32 rate)  
  18.         {  
  19.             this.m_random = new Random();  
  20.             this.m_fallNum = num;  
  21.             this.m_fallRate = rate;  
  22.   
  23.             this.m_letters = new List<Letter>();  
  24.   
  25.             for (Int32 i = 0; i < m_fallNum; i++)  
  26.             {  
  27.                 Letter letter = new Letter();  
  28.                 letter.newLetter();  
  29.                 this.m_letters.Add(letter);  
  30.             }  
  31.         }  
  32.   
  33.         public List<Letter> getLetters()  
  34.         {  
  35.             return this.m_letters;  
  36.         }  
  37.   
  38.         public Int32 getFallNum()  
  39.         {  
  40.             return this.m_fallNum;  
  41.         }  
  42.   
  43.         public Int32 getFallRate()  
  44.         {  
  45.             return this.m_fallRate;  
  46.         }  
  47.   
  48.         public void setFallRate(Int32 rate)  
  49.         {  
  50.             if (rate < 0)  
  51.             {  
  52.                 this.m_fallRate = 1;  
  53.                 return;  
  54.             }  
  55.             this.m_fallRate = rate;  
  56.         }  
  57.   
  58.         public void addFallNum(Int32 num)  
  59.         {  
  60.             if (num <= 0)  
  61.             {  
  62.                 return;  
  63.             }  
  64.             this.m_fallNum += num;  
  65.             for (Int32 i = 0; i < num; i++)  
  66.             {  
  67.                 Letter letter = new Letter();  
  68.                 letter.newLetter();  
  69.                 this.m_letters.Add(letter);  
  70.             }  
  71.         }  
  72.   
  73.         public void draw(CDraw draw)  
  74.         {  
  75.             for (Int32 i = 0; i < m_letters.Count; i++)  
  76.             {  
  77.                 m_letters[i].draw(draw);  
  78.             }  
  79.         }  
  80.     }  
  81. }  
   最后为游戏类,实现游戏的逻辑,游戏类实现为: ///TypingGame类实现 [csharp]  view plain copy print ?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using CEngine;  
  4. using CGraphics;  
  5.   
  6. namespace Typing  
  7. {  
  8.     public sealed class TypingGame : CGame  
  9.     {  
  10.         private LetterManager m_letBuilder;  
  11.         /// <summary>  
  12.         /// 打字正确个数  
  13.         /// </summary>  
  14.         private float m_rightCount;  
  15.         /// <summary>  
  16.         /// 打字个数  
  17.         /// </summary>  
  18.         private float m_typingCount;  
  19.         private CDraw m_draw;  
  20.         /// <summary>  
  21.         /// 倒计时  
  22.         /// </summary>  
  23.         private Int32 m_countDown;  
  24.         private Int32 m_lastTime;  
  25.         /// <summary>  
  26.         /// 下落速度控制  
  27.         /// </summary>  
  28.         private Int32 m_fallTime;  
  29.         /// <summary>  
  30.         /// 是否在打字  
  31.         /// </summary>  
  32.         private Boolean m_isType;  
  33.         private Random m_random;  
  34.   
  35.         protected override void gameInit()  
  36.         {  
  37.             setTitle("控制台游戏之——键盘熟练工v1.0");  
  38.             m_letBuilder = new LetterManager(20, 2);  
  39.             setCursorVisible(false);  
  40.             setUpdateRate(40);  
  41.   
  42.             m_rightCount = 0f;  
  43.             m_typingCount = 0f;  
  44.             m_countDown = 200;  
  45.             m_fallTime = 10;  
  46.             m_isType = false;  
  47.   
  48.             m_random = new Random();  
  49.   
  50.             m_draw = base.getDraw();  
  51.   
  52.             m_draw.setDrawSymbol(CSymbol.RECT_SOLID);  
  53.             m_draw.drawRect(0, 0, 31, 25, ConsoleColor.White);  
  54.             m_draw.setDrawSymbol(CSymbol.DEFAULT);  
  55.             m_draw.fillRect(1, 0, 29, 1, ConsoleColor.Black);  
  56.             m_draw.setDrawSymbol(CSymbol.RHOMB_SOLID);  
  57.             m_draw.drawRect(31, 0, 9, 13, ConsoleColor.DarkYellow);  
  58.             m_draw.setDrawSymbol(CSymbol.RHOMB_SOLID);  
  59.             m_draw.drawRect(31, 12, 9, 13, ConsoleColor.DarkYellow);  
  60.   
  61.             m_draw.drawText("倒计时:", 65, 2, ConsoleColor.Red);  
  62.             m_draw.drawText("正确率:", 65, 4, ConsoleColor.Green);  
  63.             m_draw.drawText("击键数:", 65, 6, ConsoleColor.Green);  
  64.             m_draw.drawText("FPS:", 65, 8, ConsoleColor.Green);  
  65.             m_draw.drawText("Rate:", 65, 10, ConsoleColor.Green);  
  66.   
  67.             m_draw.drawText("操作:键盘A--Z键,根据下落的字母打击键盘对应的键位,正确率体现你的键盘操作熟悉程度。", new CRect(33, 14, 5, 10), ConsoleColor.DarkGreen);  
  68.         }  
  69.   
  70.         protected override void gameDraw(CGraphics.CDraw draw)  
  71.         {  
  72.             m_letBuilder.draw(draw);  
  73.             draw.setBackcolor(ConsoleColor.Black);  
  74.             draw.drawText(m_countDown.ToString("000"), 73, 2, ConsoleColor.Red);  
  75.             if (m_typingCount != 0)  
  76.             {  
  77.                 draw.drawText(((m_rightCount / m_typingCount) * 100).ToString("000") + "%", 73, 4, ConsoleColor.Red);  
  78.             }  
  79.             else  
  80.             {  
  81.                 draw.drawText("0%", 73, 4, ConsoleColor.Red);  
  82.             }  
  83.   
  84.             draw.drawText(m_typingCount.ToString("000"), 73, 6, ConsoleColor.Red);  
  85.             draw.drawText(getFPS().ToString(), 73, 8, ConsoleColor.Blue);  
  86.             draw.drawText(getUpdateRate().ToString(), 73, 10, ConsoleColor.Blue);  
  87.         }  
  88.   
  89.         protected override void gameLoop()  
  90.         {  
  91.             if (m_fallTime == 0)  
  92.             {  
  93.                 List<Letter> letters = m_letBuilder.getLetters();  
  94.   
  95.                 for (Int32 i = 0; i < letters.Count; i++)  
  96.                 {  
  97.                     //每个字母按几率下落  
  98.                     if (m_random.Next(0, m_letBuilder.getFallNum() * m_letBuilder.getFallRate()) == i)  
  99.                     {  
  100.                         letters[i].setAlive(true);  
  101.                     }  
  102.                     //字母下落  
  103.                     if (letters[i].getAlive())  
  104.                     {  
  105.                         letters[i].setPosition(letters[i].getPosition().getX(), letters[i].getPosition().getY() + 1);  
  106.                     }  
  107.                     //字母下落到地面  
  108.                     if (letters[i].getPosition().getY() >= getClientRect().getHeight()-1)  
  109.                     {  
  110.                         letters[i].newLetter();  
  111.                         m_typingCount++;  
  112.                     }  
  113.                 }  
  114.   
  115.                 m_fallTime = 5;  
  116.             }  
  117.   
  118.             m_fallTime--;  
  119.   
  120.             if (Environment.TickCount - m_lastTime > 1000)  
  121.             {  
  122.                 m_lastTime = Environment.TickCount;  
  123.                 m_countDown--;  
  124.   
  125.                 if (m_countDown == 0)  
  126.                 {  
  127.                     setGameOver(true);  
  128.                 }  
  129.             }  
  130.         }  
  131.   
  132.         protected override void gameExit()  
  133.         {  
  134.             m_letBuilder = null;  
  135.   
  136.             base.getDraw().clear(ConsoleColor.Black);  
  137.             base.getDraw().drawText("Game Over!", 25, 10, ConsoleColor.Red);  
  138.             base.getDraw().drawText("Copyright (c) D-Zone Studio", 40, 10, ConsoleColor.White);  
  139.             Console.ReadLine();  
  140.         }  
  141.   
  142.         protected override void gameKeyDown(CKeyboardEventArgs e)  
  143.         {  
  144.             if (!m_isType)  
  145.             {  
  146.                 List<Letter> letters = m_letBuilder.getLetters();  
  147.                 for (Int32 i = 0; i < letters.Count; i++)  
  148.                 {  
  149.                     if (letters[i].getAlive())  
  150.                     {  
  151.                         //是否打对字母  
  152.                         if (letters[i].getChar().ToString() == e.getKey().ToString())  
  153.                         {  
  154.                             base.update(new CRect(letters[i].getPosition().getX(),letters[i].getPosition().getY(),1,1));  
  155.                             letters[i].newLetter();  
  156.                             m_rightCount++;  
  157.                             break;  
  158.                         }  
  159.                     }  
  160.                 }  
  161.   
  162.                 m_typingCount++;  
  163.   
  164.                 if (e.getKey() == CKeys.Escape)  
  165.                 {  
  166.                     setGameOver(true);  
  167.                 }  
  168.                 m_isType = true;  
  169.             }  
  170.         }  
  171.   
  172.         protected override void gameKeyUp(CKeyboardEventArgs e)  
  173.         {  
  174.             m_isType = false;  
  175.         }  
  176.     }  
  177. }  
   从前面和现在这个游戏可以看到,不同游戏除了逻辑上设计上的不同之外,其他很多部分都是通用的,我们没必要每次写个小游戏都要做重复的编码工作,这也是写这个小游戏框架的原因及必要性。
   最后我们来欣赏下我们的劳动成果:
试玩链接:

四、结语

   实话说,这个游戏DEMO实现得比较仓促,代码写得比较凌乱,但都是些简单的逻辑,相信大家都能看得明白。

本文标签: C游戏编程《控制台小游戏系列》之《七键盘熟练工实例》