GASを使ってVTuberの立ち絵を動かすCSSを効率よく生成する
この記事は Engineering for Vtuber Advent Calendar 2021 25日目の記事です。
針山ハリネとは
お疲れさまです!
バーチャルプログラマーの針山ハリネです。
普段はスマホアプリ開発を中心としたフリーランスエンジニアをやってます。
概要
複数人のVTuberでコラボをするときに、「立ち絵をどうするか」という問題があります。参加者全員が立ち絵をスムーズに動かせるとは限らないので、一番お手軽な「Discordのプラグインを使って、声に合わせて立ち絵を動かす」方法があります。
詳細設定は以下のリンクをご覧ください。
これを複数人で行う際に、「全員が同じ設定のCSSを簡単に生成できるようにしたい」という気持ちがありました。そこで、スプレッドシートとGASを使って、お手軽に立ち絵を動かすCSS生成シートを作りました。
GAS(Google Apps Script)とは
Googleのサービス上で動かすことができるJavaScriptをベースにしたプログラミング言語です。
GoogleスプレッドシートやGoogleフォームと連携することで、特定の条件で自動的に処理(スプレッドシートに更新があったら通知, フォームに入力があれば自動でSlack通知を送る等)を実行することができます。
Google Apps Script
Googleサービスと連携させずにGASだけで動かすことも可能です。また、実行スケジュールを設定することで定期的にGASを実行させることもできます。
設定方法
設定方法はとても簡単でGoogleスプレッドシートを作成後に、拡張機能から「Apps Script」を選択するとGASを利用することができます。
エディタもWebブラウザ上でコーディング・実行できるので開発環境を用意する必要はありません。
一応、claspを導入すれば作成したスクリプトをGitでバージョン管理することもできるようです。
今回のプログラムの内容
ざっくりこのプログラムで行ったことは以下の通りです。
- スプレッドシートに「立ち絵CSS生成」のメニューを追加する
- 「立ち絵CSS生成」メニューに「キャラ名・画像URL・DiscordID」が設定されたキャラについてCSSを生成するボタンを作る
- CSSを生成してダイアログ内のテキストエリアまたはコピーボタンからコピーできるようにする
実際に作成したコード
/**
* @param {number} id
* @return {void}
*/
const createStyleSheet = (id) => {
const dlHtml = HtmlService.createTemplateFromFile("dl_dialog");
dlHtml.css = getCharaCss(id);
SpreadsheetApp.getUi().showModalDialog(dlHtml.evaluate(), "CSS生成!");
}
/**
* @param {number} id
* @return {string}
*/
const getCharaCss = (id) => {
const data = getData();
const chara = data[id];
return css(chara["DiscordID"], chara["キャラ名"], chara["画像URL"]);
}
// スプシからデータ取得
const getData = () => {
const sheet = SpreadsheetApp.getActiveSheet();
const lastRow = sheet.getLastRow();
const lastColumn = sheet.getLastColumn();
const keys = [];
const data = [];
for (let x = 1; x <= lastColumn; x++) {
keys.push(sheet.getRange(1, x).getValue());
}
for (let y = 2; y <= lastRow; y++) {
const obj = {};
for (let x = 1; x <= lastColumn; x++) {
obj[keys[x-1]] = sheet.getRange(y, x).getValue();
}
data.push(obj);
}
return data;
}
// addMenuでは引数の定義ができないので、ここで個別に関数を作る
const createStyleSheet0 = () => createStyleSheet(0);
const createStyleSheet1 = () => createStyleSheet(1);
const createStyleSheet2 = () => createStyleSheet(2);
const createStyleSheet3 = () => createStyleSheet(3);
const createStyleSheet4 = () => createStyleSheet(4);
const createStyleSheet5 = () => createStyleSheet(5);
const createStyleSheet6 = () => createStyleSheet(6);
const createStyleSheet7 = () => createStyleSheet(7);
const createStyleSheet8 = () => createStyleSheet(8);
const createStyleSheet9 = () => createStyleSheet(9);
const onOpen = () => {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu("立ち絵CSS生成");
const charaList = getData();
menu.addItem("再読み込み","onOpen");
menu.addSeparator();
charaList.forEach((chara, index) => {
menu.addItem(`${chara["キャラ名"]}のCSS生成`,`createStyleSheet${index}`);
});
menu.addToUi();
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<title>CSS生成</title>
</head>
<body>
<textarea cols="65" rows="12" name="css" id="css" readonly><?= css ?></textarea>
<button onClick="copy()">コピー</button>
<script>
const copy = () => {
const textarea = document.getElementById("css");
textarea.select();
document.execCommand("copy");
alert("コピーしました。");
}
</script>
</body>
</html>
/**
* @param {string} id
* @param {string} name
* @param {string} image
* @return {string}
*/
const css = (id, name, image) => {
return `
/* ${name}以外の画像を非表示にする */
li.voice-state:not([data-reactid*="${id}"]) { display:none; }
/* ${name}を立ち絵画像に差し替える */
.avatar {
content:url("${image}");
height:auto !important;
width:95% !important;
border-radius:0 !important;
}
/* 発話状態の設定 */
.speaking {
border-color:rgba(0,0,0,0) !important;
position:relative;
animation-name: speak-now;
animation-duration: 300ms;
animation-fill-mode:forwards;
}
@keyframes speak-now {
0% { bottom:0px; }
50% { bottom:10px; }
100% { bottom:0px; }
}
/* ネームタグ表示位置調整 */
li.voice-state{ position: static; text-align: center;}
div.user{ position: absolute; right: 0; left: 0; bottom: 10px; margin: auto; }
/* 色々消すヤツ */
body { background-color: rgba(0, 0, 0, 0); overflow: hidden; }
`
}
base.css.gs
の中身は参考リンクのCSSジェネレータを参考に好きなように改造したものを使ってください。
作成したスプシ
スプレッドシートの設定も共有するので、良かったら参考にしてください!
実際の動き
最後に
最近、お仕事の方が忙しくて動画更新できてないですが、Twitterは頻繁に見ているので、似たような人がいたらぜひ色々教えて下さい!🙏
YouTubeのvideoIDが不正です
ここのDiscordにもよく滞在してます!
Discussion