C#中一个点的平滑移动?

时间:2015-09-18 11:39:37

标签: c#

我对使用C#编程相当新,可能需要一些帮助来解决我遇到的这个问题。 我的观点是,由于按住键时发生的延迟,球员位置的运动非常不稳定。像这样:e ... e e e e e;)

如何让它更顺畅地移动(摆脱延迟?)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;

namespace kek
{

    class player
    {
        Image Player;
        Point Playerposition = new Point(600, 200);
        Form1 game;
        public void playerinitialize(Form game)
        {
            this.game = (Form1)game;
            Player = Bitmap.FromFile("player.png");
            game.KeyDown += Game_KeyDown;
            game.KeyPress += Game_KeyPress;

        }

        private void Game_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar < 50)
            {
                Playerposition.Y -= 10;
            }

        }

        private void Game_KeyDown(object sender, KeyEventArgs e)
        {
            //Player move on keypress
            if (e.KeyCode == Keys.W)
            {
                Playerposition.Y -= 10;
            }
            if (e.KeyCode == Keys.S)
            {
                Playerposition.Y += 10;
            }
            if (e.KeyCode == Keys.A)
            {
                Playerposition.X -= 10;
            }
            if (e.KeyCode == Keys.D)
            {
                Playerposition.X += 10;
            }

            //Playerposition.Y -= GamePad.GetState(PLayerIndex.One).ThumbSticks.Left.Y;
            //Playerposition.Y += GamePad.GetState(PLayerIndex.One).ThumbSticks.Right.Y;

            //Playerposition.X -= GamePad.GetState(PLayerIndex.One).ThumbSticks.Left.X;
            //Playerposition.X += GamePad.GetState(PLayerIndex.One).ThumbSticks.Right.X;

        }
        public void Draw(Graphics graphics)
        {
            //on update

            graphics.DrawImage(Player, Playerposition);

        }
    }
}

1 个答案:

答案 0 :(得分:0)

首先,最好不要将WinForms用于游戏开发。然而,做出你想要的东西并非不可能。

您的要求的关键是不更新关键事件中的位置。基本游戏循环持续执行以下操作。首先它会更新职位&#39;其次,它在屏幕上绘制了新的位置。

首先我们要更新位置,但要做到这一点,我们必须知道是否按下了某个键。一种方法是捕获按键和按键事件,并在键盘图中标记这些事件。在更新中,我们现在只检查键是否关闭,并相应地更新“播放器”的位置。

前段时间我刚刚创建了一个有趣的小型演示项目。我会和你分享。希望它有所帮助。

更新:此外,在更新动作时,请考虑两次绘制之间的时间每次都可能不同。因此,您应该在更新中确定应添加的移动量。请参阅Snake.UpdatePositions方法?我声明玩家应该在1秒后移动100个单位(像素)。所以我根据现在和最后一帧之间的时间计算出的位移量。

创建一个名为SmoothAnimationDemo的Winform项目,并使用以下代码替换Form1.cs的代码。

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

namespace SmoothAnimationDemo
{
    public partial class Form1 : Form
    {
        // check every x frames, calculate fps, adjust x
        private readonly FrameInfo _frameInfo = new FrameInfo(DateTime.Now);
        private readonly List<Animation> _animations = new List<Animation>();

        public Form1()
        {
            InitializeComponent();

            this.DoubleBuffered = true;

            FrameInfoVisible = false; // Set to true if needed. 
            RegisterAnimation(new Snake(this));
        }

        public bool FrameInfoVisible { get; set; }

        public void RegisterAnimation(Animation animation)
        {
            _animations.Add(animation);
        }

        private int _skipframes = 1;

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            // Skip updating if needed.
            if (_skipframes > 0)
            {
                _skipframes--;
            }
            else
            {
                _frameInfo.Update();
                foreach (var animation in _animations)
                    animation.UpdatePositions(_frameInfo);

                _skipframes = 0; // adjust to skip frames or leave at 0 to not skip frames. 
            }

            // init drawing
            var gfx = e.Graphics;
            gfx.Clear(Color.Black);

            // draw each frame. 
            foreach (var animation in _animations)
            {
                animation.Draw(gfx);
                gfx.ResetTransform(); // in case the animation used transform methods.
            }

            // draw timer info on top
            if (FrameInfoVisible)
                _frameInfo.Draw(gfx);

            // wait till a certain time has passed before drawing again. 
            //Thread.Sleep(10);
            Invalidate(); // ensure new paint soon
        }
    }

    /// <summary>
    /// abstract base class for animations.
    /// An animation contains two methods. First one used for updating animation data, eg positions.
    /// Second one used for drawing the data onto a graphics object.
    /// </summary>
    public abstract class Animation : IDrawable, IAnimatable
    {
        public abstract void Draw(Graphics gfx);

        public abstract void UpdatePositions(FrameInfo frameInfo);
    }

    /// <summary>
    /// Contains info about our frames
    /// </summary>
    public class FrameInfo
    {
        public DateTime FirstFrameTime { get; set; }
        public DateTime PrevFrameTime { get; set; }
        public DateTime FrameTime { get; set; }
        public int FrameCount { get; set; }
        public double FramesPerSecond { get; set; }

        public FrameInfo(DateTime now)
        {
            FirstFrameTime = now;
        }

        public void Update()
        {
            PrevFrameTime = FrameTime;
            FrameTime = DateTime.Now;
            FrameCount++;
            FramesPerSecond = 1000.0 / (FrameTime - PrevFrameTime).TotalMilliseconds;
        }

        public void Draw(Graphics gfx)
        {
            gfx.DrawString("Frame time", SystemFonts.DefaultFont, Brushes.Black, 0, 0);
            gfx.DrawString(String.Format(": {0:hh:mm:ss.zzzz}", FrameTime - FirstFrameTime), SystemFonts.DefaultFont, Brushes.Black, 70, 0);
            gfx.DrawString("Frame", SystemFonts.DefaultFont, Brushes.Black, 0, 16);
            gfx.DrawString(": " + FrameCount, SystemFonts.DefaultFont, Brushes.Black, 70, 16);
            gfx.DrawString("FPS", SystemFonts.DefaultFont, Brushes.Black, 0, 32);
            gfx.DrawString(": " + FramesPerSecond, SystemFonts.DefaultFont, Brushes.Black, 70, 32);
        }
    }

    internal interface IAnimatable
    {
        void UpdatePositions(FrameInfo frameInfo);
    }

    internal interface IDrawable
    {
        void Draw(Graphics gfx);
    }

    /// <summary>
    /// Animation module
    /// </summary>
    internal class Snake : Animation
    {
        public Snake(Control form)
        {
            form.KeyDown += form_KeyDown;
            form.KeyUp += form_KeyUp;
        }

        readonly Dictionary<Keys, bool> _keyMap = new Dictionary<Keys, bool>
        {
            { Keys.Up, false },
            { Keys.Down, false },
            { Keys.Left, false },
            { Keys.Right, false }
        };

        void form_KeyUp(object sender, KeyEventArgs e)
        {
            if (_keyMap.ContainsKey(e.KeyCode))
                _keyMap[e.KeyCode] = false;
        }

        void form_KeyDown(object sender, KeyEventArgs e)
        {
            if (_keyMap.ContainsKey(e.KeyCode))
                _keyMap[e.KeyCode] = true;
        }

        private PointF _headPos = new PointF(100.0f, 100.0f);

        public override void UpdatePositions(FrameInfo info)
        {
            // Ensure that the motion is moving at a 
            var speed = 100; // 100 units within 1 second
            var perc = (double)(info.FrameTime - info.PrevFrameTime).TotalMilliseconds / 1000;
            var displaceAmount = (float)(speed * perc);

            if (_keyMap[Keys.Up])
                _headPos.Y -= displaceAmount;
            if (_keyMap[Keys.Down])
                _headPos.Y += displaceAmount;
            if (_keyMap[Keys.Right])
                _headPos.X += displaceAmount;
            if (_keyMap[Keys.Left])
                _headPos.X -= displaceAmount;
        }

        public override void Draw(Graphics gfx)
        {
            gfx.SmoothingMode = SmoothingMode.AntiAlias;
            gfx.DrawString(String.Format("[{0},{1}]", _headPos.X, _headPos.Y), SystemFonts.DefaultFont, Brushes.Black, 0.0f, 48.0f);
            gfx.DrawEllipse(Pens.White, _headPos.X, _headPos.Y, 10.0f, 10.0f);
        }
    }

}