どーもRCC2回生のWGGです.
RCCアドベントカレンダー2回目の投稿となります.
デスクトップマスコット
皆さん使ったことありますか? 僕はないです.
例ですが,↓こんな感じのものです(獏さんの作品)
http://www.nicovideo.jp/watch/sm27032470?via=thumb_watch&at=title&state=started
キャラクターがデスクトップに常駐して,いろいろやってくれる感じですね
今回はこのデスクトップマスコットをMMDモデルと使って基盤だけ作ってみます
あくまでデスクトップマスコットとしてのシステムの基盤だけなので,↑の動画みたいな機能までは実装していません.
デスクトップマスコットを実現するために必要なもの
透明なウィンドウを作成- そこにキャラクターを描画
- クリックなどのアクションに応答
こんな感じですね~
てことで実装してみました
今回使用するモデルはアールビット式蓮子(http://www.nicovideo.jp/watch/sm15876388 帽子被ってる方)と,TDA式ミクに同梱されていたモーションを使ってます
まぁこの辺りは動作確認のために適当なものを選んだだけです.
完成動画
まぁこんな感じです
軽く解説
詳しくは後ろのソースコードを見てください
まず,DxLibで3Dを扱う流れですが
- 3D描画モードをオン
- モデルなどの読み込み
- カメラ,ライトの初期設定
- ↓メインループ
- カメラ,ライトの移動
- モデル位置の決定
- モデルの描画
- ↑メインループ終わり
大体こんな感じになっています.
今回はライトに関しては弄ってません
また,デスクトップマスコットとして機能させるために,モデルを描画している部分以外を透明にする処理を行っています
DxLib::SetWindowStyleMode(2); // ウィンドウのスタイルを枠無しに指定 DxLib::SetUseBackBufferTransColorFlag(TRUE); // ウィンドウを透明に指定
MMDモデルの読み込みはMV1LoadModel関数でできますが,この関数にはモーションデータを自動で読み込んでくれる機能があります
“モデルデータの拡張子を除いたファイル名”+”3桁以上の数字”+”モーションデータの拡張子”のモーションファイルを同階層から勝手に読み込んでくれます.
数字の後ろにLがつくとループモーションです
model = DxLib::MV1LoadModel("model/renko.pmx");//モデルデータの読み込み //この場合,renko000.vmd,renko001L.vmdなどが読み込まれる
モデルのクリック判定にはGetMouseInput関数を使用しています.
本来であれば,http://dxlib.o.oo7.jp/function/dxfunc_3d.html#R12N11 のように,Z深度を貫くレイを照射して,モデルとの接触判定をおこなうべきなのですが,今回のように描画対象がモデルだけのデスクトップマスコットでは,モデル以外の領域をクリックするとアクティブが外れ,更にGetMouseInput()も0を返します.
今回作成したプログラムでは,キャラの移動と終了処理にCheckHitKeyを使ってますが,
一応この関数は使用非推奨なので本来はCheckHitKeyStateAllでキー入力処理を実装した方がいいです(設定面倒だもの
終わりに
誰か蓮子ちゃんのデスクトップマスコット作ってください
ソースコード
参考文献も含めて書いておきます~
この記事を書くために急いで作ったのでコピペ多めです…
namespace DxLibがついている関数,型はDxLibのもの,
その他コード内で宣言していないのに呼び出している関数,型はWindowsAPIの物のはずです.
#include "DxLib.h"
//グローバル変数
int model;//モデルハンドル
int attachIndex;//モデルに適用するアニメーションの番号
float playTime;//アニメーションの現在時刻
float totalTime;//アニメーションの総再生時間
DxLib::VECTOR charaPos;//キャラクタの座標
//DxLib_Init以前で呼び出すべき処理
void preInitialize()
{
//ディスプレイ解像度の取得
// http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q10129265945
RECT rc;
GetWindowRect(GetDesktopWindow(), &rc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
//--
//解像度,ウィンドウモード指定
DxLib::ChangeWindowMode(TRUE); // ウィンドウモードに変更
DxLib::SetGraphMode(width, height, 32); // 解像度の設定
//http://www.sys-cube.co.jp/blog/5347.html
DxLib::SetWindowStyleMode(2); // ウィンドウのスタイルを枠無しに指定
DxLib::SetUseBackBufferTransColorFlag(TRUE); // ウィンドウを透明に指定
//--
}
void afterInitialize()
{
//3D周りの初期化
DxLib::SetUseZBuffer3D(TRUE);
DxLib::SetWriteZBuffer3D(TRUE);
//文字サイズの設定
SetFontSize(32);
//ウィンドウを最前面に表示する
// http://dixq.net/forum/viewtopic.php?f=3&t=10766
HWND hWnd = GetMainWindowHandle();
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
//--
DxLib::SetAlwaysRunFlag(TRUE);//ウィンドウがアクティブ状態でなくても実行する
DxLib::SetDrawScreen(DX_SCREEN_BACK);//描画対象を裏画面にする
DxLib::SetupCamera_Ortho(2000.0f);//カメラを正射影に変更
model = DxLib::MV1LoadModel("model/renko.pmx");//モデルデータの読み込み
//アニメーション設定
// http://dixq.net/g/3d_01.html
attachIndex = DxLib::MV1AttachAnim(model, 0, -1, FALSE);
totalTime = DxLib::MV1GetAttachAnimTotalTime(model, attachIndex);
playTime = 0.0f;
//描画サイズの調整
const float scale = 80.0;
DxLib::MV1SetScale(model, VGet(scale, scale, scale));
//モデルの座標の設定
charaPos = DxLib::VGet(800, -610, 0);
//輪郭線の大きさを修正する
//通常MV1SetScaleで拡大すると,輪郭線の大きさも拡大されてしまう
//全ての輪郭線にアクセスし,MV1SetScaleで拡大した値で割ることで,太さをもとに戻す
// http://peroon.hatenablog.com/entry/20110402/1301773655
int MaterialNum = DxLib::MV1GetMaterialNum(model);
for (int i = 0; i < MaterialNum; i++)
{
// マテリアルの輪郭線の太さを取得
float dotwidth = DxLib::MV1GetMaterialOutLineDotWidth(model, i);
// マテリアルの輪郭線の太さを拡大した分小さくする
DxLib::MV1SetMaterialOutLineDotWidth(model, i, dotwidth / scale);
}
//--
}
//メインとなる処理
void mainProcess()
{
//メインループ
while (DxLib::ProcessMessage() == 0)
{
//描画内容を全削除
DxLib::ClearDrawScreen();
//モデルの移動
const float speed = 50;
if (DxLib::CheckHitKey(KEY_INPUT_RIGHT))charaPos.x += speed;
if (DxLib::CheckHitKey(KEY_INPUT_LEFT))charaPos.x -= speed;
//--
//モデルの座標指定
DxLib::MV1SetPosition(model, charaPos);
//モデルのアニメーション設定
//http://dixq.net/g/3d_01.html
// 再生時間を進める
// ↓の1.0はいい感じに動くように適当に決める
// 環境によって処理落ちなどで実行速度が変わることがある
playTime += 1.0f;
// 再生時間がアニメーションの総再生時間に達したら再生時間を0に戻す
if (playTime >= totalTime) {
playTime = 0.0f;
}
// 再生時間をセットする
DxLib::MV1SetAttachAnimTime(model, attachIndex, playTime);
//---
//モデルの描画
DxLib::MV1DrawModel(model);
//クリックに反応
if (DxLib::GetMouseInput()&MOUSE_INPUT_LEFT)
{
//キャラクターの「スクリーン上」での座標を取得する
DxLib::VECTOR pos = DxLib::ConvWorldPosToScreenPos(charaPos);
DxLib::DrawFormatString(pos.x + 100, pos.y - 500, 0xffffff, ">>クリックされてるよ~");
}
//--
//描画内容を画面に反映
DxLib::ScreenFlip();
//Qキーで終了
if (DxLib::CheckHitKey(KEY_INPUT_Q))break;
}
}
//main関数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
preInitialize();
if (DxLib::DxLib_Init() == -1)return -1;
afterInitialize();
mainProcess();
DxLib::DxLib_End(); // DXライブラリ使用の終了処理
return 0; // ソフトの終了
}
次回の記事は「かじかみ対策」です。お楽しみに!