2014年07月26日

自販機の難易度が高すぎる

数年前から、タッチパネル式の自動販売機をJRの駅で見かけるようになりましたね。
よく見てみると、タッチ操作しなくてもジュースが選べるようにカーソルボタンと決定ボタンがついていることに気が付きました。
試しにカーソルボタンを押してみたのですが、画面に全く反応がありません。あれ?と思ってポチポチやっていていたのですが、よくよく見てみると、うっすらと、本当にうっすらとスポットライトのようなものがジュースに当たっていることに気が付きました。カーソルボタンを押すことで次々とスポットライトの位置が変わっていくのですが、とにかくよくみないと分かりません。元々明るい背景に対して白いスポットライト画像が加算合成で重なっているため、全く目立たなかったというわけです。
カーソル選択モードのときだけ背景を暗くするとか、逆にスポットライト以外の部分を薄暗くするとか、どうにかしたほうがいいんじゃないかと(というか、変にオシャレにしないで素直に目立つ色のカーソル枠を表示したほうがよいと思う)。
アップデートとかで直るんですかね?
posted by JUNOSOFT at 01:42| Comment(0) | 雑談

2014年06月10日

文字列定数


Lua スクリプトなり HLSL なり、短いコードを外部ファイルに依存せずに動かしたい時には

 const char *lua_script = "function add(a, b) return a+b; end; function sub(a, b) return a-b; end";

などと書くわけですが、これだと見にくいので無理やり改行して

 const char *script = " \
  function add(a, b) return a+b; end \
  function sub(a, b) return a-b; end \
 ";

とかやっていたのですが、行の最後をエスケープするのて結構面倒なんですよね。

そこで、最近になって知ったのは #define STR(x) #x を使う方法です。
define の引数を使うときに # をつけると、引数がそのまま引用符でくくられるのですが(assert の警告文とかで使われてますよね)
これをうまく使ってやろうというやつです。つまり

 #define STR(x) #x
 const char *script = STR(
  function add(a, b) return a+b; end
  function sub(a, b) return a-b; end
 );

と書いてやれば、たとえ式の途中で改行していても問題なく引用符の中に入り、文字列定数になるというわけです。便利ですね。
もっと早くに知っていれば...

ちなみに

 #define STR(x) #x
 const char *script = STR(
  function add(a, b) return a+b; end // コメント
  function sub(a, b) return a-b; end // コメント
 );

のように定数内部にコメントを書いた場合、このコメント部分は文字列定数には入りません。しかし、

 #define STR(x) #x
 const char *script = STR(
 #if 1
  function add(a, b) return a+b; end
  function sub(a, b) return a-b; end
 #endif
 );

のようにプリプロセッサ命令を書いた場合は、文字列定数にもそのまま含まれています。
この動作がコンパイラ依存かどうかはわかりませんが、少なくとも MSVC ではそうなってました。

また、ソース上での改行文字はすべて無視されます。全部つながった1行の文字列定数として扱われるため、
HLSLのように改行が関係ないコードだったらよいのですが、lua のように改行が命令の区切りになっている言語では注意が必要です。

Lua の場合、正しく動かすためには改行文字を文字列定数にきちんと含めるか、

 #define STR(x) #x
 const char *script = STR(
  function add(a, b) return a+b; end \n
  function sub(a, b) return a-b; end \n
 );

常に ; をつけて命令の区切りを明示する必要があります

 #define STR(x) #x
 const char *script = STR(
  function add(a, b) return a+b; end;
  function sub(a, b) return a-b; end;
 );

posted by JUNOSOFT at 08:44| Comment(0) | プログラミング

2014年06月06日

GLFW

GLUT にはよくお世話になっているのですが、ウィンドウを閉じても閉じきらなかったり(?)、
マルチウィンドウ非対応とか、ウィンドウハンドルを取得する関数がないとか、不満もいくつかありました。

もう何年もメンテナンスされてないし、これ以上の進化は望めそうにありません。
GLUT の後継ぽくなっている freeglut でもいいんですが、もう少し素直に使えるやつがないかなーと思っていたら、GLFW というものがあるみたいですね。
マルチウィンドウにも対応しているし、ウィンドウハンドルもとれるぽいし、さらに GLUTと違ってウィンドウにポインタを一つ保持させることができるので、
クラスと組み合わせて使うとが簡単にできそうです。次からはこれでテストプログラムを書いてみようと思います。

http://www.glfw.org/docs/latest/index.html
posted by JUNOSOFT at 00:01| Comment(0) | プログラミング

2014年05月18日

win32+

いまさらな感じだと思いますが、最近 Win32++ をよく使っています。

wxWidgets はなんかデカいし、Qt は QtCreator 使わずに VC++ だけでやると面倒くさそうだし、CLR は楽だけどクライアント側に .NET 必須なのがなんとなく嫌だし、C# も C# だけで作るなら楽だけど C++ 資産と一緒に使うと面倒だし。

Win32++ の、余計なことをしないで最低限APIを包み込むだけな感じが大好きです。
これでいいんだよ、これで…
posted by JUNOSOFT at 08:36| Comment(0) | プログラミング

2014年04月28日

SurfacePro2アップデート

ぜんぜん気が付かなかったのですが、Surface Pro 2 でこんなアップデートが来てたみたいですね
http://www.digitalgrapher.com/2014/04/type-cover-2windows-81-update-1.html

スライドパッドがとにかく使いづらいというのは激しく同意です。アップデートで少しはマシになるかな…?
posted by JUNOSOFT at 04:21| Comment(0) | 雑談

2014年04月22日

続 SurfacePro2

Surface Pro 2 がようやく届きまして、普段デスクトップでしか使ってなかった Win8.1 を初めて本格的にタッチパネルで使ったのですが…

タッチキーボードが微妙すぎる…

まず、デフォルト状態のタッチキーボードだと、上下のカーソルキーとファンクションキーが付いていません。文字を打つのに特化している感じです。
(※ちなみに、このタッチキーボードは「Windows簡単操作」のスクリーンキーボードとは異なります)

そこで「ハードウェアに準拠したキーボードにする」を設定すると、通常のキーボードとそっくりな外観のタッチキーボードが出てきます。
ファンクションキーだろうが Alt だろうがやりたい放題です。ただ… なぜかアンダーバーがないんですね…。

そこで、タッチキーボードではなく、「Windows簡単操作」で使えるようになるスクリーンキーボードを出してみると、ディスク購入したほうの Win8.1 で使えるスクリーンキーボードと微妙に異なるんですこれが。
アンダーバーのキーが見当たらないんです。ディスク版 Win8.1(正確にいうと、ディスク版 Win8 購入後に 8.1 にアップデートしたやつ) のスクリーンキーボードではちゃんと右Shift の横に「ろ」のキーがあり、それにアンダーバーもついているのですが、Surface Pro 2 の Win8.1 だと、そもそもスクリーンキーボードにひらがなが表示されていません。記号とアルファバットのみが刻印されている感じです(しかし英語版キーボードというわけでもない)

Google先生に聞いてみるといくつか似たような質問があるのですが、「ろ」のキーにアンダーバーがあるでしょ何言ってんすか みたいな冷たい回答があるだけです。正直よくわかりません。
ディスク版 Win8.1 なら確かにそのキーがついてるんですけどね、Surface Pro 2 のやつにはないんですよ!!

なにか見落としているだけという気もしますが。

結局いまのところ、アンダーバーを打つにはデフォルト状態のタッチキーボードに戻して記号入力モードにして打ち込むか、日本語で「あんだー」と入れて変換するかしかありません。
うわあああああああああああ
posted by JUNOSOFT at 02:43| Comment(0) | 雑談

2014年04月11日

ベジェ曲線の微分

さて今回、とあるキャラクターを動かす時に、その移動経路をベジェ曲線で指定するようにしてみました。
移動そのものはサクサクできたのですが、キャラクターが常に進行方向を向くようにするという処理で少々時間がかかってしまいました。
最初に思いついたのは、実際の直前の位置と現在位置から移動量 (dx, dy) を取得し、これを逆三角関数 atan につっこむ方法です。当たり前ですが、これだと移動量が (0, 0) になった時(つまり停止中)に向きを定義する事が出来ません。
なら、移動中だけ向きを更新して停止中は向きを変更しなければよい、ということになります。しかしそれでは生成直後の向きがやっぱり分かりません。もちろん初期角度を決めても良いのですが、手入力した初期角度と実際の移動向きが一致しないと向きがパッと切り替わったように見えてしまいます。
「初期角度は違和感がないように注意して入力してね」で解決しますが、なんだか悪い予感しかしません。

そして「ベジェ曲線といえどもプログラムで描画するときは折れ線で近似しているんだから、その折れ線の向きをそのまま使えば良いじゃないか」という当たり前の結論になったわけですが、この折れ線、描画したものは滑らかに見えても、その向きにキャラクターを自動回転させると、キャラクターの向きがカクカク変わっているように見えてしまいます。

どうしようかなーと考えた時、やっと思いついた(というか真っ先に思いつくべきだった)のですが、そもそもベジェ曲線は媒介変数 t を使った x と y の式であって、知りたいのはその接線の向き(傾き)なのだから、このを微分するだけで問題解決なわけです。

というわけで

0 <= t <= 1
A = (x0, y0)
B = (x1, y1)
C = (x2, y2)
D = (x3, y3)

とおいたとき、ベジェ曲線の式は

P = A (1-t)^3 + 3 B (1-t)^2 t + 3 C (1-t) t^2 + D t^3

これを展開して t についてまとめると

P = (-A + 3B - 3C + D) t^3 + 3(A-2B+C) t^2 + 3(-A + B) t + A

これを t について微分すればよいので

P' = 3(-A + 3B - 3C + D) t^2 + 6(A-2B+C) t + 3(-A + B)

で接線の傾きを求める事が出来ます。
posted by JUNOSOFT at 09:57| Comment(0) | プログラミング

2014年04月10日

バージョン管理ツールのコミットログ

バージョン管理ツールを使っていると、当然コミット時にコミットログを書くことになるわけですが、特にフォーマットがあるわけでもなく、自由に思いつくままに書いていました。
が、ログを見返した時わかりづらい部分が多くなってきたので、そろそろ統一しようかなあと考えていたところです。

あまり細かいフォーマットだとログを書くこと自体が面倒になるし、あまりに大雑把だとログの意味が無いしで悩んでいたのですが、結局、変更種類と変更目的、場合にり変更箇所を書くのがよい、という結論になりました。

「変更種類」は以下の4つ:
追加 → 今までになかった動作を追加した
変更 → 動作内容が変化した(仕様が変更された)
修正 → 間違っていた動作を直した(仕様に変更なし)
リファクター → 動作に影響を与えない変更全般。ソース整形、クラスわけの修正など。コメント修正、タイプミス修正も含む


「変更目的」は、つまり変更理由です
簡潔に、「〜のバグ修正」とか、「○○が△△になるよう変更」 とか、そんなふうに書いています


「変更箇所」は差分を見れば一目瞭然なので、基本的には書かなくていいのですが、「差分を見ないとわからない」というのもアレだし、ログ一覧を見たときにすぐにわかるようにしたいので、一言だけ変更箇所を書いておくようにしています。
「○○クラス」とか、あるいは単に「描画関数」とか、本当に一言。変更箇所が多数にわたる場合は省略してます(それこそ差分をみてくれ、という感じ)

posted by JUNOSOFT at 06:18| Comment(1) | 雑談

2014年03月20日

AntTweakBar

AntTweakBar というGUIライブラリが使いやすいと某所で聞いたため、試しに入れてみました。
OpenGL でも Direct3D9, 10, 11 でもどれでもOKというのがいいですね。
以下、AntTweakBar をスタティックライブラリでリンクするときのメモです (VisualC++2013 にて)

http://anttweakbar.sourceforge.net/doc/ からソースをダウンロード
・src フォルダ内のプロジェクトファイルを開く
・構成プロパティを変更する
  構成の種類を「スタティックライブラリ(.lib)」に変更
  Debug版ならターゲット名を「$(ProjectName)Static_d」に変更し、Release版なら「$(ProjectName)Static」に変更
  ターゲットの拡張子を「.lib」に変更
  プラットフォームツールセットを「Visual Studio 2013 - WindowsXP (v120_xp) に変更(WindowsXPにも対応させる場合)
  プリプロセッサの定義の TW_EXPORT を TW_STATIC に置き換える
・ビルドする
・出来上がったファイル AntTweakBarStatic_d.lib と AntTweakBarStatic.lib を、ライブラリパスの通っている場所にコピー
  
  
・自分のプロジェクトの設定で、プリプロセッサに TW_STATIC と TW_NO_PRAGMA を追加する
・Debug 版なら AntTweakBarStatic_d.lib にリンクし、Release 版なら AntTweakBarStatic.lib にリンクするようにしておく
  例:
   #ifdef _DEBUG
   # pragma comment(lib, "AntTweakBarStatic_d")
   #else
   # pragma comment(lib, "AntTweakBarStatic")
   #endif

デバッグ用のGUIとしてゲームに組み込んだりして使えそうです

posted by JUNOSOFT at 21:59| Comment(0) | プログラミング

2014年02月25日

複数のウィンドウに対する描画2

以前複数ウィンドウに対する描画について書きましたが、実際に試した結果いくつか間違っていた点がありました。
というわけで、ひとつのデバイスで複数のウィンドウに描画する手順の修正版を以下に示します。

■ 準備する

 HWND hWnd1 = ウィンドウ1
 HWND hWnd2 = ウィンドウ2
 
 // プレゼントパラメータの作成
 D3DPRESENT_PARAMETERS pp1, pp2;
 pp1.hDeviceWindow = hWnd1;
 pp2.hDeviceWindow = hWnd2;
 ... 設定つづく ...

 // 普通にデバイスを作成
 D3D9->CreateDevice(..., &pp1, &Device);

 // ウィンドウ1に描画するために、最初のスワップチェーンを取得
 Device->GetSwapChain(0, &SwapChain1)

 // ウィンドウ2に描画するために、追加のスワップチェーンを作成して取得
 Device->CreateAdditionalSwapChain(&pp2, &SwapChain2);


■ hWnd1 に対して描画するとき

 // スワップチェーン1のバックバッファを描画先にする
 IDirect3DSurface9 *back;
 SwapChain1->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &back);
 Device->SetRenderTarget(0, back);
 back->Release();

 Device->BeginScene();
 // いろいろ描画
 Device->EndScene();

 // hWnd1 に転送
 SwapChain1->Present(NULL, NULL, hWnd1, NULL, 0);


■ hWnd2 に対して描画するとき

 // スワップチェーン2のバックバッファを描画先にする
 IDirect3DSurface9 *back;
 SwapChain2->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &back);
 Device->SetRenderTarget(0, back);
 back->Release();

 Device->BeginScene();
 // いろいろ描画
 Device->EndScene();

 // hWnd2 に転送
 SwapChain2->Present(NULL, NULL, hWnd2, NULL, 0);


これでOKです。
・CreateAdditionalSwapChain で作成したスワップチェーンは、Direct3DDevice9::Reset のリセット前に破棄し、リセット後に再生成する必要があります。
・エラー処理を省略しているので、実際に使う場合は適当にエラーチェックしてください

posted by JUNOSOFT at 17:27| Comment(0) | プログラミング