均匀的圆周运动

时间:2014-01-09 21:27:16

标签: c# animation uniform

我需要实现一个以均匀圆周运动方式移动的球的简单动画。我已经尝试了几个公式,以下版本似乎是迄今为止最好的。但是,仍有2个问题,我真的无法弄清楚是什么问题。

首先,在程序启动后的几秒钟内,球不稳定地移动。我认为theta的值(以弧度表示的角度)不能正确计算,但我不知道为什么。

其次,一段时间后运动变得更加均匀,但随着时间的推移它似乎会减少。

 The value for 'speed' indicates the number of seconds it takes to do a full revolution.

我想要的是一个统一的,正确的圆周运动(根据速度的值)并且在开始时没有急动。

到目前为止我的代码:

public partial class ServerForm : Form
{
    Stopwatch watch;

    //Angular velocity
    float angularVelocity;

    //Angle
    float theta = 20;

    //Speed - time to complete a full revolution, in seconds
    private float speed = 3;

    //Circle center
    private int centerX = 250;
    private int centerY = 200;

    //Circle radius
    private float R = 120;

    //Current position
    private LocationData currentLocation;

    public ServerForm()
    {
        InitializeComponent();
    }

    public void UpdateUI()
    {            
        currentLocation.CoordX = (float)(centerX + Math.Cos(theta) * R);
        currentLocation.CoordY = (float)(centerY + Math.Sin(theta) * R);
        currentLocation.Speed = speed;

        try
        {
            this.Invoke(new Action(() => { this.Invalidate(); }));
        }
        catch (Exception ex)
        {
            watch.Stop();
            Application.Exit();
        }

        theta += (float)((angularVelocity * 1000 / watch.ElapsedMilliseconds));            
        //Console.Out.WriteLine("elapsed miliseconds: " + watch.ElapsedMilliseconds + " theta = " + theta);

    }

    protected override void OnPaint(PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        Brush color = new SolidBrush(Color.BlueViolet);

        g.FillEllipse(color, currentLocation.CoordX, currentLocation.CoordY, 30, 30);

        //Draw circle & center
        g.DrawEllipse(new Pen(color), centerX, centerY, 5, 5);

        float x = centerX - R;
        float y = centerY - R;
        float width = 2 * R;
        float height = 2 * R;
        g.DrawEllipse(new Pen(color), x, y, width, height);

        base.OnPaint(e);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if (!String.IsNullOrEmpty(textSpeed.Text))
        {
            ResetValues(float.Parse(textSpeed.Text));                
        }
    }

    private void ResetValues(float newSpeed)
    {
        speed = newSpeed;
        angularVelocity = (float)(2 * Math.PI / speed);  // radians / sec

        //Start at the top
        currentLocation.CoordX = centerX;
        currentLocation.CoordY = centerY - R;

        theta = 90;

        watch.Restart();            
    }

    private void ServerForm_Load(object sender, EventArgs e)
    {
        watch = new Stopwatch();            

        timer1.Enabled = true;
        timer1.Interval = 100;
        timer1.Tick += timer1_Tick;            

        currentLocation = new LocationData();
        ResetValues(speed);          
    }

    void timer1_Tick(object sender, EventArgs e)
    {
        UpdateUI();
    }

}

LocationData只是一个包含坐标的类。现在的速度。  是时间和单位的单位角速度(以及使用毫秒的变换)是否正确?

我将BackgroundWorker更改为Timer,但是我仍然得到那种不稳定的动作,并且移动速度会在一段时间后变慢。

1 个答案:

答案 0 :(得分:2)

尝试使用System.Windows.Forms.Timer而不是BackgroundWorker。我相信你会得到更一致的结果。这绝对不是使用BackgroundWorker的好例子。

这是一个或多或少的完整解决方案。请注意,我正在根据表格的大小缩放摆动半径和球半径。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        SetStyle(ControlStyles.Opaque, true);
        SetStyle(ControlStyles.ResizeRedraw, true);
        SetStyle(ControlStyles.DoubleBuffer, true);
        SetStyle(ControlStyles.UserPaint, true);
    }

    private void Form1_Load(object sender, System.EventArgs e)
    {
        _stopwatch.Start();
    }

    private void Timer1_Tick(object sender, System.EventArgs e)
    {
        Invalidate();
    }

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

        e.Graphics.Clear(BackColor);

        const float rotationTime = 2000f;

        var elapsedTime = (float) _stopwatch.ElapsedMilliseconds;

        var swingRadius = Math.Min(ClientSize.Width, ClientSize.Height) / 4f;

        var theta = Math.PI * 2f * elapsedTime / rotationTime;

        var ballRadius = Math.Min(ClientSize.Width, ClientSize.Height) / 10f;

        var ballCenterX = (float) ((ClientSize.Width / 2f) + (swingRadius * Math.Cos(theta)));

        var ballCenterY = (float) ((ClientSize.Height / 2f) + (swingRadius * Math.Sin(theta)));

        var ballLeft = ballCenterX - ballRadius;

        var ballTop = ballCenterY - ballRadius;

        var ballWidth = ballRadius * 2f;

        var ballHeight = ballRadius * 2f;

        e.Graphics.FillEllipse(Brushes.Red, ballLeft, ballTop, ballWidth, ballHeight);

        e.Graphics.DrawEllipse(Pens.Black, ballLeft, ballTop, ballWidth, ballHeight);
    }

    private readonly Stopwatch _stopwatch = new Stopwatch();
}