🎵

プロセカ映画のワンシーンをARで再現してみたかった

2025/02/09に公開

はじめに

最近、プロセカ映画や劇場版プロセカとも呼ばれる「劇場版プロジェクトセカイ 壊れたセカイと歌えないミク」を観てきました。

映画内の撮影可能シーンでの撮影
その中にワンシーンである、「バツミクちゃんが歌で想いを伝えようとするもノイズに見えてしまう」というところで、スマートフォンからノイズ姿のバツミクちゃんが出現している様子をARで再現しようと思いました。

作ったもの

https://x.com/Taipui/status/1888194852422349071

制作期間と内訳

制作期間は2/2(日)~2/8(土)になりました。

日曜:作り方をChatGPTに聞く
月曜:リポジトリを作成し、ビルド環境を整える
火曜:送受信処理を実装する
水曜:オブジェクトがスマホの位置にリンクするようにする
木曜:作業なし
金曜:作業なし
土曜:グリッチ表現を作成する、完成させる

の流れで制作しました。

作り方について

ARコンテンツにするというのは最初に決めていて、今までにもいくつか簡易的なARコンテンツは作ったことがあったので、大体いけそうな気はしていました。
前にUnityのARTrackedImageManagerを使った画像認識のコンテンツを作ったので、今回もそれでいこうかなと思ったのですが、
今回作るものはスマホの画面から出すので、

  • 画面はスマホのホーム画面(風)にしたい(マーカー感は出したくない)
  • 画像認識だと角度によってトラッキングが外れやすい

ので、スマホから現在位置を送受信して、その位置にオブジェクトを配置する方向にしました。

なんとなくローカルIPアドレスを使って送受信できるだろうとは思っていましたが、具体的な実装法が分からなかったので、ChatGPTに聞くことにしました。

どうやらUDP通信を使えば実現できそうなので、UDP通信を実装していくことにしました。

UDP通信について

といってもUDPに関する知識はないので、ChatGPTにコードを書いてもらいました。

using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using System.Net.Sockets;
using System.Text;

public class ARPositionSender : MonoBehaviour
{
    public ARSessionOrigin arSessionOrigin;
    public ARCameraManager arCameraManager;
    private UdpClient udpClient;
    private string serverIP = "192.168.1.100"; // PCのローカルIP
    private int port = 8080;

    void Start()
    {
        udpClient = new UdpClient();
    }

    void Update()
    {
        // スマホのワールド座標を取得
        Vector3 position = arSessionOrigin.camera.transform.position;
        Quaternion rotation = arSessionOrigin.camera.transform.rotation;

        // データを送信(JSON形式)
        string data = JsonUtility.ToJson(new ARData(position, rotation));
        byte[] bytes = Encoding.UTF8.GetBytes(data);
        udpClient.Send(bytes, bytes.Length, serverIP, port);
    }

    void OnApplicationQuit()
    {
        udpClient.Close();
    }

    [System.Serializable]
    public class ARData
    {
        public float px, py, pz;
        public float rx, ry, rz, rw;

        public ARData(Vector3 position, Quaternion rotation)
        {
            px = position.x; py = position.y; pz = position.z;
            rx = rotation.x; ry = rotation.y; rz = rotation.z; rw = rotation.w;
        }
    }
}
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class ARPositionReceiver : MonoBehaviour
{
    private UdpClient udpClient;
    private Thread receiveThread;
    public Transform phoneRepresentation; // スマホの位置を再現するオブジェクト

    void Start()
    {
        udpClient = new UdpClient(8080);
        receiveThread = new Thread(new ThreadStart(ReceiveData));
        receiveThread.IsBackground = true;
        receiveThread.Start();
    }

    void ReceiveData()
    {
        IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 8080);
        while (true)
        {
            byte[] data = udpClient.Receive(ref endPoint);
            string json = Encoding.UTF8.GetString(data);
            ARData receivedData = JsonUtility.FromJson<ARData>(json);

            // メインスレッドで実行
            UnityMainThreadDispatcher.Instance().Enqueue(() => {
                phoneRepresentation.position = new Vector3(receivedData.px, receivedData.py, receivedData.pz);
                phoneRepresentation.rotation = new Quaternion(receivedData.rx, receivedData.ry, receivedData.rz, receivedData.rw);
            });
        }
    }

    void OnApplicationQuit()
    {
        receiveThread.Abort();
        udpClient.Close();
    }

    [System.Serializable]
    public class ARData
    {
        public float px, py, pz;
        public float rx, ry, rz, rw;
    }
}

これら2つのスクリプトをそれぞれアタッチしたGameObjectを作成して、Sender/Receiverに応じてどちらかのGameObjectをActiveにする処理を作成しました。

簡易的なUIも作成しました
これだけで2つのスマホ間でデータを送受信できるので驚きました。

スマホとオブジェクトの位置同期について

これで送信側のスマホの位置を受信側が知ることはできたので、あとはここにオブジェクトを表示すればいいだけだと思っていたのですが、やはり位置ズレが激しく、苦労しました…

何もしなくても位置がズレていったりする
なので、本当はスマホを手で持って、傾きに追従しますよというアピールをしたかったのですが、泣く泣くあきらめました…
(結局、UDP通信しているのに画像認識と同じような見た目に…)
位置調整も、本当はやりたくないのですが、手動で位置合わせをしています…

(開発途中、目印オブジェクトがどこに行ったか分からなくなることが多かったので、パーティクルで軌跡が見えるようにしておくのはアリかもしれないと思いました)

ノイズエフェクトについて

オブジェクトの表示に関しては週の半ばで目星がついたので、週の後半でそろそろエフェクト制作に入ろうと思っていました。
おそらく映画の予告映像にノイズのシーンがあるだろうからそれを見ながら実装考えようと思っていたのですが、探しても見当たらず…
(普通にバツミクちゃんがスマホから出ているシーンはあるのですが、ノイズ表現のシーンは予告編にはなかった…)
ここに来て、計画が破綻してしまいました。
割と真剣に、今回の制作はあきらめようかなと思っていました。(最初にちゃんと確認しておけば…)
ですが、ここまで来たら、もう再現度は後回しにして、せめて見せられるくらいまでもっていって公開までしたいと思い、自分のおぼろげな記憶を頼りに、それっぽい表現を作ることにしました。
いつもならShurikenでパーティクルを作るかアセットを探すかするのですが、今回はVisual Effect Graphを使いたい気持ちがありました。
今までほとんど使ったことがなかったのですが、これを機に挑戦してみることにしました。

といっても、テンプレートのSimpleLoopをChatGPTに聞きながらちょっと編集した程度です。
四角のエフェクトにしたかったので矩形の画像を作り、デジタル感を出すためにアルファ値の調整とHDR、ブルームのポストエフェクトを足しました。

ホーム画面について

スマホがホーム画面になっている状態から出現させたかったのですが、アプリをバックグラウンドで起動するようなアプローチは大変そうだなと思っていたので、あらかじめスクリーンショットを撮った画像をアプリ側で表示するようにしました。(ズルしている感がありますが)


「一般的なホーム画面」が分からずちょっと苦労した

Senderを選択した場合、ダミーのホーム画面が表示される
(画面下にあるホームバーを消せてないので、よく見ると嘘だとバレる)

さいごに

今回は、正直自分でもあまり満足いく作品にはできませんでした。
けれど、今年はできるだけ作ったものは公開していくと決めたので、このレベルのものでもとりあえず上げていく方向でいこうと思っています。
やはり悔しいので、次はもっとクオリティの高いものを作りたいですね…
できた作品は微妙でしたが、UDP通信やVFXGraphなど新たな経験も積めたので、それはよかったかなと思っています。

おまけ


そうはならんやろ

追記(20250214)

プロセカ公式から、本編冒頭映像が公開されました!

『劇場版プロジェクトセカイ 壊れたセカイと歌えないミク』本編冒頭映像 - YouTube
https://www.youtube.com/watch?v=YerlMl-uKTw




全然違った…

Discussion