League Of Legendsの配信ツールを作る話

LeagueOfLegends配信ツール記事のアイキャッチ LeagueOfLegends
LeagueOfLegends配信ツール記事のアイキャッチ

発端

  TwitchでLeague Of Legendsを配信していたのですが、ゲームもトークが得意ではなく、没個性になっていたので「なんか差別化できないかなー」と漠然と考えていました。

 いつも通り、ゲーム配信しながらOPGGでマッチングした相手の情報を見ていた時に「あれ・・・?マッチング相手がわかるってことはLeague Of Legendsはゲーム内情報提供しているのでは・・・?」と思い至りました。

 ゲーム内情報をリアルタイムで反映させながら配信している人は見当たらなかったので、「システムエンジニア経験が活かせるかも!!」と思ったことが発端です。

調査

 欲しかった情報は「Live Client Data API」ですぐに見つかりました。

 想定していたAPIではなかったのですが、ゲームクライアントの情報を提供しているAPIがあり、私がやりたいことが満たせるとても良いAPIでした。

 その名もずばり「Live Client Data API」です。

 「世界大会であるWorldsやMSIのように全ての情報が取得できるわけではない」ので若干物足りなさはありますが、そこは世界大会と比べても仕方ありません。納得しましょう。
 ※そもそも全情報にアクセスできたらチートですからね・・・

 あとはOBS Studioにいい感じに反映できれば・・・という話も「OBS 自動化」ですんなり見つかります。※先人に感謝
OBS WebSocketという仕組みで、OBSのテキストや画像、動画など諸々を外部的に操作できるものです。これで「できない理由」がなくなりました。

構想

LeagueOfLegends配信ツール記事のアイキャッチ

構想といっても壮大なものを考えても頓挫することは目に見えているので、方針は以下にしました。

 品質(Quality) 動けばいい
 コスト(Cost)できるだけ少なく
 納期(Delivery)できるだけ早く

仕事ではありえないですが、個人開発ですから納期(やりがい)が第一です。
モノができなければモチベーションも上がらないのでまずは形にすることを第一としました。

また、基本的な登場人物も以下になります。いっぱい出てきても扱いきれませんからね。

LiveClientDataAPIRiotが提供するLoLゲーム内情報を取得できるAPI
OBS WebSocketOBS Studioにアクセス(取得・編集)できるAPI
Pythonプログラミング言語

 先に述べたLiveClientDataAPIとOBS WebSocketは必須として、それらを操作するプログラミング言語はPythonにしました。
特に「Pythonでなければならない」というわけではなく、サンプルコードがあったのと簡単に試せる言語という簡単な理由です。

ツールの作り方

実際の作りかたは次の章で説明するので、ここでは「どうやって作ったのか」という説明です。
「そんなことはどうでもいいから、早く動かしたい」という方は次の章まで進みましょう。

LiveClientDataAPIでLeague of Legendsの情報取得

詳細は膨大になるため別の記事で解説するので触り(必要最低限)だけ紹介します。

取得できる情報のサンプルは公式サイトで提供されているので参考にしてみてください。
※「You can find a sample response here.」の箇所

概要

 LiveClientDataAPIで情報は大まかに分けて以下です。

activePlayer操作プレイヤーの情報(ステータスやゴールド)
allPlayersゲームに参加しているプレイヤーの情報(アイテムやスコア)
eventsゲーム内で発生したイベント(キルやオブジェクト獲得など)
gameDataゲームモードやマップ情報(サモリフやARAM、観戦など)

 例えば、スコア変動(キル・デス・アシストの発生)を判定したい場合は、allPlayersからスコア情報を取得することで可能となります。

キル・デスの判定

 実際にallPlayersのスコアの見方についてです。
 LiveClientDataAPIはjson形式で取得でき、スコアは以下のように設定されています。

{
    "activePlayer": {
    ・・・
    },
    "allPlayers": [
        {
            "championName": "セト",
            "isBot": false,
            "isDead": false,
            "items": [・・・],
            "level": 20,
            "position": "TOP",
            "rawChampionName": "game_character_displayname_Sett",
            "rawSkinName": "game_character_displayname_Sett",
            "respawnTimer": 0.0,
            "riotId": "サモナーネーム#タグ",
            "riotIdGameName": "サモナーネーム",
            "riotIdTagLine": "タグ",
            "runes": {・・・},
            "scores": {
                "assists": 5,
                "creepScore": 260,
                "deaths": 1,
                "kills": 9,
                "wardScore": 42.45812225341797
            },

 上記は9キル(“kills”: 9)、5アシスト(“assists”: 5)、1デス(“deaths”: 1)という感じになります。私の場合は、1~3秒毎に取得し、キル数が増加していた場合、キル発生と判断しています。
 eventsからも判断できますが、プレイヤーのスコア変動であれば上記が一番簡単だと思います。

LiveClientDataAPIのサンプルコード

Pythonで以下を実行することでLiveClientDataAPIでLeague of Legendsで取得できる全情報をゲットできます。

import requests

API_URL = "https://127.0.0.1:2999/liveclientdata/allgamedata"

response = requests.get(API_URL, verify=False)

print(response.json())

URLを変えることでactivePlayerやallPlayers、eventsに絞って取得することも可能です。

Get All Game Datahttps://127.0.0.1:2999/liveclientdata/allgamedata
All Playershttps://127.0.0.1:2999/liveclientdata/playerlist
Active Player​https://127.0.0.1:2999/liveclientdata/activeplayer
Events​https://127.0.0.1:2999/liveclientdata/eventdata
Gamehttps://127.0.0.1:2999/liveclientdata/gamestats

OBSWebSocketで配信画面を操作

 スコア変動の方法がわかったので、配信(OBS Studio)への反映方法を説明します。

 OBS WebSocketの詳細は別記事にまとめているのでそちらを見てください。

OBS WebSocketでKDAを表示

 ここでは「スコアの変動に合わせてKDAを更新するツール」を例に説明します。

# OBS WebSocketのインポート
import obsws_python as obsws

# LiveClientDataAPI用にrequestsをインポート
import requests

# Get All Game DataのURL
API_URL = "https://127.0.0.1:2999/liveclientdata/allgamedata"

# Get All Game Dataを取得
all_game_data = requests.get(API_URL, verify=False)

# allPlayersを取得
allplayers = all_game_data.json().get("allPlayers", [])

# 指定した名前のプレイヤーデータを取得
summoner_names = [player for player in allplayers if player.get('riotId') == "フェンシングωおぢさん#でまちあ"]

# スコアを取得
scores = [player.get("scores") for player in summoner_names]

# キル・デス・アシストをそれぞれ取得
kills = [score.get("kills") for score in scores]
deaths = [score.get("deaths") for score in scores]
assists = [score.get("assists") for score in scores]

# KDAを計算
kda = [(k, d, a, (k + a) / d if d != 0 else k + a) for k, d, a in zip(kills, deaths, assists)]

# KDAを整形して文字列に変換
kda_text = f"K/D/A: {kda[0][0]}/{kda[0][1]}/{kda[0][2]} (KDA Ratio: {kda[0][3]:.2f})"

# OBS接続情報
host = "localhost"
port = 4455
password = "**********"

# シーン名とソース名の指定
scene_name = "ナニカツクル"
source_name = "KDA"

# OBS WebSocketクライアントの作成
ws = obsws.ReqClient(host=host, port=port, password=password)

res = ws.base_client.req("GetInputSettings",
                   {"inputName":source_name})

# シーンアイテムのテキストを変更
ws.base_client.req("SetInputSettings",
                   {"inputName":source_name,
                    "inputSettings":{'text':kda_text},
                    "overlay":True
                    }
            )

# OBSから切断
ws.disconnect()

上記を実行すると以下のようにOBS上のテキストソースが更新されます。

K/D/A: 6/1/0 (KDA Ratio: 6.00)

上記だと都度実行になりますが、while文でループさせることで随時更新することができます。

総括

 これまで説明した通り、以下3つを組み合わせることでLeague of Legendsの配信ツールを作ることができるようになります。

 しかし、冒頭で述べた通り、LiveClientDataAPIでは全情報にアクセスできるわけではないので、視界外でのアイテム購入や位置情報、シーズン2026で追加されたクエスト達成状況などは確認できません。
 ※画像検出などを駆使すればいけると思いますがまだ未確認

 それでも配信画面を賑やかにするには十分な情報が取得できるので、いろんなアイデアを実現しながら配信に個性を出していきましょう!

コメント

  1. Curious how you’d apply this thinking to smaller teams or solo creators. The principles make sense at scale, but do you think the approach shifts meaningfully when resources are tighter?

    • comaneci より:

      Thank you for your comment.

      I’m not good at English, so I used Google Translate.

      There are some limitations, but I don’t think there’s much difference between small-scale and large-scale events.

      In my case, I use it to display game information on the screen during game streaming on Twitch.

      I believe that even in world tournaments, it’s important for listeners to be able to see the situation in real time.

      The only difference is the amount of information that can be accessed.

      In a world tournament, you should be able to access information through a development API, so I think the amount of information will be significantly different.

      Also, in a small-scale tournament, you can access each player’s local information, so it might be possible to create a more useful tool.

      原文。
      コメントありがとうございます。
      私は英語が苦手なため、Google翻訳です。

      少し制約はありますが、私は小規模と大規模で違いはないと思います。

      私の場合は、Twitchによるゲーム配信で画面にゲーム情報を表示することに使っています。
      世界大会でも同様にリスナーが状況をリアルタイムに見えることがポイントだと考えているからです。

      唯一の違いはアクセスできる情報量です。
      世界大会であれば開発用のAPIでアクセスできるはずなので情報量が大きく違うと思います。
      また小規模大会であれば、各プレイヤーのローカル情報にアクセスできるため、もう少し有意義なツールが作成できるかもしれません。