© Conmajia 2012

《倒水解密》是一款很不错的手机益智类游戏,游戏界面如下:

python注水游戏f注小 注水的游戏_python注水游戏f注小

规则是这样的:

有 N 个容量不同的瓶子,指定「将 a 升水倒入容量为 b 的瓶子」。游戏要求通过装水、倒水,达成给定的目标。

该游戏虽然简单,但可玩性高,也可有不同的难度变化,并且能锻炼玩家的心算能力。《倒水解密》不失为一个很好的繁忙工作之余的休闲方式。

说到这里,大家应该能猜到我想干什么了。没错,山寨之。

下面是山寨游戏的屏幕录像:

python注水游戏f注小 注水的游戏_游戏开发_02

虽然界面有点简陋,但至少实现了。(屏幕录像软件不能录入鼠标光标,实际上鼠标光标是动态的)。

游戏操作方式如下:

  • 在瓶子上双击右键可以把瓶子灌满水
  • 双击左键可以把瓶子里的水倒掉
  • 将一个瓶子拖动到另一个瓶子上可以把水倒过去

下面是游戏的模型结构。

Bottle 类,表示瓶子。保存了瓶子的关键属性如容量、储量,以及基本方法如倒入、倒出。该类实现了 IVisible 可视化接口。这个接口很简单,只是提供了一个 Draw() 方法,用于重绘瓶子自身并返回图像。使用这种方式,可以方便的修改瓶子的外观样式而不用修改其他部分代码。例如可以简单的用矩形画瓶子,也可以像上面的手机游戏截图一样用非常的漂亮的贴图来做。

这里我用画图鼠绘了一个丑陋的瓶子。

python注水游戏f注小 注水的游戏_业余玩家_03

python注水游戏f注小 注水的游戏_游戏开发_04

python注水游戏f注小 注水的游戏_ci_05

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 类,表示瓶子所在世界。存储了所有的瓶子,用于和游戏交互。

python注水游戏f注小 注水的游戏_游戏开发_04

python注水游戏f注小 注水的游戏_ci_05

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 类,游戏类。保存游戏世界,负责自动生成游戏和判定游戏胜利。

python注水游戏f注小 注水的游戏_游戏开发_04

python注水游戏f注小 注水的游戏_ci_05

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 }

游戏的计时、计步、界面操作等统统放在主窗体,这里不做赘述。
简单的实现,还有倒计时、高分榜等功能可以扩展,界面也可以做的更漂亮些,欢迎大家扩展。