当前位置:编程学习 > C#/ASP.NET >>

「玩一玩」自制一个《倒水解密》游戏

© Conmajia 2012
 
本文所有源代码和VisualStudio2010(.NET Fx 2.0)工程打包在本文最后下载。(别找我要源码,一概不理会)
 
《倒水解密》是一款很不错的手机益智类游戏,游戏界面如下:

规则是这样的:
 
有N个容量不同的瓶子,指定「将a升水倒入容量为b的瓶子」。游戏要求通过装水、倒水,达成给定的目标。
 
该游戏虽然简单,但可玩性高,也可有不同的难度变化,并且能锻炼玩家的心算能力。《倒水解密》不失为一个很好的繁忙工作之余的休闲方式。
 
说到这里,大家应该能猜到我想干什么了。没错,山寨之。
 
下面是山寨游戏的屏幕录像:

虽然界面有点简陋,但至少实现了。(屏幕录像软件不能录入鼠标光标,实际上鼠标光标是动态的)。
 
游戏操作方式如下:
 •在瓶子上双击右键可以把瓶子灌满水
 •双击左键可以把瓶子里的水倒掉
 •将一个瓶子拖动到另一个瓶子上可以把水倒过去
 
下面是游戏的模型结构。
 
Bottle类,表示瓶子。保存了瓶子的关键属性如容量、储量,以及基本方法如倒入、倒出。该类实现了IVisible可视化接口。这个接口很简单,只是提供了一个Draw()方法,用于重绘瓶子自身并返回图像。使用这种方式,可以方便的修改瓶子的外观样式而不用修改其他部分代码。例如可以简单的用矩形画瓶子,也可以像上面的手机游戏截图一样用非常的漂亮的贴图来做。
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类,表示瓶子所在世界。存储了所有的瓶子,用于和游戏交互。

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


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
--------------------编程问答-------------------- --------------------编程问答-------------------- 睡觉


--------------------编程问答-------------------- 年纪轻轻,睡啥觉啊。。。 --------------------编程问答-------------------- 没有写啥注释,我相信只要能看懂英文就能看懂源码。。 --------------------编程问答-------------------- 顶!!!必须支持一下!!! --------------------编程问答-------------------- --------------------编程问答-------------------- LZ终于出作品了,还以为你收山了呢
--------------------编程问答--------------------

好东西 --------------------编程问答--------------------
引用 8 楼  的回复:
好东西

+1
--------------------编程问答-------------------- 野比牛人 啊 --------------------编程问答-------------------- 顶一个 --------------------编程问答-------------------- 看不懂英文的肿么半!!! --------------------编程问答-------------------- --------------------编程问答-------------------- 支持啊,野比哥总能吸引眼球 --------------------编程问答-------------------- 期待下一个分享帖 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 这个的确有意思,这个要支持的! --------------------编程问答-------------------- 看起来很是吃力 呀 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 嗯,挺有意思的! --------------------编程问答-------------------- --------------------编程问答-------------------- 低级.. --------------------编程问答-------------------- 仰视。 --------------------编程问答-------------------- 嗯,不错不错 --------------------编程问答-------------------- 我什么时间能达到的高度 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 野比小王子一出手必是精品 --------------------编程问答-------------------- 进来求所有关卡数据和解决方法 我也想山寨一个(⊙o⊙) --------------------编程问答-------------------- 野比小王子的作品,必须支持! --------------------编程问答-------------------- 好东西!!! --------------------编程问答-------------------- 很好很强大,支持一下.不知道是不是可以直接在wp8上面玩. --------------------编程问答-------------------- 野比现在又来搞游戏了 --------------------编程问答-------------------- --------------------编程问答-------------------- 太阁立志 里面的 配药游戏? --------------------编程问答--------------------
引用 14 楼  的回复:
支持啊,野比哥总能吸引眼球
支持了。。 --------------------编程问答-------------------- 哇咔咔搞定。。 谢谢分享。。 哇咔咔!!! --------------------编程问答-------------------- 就是代码还没看懂。。哎。。  --------------------编程问答-------------------- 回复能拿分吗? --------------------编程问答-------------------- 赞一个~~~~~~~~~~。收藏,用到时慢慢研究 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 感谢楼主分享! --------------------编程问答-------------------- 前面说过,只需要修改Bottle.Draw()方法就可以改变瓶子的显示外观。于是我用画图鼠绘了一个丑陋的瓶子,就像这样:
--------------------编程问答-------------------- 不错,googd --------------------编程问答-------------------- --------------------编程问答-------------------- 用 js 在浏览器里实现可能更有意思。
瓶子可以用图片 --------------------编程问答-------------------- 这种情况的发生是因为算法问题么~!!!! --------------------编程问答-------------------- 发现好多基础都忘了。。 趁机学习学习。。哇咔咔!!! --------------------编程问答--------------------
引用 53 楼  的回复:
发现好多基础都忘了。。 趁机学习学习。。哇咔咔!!!


2222222 --------------------编程问答--------------------
引用 54 楼  的回复:
引用 53 楼 的回复:
发现好多基础都忘了。。 趁机学习学习。。哇咔咔!!!


2222222

你想表达什么!!! --------------------编程问答--------------------   学习   不过。 。。。。想表达什么 --------------------编程问答-------------------- --------------------编程问答--------------------
引用 48 楼  的回复:
前面说过,只需要修改Bottle.Draw()方法就可以改变瓶子的显示外观。于是我用画图鼠绘了一个丑陋的瓶子,就像这样:

这个瓶子 更卡通一些,耐看 --------------------编程问答-------------------- 野比好强! --------------------编程问答--------------------
引用 52 楼  的回复:
这种情况的发生是因为算法问题么~!!!!
有啥算法啊。。就随机分了几个数。。哈哈。。 --------------------编程问答-------------------- 牛人,支持楼主 --------------------编程问答-------------------- 楼主真是牛人。可惜我看不懂这种语言。。。
就会一点点C。。 --------------------编程问答-------------------- 支持,学习下。
--------------------编程问答-------------------- 又见野比,每次见到都会有个惊喜! --------------------编程问答--------------------
引用 60 楼  的回复:
引用 52 楼 的回复:

这种情况的发生是因为算法问题么~!!!!
有啥算法啊。。就随机分了几个数。。哈哈。。

哈哈你赶紧改改,这可不好,过不了关了。 --------------------编程问答-------------------- --------------------编程问答-------------------- 学习了,谢谢共享。 --------------------编程问答-------------------- 好东西啊!!! --------------------编程问答-------------------- 好东西,有耐心 --------------------编程问答-------------------- 好东西 --------------------编程问答-------------------- --------------------编程问答-------------------- 这个有些意思。 --------------------编程问答-------------------- 看了许久才知道这个游戏怎么玩。。。。数学盲看这类东西都是要花更久的时间才看懂的。 --------------------编程问答--------------------
引用 73 楼  的回复:
看了许久才知道这个游戏怎么玩。。。。数学盲看这类东西都是要花更久的时间才看懂的。
你是大师,欢迎再加工。 --------------------编程问答-------------------- 牛人,顶一个 --------------------编程问答-------------------- 好东西顶一下! --------------------编程问答-------------------- lz有的实现不了把,比如十升瓶子放入三升水,两边是八升和四升,偶数之间再怎么加减也成不了奇数吧。这点应该改进一下,不能出现全偶数的瓶子把 --------------------编程问答-------------------- 3,6,12,分2升出来,怎么分? --------------------编程问答-------------------- 3,6,9分2升出来,怎么分? --------------------编程问答--------------------
引用 77 楼  的回复:
lz有的实现不了把,比如十升瓶子放入三升水,两边是八升和四升,偶数之间再怎么加减也成不了奇数吧。这点应该改进一下,不能出现全偶数的瓶子把
这只是个原型,欢迎自己改着玩。。 --------------------编程问答-------------------- 不错不错不错不错不错不错不错 --------------------编程问答-------------------- --------------------编程问答-------------------- 不错,古墓丽影4水袋称重迷题的增强版。 --------------------编程问答-------------------- 好东西啊 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 好。。。 挺支持这个东西。 --------------------编程问答-------------------- 好,支持这个.... --------------------编程问答-------------------- --------------------编程问答-------------------- 不错,顶一个! --------------------编程问答-------------------- 没看懂什么意思 --------------------编程问答-------------------- 看完了。。好像对面向对象编程有了更清晰的轮廓... --------------------编程问答-------------------- 挺有意思啊。 --------------------编程问答-------------------- 楼主好厉害  榜样 --------------------编程问答-------------------- 我看这个真的挺有意思啊。 --------------------编程问答-------------------- 看上去很好玩的样子呢~~~ --------------------编程问答-------------------- 看上去好像不错啊 --------------------编程问答--------------------
引用 60 楼  的回复:
引用 52 楼 的回复:

这种情况的发生是因为算法问题么~!!!!
有啥算法啊。。就随机分了几个数。。哈哈。。
还以为有算法。。。  --------------------编程问答-------------------- 挺好的一款游戏啊 --------------------编程问答-------------------- 楼主厉害,路过学习下。
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,