winforms 每当我尝试旋转PictureBox时,图像就会消失

qgelzfjb  于 2023-06-24  发布在  其他
关注(0)|答案(1)|浏览(154)

我尝试在C# winforms中旋转图像,每当我按下键盘时。我试图使用这篇文章https://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations和这个答案https://stackoverflow.com/a/26455088/1907765来实现这一点,但是每当我应用变换时,图像就会消失。我不知道该怎么纠正。
图像在运行时从Resource.resx文件加载到PictureBox中。PictureBox位于面板顶部。
在这里列出代码的相关部分:

public partial class Form1 : Form
    {
        public List<PictureBox> Walls;

        public bool MoveUp, MoveDown, MoveLeft, MoveRight;
        public bool RotateClockwise, RotateAnticlockwise;
        public bool PlayerHasShot;

        public bool GameOver = false;

        public int PlayerAngle = 0;
        public Point PlayerPos = new Point(300, 300);

        Bitmap PlayerImage;

        // Game defaults
        public readonly int BulletSpeed = 20;
        public readonly int PlayerSpeed = 5;

        public Form1()
        {
            InitializeComponent();
            // <....>
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Load the player image from bitmap
            PbxPlayer.Parent = PnlGameBoard;

            Image img = Properties.Resources.tankbase_orig;
            PlayerImage = new Bitmap(img);

            int dpi = 96;
            using (Graphics G = PnlGameBoard.CreateGraphics())
                dpi = (int)G.DpiX;
            PlayerImage.SetResolution(dpi, dpi);

            PbxPlayer.Image = (Bitmap)PlayerImage.Clone();
            PbxPlayer.ClientSize = PlayerImage.Size;
        }

        private void MainTimer_Tick(object sender, EventArgs e)
        {

            Point newPoint = new Point();

            if (MoveUp == true)
            {
                // Move player up 1 pixel until they've reached 'PlayerSpeed' or collided with something
                for (int Y = 1; Y <= PlayerSpeed; Y++)
                {
                    newPoint = new Point(PbxPlayer.Location.X, PbxPlayer.Location.Y - 1);
                    if (DetectCollision(PbxPlayer, newPoint))
                        break;
                    PbxPlayer.Location = newPoint;
                }
            }

            if (MoveLeft == true)
            {
                // same logic as MoveUp
            }

            if (MoveDown == true)
            {
                // same logic as MoveUp
            }

            if (MoveRight == true)
            {                    
                // same logic as MoveUp
            }

            if (RotateAnticlockwise == true)
            {
                PlayerAngle -= 2;
                if (PlayerAngle < 0)
                    PlayerAngle = 360;
                // Debug label to check the angle is set correctly
                LblAngle.Text = PlayerAngle.ToString();

                // The amount to move the image by so that it rotates around centre point
                PointF offsets = new PointF(PlayerImage.Width / 2f, PlayerImage.Height / 2f);

                // Save old image
                Image oldImage = PbxPlayer.Image;
                // Return new image rotated by an angle of -2 degrees.
                PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, -2);
                // Force the picturebox to redraw (I think?)
                PbxPlayer.Invalidate();
                if (oldImage != null)
                    oldImage.Dispose();
            }

            if (RotateClockwise == true)
            {
                PlayerAngle += 2;
                PlayerAngle %= 360;
                LblAngle.Text = PlayerAngle.ToString();

                PointF offsets = new PointF(PlayerImage.Width / 2f, PlayerImage.Height / 2f);

                Image oldImage = PbxPlayer.Image;
                PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, 2);
                PbxPlayer.Invalidate();
                if (oldImage != null)
                    oldImage.Dispose();
            }

            foreach (Control ctl in PnlGameBoard.Controls)
            {
                if (ctl is PictureBox && (string)ctl.Tag == "bullet")
                {
                    ctl.Left += BulletSpeed;

                    if (ctl.Left > PnlGameBoard.Width || DetectCollision((PictureBox)ctl, ctl.Location))
                    {
                        RemoveBullet((PictureBox)ctl);
                    }
                }
            }
        }

        private void KeyIsDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.W || e.KeyCode == Keys.Up)
                MoveUp = true;

            if (e.KeyCode == Keys.A || e.KeyCode == Keys.Left)
                MoveLeft = true;

            if (e.KeyCode == Keys.S || e.KeyCode == Keys.Down)
                MoveDown = true;

            if (e.KeyCode == Keys.D || e.KeyCode == Keys.Right)
                MoveRight = true;

            if (e.KeyCode == Keys.Q || e.KeyCode == Keys.Oemcomma)
                RotateAnticlockwise = true;

            if (e.KeyCode == Keys.E || e.KeyCode == Keys.OemPeriod)
                RotateClockwise = true;

            if (e.KeyCode == Keys.Space && PlayerHasShot == false)
            {
                MakeBullet();
                PlayerHasShot = true;
            }

        }

        private void KeyIsUp(object sender, KeyEventArgs e)
        {

            if (e.KeyCode == Keys.W || e.KeyCode == Keys.Up)
                MoveUp = false;

            if (e.KeyCode == Keys.A || e.KeyCode == Keys.Left)
                MoveLeft = false;

            if (e.KeyCode == Keys.S || e.KeyCode == Keys.Down)
                MoveDown = false;

            if (e.KeyCode == Keys.D || e.KeyCode == Keys.Right)
                MoveRight = false;

            if (e.KeyCode == Keys.Q || e.KeyCode == Keys.Oemcomma)
                RotateAnticlockwise = false;

            if (e.KeyCode == Keys.E || e.KeyCode == Keys.OemPeriod)
                RotateClockwise = false;

            // Stop repetitive shots
            if (PlayerHasShot == true)
                PlayerHasShot = false;

            if (e.KeyCode == Keys.Enter && GameOver == true)
                RestartGame();
        }

        private void PnlGameBoard_Paint(object sender, PaintEventArgs e)
        {
            // I feel like I should be doing something else in this event to make the image persist
            e.Graphics.DrawImage(PbxPlayer.Image, PbxPlayer.Location);
        }

        public Bitmap RotateImage(Image image, PointF offset, Point imagePos, int angle)
        {
            // Credit: https://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations

            if (image == null)
                return null;

            Bitmap rotatedBitmap = new Bitmap(image.Width, image.Height);
            rotatedBitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution);

            using (Graphics G = Graphics.FromImage(rotatedBitmap))
            {
                G.TranslateTransform(offset.X, offset.Y);
                G.RotateTransform(angle);
                G.TranslateTransform(-offset.X, -offset.Y);
                G.DrawImage(image, new Point(imagePos.X, imagePos.Y));
            }

            return rotatedBitmap;

        }
        public void MakeBullet()
        {
            PictureBox bullet = new PictureBox();
            bullet.BackColor = Color.DarkGray;
            bullet.BorderStyle = BorderStyle.FixedSingle;
            bullet.Height = 5;
            bullet.Width = 10;

            bullet.Left = PbxPlayer.Left + PbxPlayer.Width;
            bullet.Top = PbxPlayer.Top + (PbxPlayer.Height / 2);

            bullet.Tag = "bullet";
            PnlGameBoard.Controls.Add(bullet);
        }

        public void RemoveBullet(PictureBox bullet)
        {
            Controls.Remove(bullet);
            bullet.Dispose();
        }

        /// <summary>
        /// Return true if 'pbx' PictureBox intersects with another PictureBox with the 'wall' tag
        /// </summary>
        /// <param name="pbx">The PictureBox we are checking for a collision</param>
        /// <returns></returns>
        public bool DetectCollision(PictureBox pbx, Point newPoint)
        {
            // Create a temporary PictureBox object to check for boundary collision without moving original pbx.
            PictureBox newpbx = new PictureBox();
            newpbx.Size = new Size(pbx.Width, pbx.Height);
            newpbx.Location = new Point(newPoint.X, newPoint.Y);
            foreach (var wall in Walls)
            {
                if (newpbx.Bounds.IntersectsWith(wall.Bounds))
                    return true;
            }
            // Release temporary object
            newpbx.Dispose();
            return false;
        }
   

    }

当我启动代码时,图像显示:

然后,如果我按下“Q”,我已经Map到逆时针旋转图像:

如果我试着顺时针旋转它,结果是一样的。
要使旋转后的图像显示在PictureBox中,我缺少什么?

dy2hfwbg

dy2hfwbg1#

图像 * 消失 *,因为您在传递给RotateImage方法的PbxPlayer.Location处绘制它。

if (RotateAnticlockwise == true)
{
    // ...
    PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, -2);
    // ...
}

if (RotateClockwise == true)
{
    // ...
    PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, 2);
    // ...
}

偏移位置时,您正在图像边界或绘图画布之外绘制图像。如果只想围绕中心旋转图像,则位置必须等于Point.Empty

public Bitmap RotateImage(Image image, PointF offset, int angle)
{
    if (image == null) return null;

    Bitmap rotatedBitmap = new Bitmap(image.Width, image.Height);
    rotatedBitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution);

    using (Graphics G = Graphics.FromImage(rotatedBitmap))
    {
        G.TranslateTransform(offset.X, offset.Y);
        G.RotateTransform(angle);
        G.TranslateTransform(-offset.X, -offset.Y);
        G.DrawImage(image, Point.Empty);
    }

    return rotatedBitmap;
}

说到旋转图像的 * 绘图画布 *。它是PbxPlayer,不是PnlGameBoard。因此,实现后者的Paint事件在这里是没有意义的,因为您在PictureBox中显示了旋转,并在定时器的Tick事件中移动了它,其中设置了PbxPlayer.Location = newPoint;。因此,您似乎不太可能想要在PnlGameBoard表面上绘制。
注意,不需要以这种方式为每个旋转Angular 持续创建图像。您只需要源映像并将RotateImage例程移动到PictureBox.Paint事件。

private readonly Image playerImage = Properties.Resources.SomeImage;
private int PlayerAngle = 0;
private bool RotateClockwise = true;

private void MainTimer_Tick(object sender, EventArgs e)
{
    PlayerAngle += RotateClockwise ? 2 : -2;
    PlayerAngle %= 360;
    PbxPlayer.Invalidate();
}

private void PbxPlayer_Paint(object sender, PaintEventArgs e)
{
    var g = e.Graphics;
    var src = (sender as Control).ClientRectangle;

    g.TranslateTransform(src.Width / 2, src.Height / 2);
    g.RotateTransform(PlayerAngle);
    g.TranslateTransform(-src.Width / 2, -src.Height / 2);
    g.DrawImage(playerImage, Point.Empty);
    g.ResetTransform();
}

相关问题