<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>アバター | imakat.com</title>
	<atom:link href="https://imakat.com/tag/%E3%82%A2%E3%83%90%E3%82%BF%E3%83%BC/feed/" rel="self" type="application/rss+xml" />
	<link>https://imakat.com</link>
	<description>工夫と改善で人生をちょっと豊かに</description>
	<lastBuildDate>Tue, 21 Apr 2026 09:10:13 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://i0.wp.com/imakat.com/wp-content/uploads/2023/07/cropped-80d64ecd340db4e2ca3224859b04caed.png?fit=32%2C32&#038;ssl=1</url>
	<title>アバター | imakat.com</title>
	<link>https://imakat.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">160909258</site>	<item>
		<title>【Mac】Webカメラと声で動く！「自作パペットシステム」を作りました〜無料配布〜</title>
		<link>https://imakat.com/2026/04/18/28865/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Fri, 17 Apr 2026 22:01:50 +0000</pubDate>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[パペット]]></category>
		<category><![CDATA[アバター]]></category>
		<category><![CDATA[Adobeサブスク]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=28865</guid>

					<description><![CDATA[今回は、Webカメラとマイクを使って、自分の顔の動きや声に合わせて画面上の動作人形（パペット）を動かせる**「自作パペットシステム」**をご紹介します。 「自分専用のオリジナルアバターを、もっと手軽に動かしてみたい！」と [&#8230;]]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=1IatZq3s.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=1IatZq3s.png" alt="" style="width:273px;height:auto"/></a></figure>



<p>今回は、Webカメラとマイクを使って、自分の顔の動きや声に合わせて画面上の動作人形（パペット）を動かせる**「自作パペットシステム」**をご紹介します。</p>



<p>「自分専用のオリジナルアバターを、もっと手軽に動かしてみたい！」という思いからPythonで開発を進めていたシステムですが、<a href="https://imakat.com/2026/03/12/28692/" target="_blank">前回の記事の内容に改良を加え</a>、やっと誰でも簡単に使えるパッケージになりました。<br></p>



<p>この辺で、<strong>Adobeのパペットにバイバイして、Adobeのサブスクをキャンセルすることにします</strong>。</p>



<p>今回は<strong>サンプルパペット付きのアプリ（ZIPファイル）を無料公開</strong>します。プログラミングが分からない方でもすぐに使えるように工夫したので、ぜひダウンロードして試してみてください。</p>


<div class="sc-vimeo-embed">
  <p style="font-size: 14px;" class="sc-link">
    <a href="https://imakat.com/vm5?movid=1185079590" target="_blank" onclick="stopVimeoBeforeNavigate(event, this)">
      動画を別ページで表示(ここをクリック)
    </a>
  </p>

  <div class="iframe-wrapper">
    <iframe
      src="https://player.vimeo.com/video/1185079590?title=0&byline=0&portrait=0&controls=1&speed=1&texttrack=ja&dnt=1&loop=0"
      frameborder="0"
      allow="autoplay; fullscreen; picture-in-picture"
      allowfullscreen></iframe>
  </div>

  <div class="dr-vimeo-sublist">
    <details translate="yes"><summary translate="yes">字幕一覧(クリック)</summary> <p translate="yes">
(<a href="#" class="imk-cue" data-seek="0:00" translate="no">00:00:00</a>) 〜自作パペットサンプル起動〜<br>
(<a href="#" class="imk-cue" data-seek="0:12" translate="no">00:00:12</a>) これがですね、ブログの記事に掲載しますサンプルパペットです。<br>
(<a href="#" class="imk-cue" data-seek="0:24" translate="no">00:00:24</a>) で、これそうですね、キャラを実行。<br>
(<a href="#" class="imk-cue" data-seek="0:29" translate="no">00:00:29</a>) うん、こんな風にですね、いくつもできちゃうんですね、面白いですね。<br>
(<a href="#" class="imk-cue" data-seek="0:34" translate="no">00:00:34</a>) 例えば、ここでimakat1号の方も起動させますとね。<br>
(<a href="#" class="imk-cue" data-seek="0:43" translate="no">00:00:43</a>) そうすると、ほら、これがなんと、<br>
(<a href="#" class="imk-cue" data-seek="0:46" translate="no">00:00:46</a>) キャラクターが、サンプルが2つと、同じものが2つと、<br>
(<a href="#" class="imk-cue" data-seek="0:58" translate="no">00:00:58</a>) もう1つ、imakat1号ですね。このキャラクターも動いていると。<br>
(<a href="#" class="imk-cue" data-seek="1:07" translate="no">00:01:07</a>) これはカメラとマイクを同じものを使っているものですから、3つとも同じ動作になっていますが。<br>
(<a href="#" class="imk-cue" data-seek="1:17" translate="no">00:01:17</a>) これはカメラもマイクも一応、キャラクターごとに設定できるようにしてあるので、<br>
(<a href="#" class="imk-cue" data-seek="1:27" translate="no">00:01:27</a>) やりようによってはですね、違うキャラクターが登場して違う人が操作すると、<br>
(<a href="#" class="imk-cue" data-seek="1:36" translate="no">00:01:36</a>) ということがですね、可能になるようになっています。<br>
(<a href="#" class="imk-cue" data-seek="1:42" translate="no">00:01:42</a>) それでは、この仕組みをどのように導入していくかということについて、<br>
(<a href="#" class="imk-cue" data-seek="1:50" translate="no">00:01:50</a>) ブログの記事をお読みいただきたいと思います。<br>
</p> </details>
<style>
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details > summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details > p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }
</style>

  </div>

  <script src="https://player.vimeo.com/api/player.js"></script>

  <script>
  (function(){
    var me = document.currentScript;
    var wrapper = me ? me.closest('.sc-vimeo-embed') : null;

    var target = wrapper ? wrapper : document;
    var iframe = target.querySelector('iframe');
    if (!iframe || !window.Vimeo || !Vimeo.Player) return;

    var player = new Vimeo.Player(iframe);

    /* --- 1. 確実に一時停止してから別ページを開く処理 --- */
    window.stopVimeoBeforeNavigate = function(event, link) {
      event.preventDefault(); 
      player.pause().then(function() {
        setTimeout(function() {
           window.open(link.href, link.target);
        }, 50);
      }).catch(function() {
        window.open(link.href, link.target);
      });
    };

    /* --- 2. 字幕のON/OFF制御（Vimeo API仕様） --- */
    var currentMode = "hidden";
    
    function useNormalMode() {
        currentMode = "hidden";
        player.disableTextTrack().catch(function(e){});
    }
    
    function useSpecialMode() {
        currentMode = "showing";
        // 日本語の subtitles が無ければ captions を探す安全設計
        player.enableTextTrack('ja', 'subtitles').catch(function() {
            player.enableTextTrack('ja', 'captions').catch(function() {});
        });
    }

    // ロード直後は枠内字幕を非表示にする
    player.ready().then(function() {
        useNormalMode();
    });

    // Vimeoプレーヤーの全画面・PiPイベントを監視して字幕を切り替え
    player.on('fullscreenchange', function(data) {
        if (data.fullscreen) useSpecialMode();
        else useNormalMode();
    });
    player.on('enterpictureinpicture', function() {
        useSpecialMode();
    });
    player.on('leavepictureinpicture', function() {
        useNormalMode();
    });

    /* --- 3. 字幕リストの初期化（自動梱包とデフォルト展開） --- */
    var checks = 0;
    var initTimer = setInterval(function(){
        var listContainer = target.querySelector('details > p');
        if (listContainer) {
            initSubtitles(listContainer);
            clearInterval(initTimer);
        }
        checks++;
        if (checks > 20) clearInterval(initTimer); // 10秒経ったら監視終了
    }, 500);

    function initSubtitles(listContainer) {
        // デフォルトで開く
        var detailsEl = target.querySelector("details");
        if (detailsEl) {
            detailsEl.open = true; 
            var summaryEl = detailsEl.querySelector("summary");
            if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
        }

        // 行ごとに見えない箱(span)で梱包
        if (!listContainer.dataset.formatted) {
            var html = listContainer.innerHTML;
            var lines = html.split(/<br\s*\/?>/i);
            var newHtml = "";
            for(var j=0; j<lines.length; j++) {
                if(lines[j].trim() === "") continue;
                newHtml += "<span class='imk-line'>" + lines[j] + "</span><br>";
            }
            listContainer.innerHTML = newHtml;
            listContainer.dataset.formatted = "true";
        }
    }

    /* --- 4. クリックシーク機能 --- */
    function parseTs(ts) {
      if (!ts) return null;
      var t = ts.trim();
      var parts = t.split(':').map(function(v){ return parseInt(v, 10); });
      if (parts.length === 2 && parts.every(Number.isFinite)) {
        return parts[0] * 60 + parts[1];
      } else if (parts.length === 3 && parts.every(Number.isFinite)) {
        return parts[0] * 3600 + parts[1] * 60 + parts[2];
      }
      return null;
    }

    target.addEventListener('click', function(e) {
      var a = e.target.closest('a.imk-cue');
      if (!a) return;
      e.preventDefault();
      var sec = null;
      if (a.dataset.seconds) {
        sec = Number(a.dataset.seconds);
      } else if (a.dataset.seek) {
        sec = parseTs(a.dataset.seek);
      }
      if (sec !== null && Number.isFinite(sec)) {
        player.setCurrentTime(sec).then(function(){ player.play(); }).catch(function(){});
      }
    });

    /* --- 5. 同期ハイライトと自動スクロール --- */
    player.on('timeupdate', function(data) {
      // Vimeoプレーヤーが勝手に字幕を出していたら隠す（最強ガード）
      player.getTextTracks().then(function(tracks) {
          var activeTrack = tracks.find(function(t){ return t.mode === 'showing'; });
          if (currentMode === "hidden" && activeTrack) {
              useNormalMode();
          }
      }).catch(function(){});

      var currentTime = data.seconds;
      var listContainer = target.querySelector('details > p');
      if (!listContainer) return;

      var cues = listContainer.querySelectorAll('a.imk-cue');
      if (cues.length === 0) return;

      var activeA = null;
      for (var i = 0; i < cues.length; i++) {
        var sec = null;
        if (cues[i].dataset.seconds) {
            sec = Number(cues[i].dataset.seconds);
        } else if (cues[i].dataset.seek) {
            sec = parseTs(cues[i].getAttribute('data-seek'));
        }
        if (sec !== null && currentTime >= sec - 0.5) {
          activeA = cues[i];
        } else if (sec > currentTime) {
          break;
        }
      }

      if (activeA) {
          var activeLine = activeA.closest(".imk-line");
          if (!activeLine) activeLine = activeA;

          if (activeLine.classList.contains("active-hl")) return;

          // 全行の色を徹底的にリセット
          var allLines = listContainer.querySelectorAll(".imk-line");
          for(var k=0; k<allLines.length; k++) {
              allLines[k].classList.remove("active-hl");
              allLines[k].removeAttribute("style");
          }
          var allLinks = listContainer.querySelectorAll("a");
          for(var m=0; m<allLinks.length; m++) {
              allLinks[m].classList.remove("active-hl");
              allLinks[m].removeAttribute("style");
          }

          // 新しい行に赤文字・黄色背景を直接塗る
          activeLine.classList.add("active-hl");
          activeLine.style.setProperty("background-color", "#ffff00", "important");
          activeLine.style.setProperty("color", "red", "important");
          activeLine.style.setProperty("font-weight", "normal", "important");
          
          var newLinks = activeLine.querySelectorAll("a");
          for(var n=0; n<newLinks.length; n++) {
              newLinks[n].style.setProperty("color", "red", "important");
              newLinks[n].style.setProperty("text-decoration", "none", "important");
          }

          // スクロール処理
          if (listContainer.offsetParent !== null) {
              var containerRect = listContainer.getBoundingClientRect();
              var activeRect = activeLine.getBoundingClientRect();
              var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (activeLine.clientHeight / 2);
              listContainer.scrollTo({
                  top: targetScroll,
                  behavior: "smooth"
              });
          }
      }
    });
  })();
  </script>

  <style>
  .sc-vimeo-embed .iframe-wrapper {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: visible;
  }
  .sc-vimeo-embed .iframe-wrapper iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }

  /* 行梱包時の基本スタイル（干渉防止のためクラス内で指定） */
  .sc-vimeo-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  /* ▼ 字幕一覧（details）の見た目調整 */
  .sc-vimeo-embed details > p {
    font-size: 14px !important;
    line-height: 1.6;
    height: 200px !important; 
    overflow: auto;
    background-color: #EDF7FF;
    padding: 2px 6px;
    margin: 0;
    box-shadow: 3px 3px 4px black;
    position: relative; 
  }
  .sc-vimeo-embed details > summary {
    font-size: 14px !important;
    padding: 2px 6px;
    width: 100%;
    background-color: #ddd;
    border: none;
    box-shadow: 3px 3px 4px black;
    cursor: pointer;
    list-style: none;
  }
  </style>
</div>



<h2 class="wp-block-heading"><strong>&#x2728; 自作パペットシステムの3つの特徴</strong></h2>



<h3 class="wp-block-heading"><strong>1. Webカメラとマイクだけでスルスル動く</strong></h3>



<p>高価なトラッキング機材は不要です。Macの内蔵カメラ（またはWebカメラ）があなたの顔の傾きを認識し、マイクが声の大きさを拾って、パペットがリアルタイムに連動します。</p>



<p>「あいうえお」の口の形にも対応しており、まばたきや視線の移動も自動で行います。</p>



<h3 class="wp-block-heading"><strong>2. 専用の設定画面（UI）でカンタン調整</strong></h3>



<p>以前はプログラムのコードを直接書き換える必要がありましたが、<strong>専用の「司令塔」となる設定画面</strong>を用意しました。</p>



<p>「マイクの感度」「パペットの大きさ」「顔の傾き具合」などを、数値で直感的に微調整できます。自分好みのセッティングを見つけたら、キャラクターごとに保存しておくことが可能です。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=VMpI3SLD.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=VMpI3SLD.png" alt="" style="aspect-ratio:0.7289086301642892;width:350px;height:auto"/></a></figure>



<h3 class="wp-block-heading"><strong>3. スペースキーでいつでも正面をリセット！</strong></h3>



<p>使っている途中で「ちょっと顔の向きがズレてきたな…」と思ったら、<strong>スペースキーをポンッと押すだけ</strong>。その瞬間のあなたの顔の位置を「真正面」として再設定（キャリブレーション）します。</p>



<h2 class="wp-block-heading"><strong>&#x1f4e5; ダウンロードと使い方</strong></h2>



<p>以下のリンクから、一式がまとまったZIPファイルをダウンロードしてください。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>&#x1f449;<strong>ダウンロード(Macのみ)(無料・許諾不要)</strong></td></tr><tr><td><a href="https://imakat.com/rd.php?id=HFOB6Eer.zip" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=ShLhgSW9.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>



<h3 class="wp-block-heading"><strong>使い方の手順（※Mac専用です）</strong></h3>



<p>使うためには、お使いのMacに「Python」が入っている必要があります。（入っていない方は<a rel="noopener" target="_blank" href="https://www.python.org/downloads/">公式サイト<span class="fa fa-external-link external-icon anchor-icon"></span></a>からインストールしてください）</p>



<ol class="wp-block-list">
<li>ダウンロードしたZIPを解凍し、フォルダを開きます。</li>



<li>初回のみ、フォルダ内の『はじめにお読みください.txt』の手順に従って、必要なAIパーツ（MediaPipeなど）をインストールします。<strong>（※ターミナルにドラッグ＆ドロップするだけです！）</strong></li>



<li>準備ができたら、**「自作パペット起動.command」**をダブルクリック！</li>



<li>設定画面が開くので、キャラクターに「サンプル」を選んで「起動」ボタンを押せば完了です。</li>
</ol>



<p><em>※初回起動時はMacのセキュリティで弾かれることがあります。その場合はファイルを「右クリック」→「開く」を選択してください。カメラのアクセス、マイクのアクセスの許可を求められたら「許可」を選択してください。</em></p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=XPV7FP9Y.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=XPV7FP9Y.png" alt="" style="width:299px;height:auto"/></a></figure>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=WxKXfHO7.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=WxKXfHO7.png" alt="" style="width:297px;height:auto"/></a></figure>



<h2 class="wp-block-heading"><strong>&#x1f3a8; 応用編：オリジナルのパペットを自作しよう！</strong></h2>



<p>今回お配りしたフォルダの中には、run_assets/サンプル という場所に、パペットのデザインの元になった <strong>「サンプル.pxd」</strong> というファイルが入っています。<br></p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=UqYOUOjm.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=UqYOUOjm.png" alt=""/></a></figure>



<p>Macの画像編集ソフト「<strong>Pixelmator Pro</strong>」をお持ちの方は、このファイルを開いて自分の好きな絵に描き換えるだけで、<strong>完全オリジナルの自作パペット</strong>を作ることができます！</p>



<p>さらに、Pixelmator Proから画像を書き出してパペットを起動するまでを「ワンクリック」で全自動化する裏技（AppleScriptとAutomatorの連携）も開発しました。私は、サブスク版ではない、以前からある単発アプリを使っています。もしサブスク版で「パペット書き出し起動.app」がうまく動かなくなった場合は、Automatorで作成したAppleScript内の tell application &#8220;Pixelmator Pro&#8221; という1行を、新しいサブスク版の正確なアプリ名に書き換えるだけで解決します。</p>



<p>コードや詳しい仕組みに興味がある方は、こちらのページにスクリプトを公開していますので、ぜひ覗いてみてください。</p>



<div class="wp-block-cocoon-blocks-toggle-box-1 toggle-wrap toggle-box block-box not-nested-style cocoon-block-toggle"><input id="toggle-checkbox-202604211808170" class="toggle-checkbox" type="checkbox"/><label class="toggle-button" for="toggle-checkbox-202604211808170"><strong><span class="marker">コードと要約</span><span class="marker">(クリック)</span></strong></label><div class="toggle-content">
<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>要約：このシステムは、顔認識と音声入力を利用してリアルタイムで動作するデスクトップパペットを実現します。<br><code>run_ui.py</code>は、パペットの各種設定を行うGUIアプリケーションです。キャラクターの新規作成、複製、選択、そしてボリューム閾値、グローバルスケール、顔の傾き制限、目の感度、カメラ/マイクIDといったパラメータ調整機能を提供します。これらの設定はキャラクターごとにJSONファイルとして保存されます。<br><code>run_puppet.py</code>は、UIで選択・設定されたキャラクターとパラメータに基づき、実際のパペット動作を制御するスクリプトです。OpenCVとMediaPipe Face Meshを用いてカメラ映像から顔の傾き、目の動き、口の形を検出し、マイクからの音声ボリュームと連携させてアバターの頭の回転、視線、口の動きをリアルタイムで再現します。キャリブレーションデータもキャラクターごとに保存されます。<br>Automatorスクリプトは、これらのPythonスクリプトの実行を補助します。「自作パペット起動.app」は設定UIを立ち上げ、「パペット書き出し起動.app」はPixelmator Proで開いているドキュメントの各レイヤーをPNG画像として書き出し、キャラクターごとの設定JSONを自動生成した後、該当するパペットを起動する一連の処理を行います。これにより、Pixelmator Proでの画像編集からパペット起動までがスムーズに連携されます。</td></tr><tr><td><a href="https://imakat.com/script_list/?pubtxt=自作パペット_py_pub.txt" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=zrU95MiY.png" alt="" style="width:120px; height:auto;"></a></td></tr><tr><td>公開スクリプトに関するお問い合わせは以下へ↓</td></tr><tr><td><a rel="noopener" target="_blank" href="https://gemini.google.com/gem/1Dp-NLSA5j5OXE3B1Ki2IaBRCvq4CGa6t?usp=sharing"><img decoding="async" src="https://imakat.com/rd.php?id=s7BMZHhB.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>
</div></div>



<p>PixelmatorProによるパーツの変更をパペット起動アプリに即座に反映している動画を以下に掲載します。<br></p>


<div class="sc-vimeo-embed">
  <p style="font-size: 14px;" class="sc-link">
    <a href="https://imakat.com/vm5?movid=1185059004" target="_blank" onclick="stopVimeoBeforeNavigate(event, this)">
      動画を別ページで表示(ここをクリック)
    </a>
  </p>

  <div class="iframe-wrapper">
    <iframe
      src="https://player.vimeo.com/video/1185059004?title=0&byline=0&portrait=0&controls=1&speed=1&texttrack=ja&dnt=1&loop=0"
      frameborder="0"
      allow="autoplay; fullscreen; picture-in-picture"
      allowfullscreen></iframe>
  </div>

  <div class="dr-vimeo-sublist">
    <details translate="yes"><summary translate="yes">字幕一覧(クリック)</summary> <p translate="yes">
(<a href="#" class="imk-cue" data-seek="0:00" translate="no">00:00:00</a>) 〜パペット生成パイプライン〜PixelmatorProからPython連携〜<br>
(<a href="#" class="imk-cue" data-seek="0:11" translate="no">00:00:11</a>) このbodyの方がですね、選ばれて書き込まれるということになります。<br>
(<a href="#" class="imk-cue" data-seek="0:18" translate="no">00:00:18</a>) で、紺のbodyの方はちょっとこう、脇にですね、よけておきます。<br>
(<a href="#" class="imk-cue" data-seek="0:25" translate="no">00:00:25</a>) さあこれで、今これファイルをですね、保存します。<br>
(<a href="#" class="imk-cue" data-seek="0:30" translate="no">00:00:30</a>) そして、「パペットの書き出し起動」を起動させます。<br>
(<a href="#" class="imk-cue" data-seek="0:36" translate="no">00:00:36</a>) そうするとダダダダダッと動いていってですね、<br>
(<a href="#" class="imk-cue" data-seek="0:42" translate="no">00:00:42</a>) なんと、あっという間に、紺の服装からベージュの服装に、お着替えができました。<br>
(<a href="#" class="imk-cue" data-seek="0:56" translate="no">00:00:56</a>) さあそれではまた今度ですね、また紺の服装に戻ってみたいと思います。<br>
(<a href="#" class="imk-cue" data-seek="1:02" translate="no">00:01:02</a>) はい今度これをずらして、紺の服装に着替えて、<br>
(<a href="#" class="imk-cue" data-seek="1:11" translate="no">00:01:11</a>) で、bodyをこっちはベージュにします。<br>
(<a href="#" class="imk-cue" data-seek="1:22" translate="no">00:01:22</a>) で、紺の方をですね、これ紺のを取り外します。bodyだけにします。<br>
(<a href="#" class="imk-cue" data-seek="1:32" translate="no">00:01:32</a>) そうすると今度入れ替わるはずですよね。はい、それではプログラムを起動させます。<br>
(<a href="#" class="imk-cue" data-seek="1:45" translate="no">00:01:45</a>) はい、どうでしょうか。入れ替わりましたね。これで入れ替わったわけです。<br>
(<a href="#" class="imk-cue" data-seek="1:57" translate="no">00:01:57</a>) こんなように簡単にですね、入れ替わる事ができます。<br>
(<a href="#" class="imk-cue" data-seek="2:02" translate="no">00:02:02</a>) やり方としては、Pixelmator Proにファイルを作って、<br>
(<a href="#" class="imk-cue" data-seek="2:07" translate="no">00:02:07</a>) そのファイル上のパーツを入れ替えるということができます。<br>
(<a href="#" class="imk-cue" data-seek="2:11" translate="no">00:02:11</a>) そしてこのアプリを起動させることによって、サッと入れ替えができるという、<br>
(<a href="#" class="imk-cue" data-seek="2:19" translate="no">00:02:19</a>) 優れた仕組みができております。以上です。<br>
</p> </details>
<style>
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details > summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details > p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }
</style>

  </div>

  <script src="https://player.vimeo.com/api/player.js"></script>

  <script>
  (function(){
    var me = document.currentScript;
    var wrapper = me ? me.closest('.sc-vimeo-embed') : null;

    var target = wrapper ? wrapper : document;
    var iframe = target.querySelector('iframe');
    if (!iframe || !window.Vimeo || !Vimeo.Player) return;

    var player = new Vimeo.Player(iframe);

    /* --- 1. 確実に一時停止してから別ページを開く処理 --- */
    window.stopVimeoBeforeNavigate = function(event, link) {
      event.preventDefault(); 
      player.pause().then(function() {
        setTimeout(function() {
           window.open(link.href, link.target);
        }, 50);
      }).catch(function() {
        window.open(link.href, link.target);
      });
    };

    /* --- 2. 字幕のON/OFF制御（Vimeo API仕様） --- */
    var currentMode = "hidden";
    
    function useNormalMode() {
        currentMode = "hidden";
        player.disableTextTrack().catch(function(e){});
    }
    
    function useSpecialMode() {
        currentMode = "showing";
        // 日本語の subtitles が無ければ captions を探す安全設計
        player.enableTextTrack('ja', 'subtitles').catch(function() {
            player.enableTextTrack('ja', 'captions').catch(function() {});
        });
    }

    // ロード直後は枠内字幕を非表示にする
    player.ready().then(function() {
        useNormalMode();
    });

    // Vimeoプレーヤーの全画面・PiPイベントを監視して字幕を切り替え
    player.on('fullscreenchange', function(data) {
        if (data.fullscreen) useSpecialMode();
        else useNormalMode();
    });
    player.on('enterpictureinpicture', function() {
        useSpecialMode();
    });
    player.on('leavepictureinpicture', function() {
        useNormalMode();
    });

    /* --- 3. 字幕リストの初期化（自動梱包とデフォルト展開） --- */
    var checks = 0;
    var initTimer = setInterval(function(){
        var listContainer = target.querySelector('details > p');
        if (listContainer) {
            initSubtitles(listContainer);
            clearInterval(initTimer);
        }
        checks++;
        if (checks > 20) clearInterval(initTimer); // 10秒経ったら監視終了
    }, 500);

    function initSubtitles(listContainer) {
        // デフォルトで開く
        var detailsEl = target.querySelector("details");
        if (detailsEl) {
            detailsEl.open = true; 
            var summaryEl = detailsEl.querySelector("summary");
            if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
        }

        // 行ごとに見えない箱(span)で梱包
        if (!listContainer.dataset.formatted) {
            var html = listContainer.innerHTML;
            var lines = html.split(/<br\s*\/?>/i);
            var newHtml = "";
            for(var j=0; j<lines.length; j++) {
                if(lines[j].trim() === "") continue;
                newHtml += "<span class='imk-line'>" + lines[j] + "</span><br>";
            }
            listContainer.innerHTML = newHtml;
            listContainer.dataset.formatted = "true";
        }
    }

    /* --- 4. クリックシーク機能 --- */
    function parseTs(ts) {
      if (!ts) return null;
      var t = ts.trim();
      var parts = t.split(':').map(function(v){ return parseInt(v, 10); });
      if (parts.length === 2 && parts.every(Number.isFinite)) {
        return parts[0] * 60 + parts[1];
      } else if (parts.length === 3 && parts.every(Number.isFinite)) {
        return parts[0] * 3600 + parts[1] * 60 + parts[2];
      }
      return null;
    }

    target.addEventListener('click', function(e) {
      var a = e.target.closest('a.imk-cue');
      if (!a) return;
      e.preventDefault();
      var sec = null;
      if (a.dataset.seconds) {
        sec = Number(a.dataset.seconds);
      } else if (a.dataset.seek) {
        sec = parseTs(a.dataset.seek);
      }
      if (sec !== null && Number.isFinite(sec)) {
        player.setCurrentTime(sec).then(function(){ player.play(); }).catch(function(){});
      }
    });

    /* --- 5. 同期ハイライトと自動スクロール --- */
    player.on('timeupdate', function(data) {
      // Vimeoプレーヤーが勝手に字幕を出していたら隠す（最強ガード）
      player.getTextTracks().then(function(tracks) {
          var activeTrack = tracks.find(function(t){ return t.mode === 'showing'; });
          if (currentMode === "hidden" && activeTrack) {
              useNormalMode();
          }
      }).catch(function(){});

      var currentTime = data.seconds;
      var listContainer = target.querySelector('details > p');
      if (!listContainer) return;

      var cues = listContainer.querySelectorAll('a.imk-cue');
      if (cues.length === 0) return;

      var activeA = null;
      for (var i = 0; i < cues.length; i++) {
        var sec = null;
        if (cues[i].dataset.seconds) {
            sec = Number(cues[i].dataset.seconds);
        } else if (cues[i].dataset.seek) {
            sec = parseTs(cues[i].getAttribute('data-seek'));
        }
        if (sec !== null && currentTime >= sec - 0.5) {
          activeA = cues[i];
        } else if (sec > currentTime) {
          break;
        }
      }

      if (activeA) {
          var activeLine = activeA.closest(".imk-line");
          if (!activeLine) activeLine = activeA;

          if (activeLine.classList.contains("active-hl")) return;

          // 全行の色を徹底的にリセット
          var allLines = listContainer.querySelectorAll(".imk-line");
          for(var k=0; k<allLines.length; k++) {
              allLines[k].classList.remove("active-hl");
              allLines[k].removeAttribute("style");
          }
          var allLinks = listContainer.querySelectorAll("a");
          for(var m=0; m<allLinks.length; m++) {
              allLinks[m].classList.remove("active-hl");
              allLinks[m].removeAttribute("style");
          }

          // 新しい行に赤文字・黄色背景を直接塗る
          activeLine.classList.add("active-hl");
          activeLine.style.setProperty("background-color", "#ffff00", "important");
          activeLine.style.setProperty("color", "red", "important");
          activeLine.style.setProperty("font-weight", "normal", "important");
          
          var newLinks = activeLine.querySelectorAll("a");
          for(var n=0; n<newLinks.length; n++) {
              newLinks[n].style.setProperty("color", "red", "important");
              newLinks[n].style.setProperty("text-decoration", "none", "important");
          }

          // スクロール処理
          if (listContainer.offsetParent !== null) {
              var containerRect = listContainer.getBoundingClientRect();
              var activeRect = activeLine.getBoundingClientRect();
              var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (activeLine.clientHeight / 2);
              listContainer.scrollTo({
                  top: targetScroll,
                  behavior: "smooth"
              });
          }
      }
    });
  })();
  </script>

  <style>
  .sc-vimeo-embed .iframe-wrapper {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: visible;
  }
  .sc-vimeo-embed .iframe-wrapper iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }

  /* 行梱包時の基本スタイル（干渉防止のためクラス内で指定） */
  .sc-vimeo-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  /* ▼ 字幕一覧（details）の見た目調整 */
  .sc-vimeo-embed details > p {
    font-size: 14px !important;
    line-height: 1.6;
    height: 200px !important; 
    overflow: auto;
    background-color: #EDF7FF;
    padding: 2px 6px;
    margin: 0;
    box-shadow: 3px 3px 4px black;
    position: relative; 
  }
  .sc-vimeo-embed details > summary {
    font-size: 14px !important;
    padding: 2px 6px;
    width: 100%;
    background-color: #ddd;
    border: none;
    box-shadow: 3px 3px 4px black;
    cursor: pointer;
    list-style: none;
  }
  </style>
</div>



<h2 class="wp-block-heading"><strong>&#x1f4a1; OBSでリアルタイム配信に使いたい方へ</strong></h2>



<p>このパペットシステムは、OBS Studioを使ってリアルタイム配信などの画面に合成することも可能です。</p>



<p>ウィンドウキャプチャでパペットの画面を取り込み、背景（グリーンバック等）を透過させればOKです。</p>



<p><strong>【ワンポイントアドバイス：音ズレの直し方】</strong></p>



<p>映像の処理には少し時間がかかるため、OBS上で「声（マイク）」に対して「パペットの口の動き（映像）」がわずかに遅れて見えることがあります。</p>



<p>その場合は、OBSの「オーディオの詳細プロパティ」から、<strong>マイクの「同期オフセット」を「200ms〜500ms」ほどプラス</strong>に設定して、声をわざと遅らせて映像に合わせると自然になります。私は今500msに設定しています。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=Avm1FlzY.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=Avm1FlzY.png" alt=""/></a></figure>



<h2 class="wp-block-heading"><strong>おわりに</strong></h2>



<p>自分が描いたイラストが、自分の動きに合わせて滑らかに動く体験は、何度やっても感動します。それではご自由に利用ください。</p>



<p></p>



<p></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">28865</post-id>	</item>
	</channel>
</rss>
