本文所有源代碼和VisualStudio2010(.NET Fx 2.0)工程打包在本文最後下載。(別找我要源碼,一概不理會)
《倒水解密》是一款很不錯的手機益智類遊戲,遊戲界面如下:
規則是這樣的:
有N個容量不同的瓶子,指定「將a升水倒入容量爲b的瓶子」。遊戲要求通過裝水、倒水,達成給定的目標。
該遊戲雖然簡單,但可玩性高,也可有不同的難度變化,並且能鍛鍊玩家的心算能力。《倒水解密》不失爲一個很好的繁忙工作之餘的休閒方式。
說到這裏,大家應該能猜到我想幹什麼了。沒錯,山寨之。
下面是山寨遊戲的屏幕錄像:
雖然界面有點簡陋,但至少實現了。(屏幕錄像軟件不能錄入鼠標光標,實際上鼠標光標是動態的)。
遊戲操作方式如下:
- 在瓶子上雙擊右鍵可以把瓶子灌滿水
- 雙擊左鍵可以把瓶子裏的水倒掉
- 將一個瓶子拖動到另一個瓶子上可以把水倒過去
下面是遊戲的模型結構。
Bottle類,表示瓶子。保存了瓶子的關鍵屬性如容量、儲量,以及基本方法如倒入、倒出。該類實現了IVisible可視化接口。這個接口很簡單,只是提供了一個Draw()方法,用於重繪瓶子自身並返回圖像。使用這種方式,可以方便的修改瓶子的外觀樣式而不用修改其他部分代碼。例如可以簡單的用矩形畫瓶子,也可以像上面的手機遊戲截圖一樣用非常的漂亮的貼圖來做。
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類,表示瓶子所在世界。存儲了所有的瓶子,用於和遊戲交互。
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類,遊戲類。保存遊戲世界,負責自動生成遊戲和判定遊戲勝利。
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)打包 :點擊下載