기억 저장소
콘솔 화면에 움직이는 애니메이션 그리기(2차원)(Ascii Art) 본문
일반적으로 코딩 입문 시기 때 많이 하는 것이 '별찍기' 이다.
별 찍기란 간단히 말해서
출력문과 알고리즘으로 콘솔 화면에 일종의 그림을 그리는 것이다.
뭐 대충 이런 식으로..
비슷하게 화면을 그리는 코드를 만들어 보면,
using System;
class Viewer
{
const int VIEW_SIZE = 40;
public static void Main(String[] args)
{
new Viewer();
}
public Viewer()
{
int center = VIEW_SIZE / 2;
//화면 출력
Console.SetCursorPosition(0,0); // 출력 시작 위치를 0,0으로.
for(int i = 0; i < VIEW_SIZE; i++)
{
for(int j = 0; j < VIEW_SIZE; j++)
{
//범위 10 ~ 30 이내, 즉 길이 20인 정사각형 그리기
if(Math.Abs(i - center) < 10 && Math.Abs(j - center) < 10)
Console.Write("*");
else
Console.Write(" ");
}
Console.WriteLine();
}
Console.ReadLine();
}
}
(ㅋㅋㅋㅋ 조금 다르긴 하지만 설명하기엔 충분하다.)
이때 콘솔 화면에 계산하면서 화면에 글자 하나씩 출력하기보다는
2차원 배열에 데이터를 저장해 뒀다가 한 번에 출력하는 것이 좋다.
이유는 코드 관리가 깔끔해지기도 하고,
콘솔 창에 출력 시 속도도 훨씬 빠르다.(중요)
해당 내용을 반영해서 고친 Viewer 생성자 코드. 출력 결과물은 동일하다.
public Viewer()
{
display = new char[VIEW_SIZE,VIEW_SIZE];
//디스플레이 초기화
for(int i = 0; i < VIEW_SIZE; i++)
for(int j = 0; j < VIEW_SIZE; j++)
display[i,j] = ' ';
int center = VIEW_SIZE / 2;
int square_start = center - 10;
int square_end = center + 10;
//정사각형 그리기
for(int i = square_start; i < square_end; i++)
for(int j = square_start; j < square_end; j++)
{
display[i,j] = '*';
}
//화면 출력
StringBuilder sb = new StringBuilder();
for(int i = 0; i < VIEW_SIZE; i++)
{
for(int j = 0; j < VIEW_SIZE; j++)
{
sb.Append(display[i,j]);
}
sb.Append('\n');
}
Console.SetCursorPosition(0,0);
Console.WriteLine(sb.ToString());
Console.ReadLine();
}
그렇다면 포스트 제목처럼 움직이는 애니메이션이 되려면 어떻게 해야 하는가??
바로 콘솔 화면을 계속 다른 모양으로 업데이트해 주면 된다.
위에서 말했듯이 2차원 배열이 있다고 가정하면,
화면을 업데이트할 때마다
<2차원 배열 초기화 → 배열에 별 찍기. → 화면에 출력.>
이 과정을 계속 반복하면 된다.
배열에 별을 찍을 때 이전 화면과는 다른 모양을 보여 줘야 움직이는 애니메이션이 된다.
이전 포스트에서 소개했던 회전 변환을 활용하면 회전하는 애니메이션을 만들 수 있다.
https://nature77s.tistory.com/2
주의해야 할 점은, 화면에 출력되는 배열 인덱스는 정수 값이고, 회전 변환 후의 좌표는 실수 값이다.
이 좌표를 화면에 뿌리기 위해서는 내림 연산하여 정수로 만들어야 하므로 향상 오차가 생기게 되는데,
기존 회전이 된 배열에 계속 조금씩 회전을 시킬 경우 그 오차가 점점 쌓여서 모양이 뭉개져 버리게 된다.
그래서 원본 배열과 회전된 배열을 따로 두고,
매 프레임마다 degree 변수를 1도씩 증가시켜서
원본 배열을 degree만큼 회전시킨 결과를 회전된 배열에 저장 후
회전된 배열을 출력하는 식으로 구현해 보았다.
(아래 Vector2 클래스는 회전 변환에 쓰임)
using System;
using System.Text;
using System.Threading;
class Viewer
{
const int VIEW_SIZE = 40;
char[,] display;
char[,] display_rotated;
public static void Main(String[] args)
{
Console.CursorVisible = false;
new Viewer();
}
public Viewer()
{
display = new char[VIEW_SIZE,VIEW_SIZE];
display_rotated = new char[VIEW_SIZE,VIEW_SIZE];
//원본 배열 초기화
for(int i = 0; i < VIEW_SIZE; i++)
for(int j = 0; j < VIEW_SIZE; j++)
display[i,j] = ' ';
int center = VIEW_SIZE / 2;
int square_start = center - 10;
int square_end = center + 10;
//정사각형 그리기
for(int i = square_start; i < square_end; i++)
for(int j = square_start; j < square_end; j++)
display[i,j] = '*';
//회전에 필요한 변수들
Vector2 pivot = new Vector2(center,center);
Vector2 p = new Vector2();
int degree = 0;
while(true)
{
ResetDisplay();//회전 배열 초기화
degree++; //프레임마다 1도씩 증가
//회전 배열에 별 찍기
for(int i = 0; i < VIEW_SIZE; i++)
for(int j = 0; j < VIEW_SIZE; j++)
{
p.x = i; p.y = j;
Vector2 r = Rotate2D(p,pivot,degree);//회전
if(OutofRange(r)) continue;//회전된 좌표 배열 범위 검사
display_rotated[(int)r.x,(int)r.y] = display[i,j];//별 찍기
}
Print();//화면 출력
Thread.Sleep(10);//프레임 딜레이.
}
}
bool OutofRange(Vector2 v)
{
return v.x < 0 || v.y < 0 || v.x >= VIEW_SIZE || v.y >= VIEW_SIZE;
}
void ResetDisplay()
{
for(int i = 0; i < VIEW_SIZE; i++)
for(int j = 0; j < VIEW_SIZE; j++)
display_rotated[i,j] = ' ';
}
StringBuilder sb = new StringBuilder();
void Print()
{
sb.Clear();
for(int i = 0; i < VIEW_SIZE; i++)
{
for(int j = 0; j < VIEW_SIZE; j++)
{
sb.Append(display_rotated[i,j]);
}
sb.Append('\n');
}
Console.SetCursorPosition(0,0);
Console.WriteLine(sb.ToString());
}
//회전행렬 함수
Vector2 Rotate2D(Vector2 p, Vector2 pivot, float degree)
{
float radian = (float)(degree * Math.PI / 180); //삼각함수 쓸 때 라디안 값으로 변환.
p -= pivot; // 중심을 원점으로 이동
float x = (float)(p.x * Math.Cos(radian) - p.y * Math.Sin(radian));
float y = (float)(p.x * Math.Sin(radian) + p.y * Math.Cos(radian));
return new Vector2(x, y) + pivot; // 중심을 pivot으로 복구
}
}
//회전 행렬에 필요한 Vector 클래스
class Vector2
{
public float x, y;
public Vector2()
{
this.x = 0;
this.y = 0;
}
public Vector2(float x, float y)
{
this.x = x;
this.y = y;
}
public static Vector2 operator +(Vector2 a, Vector2 b)
{
float x = a.x + b.x;
float y = a.y + b.y;
return new Vector2(x,y);
}
public static Vector2 operator -(Vector2 a, Vector2 b)
{
float x = a.x - b.x;
float y = a.y - b.y;
return new Vector2(x,y);
}
}
코드 컴파일 후 실행하면 다음과 같이 보인다.
컴파일 방법: https://nature77s.tistory.com/3
'Voxel Engine 제작루틴 > c#프로그래밍' 카테고리의 다른 글
콘솔 화면에 움직이는 애니메이션 그리기(3차원)(Ascii Art) (0) | 2023.02.05 |
---|---|
아무것도 안깔린 윈도우에서 메모장코딩, 컴파일 하기 (1) | 2021.09.07 |