我如何在运行时移动游戏部件(控制)
本文关键字:控制 游戏部 移动 运行时 | 更新日期: 2023-09-27 18:11:58
我正在做一个简单的c#程序的游戏骑士之旅在c#艰难的方式来学习所有我能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;*/
}
}
为什么不直接将骑士的位置设置为与Mouse_Move方法中的鼠标位置相同呢?
类似:
kpcKnight.Location = new Point(e.X, e.Y)
显然,你可以让它移动得更好,知道骑士最初被点击的位置,并根据delta平滑移动,而不会出现初始抖动。
它画得很慢,因为你每次移动鼠标都在移动面板。这意味着表单需要多次重新绘制自己,而完整的表单重新绘制是昂贵的。
基本的解决方法是——不要经常改变面板的位置。
我认为有两种方法。
-
第一种方法很简单,但可能看起来很不稳定。不要画出每一个鼠标移动。每隔5张画一张,或者你设定的任意数字。基本上保持一个你在鼠标向下时设置为0的计数器,每次鼠标移动时,检查是否++计数器% n == 0。如果是这样,请实际绘图。只要确保鼠标向上绘制即可。或者,同样有效地,只在鼠标移动距离前一个位置在x或y上有一定数量的点时绘制。
-
第二个想法更复杂,但应该是你能做的最快、最不稳定的事情。鼠标移动时不要移动面板。相反,让面板消失,并将光标设置为显示骑士的自定义光标。鼠标向上,重置光标,移动面板,使其可见。这应该是你最快的速度了
编辑
我将在这里进入隐喻的领域,只是为了让大家明白一些事情。动画是c#的一个方面,但它不是c#的特性之一。(也就是说,你可以完成它,但它并没有让这些事情变得容易,它不是一个简单的关键功能。)所以…比喻。
想象一下你在屏幕上放置的控制,让你的棋盘和骑士像一堆挤在停车场里的汽车。你所做的就是从直升机上俯瞰停车场。你告诉运行时要做的是,每次你移动一个组件,就把汽车从停车场完全推平,然后用起重机把它们放在新的位置。这就是我所说的"完整的表单重绘是昂贵的"的范围。
相反,你想从直升机上做的是感知汽车神奇地改变位置。与其用推土机和起重机,不如把你的直升机视野清空,给你想看的东西拍一张快照,一点一点地改变快照,直到它看起来像你想要的样子。这就是第二个建议——不要不断地强迫表单重新计算每个组件的外观。相反,把动画放在表单的上方,并且只在完成后更改表单。
你想要搜索的关键字是"gdi+"(. net图形包)和动画。MouseMove不会有问题,而Paint是你可能需要做动画的事件。有很多地方你可以找到,尽管如何在鼠标上下移动时绘制矩形c#可能是一个很好的开始。
编辑# 2
我有最后一个建议。你可以用它来制作任何动画。从功能上讲,它本身可以满足您的需求,但它可能不是您想要的。跟踪鼠标,并修改鼠标悬停在任何面板上的背景图像。在这种情况下,您将需要查看ImageList
,以及像控件BackgroundImage
这样的简单属性。即使你有一个更好的动画工作,这也会很好。你可以很容易地用它来显示"骑士不能移动到这里"或"骑士已经移动到这里了"。因为它是改变你的组件而不是移动你的组件,所以成本很低,而且可以很容易地跟上你的鼠标移动。它可能足以暗示您想要的运动,并且它将教会您比动画和GDI+渲染更重要和更常用的winforms方面。
所有你需要的是:
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));
}
}
. net中的透明度有点用词不当。背景只是成为父容器的颜色。当你的控件与每个控件重叠时,就像在棋盘上拖拽棋子的情况一样,那么你就会看到棋子的"背景",因为控件是矩形的。一种选择是剪辑PictureBox,使其成为不规则形状。这可以通过从GraphicsPath()创建一个Region(),然后将其分配给PictureBox的Region()属性来实现。一种简单的方法是使用图像左上角的任何颜色,并将其用作"蒙版"颜色。接下来遍历整个图像,只将像素不是掩码颜色的位置添加到GraphicsPath()中。这只需要在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);
}
}