꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 친절한티스

cocos2d에서는 스프라이트 시트를 이용한 스프라이트 애니메이션 기능을 지원하고 있습니다. 샘플 프로젝트를 통해 작동 방법을 살펴보면 plist ( XML 포맷 ) 를 통해 스프라이트 정보를 구성하여 애니메이션을 재생하는 식으로 동작하고 있습니다. 대략적인 샘플 코드를 살펴보죠.


// 배치 스프라이트 
CCSpriteBatchNode *pSpriteBatch = CCSpriteBatchNode::create( "Resources/grossini.png" );
addChild( pSpriteBatch, 0, CHILD_SPRITE_BATCH );

CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache();
frameCache->addSpriteFramesWithFile( "Resources/grossini.plist" );

// 스프라이트 객체 생성
CCSprite *pSprite = CCSprite::create();
CCSpriteFrame *frame = frameCache->spriteFrameByName("grossini_dance_01.png");
pSprite->setDisplayFrame(frame);
pSpriteBatch->addChild(pSprite);

// 애니메이션 리스트
CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();
animCache->addAnimationsWithFile( "Resources/animations.plist" );

CCAnimation *normal = animCache->animationByName( "dance_1" );
normal->setRestoreOriginalFrame(true);
CCAnimate *animN = CCAnimate::create(normal);
CCSequence *seq = (CCSequence*)CCSequence::create(animN, NULL);

pSprite->runAction( CCRepeatForever::create( seq ) );


스프라이트 시트


grossini.plist 내부를 보면 위와 같이 XML 포맷으로

각 프레임에 대한 스프라이트 좌표가 설정 되어있습니다.



animation.plist 에는 해당 시퀀스에 쓰이는 장면들의 정보가 설정 되어있습니다.


이것만으로도 충분하겠지만 개인적으로 좀더 욕심이 생기더군요. 우선 plist의 XML 포맷이 너무 보기 안좋습니다. 쓸데없는 문자열도 많이 들어가서 정보량이 많아지면 기하급수적으로 덩치도 커지고, cocos2d-x에서 plist를 사용하는 방식도 너무 정형화되어 있어 커스터마이즈도 힘들어 보이더군요.


그래서 json 포맷을 이용해 위와 같은 동작을 하는 동시에 커스터마이즈도 용이한 방법을 구현해봤습니다( 내용적으로는 거의 동일 합니다. ). 먼저 json으로 위의 grossini.plist와 animation.plist를 하나로 통합했습니다.



훨씬 깔끔하고 보기에도 좋습니다. 이제 이 데이터를 기반으로 위와 같이 스프라이트 애니메이션을 구현해보겠습니다.


// JSON 파싱
Json::Reader jsonReader;
Json::Value rootValue;
bool parsingSuccessful = jsonReader.parse( ifstream( strFileName.c_str() ), rootValue );
if( !parsingSuccessful )
	return parsingSuccessful;

// 배치 스프라이트를 읽어온다
string strImageFileName = rootValue.get( "image", "null" ).asString();
CCSpriteBatchNode *pSpriteBatch = CCSpriteBatchNode::create( strImageFileName.c_str() );
addChild( pSpriteBatch, 0, CHILD_SPRITE_BATCH );

// 배치노드 파싱
const Json::Value batchNode = rootValue[ "batchnode" ];
assert( batchNode.isArray() );
int batchNodeSize = batchNode.size();
hash_map< string, CCSpriteFrame* > spriteFrameList;
CCAnimation * anim = CCAnimation::create();
for( int batchIndex = 0; batchIndex < batchNodeSize; ++batchIndex )
{
	const Json::Value spriteValue = batchNode[ batchIndex ];
	string frameName = spriteValue.get( "name", "null" ).asString();
	const Json::Value spriteRectValue = spriteValue[ "rect" ];
	assert( spriteRectValue.isArray() );
	assert( 4 == spriteRectValue.size() );

	int jsonArrayIndex = 0;
	int xPos = spriteRectValue[ jsonArrayIndex++ ].asInt();
	int yPos = spriteRectValue[ jsonArrayIndex++ ].asInt();
	int width = spriteRectValue[ jsonArrayIndex++ ].asInt();
	int height = spriteRectValue[ jsonArrayIndex++ ].asInt();

	CCSpriteFrame *pSpriteFrame = CCSpriteFrame::createWithTexture( 
		pSpriteBatch->getTexture(), CCRectMake( xPos, yPos, width, height) );

	// 0번을 기본 스프라이트로
	if( 0 == batchIndex )
	{
		m_pSprte = CCSprite::createWithSpriteFrame( pSpriteFrame );
		pSpriteBatch->addChild( m_pSprte );
	}

	spriteFrameList.insert( make_pair( frameName, pSpriteFrame ) );
}

// 시퀀스 정보를 파싱
const Json::Value sequenceList = rootValue[ "sequence" ];
assert( sequenceList.isArray() );
int sequenceSize = sequenceList.size();
for( int sequenceIndex = 0; sequenceIndex < sequenceSize; ++sequenceIndex )
{
	const Json::Value sequenceValue = sequenceList[ sequenceIndex ];
	string sequenceName = sequenceValue.get( "name", "null" ).asString();
	float animationDelay = (float)sequenceValue.get( "delay", 1 ).asDouble();
	CCAnimation *pAnim = CCAnimation::create();
	pAnim->setDelayPerUnit( animationDelay );

	const Json::Value sequenceFrame = sequenceValue[ "node" ];
	assert( sequenceFrame.isArray() );
	int sequenceFrameSize = sequenceFrame.size();
	for( int sequenceIndex = 0; sequenceIndex < sequenceSize; ++sequenceIndex )
	{
		string sequenceFrameName = sequenceFrame[ sequenceIndex ].asString();
		hash_map< string, CCSpriteFrame* >::iterator it;
		it = spriteFrameList.find( sequenceFrameName );
		if( it == spriteFrameList.end() )
			continue;

		CCSpriteFrame *pFrame = it->second;
		pAnim->addSpriteFrame( pFrame );
	}

	CCAnimate *newSequence = CCAnimate::create( pAnim );
	m_sequenceList.insert( make_pair( sequenceName, newSequence ) );
}

이후에 원하는 시퀀스를 플레이해주고 싶을때 아래와 같이 호출해주면 됩니다.


hash_map< string, CCAnimate* >::iterator it;
it = m_sequenceList.find( strSequenceName );
if( it == m_sequenceList.end() )
	return;

CCSequence *seq = (CCSequence*)CCSequence::create( it->second, NULL );
m_pSprte->runAction( seq );


반응형
,
Posted by 밥을먹는선비

0. 들어가며...

물리학에서 물질은 중성자, 양성자,전자 3개로 되어있습니다.(물론 이거말고도 더있는것으로 알고있습니다만..) 3가지 요소는 각각의 속성을 가지고있습니다.

이중에서 중성자는 주로 질량에 관여는하는 요소이고 나머지 두개는 물질의 성질을 결정하는 역활을 한다고합니다.(양성자와 전자 갯수에의해 원자가 결정됨, 단 중성자만 질량에 관여하지는 않습니다.)


사실 갑자기 소프트웨어개발 관련글쓰면서 웬 물리? 할수도있겠지만..


우리가 많이 쓰는 MVC 패턴을 보면 이것들과 많이 닮아있다는것을 알수있습니다.


Model은 중성자 ,View는 양성자, Controller는 전자가 하는 역활이 비슷합니다. 


학생들을 교육하다가 느끼는점은 프로그래밍언어를 배울때 어떤 언어가 배우기 쉬운것인가 하는 부분인데...

Model요소가 가장 배우기 어렵고(사실 너무 무겁죠..) 그다음이 Controll 그리고 제일 쉬운것이 View입니다.


사실 모델에 비해 상대적으로 배우기쉬운 VC만으로 코드를짤수도있으나 그런 코드는 마치 반물질처럼 코드가 만들어 지면서 부식이되어 더이상 쓸모없어지는 코드가 되어져 버림니다. 매우 불안정한 상태의 코드가 만들어 집니다. 그래서 아주 유지보수는 생각도 못하는 소모적인 코딩이 됩니다.

그러나 게임프로그램밍처럼 아주 에너지가 넘치는 작업을 하기 위해서는 무턱대고 중성자로 무질서를 제어 한다는것도 방법은 아닙니다.

현재 물리학에서 반물질을 연구하는것처럼 소프트웨어 개발에서도 코루틴같은 반물질을 도입해서 그것을 마음대로 제어만 할수있다면 엄청난 생산성의 향상을 가져올수도 있습니다.


이번에 kgc강연에서 코루틴관련해서 발표를 했었는데 가장 많이 나왔던 질문이 이것 도대체 어따쓰냐는 질문이 많았던거같습니다.(실제 사용예가 궁금해 하시는 분들이 많았습니다^^)


1. 빗나간 탄도의 정리 문제


슈팅게임같은 데서 목표물을 명중시키지못한 총알들은 화면밖으로 사라지게되는데요. 이런것들을 정리하기 위해서 일정시간이되면 메모리상에서 객체를 지워버리는 처리가 필요합니다.




가장 쉽게 생각할수있는 코드가 다음과 같습니다.





델타타임값을 계속 누적시켜서 일정시간이 경과 됐는지를 판단하여 시간이 넘었으면 객체를 지워버리는 코드입니다. 

발사후 2초동안 별일이 없으면 무조건 삭제가되도록 하는 코드입니다.

비록 이정도는 간단한 코드이지만 우리가 요구했던 일에 비하면 매우 번잡스러운 코드가됩니다. 일단 함수 두개에서 하나의 전역 변수(엄밀히 말하면 전역은 아니지만...)에 접근해서 양쪽에서 서로 읽고 쓰기등을 수행합니다.


2. 코루틴으로 문제 해결하기


이것을 코루틴을 써서 구현 해보면 다음과 같습니다.






놀랍지 않습니까? 단 2줄로 똑같은 기능을 구현했습니다. 전역변수를 쓴것도 없으며 따로 두개 이상의 함수에 따로 처리 해줄일도 없고 오직 Start함수에서 그흔한 제어 구문없이 단지 선언식으로 지정을 해준것입니다. 

또한 마치 쓰레드를 쓴것처럼 어떠한 블럭킹도 없습니다.


단, 이것은 실용적인 예는 아니고 Destroy(오브잭트,딜래이시간) 으로도 같은 일을 할수있습니다.


3. 결론


사실 코루틴을 쓰고 안쓰고가 중요한것은 아닙니다. 소프트웨어 개발은 문제 해결의 연속된 고된 작업입니다. 세상에 한가지 해법만 존재하는 문제는 없습니다. 해답에 집중하기 보다는 해법에 집중할수록 좀더 완벽한 소프트웨어가 나오지 않을까 생각합니다. 













반응형
,
Posted by 알 수 없는 사용자

엄청나게 오랜만에 포스팅을 하는군요. 그 덕분에 "예전에 뭐에 대해서 썼었지?" 상태가 되어 버려 다시 본 시리즈를 처음부터 읽어보니 기업가치 산정방식에 대해서 이야기 하다 말았더군요. 게다가 글 말미에는 기업가치 산정방식 세 가지 중에서 마지막 한 가지를 떡밥으로 남겨놓고 기대감을 갖게 하고서는 이렇게 방치를 해버렸습니다. 이제 떡밥이 상할 때도 되었으니, 더 늦기전에 마지막 떡밥을 풀어보겠습니다.

"옛다 떡밥이다~"

사실  실무에서 기업가치를 평가하는 방식은 엄청나게 복잡하기도 하고, 또 평가목적이 무엇이냐에 따라 평가하는 기준이나 방법이 각각 다릅니다. 하지만 이 글에서는 소규모 스타트업 개발사에게 투자여부를 검토하는 투자자들이 평가하는 경우에 한정하며, 또 지나치게 어려운 개념은 빼고 개념 이해를 위주로 설명해볼까 합니다. (사실은 어려운 개념은 저도 뭔지 잘 모른다능...)

그러면 이번회 본론을 시작하기 전에 지난회에서 이야기했던 기업가치 산정방식 세가지를 다시 한 번 짚고 넘어가보도록 하죠.

  1. 자산가치 산정방식 ▶ 회사의 순자산 가치를 측정
  2. 수익가치 산정방식 ▶ 순이익 * 적절한 PER(주가수익률)
  3. 상대가치 산정방식 ▶ 투자대상 기업과 유사한 기업의 가치와 비교

지난번 포스팅에서는 위의 세 가지 방식 중에서 1번과 2번까지 설명을 했었죠. 그래서 1번 자산가치 산정방식은, 초기 스타트업 개발사에 적용하자니, 초기 개발 도중에는 수익이 없이 비용만 계속 소모하는데다 이렇다 할 보유자산이 없기 때문에 기업의 가치를 정확하게 평가할 수 가 없어 적합하지 않다고 했었죠. 그리고 2번의 수익가치 산정방식 또한 초기 스타트업은 아직 제대로 된 수익도 없고 또 비상장 기업이므로 신뢰성 있는 주가를 산정할 수도 없으므로 이 또한 적용하기 곤란한 방법입니다. 따라서 현실적으로는, 마지막 세 번째 방법인 상대가치 산정방식에 기대를 걸 수 밖에 없을 것입니다.

 

▣ 기업가치 평가방법 (3) - 상대가치 산정방식

상대가치 산정방식은 그 명칭에서도 엿볼 수 있듯이, 평가 대상 기업과 유사한 다른 기업을 하나 선정해서 그 기업과 비교를 통해서 평가 대상 기업의 가치를 산정하는 방식입니다. 따라서 스타트업 개발사의 가치를 평가하고자 한다면, 우선 비교 대상 기업을 선정하는 일부터 시작해야 겠죠.

이해를 돕기 위해서 실제 사례를 하나 들어보도록 합니다. 이 사례는 기업금융연구소에서 발간한 <불황에서 살아남는 금융의 기술>이라는 책에서 인용한 것으로 벤처캐피탈이 2000년에 라이코스 코리아의 기업가치를 상대가치 산정방식으로 평가한 예입니다.

라이코스 코리아는 인터넷포털서비스 기업이었습니다. 평가할 당시 국내 인터넷포털 기업은 1위가 야후 코리아였고, 2위는 다음 커뮤니케이션, 3위가 라이코스 코리아였죠. 어라? 네이버는 어디 있죠? 라고 하실 분들이 분명히 있을 텐데요, 2000년 당시 네이버는... 속된 말로 '듣보잡'이었습니다. 예전에 네이버 초창기 멤버였던 어떤 분의 이야기를 들은 적이 있는데, 그 당시에 직원수가 열댓명 남짓이었다고 하더군요. 이렇게 쓰고보니 이거 무슨 청동기 시대 고인돌 쌓던 시대 같이 느껴지는데 불과 십여년 밖엔 안된 이야기입니다. 그럼 잠시 추억을 되살릴 겸 1999년 1월의 네이버 초기화면을 한 번 보고 넘어가시죠.

초기의 국내 인터넷 포털사이트 기업들 가운데 다음커뮤니케이션이 1999년 11월에 코스닥에 상장이 된 상태였으므로, 다음(daum)과 라이코스 코리아를 비교합니다. 그래서 다음의 가치를 100이라고 할 때 라이코스 코리아의 가치가 얼마인가 하는 식으로 산정을 하는데요. 두 기업 모두 인터넷 포털 사업이므로, 가장 중요한 비교 지표는 페이지뷰나 방문자이고, 그 다음으로는 광고매출액, 재무상황, 그리고 향후 사업의 전망 등입니다. 그래서 이러한 지표들을 종합해서 분석한 결과 다음을 100이라고 할 때, 당시 라이코스 코리아는 30정도로 평가되었습니다.

그러면 다음 커뮤니케이션의 기업가치에 30%를 곱하면 라이코스 코리아의 기업가치를 산출할 수 있겠죠? 다음은 이미 1999년도에 상장을 했으므로 기업가치를 쉽게 객관적으로 평가 할 수 있었고 그 가치는 1조 6000억원이었다고 합니다. 따라서 라이코스 코리아는 그 30%인 4800억원으로 평가할 수 있을 것입니다.

평가가 여기서 끝나면 밥 아저씨가 풍경화 그리는 것처럼 참 쉬운 방법일텐데요. 여기서 고려해야 할 사항이 또 있습니다. 여러분도 많이 들어보셨을 만한 용어인 투자 리스크를 고려해야 하는데요. 투자 리스크란 다른 말로 하면 불확실성에 따른 위험입니다. 이게 무슨 소린고 하니, 코스닥에 상장된 기업은 평가시점의 가치가 이미 실현된 것이고, 회사의 지분(주식)을 코스닥 시장에서 언제든지 팔아서 현금화 할 수 있습니다. 하지만 비상장 기업은 앞으로 어떻게 될지 모르죠. 달리 말하면, 비상장기업에 지분을 투자한 투자자의 입장에서는 그 기업이 상장을 하거나 혹은 큰 기업에 인수합병(M&A) 되어 지분투자의 대가로 받은 주식을 매각하기 전까지는 그냥 보유하고 있는 수 밖에 없으며, 투자한 기업이 상장이나 인수합병 되지 못할 경우에는 투자금을 회수할 방법이 사실상 거의 없습니다. 따라서 투자시점에서는 그 기업이 미래에 어떻게 될지 모르는 불확실성이 존재하기 때문에, 투자자는 그러한 불확실성의 위험을 다른 쪽에서 보상 받으려고 하는 것이죠. 그래서 예로 든 라이코스 코리아의 경우에는 25%의 할인율을 적용하여 3600억원으로 기업가치를 평가 받았습니다. - 여기서 할인율(discount rate)의 의미를 좀 더 구체적으로 짚고 넘어가보자면, 기업가치는 4800억원으로 추정되지만, 상장되기 전이라 향후 상장여부가 불확실하다는 위험, 즉 투자 리스크가 존재하기 때문에, 기업가치를 약간 깍는 개념입니다.

 

▣ 투자자의 입장을 이해해야 투자 받을 수 있다

그럼 정리를 해볼까요? 투자자의 입장에서는 투자 여부를 고려할 때, 그리고 투자할 금액의 규모와 투자의 대가로 받는 지분 비율 등을 고려할 때 기업가치를 평가하는데, 그 평가 방법은 다음과 같이 크게 세 가지가 있다고 하였습니다. 귀찮으니까 전편에서 썼던 내용을 복붙하기로 하죠.

  1. 자산가치 산정방식 ▶ 회사의 순자산 가치를 측정
  2. 수익가치 산정방식 ▶ 순이익 * 적절한 PER(주가수익률)
  3. 상대가치 산정방식 ▶ 투자대상 기업과 유사한 기업의 가치와 비교

하지만 이런 평가방법은 지극히 이론적이라서 복잡다단한 실제 기업의 가치를 정확히 측정하기도 어렵고, 특히 스타트업의 경우에는 더욱 적용하기가 곤란하다는 문제가 있습니다. 하지만 그렇다고 해서 이러한 평가방법이 전혀 무의미한 것은 아닙니다. 이와 같은 기본적인 평가방법을 알아두어야만 자기 회사의 현재 상태를 객관적으로 평가할 수 있으며, 또한 회사가 나아가야 할 재무적 전략을 제대로 수립할 수 있기 때문입니다.

혹자는 이런 말을 합니다. "스타트업 게임 개발사라면 일단 만들고자 하는 게임을 잘 만드는 게 중요하고 다른 건 그 다음이다." 물론 맞는 말입니다. 게임 개발사가 게임을 제대로 만들지 못하면, 아무리 재무관리를 잘 해봐야 망하는 건 시간 문제겠죠.

 

 

 

반응형
,
Posted by 밥을먹는선비
반응형
,
Posted by 김포프

다니엘 박사님을 열라 족쳐서 부탁을 해서 얻어왔습니다. 즐감 -_-;



반응형
,
Posted by 대마왕J

수업하다가 이 현상을 발견해서 급히 포스팅합니다.
이렇게 해서 이번달 포스팅을 때우면 다시 16강은 다음 달로 이월되는건가 (...)

요새 또 벌려 놓은 강의/강연이 많아서 ... 물론 회사 일의 강도도 더 늘어났구요. 죄송합니다. 흐규흐규

이전에 포스팅한 3Dmax에서 shaderFX를 제대로 사용하기 라는 포스팅에서 부족한 부분이 있어 더 추가합니다.
3Dmax 2012 에서 Direct3D로 뷰포트를 바꿔서 shaderFX를 사용하면 ... 별 문제가 없긴 합니다만.
알파가 들어간 이미지를 표현할 때 이상한 문제가 일어나게 됩니다.

바로 이렇게 알파 외각이 이상하게 나온다는 거죠.

좀 더 자세히 보겠습니다.

흐음... 일단 원본 텍스쳐보다 좀 밝아 보입니다. 연해 보인다고 할까나?
그리고 알파가 제대로 안 빠진 느낌입니다. 마치 알파 값에다가 강제적으로 1.2 정도 되는 값을 곱해준 느낌이랄까요?

한참동안 고민하다가 저 위에 두 가지 힌트로 답을 일단 찾아냈습니다. 밝게 보인다 -> 1 보다 높은 값을 곱했을 때는 밝아집니다. 알파에다가도 1보다 높은 값을 곱해준다면 위와 비슷한 효과가 나오지 않을까나...

그래서 의심해 본 것이...
Gamma Correction (감마 보정)!!!!
이게 가장 비슷한 느낌의 결과물이 나오니까요.

그래서

Customize / preference 에서 Gamma and LUT 탭에서

Enable Gamma / LUT Correction 을 꺼주니까

뙇하니 해결!!!!

텍스쳐도 더 어두워졌습니다.

이전의 max 2011에서도 Gamma Correction 기능은 있던걸로 알고 있었는데, 뷰포트에서도 이런식으로 적용될줄은 몰랐거든요. 근데 2012 에서는 뷰포트에서도 확실하게 적용되는거구만요. 어쨌건 Gamma Correction 은 Shader 의 색상 연산할 때 중요한 이슈이므로 Shader를 위해서는 끄고 공부하는게 더 맞긴 하겠네요

그치만 어쨌거나 Gamma correction은 그 과정이 눈에 보이는게 아니라서 이래저래 설명할때 어려운 점이 많아요.
그래서 그거 설명은 다음으로 미루고 오늘은 해결책만 제시하고 끝낼께요.

감마 코렉션에 대해 더 알고 싶으시면

http://mgun.tistory.com/1166

이 글이 가장 정리가 잘 되어 있습니다. 여전히 이해하기 쉽지 않긴 하지만...
제 블로그도 링크가 되어 있네요 ㅎㅎ

반응형
,