[C++] 1.2 기본적인 그리기 - 이동, 회전, 애니메이션

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 게임 만들기 연습 문제 패키지 강의를 듣고 정리한 필기입니다. 😀
🌜 공부에 사용된 홍정모 교수님의 코드들 보러가기
🌜 [홍정모의 게임 만들기 연습 문제 패키지] 강의 들으러 가기!


Chapter 1. 기본 기능 구현

1.2 기본적인 그리기 - 이동, 회전, 애니메이션

이번 실습 에서는 Game2D 클래스에서 draw_grid 값을 true 로 바꾸는 것을 권장!


원점에 검은 점 그리기

DrawFunction.h

using vec2 = Vector2<float>;

vector2.h

template<typename T>
	class Vector2
	{
	public:
			union {
				struct { T x, y; };
				struct { T v0, v1; };
				T data[2];
			};

main.cpp

namespace jm
{
	class RotatingStarExample : public Game2D
	{
	public:
		void update() override
		{
			const vec2 point = vec2(0.0f, 0.0f); // 점의 위치 지정 (원점)
			drawPoint(Colors::black, point, 5.0f); // 점 그리기 5.0f 크기로
		}
	};
}

int main(void)
{

	jm::RotatingStarExample().init("This is my digital canvas!", 1280, 960, false).run();

	return 0;
}


선 그리기

image

	class RotatingStarExample : public Game2D
	{
	public:
		void update() override
		{
			const vec2 p0(0.0f, 0.0f); // 시작점 (vector2 생성자로 구현)
			const vec2 p1(0.5f, 0.5f);//  끝점
			drawLine(Colors::red, p0, Colors::blue, p1); 
			// 첫번재점 레드, 두번째점 블루 -> 빨강에서 시작해서 끝으로 갈수록 블루인 선이 됨
		}
	};


삼각형 그리기

삼각형은 선이 3개 !

image

	class RotatingStarExample : public Game2D
	{
	public:
		void update() override
		{
			const vec2 p0(0.0f, 0.0f);
			const vec2 p1(0.5f, 0.5f);
			const vec2 p2(0.5f, 0.0f);
			drawLine(Colors::red, p0, Colors::blue, p1);
			drawLine(Colors::red, p1, Colors::blue, p2);
			drawLine(Colors::red, p2, Colors::blue, p0);
		}
	};


평행이동 방법 1. 각 좌표 파라미터에 값을 더해주기

각 좌표 파라미터에 이동시킬 만큼 실수 값을 더해주면 된다.

image

	class RotatingStarExample : public Game2D
	{
	public:
		void update() override
		{
			const float dx = 0.1f;  // dy도 추가해서 y좌표에 더해주면 y 방향 평행이동도 가능

			const vec2 p0(0.0f + dx, 0.0f);
			const vec2 p1(0.5f + dx, 0.5f);
			const vec2 p2(0.5f + dx, 0.0f);
			drawLine(Colors::red, p0, Colors::blue, p1);
			drawLine(Colors::red, p1, Colors::blue, p2);
			drawLine(Colors::red, p2, Colors::blue, p0);
		}
	};


평행이동 방법 2. 좌표 자체를 넘겨주기 + translate함수 사용

  • 각 파라미터에 적용시키는건 번거로우니까 좌표 자체인 vec2객체를 넘겨주기
  • translate 함수 : 누적 해서 좌표 현재위치를 변경시킨다.

image

	class RotatingStarExample : public Game2D
	{
	public:
		void update() override
		{
			translate(0.1f, 0.0f); 
            // 점의 시작 위치가 (0.1f, 0.0f) 가 되어 이 아래에 작성한 모든 도형들이 (0.1f, 0.0f) 위치에서 그려지게 된다.

			const vec2 p0(0.0f , 0.0f);
			const vec2 p1(0.5f , 0.5f);
			const vec2 p2(0.5f , 0.0f);
			drawLine(Colors::red, p0, Colors::blue, p1); // vec2 p1을 넘겨줌
			drawLine(Colors::red, p1, Colors::blue, p2); // vec2 p2을 넘겨줌
			drawLine(Colors::red, p2, Colors::blue, p0); // vec2 p0을 넘겨줌

			// 이 아래 3개의 선은 이제 (0.2f, 0.0f) 위치에서 그려진다. (translate은 누적되니까 한번 더 translate을 해주니 (0.2f, 0.0f)가 됨)
			translate(0.1f, 0.0f); 
			drawLine(Colors::red, p0, Colors::blue, p1);
			drawLine(Colors::red, p1, Colors::blue, p2);
			drawLine(Colors::red, p2, Colors::blue, p0);
		}
	};

평행이동 방법 3. 좌표 자체를 넘겨주기 + beginTransformation, endTransformation 사용

  • 어느 일정 부분안에서만 누적되게 하고 싶을 때.
  • beginTransformation()endTransformation() 의 사이에서 이루어진 변환들이 이 범위를 벗어나면 초기화된다.

image

	class RotatingStarExample : public Game2D
	{
	public:
		void update() override
		{
			beginTransformation(); // 여기서부터

			translate(0.1f, 0.0f);  // (0.1f,0.0f)에 그려지는 아래 3개의 실선

			const vec2 p0(0.0f , 0.0f);
			const vec2 p1(0.5f , 0.5f);
			const vec2 p2(0.5f , 0.0f);
			drawLine(Colors::red, p0, Colors::blue, p1);
			drawLine(Colors::red, p1, Colors::blue, p2);
			drawLine(Colors::red, p2, Colors::blue, p0);

			translate(0.1f, 0.0f);   // (0.2f,0.0f) 에 그려지는 아래 3개의 실선
			drawLine(Colors::red, p0, Colors::blue, p1);
			drawLine(Colors::red, p1, Colors::blue, p2);
			drawLine(Colors::red, p2, Colors::blue, p0);

			endTransformation();  // 여기까지만 누적이 적용된다 !  이제 여기를 벗어나면 다시 원점인 (0.0f,0.0f) 으로 돌아감

			translate(-0.1f, 0.0f);  // (-0.1f, 0.0f) 에 그려지는 아래 3개의 실선
			drawLine(Colors::red, p0, Colors::blue, p1);
			drawLine(Colors::red, p1, Colors::blue, p2);
			drawLine(Colors::red, p2, Colors::blue, p0);
		}
	};


박스 그리기

image

	class RotatingStarExample : public Game2D
	{
	public:
		void update() override
		{
			drawWiredBox(Colors::gold, 0.5f, 0.25f);
		}
	};


회전 : 원점 회전

  • rotate함수는 원점에 대해서 회전한다.
  • 회전 하는 중심이 원점 기준임.

image

	class RotatingStarExample : public Game2D
	{
	public:
		void update() override
		{
			drawWiredBox(Colors::gold, 0.5f, 0.25f);
			rotate(30.0f); // (0.0f, 0.0f) 현재 위치인 원점에 대해서 30도 회전
			drawWiredBox(Colors::blue, 0.5f, 0.25f);
		}
	};


회전 : 그래픽스는 내부적으로 역순으로 실행된다. ⭐

image

	class RotatingStarExample : public Game2D
	{
	public:
		void update() override
		{
			drawWiredBox(Colors::gold, 0.5f, 0.25f);
			
			// 파란색 상자는 그냥 rotate만 적용된 것
			beginTransformation();
			rotate(30.0f);
			drawWiredBox(Colors::blue, 0.5f, 0.25f);
			endTransformation();

			// 빨간 상자는 translate 후에 rotate까지 함께 적용된 것
			beginTransformation();
			translate(0.25f, 0.0f); // translate이 코드 상 rotate보다 먼저 등장
			rotate(30.0f); // rotate이 translate 코드에서 아랫줄에 위치.
			drawWiredBox(Colors::red, 0.5f, 0.25f);
			endTransformation();
		}
	};
  • 사진에서 빨간 상자 주목 ! 코드 상으로는 translate을 먼저 한 후에 rotate을 하므로 이동 후 원점에 대해서 회전하는 그림이 나와햐 하는데 실제로 실행해보면 rotate을 먼저 하고 난 후에 translate 평행이동 한 그림이 나온다.
    • 컴퓨터 크래픽스 API는 내부적으로 코드의 역순으로 실행되기 때문. 코드상 평행이동 → 회전이여도 내부적으론 역순으로 회전 → 평행이동으로 실행된다.
  • draw, 그림을 그리는건 코드 순서 그대로 그려진다!
    • 이동, 회전, scale 같은 것들만 코드의 역순으로 진행되는 것.

image

drawWiredBox(Colors::gold, 0.5f, 0.25f);
			
// 블루 박스는 코드 상으론 **평행이동->회전** 순     : 그러나 실제론 **회전 -> 평행이동** 순으로 실행됨*
beginTransformation();
translate(0.25f, 0.0f);
rotate(30.0f);
drawWiredBox(Colors::blue, 0.5f, 0.25f);
endTransformation();

// 블루 박스는 코드 상으론 **회전 -> 평행이동** 순     : 그러나 실제론 **평행이동->회전** 순으로 실행됨*
beginTransformation();
rotate(30.0f);
translate(0.25f, 0.0f);
drawWiredBox(Colors::red, 0.5f, 0.25f);
endTransformation();


원점이 아닌 점에 대해서 회전하고 싶을 때

1. 원점이 아닌 점이 원점에 위치할 수 있도록 물체를 통째로 들어서 옮기기
2. 원점에 위치한 상태에서 원점에 대해서 회전 시키고
3. 다시 원래 자리로 되돌려 놓기

1 . 원점이 아닌 점이 원점에 위치할 수 있도록 물체를 통째로 들어서 옮기기

image

setLineWidth(3.0f);
const vec2 center_of_rotation(-0.25f, 0.0f);  // 이 점에 대해서 회전시키고 싶어.

translate(-center_of_rotation);  // ⭐ 실행순서 3. (+0.25f, 0.0f)만큼 이동시킨다. 원점으로 이동시키기 위해.
drawWiredBox(Colors::gold, 0.5f, 0.25f); // 실행순서 1.
drawPoint(Colors::black, center_of_rotation, 10.0f); // 실행순서 2. 회전 중점으로 하고 싶은 점을 시각적으로 보기 위해 그 점을 그림.


2 . 원점에 위치한 상태에서 원점에 대해서 회전 시키고

image

setLineWidth(3.0f);

const vec2 center_of_rotation(-0.25f, 0.0f);

rotate(45.0f);   // ⭐ 실행순서 4.  
translate(-center_of_rotation);  // 실행순서 3.
drawWiredBox(Colors::red, 0.5f, 0.25f); // 실행순서 1. 
drawPoint(Colors::black, center_of_rotation, 10.0f); // 실행순서 2.


3 . 다시 원래 자리로 돌려놓기

  • 이제 (0.25f, 0.0f) 좌표에 대해서 45도 회전하는 그림이 나오게 됐다.
  • draw는 코드 순서대로 실행되나 코드상으로는 rotate가 translate보다 먼저 나왔음에도 불구하고 translate이 먼저 실행되고 rotate이 그 다음에 실행된다.

image

setLineWidth(3.0f);

const vec2 center_of_rotation(-0.25f, 0.0f);

translate(center_of_rotation); // ⭐ 실행순서 5. 다시 원래 자리로 돌려 놓기. 
rotate(45.0f);   // 실행순서 4.  
translate(-center_of_rotation);  // 실행순서 3. 
drawWiredBox(Colors::red, 0.5f, 0.25f); // 실행순서 1. 
drawPoint(Colors::black, center_of_rotation, 10.0f); // 실행순서 2.


크기 바꾸기 scale

  • scale(a, b) : x 방향으로 a 배, y 방향으로 b 배로 크기를 바꿈

image

void update() override
		{
			setLineWidth(3.0f);

			scale(2.0f, 0.25f);  // x방향으로 2배, y방향으로 1/4배 
			drawWiredBox(Colors::blue, 1.0f, 1.0f);

		}


rotation & scale & translate 함께 쓰기

namespace jm
{
	class RotatingStarExample : public Game2D
	{
	public:
		float time = 0;
		void update() override
		{
			setLineWidth(3.0f);

			rotate(time*90.0f);   // scale -> rotate 으로 역순으로 실행되게 된다.
			scale(2.0f, 0.25f);
			drawWiredBox(Colors::blue, 1.0f, 1.0f);

			time += this->getTimeStep();
		}
	};
}
void update() override
		{
			setLineWidth(3.0f);

			scale(2.0f, 0.25f);    //  rotate 먼저 하고 그 다음에 scale 하기 때문에 결과가 다르다.
			rotate(time*90.0f);
			drawWiredBox(Colors::blue, 1.0f, 1.0f);

			time += this->getTimeStep();
		}

rotate 두번 하면 마치 자전과 동시에 공전 하는 느낌으로 회전한다.



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

C++ games 카테고리 내 다른 글 보러가기

댓글 남기기