DXライブラリを使ってLive2Dモデルを動かし、描画しよう

## はじめに
本記事はかの有名なDirectX APIを簡易化したライブラリ「DXライブラリ」を使用して、現在流行のLive2Dモデルを描画し、さらにモーションを制御する簡単なやり方を紹介します。

## 目次
1. [Visual Studioの準備](#Visual-Studioの準備)
2. [DXライブラリの導入](#DXライブラリの導入)
3. [Live2D Cubism 4 SDKとLive2Dモデルの導入](#Live2D-Cubism-4-SDKとLive2Dモデルの導入)
4. [コーディング](#コーディング)
5. [最終成果](#最終成果)
6. [参考文献](#参考文献)


# Visual Studioの準備
- Microsoft公式サイトからVisual Studio Communityをダウンロードし、インストール
https://visualstudio.microsoft.com/ja/downloads/
（IDEは何でもOKですが、今回はVisual Studioでご紹介します）

![スクリーンショット 2024-04-18 232011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/2f55b592-9527-8e24-b718-89aaeb31f36d.png)

初期セットアップする時に必ず入れて欲しいのは「ユニバーサルWindowsプラットフォーム開発」

![スクリーンショット 2024-04-18 232341.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/4b9c477b-dbbb-af4d-9b11-073f2dcd1049.png)


# DXライブラリの導入
- DXライブラリの公式ページからSDKをダウンロード
https://dxlib.xsrv.jp/dxdload.html

![スクリーンショット 2024-04-18 233013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/09101d5c-e64f-e426-a270-d183e1c66ef8.png)


# Live2D Cubism 4 SDKとLive2Dモデルの導入
- DXライブラリの公式ページも導入手順掲載してます
https://dxlib.xsrv.jp/use/dxuse_live2D_cubism4_windows.html#R1

- DXライブラリ Windows版の Live2D 関係の関数を使うには、Live2D Cubism 4 SDK for Native の中にあるLive2DCubismCore.dll が必要なので、以下のダウンロードページから Cubism 4 SDK for Native をダウンロードしてください
https://www.live2d.com/en/sdk/about/

![スクリーンショット 2024-04-18 231552.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/cb122708-b000-3115-2edd-91b745afaa57.png)

- サンプルデータをダウンロード
元々私が使用するモデルは当時無料配布だった「ミアラ」だが、この記事を執筆した現在は有料になりましたので、他の無料モデルを使用してください。
https://www.live2d.com/learn/sample/

![スクリーンショット 2024-04-19 005434.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/b0fc9749-2b2c-cfcc-9cfb-26f5022358c4.png)


- Visual Studioの設定

▼DXライブラリSDKのパス設定（すべての構成に設定）
パスは相対パス、絶対パスどちらでも構いません

![スクリーンショット 2024-04-18 224806.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/d051a5d5-8c5f-7245-8f02-919860277657.png)

![スクリーンショット 2024-04-18 224817.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/579e4341-5bab-c529-96e5-af4cc620372d.png)

▼リンカーのシステムを「Windows（/SUBSYSTEM:WINDOWS）に設定」
![スクリーンショット 2024-04-18 224826.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/a7dcf0b9-5920-fab6-59b4-48eddf440ebd.png)

▼Live2D Cubism 4 SDKをプロジェクトのディレクトリ下に入れる（プロジェクトディレクトリ以外のローカル外部でも良いですが、ファイルをどこかにクローンする時に参照がずれて、また再設定しないといけない）
![スクリーンショット 2024-04-18 233650.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/e2ae4a1f-d352-8b40-82b7-12e97775b6e1.png)


# コーディング

- まずはLive2Dモデルを読み込む準備をします
```C++:Live2D.h
#pragma once
#include"DxLib.h"

class Live2D
{
public:
	Live2D(int modelHandle);
	~Live2D();
	void Initialize();
	void Load(const TCHAR *filePath);
	void Update(const TCHAR* motion);
	void DrawBegin();
	void DrawModel();
	void DrawEnd();
	void DeleteModel();

private:
	int ModelHandle;
};
```

```C++:Live2D.cpp
#include"Live2D.h"

Live2D::Live2D(int modelHandle)
	:ModelHandle(modelHandle)
{
	
}

Live2D::~Live2D()
{

}

void Live2D::Initialize()
{

#ifdef _WIN64
	Live2D_SetCubism4CoreDLLPath(L"../CubismSdkForNative-4-r.1/Core/dll/windows/x86_64/Live2DCubismCore.dll");
#else
	Live2D_SetCubism4CoreDLLPath(L"../CubismSdkForNative-4-r.1/Core/dll/windows/x86/Live2DCubismCore.dll");
#endif

}

void Live2D::Load(const TCHAR* filePath)
{
	// Live2Dモデルの読み込み
	ModelHandle = Live2D_LoadModel(filePath);
}

void Live2D::Update(const TCHAR* motion)
{
	// モーション再生が終了していたらアイドリングモーションをランダムで再生
	if (Live2D_Model_IsMotionFinished(ModelHandle) == TRUE)
	{
		Live2D_Model_StartMotion(ModelHandle, motion, GetRand(8));
	}

	// モデルの状態を60分の1秒分進める
	Live2D_Model_Update(ModelHandle, 1 / 60.0f);
}

void Live2D::DrawBegin()
{
	// Live2D描画の開始
	Live2D_RenderBegin();
}

void Live2D::DrawModel()
{
	// モデルの描画
	Live2D_Model_Draw(ModelHandle);
}

void Live2D::DrawEnd()
{
	// Live2D描画の終了
	Live2D_RenderEnd();
}

void Live2D::DeleteModel()
{
	// Live2D モデルの削除
	Live2D_DeleteModel(ModelHandle);
}
```

- おまけに入力系のクラスも書いておきます
```C++:Input.h
#pragma once
#include"DxLib.h"

class Input
{
public:
	static char currentKey[256];
	static char previousKey[256];
	static int mousePosX, mousePosY, mouseLeftButt, mouseRightButt, mouseMidButt;
public:
	static void Mouse(int*, int*);
	static int MouseLeftButton();
	static int MouseRightButton();
	static int MouseMiddleButton();
	static int Key(int key);
	static bool KeyTrigger(int key);
	static void Update();
	static void Draw();
};
```

```C++:Input.cpp
#include"Input.h"

int Input::mousePosX = 0;
int Input::mousePosY = 0;
int Input::mouseLeftButt = 0;
int Input::mouseRightButt = 0;
int Input::mouseMidButt = 0;
char Input::currentKey[256] = { 0 };
char Input::previousKey[256] = { 0 };

void Input::Update() 
{
	GetMousePoint(&mousePosX, &mousePosY);
	if ((GetMouseInput() & MOUSE_INPUT_LEFT) != 0) mouseLeftButt++;
	else mouseLeftButt = 0;
	if ((GetMouseInput() & MOUSE_INPUT_RIGHT) != 0) mouseRightButt++;
	else mouseRightButt = 0;
	if ((GetMouseInput() & MOUSE_INPUT_MIDDLE) != 0) mouseMidButt++;
	else mouseMidButt = 0;

	memcpy(previousKey, currentKey, sizeof(previousKey));

	GetHitKeyStateAll(currentKey);
}

void Input::Mouse(int* x, int* y) 
{
	*x = mousePosX;
	*y = mousePosY;
}

int Input::MouseLeftButton() { return mouseLeftButt; }
int Input::MouseRightButton() { return mouseRightButt; }
int Input::MouseMiddleButton() { return mouseMidButt; }
int Input::Key(int key) { return currentKey[key]; }
bool Input::KeyTrigger(int key) { return !previousKey[key] && currentKey[key]; }

void Input::Draw() {}

```

- 次にゲームの環境を整う
```C++:Game.h
#pragma once
#include"DxLib.h"

class Game 
{
public:
	Game();
	~Game();
	void Initialize();
	void Load();
	void Update();
	void Draw();

public:
	int LoadTexture(TCHAR* fileName) 
	{
		int tex = LoadGraph(fileName);
		if (tex == -1) 
        {
			MessageBox(NULL, L"画像の読み込みに失敗しました", ERROR, MB_OK);
			return 0;
		}
	};
	
private:
	int texture;
	float texturePosX;
	float texturePosY;
	bool controllSwitch;
};

```

```Game.cpp
#include"Game.h"
#include"Input.h"

Game::Game() 
{
	this->Initialize();
}

Game::~Game() 
{

}

void Game::Initialize() 
{
	texture = NULL;
	texturePosX = 600;
	texturePosY = 400;
	controllSwitch = false;
}

void Game::Load() 
{

}

void Game::Update() 
{
	if (!controllSwitch) 
	{
		texturePosX = 600;
		texturePosY = 400;
	}
	else
	{
		texturePosX = 800;
		texturePosY = 600;
	}
	if (Input::Key(KEY_INPUT_SPACE) == 1) 
    {
		controllSwitch = !controllSwitch;
	}

}

void Game::Draw() 
{

}
```
- 最後にモデルを描画し、スペースキーで動作変えましょう
```Main.cpp
#include"Game.h"
#include"Input.h"
#include"Live2D.h"

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
	// ウィンドウモードに設定
	ChangeWindowMode(TRUE);

	SetMainWindowText(L"Live2D Test Program");

	// 画面解像度を設定
	SetGraphMode(1280, 720, 0);

	// ウィンドウサイズを手動では変更できず、
    // かつウィンドウサイズに合わせて拡大できないようにする
	SetWindowSizeChangeEnableFlag(FALSE, FALSE);

	bool motionChange = false;

	int live2DHandle = 0;

	Live2D* live2D = new Live2D(live2DHandle);	

	live2D->Initialize();

	// ＤＸライブラリ初期化処理
	if (DxLib_Init() == -1) return -1;

	Game game;
	game.Initialize();
	game.Load();

	live2D->Load(L"../Assets/miara_pro_jp/runtime/miara_pro_t04.model3.json");

	// 描画先を裏画面に変更
	SetDrawScreen(DX_SCREEN_BACK);

	// メインループ
	while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0)
	{
		Input::Update();

		game.Update();
		game.Draw();

		if (Input::KeyTrigger(KEY_INPUT_SPACE))
		{
			motionChange = !motionChange;
		}

		if (!motionChange)
		{
			live2D->Update(L"Idle");
		}
		else
		{
			live2D->Update(L"Flick");
		}

		live2D->DrawBegin();

		live2D->DrawModel();

		live2D->DrawEnd();

		if (CheckHitKey(KEY_INPUT_ESCAPE) != 0) break;
	}

	live2D->DeleteModel();

	delete live2D;

	// ＤＸライブラリ使用の終了処理
	DxLib_End();

	// ソフトの終了
	return 0;
}
```

- Live2Dモデルモーションに関してですが、ご自身がダウンロードしたサンプルデータのなかの「runtime」フォルダの中にある「OOO.model3.json」のような名前のファイルから見えます。

![スクリーンショット 2024-04-19 002748.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/3ad0e22f-4b51-45c3-1bd1-467fd0c92eee.png)

▲私の場合IdleとFlickを切り替えて使用してます


# 最終成果

![miara.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770983/08f7c877-7b3a-4853-e358-5c04768ad341.gif)



# 参考文献
- [Live2D サンプルデータ集（無料配布）](https://www.live2d.com/learn/sample/)
- [ＤＸライブラリ　関数リファレンスページ](https://dxlib.xsrv.jp/dxfunc.html)
- [ＤＸライブラリ　関数リファレンスページ　Live2D関係関数リファレンス](https://dxlib.xsrv.jp/function/dxfunc_live2D.html)