안드로이드 스튜디오 Libgdx 스프라이트 애니메이션
본문 바로가기
프로그래밍/게임 프로그래밍(Libgdx)

안드로이드 스튜디오 Libgdx 스프라이트 애니메이션

by 124578 2021. 1. 11.

안드로이드 스튜디오 Libgdx 스프라이트 애니메이션

 

안녕하세요.

 

오늘은 Libgdx 스프라이트 애니메이션 구현을 해보겠습니다.

 

게임은 역시 애니메이션이 기본이죠..

 

스프라이트라는 것은 일종의 띠를 말합니다.

 

그 의미는 애니메이션 동작을 띠처럼 이어놓은 이미지 모음 입니다.

 

아래와 같은 모양 입니다.

 

아래는 제가 직접 그린 이미지 입니다.

 

출처만 남기시면 이미지는 맘대로 쓰셔도 됩니다.

 

위와 같이 그린 이미지를 애니메이션으로 구현 합니다.

 

일종의 문어 외계인이 적군 유닛으로 등장 합니다.

 

스프라이트 이미지는 궂이 위의 그림과 같이 띠 모양일 필요는 없습니다.

 

이미지를 자세히 보시면 아시겠지만 게임상에서 나오는 적군 이미지의 상하좌우 이동 이미지 모션이 그려져 있습니다.

 

오늘은 저 스프라이트 이미지를 코딩에 구현 할 것 입니다.

 

어떻게 이미지가 코드에 적용되는지 아래를 보시면 됩니다.

 

하나의 이미지는 인덱스화 하여 번호를 부여합니다..

 

코드 상에서는 저 인덱스를 활용하여 코딩이 될 것입니다.

위 이미지는 아래의 경로에 넣어서 사용하시면 됩니다.

 

TowerDefense\android\assets

 

이미지 파일 이름은 enemy2.png 입니다.

 

현재 저장된 파일은 아래와 같아야 합니다.

 

아니라면 이전 포스팅을 참조하세요..

 

 

 

아래는 전체 코드 입니다.

 

-TowerDefense.java-

package com.mygdx.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.viewport.ExtendViewport;

 


public class TowerDefense extends ApplicationAdapter implements InputProcessor

{

  //타일맵 클래스 선언
  TileMap clsTileMap;
  private SpriteBatch batch;
  private Sprite sprite;
  //Enemy 텍스쳐 선언
  Texture img;

  //적군 클래스 선언

  Enemy clsEnemy;
  OrthographicCamera camera;
  ExtendViewport viewport;
  float viewportWidth, viewportHeight;
  int Size_Width = 256;
  int Size_Height=256;

  //게임 타임
  float stateTime=0.0f;
  int downposx;
  int downposy;

  public void create ()
  {
    // view port 크기 설정
    viewportWidth = 1920;
    viewportHeight =1080;

    //스프라이트 그리기 초기화
    batch = new SpriteBatch();

    //스프라이트 초기화
    sprite = new Sprite();
    camera = new OrthographicCamera();
    //뷰포트 크기 설정
    viewport = new ExtendViewport(viewportWidth/2.0f,viewportHeight/2.0f,camera);
    viewport.apply();

    camera.zoom = 1.0F;
    camera.update();

    //타일 맵 관련 초기화
    clsTileMap = new TileMap();
    clsTileMap.Create("TileMap.tmx",camera);

    //적군 클래스 초기화
    clsEnemy = new Enemy();
    //clsEnemy.Create( img , viewportWidth/2-Size_Width/2, viewportHeight/2-Size_Height/2, 256, 256, sprite,     "enemy2.png");

    clsEnemy.Create((int)(viewportWidth/2-Size_Width/2), (int)(viewportHeight/2-Size_Height/2),     100,100,"enemy2.png",1,20);
    Gdx.input.setInputProcessor(this);

}

 


  @Override
  public void render ()
  {



   // camera 업데이트
   //camera.update();

    // 배경 그리기
    Gdx.gl.glClearColor(1, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.setProjectionMatrix(camera.combined);

   //타일 맵 그리기
   clsTileMap.Render(camera);

    //적군이미지 그리기
    clsEnemy.Render(viewportWidth/2-Size_Width/2, viewportHeight/2-Size_Height/2,100,     100,Gdx.graphics.getDeltaTime() ,batch);

  }
  @Override
  public void resize(int width, int height)
  {
   viewport.update(width,height,false);
   viewport.getCamera().position.set(viewportWidth/2f,viewportHeight/2f,0);
   viewport.getCamera().update();
  }


  @Override
  public void dispose ()
  {
    clsTileMap.dispose();
    batch.dispose();
    img.dispose();
    clsEnemy.dispose(batch);
  }

  @Override
  public boolean keyDown(int keycode) {
    return false;
  }

  @Override
  public boolean keyUp(int keycode) {
    return false;
  }

@Override
public boolean keyTyped(char character) {
  return false;
}

@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {

  downposx=screenX;
  downposy=screenY;

  Vector3 vec=new Vector3(screenX,screenY,0);
  camera.unproject(vec);//// Screen 좌표와 게임상의 좌표가 다름 따라서 camera.unproject(vec)를 해주어 좌표를 변환


  return false;
}

@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {

  Vector3 vec=new Vector3(screenX,screenY,0);
  camera.unproject(vec);//// Screen 좌표와 게임상의 좌표가 다름 따라서 camera.unproject(vec)를 해주어 좌표를 변환
  return false;
}

 

@Override

public boolean touchDragged(int screenX, int screenY, int pointer)

{

  float deltax;

  float deltay;

  float drag_speed = 0.5f;

 

  // X축드래그이동값추출

  if (screenX < downposx)

  {

    deltax = (downposx - screenX) * drag_speed;

 

  }

  else

  {

    deltax = (screenX - downposx) * drag_speed * (-1);

  }

 

  // Y축드래그이동값추출

  if (screenY < downposy)

  {

    deltay = (downposy - screenY) * drag_speed * (-1);

  }

  else

  {

    deltay = (screenY - downposy) * drag_speed;

  }

 

  // X축최소이동값2이상

  if (Math.abs(deltax) < 2)

  {

    deltax = 0;

  }

  else

  {

    downposx = screenX;

  }

 

  // Y축최소이동값2이상

  if (Math.abs(deltay) < 2)

  {

    deltay = 0;

  }

  else

  {

    downposy = screenY;

  }

 

  // X축좌우조정, 화면허용가능범위에서터치한경우

  if ((camera.viewportWidth / 2f) < (viewport.getCamera().position.x) &&(viewport.getCamera().position.x) < viewportWidth - (camera.viewportWidth / 2f))

  {

    // 터치한값이허용범위보다작거나큰경우

    if ((viewport.getCamera().position.x + deltax < (camera.viewportWidth / 2f)) ||

(viewport.getCamera().position.x + deltax > viewportWidth - (camera.viewportWidth / 2f)))

    {

      camera.translate(0, 0);// 카메라조정이없다

    }

    else

    {

      camera.translate(deltax, 0); // X축을조정한다.

    }

  }

  else//범위내에없을때

  {

    if (viewport.getCamera().position.x >= viewportWidth - (camera.viewportWidth / 2f)) //Right 보다클때

    {

 

    if (deltax < 0)

    {

      camera.translate(deltax, 0);

    }

 

  }

  if (viewport.getCamera().position.x <= (camera.viewportWidth / 2f)) //Left 보다작을때

  {

    if (deltax > 0)

   {

    camera.translate(deltax, 0);

   }

  }

}

 

// Y축상하조정, 화면허용가능범위에서터치한경우

  if ((camera.viewportHeight / 2f) < (viewport.getCamera().position.y) &&

(viewport.getCamera().position.y) < viewportHeight - (camera.viewportHeight / 2f))

  {

  // 터치한값이허용범위보다작거나큰경우

    if ((viewport.getCamera().position.y + deltay < (camera.viewportHeight / 2f)) ||

(viewport.getCamera().position.y + deltay > viewportHeight - (camera.viewportHeight / 2f)))

    {

     camera.translate(0, 0); // 카메라조정이없다

    }

   else

    {

      camera.translate(0, deltay); // Y축을조정한다.

    }

  }

  else//범위내에없을때

  {

    if (viewport.getCamera().position.y >= viewportHeight - (camera.viewportHeight / 2f)) //TOP 보다클때

    {

       if (deltay < 0)

      {

        camera.translate(0, deltay);

      }

    }

    if (viewport.getCamera().position.y <= (camera.viewportHeight / 2f)) //Bottom 보다작을때

    {

      if (deltay > 0)

      {

        camera.translate(0, deltay);

      }

    }

  }

 

  camera.update();

  return false

}

 

 

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
      return false;
  }

    @Override
    public boolean scrolled(int amount) {
      return false;
  }
}

 

-TileMap.java-

package com.mygdx.game;

import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTile;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.TiledMapTileSet;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;

class TileMap

{

  // 타일 맵 관련
  TiledMap tiledMap;
  OrthogonalTiledMapRenderer tiledMapRenderer;
  TiledMapTileLayer mainLayer;
  TiledMapTileSet tileset = new TiledMapTileSet();

  //타일맵 만들기
  public void Create(String map_name, OrthographicCamera cam)
  {
    //타일 맵 관련 초기화
    tiledMap = new TmxMapLoader().load(map_name);
    tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);
    mainLayer = (TiledMapTileLayer) tiledMap.getLayers().get(0);
    //타일 맵을 카메라 뷰에 맞추기
    tiledMapRenderer.setView(cam);
  }
  //타일맵 그리기
  public void Render(OrthographicCamera cam)
  {
    tiledMapRenderer.setView(cam);
    tiledMapRenderer.render();
  }

  public void dispose()
  {
    tiledMapRenderer.dispose();
    tiledMap.dispose();

  }

}

 

-Object.java-

 

//오브젝트 클래스
package com.mygdx.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class Object

{
  int Animation_idx = 0;
  // 게임타임 저장 변수
  float stateTime=0.0f;
  // 게임 에니메이션
  float[] arrAnimation = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f,0.5f,0.6f,0.7f,0.8f,0.9f,1.0f,1.1f,1.2f,1.3f,1.4f,1.5f};
  Animation<TextureRegion> Animation; // Must declare frame type (TextureRegion)
  Texture Sheet;
  float m_fImg_posx;
  float m_fImg_posy;

  // 오브젝트 애니메이션 데이터 로드
  public TextureRegion[] Load_Animate_Data( int frame_cols, int frame_rows , String file_name)
  {
    // 스프라이트 텍스쳐 로드
    Sheet = new Texture(Gdx.files.internal(file_name));

    // 스프라이트 이미지를 인덱스에 맞게 쪼개어 준다.
    TextureRegion[][] tmp = TextureRegion.split(Sheet,
    Sheet.getWidth() / frame_cols,
    Sheet.getHeight() / frame_rows);

    // 스프라이트 이미지를 배열에 저장
    TextureRegion[] walkFrames = new TextureRegion[frame_cols * frame_rows];
    int index = 0;
    for (int i = 0; i < frame_rows; i++) {
      for (int j = 0; j < frame_cols; j++) {
        walkFrames[index++] = tmp[i][j];
      }
    }
    // 스프라이트 프레임 반환
    return walkFrames;
  }
  // 오브젝트 생성
  public void Create( int posx, int posy, float width,float height,String file_name, int frame_rows, int frame_cols)
  {
    //이미지 위치를 센터중심으로 맞춤
    m_fImg_posx = posx - ((width ) / 2);
    m_fImg_posy = posy - ((height ) / 2);

    //애니메이션 생성
    Animation = new Animation<TextureRegion>(0.1f, Load_Animate_Data( frame_cols, frame_rows , file_name));
    stateTime = 0.0f;
  }

  // 오브젝트 그리기
  public void Render (float posx, float posy, float width,float height, float GameTime, SpriteBatch batch)
  {
    //게임시간을 적산
   stateTime += GameTime; // Accumulate elapsed animation time

   // Get current frame of animation for the current stateTime
   TextureRegion currentFrame;
   currentFrame = Animation.getKeyFrame(arrAnimation[Animation_idx], true);

    // 적산한 게임시간이 특정 시간을 넘으면 인덱스를 변환하여 애니메이션 효과를 줌
   if (stateTime >= 0.1f) {
      stateTime = 0.0f;
      Animation_idx++;
   }
  // 애니메이션 인덱스가 5이면 다시 초기화 한다.
  if (Animation_idx == 5) {
    Animation_idx = 0;
  }

  m_fImg_posx = posx - ((width ) / 2);//(walkSheet.getWidth() / FRAME_COLS) // sprite sheet 1 image size
  m_fImg_posy = posy - ((height ) / 2);

  // 그리기
  batch.begin();
  batch.draw(currentFrame, m_fImg_posx, m_fImg_posy,width,height);
  batch.end();
}


  // 메모리 해제
  public void dispose( SpriteBatch batch)
  {
    batch.dispose();
    Sheet.dispose();
  }
}

 

-Enemy.java-

 

package com.mygdx.game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

import java.util.LinkedList;
public class Enemy extends Object
{



}

 

아래는 실행 화면 입니다. 문어 외계인이 움직이며 같은 동작을 반복하게 됩니다. 

다음에는 여러 방향으로 움직일수 있도록 프로그램 하겠습니다.

 

 

 

이번 포스팅은 설명이 부족한 부분이 있는데 혹시 모르시는 부분은 댓글 주시면 설명해 드리겠습니다.

 

댓글


TOP

TEL. 02.1234.5678 / 경기 성남시 분당구 판교역로