모바일 맞고 게임4-1(맞고 게임의 기본 인터페이스 구현)
DevSource/맞고 2012. 12. 27. 18:54 |지난 시간까지는, 주로 메인 메뉴와 관련된 작업을 했었죠.
이번 시간부터는 이제 실제 게임 시작을 누른 그 순간부터 게임이 끝날 때까지의 게임과 관련된 내용을 프로그래밍 하게 될 것입니다.
먼저, 게임 시작을 누르면, 게임의 화면이 뜨도록 해주어야 하겠지요?
앞 시간에 열심히 만들었던, MainMenu.java 파일의 키입력 부분을 다음과 같이 변경해 줍니다.
* MainMenu.java
또, Midlet.java에 enterGame 이라는 함수는 아직 정의를 안해 놓았었죠.
Midlet.java의 entetrGame 이라는 함수를 다음과 같이 정의 해줍니다.
* Midlet.java.
이제, 게임 시작을 누르면, 게임 화면을 보여줄 준비는 모두 끝마쳤습니다.
이제 게임 진행시에 게임 화면을 그려줄 GameView 클래스를 만들어 봅시다.
GameView 클래스는, gostop 패키지의 GostopView 라는 클래스를 상속받지만,
지금 당장은 GostopView 클래스에 아무런 내용도 구현이 되어 있질 않으므로,
이번 강좌에서는 GostopView 클래스에대해서는 언급하지 않도록 하겠습니다.
그리고 한가지 더 중요한 클래스인 common 패키지의 CyResMan이라는 클래스가
GameView 클래스 내에 포함되어 있는데요.
SK-VM에서는 공통적인 생명 주기(?)를 갖는 이미지 파일들을 하나의 파일로 합쳐,
보다 효율적으로 관리 해 주기 위해 PNGLBM Mixer 라는 유틸리티를 하나 제공합니다.
아래는 PNGLBM Mixer의 실행 화면이구요.
LBM Mixer를 이용하면, LBM 파일들을 dat라는 확장자를 가지는 파일에 이미지들을 합쳐줍니다.
이렇게 이미지들을 합치게 되면, 각각의 LBM 파일에 포함되어 있는 이미지 정보 헤더들이
dat 파일로 합쳐지면서, 이미지 정보 헤더도 하나면 충분하므로, 용량 절감의 효과까지도 볼수있게 되겠지요.
실제로, LBM 파일 각각의 용량의 합보다 Mix된 dat 파일의 용량이 적은 것을 확인하실 수 있습니다.
이렇게 합쳐진 dat 파일을 불러와서 Image 클래스에 각각의 이미지 정보를 넣어주는 역할을 하는 클래스가,
바로 CyResMan 클래스입니다.
그럼 먼저 CyResMan 클래스를 잠깐 분석해 보고 가도록 하지요.
* CyResMan.java
흠... CyResMan 클래스의 setImagedata() 함수는, LBM 이미지를 믹스해 놓은 dat파일을 열어서,
dat 파일의 모든 내용을, byte형 배열인, byteImage에 기록을 해놓는 함수입니다.
setImageData() 함수를 이용해서, byteImage에 dat 파일의 정보를 기록한 후,
addImage() 함수를 통해서, 이미지의 크기 만큼을 읽어 이미지를 생성후, Hashtable에 기록합니다.
그 뒤, 필요할 때마다, getImage() 함수를 이용하면, 원하는 이미지를 Image 클래스에 저장할수 있게 되지요.
자, 그럼, 이제 GameView 클래스를 살펴보도록 할까요?
역시 먼저 GameView 클래스의 소스를 한번 보고나서 설명을 해드리도록 하겠습니다.
* GameView.java.
소스가 깁니다. 그러나 역시, 핵심 코드만을 중점적으로 살펴본다면,
전혀 어려울 것이 없는 소스입니다.
먼저, 생성자를 보면, midlet 객체를 저장하고, width와 height을 저장하고,
각각의 클래스 객체들을 생성해 줍니다.
그리고, makeImage() 함수를 호출해 주는데요.
앞서 살펴본 CyResMan 클래스를 통해,
setImageData() 함수를 이용해서, dat 파일을 읽어오고,
addImage() 함수를 이용해서, 이미지들을 해쉬 테이블에 고루 대입 시켜줍니다.
마지막으로 getImage()를 이용해서, Hashtable의 이미지들을 Image 클래스로 추출할때에는,
addImage() 함수의 첫번째 인자로 주었던, " " 사이의 레이블을 통해 읽어올 수 있습니다.
그리고 그 다음으로 나오는 drawFloor() 함수는, 게임판의 뒷 배경을 그려주는 함수입니다.
먼저 기본으로 지정되어져 있는 클리핑영역을 함부로 바꾸면 안되므로, 기본 클리핑 영역을 저장해줍니다.
기본 클리핑 영역을 저장해 놓고, 전체 화면의 크기인 totalWidth와 totalHeight 만큼을 클리핑 영역으로 지정해줍니다.
LCD가 큰 핸드폰의 경우, 게임의 화면이 한 화면에 다 보이게 되는데, 이때 이미지가 삐져나오면 지저분하게 되므로,
클리핑 영역을 지정해 주는것입니다.
그리고 그 다음 부분은 이중 for 루프를 이용해서 타일 패턴을 화면에 깔아주는 부분인데요.
타일패턴은... 아래와 같은 이미지입니다.
어차피 같은 무늬가 반복되는 배경화면을 큰 이미지로 저장하는 것 보다는,
반복되는 무늬 타일을 쫘르륵 깔아 주는 것이 훨씬 더 효율적이겠죠.
그래서, 이 배경 타일을 화면에 for 루프를 이용해서 깔아준 것입니다.
그리고 그 다음에 나오는,
위 부분은, 배경 타일을 화면에 깔아 준후, 화면 중앙에 있는 무늬를 출력해 주기 위한 부분인데요.
현재 턴이 플레이어 턴일때와, 컴퓨터 턴일때와, 중앙의 무늬의 위치가 다릅니다.
왜냐하면, 컴퓨터의 턴일때에는, 컴퓨터의 패 부분을 봐야 하기때문에, 컴퓨터의 패부분을 넓게,
플레이어의 턴일때에는, 플레이어의 패 부분을 봐야 하기때문에, 플레이어의 패 부분을 넓게 보여줘야하겠죠.
그래서 위와 같이 현재 턴에 따라 위치를 달리해서 그려주게 되는 것이지요.
그리고 그 뒤의 drawWaitingGame(), drawDistributeCards(), drawRunningGame(), drawAcquireCards(), drawResult()
이 5개의 함수는 모두, 게임 내의 각각의 상태 화면들을 그려줍니다.
즉, drawWaitingGame() 의 경우, 게임 시작 전 대기 화면을 그려주며,
drawDistributeCards() 의 경우, 카드를 나눠주는 동작을 그려주지만, 이번시간에는 구현하지 않겠습니다.
또, drawRunningGame() 의 경우, 게임이 진행중일때의 게임 화면을 그려주지만, 역시 이번시간에는 구현하지 않습니다.
그리고 drawAcquireCards() 의 경우, 게임 진행 중에, 왼쪽 화살표나 오른쪽 화살표를 누르면,
현재 플레이어와 컴퓨터가 어떤 패를 가지고 있는지 확인 하는 창이 나오는데, 그 때의 화면을 그려주는 함수입니다.
마지막으로 drawResult() 함수는 게임 종료 후, 게임의 결과를 보여주는 함수이지만, 역시 이번시간에는 구현하지 않습니다.
위에서 설명드린 5개의 함수들은 모두 그림파일을 읽어와서 해당 좌표에 출력해주는 일밖에 하지 않으므로,
따로 긴 설명은 드리지 않겠습니다.
그런데, 앞서 말씀드린 drawXXX() 함수들을 보면, 대부분 room 이라는 객체의 함수를 사용하고 있습니다.
room 객체는 Room 클래스의 객체로, 맞고 게임 중에 게임에서 다루어지는 변수나 각종 함수들을 포함하고 있습니다.
Room 클래스 살펴보기
그리고 다음으로 drawSmallFont() 라는 함수가 있습니다.
drawSmallFont는 인자로 받은 숫자를, 이미지 형식으로 출력해 주는 함수 입니다.
가장 먼저, drawSmallNum() 함수의 윗부분을 보면, String 클래스의 valueOf()라는 함수를 통해서,
입력받은 정수형 데이터를 String 형으로 변경 해 주고 있습니다.
또 변경된 문자열 객체의 length() 함수를 통해, 문자열의 길이를 구해 len 이라는 변수에 저장을 해 주게 되죠.
위의 부분은 오른쪽 정렬로 숫자를 출력할 경우, 정확히 기준점에서부터 폰트가 출력되도록 해주는 작업입니다.
Small Font의 이미지는, 다음과 같은 형식으로 저장이 되어 있습니다.
따라서, g2d 객체로 하나하나의 숫자를 출력해 주어야 하는데, g2d 객체에는, 오른쪽 정렬 기능이 없으므로,
오른쪽 정렬일때에는, 왼쪽으로 이미지의 폭만큼을 빼준뒤 부터 그려주어야 정확히 오른쪽 정렬이 가능 하겠죠?
그 다음 부분은, 왼쪽 정렬인지, 오른쪽 정렬인지의 여부에 따라서, for 루프를 돌면서,
왼쪽 정렬 일때에는, 왼쪽에서 오른쪽으로, 오른쪽 정렬일때에는 오른쪽에서 왼쪽으로 한글자 한글자 출력을 해 주게 됩니다.
숫자 한글자를 출력해 줄때에는 drawSmallFont() 라는 함수가 사용되었는데요.
이자로 받은 x, y 좌표에 해당 이미지상에서 해당 숫자의 좌표를 계산해서 화면에 그려주는 간단한 함수입니다.
폰트 이미지에서의 숫자 한글자 한글자는 모두 4*6 사이즈로 되어 있으므로,
출력하고 싶은 숫자에 글꼴의 폭(Width)을 곱한뒤, 글꼴의 폭만큼 출력을 해주면 되겠지요.
자, 어쨋든 지금까지 작성한 코드들을 실행시켜보면,
우리가 구현한 아래와 같은 화면들을 감상 하실 수 있습니다.
어떠세요?
이제 그럴듯한 맞고 게임 분위기가 제법 풍기는 화면들까지 출력을 해봤네요.
이번 시간부터는 이제 실제 게임 시작을 누른 그 순간부터 게임이 끝날 때까지의 게임과 관련된 내용을 프로그래밍 하게 될 것입니다.
먼저, 게임 시작을 누르면, 게임의 화면이 뜨도록 해주어야 하겠지요?
앞 시간에 열심히 만들었던, MainMenu.java 파일의 키입력 부분을 다음과 같이 변경해 줍니다.
* MainMenu.java
/** * 선택된 메뉴의 해당 동작을 수행한다. * @param menuIdx */ private void choiceMenu(int menuIdx) { switch(menuIdx) { case CyMenu.GAME_START: // 게임 시작. showGameStart(); // 게임 시작. break; |
또, Midlet.java에 enterGame 이라는 함수는 아직 정의를 안해 놓았었죠.
Midlet.java의 entetrGame 이라는 함수를 다음과 같이 정의 해줍니다.
* Midlet.java.
/** * 게임을 시작한다. */ void enterGame() { initGame(); setCurrentDisplay(gameView); curState = State.GAME; } |
이제, 게임 시작을 누르면, 게임 화면을 보여줄 준비는 모두 끝마쳤습니다.
이제 게임 진행시에 게임 화면을 그려줄 GameView 클래스를 만들어 봅시다.
GameView 클래스는, gostop 패키지의 GostopView 라는 클래스를 상속받지만,
지금 당장은 GostopView 클래스에 아무런 내용도 구현이 되어 있질 않으므로,
이번 강좌에서는 GostopView 클래스에대해서는 언급하지 않도록 하겠습니다.
그리고 한가지 더 중요한 클래스인 common 패키지의 CyResMan이라는 클래스가
GameView 클래스 내에 포함되어 있는데요.
SK-VM에서는 공통적인 생명 주기(?)를 갖는 이미지 파일들을 하나의 파일로 합쳐,
보다 효율적으로 관리 해 주기 위해 PNGLBM Mixer 라는 유틸리티를 하나 제공합니다.
아래는 PNGLBM Mixer의 실행 화면이구요.
LBM Mixer를 이용하면, LBM 파일들을 dat라는 확장자를 가지는 파일에 이미지들을 합쳐줍니다.
이렇게 이미지들을 합치게 되면, 각각의 LBM 파일에 포함되어 있는 이미지 정보 헤더들이
dat 파일로 합쳐지면서, 이미지 정보 헤더도 하나면 충분하므로, 용량 절감의 효과까지도 볼수있게 되겠지요.
실제로, LBM 파일 각각의 용량의 합보다 Mix된 dat 파일의 용량이 적은 것을 확인하실 수 있습니다.
이렇게 합쳐진 dat 파일을 불러와서 Image 클래스에 각각의 이미지 정보를 넣어주는 역할을 하는 클래스가,
바로 CyResMan 클래스입니다.
그럼 먼저 CyResMan 클래스를 잠깐 분석해 보고 가도록 하지요.
* CyResMan.java
package common; import javax.microedition.lcdui.*; import java.util.Hashtable; import java.io.InputStream; /** * 이미지를 한번에 읽어들여서 나중에 할당해줄때 사용한다. */ public class CyResMan { Hashtable resList = new Hashtable(); byte[] byteImage; /** * mix해 놓은 이미지 파일을 지정해 전부 읽어 놓는다. * @param path */ public void setImageData(String path) { try { InputStream is = null; is = getClass().getResourceAsStream(path); // byteImage를 null로 초기화 후, if(byteImage != null) byteImage = null; // dat 파일의 사이즈만큼을 읽어 오고, byteImage = new byte[is.available()]; is.read(byteImage); // 파일을 닫는다. is.close(); } catch(Exception e) {} } /** * 이미지를 읽어온다. * @param path * @return */ public synchronized Image loadImage(String path) { // 동시에 2개의 thread가 읽는 경우가 있음. Image img = null; try { InputStream is = null; is = getClass().getResourceAsStream(path); // byteImage를 null로 초기화. if(byteImage != null) byteImage = null; // 해당 파일의 사이즈만큼을 읽어온다. byteImage = new byte[is.available()]; is.read(byteImage); // 파일을 닫는다. is.close(); // 메모리상에서 파일의 사이즈만큼을 읽어온다. img = Image.createImage(byteImage, 0, byteImage.length); byteImage = null; } catch(Exception e) { System.out.println("resMan.loadImage "+e.toString()+" "+path); } return img; } /** * 미리 읽어놓은 data에서 지정된 Offset과 length로 이미지를 만든다. * @param name * @param off 오프셋. * @param len 이미지 사이즈. * @return 성공여부 */ public boolean addImage(String name, int off, int len) { try { /* * setImageData를 통해 미리 읽어 둔 .dat 파일에서, * offset 지점 부터, length 만큼을 읽어와 이미지를 만든다. */ Image img = Image.createImage(byteImage, off, len); resList.put(name, img); } catch(Exception e) { // 이미지 생성 실패. return false; } // 이미지 생성 성공. return true; } /** * 자원 해제. */ public void cleanUp() { byteImage = null; System.gc(); } /** * 저장해 놓은 Image개체를 메모리에서 해제한다. * @param name */ public void removeImage(String name) { Image img = (Image)resList.get(name); resList.remove(name); img = null; } /** * 읽어놓은 메모리상에서 Image개체를 가져온다. * @param name * @return name에 해당하는 image 개체 */ public Image getImage(String name) { Image img = (Image)resList.get(name); return img; } } |
흠... CyResMan 클래스의 setImagedata() 함수는, LBM 이미지를 믹스해 놓은 dat파일을 열어서,
dat 파일의 모든 내용을, byte형 배열인, byteImage에 기록을 해놓는 함수입니다.
setImageData() 함수를 이용해서, byteImage에 dat 파일의 정보를 기록한 후,
addImage() 함수를 통해서, 이미지의 크기 만큼을 읽어 이미지를 생성후, Hashtable에 기록합니다.
그 뒤, 필요할 때마다, getImage() 함수를 이용하면, 원하는 이미지를 Image 클래스에 저장할수 있게 되지요.
자, 그럼, 이제 GameView 클래스를 살펴보도록 할까요?
역시 먼저 GameView 클래스의 소스를 한번 보고나서 설명을 해드리도록 하겠습니다.
* GameView.java.
package twogo1p; import javax.microedition.lcdui.*; import java.io.InputStream; import com.skt.m.Graphics2D; import gostop.*; import common.*; /** * 맞고의 게임 화면을 그려준다. */ |
소스가 깁니다. 그러나 역시, 핵심 코드만을 중점적으로 살펴본다면,
전혀 어려울 것이 없는 소스입니다.
먼저, 생성자를 보면, midlet 객체를 저장하고, width와 height을 저장하고,
각각의 클래스 객체들을 생성해 줍니다.
그리고, makeImage() 함수를 호출해 주는데요.
/** * 합친 이미지 파일에서 각각의 이미지를 추출한다. */ private void makeImage() { // 합친 이미지 파일을 로드후, resMan.setImageData("/image/MixImage_1.dat"); // Mix 파일에서 각각의 이미지를 추출한다. resMan.addImage("0", 0, 654); // 남자 아바타 얼굴. resMan.addImage("1", 654, 684); // 여자 아바타 얼굴. resMan.addImage("2", 1338, 924); // 바닥의 타일 패턴. resMan.addImage("3", 2262, 7448); // 게임판 중앙의 장식 무늬. resMan.addImage("4", 9710, 204); // 폰트 #1. resMan.addImage("5", 9914, 304); // 폰트 #2. resMan.addImage("6", 10218, 21144); // 카드 스프라이트 (대). resMan.addImage("7", 31362, 5304); // 카드 스프라이트 (소). // 아바타 로드. imgPlayer = resMan.getImage("0"); imgComputer = resMan.getImage("1"); // 바닥 타일 및 배경 로드. imgBgPattern = resMan.getImage("2"); imgFloorBg = resMan.getImage("3"); // 폰트 로드. imgSmallFont = resMan.getImage("5"); initPos(); // 위치 초기화. } |
앞서 살펴본 CyResMan 클래스를 통해,
setImageData() 함수를 이용해서, dat 파일을 읽어오고,
addImage() 함수를 이용해서, 이미지들을 해쉬 테이블에 고루 대입 시켜줍니다.
마지막으로 getImage()를 이용해서, Hashtable의 이미지들을 Image 클래스로 추출할때에는,
addImage() 함수의 첫번째 인자로 주었던, " " 사이의 레이블을 통해 읽어올 수 있습니다.
그리고 그 다음으로 나오는 drawFloor() 함수는, 게임판의 뒷 배경을 그려주는 함수입니다.
/** * 게임판의 배경을 화면에 그려준다. * @param g */ private void drawFloor(Graphics g) { g.setColor(204,153,255); g.fillRect(0,0,totalWidth,totalHeight); int clipx=0, clipy=0; int clipw=0, cliph=0; /* * 기본 클리핑 영역을 저장한다. */ clipx = g.getClipX(); clipy = g.getClipY(); clipw = g.getClipWidth(); cliph = g.getClipHeight(); g.setClip(0,0,totalWidth,totalHeight); /* * 타일 패턴을 화면에 깔아준다. */ for(int x=0;x<totalWidth;x+=imgBgPattern.getWidth()) { for(int y=0;y<totalHeight;y+=imgBgPattern.getHeight()) { g.drawImage(imgBgPattern, x, y, Graphics.LEFT|Graphics.TOP); } } if(curTurn == User.COMPUTER) { // 타일 배경을 그려준다. g.drawImage(imgFloorBg, width/2, rectFloor.top+6, |
먼저 기본으로 지정되어져 있는 클리핑영역을 함부로 바꾸면 안되므로, 기본 클리핑 영역을 저장해줍니다.
기본 클리핑 영역을 저장해 놓고, 전체 화면의 크기인 totalWidth와 totalHeight 만큼을 클리핑 영역으로 지정해줍니다.
LCD가 큰 핸드폰의 경우, 게임의 화면이 한 화면에 다 보이게 되는데, 이때 이미지가 삐져나오면 지저분하게 되므로,
클리핑 영역을 지정해 주는것입니다.
그리고 그 다음 부분은 이중 for 루프를 이용해서 타일 패턴을 화면에 깔아주는 부분인데요.
타일패턴은... 아래와 같은 이미지입니다.
어차피 같은 무늬가 반복되는 배경화면을 큰 이미지로 저장하는 것 보다는,
반복되는 무늬 타일을 쫘르륵 깔아 주는 것이 훨씬 더 효율적이겠죠.
그래서, 이 배경 타일을 화면에 for 루프를 이용해서 깔아준 것입니다.
그리고 그 다음에 나오는,
if(curTurn == User.COMPUTER) { // 타일 배경을 그려준다. g.drawImage(imgFloorBg, width/2, rectFloor.top+6, |
위 부분은, 배경 타일을 화면에 깔아 준후, 화면 중앙에 있는 무늬를 출력해 주기 위한 부분인데요.
현재 턴이 플레이어 턴일때와, 컴퓨터 턴일때와, 중앙의 무늬의 위치가 다릅니다.
왜냐하면, 컴퓨터의 턴일때에는, 컴퓨터의 패 부분을 봐야 하기때문에, 컴퓨터의 패부분을 넓게,
플레이어의 턴일때에는, 플레이어의 패 부분을 봐야 하기때문에, 플레이어의 패 부분을 넓게 보여줘야하겠죠.
그래서 위와 같이 현재 턴에 따라 위치를 달리해서 그려주게 되는 것이지요.
그리고 그 뒤의 drawWaitingGame(), drawDistributeCards(), drawRunningGame(), drawAcquireCards(), drawResult()
이 5개의 함수는 모두, 게임 내의 각각의 상태 화면들을 그려줍니다.
즉, drawWaitingGame() 의 경우, 게임 시작 전 대기 화면을 그려주며,
drawDistributeCards() 의 경우, 카드를 나눠주는 동작을 그려주지만, 이번시간에는 구현하지 않겠습니다.
또, drawRunningGame() 의 경우, 게임이 진행중일때의 게임 화면을 그려주지만, 역시 이번시간에는 구현하지 않습니다.
그리고 drawAcquireCards() 의 경우, 게임 진행 중에, 왼쪽 화살표나 오른쪽 화살표를 누르면,
현재 플레이어와 컴퓨터가 어떤 패를 가지고 있는지 확인 하는 창이 나오는데, 그 때의 화면을 그려주는 함수입니다.
마지막으로 drawResult() 함수는 게임 종료 후, 게임의 결과를 보여주는 함수이지만, 역시 이번시간에는 구현하지 않습니다.
위에서 설명드린 5개의 함수들은 모두 그림파일을 읽어와서 해당 좌표에 출력해주는 일밖에 하지 않으므로,
따로 긴 설명은 드리지 않겠습니다.
그런데, 앞서 말씀드린 drawXXX() 함수들을 보면, 대부분 room 이라는 객체의 함수를 사용하고 있습니다.
room 객체는 Room 클래스의 객체로, 맞고 게임 중에 게임에서 다루어지는 변수나 각종 함수들을 포함하고 있습니다.
Room 클래스 살펴보기
그리고 다음으로 drawSmallFont() 라는 함수가 있습니다.
drawSmallFont는 인자로 받은 숫자를, 이미지 형식으로 출력해 주는 함수 입니다.
/** * 화면에 작은 숫자를 표시한다. * @param g * @param x 숫자의 x. * @param y 숫자의 y. * @param num 수. * @param anchor 왼쪽/오른쪽 정렬 선택. */ private void drawSmallNum(Graphics g, int x, int y, long num, int anchor) { int cnt; int posx = x; int posy = y; String strNum = String.valueOf(num); // 숫자를 문자열로 변경해 준다. int len = strNum.length(); // 문자열의 길이를 구한다. // 오른쪽 정렬일때, if(anchor==Graphics.RIGHT) { /* * 정확히 시작점부터 시작하기 위해, * 시작점에서 폰트의 폭을 빼준다. */ posx -= SMALLFONT_WIDTH; } // 숫자를 한자 한자 출력해 준다. if(anchor == Graphics.RIGHT) { // 오른쪽에서 왼쪽으로... for(cnt=len-1; cnt>=0; cnt--) { drawSmallFont(g, posx, posy, (strNum.charAt(cnt)-'0')); posx -= SMALLFONT_WIDTH; } } else if(anchor == Graphics.LEFT) { // 왼쪽에서 오른쪽으로... for(cnt=0; cnt<len; cnt++) { drawSmallFont(g, posx, posy, (strNum.charAt(cnt)-'0')); posx += SMALLFONT_WIDTH; } } } // end private void drawSmallNum(). |
가장 먼저, drawSmallNum() 함수의 윗부분을 보면, String 클래스의 valueOf()라는 함수를 통해서,
입력받은 정수형 데이터를 String 형으로 변경 해 주고 있습니다.
또 변경된 문자열 객체의 length() 함수를 통해, 문자열의 길이를 구해 len 이라는 변수에 저장을 해 주게 되죠.
// 오른쪽 정렬일때, if(anchor==Graphics.RIGHT) { /* * 정확히 시작점부터 시작하기 위해, * 시작점에서 폰트의 폭을 빼준다. */ posx -= SMALLFONT_WIDTH; } |
위의 부분은 오른쪽 정렬로 숫자를 출력할 경우, 정확히 기준점에서부터 폰트가 출력되도록 해주는 작업입니다.
Small Font의 이미지는, 다음과 같은 형식으로 저장이 되어 있습니다.
따라서, g2d 객체로 하나하나의 숫자를 출력해 주어야 하는데, g2d 객체에는, 오른쪽 정렬 기능이 없으므로,
오른쪽 정렬일때에는, 왼쪽으로 이미지의 폭만큼을 빼준뒤 부터 그려주어야 정확히 오른쪽 정렬이 가능 하겠죠?
그 다음 부분은, 왼쪽 정렬인지, 오른쪽 정렬인지의 여부에 따라서, for 루프를 돌면서,
왼쪽 정렬 일때에는, 왼쪽에서 오른쪽으로, 오른쪽 정렬일때에는 오른쪽에서 왼쪽으로 한글자 한글자 출력을 해 주게 됩니다.
숫자 한글자를 출력해 줄때에는 drawSmallFont() 라는 함수가 사용되었는데요.
private void drawSmallFont(Graphics g, int x, int y, int num) { g2d.drawImage(x, y, imgSmallFont, num*SMALLFONT_WIDTH, 0, SMALLFONT_WIDTH, SMALLFONT_HEIGHT, Graphics2D.DRAW_COPY); } |
이자로 받은 x, y 좌표에 해당 이미지상에서 해당 숫자의 좌표를 계산해서 화면에 그려주는 간단한 함수입니다.
폰트 이미지에서의 숫자 한글자 한글자는 모두 4*6 사이즈로 되어 있으므로,
출력하고 싶은 숫자에 글꼴의 폭(Width)을 곱한뒤, 글꼴의 폭만큼 출력을 해주면 되겠지요.
자, 어쨋든 지금까지 작성한 코드들을 실행시켜보면,
우리가 구현한 아래와 같은 화면들을 감상 하실 수 있습니다.
게임 시작 전 대기 화면. (curViewState = GAME_WAIT). 게임 시작전의 그냥 단순한 대기 화면. Player와 Computer의 자금 확인이 가능. 취소, *키 : 메뉴로 돌아 감. 선택, 확인, #키 : 게임 시작. | |
게임 진행시의 화면. (curViewState = GAME_RUNNING) 게임 진행 중의 화면. 지금은 아무것도 구현이 안되있으므로, 아무 것도 없이 배경화면만 뜬다. 왼쪽 화살표 : COM의 패 확인. 오른쪽 화살표 : Player의 패 확인. 취소 키 : 메뉴로 돌아 감. 선택, 확인 키 : 현재 턴을 변경. (임시 디버그용) 턴이 변경될때, 화면 이동 체크. | |
획득 패 확인 화면. (curViewState = GAME_ACQUIRE) 각각의 플레이어가 획득한 패를 보여준다. 지금은 게임이 구현이 안되있으므로, 초기화된 상태의 user 객체 정보가 출력된다. 취소, 선택, 확인, *, #키 : 게임으로 돌아감. |
어떠세요?
이제 그럴듯한 맞고 게임 분위기가 제법 풍기는 화면들까지 출력을 해봤네요.
'DevSource > 맞고' 카테고리의 다른 글
모바일 맞고 게임5-1(실제 맞고 게임의 구현) (0) | 2012.12.27 |
---|---|
모바일 맞고 게임4-2(Room 클래스 살펴보기) (0) | 2012.12.27 |
모바일 맞고 게임강좌3-3(나머지 서브 메뉴 구현하기) (0) | 2012.12.27 |
모바일 맞고 게임3-1(각각의 메뉴구현) (0) | 2012.12.27 |
모바일 맞고 게임 강좌2 (0) | 2012.12.27 |