안녕하세요. 언제나 휴일입니다.
이번에는 Windows Forms 앱에서 간단한 그리기 실습을 합시다.
1. 실습할 내용
게임 공간은 100,100에서 너비 400, 높이 400입니다.
한 칸은 40으로 정할게요. 게임 공간의 너비는 10칸, 높이도 10칸입니다.
시작하면 0,0 위치에 공이 위치하고 움직이는 방향은 오른쪽입니다.
방향 키를 누르면 방향을 바꿀 수 있습니다.
타이머에 의해 0.2초 주기로 공은 이동합니다.
2. 컨트롤 배치 및 이벤트 핸들러 등록
Windows Forms 앱(.NET Framework) 프로젝트를 생성 후에 도구 상자에서 Timer를 Form1에 배치하세요.
Timer의 이름은 tm_ball이라 명명할게요.
tm_ball의 Interval(단위는 밀리초)은 200으로 지정하세요.
tm_ball의 Enabled 속성을 True로 지정할게요.
tm_ball의 Tick 이벤트 핸들러를 등록하세요.
Form1의 Paint 이벤트 핸들러를 등록하세요.
Form1의 KeyDown 이벤트 핸들러를 등록하세요.
Form1의 DoubleBuffered 속성을 True로 지정하세요. 화면이 깜빡거리는 것을 줄일 수 있습니다.
3. Form1.cs 구현
3.1 멤버 상수 및 공 위치, 방향 선언
한 칸을 unit 상수로 표현할게요.
시작 좌표 sx, sy도 표현합시다.
보드 공간은 너비를 bwidth, 높이를 bheight로 표현할게요.
공이 있는 위치 x,y를 0으로 선언합니다.
네 개의 방향을 열거형으로 정의한 후 초기 방향을 오른쪽으로 설정하세요.
const int unit = 40; const int sx = 100; const int sy = 100; const int bwidth = 10; const int bheight = 10; int x = 0, y = 0; Direction dir = Direction.DIR_RIGHT; enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN };
3.2 Paint 이벤트 핸들러 구현
게임 공간을 그리는 부분과 공을 그리는 부분을 별도의 메서드로 정의하기로 할게요.
private void Form1_Paint(object sender, PaintEventArgs e) { Graphics grapics = e.Graphics; DrawBoard(grapics); DrawBall(grapics); }
보드 공간은 역사선(BackwardDiagonal) 형태의 HatchBrush로 테두리를 표현할게요.
보드 내부는 칸을 표시하기 위해 수직선과 수평선을 점선으로 표시할게요.
private void DrawBoard(Graphics grapics) { Brush brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.DarkGoldenrod); Pen pen = new Pen(brush, 6); grapics.DrawRectangle(pen, new Rectangle(sx, sy, bwidth*unit, bheight*unit)); Pen pen2 = new Pen(Color.DarkGray); pen2.DashStyle = DashStyle.Dot; for(int i = 0; i < bwidth;i++)//수직선 { int x = sx + i * unit; grapics.DrawLine(pen2, new Point(x, sx), new Point(x, sy+bheight*unit)); } for (int i = 0; i < bheight; i++)//수평선 { int y = sy + i * unit; grapics.DrawLine(pen2, new Point(sx, y), new Point(sx+bwidth*unit, y)); } }
공은 SolidBrush를 이용하여 원으로 표시할게요.
private void DrawBall(Graphics grapics) { Brush brush = new SolidBrush(Color.DarkCyan); grapics.FillEllipse(brush, new Rectangle(x * unit + sx, y * unit + sy, unit, unit)); }
3.2 tm_ball_Tick 이벤트 핸들러 구현
공의 이동 방향에 따라 좌표를 이동시켜줍니다.
폼을 무효화시켜 다시 그려주게 합니다.
private void tm_ball_Tick(object sender, EventArgs e) { switch(dir) { case Direction.DIR_DOWN: y = (y + 1) % bheight; break; case Direction.DIR_UP: y = (y + bheight - 1) % bheight; break; case Direction.DIR_RIGHT: x = (x + 1) % bwidth; break; case Direction.DIR_LEFT: x = (x + bwidth - 1) % bwidth; break; } Invalidate(); }
3.3 form1_KeyDown 이벤트 핸들러 구현
누른 키가 방향 키일 때 공의 움직이는 방향을 변경합니다.
private void Form1_KeyDown(object sender, KeyEventArgs e) { switch(e.KeyCode) { case Keys.Left: dir = Direction.DIR_LEFT; break; case Keys.Right: dir = Direction.DIR_RIGHT; break; case Keys.Up: dir = Direction.DIR_UP; break; case Keys.Down: dir = Direction.DIR_DOWN; break; } }
4. 전체 코드
using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace 그기기_응용에_들어가기_앞서 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Paint(object sender, PaintEventArgs e) { Graphics grapics = e.Graphics; DrawBoard(grapics); DrawBall(grapics); } const int unit = 40; const int sx = 100; const int sy = 100; const int bwidth = 10; const int bheight = 10; int x = 0, y = 0; Direction dir = Direction.DIR_RIGHT; enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; private void DrawBall(Graphics grapics) { Brush brush = new SolidBrush(Color.DarkCyan); grapics.FillEllipse(brush, new Rectangle(x * unit + sx, y * unit + sy, unit, unit)); } private void DrawBoard(Graphics grapics) { Brush brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.DarkGoldenrod); Pen pen = new Pen(brush, 6); grapics.DrawRectangle(pen, new Rectangle(sx, sy, bwidth*unit, bheight*unit)); Pen pen2 = new Pen(Color.DarkGray); pen2.DashStyle = DashStyle.Dot; for(int i = 0; i < bwidth;i++)//수직선 { int x = sx + i * unit; grapics.DrawLine(pen2, new Point(x, sx), new Point(x, sy+bheight*unit)); } for (int i = 0; i < bheight; i++)//수평선 { int y = sy + i * unit; grapics.DrawLine(pen2, new Point(sx, y), new Point(sx+bwidth*unit, y)); } } private void tm_ball_Tick(object sender, EventArgs e) { switch(dir) { case Direction.DIR_DOWN: y = (y + 1) % bheight; break; case Direction.DIR_UP: y = (y + bheight - 1) % bheight; break; case Direction.DIR_RIGHT: x = (x + 1) % bwidth; break; case Direction.DIR_LEFT: x = (x + bwidth - 1) % bwidth; break; } Invalidate(); } private void Form1_KeyDown(object sender, KeyEventArgs e) { switch(e.KeyCode) { case Keys.Left: dir = Direction.DIR_LEFT; break; case Keys.Right: dir = Direction.DIR_RIGHT; break; case Keys.Up: dir = Direction.DIR_UP; break; case Keys.Down: dir = Direction.DIR_DOWN; break; } } private void Form1_Load(object sender, EventArgs e) { Size = new Size(600, 600); } } }