cocos2d-x android에 레티나 이미지 사용해보자

본론으로 들어가기전에 간단하게 레티나용 이미지를 android적용하려면 2가지 방법이 있겠죠.
  1. 기존 일반 리소스는 어디엔가 백업하고 레티나 리소스의 -hd를 다 빼고 일반 리소스 처럼 바꿔서 사용.
  2. 리소스는 그대로 두고 소스나 기획 데이터를 수정. 파일명에 임의로 -hd를 붙여서 로딩
뭐 일단 위처럼 해줘도 되긴 되겠죠? 하지만 기존 개발 환경에서 소스나 데이터 수정없이 바로 적용되면 좋겠죠? 자 본론 들어가봅니다.

지난 포스팅에서 안드로이드는 hd 이미지가 안된다고 했습니다. cocos2d-x 내부적으로 막혀있죠. factor를 조정해서 sd 이미지를 뻥튀기해서 출력하는 auto scale display mode를 사용하면 되죠. 예를 들어 디바이스가 800 x 480일 때 DesignResolutionSize를 480 320으로 해주면 가로 세로 비율중 작은 값으로 factor를 설정하므로 1.5로 설정이 되서 최종적으로 sd 이미지를 1.5배 뻥튀기하게 되죠.

하지만 게임 퀄리티도 그렇고 이미 hd 리소스도 있는데 굳이 sd 이미지로 쓸 필요는 없을터, 안드로이드에도 hd 리소스를 적용해보도록 해볼게요.

먼저 적용 스샷부터 보겠습니다. 모두 넥서스s에서 찍은 800 x 480 샷 입니다.

기본 + sd 이미지
auto scale display mode setDesignResolutionSize( 480, 320 )
hd 이미지 로딩
hd +  setDesignResolutionSize( 960, 640 )
마지막 스샷이 최종 적용 스샷입니다.

cocos2dx/platform/android/CCFileUtils.cpp의 fullPathFromRelativePath, fullPathFromRelativeFile을 아래와 같이 수정해서 hd 리소스를 읽을 수 있게 합니다.


///< 추가해준다.
#define CC_RETINA_DISPLAY_FILENAME_SUFFIX "-hd"

const char* CCFileUtils::fullPathFromRelativePath(const char *pszRelativePath,
                                                  ccResolutionType *pResolutionType)
{
//    if (CC_CONTENT_SCALE_FACTOR() > 1.0f)
    {
        std::string hiRes = pszRelativePath;
        std::string::size_type pos = hiRes.find_last_of("/");
        std::string::size_type dotPos = hiRes.find_last_of(".");
      bool available = false;
     
        if (std::string::npos != dotPos && (dotPos > pos || pos == std::string::npos))
        {
            hiRes.insert(dotPos, CC_RETINA_DISPLAY_FILENAME_SUFFIX);
        }
        else
        {
            hiRes.append(CC_RETINA_DISPLAY_FILENAME_SUFFIX);
        }
      bool isApkResourcePath = (hiRes[0] != '/');
      if (isApkResourcePath) {
       // when the resources are within the APK file, open the zip to check if the HD image is available
       unzFile pFile = unzOpen(s_strResourcePath.c_str());
       if (pFile) {
        available = (UNZ_OK == unzLocateFile(pFile, (string("assets/") + hiRes).c_str(), 1));
        unzClose(pFile);
       }
      }
      else {
       FILE *fp = fopen(hiRes.c_str(), "rb");
       if (fp) {
        available = true;
        fclose(fp);
       }
      }
      // a HD version of this file is available, so return the new name
      if (available) {
       CCString *pRet = new CCString();
       pRet->autorelease();
       pRet->m_sString.swap(hiRes);
       return pRet->m_sString.c_str();
      }
    }
    return pszRelativePath;
}

const char* CCFileUtils::fullPathFromRelativeFile(const char *pszFilename, const char *pszRelativeFile)
{
    std::string relativeFile = pszRelativeFile;
    CCString *pRet = new CCString();
    pRet->autorelease();
    pRet->m_sString = relativeFile.substr(0, relativeFile.rfind('/')+1);
    pRet->m_sString += pszFilename;

  //  if (CC_CONTENT_SCALE_FACTOR() > 1.0f)
    {
        std::string hiRes = pRet->m_sString.c_str();
        std::string::size_type pos = hiRes.find_last_of("/");
        std::string::size_type dotPos = hiRes.find_last_of(".");
      bool available = false;
     
        if (std::string::npos != dotPos && (dotPos > pos || pos == std::string::npos))
        {
            hiRes.insert(dotPos, CC_RETINA_DISPLAY_FILENAME_SUFFIX);
        }
        else
        {
            hiRes.append(CC_RETINA_DISPLAY_FILENAME_SUFFIX);
        }

      bool isApkResourcePath = (hiRes[0] != '/');
      if (isApkResourcePath) {
       // when the resources are within the APK file, open the zip to check if the HD image is available
       unzFile pFile = unzOpen(s_strResourcePath.c_str());
       if (pFile) {
        available = (UNZ_OK == unzLocateFile(pFile, (string("assets/") + hiRes).c_str(), 1));
        unzClose(pFile);
       }
      }
      else {
       FILE *fp = fopen(hiRes.c_str(), "rb");
       if (fp) {
        available = true;
        fclose(fp);
       }
      }
      if (available) {
       pRet->m_sString.swap(hiRes);
      }
    }
 
    return pRet->m_sString.c_str();
}
hd 이미지를 읽어들일 수 있게 되었고 이제 jni/ 안에 있는 main.cpp에 setDesignResolutionSize( 960, 640 )을 해줘서 화면에 맞게 factor를 조절합니다. 800/960, 480/640 이므로 0.75로 설정이 되겠죠.

이게 끝이구요, 가로가 남는 부분은 비율로 조정한 것이기 때문에 어쩔수가 없죠. 딱 맞게 하려면 디바이스별로 이미지를 준비하던가 아니면 비율에 상관없이 가로 세로를 강제로 사이즈 조절 하던가 해야겠죠. 강제로 리사이징 되면 조금 찌그러질테니 원하던 이쁜 결과물이 아니겠죠?

http://www.cnblogs.com/dodohua/archive/2012/07/26/2609990.html
여백 생기는 것에 대한 해결은 위 링크를 참고하세요.

CC_CONTENT_SCALE_FACTOR 부분은 주석을 해놨는데 일단 factor에 상관없이 hd를 먼저 찾고 없으면 일반 리소스를 리턴하려고 한것인데, 좀더 cocos2d-x가 추구하는 방향으로 가려면 factor값에 따라 해야겠죠? 이부분은 다른쪽과 연계 된 부분이라 좀 더 완벽해지려면 수정이 더 불가피 하겠네요.

이제부터 아래 내용들은 hd리소스 성공 시키기 위해 확인했던 과정들에 관한 내용입니다.

http://www.cocos2d-x.org/boards/6/topics/9395?r=9534
http://xiaominghimi.blog.51cto.com/2614927/969777

위 링크는 구글링을 해서 찾아본 것들인데요, 기본적으로 cocos2d-x 안드로이드 관련 소스등(CCEGLView, CCFileUtils)에서 막혀있는데 링크의 내용이 cocos2d-x 1.0 때의 것이어서 내용과 다른 것 찾아가며 수정해주고, 컴파일까지 해봤습니다.

CCAssert(CC_CONTENT_SCALE_FACTOR() == 1.0f, "retina and scale mode can't be opened at the same time!");
현재 최신인 2.0.x 에서는 억지로 하더라도 assert에 걸리더군요. assert에서 걸리기 때문에 임시 주석처리해서 빌드 성공 후 실행을 해봤지만 안됩니다. 갠히 assert를 한 것이 아니겠죠?

setDesignResolutionSize(480, 320)에 enableRetinaDisplay( true ) 또는 setContentScaleFactor( 2.0f )일 때 화면입니다. 결과적으로 일단 hd이미지가 로딩이 되긴 했습니다. -> 화살표 옆에 빨간색이 2x라는 이미지를 증명하는데요, 그게 다입니다. 스샷대로 제대로 처리가 안되고 있죠. 디자인 해상도를 다르게 해도 사이즈 차이만 조금 있을 뿐 안됩니다.


http://cocos2d-x.org/boards/6/topics/2100
https://github.com/cocos2d/cocos2d-x/pull/538

참고할만한 또 다른 링크가 있는데요, File관련 처리가 살짝 다르고 setDesignResolutionSize부분과 CCEGLView의 ContentScaleFactor set, get 이 크게 다릅니다. 링크를 따라 소스를 보시면 아시겠지만 ViewPort의 width, height를 실제 디바이스 크기로 하고 sizeInPoint는 실 디바이스 사이즈인 SizeInPixel / screenScaleFactor로 해서 줄여버립니다. 533, 320이 되네요. 결과는 위에 소개한 링크와 별반 다르지 않네요.

2개의 링크 내용을 이리저리 해상도 변경해가며 해봤지만 결과는 실패였구여, 뭐 cocos2d-x의 버젼 자체가 달라 살펴보지 못한 곳에서 뭔가 차이점이 있을 수 있겠지만 일단 제가 처음에 설명했던 방법대로 개발을 진행하면 될 듯 싶네요.


http://www.cnblogs.com/yangws/archive/2011/07/22/2114132.html
http://www.cocos2d-x.org/boards/6/topics/6696

마지막으로 2개의 링크를 더 소개하겠습니다. 위에 것은 중국사람 블로그고 밑에껀 한국분이 cocos2d-x wiki에 질문을 올려 한국분이 답을 남긴 것인데, position, size관련 소스를 다른 방식으로 접근해서 수정했네요. 두 방식다 괜찮아 보이긴 하는데 매 작업시 마다 코드 추가작업이 있다는게 좀 걸리네요. 일단 적용 테스트는 해보진 않았서 정상 작동하는지는...

포스팅이 길어졌는데 결론은 제일 처음에 소개한 hd 리소스 읽게해서 디바이스 / hd리소스 사이즈 값으로 factor를 설정하면 된다는것.

댓글

이 블로그의 인기 게시물

'xxx.exe' 프로그램을 시작할 수 없습니다. 지정된 파일을 찾을 수 없습니다.

goorm IDE에서 node.js 프로젝트로 Hello World Simple Server 만들어 띄워보기

애드센스 수익을 웨스턴 유니온으로 수표대신 현금으로 지급 받아보자.