UnityでLambdaを介してDynamoDBにアクセスするAPI連携を実装する方法

# 今回実装したこと
Unity ⇆ API gateway ⇆ Lambda ⇆ DynamoDB ⇆ S3
S3上に置いた画像データをおき、そこに至るパスをDynamoDB上にストックし、Unityからの入力をキッカケにAPI gatewayを経由してLambdaを起動して、入力情報に応じてDynamoDBから必要なS3のパスを返す実装を完成しました。
文字だけだと呪文にしか聞こえないので、実際に動いているところを共有します。

UnityでAWSのサービスを使用する場合、余計なSDKやプラグインなどを使用せず、あくまでAWSのサービスのみで完結する方法についてまとめました。
これができれば、フロントサイドは別にUnityじゃなくてもさまざまなサービスを利用することが可能です。

# 注意点
AWSの使用は従量課金制となりますので、ここから先を進んだ場合に使用量が発生する可能性があります。そのため、ここから先に進んで費用が発生しても自己責任でお願いいたします。

# AWS側の設定

## AWSへのログイン
他の記事やgoogleで検索し、AWSのコンソール画面まで進んでください。
[![Image from Gyazo](https://i.gyazo.com/5d83af0604593967102acd1e9d905328.png)](https://gyazo.com/5d83af0604593967102acd1e9d905328)

## S3の設定
まずは、動画ファイルを置いておく場所をS3上に作成します。

### バケットの作成
下記の画像を参考に、今回使用する化と作成します。
[![Image from Gyazo](https://i.gyazo.com/e2523a996fea8c74dd5648d66ae3ccee.png)](https://gyazo.com/e2523a996fea8c74dd5648d66ae3ccee)

[![Image from Gyazo](https://i.gyazo.com/6d077974607fcaf52dbfbef2f1f45a1f.png)](https://gyazo.com/6d077974607fcaf52dbfbef2f1f45a1f)

[![Image from Gyazo](https://i.gyazo.com/2bf193815758cabb02fa0b4aa1973ee8.png)](https://gyazo.com/2bf193815758cabb02fa0b4aa1973ee8)

[![Image from Gyazo](https://i.gyazo.com/061145089b9ee37ee37d1c684189587b.png)](https://gyazo.com/061145089b9ee37ee37d1c684189587b)

### バケットパブリック設定
個別のURLを叩くだけで動画が見られるようにするために、下記のように設定しパブリックアクセスを許可します。
[![Image from Gyazo](https://i.gyazo.com/0e3e4dc792d92c55d53b4cf72f62cf56.png)](https://gyazo.com/0e3e4dc792d92c55d53b4cf72f62cf56)

[![Image from Gyazo](https://i.gyazo.com/3852378b80608bad25b72e8aa0482292.png)](https://gyazo.com/3852378b80608bad25b72e8aa0482292)

[![Image from Gyazo](https://i.gyazo.com/ba4acb2fdd5e15256b237b5da4c12c15.png)](https://gyazo.com/ba4acb2fdd5e15256b237b5da4c12c15)

バケットポリシーの部分に下記のスクリプトを貼り付けます。
スクリプト内の'bucket name'は今回作成したバケット名に変更してください。

[![Image from Gyazo](https://i.gyazo.com/e4a4cce56e9977d7b9a7bd16823a67c1.png)](https://gyazo.com/e4a4cce56e9977d7b9a7bd16823a67c1)

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::<bucket　nameを入れてください>/*"
            ]
        }
    ]
}
```

### 動画のアップロード
下記を参考に、自身の動画をアップロードしていきます。
[![Image from Gyazo](https://i.gyazo.com/7015642e05cb3ecfeaacff25ccee0fb6.png)](https://gyazo.com/7015642e05cb3ecfeaacff25ccee0fb6)

[![Image from Gyazo](https://i.gyazo.com/deb32bd52d61114402d9d8af63cbc8bb.png)](https://gyazo.com/deb32bd52d61114402d9d8af63cbc8bb)

作成されたURLをコピーして、

[![Image from Gyazo](https://i.gyazo.com/4af4602b4c0fc0629c271a73b5a40ac5.png)](https://gyazo.com/4af4602b4c0fc0629c271a73b5a40ac5)

ブラウザに貼り付けると…、

[![Image from Gyazo](https://i.gyazo.com/db27b3915e889becfdb2d0ec79ef86d3.jpg)](https://gyazo.com/db27b3915e889becfdb2d0ec79ef86d3)

無事に動画が流れます。


## DynamoDBの設定
続いて、先ほど作成した動画につながるS3のURLをためておくデータベースをDynamoDBに作成します。

### テーブルの作成
DynamoDBでは作成するデータベースをテーブルと呼びます。
下記の画像を参考にテーブルを作成します。

[![Image from Gyazo](https://i.gyazo.com/38765414491a4a743b87e4df160c3126.png)](https://gyazo.com/38765414491a4a743b87e4df160c3126)

[![Image from Gyazo](https://i.gyazo.com/3da352b8567a164f2b3f6397b7d6405f.png)](https://gyazo.com/3da352b8567a164f2b3f6397b7d6405f)

[![Image from Gyazo](https://i.gyazo.com/ae85fec6fa26c5fbba7f010cf094cbdb.png)](https://gyazo.com/ae85fec6fa26c5fbba7f010cf094cbdb)

### データの更新方法
下記の手順でノーコードでDynamoDB上のデータの更新・登録が可能です。

[![Image from Gyazo](https://i.gyazo.com/109b0d89af1c924fd30b45fb4043f1d7.png)](https://gyazo.com/109b0d89af1c924fd30b45fb4043f1d7)

[![Image from Gyazo](https://i.gyazo.com/828a4487b8d9a3f7034226a6de19f12c.png)](https://gyazo.com/828a4487b8d9a3f7034226a6de19f12c)

[![Image from Gyazo](https://i.gyazo.com/0f923c332e4820f3a971575374c0beb1.png)](https://gyazo.com/0f923c332e4820f3a971575374c0beb1)

ちなみに今回は、下記のように設定しました。
[![Image from Gyazo](https://i.gyazo.com/35624a33893f6a4ee01dae9864514926.png)](https://gyazo.com/35624a33893f6a4ee01dae9864514926)


## Lambdaの設定
次に、DynamoDBからデータを引き出すために、Lambdaというサービス上に関数を作成し、UnityからLambdaに刺激が入ると関数が起動するように設定を行います。

### IAMの設定
まず初めに、LamdbaがDynamoDBの内容にアクセスできるようにIAMで権限を設定します。
下記を参考にDynamoDBとCloudWatchのFullAccessのポリシーを持つロールを作成してください。
[![Image from Gyazo](https://i.gyazo.com/29e5401e4df69160e1dad4c5bc6457a6.png)](https://gyazo.com/29e5401e4df69160e1dad4c5bc6457a6)

[![Image from Gyazo](https://i.gyazo.com/88d88585a813b736797691e3e978ff88.png)](https://gyazo.com/88d88585a813b736797691e3e978ff88)

[![Image from Gyazo](https://i.gyazo.com/8a3912683f86d80827003ff8ac494b36.png)](https://gyazo.com/8a3912683f86d80827003ff8ac494b36)

[![Image from Gyazo](https://i.gyazo.com/5ea6ab841fb69857c7b6c0c5d4bb6e17.png)](https://gyazo.com/5ea6ab841fb69857c7b6c0c5d4bb6e17)

[![Image from Gyazo](https://i.gyazo.com/841f112b5a6840f6b55793c14d557818.png)](https://gyazo.com/841f112b5a6840f6b55793c14d557818)

[![Image from Gyazo](https://i.gyazo.com/1c5ebb4ad8e05b2ceb86504e6606e78f.png)](https://gyazo.com/1c5ebb4ad8e05b2ceb86504e6606e78f)

### Lambdaの設定
Lambdaに移動して関数を作成します。

[![Image from Gyazo](https://i.gyazo.com/975fda8aa41da97efbecb80e887ad5fe.png)](https://gyazo.com/975fda8aa41da97efbecb80e887ad5fe)

今回は、Node.jsで関数を作成します。
[![Image from Gyazo](https://i.gyazo.com/389f3d7abf8b40575da615644cdc81e5.png)](https://gyazo.com/389f3d7abf8b40575da615644cdc81e5)

コードソースのindex.jsの部分に下記のコードを貼り付け、Deployを押します。
[![Image from Gyazo](https://i.gyazo.com/33a68ba5208db068808a65acb84adb62.png)](https://gyazo.com/33a68ba5208db068808a65acb84adb62)

```javascript
const AWS = require('aws-sdk')
const client = new AWS.DynamoDB.DocumentClient()

/**
 * API Gatewayを通してリクエストから受け取ったユーザーID、動画IDと一致するitemを取得して返却するLambda
 * 
 * @param {*} event APIGatwayからわたってくるパラメーターがこのevent引数を通して渡される
 * GETリクエストにおけるクエリはqueryStringParametersというプロパティに設定されている
 * @returns 
 */
exports.handler = async (event)=>{

    console.log("event", event);
    const DoctorID = event.Keys.DoctorID;
    const MovieID = event.Keys.MovieID;

    /**
     * ハッシュキー:userId、ソートキー:movieIdというDynamoDBテーブルを想定している
     */
    const result = await client.query({
        TableName:'MoviesDB',
        KeyConditionExpression:'#DoctorID = :DoctorID AND begins_with(#MovieID , :MovieID)',
        ExpressionAttributeNames:{
            '#DoctorID':'DoctorID',
            '#MovieID':'MovieID'

        },
        ExpressionAttributeValues:{
            ':DoctorID':DoctorID,
            ':MovieID':MovieID,
        }
    }).promise()


    const items = result.Items

    return {
        statusCode:200,
        body:items
    }
    
}

```

### ロールの設定
[![Image from Gyazo](https://i.gyazo.com/8a373d40d9e92a3089b8f2fe24cfdb2d.png)](https://gyazo.com/8a373d40d9e92a3089b8f2fe24cfdb2d)

名前は適宜設定し、ロール部分に先ほど作成したロールを割り当てます。
[![Image from Gyazo](https://i.gyazo.com/68cdc3e9feb231b2962af8297da4b576.png)](https://gyazo.com/68cdc3e9feb231b2962af8297da4b576)

## API Gatewayの設定
最後に、Unityからの刺激をLambdaに伝える通り道であるAPIをAPI Gatewayで作成します。

### APIの作成
下記の図を参考にAPIを作成します。

[![Image from Gyazo](https://i.gyazo.com/29c629cd354f989c537701a3d54c13e1.png)](https://gyazo.com/29c629cd354f989c537701a3d54c13e1)

今回はREST APIを作成します。

[![Image from Gyazo](https://i.gyazo.com/8b52b1b165e752b0e087506d3d5361c7.png)](https://gyazo.com/8b52b1b165e752b0e087506d3d5361c7)

名前を適当に設定しAPIを作成する
[![Image from Gyazo](https://i.gyazo.com/0f417a0e28efc70349048f584ebfc7fa.png)](https://gyazo.com/0f417a0e28efc70349048f584ebfc7fa)

[![Image from Gyazo](https://i.gyazo.com/dd2173d5149e15f2b51377f1edd568f1.png)](https://gyazo.com/dd2173d5149e15f2b51377f1edd568f1)

[![Image from Gyazo](https://i.gyazo.com/27a0f3bd1e2d708b89f93f74955414bb.png)](https://gyazo.com/27a0f3bd1e2d708b89f93f74955414bb)

[![Image from Gyazo](https://i.gyazo.com/37fa29e874558f7d5d103e1545c2d834.png)](https://gyazo.com/37fa29e874558f7d5d103e1545c2d834)

[![Image from Gyazo](https://i.gyazo.com/32efd2943b1382691306e39acd489369.png)](https://gyazo.com/32efd2943b1382691306e39acd489369)

[![Image from Gyazo](https://i.gyazo.com/2c01d161402cff1ef105b4b4f2285fda.png)](https://gyazo.com/2c01d161402cff1ef105b4b4f2285fda)

[![Image from Gyazo](https://i.gyazo.com/5339904eadffb86a1521d8118fab4772.png)](https://gyazo.com/5339904eadffb86a1521d8118fab4772)

[![Image from Gyazo](https://i.gyazo.com/d19fc2f9f9daeabd4b8e56f54d1ad4a5.png)](https://gyazo.com/d19fc2f9f9daeabd4b8e56f54d1ad4a5)

[![Image from Gyazo](https://i.gyazo.com/cf4dda47139de0dfafb1bbfa2162e26e.png)](https://gyazo.com/cf4dda47139de0dfafb1bbfa2162e26e)

[![Image from Gyazo](https://i.gyazo.com/09b5db4a07514b9c715dbbcb1906d3d9.png)](https://gyazo.com/09b5db4a07514b9c715dbbcb1906d3d9)

[![Image from Gyazo](https://i.gyazo.com/12c57a799031ab3e7e1fc6e936803e87.png)](https://gyazo.com/12c57a799031ab3e7e1fc6e936803e87)

[![Image from Gyazo](https://i.gyazo.com/6d28b0fd106d80357a1ed8c651b8e5c5.png)](https://gyazo.com/6d28b0fd106d80357a1ed8c651b8e5c5)

APIの完成
[![Image from Gyazo](https://i.gyazo.com/01e3be7d024489af455489ca46f61008.png)](https://gyazo.com/01e3be7d024489af455489ca46f61008)

# Unity側の設定
最後にUnity側から動画を呼び出す設定を行います。

## 動画を映す板の作成
動画のはめ込みに関しては他の記事に譲ります。
流れは以下の通りです。
① 3D planeを作成
② videoplayerのオブジェクトを作成
③ render textureを作成
④ materialを作成
⑤ 作成したmaterialに作成したrender textureをアタッチ
⑥ 3D planeにmaterialをアタッチ
⑦ videoplayerコンポーネントにrender textureをアタッチ

### VideoPlayerのスクリプト
下記のスクリプトをVideoPlayerオブジェクトにアタッチします。

```c#:VideoplayerManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Video;

public class VideoPlayerManager : MonoBehaviour
{
    VideoPlayer video;
    public List<string> URLs = new List<string>();

    public void VideoPlay(int num)
    {
        video = gameObject.GetComponent<VideoPlayer>();
        video.playOnAwake = false;
        video.waitForFirstFrame = true;
        video.source = VideoSource.Url;
        video.url = URLs[num];

        video.prepareCompleted += VideoPlayerOnPrepareCompleted;
        video.Prepare();
    }

    private void VideoPlayerOnPrepareCompleted(VideoPlayer source)
    {
        video.Play();
    }

    public void OnClickVideoPlay(int num)
    {
        VideoPlay(num);
    }
}
```

### AWSに接続するための設定
下記のスクリプトをからのオブジェクトにアタッチします。

```c#:unitytoaws
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Serialization;
using UnityEngine.UI;
public class unitytoaws : MonoBehaviour
{
    [SerializeField] private string DoctorID;
    [SerializeField] private string MovieID;
    [SerializeField] private DynamoResponse response;
    [SerializeField] VideoPlayerManager videoPlayerManager;

    public string URL;

    [Serializable]
    public class DynamoIftest
    {
        public DynamoQueryKey Keys;
        [Serializable]
        public class DynamoQueryKey
        {
            public string DoctorID;
            public string MovieID;
        }
    }

    [Serializable]
    public class DynamoResponse
    {
        public List<DynamoResponseItems> body;
        [Serializable]
        public class DynamoResponseItems
        {
            public string DoctorID;
            public string MovieID;
            public string URL;
        }
    }
    void Start()
    {
        StartCoroutine(AwsApiTest());
    }

    private IEnumerator AwsApiTest()
    {
        var awsElement = new DynamoIftest()
        {
            Keys = new DynamoIftest.DynamoQueryKey() { DoctorID = DoctorID, MovieID = MovieID }
        };
        string jsonStr = JsonUtility.ToJson(awsElement);

        var request = new UnityWebRequest();

        request.url = "<ApiGatewayで作成したApiのUrl>";
        var body = Encoding.UTF8.GetBytes(jsonStr);
        request.uploadHandler = new UploadHandlerRaw(body);
        request.downloadHandler = new DownloadHandlerBuffer();

        request.SetRequestHeader("Content-Type", "application/json");
        request.method = UnityWebRequest.kHttpVerbPOST;
        yield return request.Send();

        if (request.isNetworkError)
        {
            Debug.Log(request.error);        }
        else
        {
            if (request.responseCode == 200)
            {
                var responseJsonText = request.downloadHandler.text;
                Debug.Log(responseJsonText);
                response = JsonUtility.FromJson<DynamoResponse>(responseJsonText);
                Debug.Log(response.body[0].URL);
                URL = response.body[0].URL;

                videoPlayerManager.URLs.Clear();
                foreach (var item in response.body)
                {
                    videoPlayerManager.URLs.Add(item.URL);
                }

            }
            else
            {
                Debug.Log("failed");
            }
        }
    }
}

```

# 完成形
完成した挙動がこちらです。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">UnityからLambdaを刺激して、DynamoDBからS3上にある画像データまでのパスを引っ張ってくる実装が完成！<br>AWSの理解が深まってきた！<a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a><a href="https://twitter.com/hashtag/%E3%82%B9%E3%82%BF%E3%82%B8%E3%82%AA%E3%81%97%E3%81%BE%E3%81%A5?src=hash&amp;ref_src=twsrc%5Etfw">#スタジオしまづ</a><a href="https://twitter.com/hashtag/%E3%82%82%E3%81%84%E3%81%9B%E3%82%93?src=hash&amp;ref_src=twsrc%5Etfw">#もいせん</a> <a href="https://t.co/T46K6szHuy">https://t.co/T46K6szHuy</a> <a href="https://t.co/M8zwuLhJ2v">pic.twitter.com/M8zwuLhJ2v</a></p>&mdash; Dr.Teru:プログラミングとサーフィンが好きな整形外科医 (@teru3_kitashiro) <a href="https://twitter.com/teru3_kitashiro/status/1553831930738708480?ref_src=twsrc%5Etfw">July 31, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

今後もSDKを使用せずに、APIでUnityからAWSを使える実装の勉強を進めていきます。