멍멍이네 블로그

유니티로 프로젝트를 진행하다가 간혹 드로우콜(Draw Call)에 관한 문제를 많이 겪는다. - 특히나 모바일 같은 경우 -

 

PC기반은 어느정도 사양에 의해 커버가 돼는데 - 그런데도 발생하기도 함.. 극한으로 최적화를 안하거나 드로우콜을 낭비할 경우 - 모바일은 사양이 좋아졌다지만, 아직 한참 부족한 상태다 - PC처럼 최적화없이 낭비할 경우 커버가 안됀다 -

 

 

서론은 이쯤 하고, 본론으로 들어가면

 

일단 드로우 콜(Draw Call)이란 무엇일까?

CPU가 그래픽 API를 호출해서 GPU에게 렌더링을 요청하는 것을 말합니다.
이 작업은 자원이 많이 들기 때문에 드로우 콜을 줄이는 것이 성능 향상에 있어 매우 중요합니다.
스프라이트들을 스프라이트 시트에 모두 모아놓으면 단 한번의 드로우 콜로 모든 스프라이트를 화면에 표시할 수 있습니다. 게임에 사용하는 스프라이트가 많을수록 성능이 획기적으로 향상 됩니다.
(출처 : http://blog.daum.net/yy.kim.0410/278)

 

드로우 콜이란 화면에서 얼마나 많은 이미지를 뿌려주는 가에 관해 드로우 콜의 수가 늘어난다

유니티에서 많은 이미지를 낭비하게 되면 그만큼 개발프로그램에서 부담이 가는 부분이기 때문에 최적화 및 드로우콜 수의 감소는 선택이 아닌 필수이다.

 

가장 좋은방법으로는 이미지의 수를 줄이는 것.

애초에 이미지 수를 줄여서 드로우콜을 줄이면 가장 좋지만, 그것이 안되는 경우가 대다수.

 

그 다음 방법으로는 이미지의 아틀라스화 이다.

이미지 아틀라스화의 장단점은 직접 찾아보면 금방 나옵니다.

간단하게 설명하자면 장점은 드로우콜의 수를 쉽게 줄일 수 있다.

여러 이미지를 압축하는 방식이기 때문이다.

 

단점 역시 간단하게 말하자면 이미지 관리가 철저해야됀다는것이다.

이미지를 압축해서 사용하는 영역을 침범하면 안되고, 겹픽셀 같은 경우 간혹 발생하는경우도 있고 - NGUI 아틀라스 사용 시 생기는 문제 -

이미지 낭비가 생기는 경우도 없잖아 있다 - 아틀라스는 대게 이미지가 사각형이며 2의 배수이다. 500x500의 4장을 합치면 2000x 2000이 아니라 2048x2048이 되기 때문에 많은 메모리가 필요 이상으로 이용된다.

 

그밖에 자세한 글은 구글링 외 다른 곳에서 참조하시고, 제 글은 간단하게나마 이렇구나 정도로 읽어주시면 됍니다 ㅎ

 

p.s.

저는 NGUI를 이용해서 아틀라스화 할 때 생기는 문제들이였고, 이미지패커? 라는 기능이 프로버전에 지원하는 것 같더군요.

틀리거나 지적사항 및 수정사항에 대해서 댓글 부탁드립니다.

던파 열풍 주화 이벤트가 대박이다.

 

크로니클 악세를 준다는 말을 들었는데... 이렇게 줄 지는 몰랐어요.. (본격 골드 회수 이벤트)

 

 

 

 

상황은 이래요..

 

현실은..

 

주화 1개당 3만골드라고 보시면 됍니다.

 

솜사탕[이벤트] - 미카엘라

아이스크림[이벤트] - 벤딩크 / 젤루스

팝콘[이벤트] - 라붕 / 솔도로스

축제의 리본[이벤트] - 호타루 이나바 / 클라라

도르니어 풍선[이벤트] - 오리너구리 / 흔한 아라드의 모험가

 

언더풋 상점에서 파는 아이템을 각각 해당 NPC에게 사다주면 던파 열풍 주화 1개를 준다.

개당 3만골드에 상점에서 팔고 있음  / 상점에서 사는건 계정당 1회 가능 / 주는건 각 NPC당 10회 가능

 

 

 

 

300개 주화의 크로니클 악세를 받으려면 3만 x 300개 = 900만 골드가 필요하다고 보면 됍니다.

 

골드회수 이벤트!

 

 

//////////////////////////

 

15. 09. 03.

열풍 주화 얻는방법 추가하겠습니다.

1. 십생수 -> 열풍주화

던전을 돌아서 나오는 영원의 십생수를 영원의숲에서 산신령?을 클릭해서 십생수 1개당 주화 1개로 바꿀 수 있다!

 

2. 작정뱃지 -> 열풍주화

솔직히 작정뱃지는 차원의조각 항아리와 크로니클상자(각각 45개씩). 즉 90개만 쓰면 남아돌아요(보조장비와 마법석은 해체 불가능이기때문에 비효율적)

열풍주화상점에서 작정뱃지 1개당 주화 1개로 바꿀 수 있다

저는 85렙기준 190개 받아서 차조항아리랑 크로니클상자로 90개 쓰고, 주화 100개 삼ㅋ

 

3. 주말에 던전돌기.

윤디렉터 찾기 이벤트가 주말마다 있습니다.

적정레벨 던전 돌 때 보스방에서 윤디렉터가 나오는데(특정맵 혹은 일정 퀘스트맵 제외) 윤디렉터가 평균 2개씩 주더라구요~

 

주화 600개 샀는데 아직도 사고 싶은게 많아서 노가다를 뛰게 되네요 ㅜㅜ

Unity - iOS 접근 플러그인 방법

 

제목 : Unity for IOS_Screen Capture한 이미지 카메라롤 에 등록하기.

 

Explan.

IOS에서 Application.CaptureScreenShot(path) 메소드를 사용할경우
캡처는 진행되나 해당 app 의 document폴더로 들어가서 카메라 롤에서 확인할수가 없다.
이를 등록해주는 방법 포스팅.


CaptureHelper.h

#import <Foundation/Foundation.h>

@interface CaptureHelper : NSObject

+(CaptureHelper *)sharedInstancs;

@end
CaptureHelper.mm
#import "CaptureHelper.h"

static CaptureHelper * captureHelper = [CaptureHelper sharedInstancs];

@implementation CaptureHelper : NSObject

+ (void)initialize{
    if(captureHelper == nil)
        captureHelper = [[CaptureHelper alloc] init];
}

+ (CaptureHelper *)sharedInstancs{
    return captureHelper;
}

- (id)init{
    if(captureHelper != nil){
        return  captureHelper;
    }
    
    self = [super init];
    if(self){
        captureHelper = self;
    }
    
    return self;
}


- (NSString *)getDocumentDirectory {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    
    return [paths objectAtIndex:0];
}

@end

extern "C"{
    void CaptureToCameraRoll(const char *fileName)
    {
        NSString *file = [NSString stringWithUTF8String:(fileName)];
        NSString *filePath = [[captureHelper getDocumentDirectory] stringByAppendingString:file];
        UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
        
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
    }
}

위 2개의 파일을 생성후 Plugin/IOS 폴더에 위치 그후

how to used.

[DllImport("__Internal")]
public static extern void CaptureToCameraRoll(String fileName);

// 그이후 스크린 캡처 진행시.

Application.CaptureScreenShot(path);
CaptureToCameraRoll(string.Format("/{0}",path));

이후 카메라롤에서 캡쳐된 이미지를 확인하실수 있다.

 

 

 

URL : http://gomlib.blogspot.kr/2014/10/unity-for-iosscreen-capture.html

 

------------------------------------------------------------------------------

 

제목 : [iOS] 이미지, 동영상을 카메라 롤에 저장

 

이미지

 
1
2
3
4
5
6
7
8
9
10
11
12
13
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
// 이미지를 카메라 롤에 저장
UIImageWriteToSavedPhotosAlbum(image, self,
@selector(image:didFinishSavingWithError:contextInfo:), nil);
 
// 저장한 이후에 실행 될 메소드
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    if (error) {
        NSLog(@"error: %@", [error localizedDescription]);
    } else {
        NSLog(@"saved");
    }
}


동영상

?
1
2
3
4
5
6
7
8
9
10
11
12
13
NSURL *mediaUrl = [info objectForKey:UIImagePickerControllerMediaURL];
// 동영상을 카메라 롤에 저장
UISaveVideoAtPathToSavedPhotosAlbum([mediaUrl path], self,
@selector(video:didFinishSavingWithError:contextInfo:), nil);
 
// 저장한 이후에 실행 될 메소드
- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    if (error) {
        NSLog(@"error: %@", [error localizedDescription]);
    } else {
        NSLog(@"saved");
    }
}

 

출처 URL : http://fra3il.tistory.com/66

 

--------------------------------------------------------------------------------------

 

-(void) GetImageFromAlbum {
    [self GetImage:UIImagePickerControllerSourceTypeSavedPhotosAlbum];
}

 

-(void) StartCameraImagePic {
    NSLog(@"StartCameraImagePic");
    [self GetImage:UIImagePickerControllerSourceTypeCamera];
}

 

-(void) GetImageFromCamera {
    BOOL cameraAvailableFlag = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
    if (cameraAvailableFlag) {
        [self performSelector:@selector(StartCameraImagePic) withObject:nil afterDelay:0.9];
    }
}

 

-(void) GetImage: (UIImagePickerControllerSourceType )source {
    UIViewController *vc =  UnityGetGLViewController();
   
    if(_imagePicker == NULL) {
        _imagePicker = [[UIImagePickerController alloc] init];
        _imagePicker.delegate = self;

    }
   
    _imagePicker.sourceType = source;
   
    [vc presentViewController:_imagePicker animated:YES completion:nil];
}

 

-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    UIViewController *vc =  UnityGetGLViewController();
    [vc dismissViewControllerAnimated:YES completion:nil];
   
    // added video support
    NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType]; // get media type
    // if mediatype is video
    if (CFStringCompare ((__bridge CFStringRef) mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo) {
        NSURL *videoUrl=(NSURL*)[info objectForKey:UIImagePickerControllerMediaURL];
        NSString *moviePath = [videoUrl path];
        UnitySendMessage("IOSCamera", "OnVideoPickedEvent", [ISNDataConvertor NSStringToChar:moviePath]);
    } else{
        // it must be an image
        UIImage *photo = [info objectForKey:UIImagePickerControllerOriginalImage];
        NSString *encodedImage = @"";
        if (photo == nil) {
            NSLog(@"no photo");
        } else {
            // NSLog(@"MaxImageSize: %i", [self MaxImageSize]);
            //  NSLog(@"photo.size.width: %f", photo.size.width);
           
            if(photo.size.width > [self MaxImageSize] || photo.size.height > [self MaxImageSize] ) {
                NSLog(@"resizing image");
                CGSize s = CGSizeMake([self MaxImageSize], [self MaxImageSize]);
               
                if(photo.size.width > photo.size.height) {
                    CGFloat new_height = [self MaxImageSize] / (photo.size.width / photo.size.height);
                    s.height = new_height;

                } else {
                    CGFloat new_width = [self MaxImageSize] / (photo.size.height / photo.size.width);
                    s.width = new_width;
                }
                             
                photo =   [ISNCamera imageWithImage:photo scaledToSize:s];
            }
           
            NSData *imageData = nil;
            NSLog(@"ImageCompressionRate: %f", [self ImageCompressionRate]);
            if([self encodingType] == 0) {
                imageData = UIImagePNGRepresentation(photo);
            } else {
                imageData = UIImageJPEGRepresentation(photo, [self ImageCompressionRate]);
            }
            encodedImage = [imageData base64Encoding];
        }
       
        UnitySendMessage("IOSCamera", "OnImagePickedEvent", [ISNDataConvertor NSStringToChar:encodedImage]);
    }
}

 

 

카인서버의 노무현그리워

 

아이디부터 스멜이 나긴 했는데..

 

하는짓이 딱...

 

 

 

 

파티플레이하는데 아무말 없이 쌩까더니..

 

만렙도 아닌 저렙에 잡항팟이라서 딜이 부족해서 참망있냐고 불렀더니 쌩까더니 2번째판부터 시비걸기 시작함...

 

바로 대화하기 싫어서 차단 꾹.. 어우.. 저런 애들 상대해줘봤자 내 손만 불쌍하지..

 

아 카인서버 정신력 낮은 사람이 많아서 기존 유저들도 고생이 많았겠어요..

 

서버통합거래 생기면서 사람 좀 많아진거같던데.. 정신력이 낮은 사람들이 여전히 많네요..

 

진짜 저렇게 하면 기분이 좋은건가??

 

진짜 취향이 독특해서 저런거라면.. 취향존중이긴 한데..

 

남한테까지 피해주면서 쾌락을 느끼는 그런 취향까진 존중해주고 싶지 않다..

구글플레이 앱 다운로드 오류 194 가 떴다.


어떤 어플을 다운 받으려는데,오류코드 194번이 뜨면서 다운로드에 실패함.



https://support.google.com/googleplay/android-developer/troubleshooter/6169937?hl=ko&ref_topic=15868

URL로 가보면, 구글 개발자 콘솔 도움말쪽에서 검색해본것이다.


어플리케이션이 다운로드 실패 할 경우의 문제점으로,

1. 지원 국가 체크

2. 지원 기기 체크

해보라고 나와있다.



글쓴이는 인터넷을 검색해서, 

출처 : http://aromio.tistory.com/300

위 URL 방식대로 (환경설정 - 어플리케이션 관리자) 탭에 들어가서 "구글 플레이" 앱의 "데이터"와 "캐시"를 삭제한 후 "업데이트를 삭제"하고 핸드폰을 재부팅 후 어플을 깔려고 하니까 잘 됀다.


(글쓴이인 저는 밑의 방식대로 해결했지만, 혹시나 문제가 해결되지 않으면 위의 구글 개발자 콘솔 도움말 방식도 참조해보시는 것도 괜찮을 것 같습니다)

메이플스토리 2 메이드

 

 

투덜이 집사 어흥이 - 회복 및 버프 물약

 

엉뚱한 근위대원 머뭇 - 반지 및 크리스탈

 

그린후드의 메이드 에카

특기 : 목걸이 제작

 

명MC를 꿈꾸는 케이 - 음료수

 

모험가 지망생 해리 - 요리

[엘리트]  부기콜리 Lv.3

주요등장지역 : 부기콜리 해안가, 부기콜리 동굴

출현시간 : 메이플 아일랜드 : 매시 5분, 15분, 25분, 35분, 45분, 55분 / 빅토리아 아일랜드 : 매시 25분

 

[엘리트]  매드오네뜨 Lv.17

주요등장지역 : 플로라 애비뉴

출현시간 : 오후 1시~오후 10시 사이 : 매시 정각, 30분 / 그 외의 시간 : 매시 30분

 

[엘리트] 경비대장 차우 Lv. 12

주요등장지역 : 커닝 인터체인지

출현시간 : 매시 5분, 20분, 35분, 50분

 

[엘리트] 닉시 Lv. 15

주요등장지역 : 요정 나무 호수

출현시간 : 오후 1시~오후 10시 사이 : 매시 5분, 35분 / 그 외의 시간 : 매시 5분

 

 

 

[보스] 좀비머쉬맘 Lv.35

매 시간 25분 ( 1시간 / 30분 )

 

출처 : 메이플스토리2 홈페이지 -> 메이뷰

 

p.s. 잠시 정보 옮기다 힘들어서 대기중

글을 읽기 전에, 미리 말씀드리지만, 닥사냥터는 대게 한대이상 쳐서 경험치를 공유하는것을 목표로 하는겁니다.

밑에 렙업루트 및 폭업사냥터에 관련된 부분은, 사람이 많은채널에서 "빠른 사냥"을 기준으로 하셔야됍니다.

낮은 수의 채널을 돌다보면 밑에 적어둔 맵에 가보시면, 특정 구역에 사람이 많은 곳이 있습니다.

그 옆에 끼셔서 같이 툭툭 쳐주면서 경험치를 먹으시면 됍니다.(사람이 너무 많다 싶으면 조금 적은곳으로 이동)

 

그리고 솔플사냥은 렙업 및 폭업에는 어울리지 않습니다.

솔플 기준이라면 차라리 일반 및 에픽퀘스트를 깨는것을 추천합니다.

 

 

 

1레벨 ~ 12레벨

일반 에픽퀘스트 클리어.

 

12레벨 ~ 14레벨

흰 바위 산.

 

14레벨 ~ 16레벨

차가운 심장

 

16레벨 ~ 20레벨

오색나무 숲

 

20레벨 ~ 25레벨

쉐도우 게이트

 

23레벨 ~ 25레벨 // 비추

엘린 숲

 

25레벨 ~ 30레벨 / 27레벨 ~ 30레벨

프리즘 폭포       / (쉐도우 월드 - 독액)던전의 반복퀘스트

 

(28)30레벨 ~ 32레벨

스위트 밸리 or 왕궁 지하 납치사건 던전

 

32레벨 ~ 35레벨 or 36레벨(녹아내린 정원 생략)

거친 모래 언덕

 

35레벨 ~ 36레벨

녹아내린 정원

 

36레벨 ~ 40레벨 // ~41레벨

생명의틈 (4인 던전)

 

41레벨 ~ 46레벨

카트라무스 (4인 던전)

 

46레벨 ~ 50레벨

루디블 타임홀 (4인 던전)

 

 

40 -> 50 만렙 풀림 후 각종 밸런스패치를 거치면서

던전에 대한 효율(시간대비 경험치 획득량)을 높여서

던전 사냥터가 렙업의 최상위 루트라고 볼 수 있게 되었습니다.

Conversion to Dalvik format failed with error 1 에러가 떳음

인터넷에 검색해보니,

 

원인 1.

  라이브러리 충돌.

 

해결 1.

  충돌 된 라이브러리 제거 후 Project-Clean 후 다시 실행.

 

 

원인 2.

  proguard 버전 문제.

 

해결 2.

  이곳에서 progurad 최신 버전(4.6)을 다운로드해서 설치된 Android SDK 에 교체하니 

  (구체적으로, proguard 최신 버전 중 bin 과 lib 디렉터리만 복사하여 \android-sdk\tools\proguard\ 의

       디렉터리들을 대체했습니다)

 

 

원인 3.(이번에 경험)

  다른 라이브러리 프로젝트(저는 FacebookSDK를 사용함)를 추가할 경우 파일이 문제가 있는지, 해당 에러가 뜸.

 

해결 3.

  새로 최신버전을 다운받아서 연결하니까 문제없이 됌(동일한 조건.)

 

 

 

원인, 해결 2의 출처 : http://www.androidpub.com/1778287

conversion to dalvik format failed

 

원인 : 라이브러리가 중복됌.

 

해결방법 : 중복됀 라이브러리 제거(libs폴더와 library 프로젝트를 Add한것이 중복돼는경우도 있음)