코드 조각 : 60FPS - escaco95/Charcoal GitHub Wiki

using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public partial class UserControl1 : UserControl
    {
        readonly Rectangle DocRect = new Rectangle(-25000, -25000, 50000, 50000);

        volatile bool IsRunning = true;
        long CurrentFrame = 0;
        double Fps = 0.0;
        public UserControl1()
        {
            ResizeRedraw = false;
            InitializeComponent();
            if (DesignMode)
                IsRunning = false;
        }

        void Tick()
        {

        }
        void Render()
        {
            var form = this.FindForm();
            if (form != null)
                form.Text = $"{Fps}";
            Invalidate();
        }

        protected override void OnPaintBackground(PaintEventArgs e) {/**/}

        BufferedGraphics Buffer = null;
        Size BufferSize = new Size(0, 0);

        protected override void OnPaint(PaintEventArgs e)
        {
            if (Buffer == null || BufferSize != ClientSize)
            {
                Buffer = BufferedGraphicsManager.Current.Allocate(e.Graphics, ClientRectangle);
                BufferSize = ClientSize;
            }
            Graphics g = Buffer.Graphics;

            g.Clear(Color.FromArgb(24, 24, 24));

            g.ResetTransform();
            var mxt = 1.0F / Math.Max(1.0F, CurrentFrame/30.0F);
            g.TranslateTransform(ClientSize.Width / 2, ClientSize.Height / 2);
            g.ScaleTransform(mxt, mxt);
            g.TranslateTransform(CurrentFrame, 0.0F);

            g.FillRectangle(new SolidBrush(Color.FromArgb(45, 45, 48)), DocRect);

            for (var v = DocRect.Top; v <= DocRect.Bottom; v += 25)
            {
                g.DrawLine(new Pen(Color.FromArgb(32, 32, 32)), v, DocRect.Top, v, DocRect.Bottom);
                g.DrawLine(new Pen(Color.FromArgb(32, 32, 32)), DocRect.Left, v, DocRect.Right, v);
            }
            for (var v = DocRect.Top; v <= DocRect.Bottom; v += 100)
            {
                g.DrawLine(new Pen(Color.FromArgb(63, 63, 70)), v, DocRect.Top, v, DocRect.Bottom);
                g.DrawLine(new Pen(Color.FromArgb(63, 63, 70)), DocRect.Left, v, DocRect.Right, v);
            }
            for (var v = DocRect.Top; v <= DocRect.Bottom; v += 400)
            {
                g.DrawLine(new Pen(Color.FromArgb(171, 171, 171)), v, DocRect.Top, v, DocRect.Bottom);
                g.DrawLine(new Pen(Color.FromArgb(171, 171, 171)), DocRect.Left, v, DocRect.Right, v);
            }

            Buffer.Render(e.Graphics);
        }

        void GameLoop()
        {
            Stopwatch gameLoopTicks = Stopwatch.StartNew();
            long lastTime = gameLoopTicks.ElapsedTicks;
            double amountOfTicks = 30.0;
            double ns = Stopwatch.Frequency/amountOfTicks;
            double delta = 0;
            long timer = gameLoopTicks.ElapsedMilliseconds;
            int frames = 0;
            while (IsRunning)
            {
                long now = gameLoopTicks.ElapsedTicks;
                delta += (now - lastTime) / ns;
                lastTime = now;
                while (delta >= 1)
                {
                    this.Invoke(new Action(() => Tick()));
                    CurrentFrame++;
                    frames++;
                    delta--;
                }

                this.Invoke(new Action(() => Render()));

                if (gameLoopTicks.ElapsedMilliseconds - timer > 1000)
                {
                    timer += 1000;
                    Fps = frames;
                    frames = 0;
                }
            }
        }

        private void UserControl1_Load(object sender, EventArgs e)
        {
            Task.Run(new Action(() => GameLoop()));
        }

    }
}