본문 바로가기
Study/C#

C# Windows Forms 강의 44편: Timer와 GDI+를 활용한 간단한 애니메이션 효과

by wawManager 2025. 3. 19.

1. 강의 개요

이번 강의에서는 Timer와 **GDI+**를 사용하여 Windows Forms 애플리케이션에 간단한 애니메이션 효과를 추가하는 방법을 학습합니다.
GDI+를 사용해 화면에 도형을 그리며, Timer를 이용해 도형의 움직임을 구현합니다.


2. 학습 목표

  1. GDI+를 사용해 사용자 정의 그래픽(도형)을 그리는 방법.
  2. Timer를 활용해 반복적으로 그래픽을 업데이트하여 애니메이션 효과 구현.
  3. 화면에 움직이는 도형(원)을 제작.

3. GDI+와 Timer 개요

GDI+란?

**GDI+ (Graphics Device Interface Plus)**는 Windows Forms에서 그래픽을 그리는 데 사용되는 API입니다.

  • Graphics 클래스: 화면에 도형, 텍스트, 이미지 등을 그리는 데 사용.
  • Pen 및 Brush 클래스: 선과 채우기를 설정.

Timer란?

Timer는 일정한 간격으로 이벤트를 발생시키는 Windows Forms 컨트롤입니다.

  • 애니메이션, 실시간 데이터 업데이트 등 반복적인 작업에 사용.

4. 실습: Timer와 GDI+로 움직이는 원 구현

요구사항

  1. Timer를 사용해 화면에 원이 움직이는 애니메이션을 구현.
  2. 폼 크기를 벗어나지 않도록 원의 경계 처리.
  3. 버튼을 통해 애니메이션 시작/정지.

폼 구성

컨트롤 타입 이름 텍스트 위치 크기

Button btnStart "시작" 하단 왼쪽 (100 x 30)
Button btnStop "정지" 하단 오른쪽 (100 x 30)

코드 작성

Form1.cs

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private Timer timer;
        private int ballX = 50; // 원의 초기 X 위치
        private int ballY = 50; // 원의 초기 Y 위치
        private int ballSpeedX = 5; // 원의 X 축 속도
        private int ballSpeedY = 5; // 원의 Y 축 속도
        private const int ballSize = 30; // 원의 크기

        public Form1()
        {
            InitializeComponent();
            InitializeAnimation();
        }

        private void InitializeAnimation()
        {
            // Timer 설정
            timer = new Timer
            {
                Interval = 30 // 30ms마다 Tick 이벤트 발생
            };
            timer.Tick += Timer_Tick;

            // 버튼 이벤트 연결
            btnStart.Click += BtnStart_Click;
            btnStop.Click += BtnStop_Click;

            // DoubleBuffered 설정: 화면 깜박임 방지
            this.DoubleBuffered = true;
        }

        // 애니메이션 시작
        private void BtnStart_Click(object sender, EventArgs e)
        {
            timer.Start();
        }

        // 애니메이션 정지
        private void BtnStop_Click(object sender, EventArgs e)
        {
            timer.Stop();
        }

        // Timer Tick 이벤트: 원 위치 업데이트
        private void Timer_Tick(object sender, EventArgs e)
        {
            // 원의 위치 업데이트
            ballX += ballSpeedX;
            ballY += ballSpeedY;

            // 경계 체크 및 방향 반전
            if (ballX <= 0 || ballX + ballSize >= this.ClientSize.Width)
            {
                ballSpeedX = -ballSpeedX; // X 방향 반전
            }
            if (ballY <= 0 || ballY + ballSize >= this.ClientSize.Height)
            {
                ballSpeedY = -ballSpeedY; // Y 방향 반전
            }

            // 화면 다시 그리기
            this.Invalidate();
        }

        // 화면 그리기
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics g = e.Graphics;

            // 원 그리기
            using (Brush brush = new SolidBrush(Color.Blue))
            {
                g.FillEllipse(brush, ballX, ballY, ballSize, ballSize);
            }
        }
    }
}

Form1.Designer.cs

namespace WindowsFormsApp1
{
    partial class Form1
    {
        private System.ComponentModel.IContainer components = null;
        private Button btnStart;
        private Button btnStop;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.btnStart = new Button();
            this.btnStop = new Button();
            this.SuspendLayout();

            // btnStart
            this.btnStart.Location = new System.Drawing.Point(20, 240);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(100, 30);
            this.btnStart.Text = "시작";
            this.btnStart.UseVisualStyleBackColor = true;

            // btnStop
            this.btnStop.Location = new System.Drawing.Point(140, 240);
            this.btnStop.Name = "btnStop";
            this.btnStop.Size = new System.Drawing.Size(100, 30);
            this.btnStop.Text = "정지";
            this.btnStop.UseVisualStyleBackColor = true;

            // Form1
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(400, 300);
            this.Controls.Add(this.btnStart);
            this.Controls.Add(this.btnStop);
            this.Name = "Form1";
            this.Text = "애니메이션 예제";
            this.ResumeLayout(false);
        }
    }
}

5. 실행 결과

  1. 초기 상태
    • 폼에 "시작"과 "정지" 버튼이 표시됩니다.
  2. 애니메이션 시작
    • "시작" 버튼 클릭 → 파란색 원이 일정한 속도로 움직이기 시작.
    • 폼 경계에 도달하면 원이 반대 방향으로 튕겨 나옵니다.
  3. 애니메이션 정지
    • "정지" 버튼 클릭 → 원의 움직임이 멈춥니다.
  4. 화면 깜박임 방지
    • DoubleBuffered 속성을 설정하여 화면 깜박임 없이 부드러운 애니메이션이 실행됩니다.

6. 주요 개념 요약

  1. Timer
    • Interval 속성을 설정해 일정한 시간 간격으로 Tick 이벤트 발생.
    • 애니메이션 프레임 업데이트에 활용.
  2. GDI+와 OnPaint
    • Graphics.FillEllipse를 사용해 화면에 원을 그림.
    • Invalidate()를 호출하여 화면 다시 그리기.
  3. 화면 깜박임 방지
    • DoubleBuffered = true로 설정해 깜박임 현상 최소화.
  4. 애니메이션 경계 처리
    • ClientSize 속성을 사용해 폼 경계와 충돌 감지.