C#遊戲編程:《控制檯小遊戲系列》之《七、鍵盤熟練工實例》

一、遊戲分析

  相信大家對金山打字通、打字精靈等打字軟件非常熟悉,想當年剛接觸計算機時還靠它們的打字遊戲練習打字基本功。這章的遊戲DEMO就是一個打字小遊戲,當然,它很簡單,只是A~Z字母的打字練習小遊戲。遊戲的設計也不復雜,遊戲開始時先初始化下落字母的個數和每次下落字母的概率,隨着時間的進行,字母會從界面上方逐漸下落到界面底下,在這過程中你需要敲擊鍵盤上與界面下落的字母相對應的鍵位,如果鍵位的字母與下落的字母一致,則記錄打字正確數量,否則不記錄,無論如何,敲擊一次鍵盤記錄一下敲擊鍵盤數,當字母下落到界面底下時,字母停止下落,該字母被視爲無效,相應地,也記錄一次敲擊數。正確率、敲擊數等信息會在界面上顯示,遊戲還有倒計時,時間一到則被視爲遊戲結束。

二、遊戲實現

  先定義一個字母表,字母表的實現爲:
  ///LetterTable類實現
  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類實現
  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類實現
  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類實現
  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實現得比較倉促,代碼寫得比較凌亂,但都是些簡單的邏輯,相信大家都能看得明白
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章