그럼 이번 포스팅부터, Slick2D를 사용해 실제 게임을 만드는 과정을 진행해 보겠습니다.
여기서 소개하는 내용은 https://github.com/ChangseOh/LetsStudySlick2D 이곳에서 프로젝트를 다운받으실 수 있습니다.
우선 앞선 포스팅을 참조해, 이클립스에서 신규 자바 프로젝트를 생성하고 유저 라이브러리로 Slick2D를 추가해줍니다.
프로젝트를 생성했으면 src 아래 패키지를 새로 만들어줍니다.
차례대로
Main
forSlick2D
Scenes
prefab
을 생성했는데,
왜 이렇게 하느냐.. 정해진 것은 아니고, 앞으로의 작업의 편의를 위해 제가 임의로 만든 것입니다.
앞으로 여기 생성될 클래스의 종류는
forSlick2D : Slick2D 엔진 사용을 보조할 자작 함수/클래스를 담을 예정입니다.
Main : 최초 실행될 때 호출될 main 함수를 가진 클래스가 위치합니다.
prefab : 게임에서 사용될 오브젝트들을 클래스로 만들어 위치시킬 것입니다. 요시카 스크램블로 따지면 Enemy나 Bullet, Item 같은 클래스입니다.
Scenes : 타이틀, 메뉴, 메인게임, 게임결과 같이 화면 단위를 클래스로 만들어 여기에 둡니다. 화면 단위라는 것은 위치즈 플라이트의 scene과 비슷합니다.
※prefab이라고 하면 뭔가 익숙한 분도 있을텐데, unity3D에서 말하는 그 프리팹 맞습니다. 물론 유니티3D와 같은 것은 아니고 비슷한 개념인데, 굳이 말하자면 유니티의 프리팹이 이런 oop언어의 객체 개념을 구상화한 것이랄까요..
프리팹 식으로 만드는 이유에 관해서는 차차로..

앞에서의 샘플 프로젝트는 단일화면만 사용했습니다.
이번부터의 프로젝트는 여러개의 화면을 이동해 가면서 진행됩니다.
그러기 위해서 main함수를 포함하는 메인 클래스는, BasicGame이 아닌 StateBasedGame을 상속해서 구성됩니다.
StateBasedGame
public static void main(String[] args)을 갖고 있는 클래스입니다.
main함수 내에서 게임이 실행될 윈도우에 관련된 설정을 하며
반드시 오버라이드해야 하는 initStatesList에서, 사용할 state(각 화면의 단위)를 지정합니다.
그럼 일단 StateBasedGame를 상속한 메인 함수를 만들어봅니다.
프로젝트의 src->Main->new->Class

① 메인 클래스 이름을 적습니다. 적당히 좋은 것으로.
② 상속할 super 클래스를 지정합니다. Browse 버튼을 눌러 StateBasedGame을 입력/선택합니다. (나중에 extends로 추가해도 됩니다)
③ main 함수, 생성자, 추상함수 옵션을 모두 켭니다.
이렇게 하고 Finish 하면 다음과 같이 생성이 완료됩니다.

main 함수 내에는 게임 윈도우 설정에 관한 항목을 추가합니다.
해상도와 게임 제목 정도를 제외하면 이 부분의 내용은 항상 동일합니다.

기본적으로 오버라이드된 initStatesList 함수 안에서 이제 사용할 화면(state) 클래스들을 등록해줘야 합니다. (하나만 등록할 수도 있습니다)
addState로 등록하는데, 등록되는 클래스는 BasicGameState를 상속합니다.
그러니 우선 BasicGameState를 상속한 클래스를 Scenes 패키지에 추가해봅니다. 아직 내용은 써넣지 않아도 되니까.
프로젝트의 src->Scenes->new->Class
이름은 TitleScene으로 정했습니다.
superclass를 BasicGameState로 지정하고, main 생성 옵션은 끕니다.

아직 새로 생성한 State의 내용은 신경쓰지 말고, 메인 함수의 initStatesList 안에서 addState로 추가합니다.

구글에서 Slick2D 관련 강좌를 찾아보신 분은 여기서 뭔가 이상한 점을 느꼈을텐데, 대개 예제로 나오는 스테이트 추가에서는 스테이트 생성자에 스테이트 ID를 지정하도록 되어 있습니다.
예를 들면
addState(new TitleScene(0));
이런 식으로요. (0이 스테이트ID)
그런데 사실은 스테이트ID를 기본 지정하는 과정이 현재 배포중인 Slick2D에는 없습니다. (최소한 제가 Slick2D 홈에서 다운 받은 것은 그러한데, 이게 제대로 구현된 버전이 있을지는 모르겠습니다..)
화면 전환에는 스테이트ID를 사용하므로 이게 꼭 필요한 내용인 것은 맞습니다.
그럼 스테이트ID는 어떻게 부여하느냐..
BasicGameState
아까 생성한 TitleScene.java를 열어봅니다. 생성자와 필수 오버라이드함수를 함께 생성하도록 했으니 아래 내용으로 되어 있을겁니다.

여기서 맨 아래 getID라는 함수가 있고, return 0; 을 하고 있는데, 이 0값이 스테이트ID입니다.
다른 스테이트를 추가했다면 이곳의 리턴값을 일일이 바꿔줘야 하는 것입니다.
이쯤되면 'ID를 인수로 받는 생성자를 만들고, 해당 아이디를 getID에서 리턴해주면 되지 않는가?'가 당연히 떠오릅니다.
그렇게 해서 수정해 본 것이 아래 내용이고 (붉은 사각형 안이 추가 또는 수정한 것)

하는 김에 스테이트를 하나 추가하고, (GameScene이란 이름으로 TitleScene과 같은 방법으로 생성하고, 내용을 수정)
메인클래스에서도 보기 좋게 상수값을 사용해서 아래와 같이 수정합니다.

그럼 이번 TitleScene과 GameScene을 전환해보겠습니다.
아래 그림과 같이 TitleScene에 추가 코딩을 해 줍니다.

스테이트를 전환하는 것은 keyReleased 함수의 game.enterState(ProjectMain.STATE_GAME); 부분입니다.
GameScene 쪽도 TitleScene을 참조해서 비슷하게 코딩해 줍니다.
그쪽에서는 반대로
game.enterState(ProjectMain.STATE_TITLE);
로 해줍니다.
구분하기 좋게 문자 색을 하얀색 이외의 다른걸로 해도 좋겠네요.
실행하기 전에
프로젝트 -> Run As -> Run Configurations에서 실행 정보를 만들어줍니다.
Java Application에서 오른클릭->New 하고 프로젝트와 메인클래스를 입력, 그리고 Arguments 부분에서도 앞선 포스팅을 참조해 Slick2D 엔진이 있는 폴더를 지정합니다.

그렇게하고 실행하면 아래와 같은 결과를 볼 수 있습니다.

스테이트를 사용할 때 몇 가지 미리 알아둘 사항.
1. init는 스테이트 클래스를 처음 new 할 때, 그러니까 보통은 addState 하는 시점에서 실행됩니다.
2. 가장 처음에 addState한 스테이트 클래스가 최초로 실행되는 스테이트입니다.
3. 필수가 아닌 선택 가능한 오버라이드 함수중에 enter와 leave가 있습니다. 이름 그대로 enter는 이 스테이트에 들어올 때, leave는 다른 스테이트로 전환하기 직전에 각각 호출됩니다.
4. 이미지 리소스를 init에서 불러둬도 되지만 가급적 enter와 leave에서 부르고 해제하는 것이 좋겠습니다.
대충 Slick2D로 게임을 만들기 위한 기본 사항이 모두 끝났습니다.
앞으로 이 내용을 바탕으로 게임을 만들어볼려는데.. 어떤 게임이 좋을까요. 구경오시는 분도 슬슬 슈팅게임은 좀 질리지 않으셨을지..
뭘 만들지는 생각 좀 해보겠습니다.. 리소스 많이 안쓰는 게임이니까 러닝류 게임으로 할까 아니면 이전의 레가시 애니메이션을 응용해볼지.. 반영된다는 보장은 어렵지만 이런 게임 해보면 좋겠다는 말씀도 환영합니다. ^^
여기서 소개하는 내용은 https://github.com/ChangseOh/LetsStudySlick2D 이곳에서 프로젝트를 다운받으실 수 있습니다.
우선 앞선 포스팅을 참조해, 이클립스에서 신규 자바 프로젝트를 생성하고 유저 라이브러리로 Slick2D를 추가해줍니다.
프로젝트를 생성했으면 src 아래 패키지를 새로 만들어줍니다.
차례대로
Main
forSlick2D
Scenes
prefab
을 생성했는데,
왜 이렇게 하느냐.. 정해진 것은 아니고, 앞으로의 작업의 편의를 위해 제가 임의로 만든 것입니다.
앞으로 여기 생성될 클래스의 종류는
forSlick2D : Slick2D 엔진 사용을 보조할 자작 함수/클래스를 담을 예정입니다.
Main : 최초 실행될 때 호출될 main 함수를 가진 클래스가 위치합니다.
prefab : 게임에서 사용될 오브젝트들을 클래스로 만들어 위치시킬 것입니다. 요시카 스크램블로 따지면 Enemy나 Bullet, Item 같은 클래스입니다.
Scenes : 타이틀, 메뉴, 메인게임, 게임결과 같이 화면 단위를 클래스로 만들어 여기에 둡니다. 화면 단위라는 것은 위치즈 플라이트의 scene과 비슷합니다.
※prefab이라고 하면 뭔가 익숙한 분도 있을텐데, unity3D에서 말하는 그 프리팹 맞습니다. 물론 유니티3D와 같은 것은 아니고 비슷한 개념인데, 굳이 말하자면 유니티의 프리팹이 이런 oop언어의 객체 개념을 구상화한 것이랄까요..
프리팹 식으로 만드는 이유에 관해서는 차차로..

앞에서의 샘플 프로젝트는 단일화면만 사용했습니다.
이번부터의 프로젝트는 여러개의 화면을 이동해 가면서 진행됩니다.
그러기 위해서 main함수를 포함하는 메인 클래스는, BasicGame이 아닌 StateBasedGame을 상속해서 구성됩니다.
StateBasedGame
public static void main(String[] args)을 갖고 있는 클래스입니다.
main함수 내에서 게임이 실행될 윈도우에 관련된 설정을 하며
반드시 오버라이드해야 하는 initStatesList에서, 사용할 state(각 화면의 단위)를 지정합니다.
그럼 일단 StateBasedGame를 상속한 메인 함수를 만들어봅니다.
프로젝트의 src->Main->new->Class

① 메인 클래스 이름을 적습니다. 적당히 좋은 것으로.
② 상속할 super 클래스를 지정합니다. Browse 버튼을 눌러 StateBasedGame을 입력/선택합니다. (나중에 extends로 추가해도 됩니다)
③ main 함수, 생성자, 추상함수 옵션을 모두 켭니다.
이렇게 하고 Finish 하면 다음과 같이 생성이 완료됩니다.

main 함수 내에는 게임 윈도우 설정에 관한 항목을 추가합니다.
해상도와 게임 제목 정도를 제외하면 이 부분의 내용은 항상 동일합니다.

기본적으로 오버라이드된 initStatesList 함수 안에서 이제 사용할 화면(state) 클래스들을 등록해줘야 합니다. (하나만 등록할 수도 있습니다)
addState로 등록하는데, 등록되는 클래스는 BasicGameState를 상속합니다.
그러니 우선 BasicGameState를 상속한 클래스를 Scenes 패키지에 추가해봅니다. 아직 내용은 써넣지 않아도 되니까.
프로젝트의 src->Scenes->new->Class
이름은 TitleScene으로 정했습니다.
superclass를 BasicGameState로 지정하고, main 생성 옵션은 끕니다.

아직 새로 생성한 State의 내용은 신경쓰지 말고, 메인 함수의 initStatesList 안에서 addState로 추가합니다.

구글에서 Slick2D 관련 강좌를 찾아보신 분은 여기서 뭔가 이상한 점을 느꼈을텐데, 대개 예제로 나오는 스테이트 추가에서는 스테이트 생성자에 스테이트 ID를 지정하도록 되어 있습니다.
예를 들면
addState(new TitleScene(0));
이런 식으로요. (0이 스테이트ID)
그런데 사실은 스테이트ID를 기본 지정하는 과정이 현재 배포중인 Slick2D에는 없습니다. (최소한 제가 Slick2D 홈에서 다운 받은 것은 그러한데, 이게 제대로 구현된 버전이 있을지는 모르겠습니다..)
화면 전환에는 스테이트ID를 사용하므로 이게 꼭 필요한 내용인 것은 맞습니다.
그럼 스테이트ID는 어떻게 부여하느냐..
BasicGameState
아까 생성한 TitleScene.java를 열어봅니다. 생성자와 필수 오버라이드함수를 함께 생성하도록 했으니 아래 내용으로 되어 있을겁니다.

여기서 맨 아래 getID라는 함수가 있고, return 0; 을 하고 있는데, 이 0값이 스테이트ID입니다.
다른 스테이트를 추가했다면 이곳의 리턴값을 일일이 바꿔줘야 하는 것입니다.
이쯤되면 'ID를 인수로 받는 생성자를 만들고, 해당 아이디를 getID에서 리턴해주면 되지 않는가?'가 당연히 떠오릅니다.
그렇게 해서 수정해 본 것이 아래 내용이고 (붉은 사각형 안이 추가 또는 수정한 것)

하는 김에 스테이트를 하나 추가하고, (GameScene이란 이름으로 TitleScene과 같은 방법으로 생성하고, 내용을 수정)
메인클래스에서도 보기 좋게 상수값을 사용해서 아래와 같이 수정합니다.

그럼 이번 TitleScene과 GameScene을 전환해보겠습니다.
아래 그림과 같이 TitleScene에 추가 코딩을 해 줍니다.

스테이트를 전환하는 것은 keyReleased 함수의 game.enterState(ProjectMain.STATE_GAME); 부분입니다.
GameScene 쪽도 TitleScene을 참조해서 비슷하게 코딩해 줍니다.
그쪽에서는 반대로
game.enterState(ProjectMain.STATE_TITLE);
로 해줍니다.
구분하기 좋게 문자 색을 하얀색 이외의 다른걸로 해도 좋겠네요.
실행하기 전에
프로젝트 -> Run As -> Run Configurations에서 실행 정보를 만들어줍니다.
Java Application에서 오른클릭->New 하고 프로젝트와 메인클래스를 입력, 그리고 Arguments 부분에서도 앞선 포스팅을 참조해 Slick2D 엔진이 있는 폴더를 지정합니다.

그렇게하고 실행하면 아래와 같은 결과를 볼 수 있습니다.

스테이트를 사용할 때 몇 가지 미리 알아둘 사항.
1. init는 스테이트 클래스를 처음 new 할 때, 그러니까 보통은 addState 하는 시점에서 실행됩니다.
2. 가장 처음에 addState한 스테이트 클래스가 최초로 실행되는 스테이트입니다.
3. 필수가 아닌 선택 가능한 오버라이드 함수중에 enter와 leave가 있습니다. 이름 그대로 enter는 이 스테이트에 들어올 때, leave는 다른 스테이트로 전환하기 직전에 각각 호출됩니다.
4. 이미지 리소스를 init에서 불러둬도 되지만 가급적 enter와 leave에서 부르고 해제하는 것이 좋겠습니다.
대충 Slick2D로 게임을 만들기 위한 기본 사항이 모두 끝났습니다.
앞으로 이 내용을 바탕으로 게임을 만들어볼려는데.. 어떤 게임이 좋을까요. 구경오시는 분도 슬슬 슈팅게임은 좀 질리지 않으셨을지..
뭘 만들지는 생각 좀 해보겠습니다.. 리소스 많이 안쓰는 게임이니까 러닝류 게임으로 할까 아니면 이전의 레가시 애니메이션을 응용해볼지.. 반영된다는 보장은 어렵지만 이런 게임 해보면 좋겠다는 말씀도 환영합니다. ^^
at 2015/11/27 04:41
덧글
정말 감사합니다
감사합니다.
어떤가요?쓸만한가요? 물론 공부나 연습목적이요
그런 점에서는 공부용도에는 괜찮을듯 하네요.
어떤가요?쓸만한가요? 물론 공부나 연습목적이요
일단 KeyReleased는 BasicGame에 구현되어 있는 오버라이드 함수인데,
StateBasedGame이 BasicGame을 상속하기 때문에
StateBasedGame을 상속한 클래스에서도 KeyReleased를 오버라이드해서 쓸 수 있습니다.
game.enterState() 메소드로 씬이 변경되는건 알겠는데, 제(초보자)가 볼땐 game에 아직 인스턴스가 할당되지 않은거처럼보여서 너무 혼란스럽네요...
StateBasedGame을 상속한 클래스(=ProjectMain 클래스)에서 initStatesList이 끝나고 나면
add된 스테이트들의 init를 한꺼번에(보통 add한 차례대로) 부릅니다.
addState를 갖고 있는 initStatesList는 AppGameContainer 인스턴스를 start한 직후에 불리게 됩니다.
이때문에 addState를 실행하는 단계까지 오면 이미 GameContainer와 StateBasedGame 인스탄스는 메모리에 존재하고 있는 것이죠.
※확인해서 수정했습니다. 이런 순서는 라이브러리 내부에서 정해져 있는데, 소스까지 열어보지 않더라도 브레이크 포인트를 지정하고 디버그 모드로 순서대로 따라가보면서 확인할 수도 있습니다.
잘 봐주셔서 감사드리고, 요즘은 일때문에 업데이트가 뜸합니다만, 공부하시는데 작은 도움이 되었으면 좋겠습니다.
라고 콘솔에 뜨고 1을 눌러도 노란색으로 안바뀌네요.. 무슨 에러일까요?
본문은 라이브러리와 무관하게 자바 클래스를 생성하는 자바 공통적인 내용이고, 적어주신 에러 메시지는 아마 노트북용 터치패드 드라이버 에러 같은데 본편과 연관이 있을지는 모르겠습니다.