Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ckan で Kerbal Space Program プログラミング環境を setup する

More than 3 years have passed since last update.

TL; DR

Buy Kerbal Space Program now (available on Steam)
Download ckan.exe and execute
Install any of kRPC: Remote Procedure Call Server / kOS: Scriptable Autopilot System / MechJeb
gl hf!

Kerbal Space Program (KSP) とは

自分だけの宇宙開発プログラム立ち上げよう!
謎の緑の宇宙人1とミニチュアの太陽系2にリアルな物理シミュレーション3 を載せて!

KSPとは一言でいうとそんなゲームです。

このゲームの面白いところは、物理シミュレーションが容赦なくあなたの打ち上げるロケットを襲うあたりにあり、当初はロケットを惑星の周回軌道に乗ることすら難しいのですが、一度わかってしまえば月旅行や宇宙ステーション建造、外惑星への探査旅行すら可能4という自由度にあります。

開発元はメキシコに拠点を置くSquadという会社ですが、先日リリースされた v1.3 にて日本語対応を果たし、横文字の苦手なお年寄りから小さいお子様まで実際安心になりました。

Vanilla も大変プレイアブルなのですが、KSPは皆さんの大好きな mod の開発も大変盛んで、UI変更・空力シミュレーションモデルの変更・パーツ追加・宇宙基地・変形・合体・ワープです!、と各種取りそろえられておりますが、中でも今回は プログラムを書くことで宇宙船を操縦する5 modを導入して、KSPのシミュレータを遊び倒す方法をご紹介したいと思います。

未購入の方は、とりあえず買ってから考えましょう。
なぁに、最悪でも積んでるゲームが一つ増えるだけですって
-> Buy Kerbal Space Program now (available on Steam)

ckan の導入 / mod の install

ckan は 有志によってメンテされている、KSPのmodマネージャです。
KSPのmod配布は CurseForge / Spacedock / KSP Forum / mod作者のgithub 等の複数サイトで行われており6、希望のmodを一通りそろえて、定期的に最新のmodに更新していくのは一苦労です。
KSP本体も比較的ちょいちょいバージョンが上がっており、古いmodは容赦なく動かなくなる傾向にあります。

ckan は各種modの配布URLや依存関係などのメタデータをメンテナンス・配布することで、それらmodの導入・更新を飛躍的に容易にしてくれるすばらしいツールです。
ckan は .NET Framework 上にC#で書かれていますが、Monoでも動く7とのこと。

ckan を起動すると上記のようなGUIからmodの一覧を選択できます。
KSPがinstall済みならKSPのパスは自動で検知してくれるはずですが、環境によっては手動で設定するようdialogが出るかもしれません。
虫眼鏡のアイコンを Filter(Compatible)に設定すると、現在installされているKSPのバージョンと互換なmodのみがリスト表示されます。リストの中から導入したいmodのcheckboxにcheckを入れ、Apply Changesボタンで download & install です。

どんなmodがあるのか一覧を眺めて、名前の気に入ったmodのHomepageに飛ぶだけでも楽しいものです。
人気のあるmodであれば、HomepageからYouTubeの紹介動画なんかも見られるかもしれませんね。

さて、プログラマブルなmodの紹介に移ります。

MechJeb

Source Code (github) / Document / Forum
難易度: かんたん / 言語: Visual Programming / Editor: GUI

こいつは使い方は簡単です。Easy modeです。

一つだけ注意することがあるとすれば、VAB/SPH(宇宙船組み立て画面)でCommand and ControlカテゴリからMechJebのパーツを選んで、宇宙船にくっつけてあげる必要があります。

MechJebのついているロケットを打ち上げようとすると、Lunchpad(打ち上げ画面)にMechJebのメニューが表示されますので、その中の Scripting Module (Beta) を選んであげると、スクリプトエディタが現れます。
あとはちょいちょいとAdd Actionで動作を指定してあげて、STARTを押したら打ち上げるだけです。

mechjeb3.png

画像が見にくいですが、今回は下記を指定しています。
Kerbalから打ち上げて、Munの高度200kmの周回軌道に入ってくれるはずです。

ASCENT GUIDANCE (100km)
Target Celestial Body: Mun
Hohmann Transfer to Target: Schedule the burn in next transfer window
Execute next node
Fine tune closest approach to target: 200,000m
Warp to: SOI transition Lead time: 0s
Execute next node
Change Apoapsis: New apoapsis: 200km schedule the burn at the next peripsis
Execute next node

それだけです。
打ち上げ中の角度調整やステージ切り離し、トランスファウィンドウの計算はすべてMechJebがやってくれます。
ね、簡単でしょう?8

kOS

Source Code (github) / Document / Forum
難易度: ほどほど / 言語: kerboscript (独自) / Editor: In Game (or telnet)

このmodもVAB/SPHで宇宙船にパーツをつけてあげる必要があります。
Command and Control カテゴリの中、 KOSProcessor という属性のあるパーツです。

KOSProcessorを宇宙船に取り付けることができたら、Lunchpadに移ります。
MenuBar(画面右端)にkOSのiconがありますので、それをクリックしてkOSのメニューを開きましょう。
kOS.png

ターミナルのアイコンを押すとGame内のターミナルが開くので、そこにコマンドを打ち込んで行くこともできますが、今回はTELNETボタンを押して手元のtelnet teminalから下記のExampleスクリプトを流し込んでみました。

print "Launching".
lock steering to heading(90,80).
lock throttle to 1.
stage.
wait until altitude > 5000.
lock steering to heading(90,60).
wait until altitude > 15000.
lock steering to heading(90,45).
wait until altitude > 25000.
lock steering to heading(90,30).
until apoapsis > 80000 {
  print "apoapsis is " + round(apoapsis,0).
  wait 0.
}
lock throttle to 0.

どうやらスクリプトが流し込まれた時点で動作が始まってしまったようです。
ざっと中身を読む限り、スロットル全開で打ち上げ、高度が上がる毎に方位を水平に倒して、遠地点高度が80kmに達した時点でエンジンカットオフしてくれるようですね。

ふむ。遠地点付近で再度エンジンを噴射してあげなければ、そのまま地面に真っ逆さまのはずですが、このスクリプトの例では、どうやらその部分は省略されているようですね。なるほど9

kRPC

Source Code (github) / Document / Forum
難易度: そこそこ / 言語: C#, C++, Java, Lua, Python, Ruby, Haskell / Editor: お好みのもの

こいつにはVAB/SPHでのパーツ追加は不要です。

Lunchpadに移ったら、networkっぽいiconをクリックします。中央に水平に伸びた線から上に四角が2つ、下に四角1つ生えてるやつです。

kRPC.png

kRPCのMenuが出てくるので、Start Serverを押してあげると、Game側でRPC serverが立ち上がります。

あとは、お好きな言語、お好きな環境でRPC clientを上げて、ゲームに接続してあげましょう。
localhost以外から接続を受け入れたいなら、add serverからAddressをAnyにしたserverを追加してあげましょう。

Clientはいくつかの言語に対応していますが、Pythonであれば

# pip install kRPC

から

import krpc
conn = krpc.connect(name='Hello World')
vessel = conn.space_center.active_vessel
print(vessel.name)

で、 機体の名前が表示されるはずです。

ClientからServerへの接続時、Server側で「なんかRPCで繋ぎに来た子がいるけど相手していいの?」みたいなdialogが出るので、OKしてあげてください。いちいちServer側を触るのがうるさいときは、Advanced OptionsAuto-start serverAuto-accept new clientsをcheckすると煩わしさが減ります。

さて、kRPCはうれしいことに、機体を制御しようと思うとたくさんのコードを書けます。
ほぼdocumentの example ですが、高度90kmの周回軌道に乗せようと思うと、下記ぐらいの分量になります。

import math
import time
import krpc

TURN_START_ALTITUDE = 250
TURN_END_ALTITUDE = 45000
TARGET_ALTITUDE = 90000
ASCENT_HEADING = 0

def main():
    conn = krpc.connect(name='Launch into orbit')
    vessel = conn.space_center.active_vessel
    obt_frame = vessel.orbit.body.non_rotating_reference_frame

    # Set up streams for telemetry
    ut = conn.add_stream(getattr, conn.space_center, 'ut')
    altitude = conn.add_stream(getattr, vessel.flight(), 'mean_altitude')
    apoapsis = conn.add_stream(getattr, vessel.orbit, 'apoapsis_altitude')
    time_to_apoapsis = conn.add_stream(getattr, vessel.orbit, 'time_to_apoapsis')
    stage_2_resources = vessel.resources_in_decouple_stage(stage=2, cumulative=False)
    srb_fuel = conn.add_stream(stage_2_resources.amount, 'SolidFuel')

    # Pre-launch setup
    vessel.control.sas = True
    vessel.control.rcs = False
    vessel.control.throttle = 1.0

    # Countdown...
    print("3...")
    time.sleep(1)
    print("2...")
    time.sleep(1)
    print("1...")
    time.sleep(1)
    print("Launch!")

    # Activate the first stage
    vessel.control.activate_next_stage()
    vessel.auto_pilot.engage()
    vessel.auto_pilot.target_pitch_and_heading(90, ASCENT_HEADING)

    # Main ascent loop
    srbs_separated = False
    apoapsis_near = False
    apoapsis_reached = False
    turn_angle = 0

    while True:

        # Gravity turn
        if altitude() > TURN_START_ALTITUDE and altitude() < TURN_END_ALTITUDE:
            print("Gravity turn")

            frac = ((altitude() - TURN_START_ALTITUDE) /
                    (TURN_END_ALTITUDE - TURN_START_ALTITUDE))
            new_turn_angle = frac * 90
            if abs(new_turn_angle - turn_angle) > 0.5:
                turn_angle = new_turn_angle
                vessel.auto_pilot.target_pitch_and_heading(90-turn_angle, ASCENT_HEADING)

        # Separate SRBs when finished
        if not srbs_separated:
            if srb_fuel() < 0.1:
                srbs_separated = True
                print("SRB separation")
                vessel.control.activate_next_stage()

        # Decrease throttle when approaching target apoapsis
        if not apoapsis_reached:
            if apoapsis_near and apoapsis() < TARGET_ALTITUDE:
                apoapsis_reached = True
                print("Target apoapsis reached")
                vessel.control.throttle = 0.0
            elif apoapsis() > TARGET_ALTITUDE*0.9:
                apoapsis_near = True
                print("Approaching target apoapsis")
                vessel.control.throttle = 0.25

        if srbs_separated and apoapsis_reached:
            break

    # Wait until out of atmosphere
    print("Coasting out of atmosphere")
    while altitude() < 70500:
        pass

    # Plan circularization burn (using vis-viva equation)
    print("Planning circularization burn")
    mu = vessel.orbit.body.gravitational_parameter
    r = vessel.orbit.apoapsis
    a1 = vessel.orbit.semi_major_axis
    a2 = r
    v1 = math.sqrt(mu*((2./r)-(1./a1)))
    v2 = math.sqrt(mu*((2./r)-(1./a2)))
    delta_v = v2 - v1
    node = vessel.control.add_node(
        ut() + vessel.orbit.time_to_apoapsis, prograde=delta_v)

    # Calculate burn time (using rocket equation)
    F = vessel.available_thrust
    Isp = vessel.specific_impulse * 9.82
    m0 = vessel.mass
    m1 = m0 / math.exp(delta_v/Isp)
    flow_rate = F / Isp
    burn_time = (m0 - m1) / flow_rate

    # Orientate ship
    print("Orientating ship for circularization burn")
    vessel.auto_pilot.reference_frame = obt_frame
    vessel.auto_pilot.target_direction = node.direction(obt_frame)
    vessel.auto_pilot.wait()

    # Wait until burn
    print("Waiting until circularization burn")
    burn_ut = ut() + vessel.orbit.time_to_apoapsis - (burn_time/2.0)
    lead_time = 5
    conn.space_center.warp_to(burn_ut - lead_time)

    # Execute burn
    print("Ready to execute burn")
    while time_to_apoapsis() - (burn_time/2.0) > 0:
        pass
    print("Executing burn")
    vessel.control.throttle = 1.0
    time.sleep(burn_time - 0.1)
    print("Fine tuning")
    vessel.control.throttle = 0.05

    remaining_delta_v = conn.add_stream(getattr, node, "remaining_delta_v")
    min_delta_v = remaining_delta_v()
    point_passed = False
    while remaining_delta_v() > 0.1 and not point_passed:
        if min_delta_v < remaining_delta_v():
            point_passed = True
        else:
            min_delta_v = remaining_delta_v()
    vessel.control.throttle = 0.0
    node.remove()

    print("Launch complete")

if __name__ == "__main__":
    main()

たのしくなってきましたね!

おわりに

みんなももっとKSPすればいいとおもうよ!



  1. Kerbal さん達。鳴き声がかわいい 

  2. Kerbolシステム。ちっちゃい 

  3. Unityが許す限りにおいて 

  4. あなたの作るロケットが十分効率的で、Delta-V が許せば 

  5. VanillaではMouse+KeyboardないしGamePadで宇宙船を操縦することになります 

  6. リリース当初は開発元のSquadがmod配布をホスティングしていましたが、2014年頃にCurseForgeへ移行…したのですが、ぶっちゃけCurseForgeが使いにくいので流行ってない 

  7. 試したことはない。ゲーム機はWindowsに限る。GPU積んでないマシンは帰ってくれないか 

  8. この後、宇宙船は無事にMunの地表に激突し、3名のkerbalは病院送りとなりました。近点距離を調整するノードを設定したあとに、実行する指示を忘れていたのが敗因と思われます。宇宙開発の進歩に犠牲はつきものだ 

  9. またkerbalが病院送りに。再突入は大分熱そうであった 

haeena
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away