デスクトップマスコットの作り方

この記事は[CCS Advent Calendar 2017](https://adventar.org/calendars/2551)の15日目の記事です。

昨日の記事：[なんか旅について記事書きます](http://chii-botti.hatenadiary.jp/entry/2017/12/14/175542) by ちー([@chii_botti](https://twitter.com/chii_botti))

(追記)
2018/10/12 記述ミスの修正及び記述の追加(Michio029さん、ありがとうございます)

# はじめに

まずはこちらをご覧ください。

![30.gif](https://qiita-image-store.s3.amazonaws.com/0/124566/c32641c9-75a0-bf4b-cf9d-2a4087500af8.gif)

デスクトップ上に女神が降臨しています。
この記事では上のスクショのようなデスクトップマスコットを作ります。
デスクトップマスコットとは、デスクトップ上にキャラクターが常駐するタイプのソフトウェアです。
単純に癒し効果を期待したり、何か便利な機能が搭載されてたりします。

この記事では表示させるキャラクターにMMDモデルを利用し、まずモデルの描画・モーションの再生を目指します。
その後、ちょっとした機能としてキャラクターに画像をドラッグ&ドロップをする事でTwitterに投稿する機能を実装します。
使用するものはこんな感じです。

| 名称 | 説明 |
|:---:|:--:|
| VisualStudio2017 | 統合開発環境 |
| C# | プログラミング言語 |
| DXライブラリ | ゲームライブラリ |
| CoreTweet | TwitterAPIライブラリ |

# 目次

* プロジェクトの作成
* DXライブラリの導入
* MMDモデルの描画
* 全画面化・背景の透明化
* モーションの適用
* CoreTweetの導入
* 認証情報の登録
* 画像の投稿

# プロジェクトの作成

VisualStudio2017はインストール済みとします。
(インストールしていない場合は[このあたり](http://www.softantenna.com/wp/tips/visual-studio-2017-install/)を参考にしながらインストールして下さい。)

まずプロジェクトを作成します。
C#のWindowsフォームアプリケーション(.NET Framework)を選択します。
プロジェクト名と場所はお好みで。

![1.png](https://qiita-image-store.s3.amazonaws.com/0/124566/725b31a4-e3ff-5991-f134-aa28c6ee49a0.png)

プロジェクトを作成すると、Form1.cs[デザイン]が開かれていると思います。
(開いてない場合はForm1.csから開けます。)

![8.png](https://qiita-image-store.s3.amazonaws.com/0/124566/18ede12f-c8f3-79aa-6ba6-86d9fc918bf9.png)

次にProgram.csを開きます。
開くといろいろ書いてありますが、後半部分にMain関数があると思います。

![2.png](https://qiita-image-store.s3.amazonaws.com/0/124566/64d3a82f-c2ec-6e92-1652-16013b99620d.png)

次に「Form1.cs→Form1.Designer.cs→Form1→Form1()」より、Form1.csを開きます。
(少し分かりにくいと思うので注意。)

![3.png](https://qiita-image-store.s3.amazonaws.com/0/124566/416a1131-92c5-4e00-de78-db55b65afefb.png)

![4.png](https://qiita-image-store.s3.amazonaws.com/0/124566/f987a4ec-9ff2-ac05-fcba-ff6395185be2.png)

これらを中心に編集してデスクトップマスコットを作成します。

# DXライブラリの導入

MMDモデルの描画などにDXライブラリというゲームライブラリの関数を使用するので、これを導入します。
DXライブラリを使った事がある人は分かると思いますが、DXライブラリはC/C++用のゲームライブラリで、C#を使う場合は少し勝手が異なります。注意して下さい。
まず、[公式サイトのダウンロードページ](http://dxlib.o.oo7.jp/dxdload.html)からVisualC#用パッケージをダウンロードします。ダウンロードしたらそれを解凍します。
次に、先ほど作成したC#プロジェクトのフォルダの中で、実行ファイル(.exe)が作成されるフォルダ(基本的には「プロジェクト名/bin/Debug」か「プロジェクト名/bin/Release」)にDxLib.dll、DxLib_x64.dll、DxLibDotNet.dllをコピーします。

![5.png](https://qiita-image-store.s3.amazonaws.com/0/124566/27c5080b-a9af-3f9e-782d-79d411cb8aaf.png)

次にVisualStudio上で「プロジェクト→参照の追加」を選択します。
参照マネージャが開くと思うので、それの参照タブを選び、右下の参照からコピーしたDxLibDotNet.dllを選択します。
選択したらOKを押して閉じます。

![6.png](https://qiita-image-store.s3.amazonaws.com/0/124566/ffb56de5-1633-29fb-d192-3e3338d071c1.png)

![7.png](https://qiita-image-store.s3.amazonaws.com/0/124566/06ef1f9a-0036-0ff0-e496-03f35e379da6.png)

設定を終えたので、プログラムを書きます。
Form1.csとProgram.csをそれぞれ以下のように書き換えて下さい。
(スペースの都合上、一部の記述を省略したりします。)

```csharp:Form1.cs
//usingいろいろ
using DxLibDLL;//DxLibを使用

namespace DesktopMascot {
	public partial class Form1 : Form {
		//
		//必要な変数
		//

		public Form1()
		{
			InitializeComponent();//フォームの初期設定

			DX.SetOutApplicationLogValidFlag(DX.FALSE);//Log.txtを生成しないように設定
			DX.SetUserWindow(Handle);//DxLibの親ウインドウをこのフォームに設定
			DX.DxLib_Init();//DxLibの初期化処理
			DX.SetDrawScreen(DX.DX_SCREEN_BACK);//描画先を裏画面に設定

			//
			//画像の読み込み等
			//
		}

		public void MainLoop()
		{
			DX.ClearDrawScreen();//裏画面を消す

			//
			//毎フレーム呼ぶ処理
			//

			DX.ScreenFlip();//裏画面を表画面にコピー
		}

		private void Form1_FormClosed(object sender, FormClosedEventArgs e)
		{
			DX.DxLib_End();//DxLibの終了処理
		}
	}
}
```

```csharp:Program.cs
//usingいろいろ

namespace DesktopMascot {
	static class Program {
		[STAThread]
		static void Main()
		{
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);

			Form1 form = new Form1();

			form.Show();

			//Application.Runではなく自分でループを作成
			while (form.Created) {
				form.MainLoop();
				Application.DoEvents();
			}
		}
	}
}
```

DXライブラリの関数を使用する場合は「using DxLibDLL;」と書きます。
また、関数やマクロの頭に「DX.」が付きます。C#の場合一部使えない関数があるそうですが、基本的な使い方はC/C++の場合と同じです。
この記事ではDXライブラリの細かい使い方や説明については省かせてもらいます。
(MMDモデルの描画など話のメインとなる部分についてはできるだけちゃんと書きます。)
関数の詳細などについては[公式サイトの関数リファレンス](http://dxlib.o.oo7.jp/dxfunc.html)を参照して下さい。

今回はSetUserWindow関数でDXライブラリの描画先をフォームアプリケーションに設定します。
こうする事でDXライブラリとフォームアプリケーションの両方の機能を利用できます。
(参考：[C#でDxLibを使う](https://qiita.com/hart_edsf/items/0301f17e2d9acd890c5e))

また、ウインドウ(フォーム)が閉じられた時にDXライブラリの終了処理(DxLib_End関数)を実行するために、イベントを設定します。
Form1.cs[デザイン]を開き、右下のプロパティの中のイベント(雷マーク)を選択し、FormClosedという項目にForm1_FormClosed関数を設定します。
こうする事でフォームが閉じられた時にForm1_FormClosed関数が自動で実行されます。この関数ではDxLib_End関数が実行されるようにしているので、フォームが閉じられた時に自動でDXライブラリの終了処理が実行されます。

![12.png](https://qiita-image-store.s3.amazonaws.com/0/124566/19d0f785-a31b-d574-43e7-30d2ef307990.png)

この状態で実行すると、以下のような黒い画面が出ると思います。
出たらDXライブラリの導入は成功です。

![9.png](https://qiita-image-store.s3.amazonaws.com/0/124566/3c4d7973-b005-c412-912a-4a1b905c536f.png)

# MMDモデルの描画

まず描画するMMDモデルを用意します。
この記事では[ここ](http://www.vocalomakets.com/mmd.html)から結月ゆかりのMMDモデルをダウンロードして使用します。
なぜ結月ゆかりを選んだかというと、単に結月ゆかりの事が好きすぎてたまらないからです。
みなさんは好きなキャラのMMDモデルを用意して下さい。
ただし、pmx形式のモデルの場合上手く描画できないケースがあるので注意して下さい。
読み込める3Dモデルの形式については[関数リファレンス](http://dxlib.o.oo7.jp/function/dxfunc_3d.html#R1N1)を参照。

MMDモデルをダウンロード(+解凍)したら、「プロジェクト名/bin/Debug」を開き、MMDモデルなどを保存するDataフォルダを作ります。
(単に分かりやすくしたいだけなので、しなくても良いです。)

![10.png](https://qiita-image-store.s3.amazonaws.com/0/124566/3ec4deb7-dad3-d476-439d-6fb551eddeda.png)

作ったらその中にMMDモデルのデータとテクスチャを入れます。
MMDモデルのファイル名は後で使用するので、必要なら分かりやすく書き換えておいて下さい。

![11.png](https://qiita-image-store.s3.amazonaws.com/0/124566/8afd3638-8c9e-de2b-fd24-4643e55b125f.png)

MMDモデルを描画する準備ができたので、実際に描画します。
プログラムを以下のように書き換えて下さい。

```csharp:Form1.cs
//usingいろいろ

namespace DesktopMascot {
	public partial class Form1 : Form {
		private int modelHandle;

		public Form1()
		{
			InitializeComponent();//フォームの初期設定

			DX.SetOutApplicationLogValidFlag(DX.FALSE);//Log.txtを生成しないように設定
			DX.SetUserWindow(Handle);//DxLibの親ウインドウをこのフォームに設定
			DX.DxLib_Init();//DxLibの初期化処理
			DX.SetDrawScreen(DX.DX_SCREEN_BACK);//描画先を裏画面に設定

			modelHandle = DX.MV1LoadModel("Data/結月ゆかり_純.pmd");//3Dモデルの読み込み

			DX.SetCameraNearFar(0.1f, 1000.0f);//奥行0.1～1000をカメラの描画範囲とする
			DX.SetCameraPositionAndTarget_UpVecY(DX.VGet(0.0f, 10.0f, -20.0f), DX.VGet(0.0f, 10.0f, 0.0f));//第1引数の位置から第2引数の位置を見る角度にカメラを設置
		}

		public void MainLoop()
		{
			DX.ClearDrawScreen();//裏画面を消す

			DX.MV1DrawModel(modelHandle);//3Dモデルの描画

			DX.ScreenFlip();//裏画面を表画面にコピー
		}

		private void Form1_FormClosed(object sender, FormClosedEventArgs e)
		{
			DX.DxLib_End();//DxLibの終了処理
		}
	}
}
```

MV1LoadModel関数でモデルを読み込みます。引数には読み込みたいモデルのパスを指定します。
この関数は読み込んだモデルの識別番号(モデルハンドル)を返すのでそれを変数に保存しておきます。
その後、ループ内でMV1DrawModel関数を実行する事で3Dモデルを描画します。引数には描画したいモデルのモデルハンドルを指定します。先ほど保存したモデルハンドルを与える事で読み込んだモデルを描画できます。

SetCameraNearFar関数はカメラのどの距離からどの距離まで描画するかを決定する関数です。
SetCameraPositionAndTarget_UpVecY関数はカメラの位置を設定する関数です。第1引数の位置から第2引数の位置を見る向きにカメラを設定します。VGetは3次元ベクトル(x,y,z)を表します。
カメラに関しては都合の良い感じに値を調整すればokです。

以下のようにMMDモデルが表示されたら成功です。

![13.png](https://qiita-image-store.s3.amazonaws.com/0/124566/bd1c9d8a-3d4a-9e1c-715e-a6b00b740073.png)

# 全画面化・背景の透明化

このままだと、この狭いウインドウ内でしかキャラクターが描画されないのと、背景が邪魔です。なので、ウインドウの範囲をディスプレイ全体にし、背景を透明にします。

まずプログラムを以下のように書き換えます。

```csharp:Form1.cs
//usingいろいろ
using DxLibDLL;//DxLibを使用

namespace DesktopMascot {
	public partial class Form1 : Form {
		private int modelHandle;

		public Form1()
		{
			InitializeComponent();//フォームの初期設定

			ClientSize = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);//画面サイズの設定
			Text = "DesktopMascot";//ウインドウの名前を設定

			DX.SetOutApplicationLogValidFlag(DX.FALSE);//Log.txtを生成しないように設定
			DX.SetUserWindow(Handle);//DxLibの親ウインドウをこのフォームに設定
			DX.DxLib_Init();//DxLibの初期化処理
			DX.SetDrawScreen(DX.DX_SCREEN_BACK);//描画先を裏画面に設定

			modelHandle = DX.MV1LoadModel("Data/結月ゆかり_純.pmd");//3Dモデルの読み込み

			DX.SetCameraNearFar(0.1f, 1000.0f);//奥行0.1～1000をカメラの描画範囲とする
			DX.SetCameraPositionAndTarget_UpVecY(DX.VGet(0.0f, 10.0f, -30.0f), DX.VGet(0.0f, 10.0f, 0.0f));//第1引数の位置から第2引数の位置を見る角度にカメラを設置
		}

		public void MainLoop()
		{
			DX.ClearDrawScreen();//裏画面を消す
			DX.DrawBox(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, DX.GetColor(1, 1, 1), DX.TRUE);//背景を設定(透過させる)

			DX.MV1DrawModel(modelHandle);//3Dモデルの描画

			//ESCキーを押したら終了
			if (DX.CheckHitKey(DX.KEY_INPUT_ESCAPE) != 0) {
				Close();
			}

			DX.ScreenFlip();//裏画面を表画面にコピー
		}

		private void Form1_FormClosed(object sender, FormClosedEventArgs e)
		{
			DX.DxLib_End();//DxLibの終了処理
		}

		private void Form1_Shown(object sender, EventArgs e)
		{
			FormBorderStyle = FormBorderStyle.None;//フォームの枠を非表示にする
			TransparencyKey = Color.FromArgb(1, 1, 1);//透過色を設定
		}
	}
}
```

次にForm1.cs[デザイン]を開き、右下のプロパティの中のイベントのFormShownにForm1_Shown関数を設定します。

![17.png](https://qiita-image-store.s3.amazonaws.com/0/124566/0bb56731-b2e7-6999-08bc-2207bb584e23.png)

Screen.PrimaryScreen.Bounds.WidthとScreen.PrimaryScreen.Bounds.Heightでディスプレイの幅と高さを取得できるので、それをClientSizeに設定する事でウインドウの範囲がディスプレイ全体になります。

また、モデルを描画する前にDrawBox関数でウインドウ全体にTransparencyKeyで設定した透過色を描画します。こうする事で背景が透明になり、モデルだけが表示されます。
(透過色には表示するモデルに使われてなさそうな色を設定して下さい。)

また、フォームの枠を非表示にしたため、右上のバツ印が消えてしまっています。
なので、ESCキーを押したら終了するようにしておきます。(Close関数はフォームを閉じる関数です。)

ウインドウの大きさを変えたため、表示されるモデルの大きさも変わります。
なので、カメラの位置を調整しています。

以下のように背景が透明になったら成功です。

![14.png](https://qiita-image-store.s3.amazonaws.com/0/124566/4e03eec8-b996-1693-4eab-364d926ec1d4.png)

…カメラの位置を弄ってたら事故りました。

![15.png](https://qiita-image-store.s3.amazonaws.com/0/124566/95c13e7d-7200-d346-e1cc-a51f71cee763.png)

2018/10/12 追記  
コンストラクタ(public Form1())内のDXLib_Init()の直前に記述を追加し、以下のようにする事でモデルをより綺麗に描画する事ができるようです。

```csharp
DX.SetOutApplicationLogValidFlag(DX.FALSE);//Log.txtを生成しないように設定
DX.SetUserWindow(Handle);//DxLibの親ウインドウをこのフォームに設定
DX.SetZBufferBitDepth(24);//Zバッファの深度を24bitに変更
DX.SetCreateDrawValidGraphZBufferBitDepth(24);//裏画面のZバッファの深度を24bitに変更
DX.SetFullSceneAntiAliasingMode(4, 2);//画面のフルスクリーンアンチエイリアスモードの設定をする
DX.DxLib_Init();//DxLibの初期化処理
DX.SetDrawScreen(DX.DX_SCREEN_BACK);//描画先を裏画面に設定
```

用いるモデルによりどの程度綺麗になるかは変わると思われますが、今回使用しているモデルの適用前と適用後は以下のようになりました。

* 変更前  
![before.png](https://qiita-image-store.s3.amazonaws.com/0/124566/59a4a719-a567-d78a-518a-a384d630d8d4.png)

* 変更後  
![after.png](https://qiita-image-store.s3.amazonaws.com/0/124566/7c39e2cc-5a0b-ad32-4a29-7e709e1b7dab.png)

(しょうがないと思っていた)描画の乱れが改善し、なめらかに表示されるようになりました。  


# モーションの適用

キャラが動いてるところが見たいので、モーションを適用します。
まずはモーションを入手します。[この動画](http://www.nicovideo.jp/watch/sm28700654)のモーションが気に入ったので、お借りします。
動画説明文のリンクからダウンロードします。解凍パスも動画説明文を参照。
モーションについても好きなモーションがあればそれを使って下さい。
モーションデータを入手したら「プロジェクト名/bin/Debug/Data」を開きその中にモーションデータを入れて下さい。

次に、入れたモーションデータの名前を変更します。名前は「モデル名+000から始まる連番」にします。複数のモーションを入れる場合は以下の画像のように000,001,002,...となります。ループ再生するタイプのモーションについては番号の後にLを付けます。

![18.png](https://qiita-image-store.s3.amazonaws.com/0/124566/2d05077c-6497-ed40-da2b-3b112b913f85.png)

これでモーションを適用する準備ができました。
以下のようにプログラムを書き換えて下さい。

```csharp:Form1.cs
//usingいろいろ
using DxLibDLL;//DxLibを使用

namespace DesktopMascot {
	public partial class Form1 : Form {
		private int modelHandle;
		private int attachIndex;
		private float totalTime;
		private float playTime;
		private float playSpeed;

		public Form1()
		{
			InitializeComponent();//フォームの初期設定

			ClientSize = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);//画面サイズの設定
			Text = "DesktopMascot";//ウインドウの名前を設定

			DX.SetOutApplicationLogValidFlag(DX.FALSE);//Log.txtを生成しないように設定
			DX.SetUserWindow(Handle);//DxLibの親ウインドウをこのフォームに設定
			DX.DxLib_Init();//DxLibの初期化処理
			DX.SetDrawScreen(DX.DX_SCREEN_BACK);//描画先を裏画面に設定

			modelHandle = DX.MV1LoadModel("Data/結月ゆかり_純.pmd");//3Dモデルの読み込み
			attachIndex = DX.MV1AttachAnim(modelHandle, 2, -1, DX.FALSE);//モーションの選択
			totalTime = DX.MV1GetAttachAnimTotalTime(modelHandle, attachIndex);//モーションの総再生時間を取得
			playTime = 0.0f;//モーションの再生位置
			playSpeed = 0.2f;//モーションの再生位置を進める速度

			DX.SetCameraNearFar(0.1f, 1000.0f);//奥行0.1～1000をカメラの描画範囲とする
			DX.SetCameraPositionAndTarget_UpVecY(DX.VGet(12.0f, 25.0f, -35.0f), DX.VGet(0.0f, 15.0f, 0.0f));//第1引数の位置から第2引数の位置を見る角度にカメラを設置
		}

		public void MainLoop()
		{
			DX.ClearDrawScreen();//裏画面を消す
			DX.DrawBox(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, DX.GetColor(1, 1, 1), DX.TRUE);//背景を設定(透過させる)

			playTime += playSpeed;//時間を進める

			//モーションの再生位置が終端まで来たら最初に戻す
			if (playTime >= totalTime) {
				playTime = 0.0f;
			}

			DX.MV1SetAttachAnimTime(modelHandle, attachIndex, playTime);//モーションの再生位置を設定


			DX.MV1DrawModel(modelHandle);//3Dモデルの描画

			//ESCキーを押したら終了
			if (DX.CheckHitKey(DX.KEY_INPUT_ESCAPE) != 0) {
				Close();
			}

			DX.ScreenFlip();//裏画面を表画面にコピー
		}

		//省略

	}
}
```

MV1AttachAnim関数で適用するモーションを指定します。2つ目の引数で何番目のモーションを使うかを決めます。
モデルを読み込んだ時に「モデル名+000から始まる連番」となっているモーションファイルが自動的に読み込まれています。
今回は000～002まであるので、0,1,2がそれぞれ000,001,002のモーションに対応しています。
上のプログラムでは2番(3つ目のモーション)を選択しています。
MV1AttachAnim関数は選択したモーションの識別番号を返すので、モデルを読み込んだ時と同様に変数に保存します。
MV1SetAttachAnimTime関数でモーションのどの位置の状態を適用するかを決定します。
自動的に再生される訳ではなく、「○秒の状態にする」という関数なので、再生位置(時間)を示す変数を用意し、ループ内で値を増やす事で再生しているように見せています。
また、MV1GetAttachAnimTotalTime関数でモーションの総再生時間を取得し、それを過ぎたら再生位置(時間)をリセットするようにしています。
(このアニメーションの場合、突然上空に移動してしまいますが…)
モーションによりキャラの見え方が変わるので、またカメラの位置を変えて調整しています。
また、PCのスペックなどの差で処理速度が変わるため、playSpeedの値はいい感じに変更して下さい。

![19.png](https://qiita-image-store.s3.amazonaws.com/0/124566/e477f8d2-ab57-5958-19de-29a5c332274d.png)

…これは事故です。

![20.png](https://qiita-image-store.s3.amazonaws.com/0/124566/5ebeeb6c-11dc-53e8-cde0-757016f2eb8f.png)

いろいろなモーションを試してみると面白いと思います(
<http://www.nicovideo.jp/watch/sm31368233>

![29.gif](https://qiita-image-store.s3.amazonaws.com/0/124566/f6b5ca83-499f-a336-1221-ba96353997f8.gif)

# CoreTweetの導入

C#でTwitterに画像を投稿するために、CoreTweetというライブラリを導入します。
VisualStudio上で「プロジェクト→NuGetパッケージの管理」を選択します。
NuGetはC#のライブラリを簡単にダウンロード・インストールできる機能です。

![21.png](https://qiita-image-store.s3.amazonaws.com/0/124566/c319f30b-c2da-4371-6d3b-3de29b69ff38.png)

画面が開いたら「参照」を選択し、「CoreTweet」と検索をかけます。
すると、CoreTweetのパッケージが出てくると思うので、インストールします。

![22.png](https://qiita-image-store.s3.amazonaws.com/0/124566/dba059f1-f099-0052-79c6-aa99c761314f.png)

# 認証情報の登録

Twitterにアプリケーションを登録して画像の投稿などの機能(TwitterAPI)を利用できるようにします。
まず、[TwitterApplicationManagement](https://apps.twitter.com/)にアクセスします。
その後、自分のアカウントでログインし、「Create New App」を押します。

![23.png](https://qiita-image-store.s3.amazonaws.com/0/124566/acfbd3d5-6689-2b3a-7af4-25e4ca118435.png)

登録画面が開いたら、アプリケーションの情報を入力します。

![24.png](https://qiita-image-store.s3.amazonaws.com/0/124566/c0f6b0f7-e0c8-864d-a3c3-724c1b0691f2.png)

Name、Description、Websiteは必須です。
NameはTweetDeckなどでどこから投稿されたかとして表示されます。なので、見られても大丈夫そうな名前にしておきましょう。
Descriptionは適当にアプリケーションの説明を書きます。
Websiteはアプリに関するサイトを登録するのですが、今回は特にないのでとりあえず自分のTwitterアカウントのURLでも書いておきましょう。
最後に「Developer Agreement」の所にチェックを入れて「Create Your Twitter Application」を考えます。

登録が完了したら、次にアクセストークンを発行します。
(ツイートを許可するためにあらかじめAccess LevelをRead and writeに変更しておいて下さい。「Permissions」から変更できます。)

![25.png](https://qiita-image-store.s3.amazonaws.com/0/124566/a6c7fb39-170a-123a-d25c-687f3bd748f2.png)

![26.png](https://qiita-image-store.s3.amazonaws.com/0/124566/dca4802e-350d-d8b2-a5c8-d0fe971f0a1c.png)

「Keys and Access Tokens」を選択し、下の方の「Create my access token」を押します。
アクセストークンの発行が終了したら、次にプログラム上にこれらの認証情報を入力し、プログラムからTwitterへのツイートや画像投稿ができるようにします。
CoreTweetを使う場合は以下のように記述します。

```csharp
//usingいろいろ
using DxLibDLL;//DxLibを使用
using CoreTweet;//CoreTweetを使用

namespace DesktopMascot {
	public partial class Form1 : Form {
		private int modelHandle;
		private int attachIndex;
		private float totalTime;
		private float playTime;
		private float playSpeed;
		private Tokens tokens;

		public Form1()
		{
			InitializeComponent();//フォームの初期設定

			ClientSize = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);//画面サイズの設定
			Text = "DesktopMascot";//ウインドウの名前を設定

			tokens = Tokens.Create("ConsumerKey", "ConsumerSecret", "AccessToken", "AccessSecret");//Twitterアカウントの認証

			DX.SetOutApplicationLogValidFlag(DX.FALSE);//Log.txtを生成しないように設定
			DX.SetUserWindow(Handle);//DxLibの親ウインドウをこのフォームに設定
			DX.DxLib_Init();//DxLibの初期化処理
			DX.SetDrawScreen(DX.DX_SCREEN_BACK);//描画先を裏画面に設定

			modelHandle = DX.MV1LoadModel("Data/結月ゆかり_純.pmd");//3Dモデルの読み込み
			attachIndex = DX.MV1AttachAnim(modelHandle, 2, -1, DX.FALSE);//モーションの選択
			totalTime = DX.MV1GetAttachAnimTotalTime(modelHandle, attachIndex);//モーションの総再生時間を取得
			playTime = 0.0f;//モーションの再生位置
			playSpeed = 0.2f;//モーションの再生位置を進める速度

			DX.SetCameraNearFar(0.1f, 1000.0f);//奥行0.1～1000をカメラの描画範囲とする
			DX.SetCameraPositionAndTarget_UpVecY(DX.VGet(12.0f, 25.0f, -35.0f), DX.VGet(0.0f, 15.0f, 0.0f));//第1引数の位置から第2引数の位置を見る角度にカメラを設置
		}

		//省略
	}
}
```

"ConsumerKey"、"ConsumerSecret"、"AccessToken"、"AccessSecret"にはそれぞれ先ほど登録した時に得られた文字列を入れて下さい。
生成したトークンを用いてプログラムからツイートや画像投稿を行います。

# 画像の投稿

実際に画像を投稿できるようにします。プログラムを以下のように書き換えて下さい。

> 2018/10/12 追記
> プログラムの記述ミスを修正

```csharp:Form1.cs
//usingいろいろ
using DxLibDLL;//DxLibを使用
using CoreTweet;//CoreTweetを使用
using System.IO;//FileInfoを使用

namespace DesktopMascot {
	public partial class Form1 : Form {
		private int modelHandle;
		private int attachIndex;
		private float totalTime;
		private float playTime;
		private float playSpeed;
		private Tokens tokens;

		public Form1()
		{
			InitializeComponent();//フォームの初期設定

			ClientSize = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);//画面サイズの設定
			Text = "DesktopMascot";//ウインドウの名前を設定
			AllowDrop = true;//ドラッグ&ドロップを許可

			tokens = Tokens.Create("ConsumerKey", "ConsumerSecret", "AccessToken", "AccessSecret");//Twitterアカウント認証

			DX.SetOutApplicationLogValidFlag(DX.FALSE);//Log.txtを生成しないように設定
			DX.SetUserWindow(Handle);//DxLibの親ウインドウをこのフォームに設定
			DX.DxLib_Init();//DxLibの初期化処理
			DX.SetDrawScreen(DX.DX_SCREEN_BACK);//描画先を裏画面に設定

			modelHandle = DX.MV1LoadModel("Data/結月ゆかり_純.pmd");//3Dモデルの読み込み
			attachIndex = DX.MV1AttachAnim(modelHandle, 2, -1, DX.FALSE);//モーションの選択
			totalTime = DX.MV1GetAttachAnimTotalTime(modelHandle, attachIndex);//モーションの総再生時間を取得
			playTime = 0.0f;//モーションの再生位置
			playSpeed = 0.2f;//モーションの再生位置を進める速度

			DX.SetCameraNearFar(0.1f, 1000.0f);//奥行0.1～1000をカメラの描画範囲とする
			DX.SetCameraPositionAndTarget_UpVecY(DX.VGet(12.0f, 25.0f, -35.0f), DX.VGet(0.0f, 15.0f, 0.0f));//第1引数の位置から第2引数の位置を見る角度にカメラを設置
		}

		public void MainLoop()
		{
			DX.ClearDrawScreen();//裏画面を消す
			DX.DrawBox(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, DX.GetColor(1, 1, 1), DX.TRUE);//背景を設定(透過させる)

			playTime += playSpeed;//時間を進める

			//モーションの再生位置が終端まで来たら最初に戻す
			if (playTime >= totalTime) {
				playTime = 0.0f;
			}

			DX.MV1SetAttachAnimTime(modelHandle, attachIndex, playTime);//モーションの再生位置を設定


			DX.MV1DrawModel(modelHandle);//3Dモデルの描画

			//ESCキーを押したら終了
			if (DX.CheckHitKey(DX.KEY_INPUT_ESCAPE) != 0) {
				Close();
			}

			DX.ScreenFlip();//裏画面を表画面にコピー
		}

		private void Form1_FormClosed(object sender, FormClosedEventArgs e)
		{
			DX.DxLib_End();//DxLibの終了処理
		}

		private void Form1_Shown(object sender, EventArgs e)
		{
			FormBorderStyle = FormBorderStyle.None;//フォームの枠を非表示にする
			TransparencyKey = Color.FromArgb(1, 1, 1);//透過色を設定
		}

		private void Form1_DragEnter(object sender, DragEventArgs e)
		{
			//ファイルがドラッグされた場合受け付ける
			if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
				e.Effect = DragDropEffects.Copy;
			} else {
				e.Effect = DragDropEffects.None;
			}
		}

		private void Form1_DragDrop(object sender, DragEventArgs e)
		{
			string[] path = (string[])e.Data.GetData(DataFormats.FileDrop, false);//ドロップされたファイルのパスを取得(複数可)
			var ids = new List<long>();

			//各画像をアップロードしIDを取得
			foreach(var p in path) {
				MediaUploadResult image = tokens.Media.Upload(media: new FileInfo(p));
				ids.Add(image.MediaId);
			}

			Status s = tokens.Statuses.Update(status: "upload image", media_ids: ids);//画像をツイート
		}
	}
}
```

AllowDropをtrueにする事でファイルのドラッグ&ドロップを許可しています。
次にForm1.cs[デザイン]から右下のプロパティの中のイベントのDragEnterとDragDropにそれぞれForm1_DragEnter関数とForm1_DragDrop関数を設定します。
これでファイルをドラッグ&ドロップした時に自動でこれらの関数が実行されます。

![27.png](https://qiita-image-store.s3.amazonaws.com/0/124566/27641fe1-fdf4-657f-6e6d-c181191f906b.png)

ファイルがドロップされた場合、ファイルのパスを取得し、そのパスを利用してUpload関数で画像をアップロードし、Update関数で投稿しています。
statusにはツイート文章の文字列を、media_idsにはアップロードした画像ID(MediaIdで取得)の配列をそれぞれ渡します。配列なので、複数枚まとめての投稿にも対応しています。(1ツイート4枚までだったと思います。)

これでキャラクターにドラッグ&ドロップした画像がTwitterに投稿されるようになります。

![28.gif](https://qiita-image-store.s3.amazonaws.com/0/124566/e33a8216-76cb-b158-3e8b-dec0bf2e51a3.gif)

こんな感じに投稿されます。

![24.png](https://qiita-image-store.s3.amazonaws.com/0/124566/828c6d2e-8eb4-8141-1b05-4b3046cdf88f.png)

# おわりに

以上、デスクトップマスコットの作り方でした。
ここまで作り方についてあれこれ書いてきたわけですが、実はMMDモデルを用いたデスクトップマスコットは[既に配布されたりしています](http://www.nicovideo.jp/watch/sm28925683)。
~~つまりこの記事でやっている事はいわゆる二番煎じ~~

明日の記事はこまつなさん([@starealnight](https://twitter.com/starealnight))の「自作音ゲー紹介・DXライブラリで作る音ゲー」です。
よろしくお願いします。