猜猜我的画!






4.87/5 (21投票s)
一个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;