てくてくテック

【やってみた】原神のAPIを触ってみる...!

雑学

どーも!

たかぽんです!

今回は筆者も結構ハマっている原神"Genshin impact"で遊んでいこうと思います...!

23/12/02追記

enka networkのAPI仕様が変更され、本記事の内容で一部現在と乖離がある部分があります。

変更内容の詳細は以下の通りです。

具体的に説明しておくと...

エンドポイント(=アクセスしたらアカウントのキャラ情報等がjson形式で取得できたURL)が元々以下の形でしたが...

https://enka.network/u/800169568/__data.json

新規のエンドポイントは以下の通りです。(以下、筆者のUIDでの例となるので、各自で置き換えてください)

https://enka.network/api/uid/800169568

また、詳細の説明もあったので、詳しく知りたい方は以下を参照してみてください。

パッとみた感じ、説明に用いているjsonとおなじようなので、エンドポイントさえ置き換えて本記事を読んでいただければ確認しつつ進められるかと思います。

筆者も後日時間のあるタイミングであらためて触ってみよとおもいます。

原神って何?

さて、原神と聞くと、最近twitterやらニュースやらで有名になってきているので知っている人も多い気がしますが...

オープンワールドのソーシャルゲームです。

実はこの原神、APIがあるんですね...

そのため、例えばアカウントIDなどから、そのアカウントの所持しているキャラクターの情報などの、本来ならゲーム内でしか閲覧できない様な情報をプログラムで取得することができます。

もしかしたら他にもあるかもですが...少なくとも有名になっているゲームで外部にAPIが公開されているゲームってないきがします...

実際、APIを公開するとリスクもあるかもしれませんが...

  1. コミュニティでプレイヤーがAPIを使って分析するシステム等を組んでくれる
  2. 他のゲームに比べてそういった補助機能やゲーム外コンテンツが充実
  3. ユーザーが増える

といったループになりそうなきがします...!

実際、このAPIを用いて色々行われているようです。(釣りの自動化とか?)

以下あたりで色々調べることができるかと思います。(ただし、利用等は自己責任でお願いします!)

で、今回は以下を利用させていただこうと思います。

上記のAPIは、実際に以下のwebページで使われていて...

アカウントのUIDを入れることで、現在公開しているキャラクターや聖遺物等の情報を画像形式で書き出すことができるサービスです。

twitterとかでよくみる画像も大体はこれだと思います。

では次の章からじっさいにやることの確認をしてAPIを触っていきましょう!

今回やってみること

さて、今回やっていくことを確認しておきます。

今回はpythonでdiscordのスラッシュコマンドとして、指定したUIDのアカウントのキャラクターの聖遺物スコアを計算して返す...といったことをやってみようと思います...!

ただ、本当に最低限で今後もう少し拡張性等考慮した形にしていく感じで考えています...!

ここで、聖遺物のスコアについて確認しておきます。

聖遺物スコアとは、キャラクターが装備できる聖遺物のパラメータを点数化して、それをスコア換算している非公式のものさしみたいなものです。

例えば攻撃を中心とした場合...

会心率% * 2 + 会心ダメージ% + 攻撃力% = スコア

といった具合で算出されます。

そのため...

discordにて"/calc uid"といった入力がされた場合、現在設定されているキャラクターのスコアを一通り計算して、各キャラクターごとのこの聖遺物スコアの合計を出す形をゴールとしてみます。

では、やっていきましょう!

APIを叩いてみる

まずは雑に手元のブラウザでAPIを叩いてみましょう。

APIを実際に叩いた出力が以下です。(筆者のアカウントUIDでの例になっています。)

{
	"playerInfo": {
		"nickname": "そーれお茶",
		"level": 58,
		"signature": "無限フィンドニール編...",
		"worldLevel": 8,
		"nameCardId": 210053,
		"finishAchievementNum": 567,
		"towerFloorIndex": 12,
		"towerLevelIndex": 3,
		"showAvatarInfoList": [
			{
				"avatarId": 10000037,
				"level": 90
			},
			{
				"avatarId": 10000046,
				"level": 90
			}
		],
		"profilePicture": {
			"avatarId": 10000037
		}
	},
	"avatarInfoList": [
...
...
...

ちょっと長すぎるので一部だけ抽出して載せました。(全文は各自で確認していただければと)

叩いたら上記の様なものが出てくると思います。

読みづらい場合は出力結果を以下ページ等で整形すれば見やすくなると思います。

見てもらえればわかるんですが、キャラクター情報等が出ていることがわかるかと思います。

また、現在キャラクター(avater)を二つプロフィールに設定していて、どちらもレベルは90のキャラクターということがわかります。

また、avaterIdというのがどのキャラなのか?のIDですが、以下あたりで調べることができます。

また、各キャラクターの情報が続く"avatarInfoList"に羅列されています。

先ほどのAPIのドキュメントにこれらのパラメータの詳細は記載されているので、より詳しく気になる方は確認してみてください。

こういったデータ構造で取得できるjsonからデータを取得して、聖遺物スコアを計算していくイメージです。

では、次節にて実際のコードを書いていきます...!

discordのslash command botを作る!

まず、最低限のdiscordのスラッシュコマンドボットは過去の記事を参考にしてみてください。

ここまでできていて、あとはそれを書き換えていく形で作ります。

では、早速ですがどんどん完成系のプログラムを載せていきます...!

まず最初にスラッシュで呼び出されるコマンドは以下です。

@client.slash_command(description="原神スコア計算", guild_ids=[DISCORD_SERVER_IDS])
async def calc(
    ctx: discord.ApplicationContext,
    user_id: Option(int, required=True, description="UIDを入力してください")
):
    endpoint = f"https://enka.network/u/{user_id}/__data.json"
    response = requests.get(endpoint)

    embed = artifacts_total_points(response.json()['avatarInfoList'])
    await ctx.respond(content=None, embed=embed)

簡単に説明しておくと"calc"という名前のコマンドで、user_idを必須で渡す想定です。

endpointというのが先ほどAPIを叩いた際のURL(=APIの入口)で、エンドポイントに対してuidを入力されたidで置き換えてgetリクエストを送信します。

その結果responseへ先ほどのJSONが入っている状態です。

そして、次にartifacts_total_pointsというメソッドを用意して、先ほどのJSONのavatarInfoList以下のデータを渡します。(聖遺物の計算に使うため)

そして、計算した結果をembedという形で返している感じですね。

await ctx.respond...は、処理が終わるままで待ち、embedを返せる様になったらdiscord側へ値を返しています。

では、次はartifacts_total_pointsをてみてみます。

あらかじめお伝えしておきたのですが、今回筆者はプロフィールに甘雨と胡桃のみ設定していて、一旦はその二人が常に出る形で実装しています。

そのため、厳密に最適化する場合はTODOに記載している通り、どのキャラクターならATKなのか?といった情報を別途管理してlist等で攻撃主体で計算するのか?HP主体で計算するのかといったのを振り分けるのが良いかと思います。

def artifacts_total_points(avatarInfoList):
    embed = discord.Embed( 
        title='原神スコア計算',
        color=0x1e90ff, 
        description="原神のスコア計算結果です!"
    )
    
    for avatarInfo in avatarInfoList:
        # TODO:後でjsonのJPデータを取得して、キャラIDを取得してからそれぞれで処理を変える様に
        if 10000037 == avatarInfo['avatarId']:
            embed.add_field(inline=True,name="甘雨",value=calc_artifacts_score(avatarInfo, 'ATK'))
        elif 10000046 == avatarInfo['avatarId']:
            embed.add_field(inline=True,name="ふーたお",value=calc_artifacts_score(avatarInfo, 'HP'))
    return embed

さて、やっていることは非常に簡単で、最初にembedで、最終的にdiscordに表示されるembedの投稿内容を作成し、そのembedへ数値等の情報を書き加えています。

引数として渡したavatarInfoListにはプロフィールに設定している人数分の情報がそれぞれ格納されているので、forで一人づつ取り出し、そのavatarIdでどのキャラかを確認、甘雨(=10000037)であれば、calc_artifacts_scoreメソッドにavatarInfoと'ATK'の文字列を。

また、胡桃のの場合はHPを渡して、実際の値の計算は別メソッドで行っています。

最終的に全キャラ分処理を終えたらキャラごとのスコア合計を追記したembedを返している形です。

では、最後にcalc_artifacts_scoreをみてみます。

def calc_artifacts_score(avatarInfo, baseType):
    TYPE_PARAM = {
        'ATK' : 'FIGHT_PROP_ATTACK_PERCENT', 
        'HP' : 'FIGHT_PROP_HP_PERCENT',
    }
    total_score = 0
    for artifact in avatarInfo['equipList']:
        if 'weapon' in artifact:
            continue
        artifact_score = 0
        for substat in artifact['flat']['reliquarySubstats']:
            if substat['appendPropId'] == "FIGHT_PROP_CRITICAL":
                artifact_score += substat['statValue'] * 2
            elif substat['appendPropId'] == "FIGHT_PROP_CRITICAL_HURT" or substat['appendPropId'] == TYPE_PARAM[baseType]:
                artifact_score += substat['statValue']
        total_score += artifact_score

    return total_score

こちらはまず、TYPE_PARAMというのを定義しています。

原神では先ほど記載した通り、攻撃主体なら会心率・会心ダメージ・攻撃力%を計算します。

ただ、HP主体の場合は会心率・会心ダメージ・HP%でスコアを出します。

基本的に会心率・会心ダメージは共通してスコア化し、そうでない1要素が変わってくる形が多く、引数として渡したbaseTypeごとに実際のどのパラメータをスコア化するのか?を保存している形です。(厳密には防御主体や元素熟知主体など他もありますが、一旦甘雨と胡桃の攻撃・HPだけ対応しています。

そして、total_scoreはこのキャラの聖遺物5つ分の合計スコアで、この値にひとつづつの値を加算し最終的に返します。

次がavatarInfoのequipListというところに、武器と聖遺物の情報が入っているので、一件づつみていき、武器だった場合はスキップ(スコアに入れないので)し、次にこの聖遺物のスコアをartifact_scoreとして出していきます。

武器でなければ聖遺物になるので、artifact['flat']['reliquarySubstats']として、その聖遺物のパラメータのリストから、一件づつみていきます。

もしそのパラメータが"FIGHT_PROP_CRITICAL"(会心率)だったら、その二倍をartifact_scoreへ加算します。

"FIGHT_PROP_CRITICAL_HURT"(会心ダメージ)か、"FIGHT_PROP_ATTACK_PERCENT"(攻撃力%) or "FIGHT_PROP_HP_PERCENT"(HP%)の場合は、その倍率をそのまま加算します。

そしてその結果をtotal_scoreへ加算します。

これを聖遺物の数だけくりかえし、最終的に合計値を返しています。

さて、プログラムとしては以上で終わりです...!

コマンドボットを動かして試してみる...!

さて、それではlocal環境で動かして試してみます...!

taka@Taka discord % python3 command-bot.py
takapon-test#9930 コマンド待機中...

筆者の前の記事を参考にしている場合は上記で動くかと思います。

では実際にdiscordへ行くと...?

uidを入れて実行すると...?

できたっぽいです...!

答え合わせではないのですが...

GenshinArtifactorという聖遺物の合計スコアも画像として生成してくれるサービスあるので、そちらを使わせてもらおうと思います。

おそらく個人でつくってくださっており、下記のtwitter等から是非確認してみてください!

追記

3/13日時点でGenshinArtifactorのサービス終了がアナウンスされていることを確認いたしました。

前述のAPIは引き続き使えるとおもいますが、後述で生成している画像が生成できるサービスは4/31で終了するそうです。

コードは公開していただいている様なので、個人で使いたい人は参考にさせていただくと良いかもです。

また、後続で有志の方が似た形で動作するサーバーを用意してくださっている様なので、そちらをつかわせていただくのも良いかもしれません。

上記周りの情報はもろもろ、下記のtwitterから確認してみてください。

出力的にいい感じっぽいです。

画像の右側にある"総合スコア"に当たる計算を今回のプログラムで出した形です。

まとめ

さて、今回は原神のAPIを使って遊んでみました...!

完全上位互換のサービスもあるので、べつに作る意味はないんですが...!w

また、今回のプログラムは正直改善点多いです...

特にキャラクターの対応周りは全然詰められていないので、要検討ですね...!

なんとなーくですが、ユーザーのリテラシーも相まって、APIでゲームデータへアクセスできるのが大前提...といった潮流がきている気がします。

例えば将来ギルド戦があったとして、ギルドメンバーのdiscordサーバーがあり、ギルドメンバーのログイン率とかをAPIで計算して出したり、相手の装備品やらステータスやらを分析して挑むなり...

番外戦術といったら少し違うかもしれないですが、単純にゲームの内部で勝敗や楽しみが完結していたいままでから、より現実での楽しみも増える部分があるのかなぁと。

特に筆者が別で少し触っているweb3のブロックチェーンゲームなどもプログラムで色々書けたりしていて、それに似たものを感じています。

一概にそれが良いとは限らないのですが、そういう将来を想像するのも楽しいですね...!

それでは!

モバイルバージョンを終了