1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Assetto Corsa 用にセクションタイムを計測するModを作った話

Posted at

概要

レーシングシミュレータであるAssetto Corsaには、有志が様々なModを公開している。ちょっと走る分にはよいのだが、タイムを縮めるなりやりこもうとすると、どうしてもセクションタイムが欲しくなる。

探したけれど見つからないので、自分で作ってしまえ、という話。

Assetto Corsaは、上述のようにもう10年選手であるし、最新作Evoもプレビュー中なので、今更新しく作ろうなんて人はいないはず。なので、基本的な考え方と注意点だけを記載しておく。

リソース

出来上がったもの

基本と戦略

経過時間の取得

APIを用いれば比較的簡単に取得できる。

Lap Timeを取得する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となる。

splineを取得するAPI
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は次の通り。

車両がピットレーンにあるかどうかを取得する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の方に投げてくれるとありがたいです

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?