winforms 我想实现一个迷宫游戏功能,防止玩家在遇到墙壁时移动

watbbzwu  于 2022-11-16  发布在  其他
关注(0)|答案(1)|浏览(216)

蓝色图片框=播放器图片框
新游戏按钮= btn创建

首先,迷宫的形状在gdi+中实现。

如何实现玩家移动的功能,但如果遇到墙,如何实现不移动的功能?

private void btnCreate_Click(object sender, EventArgs e)
    {
        int wid = 15;
        int hgt = 15;

        CellWidth = picMaze.ClientSize.Width / (wid+2);
        CellHeight = picMaze.ClientSize.Height / (hgt+2);

        Xmin = (picMaze.ClientSize.Width - wid * CellWidth) / 2;
        Ymin = (picMaze.ClientSize.Height - hgt * CellHeight) / 2;
    
        MazeNode[,] nodes = MakeNodes(wid, hgt);
        MakeRandomMaze(nodes[0, 0]);
        DisplayMaze(nodes);

        PlayerPictureBox.Visible = true;
        arrivePicturBox.Visible = true;

        PlayerPictureBox.Location = new Point(70,51);
    }


    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        switch (keyData)
        {
            case Keys.Left:

                PlayerPictureBox.Left -= 5;
                break;

            case Keys.Right:
                PlayerPictureBox.Left += 5;
                break;

            case Keys.Up:
                PlayerPictureBox.Top -= 5;
                break;

            case Keys.Down:
                PlayerPictureBox.Top += 5;
                break;

            default: return base.ProcessCmdKey(ref msg, keyData);
        }

        return true;
    }
cetgtptt

cetgtptt1#

你需要在一个矩形和一个(在你的例子中,它是一个正方形)和一条线段。尽管所有的形状都是轴对齐的,但算法相当复杂。它涉及到大量的矢量数学和计算几何技巧。我认为你可能不想学习背后的数学。因此,我不解释算法。如果你想,你可以问另一个关于它如何工作的问题。
实现是我自己的实现。它不是基于任何关于主题的论文或外部示例源代码。它可能没有优化好。请记住这一点。
我已经创建了一个LineSegment类来保存行信息,如果您要使用我的算法,我建议您根据需要使用该类或扩展它。

PS:* 我必须承认,我认为碰撞算法会很简单,但它已经演变成一个复杂得多的算法,特别是对于一个初学者的碰撞数学。*

下面是Winforms应用程序的例子,你可以根据自己的需要修改它。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace LineRectangleCollision
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
            InitializeComponent();
            lineSegments = new List<LineSegment>();
            lineSegments.Add(new LineSegment(new PointF(100, 100), new PointF(200, 100)));
            lineSegments.Add(new LineSegment(new PointF(100, 100), new PointF(100, 300)));
            lineSegments.Add(new LineSegment(new PointF(400, 100), new PointF(400, 300)));

            points = new List<PointF>();
            points.Add(new PointF(10, 10));
            points.Add(new PointF(256, 485));
            points.Add(new PointF(110, 50));

            rectangle = new RectangleF(0, 0, 30, 30);
        }
        private List<LineSegment> lineSegments;
        private List<PointF> points;
        private RectangleF rectangle;
        private float velocity = 5f;
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics g = e.Graphics;
            g.Clear(Color.White);
            for (int i = 0; i < points.Count; i++)
            g.FillEllipse(Brushes.Blue, points[i].X - 2, points[i].Y - 2, 4, 4);
            for (int i = 0; i < lineSegments.Count; i++)
                lineSegments[i].Draw(g);
            
            g.FillRectangle(Brushes.Red, rectangle);
        }
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            switch (e.KeyCode)
            {
                case Keys.Left:
                    rectangle.Offset(-velocity, 0);
                    break;
                case Keys.Up:
                    rectangle.Offset(0, -velocity);
                    break;
                case Keys.Right:
                    rectangle.Offset(velocity, 0);
                    break;
                case Keys.Down:
                    rectangle.Offset(0, velocity);
                    break;
            }
            bool result = false;
            for (int i = 0; i < lineSegments.Count; i++)
            {
                PointF translationVector = CheckCollision(rectangle, lineSegments[i], out result);
                if (result)
                {
                    rectangle.Offset(translationVector.X, translationVector.Y);
                    continue;
                }
            }
            for (int i = 0; i < points.Count; i++)
            {
                PointF translationVector = CheckCollision(rectangle, points[i], out result);
                if (result)
                {
                    rectangle.Offset(translationVector.X, translationVector.Y);
                    continue;
                }
            }
            Invalidate();
        }
        private PointF CheckCollision(RectangleF rect, LineSegment line, out bool result)
        {
            PointF lineParallel = line.Unit;
            PointF rectCenter = new PointF(rect.X + rect.Width / 2.0f, rect.Y + rect.Height / 2.0f);
            PointF collisionVector = VectorMath.Subtract(rectCenter, line.Start);
            float minHalfLength = line.IsVertical ? rect.Width : rect.Height;
            minHalfLength /= 2.0f;
            float length = VectorMath.Length(VectorMath.Subtract(line.End, line.Start))
            float t = VectorMath.Dot(collisionVector, lineParallel);

            t = t < 0 ? t + minHalfLength : t - minHalfLength;

            if (t > 0 && t <= length)
            {
                PointF translationVector = CheckCollision(rect, line.Start, out result);
                if (result)
                    return translationVector;
                translationVector = CheckCollision(rect, line.End, out result);
                if (result)
                    return translationVector;

                PointF collisionNormal = VectorMath.RightPerp(lineParallel);
                float d = VectorMath.Dot(collisionNormal, collisionVector);

                if (d < 0)
                    collisionNormal = new PointF(-collisionNormal.X, -collisionNormal.Y);
                d = Math.Abs(d);
                if (d > minHalfLength)
                {
                    result = false;
                    return PointF.Empty;
                }
                result = true;
                float penetration = minHalfLength - d + 0.5f;
                return new PointF(penetration * collisionNormal.X, penetration * collisionNormal.Y);
            }
            result = false;
            return PointF.Empty;
        }
        private PointF CheckCollision(RectangleF rect, PointF point, out bool result)
        {
            if(rect.Contains(point))
            {
                LineSegment[] sides = new LineSegment[4];
                sides[0] = new LineSegment(rect.X, rect.Y, rect.X + rect.Width, rect.Y);
                sides[1] = new LineSegment(rect.X + rect.Width, rect.Y, rect.X + rect.Width, rect.Y + rect.Height);
                sides[2] = new LineSegment(rect.X + rect.Width, rect.Y + rect.Height, rect.X, rect.Y + rect.Height);
                sides[3] = new LineSegment(rect.X, rect.Y + rect.Height, rect.X, rect.Y);

                result = true;
                float minPen = float.MaxValue;
                int index = 0;
                for (int i = 0; i < 4; i++)
                {
                    float d = VectorMath.GetDistanceBetweenPointLine(point, sides[i]);
                    if (d < minPen)
                    {
                        minPen = d;
                        index = i;
                    }
                }
                return VectorMath.Multiply(VectorMath.RightPerp(sides[index].Unit), minPen);
            }
            result = false;
            return PointF.Empty;
        }
        private class LineSegment
        {
            public PointF Start;
            public PointF End;

            public LineSegment(float x0, float y0, float x1, float y1) : this(new PointF(x0, y0), new PointF(x1, y1))
            {
            }
            public LineSegment(PointF start, PointF end)
            {
                Start = start;
                End = end;
            }
            public bool IsVertical
            {
                get
                {
                    return Start.X == End.X;
                }
            }
            public PointF Unit
            {
                get
                {
                    PointF unit = new PointF(End.X - Start.X, End.Y - Start.Y);
                    float length = VectorMath.Length(unit);
                    unit.X /= length;
                    unit.Y /= length;
                    return unit;
                }
            }
            public void Draw(Graphics g)
            {
                using (Pen pen = new Pen(Color.Black, 2.0f))
                    g.DrawLine(pen, Start, End);
            }
        }
        private class VectorMath
        {
            public static float Dot(PointF v0, PointF v1)
            {
                return v0.X * v1.X + v0.Y * v1.Y; 
            }
            public static float Length(PointF vector)
            {
                return (float)Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y);
            }
            public static PointF LeftPerp(PointF vector)
            {
                return new PointF(vector.Y, vector.X);
            }
            public static PointF RightPerp(PointF vector)
            {
                return new PointF(-vector.Y, vector.X);
            }
            public static PointF Add(PointF v0, PointF v1)
            {
                return new PointF(v0.X + v1.X, v0.Y + v1.Y);
            }
            public static PointF Subtract(PointF v0, PointF v1)
            {
                return new PointF(v0.X - v1.X, v0.Y - v1.Y);
            }
            public static PointF Multiply(PointF vector, float scaler)
            {
                return new PointF(vector.X * scaler, vector.Y * scaler);
            }
            public static PointF Negate(PointF vector)
            {
                return Multiply(vector, -1.0f);
            }
            public static float GetDistanceBetweenPointLine(PointF point, LineSegment line)
            {
                PointF unitParallel = line.Unit;
                PointF normal = RightPerp(unitParallel);
                float d = Dot(normal, Subtract(point, line.Start)); 
                return Math.Abs(d);
            }
        }
    }
}

相关问题