稲枝の押入れ

いなえが適当なことを書いては、しまっておく場所

ウィンドウの自動整列ショートカットキーが使えない場合の対処法

最近ディスプレイが3枚に増え、以前にも増してウィンドウを動かすショートカットキーを頻繁に使うようになりました。

その(Windows7の)ショートカットキーというのは

  • Windows ロゴ キー + 左方向キー
    画面の左側にウィンドウを固定する
  • Windows ロゴ キー + 右方向キー
    画面の右側にウィンドウを固定する
  • Windows ロゴ キー + 上方向キー
    ウィンドウを最大化する
  • Windows ロゴ キー + 下方向キー
    ウィンドウを最小化する
  • Windows ロゴ キー + Shift + 上方向キー
    画面の上下にウィンドウを拡大する
  • Windows ロゴ キー + Shift + 左方向キーまたは右方向キー
    モニター間でウィンドウを移動する

(こちらから引用)

といったWindows ロゴ キーと矢印キーを主に組み合わせた様なショートカットキー達です。

頻繁に使うようになって、手にも馴染んできた所で、ノートパソコンを使う機会があったのですが、そちらで何故かこれらのショートカットキーが使えなくて困惑したので、簡単な話でしたが解決法を残しておきます。

デスクトップ環境はWindows7、ノートパソコンはWindows8なのでその違いなのかな?とも思いましたが、調べてみたらWindows8にもこれらのショートカットは(一部なくなっているようですが?)ある模様。

さて何が原因かと考えたところで、思いつきでウィンドウを画面の端に持っていって自動整列をしようと試してみるも出来ない。もしやコレのせいでは?と思い試してみたらまさにそれが原因でした。

具体的に書くと、「コントロール パネル>コンピューターの簡単操作>コンピューターの簡単操作センター>マウスを使いやすくします」から、「ウィンドウが画面の端に移動されたとき自動的に整列されないようにします」のチェックを外すと無事にこれらショートカットキーが使えるようになりました。

画面の小さいノートパソコンで幾つかのウィンドウを開きたい場面では、時にこの自動整列機能は邪魔なのですが、ショートカットキーは使いたいので困ってしまいました。これWindows10とかでは直ってるんでしょうか?

設定に依存せずショートカットキーは使えてほしいですね…

Rigidbodyをスリープモードにしない方法

導入

Rigidbodyとは

UnityにはRigidbodyと言われる物理演算等をしてくれるコンポーネントが有ります。

Unityの物理演算を利用しなくとも、Unityの当たり判定機能を使う為にもこのRigidbodyコンポーネントを少なくとも片方のオブジェクトが持っている必要があるので、Unityを使ったことがある人はおよそほとんどが使ったことがあるであろうコンポーネントです。

Sleepという機能

普通に使っている分には非常に便利なのですが、ちょっとした落とし穴があります。それはスリープ(Sleep)という機能です。

リジッドボディが決められた最低直線、または、回転スピードよりも動きが遅いとき、物理エンジンはそれが休止していると仮定します。これが発生すると、ゲームオブジェクトは衝突されるか力が与えられない限り再び動きません。そのため、それは「スリープ」モードに設定されます。この最適化により、次にリジッドボディが「起きる」 (これは、再動作のこと) までプロセッサーの時間が更新に使われることがありません。 公式より引用

つまり、Rigidbodyは決められた速度(速度、というのは厳密ではない)よりも遅く移動している等の場合、処理軽減のためスリープモードとなり、力を与えられない限り物理演算を行わず動かなくなります

一見それほど問題はなさそうですが、力を与えられない変更、例えばそのスリープとなっているオブジェクトの下の床が消えたといった場合に、オブジェクトは物理演算を行わないのでそのまま宙に浮いたままになったりします。

問題

さて、今回は「OnCollisionStayでプレイヤーの接地判定を取り、その判定がある時だけジャンプが可能になる」という実装をしようとしていたのですが、プレイヤーが止まるとスリープになってしまいOnCollisionStayが呼ばれなくてうまくいきませんでした。じゃあスリープしないようにしよう!となったわけです。

スリープをしないようにしよう!

SleepingModeがない!!

さてここまではよくある話なのですが、僕はここで徐にインスペクタでSleepingMode(スリープする条件を指定する項目)をNever Sleep(スリープを無効にする)にしようとしました。処理が重くなるので本来推奨されないのですが、面倒なのでそもそもスリープしなくしてしまいたかったのです。

しかし、インスペクタを見てもSleepingModeがありません。

以前に使った事があったので(白目)おかしいなと思いながら調べてみるとどうやらRigidbodyにそんなものはないらしい。

え?うそ?前使ったよ?バージョンアップでなくなったの?と思って「Rigidbody never sleep」とかで検索。

結論から言うとRigidbodyにはSleepingModeはなく、Rigidbody2Dのみにあるということらしいです。

なんじゃそりゃ、という感じなのですが、3Dの処理はやはり大変なので簡単にNever Sleepにはされたくないんでしょうかね。

それでもNever Sleepにしたい!!

でも僕はSleepしないようにしたい…

調べるとWakeup関数(強制的にスリープモードを解除する関数)を毎フレーム呼んだり、IsSleeping関数(スリープモードかどうかを返す関数)で判定して、スリープモードの時はWakeUp関数を呼んだりしてるのを見つけたんですが、毎フレーム呼んだりするのはなんとなく抵抗が…

もう少しスマートに出来ないかなと思って公式マニュアルをみていた所、RigidbodysleepThresholdというメンバ変数を発見。

スリープをするのは決められた速度(上述の通り厳密ではない)よりも遅く移動している時と言いましたが、大雑把に言うとこの変数はそのスリープをする決められた速度を規定する変数です。

よって例えば、sleepThresholdを-1とかにすると、「速度が-1より小さくなった時にスリープする」=「速度は0以上なのでスリープしない」となって、Start()内で一回rigid_body.sleepThreshold=-1;みたいな感じで書いてやればずっとスリープしなくなるわけです。

取り敢えずこんな方法でスリープを回避できるようにはなったんですが、急に思いついた方法(かつ正確に理解できているかわからない)なのでどういう副作用があるかわからないというのと、スリープを切るともちろん処理は重くなるので、使用する場合は自己責任かつあまり使わないようにした方がいいかな、と思います。

面倒なのでプレイヤーだけなら大丈夫かな?とか思って僕は使っちゃいますが。

DXライブラリを使って、VS2015でゲームのアイコンを変える方法

導入

こんにちは。

ふと、そういえば前々からつけるつけると言ってたゲームのアイコンをつけてなかったなあと思い、DXライブラリを暫くぶりに書いていたのですが、こちら公式情報には「VisualC++ 2005 Express Edition ~ Visual Studio Express 2013 for Windows Desktop の場合」しか書いていない。

自分の環境はVS2015だったので、.exeのアイコンを変えるだけならこのやり方でいけたものの、SetWindowIconID関数を使ったタスクバーやウインドウの左上端に表示されるアイコンの変更はうまくいきませんでした(このページにあるようにresource.hが自動生成されなかった)。

調べたらすぐ出てきたものの備忘録として残しておくと、簡単に言うとさっきのSetWindowIconID関数のページにある「Borland C++ の場合」と同じ方法でいけました。

全体の流れ

つまり全体としては以下の手順で進めると.exeのアイコンとウィンドウタスクバーやウインドウの左上端に表示されるアイコンの書き換え出来ることとなります。

アイコンの準備

1. 縦横32ドット、256色(8ビット)のアイコンファイル(拡張子.ico)を作成する。(『アイコン用ユーティリティ』等でWEB検索するとアイコンを作成するためのソフトが色々見つかるらしいです。GIMPでも書き出せたので僕はGIMPを使いました) 

2. そのアイコンファイルをVisualStudioのプロジェクトファイル(拡張子.sln)が あるフォルダに保存する。

テキストファイルの準備

3. 同様にプロジェクトファイルがあるフォルダに任意の名前をつけたテキストファイルを作成する。

4. そのファイルをメモ帳等のテキストエディタで開き、中身に次のように入力して保存して下さい。

ここに任意の自然数(アイコンID) ICON "ここにアイコンファイルのファイル名"

例えば

333 ICON "icon.ico"

といった感じ。*1

5. 保存したファイルの拡張子がおそらく.txtになっていると思うが、それを.rcに変更する。

VSに追加

6. プロジェクトを VisualStudioで開き、プロジェクト』→『既存項目の追加』で、追加ファイル選択ダイアログを表示して、作成した拡張子が.rcのファイルをプロジェクトに追加します。

7. 次にビルドした時には作成される実行可能ファイルのアイコンがオリジナルのアイコンになるのでそれを確認する。

SetWindowIconID関数を使う

8. SetWindowIconID()関数をゲームの初期化部分に書き、引数に.rcファイルで指定したアイコンIDを渡してやる。さっきの例なら、

SetWindowIconID(333);

と書く。

*1:勿論自然数ならなんでもいけるってわけではない(ハード資源にも限界がある)のでそのあたりを考えた数にしないといけないけどその辺はつっこまないで

__PRETTY_FUNCTION__マクロが使えない

__FUNCTION__

ちょっと気になることがあって小さい規模のコードを書いていたのだが、デバッグの時に今いる関数が表示できたら楽だなと思い、そんなマクロがあった気がするとか思いながら適当な文字列打ってたら__FUNCTION__マクロをサジェストに発見。

しかしこいつは関数の名前しか返してくれない。オーバーロードされた関数が複数あって、その中で__FUNCTION__マクロを使うような事があったら、実際にはどの関数が走っているのかがわからない。

__PRETTY_FUNCTION__

そんなわけで、型や引数の型なんかも返してくれるものないかなと思ったら__PRETTY_FUNCTION__マクロというものを発見。こいつは型や引数の型も返してくれるらしい。

よしこれだと書いてみると

C2065 '__PRETTY_FUNCTION__': 定義されていない識別子です。

とのこと。

こういった所謂「事前定義済みマクロ」はどうもコンパイラの独自実装のことが多いらしいので、そのへんが関係しているのかも?と思ったら、VSのサジェストでもちゃっかり出てきている。でもコンパイラさんはこんな識別子は知らないらしい。

ちょっと調べてみたらg++の独自実装だとか書いてあったけど、ますます何故サジェストが効いたのかわからなくなってきた。

原因が知りたいところではあるけれど、その前にまずは何とか動くようにしたい。

__FUNCSIG__

他にも型や引数の型を返してくれる事前定義済みマクロがないかなあと思って探していたら__FUNCSIG__マクロなるものを発見。これを使ったらうまく行った。

試しにmain関数の中で使ってみたら

int __cdecl main(void)

こんな感じで実行中関数の情報をくれた。

マイクロソフトの公式情報によると、__FUNCSIG__マクロは

__FUNCSIG__ Defined as a string literal that contains the signature of the enclosing function. The macro is defined only within a function. The __FUNCSIG__ macro is not expanded if you use the /EP or /P compiler option. When compiled for a 64-bit target, the calling convention is __cdecl by default. For an example of usage, see the __FUNCDNAME__ macro.

引用元
日本語がいい人はこっち

てな感じらしい。相も変わらず翻訳が意味わからなかったので原文で。まだ日本語訳してくれているだけありがたいので文句ばかりはいってられないけれど、マイクロソフトのあのわかりにくい翻訳は何とかならないのだろうか…

__func__

余談だけれど、「事前定義済みマクロ」はどうもコンパイラーの独自実装のことが多い、とさっき書いたけれど、その中でもこの関数名を表示する系のもののうち一部は、今では(という書き方をすると怒られそうなので補足しておくと一定の規格からは)規格として定義されているらしい。

例えば、C99では__FUNCTION__と同等の機能のものが__func__として追加されたそうな。ただし、

C++では現時点では定義が無いが、g++やclang/LLVMではC++コンパイル時にも利用できる。 g++の場合、C++で使用した場合は __FUNCTION__ とは違い関数名や引数はクラス名付きで得られる。

引用元

ということで、C++だと同等とは言えないみたいだけど。

まとめ

簡単にまとめると

  • __FUNCTION__
    • 関数の名前のみを得られる
  • __func__
    • C99で定義
    • __FUNCTION__と基本的に同じ
    • C++環境では違うことも?
  • __FUNCSIG__
    • 関数の型と名前と引数の型を得られる
  • __PRETTY_FUNCTION__
    • 関数の型と名前と引数の型を得られる(らしい)
    • VS環境ではコンパイル時にエラーが出て使えない(サジェストには表示される)
  • __FUNCDNAME__
    • 関数の名前を装飾名付で得られる

という感じ。一番下のものはこの記事では触れてませんが気になったら調べてみてください。さっきのMSのページにも書いてます。

あと、ここではわかりやすいように「得られる」と書いたが、厳密には「定義されている」だと思う。関数ではなくマクロなので。

もし__PRETTY_FUNCTION__が僕のVS環境下でコンパイルするとエラーが出るのか、思い当たる人は是非教えていただきたいです…

VisualStudioでコンソールプログラムを実行後、コンソール画面を消さない方法

調べた感じVS2010からどうも「空のプロジェクト」を作るとデフォの状態だとプログラムの実行が終わると自動でウィンドウが消えてしまう様になっているらしい。

最近はGUIの開発が多かったので知らなかったのだけれど、ちょっと試したいことがあって久々にコンソールプログラムを作って実行してみたら一瞬でウィンドウが消えて結果が全く見えなかった。

というわけで、それをどう直したらいいかを調べるのがかれこれ3回目くらいなので、大した分量もないのだが大まかに4つの方法を備忘録的に書いておく。

system("pause");

これはCもしくはC++を使っている場合に使える方法だが、まず、そもそも調べるのが面倒くさい時はsystem関数を使ってしまう。これは簡単に言うと文字列を受け取ってその文字列をコマンドプロンプトに打ち込んだのと同じような動きをしてくれる関数だ。

つまり、

system("pause");

と書くと、pauseコマンド(任意のキーを押すまで処理を中断するコマンド)が実行される。

これをプログラムの最後においておけば、処理が中断されてプログラムが終わらないのでウィンドウが消えなくなる(勿論キーを押すと終わってしまう)。

また、この関数を使うにはC言語なら<stdlib.h>C++なら<cstdlib>をインクルードする必要がある。ただ、<iostream><random>等を辿っていくとその中でそれらがインクルードされていたりするので、ユーザーが自らインクルードせずとも使える場合もある。

入力を待つ

CもしくはC++を使っている場合の方法その2。

Cなら

int hoge;
scanf("%d",&hoge);

C++なら

int hoge;
std::cin>>hoge;

などとして適当な変数に入力を入れるようにする。C言語なら<stdio.h>C++なら<iostream>のインクルードが必要。

ブレークポイント

3つめにもっと面倒くさい時、と言うか特にコードを汚したくない時はVSの機能であるブレークポイントを使ってしまう。これをプログラムの終わりのあたりに設定しておき、デバッグありで実行すれば、おそらくブレークポイントのところにプログラムが達した段階でVSの方に戻ってきてプログラムが一度止まる。ここで出力が確認できる。

ブレークポイントはコードの止めたい位置の左端のあたりをクリックするか、F9キーを押せば設定/解除が出来るはず。

ただし、ブレークポイントで止めた場合はプログラムの画面よりVSのコードの画面が全面に来てしまって、結局画面の出力結果が見辛くなってしまう可能性がある。

設定を変える

もしくはそもそも設定を変えることでこれを回避できる。少し長い間使うプロジェクトならこっちのほうが楽だ。これだとデバッグなし実行(Ctrl+F5)をした時に勝手にプログラムが終了するのを防げるようになる。

やり方は、

  1. ソリューションエクスプーラーで設定したいプロジェクトを右クリックしてプロパティを開く
  2. 左のボックスの中から、構成プロパティ/リンカ/システムを選ぶ
  3. 右のボックスから一番上のサブシステムコンソール(/SUBSYSTEM:CONSOLE)を選択

以上と言った感じ。そもそも何でデフォの設定変わったんですかね。

distributionで乱数の幅を決める時、負の値が指定できない

追記: 自分のアホでミスっていただけでした。結論から言うとresultをunsigned intにしているのに気付いていないままコードを書いていただけでした…お騒がせしました…
よって、この記事は情報が0に近い記事になってしまったわけですが、同じようなうっかりさんが居るかもしれないんのでこのまま残しておきます。

色々あって精度のそれなりな乱数を使いたいなって事でC++11から入ったメルセンヌ・ツイスタを使って乱数生成クラスを作ってたんですが、テストコードを走らせてる段階で何か少し変な挙動を発見。

コードの規模を小さくして検証してみると、どうもdistributionの動きが僕が思ってるのと違うっぽい?

以下0~100の範囲で乱数を100回生成させるようなコードなのですが、これはまあ思った通りに動く。 <コード>

#include<iostream>
#include<iomanip>
#include<random>


int main() {

    std::random_device rand_seed;//非決定論的乱数生成器
    std::mt19937 mt_obj(rand_seed());//メルセンヌ・ツイスタで乱数を生成する
    std::uniform_int_distribution<int>::param_type uniform_parameter(0, 100);//整形する幅を決める
    std::uniform_int_distribution<int> uniform(uniform_parameter);//生成された乱数を指定された幅の一様分布に整形する

    static int calc_num = 0;
    unsigned int result = uniform(mt_obj);


    for (int i = 0; i < 100; i++) {

        if (i != 0) {//初回は飛ばす
            result = uniform(mt_obj);
        }

        calc_num++;

        //表示の幅やレイアウトについて指定して出力している
        std::cout
            << std::setw(5) << std::left << calc_num
            << ":result = "
            << std::setw(5) << std::right << result
            << std::endl;
    }
    return 0;
}



<実行結果>

1    :result =    77
2    :result =    64
3    :result =    63
4    :result =    87
5    :result =    22
6    :result =     3
7    :result =    48
8    :result =    62
9    :result =     0
10   :result =    42
11   :result =    78
12   :result =    52
13   :result =    64
14   :result =    37
15   :result =    51
16   :result =    94
17   :result =    14
18   :result =    24
19   :result =    86
20   :result =    16
21   :result =    25
22   :result =    53
23   :result =    30
24   :result =    45
25   :result =    48
26   :result =    47
27   :result =    17
28   :result =    54
29   :result =    85
30   :result =    99
31   :result =    76
32   :result =    18
33   :result =   100
34   :result =    15
35   :result =    18
36   :result =    17
37   :result =    37
38   :result =    58
39   :result =    77
40   :result =    59
41   :result =    50
42   :result =     4
43   :result =    54
44   :result =    27
45   :result =    66
46   :result =    41
47   :result =    57
48   :result =    63
49   :result =    43
50   :result =    75
51   :result =    10
52   :result =    58
53   :result =    24
54   :result =     3
55   :result =     6
56   :result =    97
57   :result =    37
58   :result =     8
59   :result =    74
60   :result =    35
61   :result =    46
62   :result =    88
63   :result =    66
64   :result =    30
65   :result =    93
66   :result =    33
67   :result =    50
68   :result =    43
69   :result =    19
70   :result =    88
71   :result =    93
72   :result =    63
73   :result =    57
74   :result =    42
75   :result =    87
76   :result =    51
77   :result =    44
78   :result =    62
79   :result =    19
80   :result =    88
81   :result =    10
82   :result =    11
83   :result =    40
84   :result =    12
85   :result =    38
86   :result =    90
87   :result =    11
88   :result =    18
89   :result =    42
90   :result =    78
91   :result =    38
92   :result =     2
93   :result =    35
94   :result =    44
95   :result =    92
96   :result =    92
97   :result =    25
98   :result =     0
99   :result =    18
100  :result =    74

ところが、distributionの範囲にマイナスを含むようにすると動きが少しおかしくなる。

以下は-100~100の範囲で乱数を100回生成するコードだと… <コード>

#include<iostream>
#include<iomanip>
#include<random>


int main() {

    std::random_device rand_seed;//非決定論的乱数生成器
    std::mt19937 mt_obj(rand_seed());//メルセンヌ・ツイスタで乱数を生成する
    std::uniform_int_distribution<int>::param_type uniform_parameter(-100, 100);//整形する幅を決める
    std::uniform_int_distribution<int> uniform(uniform_parameter);//生成された乱数を指定された幅の一様分布に整形する

    static int calc_num = 0;
    unsigned int result = uniform(mt_obj);


    for (int i = 0; i < 100; i++) {

        if (i != 0) {//初回は飛ばす
            result = uniform(mt_obj);
        }

        calc_num++;

        //表示の幅やレイアウトについて指定して出力している
        std::cout
            << std::setw(5) << std::left << calc_num
            << ":result = "
            << std::setw(5) << std::right << result
            << std::endl;
    }
    return 0;
}



<実行結果>

1    :result =   100
2    :result =    72
3    :result =    13
4    :result =    12
5    :result = 4294967282
6    :result =     9
7    :result =    67
8    :result =    91
9    :result =    25
10   :result =    39
11   :result = 4294967232
12   :result =     5
13   :result =    67
14   :result =    45
15   :result = 4294967221
16   :result = 4294967211
17   :result = 4294967266
18   :result =    35
19   :result = 4294967293
20   :result = 4294967256
21   :result = 4294967258
22   :result = 4294967197
23   :result =    68
24   :result =    46
25   :result =    83
26   :result =    71
27   :result = 4294967256
28   :result =     1
29   :result =    63
30   :result = 4294967235
31   :result =    97
32   :result = 4294967291
33   :result = 4294967202
34   :result =    45
35   :result = 4294967272
36   :result =    17
37   :result = 4294967287
38   :result =    33
39   :result = 4294967288
40   :result =     3
41   :result = 4294967271
42   :result =    52
43   :result =    31
44   :result =    16
45   :result = 4294967278
46   :result = 4294967295
47   :result = 4294967204
48   :result =     9
49   :result = 4294967270
50   :result =    44
51   :result = 4294967273
52   :result =    88
53   :result = 4294967221
54   :result =    36
55   :result = 4294967250
56   :result =    29
57   :result =    98
58   :result =    58
59   :result = 4294967277
60   :result = 4294967246
61   :result =    86
62   :result =    66
63   :result =    82
64   :result = 4294967212
65   :result =    60
66   :result = 4294967268
67   :result =     3
68   :result =    83
69   :result =     5
70   :result = 4294967291
71   :result = 4294967263
72   :result = 4294967288
73   :result =    73
74   :result =    88
75   :result = 4294967236
76   :result =     6
77   :result =    39
78   :result =     3
79   :result =    62
80   :result = 4294967212
81   :result = 4294967200
82   :result =    17
83   :result = 4294967224
84   :result =    75
85   :result =     5
86   :result = 4294967289
87   :result =    29
88   :result = 4294967213
89   :result =    59
90   :result = 4294967253
91   :result =     0
92   :result =    10
93   :result = 4294967289
94   :result =    49
95   :result =    37
96   :result =    94
97   :result =    45
98   :result = 4294967293
99   :result = 4294967259
100  :result = 4294967221

と、実行結果がdistributionで定めた範囲の一様分布から外れている。これは負の値はdistributionの範囲に指定できないということなのだろうか?

ただ、std::uniform_int_distribution<int>::param_typeのコンストラクタでは幅の最小最大についてunsigned intではなくintを引数に取っているし、param_typeを使わずにstd::uniform_int_distribution<int>のコンストラクタもunsigned intではなくintを引数に取っている。

仮にこのdistributionが負の値を範囲指定出来ないなら、この辺がunsigned intで引数に渡せと言われそうなものだけど、これは本当に負の値を範囲指定できないという解釈で良いのだろうか。

もちろん、欲しい乱数の幅をズラすのは、得られた範囲にズラしたい分の幅を足したり引いたりすればいい(例えばdistributionの幅を0~200にしておいて、result = uniform(mt_obj);result = uniform(mt_obj) - 100;というようにして、得られた値を-100すれば、-100~100の範囲で乱数は得られる)

でもこんなの標準で出来るようにしておいて欲しい、という願望もあるので、何かできる方法は無いのかなあと思うものの、やはりよくわからない。 `

誰か知っていたら教えて下さい。

VisualStudioでプロジェクトのファイル名等を変える



!!!!注意!!!!

この記事は僕が自己の備忘録も兼ねて書いたものです。自分はプロでもなんでもないのでおそらく間違いもあると思います。 そのため、この記事に基づいてリネームをしようという場合、必ずソリューションフォルダをバックアップしておいてください。 また、ここに書いてあることを元に何かをして何かが起きても僕は一切の責任を取りません。誤りは無いように努力してはいますが、過信せず自分でも調べたり考えたりしながらお読みください。 また、Gitなどのバージョン管理を使っている方はリネームでフォルダの名前を変えたりもするのでで少し面倒な事になるかもしれませんが、そこも自己責任でお願いします。まあ手動で直せる範囲だとは思いますが。 また、間違った点については指摘をくださると、僕も勉強になるので助かります。



前提

出展

この記事はいなえがQiitaに上げた記事があまりにもわかりにくかった為、加筆修正を加えて再編成したものです。

例を持ち出す際に想定されるファイル構成

※拡張子の書いていないものはフォルダ

  • Project
    • RPG(ソリューションフォルダ)
      • RPG.sln
      • RPG.sdf
      • RPG.suo
      • RPG(プロジェクトフォルダ)
        • RPG.opensdf
        • RPG.sdf
        • RPG.vcxproj
        • RPG.vcxproj.filters
        • RPG.vcxproj.user
        • RPG.suo
        • (その他略)
      • (その他略)
    • (その他略)

そもそも何をしたいのか

目的

あるソリューションにおいて、

  1. ソリューション名とプロジェクト名
  2. フォルダ名とファイル名

のそれぞれを新しいものに変更することを目指します。

理由

現行の名前と以前の名前が混在していると統一感がなく混乱を招き得るので。

やり方

簡潔なまとめ

以下長々と色々書いていくのですが、自分で読んでも読むのが辛い駄文ゆえ、簡潔に手順をまとめておこうと思います。

  1. VisualStudioのソリューションエクスプローラーでソリューション名とプロジェクト名を変更する。
  2. 保存してVisualStudioを落とす
  3. .slnファイル(ソリューションファイル)をテキストエディタで開いて、プロジェクトフォルダと.vcxprojファイルへの関連付けを変更する
  4. ソリューションフォルダやプロジェクトフォルダをはじめ、拡張子が
    • .vcxproj.filters
    • .suo
    • .sdf
    • .opensdf
    • .vcxproj.user などのファイルを含め、すべてのファイル名を変更する。

ここでの「変更する」とは「現行の名前から新しい名前に書き換える」ということです。

ソリューション名とプロジェクト名の変更

VisualStudio(以下VS)ではソリューション名やプロジェクト名を変更する際、ソリュションエクスプローラで名前の変更をすれば簡単にそれらの名前を変更することが出来ます。ソリューション名とプロジェクト名の変更についてはこれで終了です。

ここで注意したいのは、ソリューションエクスプローラソリューションの名前を変更すると、拡張子が".sln"のファイル(ソリューションファイル)の名前が変更されることです。これとは反対に、プロジェクトの名前を変更しても、.vcxprojファイルの中に記されたプロジェクトファイルへの関連付けが変わるのみ*1で、ソリューションファイルの名前以外、どのファイル・フォルダの名前も変わりません。

フォルダ名とファイル名

上記変更はあくまでVS上で扱うソリューション名やプロジェクト名の変更でしかなく、(ソリューションファイルの変更を除き)それらのあるフォルダやファイルの名前は変更されません。 それが何か悪いの?という話になるのですが、次の場合を見てみてください。

例えば仮にRPGゲームを作ろう!!とC:\Users\you\Documents\Visual Studio 2015\Projects\RPGというソリューションを作り、開発を始めるとします。具体的にはその中のRPGプロジェクトを進めていくことになるのでしょうが、開発が進むに連れてタイトルが決まったりすると思います。ここでは「おさげ少女と勇者の大冒険」という名前に決まったとしましょう。そこで、このタイトルに合わせてせっかくだからソリューション名やプロジェクト名も「OsageRPG」に変えてしまおう!となったとします。

VS上でソリューション名やプロジェクト名を変えても、ファイル名の変更は"RPG.sln"が"OsageRPG.sln"に変わるだけで(隠しファイルの中などどうも実は一部変わっているっぽいが)その他には変化はありません。

そうすると、VS上はプロジェクト名やソリューション名は"OsageRPG"になっているのに、ディレクトリ上は殆どがRPGの名前のままで(しかもソリューションファイル名だけ"OsageRPG.sln"!!)統一性がなく、不格好なだけでなく混乱の元になりかねません。

※此処から先ではとりあえずVS上でソリューション名やプロジェクト名をVSから変更していない状態(=ソリューションファイル名が"RPG.sln"のままの状態)であるとして話を進めます

じゃあ直接エクスプローラーやらでフォルダ名やファイル名を変えてやればいいじゃないか

と思うでしょうがそう上手くは行きません。Project直下のRPGディレクトリに関しては問題無いでしょうが、"Project\RPG\RPG"(つまりはソリューションフォルダ直下のプロジェクトフォルダ)の名称をエクスプローラーで直接"Project\RPG\OsageRPG"変えてやったとしましょう。すると、フォルダ名を覚えている"RPG.sln"が「"Project\RPG\RPG"を探してるんだけど無いな。このソリューションおかしいぞ?」と"Project\RPG\OsageRPG"を見つけ出せずエラーを出します。

これをなんとかして解決して、このソリューションフォルダの中のファイル名の"RPG"となっている部分を"OsageRPG"何とか変更できないか、というのが今回の一番大事なお話です。

早い話の結論

グーグル先生は偉大

こういうことはやはり他の誰かもしたがっているもので、当然情報が転がっていました。

まず"RPG.sln"が探そうとするプロジェクトファイルを"Project\RPG\RPG"ではなく"Project\RPG\OsageRPG"に書き換えてやれば、エクスプローラーでプロジェクトフォルダの名前を変えてやっても問題なさそうです。そこで、RPG.slnテキストエディタで開きます。

すると、

Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RPG", "RPG\RPG.vcxproj", "{BE0A076E-539C-40BE-977E-7125AEEECEE7}" EndProject

と書いてある部分があると思います。

これを

Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OsageRPG", "OsageRPG\RPG.vcxproj", "{BE0A076E-539C-40BE-977E-7125AEEECEE7}" EndProject

と書き換えてやればプロジェクトフォルダの名前をエクスプローラーで"OsageRPG"に変更しても"RPG.sln"がちゃんと見つけてくれるようになります。(実は一つ目の" "内に書かれている名前はどうもVSが内部的に使っている名前のようなので、変えなくても見つけてはくれるみたいです。気になるので変更しましたが。)

でもまだ前の名前のままのところもあるよね?

気になりません?

フォルダ名のみを変えたいという人は上の部分までで終わりです。でもきっと、「まだ"OsageRPG"になってないところがあるじゃないか!!」と気になってしまう人もいると思います。例えば"Project\RPG\OsageRPG"直下にある"RPG.vcxproj"や"RPG.vcxproj.filters"など…

ではこいつらもおさげにしてやりましょう!!("OsageRPG"にリネームしよう、の意)

おさげだ!

VS上で既にソリューション名を"OsageRPG"にしている場合、.slnファイルは既におさげになっているでしょう。よってその他の、さっき例に上がった、"Project\RPG\OsageRPG"直下にある"RPG.vcxproj"等をリネームしていきましょう。

どうせこうするんでしょ?

お察しの方も多いかと思いますが、こいつも単に"OsageRPG.vcxproj"などとエクスプローラーでリネームしてしまうと、エラーが出ます。ではどうすればいいのか?これも気付いている方もいらっしゃるかと思いますが、もう一度"OsageRPG.sln"をテキストエディタで書き換えます。

ここまでで

Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OsageRPG", "OsageRPG\RPG.vcxproj", "{BE0A076E-539C-40BE-977E-7125AEEECEE7}" EndProject

上記の様になっているかと思いますが、これを

Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OsageRPG", "OsageRPG\OsageRPG.vcxproj", "{BE0A076E-539C-40BE-977E-7125AEEECEE7}" EndProject

のように書き換えます。その後、"Project\RPG\OsageRPG"直下にある"RPG.vcxproj"を"OsageRPG.vcxproj"にエクスプローラーでリネームすればOKです。

やったぜ!!

と言いたいところですが、これでVSを起動すると、VS上でソースコードをフォルダ分け等をして整理して管理していた情報等は失われソースコードが整理もされずにプロジェクト内に入った状態になります。これをもう一度フォルダ分けしていったりするのは、正直面倒ですよね…

しかし大丈夫

"Project\RPG\OsageRPG"直下にある"RPG.vcxproj.filters"がこのフォルダ分けなどの情報を持っているのでこいつを"OsageRPG.vcxproj.filters"とリネームしてやればVS上では今までどおりの状態に戻ります。

仕上げ

残党狩り

さて、大体のリネームは終わりましたが、一部まだおさげになっていないファイルたちがいると思います。そいつらもおさげにしてやりましょう。

ソリューションフォルダ

Projectフォルダ直下のソリューションフォルダは普通にリネームしても構いません。ここまで"RPG"のままにしていたのは説明で混乱を避けるためでした。

ソリューションフォルダ直下

おそらく.suo.sdfという拡張子のファイルが未だにリネームがされていないと思います。

.suoは「ソリューションユーザーオプション」の略で、VSで行ったブレークポイントなどのユーザーレベルのカスタマイズ情報を持っています

.sdfインテリセンス用のキャッシュDBファイルです。おそらく「ソリューションデータベースファイル」とかその辺の略じゃないでしょうか。
.sdfは放っておいて開発していても自動で何処かのタイミングでリネーム後の名前のものが生成されると思いますが、何にしてもどちらもエクスプローラーでリネームして構いません。

プロジェクトフォルダ直下

.opensdfがリネームされていないかと思いますがこれもリネームして構いません。
.vcxproj.userもリネームされていないかと思いますが、これはデバッグと配置の設定などのユーザー固有のプロパティが格納されたもので、特定のユーザーのすべてのプロジェクトに適用されます。これもリネームして構いません。

おしまい!

おそらくこれですべてのリネームが完了したでしょう。お疲れ様でした。.exe等は残ってるかもしれませんがその辺りは当然わかると思いますので自分でお願いします。

余談

リネームソフト

ここまで手動でのリネームの仕方を書いておいてなんですが、名前を指定したら自動でリネームしてくれるVisual Studio Project Renamerというソフトが既にあるそうです。 僕は使ったことがないのでよくわかりませんが、おそらくこのソフトを使ったほうが楽でしょう。使ってみて良かったら教えて下さい。

namespaceは変更されていない?

.vcxprojファイルをテキストエディタで開いてみると<RootNamespace>タグで囲まれた部分が旧名称のままになっています。ここを手動で書き換えて良いのかは調べてもよくわからなかったのですが、気になる人は試してみても良いかもしれません。名前からするにrootnamespaceの設定だろうので問題は無いとは思うのですが…

参考文献

http://d.hatena.ne.jp/oira3ryu/20110828/1314518629

https://blogs.msdn.microsoft.com/vcblog/2010/03/09/intellisensebrowsing-options-in-vc-2010/

https://msdn.microsoft.com/ja-jp/library/xhkhh4zs.aspx

https://msdn.microsoft.com/ja-jp/library/ee862524.aspx

http://stackoverflow.com/questions/12188975/how-to-rename-the-name-of-project-in-vs2010

*1:.vcxprojファイル内のタグで囲まれた部分が変わる