cocos2dでLive2Dモーション再生

フォーラムでLive2Dのcocos2dでつまづいていた人がいたので、シンプルなモーション再生させてみました。
・[Live2Dコミュニティ - cocos2dx 3.9 mtnファイルの読み込みでエラー](http://forum.live2d.com/discussion/363/cocos2dx-3-9-mtn%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF%E3%81%A7%E3%82%A8%E3%83%A9%E3%83%BC-file-open-error#latest)

# 開発環境
・[Live2D Cocos2d-x 3.5](https://sites.google.com/a/cybernoids.jp/cubism-sdk2/opengl/opengl_dl)のSimpleプロジェクトをカスタム
・Xcode ver 6.4 (Mac 10.10.5 - Yosemite)

# 修正手順
**１）モーションファイルをResoucesフォルダ配下におく**
<img width="208" alt="screenshoto.png" src="https://qiita-image-store.s3.amazonaws.com/0/15028/72436f02-d54d-db89-6cb4-767b5cb02187.png">

**２）SampleLive2DSprite.hのコードを修正**
モーションマネージャーを追加（コメントのADD参照）

```cpp:SampleLive2DSprite.h
#ifndef __sampleCocos2dx__SampleLive2DSprite__
#define __sampleCocos2dx__SampleLive2DSprite__

#include "cocos2d.h"
#include "2d/CCSprite.h"
#include <vector>
#include "Live2DModelOpenGL.h"
#include "motion/Live2DMotion.h"         // ADD
#include "motion/MotionQueueManager.h"   // ADD


class SampleLive2DSprite :public cocos2d::DrawNode {
	
	live2d::Live2DModelOpenGL* live2DModel ;
    live2d::MotionQueueManager* motionManager;  // ADD
	std::vector<cocos2d::Texture2D*> textures ;
    
public:
	SampleLive2DSprite();
	virtual ~SampleLive2DSprite();

	virtual void draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags) override;
	void onDraw(const cocos2d::Mat4 &transform, uint32_t flags);
	static SampleLive2DSprite* createDrawNode();
protected:
	cocos2d::CustomCommand _customCommand;
};

#endif /* defined(__sampleCocos2dx__SampleLive2DSprite__) */
```

**３）SampleLive2DSprite.cppのコードを修正**
モーションロードと再生処理を追加（コメントのADD参照）

```cpp:SampleLive2DSprite.cpp
#include "SampleLive2DSprite.h"
#include "base/CCDirector.h"
#include "util/UtSystem.h"

#include "graphics/DrawProfileCocos2D.h"
#include "platform/CCFileUtils.h"

using namespace live2d;
USING_NS_CC;
const char* TEXTURES[] ={
    "haru/haru_02.1024/texture_00.png" ,
    "haru/haru_02.1024/texture_01.png" ,
    "haru/haru_02.1024/texture_02.png" ,
    NULL
};

AMotion* motion; // モーション ADD

SampleLive2DSprite::SampleLive2DSprite()
{
	//Live2D Sample
	const char* MODEL = "haru/haru_02.moc" ;
	unsigned char* buf;
	ssize_t bufSize;
	buf = FileUtils::getInstance()->getFileData(MODEL,"rb", &bufSize);
    // mocロード
	live2DModel = Live2DModelOpenGL::loadModel( buf,bufSize ) ;
	free(buf);
    
    // Textureロード
	for( int i = 0 ; TEXTURES[i] != NULL ; i++ ){
		auto texture =Director::getInstance()->getTextureCache()->addImage(std::string(TEXTURES[i]));		
		Texture2D::TexParams texParams = { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };
		texture->setTexParameters(texParams);
		texture->generateMipmap();

		int glTexNo = texture->getName() ;

		live2DModel->setTexture( i , glTexNo ) ;// テクスチャとモデルを結びつける
		textures.push_back( texture ) ;
	}

    // Motionロード ADD Start
    const char* MOTION = "haru/motions/tapBody_00.mtn";
    unsigned char* motionbuf;
    ssize_t MotionSize;
    motionbuf = FileUtils::getInstance()->getFileData(MOTION,"rb", &MotionSize);
    // モーションロード
    motion=Live2DMotion::loadMotion(motionbuf,(int)MotionSize);
    // モーションマネージャー生成
    motionManager = new MotionQueueManager();
    // Motionロード ADD End
    
	// Live2Dモデルはローカル座標で左上を原点にして(CanvasWidth、CanvasWidth)
	// のサイズになるため、以下のようにして調整してください。
	float w = Director::getInstance()->getWinSize().width;
	float h = Director::getInstance()->getWinSize().height;
	float scx = 2.0 / live2DModel->getCanvasWidth() ;
	float scy = -2.0 / live2DModel->getCanvasWidth() * (w/h);
	float x = -1 ;
	float y = 1 ;
	float matrix []= {
		scx , 0 , 0 , 0 ,
		0 , scy ,0 , 0 ,
		0 , 0 , 1 , 0 ,
		x , y , 0 , 1
	} ;
	
	live2DModel->setMatrix(matrix) ;// 位置を設定
	
}

SampleLive2DSprite::~SampleLive2DSprite()
{
	delete live2DModel;
	// テクスチャを解放
	Director::getInstance()->getTextureCache()->removeTextureForKey(std::string(TEXTURES[0]));
	Director::getInstance()->getTextureCache()->removeTextureForKey(std::string(TEXTURES[1]));
	Director::getInstance()->getTextureCache()->removeTextureForKey(std::string(TEXTURES[2]));

}


void SampleLive2DSprite::draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags)
{
	DrawNode::draw(renderer, transform, flags);
    
	_customCommand.init(_globalZOrder);
	_customCommand.func = CC_CALLBACK_0(SampleLive2DSprite::onDraw, this, transform, flags);
	renderer->addCommand(&_customCommand);
}

void SampleLive2DSprite::onDraw(const cocos2d::Mat4 &transform, uint32_t flags)
{
	kmGLPushMatrix();
    kmGLLoadMatrix(&transform);
	 
	// モデルのパラメータを変更。動作確認用です。
	double t = (UtSystem::getUserTimeMSec()/1000.0) * 2 * M_PI  ;// 1秒ごとに2π(1周期)増える
//	double cycle=3.0;// パラメータが一周する時間(秒)
//	double value=sin( t/cycle );// -1から1の間を周期ごとに変化する
//	live2DModel->setParamFloat( "PARAM_ANGLE_X" , (float) (30 * value) ) ;// PARAM_ANGLE_Xのパラメータが[cycle]秒ごとに-30から30まで変化する

    // Motion再生 ADD Start
    if(motionManager->isFinished())
    {
        motionManager->startMotion(motion, false);
    }
    motionManager->updateParam(live2DModel);
    // Motion再生 ADD End
    
	// Live2Dの描画前と描画後に以下の関数を呼んでください
	// live2d::DrawProfileCocos2D::preDraw() ;
	// live2d::DrawProfileCocos2D::postDraw() ;
	// これはOpenGLの状態をもとに戻すための処理です。
	// これを行わない場合、Cocos2DかLive2Dどちらかで状態の不整合が起こります。
	live2d::DrawProfileCocos2D::preDraw();
        
	live2DModel->update() ;
	live2DModel->draw() ;
	
	live2d::DrawProfileCocos2D::postDraw() ;

	kmGLPopMatrix();
}

SampleLive2DSprite* SampleLive2DSprite::createDrawNode()
{
	SampleLive2DSprite *ret = new SampleLive2DSprite();
	if (ret && ret->init())
	{
		ret->autorelease();
		return ret;
	}
	else
	{
		CC_SAFE_DELETE(ret);
		return nullptr;
	}
}
```

# 実行結果
これで簡単にモーション再生できました！
![001.gif](https://qiita-image-store.s3.amazonaws.com/0/15028/b80e4a43-50c0-50cf-8c06-82d90b1c1667.gif)