자바 게임 프로그래밍 강좌 3

하이텔 자바 동호회 김명현 (MadOx@hitel.net)님의 글입니다.
이글은 김명현님의 동의없이 함부로 배포하실 수 없습니다.

안녕하세요? 두 개 연속 편집 (2 Hit Combo?)의 과업을 달성한 친소맨 입니 다. 꼴은 모니터를 장장 두시간이나 쳐다보고 있었더니, 눈이 아른 아른 거 리는 군요.. @_@ 이번 강좌로 자바 게임 프로그래밍의 1장이 끝났습니다. 이번 강좌까지 만 들어 내게될 각종 소스파일들을 자료실에 업로드 해놓겠습니다. 잘라서 편 집하기 싫으신 분들은 자료실에서 다운로드 받아서 바로 실행해 보세요. * 본 문서는 Java Development Kit 1.0.2 를 기준으로 작성되었습니다. * Java Development Kit 1.1.x 버젼을 사용할 경우 일부 소스가 실행이 되지 않을 수 있습니다. ● 완성된 PingPong2 이제 더블버퍼링을 구현하는 법도 배웠고, Multi-Thread를 구현하는 법도 배웠으니, 실전에 써먹어 보도록 하겠습니다. 아래에 쓰레드와 더블버퍼링 을 이용한 PingPong2 애플릿의 소스가 있습니다. 지금까지 배운 것을 적용 하며 읽다보면 반이상은 이해가 되실테니 천천히 읽어 보고, 다음으로 넘어 가시길 바랍니다. 모르는 부분이 많더라도 낙담하실 필요는 없습니다. 앞으 로 수십번은 더봐야 할 내용이니까요. 01: import java.awt.*; 02: import java.applet.*; 03: import java.lang.Runnable; 04: 05: public class PingPong2 extends Applet implements Runnable 06: { 07: Thread trdMyThread;// 애플릿의 주 쓰레드 08: 09: Image imgOffScr; // 메모리 버퍼로 쓸 Image 10: Graphics grpOffScr; // 메모리 버퍼의 Graphics 핸들 11: 12: int ballXp, ballYp, ballXs, ballYs; 13: 14: public void init() 15: { 16: resize(400, 400); 17: } 18: 19: public void start() 20: { 21: // 아직 쓰레드가 만들어져 있지 않으면 새로 만드시오 22: if (trdMyThread == null) 23: trdMyThread = new Thread(this); 24: 25: // 메모리 버퍼가 만들어져 있지 않다면 400x400의 26: // 크기로 만드시오(애플릿의 영역이 400x400이므로) 27: if (imgOffScr == null) 28: imgOffScr = createImage(400, 400); 29: 30: // 메모리 버퍼에 대한 Graphics 핸들을 얻어 온다 31: grpOffScr = imgOffScr.getGraphics(); 32: 33: ballXp = 100; 34: ballYp = 200; 35: ballXs = 4; 36: ballYs = 4; 37: 38: // 만들어진 쓰레드를 활성화 시킨다 39: trdMyThread.start(); 40: } 41: 42: // 쓰레드의 주 실행 루틴 43: public void run() 44: { 45: // 무한 루프가 쓰레드 안으로 옮겨졌다! 46: while (true) { 47: // paint를 한번 불러준다 48: repaint(); 49: // 0.1초간 쉬고 50: try { 51: Thread.sleep(100); 52: } catch (InterruptedException ie) {} 53: 54: // 공을 옮긴다 55: ballXp += ballXs; 56: ballYp += ballYs; 57: 58: // 벽에 부딪혔으면 방향을 반전 59: if (ballXp > 400 || ballXp < 0) 60: ballXs = -ballXs; 61: if (ballYp > 400 || ballYp < 0) 62: ballYs = -ballYs; 63: } 64: } 65: 66: public void paint(Graphics g) 67: { 68: // 메모리 버퍼를 까맣게 지우고 69: grpOffScr.setColor(Color.black); 70: grpOffScr.fillRect(0, 0, 400, 400); 71: 72: // 메모리 버퍼에 공을 그린다 73: grpOffScr.setColor(Color.white); 74: grpOffScr.fillArc(ballXp, ballYp, 25, 25, 0, 360); 75: 76: // 완성된 그림을 화면에 뿌린다 77: g.drawImage(imgOffScr, 0, 0, this); 78: } 79: 80: public void update(Graphics g) 81: { 82: paint(g); 83: } 84: 85: public void stop() 86: { 87: // 쓰레드가 살아 있다면 활동을 중지시키고, 없애버린다 88: if (trdMyThread != null) { 89: trdMyThread.stop(); 90: trdMyThread = null; 91: } 92: } 93: } [소스5] PingPong2.java 소스의 구조가 2 회 강좌에서 봤던 [표3]의 Runnable Interface를 포함한 Applet의 기본구조와 거의 같아 졌습니다. 내용을 살펴보도록 하겠습니다. ◇ Thread 의 생성 [소스5]에서 쓰레드를 생성하는 곳은 23번 라인입니다. 23번 라인에 서 new 연산자를 이용해 Thread 객체를 생성 합니다. 이때 this라고 불 려지는 특수한 포인터를 넘겨주게 되는데, 여기에서 this 라는 것은 애 플릿 자체를 뜻합니다. Thread의 생성자(Constructor)중 Runnable 인터 페이스를 인수(Parameter)로 받아 들이는 것이 있습니다. 이 생성자는 Runnable 인터페이스의 run메소드를 쓰레드의 주 루틴으로 연결해 주는 역할을 합니다.(Thread의 주 루틴도 run 으로 Runnable 인터페이스에서 의 run과 메소드 명이 같습니다) PingPong2 애플릿 역시 Runnable 인터 페이스를 상속받은 서브 클래스이므로, Runnable 인터페이스를 인수로 하는 생성자에게 넘겨질 수 있고, 그 덕택에 우리의 PingPong2 애플릿 이 쓰레드화(?) 되는 것입니다. 39번 라인에서 trdMyThread라는 쓰레드 를 활성화 시키는데, 이 쓰레드의 주 루틴은 PingPong2 애플릿의 run메 소드이므로, PingPong2애플릿의 run 메소드 부분이 쓰레드가 되어 실행 이 되는 것입니다. (소스의 어느 곳을 봐도 run메소드를 직접 불러주는 곳은 없습니다. 그 이유는 Thread의 start 메소드가 쓰레드를 생성시키 고, 바로 run메소드를 불러주기 때문입니다) ◇ 메모리 버퍼의 생성 이 내용은 앞에서 배운 `Double Buffering의 구현'에서 이미 다룬 내 용이지만, 확인도 할겸해서 한번 더 설명하도록 하겠습니다. [소스5]의 28번 라인에서 더블버퍼링을 위해 메모리버퍼를 만드는 작업을 하고 있 습니다. createImage메소드로 애플릿이 화면상에 차지하고 있는 영역과 크기가 같은 400x400 짜리 Image를 생성 합니다. 다음으로 31번 라인에 서 메모리내에 할당된 Image에 대한 Graphics핸들을 getGraphics메소드 를 이용해 얻어내서 이것을 grpOffScr에 저장합니다. 이후 모든 작업은 grpOffScr을 통해 이루어질 것입니다. ◇ run 메소드 (쓰레드의 주 루틴) paint메소드에서 무한 루프가 없어지는 대신 새로 생긴 run 메소드에 무한 루프가 생겼습니다. 한 두 차례 실행하고 말것이라면 궂이 쓰레드 로 만들 필요가 없기 때문에 쓰레드의 주 루틴은 무한루프인 경우가 많 습니다. run메소드는 우선 repaint메소드를 호출해서 paint메소드를 간 접적으로 불러 줍니다. 바로 paint 메소드를 부르지 않고 repaint 메소 드를 불러주는 이유는 run 메소드 내에서는 paint메소드에 넘겨질 애플 릿에 대한 Graphics 핸들을 얻을 수 없기 때문입니다. 다음으로 Threa- d.sleep(100) 이라는 문장을 수행하게 되는데, Thread 클래스의 메소드 인 sleep는 밀리초 (1/1000초) 단위로 쓰레드의 활동을 중단 시키는 역 할을 합니다. PingPong2 애플릿 에서는 이 수치가 100 이므로 0.1초 정 도의 시간동안 공이 멈추게 됩니다. 이 수치를 작게하면 작게할수록 공 이 빨리 움직일 것입니다. 만약에 이 문장을 빼 버린다면 공은 애플릿 영역속을 미친 듯이 튀어 다닐 것입니다. (너무 빨라서 공이 순간이동 을 하는 것 처럼 보일지도 모릅니다) sleep는 단순히 공의 이동 속도를 지연 시키는것 이외에도 CPU를 다른 프로그램들에게 양보하는 의미에서 주로 사용되어 집니다. ◇ update 메소드 못보던 녀석이 나타났습니다. update메소드를 살펴보면 별로 하는 일 이 없어 보입니다. 단순히 paint 메소드를 불러주는군요. 왜 이렇게 했 을까요? 이유를 알고 싶다면 다음 글을 읽기 전에 update메소드 부분을 주석처리하고 컴파일하여 애플릿을 다시 Web Browser로 불러 보시기 바 랍니다. 아마 엄청나게 껌벅이는 화면을 보실수 있을 것입니다. 우리가 repaint메소드를 불러주면 바로 paint메소드가 불려 지는 것이 아니라, update메소드가 먼져 불려지고, 다음으로 paint메소드가 update 메소드 에 의해 불려지게 됩니다. 그런데 update 메소드는 아주 고약한 버릇을 가지고 있습니다. 그 버릇이 무엇이냐면, 화면을 흰색으로 깨끗이 지우 고 paint메소드를 부르는 것입니다. 이 때문에 화면이 심하게 껌벅이게 되는 것이죠. 그래서 그 못된 버릇을 고쳐주기 위해 부모의 update메소 드를 재정의하여 나쁜 버릇을 없앤 것 입니다. ☞ 새로 등장한 것들 ◇ Component.createImage(int w, int h) 거의 모든 AWT 요소들의 부모인 Component의 메소드로서 지정된 크기 (w x h)의 Image를 만들고 이것을 되돌립니다. ◇ Image.getGraphics() createImage 메소드에 의해서 만들어진 Image 에 한해서 실행 가능한 메소드 입니다. 이 메소드는 메모리에 만들어진 Image에 대한 Graphics 핸들을 되돌립니다. ◇ Graphics.drawImage(Image img, int x, int y, ImageObserver obsrvr) Graphics 핸들로 지정되는 사각영역의 (x, y)좌표를 좌측상단으로 하 는 Image 를 그립니다. 이때 Image 가 완전하지 못할 경우 obsrvr 에게 통보합니다. 보통 obsrvr는 this(애플릿 자체)를 넣어 주면 됩니다. 1.3.4 좀더 범용적인 PingPong을 만들어 봅시다. 이번 단원에서는 APPLET Tag 중 PARAM Tag 를 어떻게 사용하는 지를 배울 수 있을 것입니다. 앞 단원에서 완성시킨 PingPong 애플릿은 범용성이 떨어 집니다. 우리는 공의 크기를 바꾸거나 공의 속도를 바꾸기 위해 매번 컴파 일을 다시 하고 싶지는 않습니다. 또 공의 색깔을 바꾸고 싶어도 다시 컴파 일을 해야 합니다. 무척 불편하군요. 이제 이런 불펀함을 고쳐보도록 합시 다. ● 어떻게 HTML 로 부터 데이터를 넘겨 받을 수 있습니까? 우리는 첫번째 강좌의 1.2 절에서 APPLET Tag 의 사용법에 대해서 배웠습 니다. (기억이 잘 안나시는 분은 다시 첫번째 강좌를 펴서 275 번째 줄부터 읽어 보십시오) 여기에서 <PARAM...> 이라고 적혀있던 Tag가 기억날 것입니 다. 바로 이 Tag 를 이용하여 HTML 이 Java 애플릿에게 데이터를 넘겨줄 수 있습니다. 1.2 절에서 만들어 보았던 HelloWorld 애플릿을 개선하여 우리가 원하는 문장을 출력하는 애플릿으로 만들어 봅시다. 01: import java.awt.*; 02: import java.applet.*; 03: 04: public class ShowMessage extends Applet 05: { 06: private String strMsg; 07: 08: public void init() 09: { 10: resize(100, 100); 11: } 12: 13: public void start() 14: { 15: strMsg = getParameter("MESSAGE"); 16: } 17: 18: public void paint(Graphics g) 19: { 20: g.drawString(strMsg, 20, 45); 21: } 22: } [소스6] ShowMessage.java [소스6] 은 HelloWorld 애플릿을 개조하여 만든 ShowMessage 라는 애플릿 입니다. 이 애플릿은 HTML파일에서 넘어온 메시지를 화면에 출력해 줄 것입 니다. 이 소스에서 주의해서 살펴볼 곳은 15 번 라인 입니다. getParameter 라는 메소드는 인수로 넘어간 문자열과 일치 하는 변수명을 가진 PARAM Tag 로 부터 VALUE 뒤에 명시된 값을 String 형태로 받아 오는 역할을 합니다. 만약 우리가 'I am a boy!' 라는 문장을 출력하고 싶다면 다음과 같이 HTML 파일을 만들어 주면 될 것입니다. 01: <HTML> 02: <BODY> 03: <CENTER> 04: ShowMessage Applet 05: <HR> 06: <APPLET 07: CODE=ShowMessage.class 08: WIDTH=100 09: HEIGHT=100> 10: <PARAM NAME="MESSAGE" VALUE="I am a boy!"> 11: </APPLET> 12: <HR> 13: </BODY> 14: </HTML> [소스7] ShowMessage.html ● PingPong 애플릿을 어떻게 개조할까요? HTML로 부터 데이터를 넘겨 받는 방법을 배웠으니, 이제 PingPong 애플릿 을 좀더 실용적으로 바꿔 봅시다. 다음과 같은 기능을 추가시킬 것입니다. ① RADIUS 변수를 통해 공의 반지름을 넘겨줄수 있도록 한다. ② XSPEED, YSPEED 변수를 통해 한 번에 공이 이동하는 거리를 넘겨 주도 록 한다. ③ DELAY변수를 통해 PingPong애플릿이 쉬는 시간을 조정할 수 있도록 한 다. 우선 알아야할 내용을 정리한후, 소스를 보도록 하겠습니다. ◇ String 값을 int 값으로 바꾸는 방법 getParameter 메소드를 통해 HTML로 부터 받아오는 값은 String 형태 의 값들 입니다. 반지름이나 속도, 지연시간등은 String 값이 아닌 int 값이어야 하므로, 이 값들을 PingPong 애플릿에서 사용하려면 int 형으 로 바꿔야 합니다. <Tip!> (Integer.valueOf(getParameter("변수명"))).intValue(); String을 int로 바꾸기 위해서는 위와 같은 방법을 사용하면 됩니다. 다소 복잡해 보이지만 풀어서 보면 크게 복잡하지는 않습니다. getPar- ameter 메소드로 받아온 변수값을 String형에서 Integer 클래스의 val- ueOf메소드를 이용해 Integer 형으로 변환합니다. 다음으로 Integer 클 래스의 메소드인 intValue메소드로 다시 int 값으로 변환 하면 됩니다. ● 완벽한 PingPong 애플릿 지금까지 배운 지식을 총동원 하여 만들어낸 완벽한 PingPong애플릿의 소 스가 아래에 있습니다. [소스5] 의 PingPong2.java와 거의 대부분의 코드가 동일 하고, HTML 로부터 몇몇파라메터들을 읽어와서 적용하는 부분만 추가 되었습니다. [소스9]는 완성된 PingPong 애플릿을 테스트 하기위한 HTML 파 일 소스입니다. 001: import java.awt.*; 002: import java.applet.*; 003: import java.lang.Runnable; 004: 005: public class PingPong extends Applet implements Runnable 006: { 007: Thread trdMyThread;// 애플릿의 주 쓰레드 008: 009: Image imgOffScr; // 메모리 버퍼로 쓸 Image 010: Graphics grpOffScr; // 메모리 버퍼의 Graphics 핸들 011: 012: int ballXp, ballYp, ballXs, ballYs; 013: int ballRad, delay; 014: 015: public void init() 016: { 017: resize(400, 400); 018: } 019: 020: public void start() 021: { 022: // 아직 쓰레드가 만들어져 있지 않으면 새로 만드시오 023: if (trdMyThread == null) 024: trdMyThread = new Thread(this); 025: 026: // 메모리버퍼가 만들어져 있지 않다면 400x400의 크기로 027: // 만드시오 028: if (imgOffScr == null) 029: imgOffScr = createImage(400, 400); 030: 031: // 메모리 버퍼에 대한 Graphics 핸들을 얻어 온다 032: grpOffScr = imgOffScr.getGraphics(); 033: 034: ballXp = 100; 035: ballYp = 200; 036: ballXs = 037: (Integer.valueOf(getParameter("XSPEED"))).intValue() 038: ballYs = 039: (Integer.valueOf(getParameter("YSPEED"))).intValue() 040: ballRad = 041: (Integer.valueOf(getParameter("RADIUS"))).intValue() 042: delay = 043: (Integer.valueOf(getParameter("DELAY"))).intValue(); 044: 045: // 만들어진 쓰레드를 활성화 시킨다 046: trdMyThread.start(); 047: } 048: 049: // 쓰레드의 주 실행 루틴 050: public void run() 051: { 052: // 무한 루프가 쓰레드 안으로 옮겨졌다! 053: while (true) { 054: // paint를 한번 불러준다 055: repaint(); 056: // 0.1초간 쉬고 057: try { 058: Thread.sleep(delay); 059: } catch (InterruptedException ie) {} 060: 061: // 공을 옮긴다 062: ballXp += ballXs; 063: ballYp += ballYs; 064: 065: // 벽에 부딪혔으면 방향을 반전 066: if (ballXp > 400 || ballXp < 0) 067: ballXs = -ballXs; 068: if (ballYp > 400 || ballYp < 0) 069: ballYs = -ballYs; 070: } 071: } 072: 073: public void paint(Graphics g) 074: { 075: // 메모리 버퍼를 까맣게 지우고 076: grpOffScr.setColor(Color.black); 077: grpOffScr.fillRect(0, 0, 400, 400); 078: 079: // 메모리 버퍼에 공을 그린다 080: grpOffScr.setColor(Color.white); 081: grpOffScr.fillArc( 082: ballXp, ballYp, ballRad, ballRad, 0, 360); 083: 084: // 완성된 그림을 화면에 뿌린다 085: g.drawImage(imgOffScr, 0, 0, this); 086: } 087: 088: public void update(Graphics g) 089: { 090: paint(g); 091: } 092: 093: public void stop() 094: { 095: // 쓰레드가 살아 있다면 활동을 중지시키고 096: // 없애버린다 097: if (trdMyThread != null) { 098: trdMyThread.stop(); 099: trdMyThread = null; 100: } 101: } 102: } [소스8] PingPong.java 01: <HTML> 02: <BODY> 03: <CENTER> 04: PingPong Applet 05: <HR> 06: <APPLET 07: CODE=PingPong.class 08: WIDTH=400 09: HEIGHT=400> 10: <PARAM NAME="RADIUS" VALUE="30"> ← 공의 반지름을 30으로 11: <PARAM NAME="XSPEED" VALUE="2"> ← 가로축 속도를 2 12: <PARAM NAME="YSPEED" VALUE="3"> ← 세로축 속도를 3 13: <PARAM NAME="DELAY" VALUE="10"> ← 지연시간을 10밀리초로 14: </APPLET> 15: <HR> 16: </BODY> 17: </HTML> [소스9] PingPong.html

'자바 > 자바게임' 카테고리의 다른 글

자바프로그래밍#6  (0) 2010.10.28
자바게임프로그래밍#5  (0) 2010.10.28
자바게임프로그래밍#4  (0) 2010.10.28
자바게임프로그래밍#2  (1) 2010.10.28
자바게임프로그래밍#1  (1) 2010.10.28

+ Recent posts