정보공간_1

[4기 광주 박주연] 2D 그래픽 애니메이션의 기본 원리 본문

IT 놀이터/Elite Member Tech & Talk

[4기 광주 박주연] 2D 그래픽 애니메이션의 기본 원리

알 수 없는 사용자 2013. 11. 5. 10:37

2D 그래픽 애니메이션의 기본 원리


이번 글에서는 프로그래밍을 할 때 종 종 접하게 되는 2D 그래픽 처리에서의 애니메이션 처리에 대한 기본 원리에 대해서 이야기 해보려고 합니다. 그 이유는 보통 기본 GUI 컴포넌트들을 사용해서 프로그램을 작성할 때는 별 문제가 없더라도 제공되지 않는 GUI 컴포넌트의 경우 프로그램을 작성하는 사람이 직접 만들어 쓸 수 있기 때문입니다. 이러한 작업을 위해서는 2D 그래픽 처리와 애니메이션에 대한 기본 원리를 이해하는 것이 꼭 필요 합니다.

 먼저 2D 그래픽 처리와 함께 1장의 그림이 화면에 어떻게 표시되는지 살펴 보겠습니다. [그림 1] 2D 그래픽 처리에 대한 과정을 보여줍니다. Java의 경우 Frame을 생성하고 사용자가 그리기 작업을 만들 수 있는 Canvas 컴포넌트를 Frame에 추가한 후 Canvas x,y 위치에 Image1을 그리도록 합니다. 그리기 작업은 paint() 함수를 재정의(Overriding)하는 것을 통해 정의하도록 하고 있습니다. Canvas 2D 그래픽에 대한 처리를 수행할 수 있는 메소드와 사용자가 그리기 작업을 정의 할 수 있는 paint()에 대한 콜백 메소드(함수)를 제공하고 있습니다. 이후에 paint() 메소드를 통해서 애니메이션을 처리를 정의하는 방법에 대해 설명할 것입니다.


그림 1. 2D 그래픽 처리 과정

이러한 처리 과정은 java, C#, SDL 등 각기 환경은 다르지만 모두 유사한 처리 과정을 가지고 있습니다. 때문에 2D 처리와 애니메이션의 기본 원리만 이해 한다면 개발 환경이 달라지더라도 기본 원리를 응용해 프로그램을 작성할 수 있습니다. 본 글에서의 내용은 java Canvas를 사용하여 설명합니다.

위에서 설명한 2D 그래픽 처리의 기본 개념에서는 그림 한 장을 표시하는 예를 보였다면 애니메이션 처리를 위해서는 애니메이션 할 그림 여러 장을 빠르게 전환하는 것을 통해서 가능합니다. 이것은 사진과 동영상의 차이와 유사합니다. 그리고 그림을 전환하는 시간 간격을 어떻게 제어하느냐에 따라서 애니메이션이 부드럽게 보여지거나 또는 끊어져 보여지게 됩니다. 이것을 우리는 FPS(Frame Per Second)라고 부르며 각 이미지는 한 시점에 보여지는 화면을 Frame 이라고 말합니다.


Frame: 한 시점에 보여지는 화면

FPS(Frame Per Second): 1초 동안 보여지는 화면의 개수


[그림 2] 는 애니메이션 처리의 동작을 보여줍니다. 1초 동안에 화면을 갱신하는 수와 갱신되는 화면의 내용이 무엇인지에 따라서 애니메이션을 보여줄 수 있으며 그림에서는 1초에 4번 화면이 갱신되면서 x,y 위치에 Image1 ~ Image4를 보여주는 것을 통해 Image에 대한 애니메이션을 만들게 됩니다.

그림 2. 애니메이션 처리 동작

애니메이션의 속도는 프레임과 프레임 사이의 시간을 제어 하는 것을 통해 처리하게 됩니다. 이것은 보통 paint() 메소드 작업을 요청하는 쪽의 주기 즉 repaint() 함수 호출의 간격을 조절하면 됩니다. 이러한 간격을 설정하는 가장 쉬운 방법은 화면 갱신을 위한 Thread를 가지고 요청을 위한 delay를 만드는 것입니다.

갱신 주기는 위에서 설명된 내용을 통해 간단히 수식을 만들어 볼 수 있습니다.

 

repaint를 위한 지연 주기(ms) = 1000 (ms) / 초당 표시할 화면의 수

 

애니메이션 처리를 위한 지금까지의 설명을 정리해 보면 애니메이션은 하나의 화면을 그리되 화면을 얼마의 간격으로 다시 그리도록 주기를 결정하느냐에 따라 애니메이션의 속도가 달라지며 애니메이션 하는 내용은 각 화면 단위로 변화된 각기 다른 내용을 표시하도록 프로그램을 작성하면 된다는 것을 알 수 있습니다.

  지금까지의 설명은 2D 처리에서의 애니메이션 구현의 기본원리를 설명하였으며 세부적인 내용들이 많이 생략되었습니다. 마지막으로 설명에 대한 코드 구현의 간단한 예를 보이고 글을 맺도록 하겠습니다.

감사합니다. ^^


예제 코드

  1 package org.secmem.blog.ex;
  2 
  3 import java.awt.Canvas;
  4 import java.awt.Graphics;
  5 import java.awt.Image;
  6 import java.io.File;
  7 import java.io.IOException;
  8 
  9 import javax.imageio.ImageIO;
 10 import javax.swing.JFrame;
 11 
 12 public class AnimationExample extends Canvas{
 13 
 14 	private static final long serialVersionUID = 1L;
 15 	
 16 	private JFrame mwindow  = null;
 17 	
 18 	private Image[] animatedImages = new Image[4];
 19 
 20 	private int animatedImageIndex = 0;
 21 	
 22 	public static void main(String args[]) {
 23 		AnimationExample example = new AnimationExample();
 24 		example.app();
 25 	}
 26 
 27 	private void app() {
 28 
 29 		mwindow = new JFrame();
 30 		mwindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 31 		mwindow.setSize(500, 500);
 32 		
 33 		loadImages();
 34 		
 35 		setSize(500, 500);
 36 		
 37 		mwindow.add(this);
 38 		mwindow.setVisible(true);
 39 		
 40 		Thread frameRateWorker = new Thread(new Runnable() {
 41 
 42 			@Override
 43 			public void run() {
 44 				while (true) {
 45 					int frameRate = 1000 / 4;   // FPS는 4 fps
 46 					try {
 47 						Thread.sleep(frameRate);
 48 					} catch (InterruptedException e) {
 49 						e.printStackTrace();
 50 					}
 51 					repaint();
 52 				}
 53 			}
 54 		});
 55 		frameRateWorker.start();
 56 		
 57 	}
 58 	
 59 	/**
 60 	 * 애니메이션할 이미지 불러오기
 61 	 */
 62 	private void loadImages() {
 63 		try {
 64 			animatedImages[0] = ImageIO.read(new File("Image1.png"));
 65 			animatedImages[1] = ImageIO.read(new File("Image2.png"));
 66 			animatedImages[2] = ImageIO.read(new File("Image3.png"));
 67 			animatedImages[3] = ImageIO.read(new File("Image4.png"));
 68 		} catch (IOException e) {
 69 			e.printStackTrace();
 70 		}
 71 	}
 72 	
 73 	/**
 74 	 * 다음 표시할 프레임의 이미지 얻기
 75 	 * @return
 76 	 */
 77 	private Image getNextFrame() {
 78 		animatedImageIndex ++;
 79 		int index = animatedImageIndex % 4;
 80 		return animatedImages[index];
 81 	}
 82 	
 83 	@Override
 84 	public void paint(Graphics g) {
 85 		
 86 		int drawPosX = 200;
 87 		int drawPosY = 200;
 88 		
 89 		g.drawImage(getNextFrame(), drawPosX, drawPosY, this);
 90 	}
 91 }
 92 


실행결과