こんにちは、やまだたいし( やまだ たいし (@OrotiYamatano) / Twitter )です。
私はゲーム業界にいるわけですが、面倒くさい作業は結構な割合で企画職がやってくれます。
私が前にシステムエンジニアをしていた時は結構な割合で自動化ツールが豊富にありました。
しかし、現在はソコまで自動化ツールは見当たりません。企画職である方々には何が自動化出来るのか判別出来ないのと、
プログラマーがソコまで気が回っていないからだと思いました。
なので、そういったツールを民主化させる第一歩としてRPAツールを作る方法を共有できれば良いなと思い今回作ってみることにしました。
目次
既存RPAもどきツールについて
ゲーム業界にも自動化ツールはありますが、ほとんどがレガシーな技術、エクセルVBAがせいぜいです。
今回はあえて処理速度の観点からゲームプログラマーが絶対触らないであろうPythonを触ってツールを作っていこうと思います。
環境
Windows10
Python3.9
PyCharm2021.3系(VSCodeとかでも良いと思います)
利用プラグイン
PyAutoGui
Pillow
opencv-python
参考記事
今回自動化する流れ
音声テキストを生成するツールVoicePeakというのがあるが、csv,tsv,改行区切りなどのインプット形式でインプット可能だが、
どのキャラでインポートするかまでは指定できない。
一括で初回ぐらいはやってしまいたいが、テキスト形式では無理がある。
なので、そこの指定を自動化出来るツールを作ろうと思い今回対応。
大まかとなる流れはこうだ。
Spreadsheetでセリフを管理
↓
GASによりJson形式でセリフを吐き出し
↓
Pythonを使いVoicepeakに取り込み
Spreadsheetでセリフを管理
Spreadsheetでは以下のような構成にしました。
GASによりJson形式でセリフを吐き出し
GASは以下のような内容にして、
ボタンを押したらJson形式でファイルがダウンロードされるようにしました。
GAScript
function getJsonData(sheetName) { var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName); var maxRow = sheet.getLastRow(); var maxColumn = sheet.getLastColumn(); var keys = []; var data = []; for (var x = 1; x <= maxColumn; x++) { keys.push(sheet.getRange(1, x).getValue()); } //実際のデータが2行目からなので【y = 2】から開始 for (var y = 2; y <= maxRow; y++) { var json = {}; if(sheet.getRange(y, 1).getValue() === false){ continue; } for (var x = 2; x <= maxColumn; x++) { if(x === 2){ const sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("シート2"); json[keys[x-1]] = vlookup(sheet.getRange(y, x).getValue(),sheet2,2); }else{ json[keys[x-1]] = sheet.getRange(y, x).getValue(); } } //データ格納 data.push(json); } Logger.log(data); return JSON.stringify(data, null, '\t'); } function getJson(){ return getJsonData('シート1'); } function main() { var dl_html = HtmlService.createTemplateFromFile("dl_dialog").evaluate(); SpreadsheetApp.getUi().showModalDialog(dl_html, "JSONファイルをダウンロード"); } function vlookup(value,sheet,column) { let returnValue = "1"; for (var i = 2; i <= sheet.getLastRow(); i++) { if(value == sheet.getRange(i,1).getValue()){ returnValue = sheet.getRange(i,column).getValue(); break; } } return returnValue; }
HTML
<!DOCTYPE html> <html> <head> <base target="_top"> <script type='text/javascript'> function downloadJson(elm) { var content = <?= getJson(); ?>; var blob = new Blob([content], { "type": "application/json" }); document.getElementById("download").href = window.URL.createObjectURL(blob); } </script> </head> <body> <!-- JSONダウンロードボタン:json名は適宜設定してください--> <a id="download" href="#" download="VoicePeak.json" onclick="downloadJson()">ダウンロード</a> </body> </html>
Pythonを使いVoicepeakに取り込み
自動化の仕組み
今回はPyAutoGuiを利用しますので、要素の検査とかは必要ありません。
要素の検査をして色々するツールの方が動作の確実性が高いですが、実装の敷居が若干上がます。
後、単純に私がPython使いたい。
もしPyAutoGui以外を使ってWindowsで要素の精査を行うならコチラ↓の利用を推奨。
https://accessibilityinsights.io/
仕組みとしては簡単で、画像認識や座標指定などでクリック位置を特定してPython経由でマウスやキーボードを操作してあげる感じです。
そのためクラウド上の挙動は出来ません。
PyAutoGuiインストール
pipコマンドを打ってインストールします。
大分前のことで忘れたけど↓あたり?
pip install pyautogui
pip install opencv-python
pip install Pillow
pythonのコードを書く
main.py
import json import os import tkinter.filedialog import tkinter.messagebox import pyautogui import time import pygetwindow import keyboard import pyperclip # Jsonを読み込むところ def open_json(json_path): json_file = open(json_path, 'r', encoding="utf-8") json_dict = json.load(json_file) # 辞書型変数にデータをつっこむ json_file.close() print('json_dict:{}'.format(type(json_dict))) return json_dict # 指定の画像が表示されるまで待つ def wait_picture(f, time_out): print(f) ret = None while ret is None: ret = pyautogui.locateOnScreen(f, grayscale=True, confidence=.7) print(ret) if ret is not None: return ret time.sleep(0.1) time_out -= 1 print(time_out) if time_out < 0: return None # プロジェクトを作り直す def new_project(): while True: time_zero_item = wait_picture("image/0.png", 1) if time_zero_item is not None: x, y = pyautogui.center(time_zero_item) pyautogui.click(x, y - 15) # 一旦アイテムの上にカーソル持っていく time.sleep(0.05) keyboard.press_and_release('ctrl+n') item = wait_picture("image/No.png", 1) pyautogui.click(item) time.sleep(0.05) break # なければ終わる def window_resize(): voicepeak.maximize() # 最大化して time.sleep(0.1) voicepeak.restore() # もとに戻す time.sleep(0.1) # Press the green button in the gutter to run the script. if __name__ == '__main__': exe_name = 'VoicePeak Json Importer' root = tkinter.Tk() root.withdraw() file_type = [("", "*.json")] directory = os.path.abspath(os.path.dirname(__file__)) tkinter.messagebox.showinfo(exe_name, '処理ファイルを選択してください') file_path_str = tkinter.filedialog.askopenfilename(filetypes=file_type, initialdir=directory) print('読み込みファイル:', file_path_str) if file_path_str == "": exit() title = 'VOICEPEAK' voicepeak = [g for g in pygetwindow.getWindowsWithTitle(title) if g.title == title][0] voicepeak.activate() time.sleep(1) window_resize() new_project() window_resize() voicepeak.activate() mainItem = wait_picture("image/addword.png", 10) pyautogui.click(mainItem) # 「クリックしてセリフを入力してください」をクリック json_data = open_json(file_path_str) for row in json_data: print('row:{}'.format(row)) select_voice = row['VoiceData'] if bool(select_voice != "1"): pulldown_item = wait_picture("image/PulldownItem.png", 1) pyautogui.click(pulldown_item) x, y = pyautogui.center(pulldown_item) time.sleep(0.1) y = (25 * (select_voice - 1)) + y + 30 pyautogui.moveTo(x-10, y) # 何故か一度動かしてからじゃないとクリック反応しない…… pyautogui.click(x-10, y) word = wait_picture("image/addword.png", 10) pyautogui.click(word) voice_text = row['VoiceText'] pyperclip.copy(voice_text) keyboard.press_and_release('ctrl+v') time.sleep(0.05) keyboard.press('enter') pyautogui.scroll(-500)
使った画像たち
後はビルドしてexeファイルを実行できるようにする。
これで実行できるようになった
実際の実行の様子
Voicepeakへの自動インポート機能を作ってみた。 pic.twitter.com/2b6SoV5iFB
— やまだ たいし (@OrotiYamatano) 2022年3月20日
まとめ
かなり前に書いていたのですが、Scriptなど公開してなかったので公開してみました。
アップデートがあってVoicepeakは設定周り保存できるようになったりした気がしますが、同じようにGUIツールの自動化ツールは作れるので是非試してみてください。
皆様の助けになれば幸いです。