libgdx게임개발 (4)

안녕하세요~케이치입니다~

 방금 전에 포스팅 했었는데 너무 길어지는 것 같아서 끊고 또 포스팅하네요 ㅎㅎ


지난 시간에는 배경화면 넣고 땅도 만들어보고 케릭터도 넣어서 움직여봤습니다.


그런데 문제가 하나있었죠? 보통 화살표를 누르고있으면 계속 연속적으로 움직여줘야 하는데 그렇게 되지 않았던 거죠.


자, 그 해결방법은 생각보다 간단합니다.


우선 수정된 Player 소스를 보여드릴게요 ~

public class Player extends Actor {

private Sprite characterSprite;

boolean leftMove;
boolean rightMove;

float x;

Player(){
characterSprite = new Sprite(new Texture("player.png"));
characterSprite.setBounds(Gdx.graphics.getWidth()/2-100, 100,
 
Gdx.graphics.getWidth()/10, Gdx.graphics.getHeight()/5);
}
public Sprite getCharacterSprite(){
return characterSprite;
}
public void updateMotion(){
if ( leftMove ){
x = characterSprite.getX() - 5;
characterSprite.setX(x);
}
if ( rightMove ){
x = characterSprite.getX() + 5;
characterSprite.setX(x);
}
}

public void setLeftMove(boolean t){
if ( rightMove && t ) rightMove = false;
leftMove = t;
}
public void setRightMove(boolean t){
if ( leftMove && t ) leftMove = false;
rightMove = t;
}
}

setLeftMove와 setRightMove 그리고 updateMotion이라는 메소드가 새로 생겼네요.


그럼 이번엔 CharacterProcess의 소스가 어떻게 바뀌었는지 볼까요??

@Override
public boolean keyDown(int keycode) {
System.out.println("keycode : " + keycode);
switch(keycode){
case 21:
player.setLeftMove(true);
break;
case 22:
player.setRightMove(true);
break;
}
return true;
}

@Override
public boolean keyUp(int keycode) {

switch(keycode){
case 21:
player.setLeftMove(false);
break;
case 22:
player.setRightMove(false);
break;
}
return true;
}

네, 위 처럼 keyUp 메소드를 새롭게 구현해줬고요 keyDown메소드는 뭔가 되게 간단해졌네요 그쵸? ^-^


이렇게 수정하면 키 이벤트 처리는 끝난겁니다. 에? 이게 뭐냐구요?

한번 소스를 간략하게 설명드릴게요 ^-^ 저는 친절한 케이치니까요 ㅎㅎㅎ

자, 일단 간단하게 바뀐 CharacterProcess에서의 처리부터 한번 보시죠.

왼쪽 화살표 키가 눌리면

1. player.setLeftMove(true)를 호출합니다.

2. setLeftMove(true)가 호출되면 아래 로직을 따라갑니다.

 - 만약 오른쪽으로 이동중이었다면 rightMove를 false로 변경합니다.

 - 무조건 leftMove를 true로 변경합니다.

3. 끝


왼쪽 화살표 키가 올라가면 

1. player.setLeftMove(false)를 호출합니다.

2. setLeftMove(false)가 호출되면 아래 로직을 따라갑니다.

 - if문에서 false와 && 조건이 맞물리면서 if문은 실행되지 않습니다.

 - 무조건 leftMove를 false로 변경합니다.


오른쪽 화살표 키가 눌리거나 올라갔을 때도 동일한 로직을 따라갑니다.


자, 여기서 하는 일은 키가 눌렸을 때 케릭터의 이동을 결정짓는 boolean값을 변경하기만 하는 일을 합니다. 좌표를 변경한 것도 아니고 아직 화면에 뿌려지는 것이 아닙니다.


자 그럼 메인 클래스에서는 뭐가 수정이 됐는지 한번 보실까요?

    @Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.disableBlending();
batch.draw(background, 0, 0, 800, 480);
batch.enableBlending();
// let's draw ground image
batch.draw(groundTextureRegion, backgroundPos, 0, 800, 100);
if ( backgroundPos + 800 > 0 && backgroundPos + 800 < 800){
batch.draw(groundTextureRegion, backgroundPos + 800, 0, 800, 100);
}
// let's draw player
inputProcessor.getPlayer().getCharacterSprite().draw(batch);
batch.end();
updateScene();
inputProcessor.getPlayer().updateMotion();

}


자, 메인 클래스에서 변경된 부분은 render()메소드의 마지막 줄에 inputProcessor.getPlayer().updateMotion() 메소드가 호출됐다는 것입니다. 저게 무슨 메소드였는지 위에서 보셨나요?

그냥 지나친 분들을 위해서 아래 다시 보여드릴게요.


    public void updateMotion(){
if ( leftMove ){
x = characterSprite.getX() - 5;
characterSprite.setX(x);
}
if ( rightMove ){
x = characterSprite.getX() + 5;
characterSprite.setX(x);
}
}

이 메소드는 위에서 Player클래스에 새로이 추가되었던 메소드입니다.

여기서 하는 일은 Player객체가 가지고있는 leftMove와 rightMove 불리언 값에따라 케릭터의 좌표를 변경해주는 일을 합니다.

단순히 좌표만 변경합니다.


그리고 메인 클래스에서 이 케릭터를 뿌리고있죠.


이제 이렇게 수정된 소스를 실행해보세요.


방향키를 누르고있는 동안 케릭터가 계속 움직입니다.


아, 그리고 전에는 왼쪽 오른쪽 방향에 대해서 케릭터가 스크린에서 벗어날 수 없도록 했었는데 이번에 수정하면서 그부분이 빠졌네요.


화면에서 사라지지는 못하도록 여러분들이 한번 조건을 넣어보세요~~~  ^-^ 히힛~ 숙제입니다~~~


그리고 점프기능이나 뭐 이런것들을 넣으면 재밌겠죠? ㅋㅋ


그럼 오늘은 이만 줄이겠습니다.


계속 장맛비가 내리는데 곰팡이 조심하시고 즐프하세요~~ ㅋㅋ


이상 친절한 케이치였습니다.






그저께 그래픽 출력하기 1편을 포스팅 했었는데 소스가 없었죠? 어제 올리려 했는데 일이 좀 바빠서 오늘 포스팅합니다.

 

자!!!!

 

오늘은 제가 만든 소스와 이미지 파일을 모두 공유할 예정이니 잘 따라와만 주신다면 여러분들도 멋진 게임을 만드실 수 있습니다 ㅋㅋ

( 저도 처음인데 이런 말을 하고있네요 ㅋㅋㅋ)

 

저와 같은 안드로이드 스튜디오를 사용하고 libGDX가 설치되어있으며 이곳의 설정(assets)을 똑같이 하셨다는 가정하에 진행합니다. 그리고 테스트는 안드로이드 폰이 아닌 데스크탑에서 진행합니다. ( 아직 폰에서는 테스트를 못해봤습니다. )

 

 

질문 있으시면 댓글 남겨주세요.

 

그럼 시작합니다.

 

오늘 보시게될 소스는 아래와 같이 실행이 됩니다.

 

 

 

땅은 왼쪽으로 계속 움직이는 것 처럼 보이고 케릭터는 왼쪽, 오른쪽 화살표로 이동이 가능합니다.

 

 

1. 추가된 파일 및 클래스 ( 소스 실행에 필요한 추가된 이미지 파일은 첨부하였습니다. )

- carl-carlson.jpg는 없어도 되는 파일인데 들어가있네요 ㅈㅅ;;;;

 

 

 

2. 첨부된 파일을 다운받아 해당 위치 ( android/assets )에 넣어주세요.

3. core에 있는 소스는 아래와 같습니다.

 

우선 Player 입니다.

 

 

 

 

 

 

 

자, 플레이어는 게임속 주인공 케릭터입니다. Sprite를 이용해서 뿌려줄거구요 setBounds메소드를 이용해서 케릭터의 사이즈를 지정해줬습니다. 그리고 외부에서 characterSprite를 불러쓸 수 있도록 getter를 하나 선언해줬죠.

 

다음은 CharacterProcessor입니다.

 

 

CharacterProcessor가 InputProcessor를 구현하면 그 안에 각종 메소드들을 오버라이드해야하는데 저는 keyDown메소드만 구현을 했고 나머지 메소드들은 기본으로 놔두었습니다.

 

일단 기본적으로 플레이어를 하나 생성해서 들고있고 그 플레이어에대한 getter인 getPlayer()를 만들어줬습니다.

 

이제 키보드 입력을 받아서 케릭터를 움직이기위해 keyDown메소드를 구현합니다.

 

여기서 keycode 21은 왼쪽, 22를 오른쪽 화살표를 말합니다.

왼쪽화살표가 눌리면 좌측으로 이동하는데 조건이 있습니다. 화면의 가장 좌측까지 도달하면 더이상 움직이지 않도록 if 조건을 주었습니다. 오른쪽으로 이동하는것도 마찬가지로 동일한 방법으로 처리했습니다.

 

 

자, 이제 메인클래스인 MyGdxGame 클래스를 보실까요?

public class MyGdxGame extends ApplicationAdapter {
    SpriteBatch batch;
    Texture background;
    Texture terrain;
    TextureRegion groundTextureRegion;
    float backgroundPos = 0;
    CharacterProcessor inputProcessor;

    @Override
    public void create () {
        batch = new SpriteBatch();
        background = new Texture("sky2.jpg");
        terrain = new Texture("grey_stone_1.png");
        terrain.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);
        groundTextureRegion = new TextureRegion(terrain);
        inputProcessor = new CharacterProcessor();
        Gdx.input.setInputProcessor(inputProcessor);
    }
    @Override
    public void render () {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        batch.begin();
        batch.disableBlending();
        batch.draw(background, 0, 0, 800, 480);
        batch.enableBlending();
        
        // let's draw ground image
        batch.draw(groundTextureRegion, backgroundPos, 0, 800, 100);
        if ( backgroundPos + 800 > 0 && backgroundPos + 800 < 800){
            batch.draw(groundTextureRegion, backgroundPos + 800, 0, 800, 100);
        }

        // let's draw player
        inputProcessor.getPlayer().getCharacterSprite().draw(batch);

        batch.end();

        updateScene();
    }
    private void updateScene(){
        float deltaTime = Gdx.graphics.getDeltaTime();
        backgroundPos -= 200 * deltaTime;
        if ( backgroundPos <= -800 ){
            backgroundPos = 0;
        }
    }
}

 

 

우선 create()에서는 대부분의 객체를 초기화해주었습니다.

 

render()에서는 뿌려주는 것을 담당하는데 게임이 실행되는동안 끊임없이 호출되는 메소드인것 같습니다.

 

enableBlending으로 배경(sky2.jpg)위에 땅(grey_stone_1.png)과 케릭터(player.png)를 그릴 수 있도록 해주었습니다.

 

땅을 그릴때는 batch.draw()에서 TextureRegion을 이용했고, 케릭터는 Sprite.draw를 이용했습니다.

 

그리고 땅을 그릴때 땅이 좌측으로 계속 이동하면서 마치 케릭터가 우측으로 이동하는것 같은 효과를 줄 수 있도록 해보았습니다.

 

updateScene()에서 그 효과를 줄 수 있도록 backgroundPos ( 땅 그림이 뿌려질 위치 )를 변경해주고있습니다.

 

자, 그런데 이렇게 하면 화살표를 계속 누르고 있어도 한번 밖에 움직이지 않습니다.

 

이부분에 대해서는 다음 포스팅에서 해결방법을 알려드리도록 하겠습니다.

 

그럼 오늘도 즐프하시길 ~ ^-^

 

지금까지 libGDX를 독학하고있는 케이치였습니다~

 

P.S : 도움이 되셨으면 댓글 하나 남겨주세요 ^-^

 

 

 

 

 

 

[[ 원본 이미지 파일첨부 ]]

 

 

 

 

 

libGdx 프로젝트 생성 직후 기본적으로 안드로이드 버전은 테스팅이 원활히 진행이 된다.

 

하지만 데스크탑 버전으로 테스팅하려고 하면 아래와 같은 에러가 발생한다.

 

    Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: Couldn't load file: badlogic.jpg
        at com.badlogic.gdx.graphics.Pixmap.<init>(Pixmap.java:140)
        at com.badlogic.gdx.graphics.TextureData$Factory.loadFromFile(TextureData.java:98)
        at com.badlogic.gdx.graphics.Texture.<init>(Texture.java:100)
        at com.badlogic.gdx.graphics.Texture.<init>(Texture.java:92)
        at com.badlogic.gdx.graphics.Texture.<init>(Texture.java:88)
        at com.mygdx.game.MyGdxGame.create(MyGdxGame.java:18)
        at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:143)
        at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:120)
    Caused by: com.badlogic.gdx.utils.GdxRuntimeException: File not found: badlogic.jpg (Internal)
        at com.badlogic.gdx.files.FileHandle.read(FileHandle.java:136)
        at com.badlogic.gdx.files.FileHandle.readBytes(FileHandle.java:222)
        at com.badlogic.gdx.graphics.Pixmap.<init>(Pixmap.java:137)
        ... 7 more

    Process finished with exit code 0

 

 

해결 방법은 아래와 같다.

 

1. 이클립스 사용자의 경우

Run => Run Configurations.. => DesktopLauncher 의 Arguments 탭 => Working Directory => Others then browse to yourproject-android/assets/ and click Apply => Run

 

 

 

 

 

 

 

2. 안드로이드 스튜디오의 경우

 

  1. Run -> Edit Configurations

  2. "Working Directory:" 를 가면 기본적으로 안드로이드 스튜디오 프로젝트 최상위 디렉토리로 설정되어있는데 => "android/assets" 디렉토리까지 들어가도록 설정

     

 

 

 

 

이제 다시 실행해보시면 잘 될겁니다.

 

 

즐프~

 

 

 

 

- 이건 제가 LibGDX Game Development Essentials 라는 책을 보면서 정리한 내용으로 제가 정확히 이해하지 못한 부분에 대해서는 여러분들이 알고있는 것과 차이가 있을 수 있음을 말씀드리며, 혹여나 제가 잘못 이해한 부분이 있으면 댓글로 저에게 지식을 좀 나누어 주시면 감사하겠습니다. ^-^



오늘은 Displaying graphics에 대해서 포스팅을 하려합니다.

일 단 Texture가 뭔지에 대해서 이해를 해야할 것 같네요. Texture란 GPU로 이미지가 전달되는 단위(?)라고 생각하시면 됩니다. 그런데 이게 expensive process라서 좀 더 효율적으로 그리기 위한 도구가 필요했는데 그래서 나온것이 SpriteBatch입니다.


SpriteBatch 클래스는위치 정보를 이용해서 좀더 효율적으로 그림을 그릴 수 있도록 도와주는 클래스입니다. 동일한 Texture를 여러군데에 뿌려줘야할 때 유용하게 쓰입니다. 하지만 만약 Texture가 바뀌게되면 비효율적인데요 이럴 때는 texture packing과정을 통해서 여러 이미지를 하나의 큰 이미지로 합쳐서 사용하게 됩니다.


TextureRegion이라는 클래스를 사용할 수도 있습니다. TextureRegion은 Texture의 조각이라고 생각하시면 됩니다. Texture의 일부를 떼어낸 것이죠. 예를들어, 일반적인 벽지는 무늬가 반복이 되죠? 이 반복되는 무늬를 TextureRegion이라고 보시면 됩니다.


또 다른 방법은 Sprite 클래스를 이용하는 방법입니다. Sprite클래스는 Texture와 위치 정보, 사이즈 정보를 함께 합쳐놓은 클래스입니다. 따라서 화면에 그림을 그릴 때 draw코드내에 위치정보나 사이즈 정보를 입력하지 않아도 됩니다. 대부분의 경우 Sprite클래스를 사용하면 편리하다고 합니다.


자, 그럼 이 세가지를 어떤 경우에 써야 할까요?


일단 간단한 게임을 하나 만든다고 생각해봅시다.


우 선 배경(1)이 있고 그 위에 그려지는 여러 장애물들(2)이 있고, 마지막으로 배경이 움직이는 것처럼 보일 수 있도록 게임진행 내내 스크롤링이 되는 구름배경이나 땅이 움직이는 것처럼 보이도록 해주는 부분(3)이 있다고 가정해보죠.


1번같은 경우에는 그냥 고정해놓으면 됩니다. 따라서 Texture를 이용하면 됩니다.

2번의 경우에는 계속 움직여야되는 아이템들이라 Sprite를 이용합니다.

3번의 경우에는 TextureRegion을 이용합니다. 보이지 않는 스크롤링 효과를 만들기 위해서 여러번 그림을 그려줘야 하기 때문이죠.


자, 여기까지 정리를 해보자면 그래픽 출력하기에는 세가지 방식이 있습니다.


1. SpriteBatch

2. TextureRegion

3. Sprite


아직 저도 많이 사용해보질 않아서 어떤 경우에 이렇게 써야한다라고 딱 잘라서 말할 수는 없지만, 지금 당장은 위에 잠깐 설명드린대로 사용하시면 될것 같습니다.



( 조만간 간략한 소스까지 올리도록 하겠습니다. )