自己動手實現一個《倒水解密》遊戲

本文所有源代碼和VisualStudio2010(.NET Fx 2.0)工程打包在本文最後下載。(別找我要源碼,一概不理會)

《倒水解密》是一款很不錯的手機益智類遊戲,遊戲界面如下:

規則是這樣的:

有N個容量不同的瓶子,指定「將a升水倒入容量爲b的瓶子」。遊戲要求通過裝水、倒水,達成給定的目標。

該遊戲雖然簡單,但可玩性高,也可有不同的難度變化,並且能鍛鍊玩家的心算能力。《倒水解密》不失爲一個很好的繁忙工作之餘的休閒方式。

說到這裏,大家應該能猜到我想幹什麼了。沒錯,山寨之。

下面是山寨遊戲的屏幕錄像:

雖然界面有點簡陋,但至少實現了。(屏幕錄像軟件不能錄入鼠標光標,實際上鼠標光標是動態的)。

遊戲操作方式如下:

  • 在瓶子上雙擊右鍵可以把瓶子灌滿水
  • 雙擊左鍵可以把瓶子裏的水倒掉
  • 將一個瓶子拖動到另一個瓶子上可以把水倒過去

下面是遊戲的模型結構。

Bottle類,表示瓶子。保存了瓶子的關鍵屬性如容量、儲量,以及基本方法如倒入、倒出。該類實現了IVisible可視化接口。這個接口很簡單,只是提供了一個Draw()方法,用於重繪瓶子自身並返回圖像。使用這種方式,可以方便的修改瓶子的外觀樣式而不用修改其他部分代碼。例如可以簡單的用矩形畫瓶子,也可以像上面的手機遊戲截圖一樣用非常的漂亮的貼圖來做。

Bottle類源代碼
  1 public class Bottle : Element, IVisible
  2 {
  3     const int SIZE_FACTOR = 10;
  4 
  5     #region Variables
  6     int content = 0;
  7     int capacity = 0;
  8     Size size = Size.Empty;
  9     Rectangle bounds = Rectangle.Empty;
 10 
 11     Bitmap canvas;
 12     Graphics painter;
 13     bool dirty = true;
 14 
 15     #endregion
 16 
 17     #region Properties
 18     public int FreeSpace
 19     {
 20         get { return capacity - content; }
 21     }
 22 
 23     public int Content
 24     {
 25         get { return content; }
 26     }
 27 
 28     public int Capacity
 29     {
 30         get { return capacity; }
 31     }
 32 
 33     public bool IsEmpty
 34     {
 35         get { return content == 0; }
 36     }
 37 
 38     public bool IsFull
 39     {
 40         get { return content == capacity; }
 41     }
 42     #endregion
 43 
 44     #region Initiators
 45     public Bottle(int capacity)
 46         : this(capacity, 0)
 47     {
 48 
 49     }
 50 
 51     public Bottle(int capacity, int content)
 52     {
 53         if (capacity > 0)
 54         {
 55             this.capacity = capacity;
 56             if (content > -1 && content <= capacity)
 57                 this.content = content;
 58 
 59             size.Width = 30;
 60             size.Height = SIZE_FACTOR * capacity;
 61             bounds.Size = size;
 62             canvas = new Bitmap(size.Width, size.Height);
 63             painter = Graphics.FromImage(canvas);
 64         }
 65     }
 66     #endregion
 67 
 68     #region Methods
 69     public void DropIn()
 70     {
 71         DropIn(capacity);
 72     }
 73     public void DropIn(int amount)
 74     {
 75         if (amount > 0)
 76         {
 77             content += amount;

 78             if (content > capacity)
 79                 content = capacity;

 80 
 81             dirty = true;
 82         }
 83     }
 84 
 85     public void DropOut()
 86     {
 87         DropOut(capacity);
 88     }
 89     public void DropOut(int amount)
 90     {
 91         if (amount > 0 && amount < content)
 92         {
 93             content -= amount;
 94         }
 95         else
 96         {
 97             content = 0;
 98         }
 99 
100         dirty = true;
101     }
102     #endregion
103 
104     #region IVisible
105     public Rectangle Bounds
106     {
107         get { return bounds; }
108     }
109 
110     public int X
111     {
112         get { return bounds.X; }
113         set { bounds.X = value; }
114     }
115 
116     public int Y
117     {

118         get { return bounds.Y; }
119         set { bounds.Y = value; }
120     }
121 
122     public Bitmap Draw()
123     {
124         if (dirty)
125         {
126             painter.Clear(Color.Transparent);
127 
128             // simple look bottle
129             int contentHeight = (int)((float)bounds.Height * ((float)content / (float)capacity));
130 
131             if (contentHeight > 0)
132             {
133                 using (Brush b = new LinearGradientBrush(
134                     new Rectangle(
135                         0,
136                         bounds.Height - contentHeight - 1,
137                         bounds.Width,
138                         contentHeight
139                         ),
140                     Color.LightBlue,
141                     Color.DarkBlue,
142                     90))
143                 {
144                     Rectangle contentRect = new Rectangle(
145                         0,
146                         bounds.Height - contentHeight,
147                         bounds.Width,
148                         contentHeight

149                         );
150                     painter.FillRectangle(b, contentRect);
151                 }
152             }
153 
154             painter.DrawRectangle(
155                 Pens.Silver,
156                 0,
157                 0,
158                 bounds.Width - 1,
159                 bounds.Height - 1
160                 );
161 
162             string s = string.Format("{0}/{1}", content, capacity);
163             painter.DrawString(
164                 s,
165                 SystemFonts.DefaultFont,
166                 Brushes.Black,
167                 2,
168                 1
169                 );
170             painter.DrawString(
171                 s,
172                 SystemFonts.DefaultFont,
173                 Brushes.Black,
174                 1,
175                 2
176                 );
177             painter.DrawString(
178                 s,
179                 SystemFonts.DefaultFont,
180                 Brushes.Black,
181                 2,
182                 3
183                 );
184             painter.DrawString(
185                 s,
186                 SystemFonts.DefaultFont,
187                 Brushes.Black,
188                 3,
189                 2
190                 );
191             painter.DrawString(
192                 s,
193                 SystemFonts.DefaultFont,
194                 Brushes.White,
195                 2,
196                 2
197                 );
198 
199             dirty = false;
200         }
201 
202         return canvas;
203     }
204     #endregion
205 
206     #region Elemenet
207     public override Type Type
208     {
209         get { return typeof(Bottle); }
210     }
211     #endregion
212 
213 }

World類,表示瓶子所在世界。存儲了所有的瓶子,用於和遊戲交互。

World類源代碼
  1 public class World
  2 {
  3     const int PADDING = 20;
  4 
  5     #region Variables
  6     List<Bottle> bottles = new List<Bottle>();
  7 
  8     Rectangle bounds = Rectangle.Empty;
  9     #endregion
 10 
 11     #region Properties
 12     public List<Bottle> Bottles
 13     {
 14         get { return bottles; }
 15     }
 16 
 17     public Rectangle Bounds
 18     {
 19         get { return bounds; }
 20         set
 21         {
 22             bounds = value;
 23             arrangeBottles();
 24         }
 25     }
 26     #endregion
 27 
 28     #region Initiators
 29     public World()
 30     {
 31 
 32     }
 33     public World(Rectangle bounds)
 34     {
 35         this.bounds = bounds;
 36     }
 37     #endregion
 38 
 39     #region world methods
 40     public Bottle CreateBottle(int capacity)
 41     {
 42         return CreateBottle(capacity, 0);
 43     }
 44     public Bottle CreateBottle(int capacity, int content)
 45     {
 46         Bottle b = new Bottle(capacity, content);
 47         bottles.Add(b);
 48         arrangeBottles();
 49         return b;
 50     }
 51 
 52     public void DestroyBottle()
 53     {
 54         bottles.Clear();
 55     }
 56     public void DestroyBottle(Bottle b)
 57     {
 58         bottles.Remove(b);
 59     }
 60     public void DestroyBottle(int capacity)
 61     {
 62         List<Bottle> tmp = new List<Bottle>();
 63         foreach (Bottle b in bottles)
 64         {
 65             if (b.Capacity != capacity)
 66                 tmp.Add(b);
 67         }
 68 
 69         bottles.Clear();
 70         bottles.AddRange(tmp);
 71     }
 72 
 73     #endregion
 74 
 75     #region paint helpers
 76     Size getTotalSize()
 77     {
 78         Size sz = Size.Empty;
 79         foreach (Bottle b in bottles)
 80         {
 81             sz.Width += PADDING + b.Bounds.Width;
 82             if (sz.Height < b.Bounds.Height)
 83                 sz.Height = b.Bounds.Height;
 84         }
 85 
 86         return sz;
 87     }
 88 
 89     void arrangeBottles()
 90     {
 91         Size sz = getTotalSize();
 92         Point offset = new Point(
 93             (bounds.Width - sz.Width) / 2,
 94             (bounds.Height - sz.Height) / 2 + sz.Height
 95             );
 96 
 97         foreach (Bottle b in bottles)
 98         {
 99             b.X = offset.X;
100             b.Y = offset.Y - b.Bounds.Height;
101 
102             offset.X += PADDING + b.Bounds.Width;
103         }
104     }
105     #endregion
106 }

Game類,遊戲類。保存遊戲世界,負責自動生成遊戲和判定遊戲勝利。

Game類源代碼
 1 public class Game
 2 {
 3     string name = string.Empty;
 4     int bottleCount = 0;
 5     bool initiated = false;
 6     int targetBottle = -1;
 7     int targetAmount = -1;
 8 
 9     World world;
10 
11     Random rand = new Random();
12 
13     public string Name
14     {
15         get { return name; }
16     }
17 
18     public int Target
19     {
20         get { return targetBottle; }
21     }
22 
23     public int Amount
24     {
25         get { return targetAmount; }
26     }
27 
28     public World World
29     {
30         get { return world; }
31     }
32 
33     public Game()
34     {
35         world = new World();
36     }
37 
38     /// <summary>
39     /// Difficaulty of game.
40     /// </summary>
41     /// <param name="difficaulty">Difficaulty from 1 to 3.</param>
42     public void AutoGenerate(int difficaulty)
43     {
44         if (difficaulty < 1)
45             return;
46 
47         world.DestroyBottle();
48 
49         int bottleCount = rand.Next(3, 5); //3 + difficaulty);
50         targetBottle = rand.Next(0, bottleCount - 1);
51 
52         int maxAmount = 10;
53         for (int i = 0; i < bottleCount; i++)
54         {
55             int cap = 0;
56             do
57                 cap = rand.Next(3, maxAmount + difficaulty);
58             while (capacityInside(cap));
59             world.CreateBottle(cap);
60         }
61 
62         targetAmount = rand.Next(1, world.Bottles[targetBottle].Capacity);
63 
64         initiated = true;
65     }
66 
67     bool capacityInside(int cap)
68     {
69         foreach (Bottle b in world.Bottles)
70             if (b.Capacity == cap)
71                 return true;
72         return false;
73     }
74 
75     public bool CheckSuccess()
76     {
77         if (targetBottle > -1)
78             if (initiated && world.Bottles.Count > targetBottle)
79                 return world.Bottles[targetBottle].Content == targetAmount;
80         return false;
81     }
82 }

遊戲的計時、計步、界面操作等統統放在主窗體,這裏不做贅述。
簡單的實現,還有倒計時、高分榜等功能可以擴展,界面也可以做的更漂亮些,歡迎大家擴展。
源碼及工程文件(VS2010)打包 :點擊下載

發佈了10 篇原創文章 · 獲贊 7 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章