#はじめに
巷ではUE4やUnity5が無料になったりしており、ハイエンドなゲームも
簡単に作れるような世の中になったんだなぁと感心することしきりの今日この頃です。
しかしまだまだコードベースのゲーム作りも現役ですよということで
今回はDXライブラリとミドルウェア3種でなんやかんやするための
導入記事を書いてみたいと思います。
まあ、皆が皆UnityとUEのTips記事を書いていても面白みないってのもありますが…
#目標
DXライブラリ上で3つのミドルウェアの 超最低限の機能 を使用するところまでやります
高度な機能はまた今度。
#準備するもの
導入手順自体は下の方で解説します。とりあえず今回使うものだけ列挙。
###前提として必要なもの
・ Microsoft Visual Studio Express 2013 for Windows Desktop
今回はこいつで開発を行います。インストールしておいてください。
・ DXライブラリ Ver.3.13d
VisualC++用をDL後、解凍して C:\DxLib_VC
に配置してください。
###ミドルウェア類
・ADX2 LE
株式会社CRI・ミドルウェアが提供している無償ミドルウェアです。
音周りの機能を提供します。
・Live2D Cubism
株式会社Live2Dが提供している、キャラクタモーフィング機能が
簡単に行えるミドルウェアです。
・OPTPiX SpriteStudio
株式会社ウェブテクノロジが提供している、スプライトアニメーション機能を
提供するミドルウェアです。
##ミドルウェア類のインストール
・ADX2 LE
ページ左上の「はじめて使う方へ」「アプリを配布する方へ」と
DLページの契約要項あたりをよく読んだ後、
ダウンロードページからWindows版をDLします。
zipがDLできたら解凍して、中身のcriフォルダを適当な場所に保存しておきましょう。
・Live2D Cubism
Live2Dの無償版エディタはこちらからDLできるのですが、
今回必要なのはSDKなので、別途SDK試用申し込み手続きが必要になります。
年間売り上げ1千万円以下であれば無償利用可とのことなので、
ライセンス条件に触れなければ
こちらから小規模事業者向けSDK試用ページに飛びましょう。
諸条件に合意して、アンケートフォームを埋めて送信すると、登録メールアドレス宛に
SDKのDL方法が書かれているはずです。
今回は「DirectX」版のSDKを使用することにします。
このSDKも解凍してどこかへ保存しておきましょう。
・OPTPiX SpriteStudio
エディタに関しては、Live2Dと同じくユーザ登録する方式みたいです。
今回使うのはSDK内の補助サンプルなので、ユーザ登録が必須かどうか微妙ですが
まあ登録しておいた方がいいでしょう。
ライセンスをよく読んで、「お申込み・お問い合わせ」から登録してください。
なおこちらも年間売り上げ1千万円以下であれば無償使用できますが、
別途利用期間があるとのこと。
DXライブラリでの開発に使用するSpriteStudio 5 SDKのサンプルは
こちらからDL可能です。
いくつかブランチがあるようですが、masterブランチので大丈夫でしょう。たぶん…
Gitから直にDLするのでなければ、ページ右下の「Download ZIP」をクリックでOKです。
これも解凍したらどこかへ保存します。
#プロジェクト設定
けっこうしんどいです。
とりあえず、まずはこのページを参考にして、DXライブラリ用のVS2013プロジェクト設定を行ってください。
ビルドして正常に動作確認できればよし。
###プロジェクトフォルダ設定
・ADX2 LE
先ほど解凍した「cri」フォルダを、プロジェクト直下にそのままコピーします。
実質今回必要になるのは「cri/pc/」以下になります。
その後、「cri/pc/libs/x86/cri_ware_pcx86_LE.dll」をプロジェクト直下にコピーします。
・Live2D Cubism SDK
先ほど解凍したフォルダの中の「include」と「lib」フォルダを、プロジェクト直下にそのままコピーします。
それと、「sample/Simple/res/」フォルダをプロジェクト直下にコピーします。
プロジェクト直下に「include」「lib」「res」の3つのフォルダがコピーされることになります。
・SpriteStudio 5 SDK
先ほど解凍したフォルダの中の「samples/ssbpLib/basic/SSPlayer/」フォルダを、プロジェクト直下にそのままコピーします。
それと、「samples/ssbpLib/basic/character_template_comipo」フォルダをプロジェクト直下にコピーします。
プロジェクト直下に「SSPlayer」「character_template_comipo」の2つのフォルダがコピーされることになります。
最後に、コピーしたフォルダ内の全てのソースコードを Unicode、改行コードCR/LF で保存し直しておきます。
なんかツールを使うと楽かもしれません。
※unicodeに直さないとビルド時にエラーが出る
###プロジェクト設定
・プロジェクトのプロパティを開き、左上の「構成」を「すべての構成」に変更
・「構成プロパティ」→「C/C++」→「全般」→「追加のインクルード ディレクトリ」に以下を追加
C:\DxLib_VC\プロジェクトに追加すべきファイル_VC用;./include;$(DXSDK_DIR)\Include;./cri/pc/include
・「構成プロパティ」→「C/C++」→「プリプロセッサ」→「プリプロセッサの定義」に以下を追加
L2D_TARGET_D3D;
・「構成プロパティ」→「リンカー」→「全般」→「追加のライブラリディレクトリ」に以下を追加
C:\DxLib_VC\プロジェクトに追加すべきファイル_VC用;./lib;$(DXSDK_DIR)\Lib\x86;./cri/pc/libs/x86
・「構成プロパティ」→「リンカー」→「入力」→「追加の依存ファイル」に以下を追加
cri_ware_pcx86_LE_import.lib;d3d9.lib;d3dx9.lib;live2d_directX_mt.lib;
・「適用」を押して「OK」をクリック。
・ソリューションエクスプローラーのソースファイルのところを右クリックして「追加」→「既存の項目」を選択
・「SSPlayer」以下のファイル、「SSPlayer/common」以下のファイルを全て追加。
・その他の設定はDXライブラリの設定に準じます
#ソースコード
やっとこさ設定が終わったので、本丸のソースコードです。
正直あまりまとまってないので見難くて申し訳ないですが、
関数名から何のミドルウェアを使って何やってるかぐらいは判断できるかと思います。
ちゃんと使うなら、どれもクラス化・ラップ化必須だと思います。
//********************インクルード****************************
#include "DxLib.h"
//Live2D フレームワーク
#include <D3D9.h>
#include <Live2D.h>
using namespace live2d;
#include <Live2DModelD3D.h>
#include <util/UtSystem.h>
//ADX2LE
#include <cri_adx2le.h>
#include "cri/pc/samples/criatom/data/Public/ADX2_samples_acf.h"
//SpriteStudio
#include "SSPlayer\SS5Player.h"
//********************定義****************************
// SpriteStudio フレーム待ち
#define WAIT_FRAME (16)
// ADX2LE 最大ボイス数を増やすための関連パラメータ
#define MAX_VOICE (24)
#define MAX_VIRTUAL_VOICE (64) /* ざっくり多め(通常ボイス+HCA-MXボイス+α) */
#define MAX_CRIFS_LOADER (64) /* ざっくり多め(通常ボイス+HCA-MXボイス+α) */
// ADX2LE 最大サンプリングレート(ピッチ変更含む)
#define MAX_SAMPLING_RATE (48000*2)
// ADX2LE HCA-MXコーデックのサンプリングレート
#define SAMPLINGRATE_HCAMX (32000)
#define X_POS_OFFSET_FOR_MAP_DRAW (20)
#define PITCH_CHANGE_VALUE (-200.0f)
// 画面サイズ
#define SCREEN_SIZE_X 800
#define SCREEN_SIZE_Y 600
//********************グローバル変数群****************************
// Live2d
Live2DModelD3D* live2DModel;
LPDIRECT3DDEVICE9 g_Device9;
// SS5プレイヤー
ss::Player *ssplayer;
ss::ResourceManager *resman;
static int previousTime;
static int waitTime;
int mGameExec;
// ADX2LE アプリケーション構造体
typedef struct ADX2LE_AppTag{
CriAtomExPlayerHn player; /* 再生プレーヤ */
CriAtomExVoicePoolHn standard_voice_pool; /* ボイスプール(ADX/HCAコーデック用) */
CriAtomExVoicePoolHn hcamx_voice_pool; /* ボイスプール(HCA-MX用) */
CriAtomExAcbHn acb_hn; /* ACBハンドル(音声データ) */
CriAtomDbasId dbas_id; /* D-BASの作成*/
CriAtomExPlaybackId playback_id; /* VoiceキューのプレイバックID(再生開始時に保持する) */
CriSint32 ui_cue_idnex; /* ユーザ選択中のキュー */
} ADX2LE_AppObj;
//********************関数宣言****************************
void Live2D_Render2D();
bool Live2D_Create();
void ADX2LE_Initialize(ADX2LE_AppObj& appobj);
void ADX2LE_Finalize(ADX2LE_AppObj& appobj);
void ADX2LE_Load(ADX2LE_AppObj& appobj);
void ADX2LE_Server();
long long ADX2LE_GetTime(CriAtomExPlaybackId playback_id);
void ADX2LE_Play(ADX2LE_AppObj& appobj, CriAtomExCueId start_cue_id);
void ADX2LE_StopPlayer(CriAtomExPlayerHn player);
void ADX2LE_StopCue(CriAtomExPlaybackId playback_id);
void SpriteStudio_Init(void);
void SpriteStudio_Load();
void SpriteStudio_Update(float dt);
void SpriteStudio_Relese(void);
//********************関数群****************************
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
int x, add;
//********************初期化****************************
// タイトルを test に変更
SetMainWindowText("Test_exe");
// ウインドウモードに変更
ChangeWindowMode(TRUE);
// 描画先を裏画面に変更
SetDrawScreen(DX_SCREEN_BACK);
// Direct3D9Ex ではなく Direct3D9 を使う
SetUseDirect3D9Ex(FALSE);
// 画面解像度の設定
SetGraphMode(SCREEN_SIZE_X, SCREEN_SIZE_Y, 32);
// DXライブラリ初期化
if (DxLib_Init() == -1)
return -1;
// Live2D 初期化
Live2D::init();
// ADX2LE 初期化
ADX2LE_AppObj adx2le_obj;
ADX2LE_Initialize(adx2le_obj);
//SpriteStudio初期化
SpriteStudio_Init();
// DXライブラリが使用している Direct3DDevice9 を取得
g_Device9 = (LPDIRECT3DDEVICE9)GetUseDirect3DDevice9();
//********************ロード&登録****************************
// フォントサイズの変更
SetFontSize(20);
// Live2D データの読み込み
if (!Live2D_Create())
return -1;
// ADX2LE データの読み込み
ADX2LE_Load(adx2le_obj);
// ADX2LEで再生
ADX2LE_Play(adx2le_obj, 0);
// SpriteStudio データの読み込み&登録
SpriteStudio_Load();
// 左右に動く文字列の変数を初期化
x = 0;
add = 8;
// SpriteStudio メインループ用変数初期化
mGameExec = 1;
previousTime = GetNowCount();
// 描画先を裏画面に変更
SetDrawScreen(DX_SCREEN_BACK);
//********************メインループ****************************
while (mGameExec == 1)
{
// 画面をクリア
ClearDrawScreen();
// 左右に動く文字列を動かす
x += add;
if (x < 0 || x > SCREEN_SIZE_X)
{
add = -add;
}
// Live2D で描くキャラクターの奥に"Live2D"と描画
DrawString(x, 64, "Live2D", GetColor(255, 255, 255));
// Live2D の描画
Live2D_Render2D();
// Live2D で描くキャラクターの手前にも"Live2D"と描画
DrawString(SCREEN_SIZE_X - x, SCREEN_SIZE_Y - 100, "Live2D", GetColor(255, 255, 255));
SpriteStudio_Update((float)waitTime / 1000.0f);
// 裏画面を表画面に転送
ScreenFlip();
//ADX2LEのサーバ処理
ADX2LE_Server();
//SpriteStudioのサーバ処理。フレーム単位で時間指定の必要があるため
waitTime = GetNowCount() - previousTime;
previousTime = GetNowCount();
if (waitTime < WAIT_FRAME)
{
WaitTimer((WAIT_FRAME - waitTime));
}
else
{
//ESC押すか処理失敗したら終了
if (ProcessMessage() == -1 || CheckHitKey(KEY_INPUT_ESCAPE)) mGameExec = 0;
}
}
//********************終了処理****************************
// SpriteStudio 終了処理
SpriteStudio_Relese();
// ADX2LE 終了処理
ADX2LE_StopPlayer(adx2le_obj.player);
ADX2LE_Finalize(adx2le_obj);
// DXライブラリ使用の終了処理
DxLib_End();
// ソフトの終了
return 0;
}
//*************Live2D関連関数*******************************
//Live2Dで描画
void Live2D_Render2D()
{
// モデルが初期化されていなければ何もしない
if (live2DModel == NULL) return;
// DXライブラリの描画処理を完了させる
RenderVertex();
// Live2D の描画
{
// 射影行列の設定
D3DXMATRIX Ortho2D;
float modelWidth = live2DModel->getModelImpl()->getCanvasWidth();
float modelHeight = live2DModel->getModelImpl()->getCanvasHeight();
D3DXMatrixOrthoLH(&Ortho2D, modelHeight, -modelHeight *SCREEN_SIZE_Y / SCREEN_SIZE_X, -1.0f, 10.0f);
g_Device9->SetTransform(D3DTS_PROJECTION, &Ortho2D);
// ビュー行列の設定
D3DXMATRIX Identity;
D3DXMatrixIdentity(&Identity);
g_Device9->SetTransform(D3DTS_VIEW, &Identity);
// ワールド行列の設定
D3DXMATRIXA16 world;
D3DXMatrixTranslation(&world, -modelWidth / 2, -modelHeight / 2, 0);
g_Device9->SetTransform(D3DTS_WORLD, &world);
// --- モデルのパラメータを直接設定するサンプル
double t = (UtSystem::getUserTimeMSec() / 1000.0) * 2 * 3.14;// 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まで変化する
// --- 描画
live2DModel->update();
live2DModel->draw();
}
// DXライブラリの Direct3D9 の設定を復帰させる
RefreshDxLibDirect3DSetting();
}
//Live2D準備
bool Live2D_Create()
{
LPCWSTR TEXTURES[3] =
{
L"res/haru/haru.1024/texture_00.png",
L"res/haru/haru.1024/texture_01.png",
L"res/haru/haru.1024/texture_02.png"
};
live2DModel = Live2DModelD3D::loadModel("res/haru/haru.moc");
live2DModel->setDevice(g_Device9);
// テクスチャのロード
for (int i = 0; i < 3; i++){
LPDIRECT3DTEXTURE9 tex;
// テクスチャ画像をDirextXでの表示用に変換
if (FAILED(D3DXCreateTextureFromFileExW(g_Device9
, TEXTURES[i]
, 0 //width
, 0 //height
, 0 //mipmap //( 0なら完全なミップマップチェーン)
, 0 //Usage
, D3DFMT_A8R8G8B8
, D3DPOOL_DEFAULT
, D3DX_FILTER_LINEAR
, D3DX_FILTER_BOX
, 0
, NULL
, NULL
, &tex)))
{
live2d::UtDebug::print("Could not create texture");
return false;
}
else
{
// 変換が成功したらセット
live2DModel->setTexture(i, tex);
}
}
return true;
}
//*************ADX2LE関連関数*******************************
// エラーコールバック関数
static void user_error_callback_func(const CriChar8 *errid, CriUint32 p1, CriUint32 p2, CriUint32 *parray)
{
const CriChar8 *errmsg;
/* エラー文字列の表示 */
errmsg = criErr_ConvertIdToMessage(errid, p1, p2);
printf(errmsg);
return;
}
// ユーザ定義のアロケータ
void *user_alloc_func(void *obj, CriUint32 size)
{
void *ptr;
ptr = malloc(size);
return ptr;
}
/// ユーザ定義のfree
void user_free_func(void *obj, void *ptr)
{
free(ptr);
}
//初期化
void ADX2LE_Initialize(ADX2LE_AppObj& appobj)
{
/* カーソルのリセット */
appobj.ui_cue_idnex = 0;
/* 未取得なプレイバックID(Voiceキュー再生時のみ取得) */
appobj.playback_id = 0;
/* エラーコールバック関数の登録 */
criErr_SetCallback(user_error_callback_func);
/* メモリアロケータの登録 */
criAtomEx_SetUserAllocator(user_alloc_func, user_free_func, NULL);
/* ライブラリの初期化(最大ボイス数変更) */
CriAtomExConfig_PC lib_config;
CriFsConfig fs_config;
criAtomEx_SetDefaultConfig_PC(&lib_config);
criFs_SetDefaultConfig(&fs_config);
lib_config.atom_ex.max_virtual_voices = MAX_VIRTUAL_VOICE;
lib_config.hca_mx.output_sampling_rate = SAMPLINGRATE_HCAMX;
fs_config.num_loaders = MAX_CRIFS_LOADER;
lib_config.atom_ex.fs_config = &fs_config;
criAtomEx_Initialize_PC(&lib_config, NULL, 0);
/* D-Basの作成(最大ストリーム数はここで決まります) */
appobj.dbas_id = criAtomDbas_Create(NULL, NULL, 0);
/* ACFファイルの読み込みと登録 */
criAtomEx_RegisterAcfFile(NULL, "cri/pc/samples/criatom/data/Public/ADX2_samples.acf", NULL, 0);
/* DSPバス設定の登録 */
criAtomEx_AttachDspBusSetting("DspBusSetting_0", NULL, 0);
/* ボイスプールの作成(最大ボイス数変更/最大ピッチ変更/ストリーム再生対応) */
CriAtomExStandardVoicePoolConfig standard_vpool_config;
criAtomExVoicePool_SetDefaultConfigForStandardVoicePool(&standard_vpool_config);
standard_vpool_config.num_voices = MAX_VOICE;
standard_vpool_config.player_config.max_sampling_rate = MAX_SAMPLING_RATE;
standard_vpool_config.player_config.streaming_flag = CRI_TRUE;
appobj.standard_voice_pool = criAtomExVoicePool_AllocateStandardVoicePool(&standard_vpool_config, NULL, 0);
/* HCA-MX再生用:ボイスプールの作成 */
CriAtomExHcaMxVoicePoolConfig hcamx_vpool_config;
criAtomExVoicePool_SetDefaultConfigForHcaMxVoicePool(&hcamx_vpool_config);
hcamx_vpool_config.num_voices = MAX_VOICE;
hcamx_vpool_config.player_config.max_sampling_rate = MAX_SAMPLING_RATE;
hcamx_vpool_config.player_config.streaming_flag = CRI_TRUE;
appobj.hcamx_voice_pool = criAtomExVoicePool_AllocateHcaMxVoicePool(&hcamx_vpool_config, NULL, 0);
}
//終了
void ADX2LE_Finalize(ADX2LE_AppObj& appobj)
{
/* DSPのデタッチ */
criAtomEx_DetachDspBusSetting();
/* プレーヤハンドルの破棄 */
criAtomExPlayer_Destroy(appobj.player);
/* ボイスプールの破棄 */
criAtomExVoicePool_Free(appobj.hcamx_voice_pool);
criAtomExVoicePool_Free(appobj.standard_voice_pool);
/* ACBハンドルの破棄 */
criAtomExAcb_Release(appobj.acb_hn);
/* ACFの登録解除 */
criAtomEx_UnregisterAcf();
/* D-BASの破棄 */
criAtomDbas_Destroy(appobj.dbas_id);
/* ライブラリの終了 */
criAtomEx_Finalize_PC();
}
//データロード&登録
void ADX2LE_Load(ADX2LE_AppObj& appobj)
{
/* サウンドデータの読み込み */
appobj.acb_hn = criAtomExAcb_LoadAcbFile(NULL, "cri/pc/samples/criatom/data/Public/Basic.acb", NULL, "cri/pc/samples/criatom/data/Public/Basic.awb", NULL, 0);
/* プレーヤハンドルの作成 */
appobj.player = criAtomExPlayer_Create(NULL, NULL, 0);
}
//サーバ処理
void ADX2LE_Server()
{
/* サーバ処理の実行 */
criAtomEx_ExecuteMain();
}
//再生時間取得
long long ADX2LE_GetTime(CriAtomExPlaybackId playback_id)
{
/* 再生時刻の取得(ms) */
return criAtomExPlayback_GetTime(playback_id);
}
//再生開始
void ADX2LE_Play(ADX2LE_AppObj& appobj, CriAtomExCueId start_cue_id)
{
/* キューIDの指定 */
criAtomExPlayer_SetCueId(appobj.player, appobj.acb_hn, start_cue_id);
/* 再生の開始 */
appobj.playback_id = criAtomExPlayer_Start(appobj.player);
}
//停止
void ADX2LE_StopPlayer(CriAtomExPlayerHn player)
{
/* プレーヤの停止 */
criAtomExPlayer_Stop(player);
}
//キューIDを指定してストップ
void ADX2LE_StopCue(CriAtomExPlaybackId playback_id)
{
/* 特定の再生音のみ停止 */
criAtomExPlayback_Stop(playback_id);
}
//*************SpriteStudio関連関数*******************************
//初期化
void SpriteStudio_Init()
{
//リソースマネージャの作成
resman = ss::ResourceManager::getInstance();
//プレイヤーの作成
ssplayer = ss::Player::create();
}
//データロード&登録
void SpriteStudio_Load()
{
//アニメデータをリソースに追加
//それぞれのプラットフォームに合わせたパスへ変更してください。
resman->addData("character_template_comipo/character_template1.ssbp");
//プレイヤーにリソースを割り当て
ssplayer->setData("character_template1"); // ssbpファイル名(拡張子不要)
//再生するモーションを設定
ssplayer->play("character_template_3head/stance"); // アニメーション名を指定(ssae名/アニメーション名も可能、詳しくは後述)
//表示位置を設定
ssplayer->setPosition(SCREEN_SIZE_X / 2 - 250, SCREEN_SIZE_Y - 200);
//スケール設定
ssplayer->setScale(0.5f, 0.5f);
//回転を設定
ssplayer->setRotation(0.0f, 0.0f, 0.0f);
//透明度を設定
ssplayer->setAlpha(255);
//反転を設定
ssplayer->setFlip(false, false);
}
bool push = false;
int count = 0;
bool pause = false;
//更新
void SpriteStudio_Update(float dt)
{
char str[128];
//パーツ名から座標の取得を行う
//パーツのステータスを更新するため、指定フレームから情報を取る場合は、描画前にupdateを予備出すこと。
ss::ResluteState result;
ssplayer->getPartState(result, "body");
//プレイヤーの更新、引数は前回の更新処理から経過した時間
ssplayer->update(dt);
//プレイヤーの描画
ssplayer->draw();
}
//終了
void SpriteStudio_Relese(void)
{
//テクスチャの解放
resman->releseTexture("character_template1");
//SS5Playerの削除
delete (ssplayer);
delete (resman);
}
カオスな絵面が出てきたら成功です。
#参考
→Live2Dのリンクするライブラリですが、プロジェクト設定をMT.MTdにしている場合は live2d_directX_mt.lib;
と記述しないとエラーになります。
→あと、読み込むデータのパスが間違っていたりすると実行時エラーで死にます。
パスは要確認!
→「サンプル」→「サンプル共通事項」→「Visual Studioプロジェクト設定」とか
「CRI ADX2 ユーザーズマニュアル」→「ADX2クイックスタート」→「[ゲームへの組み込み]編」に
インクルードが必要なヘッダファイルは /cri/pc/include/cri_adx2le_pc.h です。
という記述がありますが、恐らく cri_adx2le.h
の間違いでしょう…
→先ほども書きましたがUnicodeに変換しないとビルドできない部分でハマりました。
(私の環境依存な問題かも)
#おわりに
や
D
神