takeyohのおぼえがき

気になったこと、試したことの記録です。

パドルシフトアニメーションが動かない!

パドルシフトの車で、ドライバのパドルシフトアニメーションを作りました。
左手(ダウン)がshift_dw.ksanim、右手(アップ)がshift_up.ksanimという名前のファイルで作成し、car modのanimationsフォルダの中にコピーします。

ですが、なぜかアニメーションが動かない!
思い当たるところをいくつか変更して試したところ、原因が分かったので覚書で残しておきます。

ポイントは一つ。

dataフォルダ内のdriver3d.iniファイルを開き、SHIFT_ANIMATIONセクションのINVERT_SHIFTING_HANDSを0に設定する

以上です。

右ハンドルのHシフトパターンの車とかを作っていると、この値を1に設定することで、左手のシフトアニメーションが再現されますが、これが1のままだと、パドルシフトアニメーションが認識できないんですね。う~ん。

CHASE CAMERA時に音が出ない(今回はドアの開閉音)

ドアのアニメーションが設定されていると、ゲーム開始時にドアが開いた状態になっていて、スタートするとドアが閉まって、ガチャという閉まる音がすると思います。
この音を別に切り出して、bankファイル化し、ゲームが始まった後もドアを開閉すると開閉音が出るようにすることがあります。(気まぐれ)
luaで実装しています。luaからksanimを読み込んで、サウンドイベントとして音を出します。
ただ、今回気づいたのは、CHASE CAMERA(車の後ろからの視点)にすると音が出ないということです。それ以外の視点は音が出ます。
これは、fmodの設定が必要でした。
CHASE CAMERAモードでも音を出したい場合は、対象の音源のMaster設定でStealingを「Virtualize」に設定すると解決できました。
今回は、ドア開閉用のbankファイルを作成していますが、イベントを選択し、Masterの欄をクリックすると、下にマスター用の設定画面が出ます。
その一番右側、緑色で丸を付けた部分です。

この設定をしてから、bankファイルを生成すると、CHASE CAMERA視点でも音が出るようになりました。

あー、昔作った車はこれに対応してないなぁ。でも直すの面倒だから、そのままにします。w

fmodでのリバーブ設定(この記事は不確実かもしれません)

bankファイルを自作したcar modでSRPのトンネルに入るとリバーブの効果で、スピーカーやヘッドフォンが壊れるのでは?というくらいの爆音になる症状に困っていました。
最初はCSPの設定の問題かとも思っていましたが、もしかしてfmod側の問題かもしれない?と思って調べてみました。

fmodでエンジン音(中、外両方)のそれぞれのMasterを選択し、左の3D Pannerを右クリックするとメニューがでるので、FMDO Reverbをクリックします。

すると、右側にReverbという欄が出てきます。

ここで各設定を下記の様に変更しました。

この状態でbankファイルを生成し、ゲームで使って見たところ、リバーブはかかりますが爆音にはならなくなりました。
このfmodの設定が適切なのかの検証はできていません。なのでこの設定は不確実です。
ただ、fmodのリバーブ機能とCSPのリバーブ機能が両方機能する結果が爆音につながっているのは確かです。
asettocorsaのcar mod用bank作成限定の話ではありますが、fmod側でのリバーブ機能は最低限にしておくのがよさそうです。

路面の反射がおかしいときの回避方法(根本的ではないかも)

アセットコルサでなぜか2輪車を作ったりしているわけですが、その場合、4輪の制御システムをごまかしながら2輪車っぽい動きを再現しているわけです。
そのため、HUB_XX, SUSP_XX, WHEEL_XXなどのノードの下にタイヤなどのオブジェクト(メッシュ)を入れずに作ったりします。
(ノードは作ってあるのですが、その中にメッシュが一つもない)
この時、trackにもよるのですが、昼は問題なくても、夜になった時、路面や周りの物体が全く反射せず、ヘッドライトで前を照らしているはずなのに、まったく明るくならない症状が出ることがあります。
SRPだとこんな感じです。

白線は見えるのですが、路面は真っ黒です。

先に、回避方法を適用した後を紹介すると、こうなります。

これが普通ですよね。ヘッドライトもちゃんと照らしています。

で、回避方法です。
HUB_XX、SUSP_XX、WHEEL_XXのノードの構成はこうなっていると思います。

普通、車を作るときは、ここにタイヤやホイール、ブレーキディスクやキャリパーなんかのオブジェクトを入れると思いますが、この例では、bikeというノードの中にすべてのオブジェクトが入っていて、これらのノードの下には一つもオブジェクトがありません。
この状態でkn5を作ってしまうと、先ほどの様に反射が正常に動かないことがあるようです。

そこで、適当に(ここでは、WHEEL_LRの下)、ダミーのオブジェクトを作成します。

ここにあるオブジェクトはタイヤの回転と一緒に回るものですので、大きいと目立ちます(邪魔)。
また負荷を極小化するには、なるべく面数の少ないオブジェクトがよいかと。
そこで、今回はWHEEL_LRのエンプティの位置に正方形のオブジェクトを追加し、名前をdummyTyreとか適当につけています。
マテリアルは、そのcar modで使われているマテリアルから適当なものを割り当てます。

で、この正方形を編集モードにして、限りなく小さく(見えないくらい)します。

この絵はまだ小さな正方形が見えますが、もっともっと見えないくらいまで小さくしてください。

小さくしたら、正方形(dummyTyre)のスケール(X,Y,Z)は1.000になっているか確認してくださいね。

オブジェクトモードのまま小さくするとスケールの値が変わってしまいます。(なので編集モードにしてから縮小する)

あとは、いつも通りfbxでエクスポートして、ksEditorでkn5を作成すればOKです。


これだけです。

オフラインtrack day(走行会)モードで、AI車両が止まる場合の対処

今回もメモだけ。
オフラインのtrack day(走行会)モードで、ランダムなのですがAI車両が道の真ん中で止まっていて困ることがあります。
特にAIの車両数を増やしたりすると顕著になります。

根本的な解決か変わりませんが、スムーズに流れるようになったので、設定部分をメモします。
これを書いている時点で使用しているCSPは0.2.8preview1です。

CSP設定のNEW AI BEHAVIORから、AI flood(works in Track Day mode):の欄を探し、
Behavior variation:の選択肢を「Never」にする。

です。
あらゆるパターンでこれが有効なのかは検証できていませんが、もし同じような症状の方が入ればお試しください。

lodAとlodB両方でアニメーションを反映させる。

もう一つ覚書を。
クレーン車を持ち上げるスクリプトの話を書きましたが、同じ車での実装です。
物理制御をする、data/script.luaは表示されているLODに関係なく動作します。
一方、今回の足場の展開やクレーンのアニメーションはextensionのext_config.iniやluaスクリプトで制御します。
lodAのみで作られているcar modは問題ないのですが、今回はlodDまであるので、lodAとlodBの時は、アニメーションが反映されるようにします。
整理すると、lodAとlodBのノードを取得して、それぞれに同じアニメーションの処理を設定していきます。
今回は、ext_config.iniのANIMATIONセクションでの設定ではなく、luaスクリプトで、progressを制御していく方法を使っています。

script.update(dt)の中で、常にアニメーションの設定をし続けている場合は、現在表示されているlodがどれかをチェックして、そのlodにアニメーションを設定するという仕組みでよいのですが、動いていないときはアニメーションの設定をしない作りだと、例えばlodBでアニメーションが終了してから、近づいてlodAに切り替わるとアニメーション前の状態に戻っている、みたいなことが起きます。
ですので、アニメーションの設定自体をlodA、lodB両方にやり続けることで、同期していきます。

クレーン車の足場のアニメーションをcar_stand.ksanimとして作成しました。これはcar modのanimationsフォルダにコピーしておきます。
extensionフォルダに足場のアニメーションを制御するスクリプトとして、car_stand.luaというファイルを作成します。(中身は下に貼ります。)
extension/ext_config.iniを開き、car_stand.luaを実行するSCRIPTセクションを追記します。

[SCRIPT_...]
SCRIPT = car_stand.lua
SKIP_FRAMES = 0
ACTIVE_FOR_UNFOCUSED = 1
ACTIVE_FOR_LOD = B
ACTIVE_FOR_NEAREST = 12

通常SCRIPTセクションでSCRIPTに実行するスクリプトのファイル名を指定するだけですが、それだとlodAしか認識できません。
lodBにもアニメーションを認識させるには、
ACTIVE_FOR_UNFOCUSED = 1
ACTIVE_FOR_LOD = B
が必要です。

続いて、設定したcar_stand.luaの中身を例として記録しておきます。

local parentA = ac.findNodes('{ TRANSMISSION_R_0 & lod:A }'):getParent()
local parentB = ac.findNodes('{ TRANSMISSION_R_0 & lod:B }'):getParent()
local standAnimFront = "../animations/car_stand_front.ksanim"
local progressFront = 0
local standAnimRear = "../animations/car_stand_rear.ksanim"
local progressRear = 0
local craneAnim = "../animations/car_crane.ksanim"
local progressCrane = 0

function script.update(dt)
    ac.debug('activeLOD', car.activeLOD)

    if car.extraA and car.gear == 0 then
        if progressFront < 0.5 then
            progressFront = progressFront + dt / 10
            parentA:setAnimation(standAnimFront,progressFront,false)
            parentB:setAnimation(standAnimFront,progressFront,false)
        elseif progressFront < 0.67 then
            progressFront = progressFront + dt / 20
            parentA:setAnimation(standAnimFront,progressFront,false)
            parentB:setAnimation(standAnimFront,progressFront,false)
        elseif progressFront < 0.81 then
            progressFront = progressFront + dt / 200
            parentA:setAnimation(standAnimFront,progressFront,false)
            parentB:setAnimation(standAnimFront,progressFront,false)
        elseif progressFront < 1 then
            progressFront = progressFront + dt / 40
            parentA:setAnimation(standAnimFront,progressFront,false)
            parentB:setAnimation(standAnimFront,progressFront,false)
        end
        if progressRear < 0.5 then
            progressRear = progressRear + dt / 10
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        elseif progressRear < 0.63 then
            progressRear = progressRear + dt / 20
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        elseif progressRear < 0.67 then
            progressRear = progressRear + dt / 200
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        elseif progressRear < 0.754 then
            progressRear = progressRear + dt / 160
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        elseif progressRear < 0.784 then
            progressRear = progressRear + dt / 20
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        elseif progressRear < 1 then
            progressRear = progressRear + dt / 90
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        end

        if progressFront >= 1 and car.extraB then
            if progressCrane < 1 then
                progressCrane = progressCrane + dt / 20
                parentA:setAnimation(craneAnim,progressCrane,false)
                parentB:setAnimation(craneAnim,progressCrane,false)
            end
        elseif progressFront >= 1 then
            if progressCrane > 0 then
                progressCrane = progressCrane - dt / 20
                parentA:setAnimation(craneAnim,progressCrane,false)
                parentB:setAnimation(craneAnim,progressCrane,false)
            end
        end

    elseif progressCrane <= 0 then
        if progressFront > 0.9 then
            progressFront = progressFront - dt / 40
            parentA:setAnimation(standAnimFront,progressFront,false)
            parentB:setAnimation(standAnimFront,progressFront,false)
        elseif progressFront > 0.5 then
            progressFront = progressFront - dt / 70
            parentA:setAnimation(standAnimFront,progressFront,false)
            parentB:setAnimation(standAnimFront,progressFront,false)
        elseif progressFront > 0.0 then
            progressFront = progressFront - dt / 10
            parentA:setAnimation(standAnimFront,progressFront,false)
            parentB:setAnimation(standAnimFront,progressFront,false)
        end
        if progressRear > 0.85 then
            progressRear = progressRear - dt / 50
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        elseif progressRear > 0.6 then
            progressRear = progressRear - dt / 100
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        elseif progressRear > 0.5 then
            progressRear = progressRear - dt / 70
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        elseif progressRear > 0 then
            progressRear = progressRear - dt / 10
            parentA:setAnimation(standAnimRear,progressRear,false)
            parentB:setAnimation(standAnimRear,progressRear,false)
        end

    end
end

こちらもぐちゃぐちゃといっぱい書いてますが、要は、最初にlodAとlodBの親ノードを取得して置いて(1,2行目)、その後のアニメーション設定をすべて、両方のノードに設定し続ける、これだけです。

これで、アニメーションしていないときに、lodAとBが切り替わっても、アニメーションの進捗が同期しているので表示は問題ありません。
もちろん、lodC, lodDは何も設定していないので、lodCの距離まで離れると、全てのアニメーションは消滅します。

以上です。

/* -----codeの行番号----- */