How do I move a game piece(control) during runtime?

无人久伴 提交于 2019-12-11 13:57:16

问题


I am doing a simple C# program of the game Knights Tour in C# the hard way to learn all I can of C#. I have a board and a knight piece and the knight is a custom panel with the picture of the knight.

What I am attempting to do is allow the user to click and drag the knight piece control during run time (exactly the way you can move the control in design time to place it), but for whatever reason I have getting some very undesired results.

    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();
    }

I see no real reason why this code would not do the same thing, but I am obviously missing something. When I click on the knight and move it, it does not move at the same speed as the mouse, it moves much slower. It also fades while moving it where you cant see it.

How do i make the knight piece move the same way it does in the form designer in a way that makes sense moving a chess piece across a chess board?

Any assistance will be appreciated.


I updated the code a bit and it does seem to help, but the animation aspect of it is still quite choppy and the panel picks up a bit of the background as it moves and placed.

how does it do it in the form designer so smoothly?

    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;*/
        }
    }

回答1:


Why don't you just set the Knights position the same as the mouse position in the Mouse_Move method?

Something like:

kpcKnight.Location = new Point(e.X, e.Y)

Obviously you can make it move nicer by knowing where the Knight got initially clicked and move smoothly according to that delta without having the initial jitter.




回答2:


It's drawing so slowly because you're moving a panel with every mouse motion. That means the form needs to redraw itself several times, and a complete form redraw is expensive.

The basic solution is - don't change the panel's position that often.

I see two ways of doing it.

  1. The first way is simple, but may look jerky. Don't draw every mouse movement. Draw every 5th one, or some arbitrary number you set. Basically keep a counter that you set to 0 on mouse down, and every time you get a mouse move, check if ++counter % n == 0. If so, do the actual drawing. Just make sure to draw on mouse up, as well. Or, just as effectively, only draw when one mouse movement is a certain number of points in x or y away from the previous position.

  2. The second idea is more complicated, but should be the fastest, least jerky thing you can do. Don't move the panel at all while the mouse is moving. Instead, make the panel disappear, and set the cursor to a custom cursor showing the knight. On mouse up, reset the cursor, move the panel and make it visible. This should be about as fast as you can get.

Edit

I'm going to go into the realm of metaphor here, just to get a few things across. Animation is an aspect of C#, but it's not one of the features of it. (i.e., you can accomplish it, but it doesn't have much to make these things easy on you, and it's not a simple key feature.) So... metaphor.

Think of the controls you've placed on your screen to make your board and knight as a bunch of cars packed tight into a parking lot. All you're doing is looking at the parking lot from a helicopter high up. What you're telling the runtime to do, every time you move a component, is completely bulldoze the cars off the parking lot, then place them with a crane in new positions. That's the scope that I'm talking about when I say "a complete form redraw is expensive."

Instead, what you want to do from your helicopter is percieve that the cars are magically changing position. Rather than have a bulldozer and a crane, just blank out your helicopter view, take a snapshot of what you want to see, and change the snapshot little by little, until it looks the way you want. That's what the second suggestion is - don't consantly force the form to recalculate each component's look. Instead, put the animation above the form, and only change the form when you're done.

The keywords you want to search for are "gdi+" (the .NET graphics package), and animation. MouseMove wouldn't hurt, and Paint is the event where you may need to do the animations. There are plenty of places you can find, though How to draw rectangle on MouseDown/Move c# could be a good start.

Edit #2

One last suggestion I have. You can use this in addition to any animation you make. Functionally, it satisfies your requirements on its own, but it's probably not what you're looking for. Track the mouse, and modify the background image of whatever panel the mouse is hovering over. In that case, you'll want to be looking at ImageList, and simple properties like your control BackgroundImage. This could be nice even if you do have a better animation working. You can easily use this to show "the knight can't move here" or "the knight has already moved here." Since it's changing your component instead of moving your component, it's really inexpensive to do, and can easily keep up with your mouse movement. It may be sufficient to imply the movement you want, and it will teach you aspects of winforms that are more important and frequently used than animation and GDI+ rendering.




回答3:


All you need is:

    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));
        }
    }



回答4:


Transparency in .Net is a bit of a misnomer. The background simply becomes the color of the parent container. When your controls overlap with each, as will likely be the case when the pieces are dragged across the board, then you'll see the "background" of the piece because controls are rectangular. One option would be to actually CLIP the PictureBox so it is an irregular shape. This can be accomplished by creating a Region() from a GraphicsPath() and then assigning that to the Region() property of the PictureBox. A simplistic approach is to use whatever color is in the top left of the Image and use that as the "mask" color. Next walk the entire image and only add locations where pixels are not the mask color to the GraphicsPath(). This only needs to be done once with the PictureBox after the Image() has been assigned. Again, this approach requires that the "background" of your image (the parts you do NOT want to keep) are all the same color, AND also that this color is not present anywhere as part of the image you want to keep. Here's an example:

    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);
        }
    }


来源:https://stackoverflow.com/questions/16366453/how-do-i-move-a-game-piececontrol-during-runtime

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!