電源タップをイオンで25元(約500円。相当高い)で買ったものの、
しょっぱなからLEDすらつかないので中身を見てみたら、見事に
半田が剥離してた。
もう見た目からして品質が怪しかったけど、一旦日本に帰るまでの
辛抱だと思って買ってみたら、これ以上ないくらい見事なまでの
ちゃいなくおりてーだった。これはもう、中学校の技術の授業とかに
教材として使えるレベルなんじゃないかと思った。
ちなみに、日本だったら100均でももっとマシなのが売ってる。
ホントにちゃいなってくおりてーが低いんだな・・・。まともな
店だからまともなものが売ってるかと思ったら、「安かろう悪かろう」
じゃなくて「高くても悪かろう」だったとは。この言葉は日本にしか
通用しないらしい。
なお、右に張ってあるのは見てのとおり、合格証である。
検査の瞬間さえ通過すれば合格であろうことは言うまでもなかった。
タクシーに乗って目的地を言ったら「How much?」と聞かれた。
こっちのセリフだ。
ただ、これには裏があるようだ。
タクシーはメーター使うと会社の利益として計上されるため、
歩合制だったとしても給料に反映される取り分は極めて少なく、
せいぜい1/20や1/30だろう。例えば、日本円で3000円の売り上げを
出したら300円その月の給料が増えるとか。
しかし、仮にメーターを回さなかった場合、それは監視されて
いない限りは空車のまま回送しているのと同じことになる。
つまり、その間に客を乗せていようがいまいが、稼ぎはゼロだ。
当然会社への売り上げにもならない。
しかし、敢えてメーターを回さないで客を乗せた場合はどうなるか。
自分が走ったことで儲けたお金を、管理元であるタクシー会社が
8割9割ピンハネする代わりに、車を貸して商売させていて、福利厚生
(があるかは知らないが)を保障するのが本来の会社契約だろう。
逆に言えば、売り上げの大半は会社にピンハネされるということ。
だがメーターを回さなければ客は乗っていないことになるので、
売り上げにはならないので自分の営業成績は悪化するが、ピンハネ
されることもない。
よって、最低限の成績を確保できたなら、後はメーターを回さず、
客をひたすら拾っては直接金を受け取れば、自分は大もうけという
ことになる。もちろん契約違反だ。しかし日本のように、いちいち
そういうことをするタクシーを通報するようなクソ真面目な人間など
いないし、今どこを走っているか監視もないので、やりたい放題だ。
ところが、メーターを回さないと最終的な金額が分からない。だから、
「俺はメーターを回す気は無い。そこに行くとして、いくらまでなら
出す?」という意図で「How much?」が出てきたのだろう。
なので、本来なら一度走れば大儲けなのだから、相場よりもかなり安め
でもOKするはずだ。なので、タクシー側から「How much?」と
聞かれて、目的地までの相場が分からないのであればふっかけられる
可能性があるため、そのタクシーは断る方が無難だ。
逆に、相場をしっかり知っているなら、かえって安くタクシーが
利用できるチャンスでもある。元々正規のタクシーは白タク(モグリ)
よりも遥かに安いので、ある意味でありがたい。こんなのは中国では
日常もいいところなんだろう。
注意点は、正規な料金が分からないため領収証を切ってくれないこと。
領収証が欲しいなら、メーターを使えと指示するべし。
DOFさんへの返信用記事です。
以下のコードは、CJからもっとも近くにある乗り物のハンドルを、
自分で追加したメモリプールの0x000番地に書き込むためのものです。
まず、一番下のMemoryPoolルーチンでメモリを確保します。0が8つあるので
最大0xFFFFFFFFまで、つまり4byteデータを1つ格納できます。それを5つ
用意してあります。GTAで扱う変数は、1つ当たり4byteあれば充分なので、
私は基本的に4byteメモリを1変数として使っています。
次にGetPointerルーチンは、メモリプールのMemoryPoolの位置を調べて
そのメモリ上のアドレスを変数30@に書き込む処理です。csの最初に
gosub @GetPointer
と書いておけば、以降30@を基準として追加メモリにいつでもアクセス
できるようになります。なので30@はcs内では絶対書き換えないように
しましょう。
そしてGetNearVehicleを呼び出すと、現在画面内に生成されている乗り物
全てを、ハンドルの数値が若い順から1つずつ探していきます。もし1つ
見つかったら、その乗り物とCJの距離を計算し、記憶します。同時にその
乗り物のハンドルも記憶しておきます。そして次の乗り物を探し、また
その乗り物との距離を計算し、もし現在記憶している距離よりも短ければ
上書き記憶します。長ければ無視します。これを全ての乗り物に対して
実行すれば、最期に記憶された乗り物はCJとの距離が一番近いものです。
ここで、このコードでは「CJとの距離」を条件にしています(赤字部分)が、
これを「規定の直方体の中にある乗り物で、最初に見つかったNGR500」
に変更すれば、GetNearVehicleを呼び出した時に規定の範囲内にNGR500が
存在するかをチェックできます。もし存在していたなら何もしなくていいですし、
1台も見つからなかった時はその範囲内の指定の位置に生成させればOKです。
あとはそのチェックするタイミングですが、これはCJが規定範囲内のある位置から
一定距離離れた瞬間とか、一定時間ごとなど、色々やり方はあると思います。
すいませんが、帰宅したばかりでもう寝ないとヤバいのでこの辺で(´・ω・`)ノシ
かなりアバウトな内容だとは思いますが、ほとんど全てを書いてます。
サンプルコード
--------------------------------
// ************************
// 追加メモリの先頭アドレスを取得する
// ************************
:GetPointer
0A9F: 30@ = current_thread_pointer
000A: 30@ += 0x10
0A8D: 30@ = read_memory 30@ size 4 virtual_protect 0
000E: 30@ -= @MemoryPool
return
// ********************************************************
// もっとも近くにある乗り物のハンドルを0x000に格納する
// ********************************************************
:GetNearVehicle
0A8E: 31@ = 30@ + 0x000
0A8C: write_memory 31@ size 4 value -1 virtual_protect 0
0A8D: 27@ = read_memory 0xB74494 size 4 virtual_protect 0
000A: 27@ += 0x4
0A8D: 27@ = read_memory 27@ size 4 virtual_protect 0
for 28@ = 0 to 27904 step 0x100
0A8D: 26@ = read_memory 27@ size 1 virtual_protect 0
000A: 27@ += 0x1
if and
0029: 26@ >= 0x00
001B: 0x80 > 26@
then
005A: 26@ += 28@ // (int)
if
056E: car 26@ defined
then
0A8E: 31@ = 30@ + 0x05C
0A8D: 15@ = read_memory 31@ size 4 virtual_protect 0
if
15@ == -1
then
0A8C: write_memory 31@ size 4 value 26@ virtual_protect 0
else
00A0: store_actor $PLAYER_ACTOR position_to 10@ 11@ 12@
00AA: store_car 15@ position_to 13@ 14@ 15@
00AA: store_car 26@ position_to 16@ 17@ 18@
050A: 19@ = distance_between_XYZ 10@ 11@ 12@ and_XYZ 13@ 14@ 15@
050A: 20@ = distance_between_XYZ 10@ 11@ 12@ and_XYZ 16@ 17@ 18@
if
0025: 19@ > 20@
then
0A8C: write_memory 31@ size 4 value 26@ virtual_protect 0
end
end
end
end
end
return
// ************************
// 追加メモリのプール (5個分)
// ************************
:MemoryPool
hex
00000000 00000000 00000000 00000000 00000000
end
--------------------------------
できましたヾ( ゚д゚)ノ゙
地味な変更ですが、フェードモードが以前よりも滑らかに動くようになっています。
また、微少な距離での動作も不正な動きをすることなく、正確に動作します。
このエンジンは下で書いた原理をそのまま実装してあります。
なお、動作にはCLEO4のインストールが必須ですのでご注意下さい。
ブログページ
http://blogs.yahoo.co.jp/usualgamer/32738694.html
間が空いたけど、続き書くよ!
前回のおさらい。
・・・①
こういう式がありまして、これをグラフに描画すると
こうなるんでした。でも、これは入力するべきxが-6~+6で、使いづらい。
xの値域を0~1にして、適宜ゲインをかけて調整できるようにしたいってことでした。
ちなみに、こんな風に使いやすい値(1とか)で処理できるように改良することを
「正規化」と呼びます。正規化という言葉は意味がわかりにくく、難しい印象がありますが、
「~をまとめて1と考えると」などの思考は皆さん日常でも頻繁に使っていると思いますが、
それがまさに正規化です。鉛筆12本を1ダースとして処理するのも、一種の正規化です。
てことで、上の式を「正規化」して
・・・②
という式を作りました。これは要するに、zに0~1を入れたとき、①の式でxに
-6~+6を入れたときと同じ結果になるようにしようというのが目的でした。
xにその値域を入れるということは、-ax(eの指数)の結果は
※a=1とする
x=-6のとき、-ax=6
x=±0のとき、-ax=0
x=+6のとき、-ax=-6
です。
では、zを入れてこうなってくれるでしょうか。
z=0のとき、eの指数=6
z=0.5のとき、eの指数=0
z=1のとき、eの指数=-6
ちゃんとできてますね。それじゃ、Yはどうなるんでしたっけ。
z=0(x=-6相当)のとき、Y=1/(1+e^6)=1/403.43 ≒ 0
z=0.5(x=0相当)のとき、Y=1/(1+e^0)=1/2=0.5
z=1(x=+6相当)のとき、Y=1/(1+e^-6) ≒ 1/1=1
はい。見事にzが0~1のとき、Yが0~1になっています。
ちなみに、これをEXCELでグラフに描いてみた結果がこちら。
線が3本ありますが、これは今まで「a=1」と言ってきた、いわゆるゲインを
調節してみたものです。このaはeの指数全体に掛けるものなので、やっていることは
eの指数部分「6-12z」を「a*(6-12z)」としているだけです。最終的には、このaを
GTA内で調節することにより、こんな感じでカメラの加速の仕方をある程度調節
できるようにします。
もう少し進めましょう。
GTAは3Dですから、座標も当然X・Y・Zの3つがありますが、基本的には、
上にある式を3つの軸それぞれに同じように適用させて処理します。
3軸別々のゲインを設定できるようにすればかなり柔軟なカメラワークが作れると
思いますが、その調整はイメージしにくく非常に面倒になることは間違いないので、
3軸独立ゲインは実装しないことにします。
さて、それでは具体的にGTA上で話をしましょう。
仮に、カメラワークを以下のように実行したいとします。
初期値(始点)
X:2500.0
Y:1300.0
Z:20.0
目標値(終点)
X:2550.0
Y:1400.0
Z:25.0
※ここでのXというのは、GTAの3D座標を示す変数です。これまでにやってきた計算式の
Xとは意味が違いますので注意してください。
GTAでは、この座標の単位1が約1m程度のようなので、この動きはざっくりと言って
けっこう大きな動きです。Zも20から25なので、地上から屋根の高さくらいまで動くことを
意味します。まずはこれを、1000フレームで動かしてみようと思います。
目標値まで動かすということは、初期値から、進んだフレーム分の変化量を足すという
ことです。0フレーム目であれば変化量は0ですから、初期値+0、つまり初期値と
同じ、要するにまだ全然動いていないということを示します。
逆に1000フレーム目であれば、「終点-始点」をまるまる変化したわけですから、
③
Xの変化量=2550-2500=50
Yの変化量=1400-1300=100
Zの変化量=25-20=5
となりますね。その変化量を初期値に足すので、変化した結果
変化後のXの値=2500+50=2550
変化後のYの値=1300+100=1400
変化後のZの値=20+5=25
となります。当たり前ですね。ここまでは長々と書いてきた計算式なんて使う必要も
ありません。
では、500フレーム動いたときはどうでしょうか?
500フレームというのは、総フレーム、つまり1000フレームのうち、何%でしょう?
これは簡単です。500/1000=0.5=50%です。全体を1としたとき、0.5進んだ地点
ということです。
さあ、出ましたよ。この0.5です。
今まで色々計算式をいじってきました。その目的が「0~1で操作したい」ということ
でしたね。この0~1の値が、この「どのくらい進んだか」を意味するわけです。
つまり、500フレーム動いたときには、この動いた割合0.5を、式に代入して計算すれば
よいのです。
まず、元々「変化する量」とはいくつでしたか?要するに、全部動かしたらどのくらい
でしょうか?それは③で出ていますね。そのうちの半分、0.5なんですから、
「0.5の変化に対して、実際に動く量はいくつか」ということを計算します。ここでいう
変化が、②のXに相当します。実際に動く量の割合が②式でのYです。実際に動く量の
割合が求まれば、「全部動かしたときの変化量」にその割合を掛け算することで、
「実際に動く量」が求められます。
計算してみましょう。
動いた割合が0.5のとき、実際に変化する量に対する、変化するべき量の割合は
変化するべき量の割合=1/(1+e^(6- 12 × 0.5))
=1/(1+e^0)
=1/(1+1)
=0.5
となります。これに「実際に変化する総量」をかけると
変化するべきX=50 × 0.5 = 25.0
変化するべきY=100 × 0.5 = 50.0
変化するべきZ=5 × 0.5 = 2.5
最終的に、500フレーム経過したときのカメラの座標は
500F後のXの値=2500+25.0=2525.0
500F後のYの値=1300+50.0=1350.0
500F後のZの値=20+2.5=22.5
となりました。これが本当に求めたい答えです。
これが出れば、あとはプログラムコードを組むだけです。
とゆーことで、今までの流れを一般化してみましょう。
1、初期値(始点)を記録する
2、目標値(終点)を記録する
3、総変化量を求める(目標値-初期値)
4、総フレーム数を記録する
5、任意のフレームが総フレームのうちどのくらいか、その割合を計算する
6、求めた割合を計算式に当てはめ、実際に動く量の割合を求める
7、「総変化量 × 実際に動く量の割合」を計算し、「実際に動く量」を求める
という流れになります。これこそが「理論を実装に変えた」瞬間です。
ついに、あーだこーだ机上で述べてきた空論が、形を持って結果として出てきました。
ついでに、実際の計算も一般化しておきましょう。
1、ゲインをaとして代入しておく
2、始点をそれぞれ「bX,bY,bZ」とし、それぞれにXYZ値を代入する
3、終点をそれぞれ「eX,eY,eZ」とし、それぞれにXYZ値を代入する
4、総変化量を「dX,dY,dZ」とし、1と2から計算した結果を代入する
5、総フレーム数をtFとして、これに代入しておく
6、nフレーム目のtFに対する割合をrFとし、計算して代入する
このとき、rFは
rF = n / tF
7、実際に動く量を「ΔX,ΔY,ΔZ」として、計算して代入する
このとき、7の値は
ΔX = dX /(1+e^(a×(6-12 × rF)))
ΔY = dY /(1+e^(a×(6-12 × rF)))
ΔZ = dZ /(1+e^(a×(6-12 × rF)))
8、nフレーム目のカメラ座標を「rX,rY,rZ」として、結果を代入する
このとき、8の結果は
rX = bX + ΔX
rY = bY + ΔY
rZ = bZ + ΔZ
はい!できましたー!
このrX,rY,rZが、nフレーム目におけるカメラ座標となるわけです。
GTAは変数に名前を付けられない上に、わずか31個しか使用できないのでこの記事の
ように分かりやすいまま実装することはできませんが、なんとかうまくコーディング
すれば同じ処理を実現できます。
ということで、あとはプログラムの話になりますので以降は割愛。
割愛しないと、何百行ものコードを記事に羅列するだけで、
誰も、どころか自分もわからないと思うので。
こんな感じで、1つ1つ目標に向かって適切に考えて、しっかり理論と数式を結び付けて
いくことで、プログラムコードという実装へ落とし込むことができます。最初は何を
やっていて、どこを目指しているのかわからなくなることも多いと思いますが、その時は
素直に一旦諦めて棚上げしてしまった方がいいです。もっと小型の、シンプルなアイデアを
形にすることを何度か実践して練習し、その流れを身体で覚えた方がいいでしょう。
たとえば、今回のようにカメラワークのエンジンそのものを作ろうとすると、今が
どうなっていて、何が不便で、どうすれば改善できて、そのためには何が必要で、
どれだけ複雑なのか、ちゃんと把握できていないとパンクしてしまいます。今回の
改良も、これまでのSlideCameraWorkという実績があるからこそスムーズに行えたもの
なんだということを知っておいてください。
本当の最初は、確か何かキーを押すと今の視点で固定されるだけ、という極めて
シンプルなものだった気がします。そこから、カメラを動かすコードの存在を知り、
でも2つのコードがセットになっていて、試行錯誤しながら動作を覚えていき、
カメラを移動させる方法を考えたけれども満足できず、もしかして、ものすごく細かく
瞬間移動カメラを連続実行させれば、滑らかなカメラワークとして機能するんじゃないかと
思ったらうまくいって、そこから始点と終点を決定するためのインタフェース作りに
悩んで、変数が多すぎて大混乱の中でメモリ操作を覚えて、そしてカメラの回転なども
改良に改良を重ねて、そして現在のSlideCameraWorkがあるという感じです。Pixivだったら
評価点100点ももらえないような子供のおもちゃレベルのものからのスタートだった
わけです。なんとかなるものですね。
なのでこれが実装へ漕ぎつくための唯一のやり方ではありません。しかし、いい加減な
ところがあってはそこから先へは進まないということは分かってもらえたと思います。
漠然としたやる気だけで事に臨まず、堅実に着実に、真剣に取り組んで作ってみて下さい。
ちょっとずつだけどよくなっていき、次第にできることが広がっていくと思います。
以上、また何かあったらそのときに。
この新しいエンジンを積んだSlideCameraWorkは、なるべく早く公開まで持っていきたいと
思います・・・。がんばる。
.
見ての通りの、円月輪です。
元々は円状のもので、モーションを円形になぞるのを補助するために作ったものです。
つまり、「フランの風切」と同じ、補助ツールになります。
ただ、武器として使えなくもありません。物理演算を入れたので、投げられます。
ダウンロードはこちらから
新しい理論を実装する場合、何から手を付けていいのか分からないと思います。
そんなとき、まず自分がやりたいことを思い出してみましょう。
今回やりたいことは、
● 最初と最後がゆっくりなカメラワーク
ですが、それは具体的にどのように動くでしょうか?
カメラワークを実行するには何が必要で、実行した結果どうなるんでしたでしょうか。
まず、必要なのは
・始点位置
・終点位置
・総フレーム数
・nフレーム目での進行度合いを計算してくれる式(nは任意のフレーム位置)
でした。始点(0フレーム目)から始まり、nフレーム目での進行度が定まり、
(終点-始点)× 進行度 でnフレーム目の現在位置が求まります。これを繰り返す
ことで連続的なカメラワークが実現できるわけです。
よって、実行した結果は
・nフレーム目に、或る位置にカメラが移動する
となります。言葉にしてしまうとシンプルな話ですね。
要するに、「動作」には「入力」と「出力」が存在するわけです。
何かを入力したら、入力したものを加工して何かが出力される。つまり、あらゆる処理の
シーケンス(順序)は
1,初期値を与える
2,初期値による出力の計算
3,結果の出力
4,次の値へカウントアップ、もしくは次の値を計算
5,次の値による出力の計算
6,結果の出力
7,4に戻る。ただし、もし提示しておいた条件を満たしたら終了
となるのです。どんなものでも、これに当てはめれば動作します。動作し得ないのなら、
この1~7の何かが抜けているのです。
では、実際に当てはめてみましょうか。
1,初期値を与える ← 初期カメラ位置(ユーザーが指定する)。これがn=0
2,初期値による出力の計算 ← Yを求める計算式に0を入れる
3,結果の出力 ← カメラ位置を移動させるコードの実行
4,次の値へカウントアップ ← フレームカウントをn+1にする
5,次の値による出力の計算 ← Yを求める計算式にnを入れる
6,結果の出力 ← カメラ位置を移動させるコードの実行
7,条件を満たしたら終了 ← nが総フレーム数と等しくなったら
はい。今までに提示した内容で全部埋まりましたね。
次に、一番重要な中核部分、「Yを求める」です。これを確認してみましょう。
式はコレだと言いましたが、数学やプログラムの関数に触れていない人にはそれでも
ピンと来ないかもしれませんので、実際のカメラ位置も含めて手順をなぞってみましょう。
この「手順を手動でなぞる」というのは極めて大切です。まずはこれを何ループか
繰り返して、全体を把握してから「一般化」を行うのが正しい道筋です。
例えば、Y=X+2がどういう軌跡を成すか分かりますか?
ある程度数学を身に付けた人ならすぐに分かりますが、そうでないと直感的には難しい
でしょう。そういうときは、1つずつ値を入力していくはずです。
X=0 のとき Y=0+2=2
X=1 のとき Y=1+2=3
X=2 のとき Y=2+2=4
こんな感じです。これで、XがY軸と同じ位置にあるとき、Y=2に点があることが分かり、
Xが1増えるごとにYも1増えていくことが分かります。つまり、Y=2を通る、傾きが
1(Xの増分1/Yの増分1)の直線だと言えます。これが一般化です。
まあ、実のところ、上で書いた複雑な式がどういう軌跡を描くかはもう分かってますよね。
はい、こちらですね。X軸が-6~+6になっていますが、将来的には0~1になるように
調整したいです。どうするかって?X=1を入れたら、実際にはX=6を入れたのと
同じになればいいんですよね。さらに、X=0ならX=ー6を入れたのと同じになればいい。
また、X=0.5を入れたらX=0入れたみたいになればいい。
これはつまり、X=0の時はY=ー6,X=1の時はY=6っていう直線を示してるのと
同じですよね。つまり、Y=12Xー6じゃあありませんか。
さて、この(便宜上の)YっていうのはグラフのX軸の値を示してますので、式のaxが
Yだと思えばOKです。ここで、aはただのゲイン(任意の係数)なので1としてしまうと、
実際は-xですから、まとめるとeの指数は
6-12Z(Zは0~1)
となります。つまり、Zを0にすると-xが6になったことになります。-xが6という
ことは、X=-6を入れたのと同義です。Zを1にすると-xがー6になったことに
なります。X=6を入れたのと同義です。つまり、Zに0~1を入れるだけで、上の
グラフのX=-6~+6を行っているのと同じ効果が得られるということです。
なので、これからeの指数は「6-12Z」と置き換えます。
一旦計算してみましょう。上の式のZに、まずは0.5を入れてみます。
σa(x)=1/(1+e^0)
ここで、eの0乗は、累乗はどんな数字でも0乗は1ですから、
σa(x)=1/(1+1)=0.5
となりました。なるほど、グラフ上でもY=0.5ですね。合ってます。
じゃあ、グラフ上の「X=2」の値になるようなZを入れてみます。Zは端から端までが
0~1ですから、X=2に対応するならZ=4/6=0.667ですね。つまりeの指数は
6-12×0.667≒-2です。
σa(x)=1/(1+e^-2)=1/(1+0.135)=1/1.135=0.881
うん、グラフを見ても、0.9にほど近い値ですから、この値も合ってる感じです。
じゃあ、Z=2/6=0.333を入れてみましょう。
σa(x)=1/(1+e^+2)=1/(1+7.389)=1/8.389=0.1192
はい、対象形ですから、これも正しいですね。
それじゃもう一つ、Z=1を入れてみます。eの指数はー6ですね。
これは元々X=6を入れているのと同じ。つまり-axがー6になってるのと同じでした。
σa(x)=1/(1+e^-6)=1/(1+0.002)=1/1.002≒1
成りました。正確な1ではありませんから、正確に1になるのを待っていたらいつまでも
1にならないので、プログラム的にはどこかで値を丸めるなりしてあげる必要があります。
つまり、1/1.002=0.998なので、Yが0.997を超えたらもう終了しちゃおう!という
終了条件にするのです。この辺は、プログラムを知らない人なら「こまっけえなあ」って
思うでしょうが、プログラムは「一切の融通が利かない」ので、やらないとダメです。
ということで、とうとう0~1という入力を与えると、0~1の範囲でYが滑らかに
変化する式が得られました。それがこちら。
Y=1/(1+e^(6-12Z))
です。
これで、次の節で具体的なカメラ座標の計算ができそうですね。
いよいよ実際のGTAの値との調整になります。こうご期待ヾ( ゚д゚)ノ゙
.
一番難しいのは、理論を実際の動作へ落とし込むことです。EXCELだって、1行おきに
1、2、3と入力するのは簡単ですが、その理論をプログラムに落とし込んで、自動で1000まで、
2000行の入力を一瞬でやってくれるようにするには、意外にできないものです。
いわゆる「動くアルゴリズム」ができてこそ、モノづくりと言えます。高尚な理論や理想ばかりを
語る人よりも、稚拙なオモチャでもいいから実際に動くモノにしてみて下さい。そういう人の方が
「できる」人だと思いますよ。
● 2,どんな風に動くのか
前回こんなイメージを載せました。これを元に話を進めます。
まず、カメラはそもそもどう動くのかを確認しましょう。どういうものかを把握していないと
何も始まりません。
カメラ位置を指定する方法はこんな感じです。
1,コード015Fでカメラ位置(と回転)を指定
2,コード0160でターゲット位置を指定
3,コード0160でカメラの動作モードを指定
実際のコードはこれ
-------------------------------------
015F: set_camera_position 664.9277 -479.6112 16.1668 rotation 0.0 0.0 0.0
0160: set_camera_point_at 665.8948 -479.5685 16.4175 mode 2
-------------------------------------
まず、カメラはカメラ本体とターゲット、2つを動かすということになります。人間なら、
目の場所、そして見る対象の場所です。この2点が決まって初めてどこかを見るという動作が
成り立ちます。
この時、カメラとターゲットが全く同じ地点だった時どうなるでしょう?見る方向が定まり
ませんよね?なので、カメラ位置 = ターゲット位置にならないように留意する必要があります。
また、カメラとターゲットが近いほど、どちらかが少し動いただけで視点が大きく動くのは
想像できるでしょうか?逆に2者の距離が離れているほど、少しくらい動いただけでは画面は
ほとんど動かないですよね。目の前の蚊を追うのは大変ですが、東京ドームでランナーを追う
のに目はほとんど動かす必要がありません。
要するに、動かす「利き量」が違うわけです。目の前のコップを5cm動かせと言われたら、
4.5cm~5.5cmくらいのところに収まっていれば、早々文句は言われないでしょう。でも
1cm動かせと言われたら、せめて9mm~11mmくらいには収めないと1cmとは言い難いかも
しれません。1mm単位の要求があるのだから、その精度で動かす必要があります。つまり、
操作が難しくなるわけです。
とはいえ、動かすのはパソコンの中のカメラですから、小さい数字を与えればいいじゃない、
と思うわけですが、これがまた微々たる数字なのです。0.001とか、そういう数字なわけです。
これを2乗3乗するような計算が入ると0.000001とかになります。ここまでくると、いかに
パソコンといえど計算誤差が出てきて、ひょっとすると小さすぎるからゼロだと見なして
しまうかもしれません。それでは困ります。
ですが、もしカメラとターゲットの距離をそれなりにとっておけば、同じ視線の動き量に
対して2とか3くらいの数字を与えてもOKになったりします。これなら2乗3乗しても4とか
27とか。人間でさえ困らない数字で処理できます。もちろん、逆に大きすぎる数字でも困るので、
とにかくゼロに近い数字、利き量がほどよい数字で操作をしたいですね。なるべくそうなるよう、
留意してプログラムする必要があります。
なぜ長々とそんな基本を語ったかと言うと、今までの動作がまさにその問題を受けていたから
です。移動距離が短いのに、その距離を1000フレームかけて動けと言われたりしたら、
パソコンの計算精度でも無理があります。具体的には、0.2くらいの距離を1000フレームで動けと、
つまり1フレームを0.0002で動けという命令になりかねなかった。ていうかなってた。でも、
実はGTAにおいて、カメラの最小動作は確か0.0006くらい。これ以上小さいと、カメラの位置が
変わらないんです。変わらないということは、動かして、その位置を取得しても同じ座標の
ままです。また0.0002動けと指示しても、動いたつもりで位置は変わらないから、またそのまま。
これがずっと続いてしまい、カメラはまったく動かないことになる。これが動作不良の正体の
一つです。
というわけで、数字の最小精度というものを常に意識して下さいね、ってことなのです。
話を戻しましょう。
カメラのモードは、確か2だと瞬時に、1だと現在地点から指定地点までフェード移動です。
先に言っておきますが、このフェードは動作時間が固定のため、どんなに距離が離れていても
凄い速度で移動しますし、逆もしかり。なので代用はとても無理です。
ということで、カメラ位置は瞬時に移動するわけです。つまり、離散的に動くわけですから、
移動量は小さくないと滑らかに見えません。滑らかに見える量は0.01とかのレベルなので、
滑らかかつスローに動くとなると、1フレーム当たりの移動量はさらに小さくなります。
上で述べた問題が生じやすいわけです。繰り返しますが重々留意して下さい。
さて、ここで上のグラフを見て下さい。カメラは最初と最後がゆっくりですから、つまり
「時間の経過に対して変化量が小さい」と言えます。このことから、グラフの横軸を時間の経過、
縦軸を変化量として見るべきだということが分かります。
さらに、カメラには始点と終点があり、総フレーム数を指定するわけですが、始点と終点が
距離に、総フレーム数が時間に関わってくることは言うまでもないでしょう。要するに、始点から
終点までの間の、ある座標を与えることが「移動」、総フレームの中の任意のフレームについて
処理することが「時間の経過」となります。この解釈に辿り着けるかがカギです。
ちなみに、「総フレーム数じゃなくて総動作時間で管理して、そこからフレーム数を割り出した
方が、使う人にとっては使いやすいのでは?」と思った人はいませんか?1フレームが何秒に
なるかなんてわかりにくいのだから、「5秒間かけて動く」という方が確かに使いやすいかも
しれませんね。
ですが、残念ながらGTAはフレームリミットモードというものがあり、これを解除すると
パソコンの性能によって1フレーム当たりの時間がかわってきます。オンにすれば一定になる
のでしょうが、カクカクし過ぎて撮影には使えません。なので、規定時間が何フレームになるのか
事前に知ることができませんから、総フレーム数を指定せざるをえないのです。あとは使用者が
試行錯誤で、フレームをいくつくらいにするとそのパソコンでは何秒くらいになるという感覚を
身に付けてもらうしかありません。
では、まとめましょう。
グラフの横軸の端から端が「動作開始~動作終了」を意味し、縦の下から上が「始点・終点」を
意味することになります。というより、自分でそう意味付けします。
そして、軌跡は既に「開始直後と終了直前は移動量が小さい」ことを意味する曲線を描いて
いるわけですから、「フレームを一定間隔で進めれば、勝手にフェード移動を行う」という
ことが分かります。
そして、あるフレームにおいての座標は、グラフが描かれる式
がありますので、そのあるフレームをχとして、座標がσa(χ)として求まります。
いえ、実際には逆です。この曲線を描いてくれる式を見つけ出したのです。だから、この式の
通りに座標を求めれば理想の動きになるのです。
いきなりこの式が出てきて「なにこれ」と思ったかもしれませんが、上のグラフはこの式で
表されるのです。χを入力すると、グラフのような曲線を描くYを出力してくれる式はないだろうか
と、探して見つけてきたわけです。決していきなり湧いて出たわけではありません。
そんな曲線だけからどうやって式が見つかったのかというなら、探し回ったというのが正解です。
皆さんも、円の軌跡がx^2 + y^2 = 1で表されることを知ってますよね?じゃあ楕円なら?
渦巻きなら?そうやって似たような曲線を探して、ようやく見つけたのがコレというわけです。
ってことで、どんな風に動かすべきかが分かってきたと思います。
次回は実際に値を入れてみて、机上で動作の確認をしてみましょう。
【アクセサリ配布】瑞鷹梅酒(ワンカップ) |
(04/30)
|
結月ゆかり・弦巻マキをWindows10で起動する |
(04/20)
|
iコミックらんど 解約方法 |
(04/05)
|
MMDにおける視野角設定 |
(01/18)
|
ちゃいなくおりてー2 |
(11/02)
|