65.9K
CodeProject 正在变化。 阅读更多。
Home

猜猜我的画!

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (21投票s)

2012年12月26日

CPOL

3分钟阅读

viewsIcon

51369

downloadIcon

727

一个Windows Forms应用程序,以一种游戏化的方式与许多客户端共享一个白板,只有一个绘图者。

引言

这段代码是一个Windows应用程序,它基本上是一个白板共享服务,用户之间使用另一个代码作为服务器;它由一个供多人共享的白板组成,其中一人作为绘图者,其他人作为猜谜者。绘图者是可以在白板上画画的人,而猜谜者是接收绘画并尝试猜测绘画的人。第一个人(绘图者)写一个词来描述他正在画的东西,而猜谜者不知道,然后他开始画画,猜谜者试图猜出画的内容,游戏就继续进行。代码是用C#编写的,数据通过UDP传输。

客户端可以通过选择他们喜欢的任何名称和服务器的IP来登录。

使用代码 

有两个代码文件,一个是服务器,一个是客户端。

服务器

这是一个控制台应用程序,它将客户端添加到其列表中,并接收来自客户端的UDP消息,分析它们,然后将它们转发/不转发给其他客户端。例如,如果一个猜谜者猜对了词,其他猜谜者将在文本框中收到通知;另一个例子是绘画的秘密词,它不会转发给其他客户端,而是保留在服务器中,以便在每次猜测时进行检查。

客户端:

这个解决方案有两个类,Windows Forms类和Panel类。

Windows Forms类称为Form1,一个愚蠢的名字!但它有点意义,因为这是我的第一个C#项目。

总之,该类由四个方法组成,用于捕获界面上四个按钮的点击事件。

它有两个后台线程,read()DrawOnPanel()

首先是read()方法,它用于在用户加入绘图者或猜谜者之后从UDP连接读取数据。

public void read()
{
    Thread t = new Thread(new ThreadStart(delegate
    {
        while(!abortThreads)
        {
            IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
            byte[] data = {0};
            try
            {
                data = client.Receive(ref remoteEP);
                msg = Encoding.ASCII.GetString(data, 0, data.Length);

            }
            catch (SocketException e)
            {
                abortThreads = true;
            }

            if (msg.StartsWith("draw"))
            {
                msg = msg.Substring(5);
                int xVal = 0, yVal = 0;
                for (int i = 0; msg.Length - 1 > 0; i++)
                {
                    if (msg.Substring(i, 1) == "y")
                    {
                        xVal = Int32.Parse(msg.Substring(0, i));
                        msg = msg.Substring(i + 1);
                        i = 0;
                    }

                    if (msg.Substring(i, 1) == "x")
                    {
                        yVal = Int32.Parse(msg.Substring(0, i));
                        msg = msg.Substring(i + 1);
                        toDraw.Add(new Point(xVal, yVal));
                        i = 0;
                    }
                }
                startDrawing = true;
            }

            else if (msg.StartsWith("accept"))
                SetText(Encoding.ASCII.GetString(data, 0, data.Length));

            else if (msg.StartsWith("gues"))
            {
                if (msg.EndsWith("true"))
                    SetText(msg.Substring(5, msg.Length - 9) + " which was RIGHT! ");
                if (msg.EndsWith("false"))
                    SetText(msg.Substring(5, msg.Length - 10) + " which was WRONG! ");
            }

            else if (msg.StartsWith("quit"))
            {
                msg = "quit";
                data = Encoding.ASCII.GetBytes(msg);
                client.Send(data, data.Length);
                client.Close();
                SetText("The drawer quitted, the game is finished! ");
                abortThreads = true;
                Thread.CurrentThread.Abort();
            }
            else
            {
                if(msg.Length > 5) 
                    SetText(msgList.Text + Environment.NewLine + 
                      Encoding.ASCII.GetString(data, 4, data.Length - 4));
            }
        }
    }));
    t.IsBackground = true;
    t.Start();
}

它基本上等待UDP的接收方法发送消息,然后将其编码为字符串msg,然后通过一系列if-else语句检查其内容。

第二个线程DrawOnPanel(),每当有绘图消息时就会进行绘图,它由read()线程中的一个布尔变量startDrawing控制。

private void DrawOnPanel()
{
    hwnd = thePanel.Handle;
    Thread t0 = new Thread(new ThreadStart(delegate
        {
            using (Graphics graphics = Graphics.FromHwnd(hwnd))
            {
                while (!abortThreads)
                {
                    if(startDrawing)
                    if (toDraw.Count > 0)
                    {
                        object holder = toDraw[0];
                        Point now = (Point)holder, previous = (Point)holder;
                        for (int i = 0; i < toDraw.Count; i++)
                        {
                            holder = toDraw[i];
                            now = (Point)holder;
                            graphics.DrawLine(MyPanel.p, now, previous);
                            previous = now;
                        }
                        Console.WriteLine(now + "  last  " + previous);
                        startDrawing = false;
                        toDraw.Clear();
                    }
                }
            }
        }));

    t0.IsBackground = true;
    t0.Start();
} 

.. 

这就是Form1类的内容,其他方法可以在源代码中找到。

Panel类称为MyPanel,它重写了Panel类。

public class MyPanel : Panel ,它重写了三个鼠标事件方法:OnMouseDown()OnMouseUp()OnMouseMove()

我将逐一介绍它们。

第一个方法OnMouseDown()只有一行,只是将一个布尔值设置为true,以指示我们可以开始获取点并将其存储在绘图者的情况下。

protected override void OnMouseDown(MouseEventArgs e)
{
    mouse_down = true;
}

第三个方法OnMouseMove()将点存储在ArrayList中以供发送。

protected override void OnMouseMove(MouseEventArgs e)
{
    if (Form1.isDrawer)
    {
        if (last_point.Equals(Point.Empty))
            last_point = new Point(e.X, e.Y);
        if (mouse_down)
        {

            IntPtr hwnd = this.Handle;

            using (Graphics graphics = Graphics.FromHwnd(hwnd))
            {
                Point pMousePos = new Point(e.X, e.Y);
                graphics.DrawLine(p, pMousePos, last_point);
                Console.WriteLine(pMousePos + "  last  " + last_point);
                pointss.Add(pMousePos);
            }
        }
        last_point = new Point(e.X, e.Y);
    }
} 

第二个方法OnMouseUp()会将ArrayList转换为字符串,然后转换为字节,并通过建立的UDP连接发送。

protected override void OnMouseUp(MouseEventArgs e)
{
    mouse_down = false;
    if (Form1.isDrawer)
    {
        string msg = "draw";
        for (int i = 0; i < pointss.Count; i++)
        {
            Point poi = (Point)pointss[i];
            msg += ("x" + poi.X + "y" + poi.Y);
        }
        msg += "x";
        pointss.Clear();
        byte[] data = Encoding.ASCII.GetBytes(msg);
        if (client != null)
            client.Send(data, data.Length);
    }
}

以上就是该工具的主要思想,关于代码的任何问题都欢迎提出。

兴趣点 

我以一种奇怪的方式完成了一些事情,以使我的工具得以实现,你可能会觉得它们有趣或有创意,但最终它们工作得非常出色!这里有一些:

- C#中没有自由绘图能力,所以我的技巧是画小线段,使其看起来像自由绘图风格。

- 我将ArrayList中的线段点转换为字符串,以便轻松地通过UDP连接发送。

没有调用Paint()方法,在Panel上绘图一直是个难题!我只在Panel的一个小区域绘图,所以我使用了我在DrawOnPanel()方法中开发的一种技巧来解决这个问题,这得益于Stackoverflow网站上的这个问答帖。

参考。

 

 结论

至此,我完成了我的第一篇在线文章,如果您有任何反馈、问题、想法或任何评论,请畅所欲言,我很乐意听到您富有创造力的头脑中正在发生的一切。

历史 

null; 

© . All rights reserved.