概要
レーシングシミュレータであるAssetto Corsaには、有志が様々なModを公開している。ちょっと走る分にはよいのだが、タイムを縮めるなりやりこもうとすると、どうしてもセクションタイムが欲しくなる。
探したけれど見つからないので、自分で作ってしまえ、という話。
Assetto Corsaは、上述のようにもう10年選手であるし、最新作Evoもプレビュー中なので、今更新しく作ろうなんて人はいないはず。なので、基本的な考え方と注意点だけを記載しておく。
リソース
- Python 用のAPIリファレンス(有志作成)
- HowTo(有志作成)
- 既存のMod
出来上がったもの
基本と戦略
経過時間の取得
APIを用いれば比較的簡単に取得できる。
import ac
import acsys
# My Car ID is always 0
CAR_ID = 0
# LapTime, Current LapTime in milliseconds [0, …]
lap_time = ac.getCarState(CAR_ID, acsys.LapTime)
ただし、このLap Time
が曲者で、**スタートラインをまたいだ瞬間か、セッションリセットがかかった瞬間に0
となる。
区間設定の仕組み
セクションタイムを計測する地点をSpline
で表現し、その地点でのラップタイムを記録する。事前に計測する地点のspline
を任意の数記憶しておけば、セクションを設定することができる。
Spline
は[0.0,1.0]の範囲をとり、スタートラインにおいて0.0
、ゴールラインにおいて1.0
となる。
import ac
import acsys
# My Car ID is always 0
CAR_ID = 0
# Position of the car on the track in normalized [0,1]
spline = ac.getCarState(CAR_ID, acsys.NormalizedSplinePosition)
Trackにおける車の位置
Trackはいくつかの区間に分けられる。当然、スタートラインとゴールラインがある(周回コースの場合それは一致する)。それ以外にもAssetto Corsaの場合は、ピットレーン、ピット がある。
車両がピットレーンにあるかどうかを取得するAPIは次の通り。
import ac
import acsys
# My Car ID is always 0
CAR_ID = 0
# <CAR_ID> must be the car ID, 0 for the player’s car
# This function returns 1 if the car is currently in the Pitline
is_car_in_pitlane = ac.isCarInPitline(CAR_ID)
# <CAR_ID> must be the car ID, 0 for the player’s car
# This function returns 1 if the car is currently in the Pitbox
is_car_in_pit = ac.isCarInPit(CAR_ID)
スタートラインを越えるとタイム計測が開始し、ゴールラインを越えると終了する。
ただし、それらがどこに設置されているかは、コースがサーキットのような周回コースか、峠のような一直線コースかによって異なり、それが問題となる。
周回コースの場合
+---------------+
| Pit/PitLane |
+--------------+----------------+
| 計測区間 | 計測区間 |
+--------------+----------------+
↑ スタートライン/ゴールライン
峠コースの場合
+-----------+----+------------------------+-------+
|Pit/PitLane| ? | 計測区間 | ? |
+-----------+----+------------------------+-------+
↑ スタートライン ↑ ゴールライン
先述のspline
を用いたセクションの設定方法は峠コースの場合に問題となる。
- 峠コースではピットからスタートラインまでと、ゴールした後にも区間があり、splineの値が設定されている。つまり、
spline
の値で一意にコース上の位置が決まらない。 - APIで取得できる経過時間はスタートラインを越えた瞬間に
0
になる。ユーザーがUIでBack to Pit
操作を行ったとしても、時間は0
にならない
なので、厳密に車両がどこにいるかを逐次チェックしておく必要がある。
注意点
実際にModを作ってみてのはまりポイントを挙げておく。
1. ListBoxは使えない
UIを構築するAPIの中にListBox
を作成するac.addListBox(<CONTROL_IDENTIFIER>,<NAME>)
がある。しかし、'module' object has no attribute 'addListBox'"
となる。
原因は不明
2. UIコンポーネントの更新をコールバックで行うとクラッシュする
例えば、ボタンがクリックされたときにラベルを追加するようなアプリを考える。
単純に、コールバックでコンポーネントの追加操作ができそうだが、追加した瞬間にゲームがクラッシュする。
import ac
import acsys
app = ac.newApp("Sample App")
button = ac.addButton(app, "Click Me")
ac.addOnClickedListener(button, on_click)
labels = []
def on_click(*args):
# コールバックでラベルを追加した瞬間にクラッシュ
label = ac.addLabel(app, "# {} label".format(len(labels)))
labels.append(label)
def acMain(version):
pass
def acUpdate(delta):
pass
なので、ユーザの操作をQueue
なりで保存しておいて、acUpdate
のループで後から処理するか、そもそもコンポーネント事態を追加・削除しないような作りが必要。
import queue
import ac
import acsys
eventq = queue.Qeueu()
app = ac.newApp("Sample App")
button = ac.addButton(app, "Click Me")
ac.addOnClickedListener(button, on_click)
labels = []
def on_click(*args):
eventq.put("add_label")
def acMain(version):
pass
def acUpdate(delta):
while not queue.empty():
evt = eventq.get()
if evt == "add_label":
label = ac.addLabel(app, "# {} label".format(len(labels)))
labels.append(label)
3. スタートラインでspline
が0にならないTrackがある
車体がスタートラインを越えてLap Timeの計測が開始しているのに、Spline
が 0.9などを示すTrackがある。(具体的には、筑波サーキット)
さいごに
みんな使ってみてね。
フィードバックお待ちしています。
バグを見つけたら、Githubの方に投げてくれるとありがたいです