您的位置:首页 > 其它

自己动手实现一个《倒水解密》游戏

2012-10-08 09:36 656 查看
本文所有源代码和VisualStudio2010(.NET Fx 2.0)工程打包在本文最后下载。(别找我要源码,一概不理会)

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



规则是这样的:

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

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

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

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



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

游戏操作方式如下:

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

下面是游戏的模型结构。

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

1、这里我用画图鼠绘了一个丑陋的瓶子作为例子。



// By Conmajia<conmajia@gmail.com> on September 6th., 2012
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace DropWaterGame
{
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

}
}

(1)我们可以看到Bottle类的构造函数是Bottle(int capacity)又调用了另一构造函数Bottle(int capacity, int content).

这样的表达方式要是在C++中会编译报错的:

1>c:\users\chenyj\desktop\temp\temp\tempdlg.cpp(14) : error C2059: 语法错误 : “this”




(2)比较复杂的是Draw方法。他做的事情有:

(2、1)画出水里占有的方形图。

画的方法是通过using里创建的线性画刷。

(2、2)分别写出文字。文字是分为5个点来写的(为什么要这样呢?至今未知)。

2、

World类,表示瓶子所在世界。存储了所有的瓶子,用于和游戏交互。

// By Conmajia<conmajia@gmail.com> on September 6th., 2012
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

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

// By Conmajia<conmajia@gmail.com> on September 6th., 2012
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace DropWaterGame
{
    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;
        }
    }
}




游戏的计时、计步、界面操作等统统放在主窗体,这里不做赘述。

简单的实现,还有倒计时、高分榜等功能可以扩展,界面也可以做的更漂亮些,欢迎大家扩展。



源码及工程文件(VS2010)打包 :点击下载



© Conmajia 2012
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: