プロフィール

Na-7

Author:Na-7
SE(システムエンジニア)として約15年間システム系ソフト会社を勤めあげ、2008年3月退社。現在、ゲーム制作会社設立を目指して活動中。


アクセスカウンター


最新記事


最新コメント


最新トラックバック


月別アーカイブ


カテゴリ


DATE: CATEGORY:スポンサー広告
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
DATE: CATEGORY:三国志軍記開発
ネームプレートの改良02
開発中のメイン画面
ネームプレートの文字が、近い部隊は大きく、遠い部隊は小さく表示されるようになった。カメラ移動に伴う不自然な表示も緩和されたようだ。



◎ネームプレートの画像修正

画面の解像度を大きくしたので、文字やネームプレートも大きくしましょう。

ネームプレートの改良01

文字が大きくなって読みやすくなりました。



◎ネームプレートの座標修正

カメラが移動すると、カメラに映ったメイン画面の地形/部隊/ネームプレート等も移動します。しかしこの時のネームプレートの移動が不自然な感じで、以前から気になってました。これを機に修正しましょう。

私が考えた案はこんな感じです。

ネームプレートの配置座標案



◎試してみる

最初はクォータニオンを試してみました。

Quaternion q = Quaternion.CreateFromYawPitchRoll(0, MathHelper.ToRadians(90), 0);
Vector3 targetPos = Vector3.Transform(unitPos, q);


しかし計算結果を確認すると、全然違いました。どうやら、CreateFromYawPitchRollとCreateRotationX/Y/Zは同じっぽいです。別物だと思ってました(爆)

クォータニオンにはCreateRotationX/Y/Zが無いので、MATRIXによる比較テストで確認しましょう。

XNAのコードと実行結果

おや?値が異なるケースがあるなぁ…ははぁ、これがいわゆるシンバルロックというヤツですね?
ちなみに、45度だと同一の値でした。



◎三角関数の応用

MatrixやQuaternionのメソッドを一通り調べたのですが、簡単に算出する方法がわかりません。仕方がないので、三角関数を地道に組み合わせて計算することにしました。

XNAのコード

この程度のプログラムなのに、作り上げるまで数日悩みまくりでした(爆)

で、いざカメラを動かしてみると、ネームプレートの位置が不自然です。スケールレート(カメラから部隊までの距離が基準)を調整すれば良くなりそうですが、それならスクリーン座標とスケールレートの調整だけで十分でしょう。

…うわー何無駄なことやってんだオレ!(連爆)

ネームプレートの改良02

最後は満足できる状態になったものの、こんなことで悩みまくった自分自身に自己嫌悪(ーー;



◎次回予告

今月前半まで開発好調だったのに、回転について考え始めたら、急に調子悪くなってきました。昔は三角関数得意だったのに…って20年前の話じゃアウトかw

次回は城の座標修正、地形テクスチャ修正などを行う予定です。

スポンサーサイト

テーマ : ゲーム製作 関連 - ジャンル : ゲーム

コメント

ジンバルです(汗)

図を拝見しましたが、ベクトルAとオブジェクトの座標Pで直行するベクトルBはそもそも1つだけじゃないです。鉛筆の先に別の鉛筆をくっつけて、90度を保ったままくるくる回すことができるのをイメージしていただければ分かります(ここが2次元の紙の上で考えた場合と異なる)。「Pを通り、Aと直行する」のは直線ではなく平面なのです。何らかの制約条件を与えて平面上のどれか1つの直線に絞りこむ必要があります。

またベクトルAについてX=0でない(横に斜めの)ケースもあるのだから、単純にX軸を中心に90度回転するだけだとそちらの思ったイメージとは違う挙動になってしまうような。この辺も、感覚がつかめるまでは鉛筆や割りばしで実験した方がいいです。

>値が異なるケース
1.383763E-06と-1.01479E-06かな? これはE-06(100万分の1)ですから、単なる誤差だと思います。doubleでなくfloatなのでそのくらいは出るはすです(ジンバルロックは回転同士を合成する場合に起こる問題で、回転を1つ作るだけなら関係ない)。

ご指摘ありがとうございます

危うくシンバルと憶えるところでした(汗)

>直線ではなく平面
ここまでは理解してて、平面の中でy座標が一番高い直線に絞り込むことを考えてました。

>横に斜めのケース
ご指摘の通り、3D回転の感覚が把握できてません。
実はメタセコで図形を斜めに30度回転させる実験などもやったのですが、回転後の頂点座標予想値がハズレてちょっとショックでした(^^;

>値が異なるケース
えっ!?これは誤差なんですか?
…すみませんE-06見過ごしてました(爆)

あらためてジンバルロックが気になったので、こちらで体験して確認しました。
http://wonderfl.net/c/uTHS
なるほど、1度回転させるだけなら発生しない現象ですね。

>ここまでは理解してて、平面の中でy座標が一番高い直線に絞り込むことを考えてました。

それなら特にややこしく考える必要はなく、ベクトルAをzx成分とy成分に分離していったん正規化し、それぞれの長さを調節すればいいのです。90度回転なら、zx成分とy成分の比を入れ替え、符号を逆にするだけでOKです。

Azx:-Ay = By:Bzx
※zxはそちらが用意された図の横軸、yは縦軸。

こんな感じでしょうか?

// 元のベクトル
Vector3 vecA = new Vector3(2.0f, 5.0f, 3.0f);

// ベクトルをZX成分とY成分に分離する
Vector3 vecZX = new Vector3(vecA.X, 0, vecA.Z);
Vector3 vecY = new Vector3(0, vecA.Y, 0);

// いったん正規化する
Vector3 normalZX = Vector3.Normalize(vecZX);
Vector3 normalY = Vector3.Normalize(vecY);

// それぞれの比率を算出する
float rateZX = vecZX.X / normalZX.X;
float rateY = vecY.Y / normalY.Y;

// 新しいベクトル
Vector3 newY = normalY * rateZX;
Vector3 newZX = - normalZX * rateY;
Vector3 newVec = new Vector3(newZX.X, newY.Y, newZX.Z);

はい、それで…ち、違うぞ!? 危うくそれでいいと言ってしまうところでした(笑)。

float rateY = vecY.Y / normalY.Y;

normalY.Yは常に1です。比と言ったのは「正規化前と正規化後の比」ではなく、「正規化前のZX成分の長さとY成分の長さの比」です。

適当な係数aをきめてやって、

var lenZX = VecZX.length();
var lenY = VecY.length();
Vector3 newY = normalY * lenZX * a;
Vector3 newZX = normalZX * lenY * a;

のような感じで。normalYとnormalZは長さ1なのであくまで向きだけで、長さは巣からであるlen*aで決まるのがポイントです。lenY.Length()で絶対値を取っているのでYの符号も自動的に反転されています。で、aの決め方です。例えばneyYの長さを10にしたいのであれば、a=10/lenZXなので、

Vector3 newY = Vector3(0, 10, 0);
Vector3 newZX = normalZX * lenY / lenZX * 10;

とすればよいわけです。lenY/lenZXを掛けることで比率が逆転し、90度回転したのと同じことになるわけですね。

頭の悪い生徒ですみません

// 元のベクトル
Vector3 vecA = new Vector3(1.0f, 1.0f, 1.0f);

// 係数
float a = 1.0f;

// ベクトルをZX成分とY成分に分離する
Vector3 vecZX = new Vector3(vecA.X, 0, vecA.Z);
Vector3 vecY = new Vector3(0, vecA.Y, 0);

// vecZXとvecYの長さを取得する
var lenZX = vecZX.Length();
var lenY = vecY.Length();

// いったん正規化する
Vector3 normalZX = Vector3.Normalize(vecZX);
Vector3 normalY = Vector3.Normalize(vecY);

// 新しいベクトル(パターン1)
Vector3 newY1 = normalY * lenZX * a;
Vector3 newZX1 = normalZX * lenY * a;
Vector3 newVec1 = new Vector3(newZX1.X, newY1.Y, newZX1.Z);

// 新しいベクトル(パターン2)
Vector3 newY2 = new Vector3(0, a, 0);
Vector3 newZX2 = normalZX * lenY / lenZX * a;
Vector3 newVec2 = new Vector3(newZX2.X, newY2.Y, newZX2.Z);

(1, 1, 1)は(-1, 1, -1)になるべきでしょうが、パターン1もパターン2もそうなりません。何が悪いのかさっぱりです(--;

以前の図のベクトルAとベクトルBを見る限り、ZとXの符号はそのままで、Yだけ逆転するのが正しいのではないでしょうか。

えっ!?

……そうか、カメラの位置を基準に考えるべきだったのですね!ユニットの位置を基準に(ベクトルAの向きを逆方向に)考えてました(爆)

ということは、(1,1,1)は(2,0,2)に…じゃなくて、ベクトルBは(1,-1,1)ですね。仰る通りです。

ただ、パターン1もパターン2も、そうならないような…?

右手座標系ですからベクトルAは(1,1,1)とかでなく、例えば(0,-1,-10)などになるのでそうした適切な値を例にとったほうがいいですね。

あ、と言うことはカメラがやや下向きならnormalY.Yは負になりますから、やはり-1して符号を逆転させないとダメかも知れません。

理解できました!!

私の間違いは下記2点でした。

・原点をユニット位置で考えていた
 →原点はカメラ位置

・A(1,-1,1)→B(1,1,1)と思っていた
 →正しくはB(0.7071068, 1.414214, 0.7071068)

もっと早く気付けるようにならないといけませんね(汗)

yohさん!最後まで御教授頂き、まことにありがとうございました!

コメントの投稿


管理者にだけ表示を許可する

トラックバック


この記事にトラックバックする(FC2ブログユーザー)



copyright © ゲーム制作の舞台裏 all rights reserved.Powered by FC2ブログ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。