我正在用C#做一个简单的C#游戏Knights Tour的程序,这是我学习C#所有方法的难点。我有一个棋盘和一个骑士棋子,骑士是一个定制的面板,上面有骑士的照片。
我试图做的是允许用户在运行时点击并拖动骑士棋子控件(完全按照设计时间移动控件放置它的方式),但无论出于什么原因我都得到了一些不受欢迎的结果。
private void KTmain_Load(object sender, EventArgs e)
{
boolKnightmoves = false;
}
private void kpcKnight_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
switch (e.Button)
{
case MouseButtons.Left:
boolKnightmoves = true;
intCurMouseX = e.X;
intCurMouseY = e.Y;
break;
case MouseButtons.Right:
case MouseButtons.Middle:
case MouseButtons.XButton1:
case MouseButtons.XButton2:
case MouseButtons.None:
default:
boolKnightmoves = false;
break;
}
}
private void kpcKnight_MouseUp(object sender, MouseEventArgs e)
{
switch (e.Button)
{
case MouseButtons.Left:
boolKnightmoves = false;
break;
case MouseButtons.Right:
case MouseButtons.Middle:
case MouseButtons.XButton1:
case MouseButtons.XButton2:
case MouseButtons.None:
default:
boolKnightmoves = false;
break;
}
}
private void kpcKnight_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (boolKnightmoves)
{
txtTest.Text = e.X + ", " + e.Y;
txtTest.Text += Environment.NewLine + kpcKnight.Location;
int i = e.X == intCurMouseX ? 0 : e.X > intCurMouseX ? 1 : -1;
int j = e.Y == intCurMouseY ? 0 : e.Y > intCurMouseY ? 1 : -1;
txtTest.Text += Environment.NewLine + i.ToString() + ", " + j.ToString();
kpcKnight.Location = new Point(
kpcKnight.Location.X + i,
kpcKnight.Location.Y + j);//e.Y == intCurMouseY ? 0 : e.Y > intCurMouseY ? 1 : -1);
//e.X == intCurMouseX ? 0 : e.X > intCurMouseX ? 1 : -1,
intCurMouseX = e.X;
intCurMouseY = e.Y;
}
}
private void kpcKnight_MouseLeave(object sender, EventArgs e)
{
boolKnightmoves = false;
}
private void kpcKnight_LocationChanged(object sender, EventArgs e)
{
kpcKnight.Refresh();
}
我看不出为什么这段代码不会做同样的事情,但我显然错过了一些东西。当我点击骑士并移动它时,它不会以与鼠标相同的速度移动,它移动得慢得多。它在你无法看到的地方移动时也会消失。
如何让骑士作品在表格设计师中的运动方式与在国际象棋棋盘上移动棋子的方式相同?
任何帮助将不胜感激。
我稍微更新了代码并且看起来似乎有所帮助,但它的动画方面仍然非常不稳定,并且面板在移动和放置时会拾取一些背景。
如何在表单设计器中如此顺利地完成它?
private void kpcKnight_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (boolKnightmoves)
{
txtTest.Text = e.X + ", " + e.Y;
txtTest.Text += Environment.NewLine + kpcKnight.Location;
int x = kpcKnight.Location.X + e.X - intCurMouseX;
int y = kpcKnight.Location.Y + e.Y - intCurMouseY;
kpcKnight.Location = new Point(x, y);
kpcKnight.Refresh();
/*
int i = e.X == intCurMouseX ? 0 : e.X > intCurMouseX ? 1 : -1;
int j = e.Y == intCurMouseY ? 0 : e.Y > intCurMouseY ? 1 : -1;
txtTest.Text += Environment.NewLine + i.ToString() + ", " + j.ToString();
kpcKnight.Location = new Point(
kpcKnight.Location.X + i,
kpcKnight.Location.Y + j);//e.Y == intCurMouseY ? 0 : e.Y > intCurMouseY ? 1 : -1);
//e.X == intCurMouseX ? 0 : e.X > intCurMouseX ? 1 : -1,
intCurMouseX = e.X;
intCurMouseY = e.Y;*/
}
}
答案 0 :(得分:2)
为什么不将Knights的位置设置为与Mouse_Move方法中的鼠标位置相同?
类似的东西:
kpcKnight.Location = new Point(e.X, e.Y)
显然,你可以通过了解Knight最初点击的位置并根据该delta平滑移动而不会产生初始抖动来使其更好地移动。
答案 1 :(得分:2)
它的绘制速度非常慢,因为每次鼠标移动时都会移动一个面板。这意味着表单需要多次重绘,并且完整的表单重绘很昂贵。
基本解决方案是 - 不要经常更改面板的位置。
我看到了两种方法。
第一种方式很简单,但可能看起来很生涩。不要画每一个鼠标移动。每隔5个画一次,或者你设定的任意数字。基本上保留一个在鼠标按下时设置为0的计数器,每次鼠标移动时,检查++ counter%n == 0.如果是,请执行实际绘图。只要确保在鼠标上画画。或者,同样有效的是,只有当一个鼠标移动距离前一个位置的x或y一定数量的点时才会绘制。
第二个想法更复杂,但应该是你能做的最快,最不生涩的事情。鼠标移动时,请勿移动面板。相反,让面板消失,并将光标设置为显示骑士的自定义光标。在鼠标向上时,重置光标,移动面板并使其可见。这应该尽可能快。
我将在这里进入隐喻领域,只是为了得到一些东西。动画是C#的一个方面,但它不是它的一个特性。 (也就是说,你可以完成它,但它并没有太多让你轻松的事情,而且它不是一个简单的关键特征。)所以...隐喻。
想想你放在屏幕上的控件,让你的棋盘和骑士像一堆车紧紧地塞进一个停车场。你所做的只是从高处直升机上看停车场。每当你移动一个组件时,你告诉运行时要做的就是将汽车完全推倒停车场,然后用起重机将它们放在新的位置。当我说“完整的表格重绘是昂贵的”时,这就是我所说的范围。
相反,您想要从直升机上做的事情就是让汽车神奇地改变位置。不要使用推土机和起重机,只需清空直升机视图,拍摄想要看到的内容的快照,然后一点一点地更改快照,直到它看起来像你想要的那样。这就是第二个建议 - 不要强迫表单重新计算每个组件的外观。相反,将动画放在表单上方,只有在完成后才更改表单。
您要搜索的关键字是“gdi +”(.NET图形包)和动画。 MouseMove不会受到伤害,而Paint是你可能需要做动画的事件。虽然How to draw rectangle on MouseDown/Move c#可能是一个良好的开端,但您可以找到很多地方。
我有一个最后的建议。除了您制作的任何动画外,您还可以使用此功能。从功能上来说,它可以满足您自己的要求,但它可能不是您想要的。跟踪鼠标,并修改鼠标悬停在任何面板上的背景图像。在这种情况下,您需要查看ImageList
和简单属性,例如您的控件BackgroundImage
。即使你有更好的动画效果,这也许会很好。你可以很容易地用它来表明“骑士不能在这里移动”或“骑士已经搬到这里”。由于它正在改变您的组件而不是移动您的组件,因此它的成本非常低廉,并且可以轻松跟上您的鼠标移动。它可能足以暗示你想要的运动,并且它将教你比动画和GDI +渲染更重要和经常使用的winforms方面。
答案 2 :(得分:1)
您只需要:
private int intCurMouseX, intCurMouseY;
private void kpcKnight_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
intCurMouseX = e.X;
intCurMouseY = e.Y;
}
}
private void kpcKnight_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
kpcKnight.Location = new Point(
kpcKnight.Location.X + (e.X - intCurMouseX),
kpcKnight.Location.Y + (e.Y - intCurMouseY));
}
}
答案 3 :(得分:0)
.Net中的透明度有点用词不当。背景只是成为父容器的颜色。当您的控件与每个控件重叠时,可能就是将各个部件拖过棋盘时的情况,然后您将看到该部件的“背景”,因为控件是矩形的。一种选择是实际CLIP PictureBox,因此它是不规则的形状。这可以通过从GraphicsPath()创建Region()然后将其分配给PictureBox的Region()属性来实现。一种简单的方法是使用图像左上角的任何颜色,并将其用作“蒙版”颜色。接下来,遍历整个图像,仅添加像素不是MaskPath()的蒙版颜色的位置。在分配Image()之后,只需要使用PictureBox执行一次。同样,这种方法要求图像的“背景”(您不想保留的部分)都是相同的颜色,并且此颜色不会作为您想要保留的图像的一部分存在。这是一个例子:
private void Form1_Load(object sender, EventArgs e)
{
// perform this for all your PictureBox pieces:
this.ClipPictureBoxPiece(this.kpcKnight);
// ...
// ...
}
private void ClipPictureBoxPiece(PictureBox pb)
{
if (pb != null && pb.Image != null)
{
System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
using (Bitmap bmp = new Bitmap(pb.Image))
{
Color mask = bmp.GetPixel(0, 0);
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
if (!bmp.GetPixel(x, y).Equals(mask))
{
gp.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
}
pb.Region = new Region(gp);
}
}