Conmajia 2012
All source code and Visual Studio 2.0 (. net fx) projects in this article are packaged at the end of this article. (Don't ask me for the source code. I will ignore it)
Reverse decryption is a very good mobile smart game with the following interface:
The rule is as follows:
There are n bottles with different capacities. Specify "Import A to a bottle with a capacity of B 」. The game requires water and pouring water to achieve a given goal.
Although the game is simple, it has high playability and different difficulty changes, and can exercise Players' Mental computing capabilities. "Reverse decryption" is a good way to relax while busy working.
Here, we should be able to guess what I want to do. That's right.
The screen recordings of the shanzhai game are as follows:
Although the interface is a bit simple, it is at least implemented. (The screen recording software cannot input the mouse cursor. In fact, the mouse cursor is dynamic ).
The game operation method is as follows:
- Right-click the bottle to fill the bottle with water
- Double-click the left button to drop the water from the bottle.
- Drag a bottle to another bottle to pour the water over.
The following is the model structure of the game.
The bottle class, indicating the bottle. Stores key attributes of a bottle, such as capacity and reserves, and basic methods such as import and export. This class implements the ivisible visual interface. This interface is simple, but provides a draw () method for re-painting the bottle itself and returning the image. In this way, you can easily modify the appearance style of the bottle without modifying other code. For example, you can simply use a rectangle to draw a bottle, or use a very beautiful texture like the phone game above.
Here I use a mouse to draw an ugly bottle as an example.
public class Bottle : Element, IVisible{ const int SIZE_FACTOR = 10; #region Variables int content = 0; int capacity = 0; Size size = Size.Empty; Rectangle bounds = Rectangle.Empty; Bitmap canvas; Graphics painter; bool dirty = true; #endregion #region Properties public int FreeSpace { get { return capacity - content; } } public int Content { get { return content; } } public int Capacity { get { return capacity; } } public bool IsEmpty { get { return content == 0; } } public bool IsFull { get { return content == capacity; } } #endregion #region Initiators public Bottle(int capacity) : this(capacity, 0) { } public Bottle(int capacity, int content) { if (capacity > 0) { this.capacity = capacity; if (content > -1 && content <= capacity) this.content = content; size.Width = 30; size.Height = SIZE_FACTOR * capacity; bounds.Size = size; canvas = new Bitmap(size.Width, size.Height); painter = Graphics.FromImage(canvas); } } #endregion #region Methods public void DropIn() { DropIn(capacity); } public void DropIn(int amount) { if (amount > 0) { content += amount; if (content > capacity) content = capacity; dirty = true; } } public void DropOut() { DropOut(capacity); } public void DropOut(int amount) { if (amount > 0 && amount < content) { content -= amount; } else { content = 0; } dirty = true; } #endregion #region IVisible public Rectangle Bounds { get { return bounds; } } public int X { get { return bounds.X; } set { bounds.X = value; } } public int Y { get { return bounds.Y; } set { bounds.Y = value; } } public Bitmap Draw() { if (dirty) { painter.Clear(Color.Transparent); // simple look bottle int contentHeight = (int)((float)bounds.Height * ((float)content / (float)capacity)); if (contentHeight > 0) { using (Brush b = new LinearGradientBrush( new Rectangle( 0, bounds.Height - contentHeight - 1, bounds.Width, contentHeight ), Color.LightBlue, Color.DarkBlue, 90)) { Rectangle contentRect = new Rectangle( 0, bounds.Height - contentHeight, bounds.Width, contentHeight ); painter.FillRectangle(b, contentRect); } } painter.DrawRectangle( Pens.Silver, 0, 0, bounds.Width - 1, bounds.Height - 1 ); string s = string.Format("{0}/{1}", content, capacity); painter.DrawString( s, SystemFonts.DefaultFont, Brushes.Black, 2, 1 ); painter.DrawString( s, SystemFonts.DefaultFont, Brushes.Black, 1, 2 ); painter.DrawString( s, SystemFonts.DefaultFont, Brushes.Black, 2, 3 ); painter.DrawString( s, SystemFonts.DefaultFont, Brushes.Black, 3, 2 ); painter.DrawString( s, SystemFonts.DefaultFont, Brushes.White, 2, 2 ); dirty = false; } return canvas; } #endregion #region Elemenet public override Type Type { get { return typeof(Bottle); } } #endregion}
World class, indicating the world where the bottle is located. Stores all the bottles for interacting with the game.
public class World{ const int PADDING = 20; #region Variables List<Bottle> bottles = new List<Bottle>(); Rectangle bounds = Rectangle.Empty; #endregion #region Properties public List<Bottle> Bottles { get { return bottles; } } public Rectangle Bounds { get { return bounds; } set { bounds = value; arrangeBottles(); } } #endregion #region Initiators public World() { } public World(Rectangle bounds) { this.bounds = bounds; } #endregion #region world methods public Bottle CreateBottle(int capacity) { return CreateBottle(capacity, 0); } public Bottle CreateBottle(int capacity, int content) { Bottle b = new Bottle(capacity, content); bottles.Add(b); arrangeBottles(); return b; } public void DestroyBottle() { bottles.Clear(); } public void DestroyBottle(Bottle b) { bottles.Remove(b); } public void DestroyBottle(int capacity) { List<Bottle> tmp = new List<Bottle>(); foreach (Bottle b in bottles) { if (b.Capacity != capacity) tmp.Add(b); } bottles.Clear(); bottles.AddRange(tmp); } #endregion #region paint helpers Size getTotalSize() { Size sz = Size.Empty; foreach (Bottle b in bottles) { sz.Width += PADDING + b.Bounds.Width; if (sz.Height < b.Bounds.Height) sz.Height = b.Bounds.Height; } return sz; } void arrangeBottles() { Size sz = getTotalSize(); Point offset = new Point( (bounds.Width - sz.Width) / 2, (bounds.Height - sz.Height) / 2 + sz.Height ); foreach (Bottle b in bottles) { b.X = offset.X; b.Y = offset.Y - b.Bounds.Height; offset.X += PADDING + b.Bounds.Width; } } #endregion}
Game, game. Stores the game world and is responsible for Automatically Generating and determining the game's victory.
public class Game{ string name = string.Empty; int bottleCount = 0; bool initiated = false; int targetBottle = -1; int targetAmount = -1; World world; Random rand = new Random(); public string Name { get { return name; } } public int Target { get { return targetBottle; } } public int Amount { get { return targetAmount; } } public World World { get { return world; } } public Game() { world = new World(); } /// <summary> /// Difficaulty of game. /// </summary> /// <param name="difficaulty">Difficaulty from 1 to 3.</param> public void AutoGenerate(int difficaulty) { if (difficaulty < 1) return; world.DestroyBottle(); int bottleCount = rand.Next(3, 5); //3 + difficaulty); targetBottle = rand.Next(0, bottleCount - 1); int maxAmount = 10; for (int i = 0; i < bottleCount; i++) { int cap = 0; do { cap = rand.Next(3, maxAmount + difficaulty); } while (capacityInside(cap)); world.CreateBottle(cap); } targetAmount = rand.Next(1, world.Bottles[targetBottle].Capacity); initiated = true; } bool capacityInside(int cap) { foreach (Bottle b in world.Bottles) { if (b.Capacity == cap) return true; } return false; } public bool CheckSuccess() { if (targetBottle > -1) { if (initiated && world.Bottles.Count > targetBottle) { return world.Bottles[targetBottle].Content == targetAmount; } } return false; }}
The timing, step counting, and interface operations of the game are all placed on the main form, which is not described here.
Simple implementation, as well as Countdown, high score rankings and other functions can be expanded, the interface can also be more beautiful, you are welcome to expand.
Package source code and engineering files (vs2010): Click to download
Conmajia 2012