[Twitch][Python] チャット欄からゲーム(キーボード)を動かす
この記事は2022年10月5日に書かれたものです。情報が古い可能性がありますのでご注意ください。
Twitch Plays Pokémonについて
「Twitch Plays Pokemon」という視聴者がチャットでゲームを操作するチャンネルがある。
2014年から活動している有名チャンネルである。活動詳細についてはWikipediaを参照。
自分のTwitchチャンネルで遊んでみる
このシステムを気軽に体験できるシンプルなpythonのコードがGithubにて公開されている。
これを少し改良して動作確認まで行ったヤツと導入手順を記す。
2秒ごとにチャット内容を集計し、最も投票の多かったキー操作を行う内容となっている。
Wikipediaにある「民主主義モード」のコードである。
改善点の多いコードだが、簡単に解説も入れる。
導入手順
- pythonインストール
- 下記リンクより「twitchchatplay.rar」をダウンロードし、解凍
- 「twitchchatplay.py」をテキストファイルとして開き、コードを編集
- 17-28行目:押されるキーの設定。
「up」「↑」などの上判定を指すコメントがあったら「w」キーが入力されるように設定している。
「#」以降はプログラムに影響しないメモ欄。 - 39行目:自分のTwitchユーザ名を記載する。
- 40行目:Twitchの認証で使われる「OAuthトークン」というコードを記載する。
このリンクへ飛んで「Connect」を選択すると生成される。
「oauth:~」と書かれているすべてをコピーして貼り付けること。 - 41行目:自分のチャンネル名を記載する。「#」を頭に付けること。
- 53行目:「seconds=2」という部分が投票の集計を行う間隔。2秒毎で良いと思う。
秒数を変更する場合は150-186行目の連射処理を調整する必要があるかも。 - 79-80行目:連射を行うための条件。
例ではチャット内容のどこかに「-(ハイフン・マイナス)」があったら連射するようにしている。 - 82-105行目:投票の集計を行う条件。
「チャット内容に"○○"が含まれていたら1票」という判定を行っている。
ダブルクォーテーションで判定条件を決め打ちしているので「sTarT」みたいに大文字・小文字が混じっていると投票されない。
これを解消したい場合はlowerメソッドなどを利用してチャット内容を変換させる必要があるだろう。
スタート・セレクト・L・R・十字キー・A・B・X・Yの計12ボタン分書いてある。
それぞれの判定をどのように行うかを考える必要がある。 - 119-142行目:投票が最も多かったキーを押すための設定。
17-28行目のモノと紐付いている。 - 144-147行目:投票があった場合、キーを押して離す動作を行う。
押したら必ず離す処理を入れる必要がある。
time.sleepによる待ち時間処理を挟まないと一気に処理されてしまいバグる可能性がある。
0.1ではなく0.05でも良い。 - 150-186行目:連射処理。
ループ処理を書くのが面倒だったので押して離す処理をコピペペペペしている。
2秒の間に10連射を行うように調整している。 - スタートメニューよりコマンドプロンプトを起動
- pythonの外部パッケージ「pynput」をインストール
- pythonの外部パッケージ「emoji」をインストール
- pythonの外部パッケージ「apscheduler」をインストール
- 「twitchchatplay.py」を実行
- 「snes9x」などの動かしたいエミュレータを起動し、17-28行目と紐付いたキー入力の設定を行う
- エミュレータよりゲームを起動
- Twitchチャットで動作確認
コマンドプロンプトを閉じると終了する
keys = [
# Key.up, # 0:UP
# Key.down, # 1:DOWN
# Key.left, # 2:LEFT
# Key.right, # 3:RIGHT
# Key.enter, # 10:START
# Key.shift_r, # 11:SELECT
pynput.keyboard.KeyCode.from_char('w'), # u:0:up,↑
pynput.keyboard.KeyCode.from_char('s'), # d:1:down,↓
pynput.keyboard.KeyCode.from_char('a'), # l:2:left,←
pynput.keyboard.KeyCode.from_char('d'), # r:3:right,→
pynput.keyboard.KeyCode.from_char('h'), # a:4:A
pynput.keyboard.KeyCode.from_char('g'), # b:5:B
pynput.keyboard.KeyCode.from_char('t'), # x:6:X
pynput.keyboard.KeyCode.from_char('f'), # y:7:Y
pynput.keyboard.KeyCode.from_char('r'), # l1:8:L1
pynput.keyboard.KeyCode.from_char('y'), # r1:9:R1
pynput.keyboard.KeyCode.from_char('n'), # start:10:start
pynput.keyboard.KeyCode.from_char('v'), # select:11:select
]
def __init__(self):
self.server = 'irc.chat.twitch.tv'
self.port = 6667
self.nickname = 'ユーザ名'
self.token = 'oauthトークン'
self.channel = '#チャンネル名'
self.sched.add_job(self.voteCount, 'interval', seconds=2) #2秒おきに判定
#連射フラグ設定
global barrage
barrage = 0 #初期化
if msgContent.find("-") >=0 or msgContent.find("-") >=0:
barrage = 1
if msgContent.find("start") >=0 or msgContent.find("START") >=0 or msgContent.find("Start") >=0 or msgContent.find("すたーと") >=0 or msgContent.find("スタート") >=0 or msgContent.find("スタート") >=0:
self.voteDict["start"] += 1
elif msgContent.find("select") >=0 or msgContent.find("SELECT") >=0 or msgContent.find("Select") >=0 or msgContent.find("せれくと") >=0 or msgContent.find("セレクト") >=0 or msgContent.find("セレクト") >=0:
self.voteDict["select"] += 1
elif msgContent.find("l1") >=0 or msgContent.find("L1") >=0 or msgContent.find("える") >=0 or msgContent.find("エル") >=0 or msgContent.find("エル") >=0:
self.voteDict["l1"] += 1
elif msgContent.find("r1") >=0 or msgContent.find("R1") >=0 or msgContent.find("あーる") >=0 or msgContent.find("アール") >=0 or msgContent.find("アール") >=0:
self.voteDict["r1"] += 1
elif msgContent.find("u") >=0 or msgContent.find("U") >=0 or msgContent.find("up") >=0 or msgContent.find("UP") >=0 or msgContent.find("Up") >=0 or msgContent.find("↑") >=0 or msgContent.find("上") >=0 or msgContent.find("うえ") >=0 or msgContent.find("ウエ") >=0 or msgContent.find("ウエ") >=0:
self.voteDict["u"] += 1
elif msgContent.find("d") >=0 or msgContent.find("D") >=0 or msgContent.find("down") >=0 or msgContent.find("DOWN") >=0 or msgContent.find("Down") >=0 or msgContent.find("↓") >=0 or msgContent.find("下") >=0 or msgContent.find("した") >=0 or msgContent.find("シタ") >=0 or msgContent.find("シタ") >=0:
self.voteDict["d"] += 1
elif msgContent.find("l") >=0 or msgContent.find("L") >=0 or msgContent.find("left") >=0 or msgContent.find("LEFT") >=0 or msgContent.find("Left") >=0 or msgContent.find("←") >=0 or msgContent.find("左") >=0 or msgContent.find("ひだり") >=0 or msgContent.find("ヒダリ") >=0 or msgContent.find("ヒダリ") >=0:
self.voteDict["l"] += 1
elif msgContent.find("r") >=0 or msgContent.find("R") >=0 or msgContent.find("right") >=0 or msgContent.find("RIGHT") >=0 or msgContent.find("Right") >=0 or msgContent.find("→") >=0 or msgContent.find("右") >=0 or msgContent.find("みぎ") >=0 or msgContent.find("ミギ") >=0 or msgContent.find("ミギ") >=0:
self.voteDict["r"] += 1
elif msgContent.find("a") >=0 or msgContent.find("A") >=0 or msgContent.find("えー") >=0 or msgContent.find("エー") >=0 or msgContent.find("エー") >=0:
self.voteDict["a"] += 1
elif msgContent.find("b") >=0 or msgContent.find("B") >=0 or msgContent.find("びー") >=0 or msgContent.find("ビー") >=0 or msgContent.find("ビー") >=0:
self.voteDict["b"] += 1
elif msgContent.find("x") >=0 or msgContent.find("X") >=0 or msgContent.find("えっくす") >=0 or msgContent.find("エックス") >=0 or msgContent.find("エックス") >=0:
self.voteDict["x"] += 1
elif msgContent.find("y") >=0 or msgContent.find("Y") >=0 or msgContent.find("わい") >=0 or msgContent.find("ワイ") >=0 or msgContent.find("ワイ") >=0:
self.voteDict["y"] += 1
if nullCheck:
print('入力待ち')
elif voteWinner=="u":
idx = 0
elif voteWinner=="d":
idx = 1
elif voteWinner=="l":
idx = 2
elif voteWinner=="r":
idx = 3
elif voteWinner=="a":
idx = 4
elif voteWinner=="b":
idx = 5
elif voteWinner=="x":
idx = 6
elif voteWinner=="y":
idx = 7
elif voteWinner=="l1":
idx = 8
elif voteWinner=="r1":
idx = 9
elif voteWinner=="start":
idx = 10
elif voteWinner=="select":
idx = 11
if idx is not None:
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
#連射
if idx is not None and barrage == 1:
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
time.sleep(0.1)
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
time.sleep(0.1)
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
time.sleep(0.1)
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
time.sleep(0.1)
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
time.sleep(0.1)
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
time.sleep(0.1)
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
time.sleep(0.1)
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
time.sleep(0.1)
keyboard.press(keys[idx])
time.sleep(0.1)
keyboard.release(keys[idx])
barrage = 0 #連射フラグ初期化
pip install pynput
pip install emoji
pip install apscheduler
py "C:\Users\(ユーザ名)\Desktop\twitchchatplay.py"
その他
本当はキーではなくゲームパッドの入力をさせたかったのだが…
大変そうだったので調べるのを止めてしまった。
これを動かしている間、キーボードに触れなくなるのがデメリット。
「aaaa」「a4」とチャットがあった場合はAボタンを4回入力させたい!
といった機能を追加したい場合は条件を新たに考えて組み込む必要がある。
興味があれば自分自身でカスタマイズして使ってみてほしい。
可能性は無限大である。
ディスカッション
コメント一覧
まだ、コメントがありません