🌐

Unity 製 WebGL ゲームを AWS でお手軽公開

2023/12/15に公開

この記事は「Happy Elements Advent Calendar 2023」および「AWS for Games Advent Calendar 2023」12月15日の記事です。

はじめに

Happy Elements 株式会社でインフラグループのグループリーダーを務めております、長谷川です。
普段は全社横断で AWS, GCP, Azure といったクラウドでのサービス設計・構築・運用を担当するグループのリーダーを担当しています。

Happy Elements カカリアスタジオでは、少人数かつ限られた期間内でゲームを企画から開発まで行うチャレンジプロジェクト「SuperLite アプリ」という取り組みを始めました。
Super Lite アプリでは、新しいチャレンジを希望するメンバーでチームを結成し、およそ1ヶ月程度の作業時間を使って1つのゲームを作り上げます。

限られた作業時間の中で App Store や Google Play 向けにアプリを書き出すのは地味に大変な作業ですし、アプリを都度インストールしてもらうのもユーザー視点では面倒です。
これらの課題に対して Super Lite アプリでは、できるだけ気軽にアプリを公開し遊んでもらうために、お手軽に WebGL 版アプリを公開する仕組みを準備しました。

今回はこのお手軽 WebGL ビルドの設定方法や AWS での環境構築についてのお話を紹介したいと思います。

なお、今回紹介する方法で公開されたゲームたちは、 以下のリンクからどなたでも遊べますので、是非こちらもご覧ください。
https://superlite.games/

Unity で PWA ビルドを行う

Super Lite アプリでは、できるだけネイティブアプリと同じような動作を行えるように、 PWA として WebGL ビルドを配布することとしました。
Unity では PWA アプリの書き出しに対応していますが、標準の設定のままでは少し物足りないポイントがありますので、お手軽にできる範疇でさっと体裁を整えていきます。

PWA テンプレートを追加する

Unity では標準の組み込み WebGL テンプレートをカスタマイズして、独自のテンプレートを利用できます。
元となるテンプレートは Unity に予め組み込まれているものをありがたく利用します。
macOS で Unity Hub を利用している場合は、以下のパスに WebGL テンプレートがありますので、 PWA フォルダをコピーします。

/Applications/Unity/Hub/Editor/${エディタのバージョン}/PlaybackEngines/WebGLSupport/BuildTools/WebGLTemplates

WebGL テンプレートは Assets フォルダ直下の WebGLTemplates フォルダ以下に配置する必要があり、フォルダ名がそのままテンプレート名として解釈されます。
今回は Assets/WebGLTemplates/Superlite としてコピーしました。

以下のように Unity Editor から認識されていれば OK です。

PWA テンプレートを手直しする

残念ながら、組み込みのテンプレートそのままでビルドを行うと以下のような問題があります。

  • Web ページのタイトルに Unity WebGL Player という表記が混じる。
  • Web ページが英語扱いされる。
  • favicon や PWA を端末にインストールしたときのアイコンが Unity ロゴになる。
  • ゲームのローディング時、 Unity ロゴが出る。(スプラッシュスクリーンとは別)

このまま公開しても動作に影響は無いのですが、簡単に手直しできそうなものばかりなので順次手直ししていきます。

Web ページのタイトルと言語設定を修正する

html の title タグを修正する必要があります。
WebGL テンプレートのうち、 index.html ファイル冒頭にある head タグ部分を修正します。
ついでに、<html lang="en-us"> も修正しておきましょう。

<!DOCTYPE html>
<html lang="en-us">
<head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Unity WebGL Player | {{{ PRODUCT_NAME }}}</title>
    <link rel="shortcut icon" href="TemplateData/favicon.ico">
    <link rel="stylesheet" href="TemplateData/style.css">
    <link rel="manifest" href="manifest.webmanifest">
</head>

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>{{{ PRODUCT_NAME }}}</title>
    <link rel="shortcut icon" href="TemplateData/favicon.ico">
    <link rel="stylesheet" href="TemplateData/style.css">
    <link rel="manifest" href="manifest.webmanifest">
</head>

{{{ PRODUCT_NAME }}} は Unity がビルド時にアプリ名に置換してくれますので、そのままにしておきましょう。

favicon やその他アイコンを Unity ロゴ以外にする

まずは、先程と同じく index.html ファイルの以下の行を修正します。
rel="shortcut icon は誤りなので rel="icon" とします。[1]
また、現代では png ファイルをファビコンに指定できますので、加工が容易な png ファイルをそのまま指定します。

    <link rel="shortcut icon" href="TemplateData/favicon.ico">

    <link rel="icon" type="image/png" href="TemplateData/favicons/favicon.png">

PWA は manifest.webmanifest ファイルでアプリのアイコンが指定されていますので、以下の行を編集し、任意のアイコン画像を参照するようにします。

   "icons": [{
      "src": "TemplateData/icons/unity-logo-{{{ SPLASH_SCREEN_STYLE.toLowerCase() }}}.png",

   "icons": [{
      "src": "TemplateData/icons/icon.png",

起動時に表示される Unity ロゴは TemplateData/style.css で以下のように指定されていますので、やはり任意のアイコン画像を参照するようにします。

#unity-logo { width: 154px; height: 130px; background: url('unity-logo-{{{ SPLASH_SCREEN_STYLE.toLowerCase() }}}.png') no-repeat center }

#unity-logo { width: 128px; height: 128px; background: url('icons/icon.png') no-repeat center }

これらの対応で手直しは完了です。

ビルドする

あとは、 Unity Editor から先程作成したテンプレートを選択し、ビルドを実行すれば OK です。
注意点として、ビルド名として指定した文字列は WebGL 版の成果物の中に URL の一部として埋め込まれますので、開発コードを入力してしまうとそれがそのまま公開されてしまいます。
そのため、一般公開時に利用する正式名を入力しておくのが無難かと思われます。

AWS 環境を構築する

上記の手順で準備した成果物をホスティングするための環境を構築します。
AWS で静的ページをお手軽ホスティングするために、 CloudFront + S3 による構成を採用しました。
証明書管理には AWS Certificate Manager を利用し、ドメインは Route53 で管理します。

S3 を準備する

特に複雑な要件はなく、S3バケットを準備すれば良いです。
アクセス制御には CloudFront の Origin Access Control を利用するため、パブリックアクセスも拒否してしまいます。

resource "aws_s3_bucket" "example" {
  bucket = "example-bucket"
}
resource "aws_s3_bucket_public_access_block" "example" {
  bucket = aws_s3_bucket.example.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

CloudFront を準備する

S3 をオリジンとした一般的な CloudFront を準備する心づもりで大丈夫なのですが、一部の設定で注意が必要です。

OriginRequestPolicy と CachePolicy

OriginRequestPolicy は AWS が予め提供してくれている Managed-CORS-S3Origin を利用すれば良いのですが CachePolicy は圧縮についての設定が必要でした。
具体的には、下記のプロパティを false として設定しておかないと、 Unity が出力したアーカイブを正しく展開できずに起動に失敗する事象が発生します。

  • enable_accept_encoding_gzip
  • enable_accept_encoding_brotli
data "aws_cloudfront_origin_request_policy" "managed_cors_s3origin" {
  name = "Managed-CORS-S3Origin"
}

resource "aws_cloudfront_cache_policy" "this" {
  name        = "example-cache-policy"
  comment     = "example-cache-policy"
  min_ttl     = 1
  max_ttl     = 86400
  default_ttl = 86400
  parameters_in_cache_key_and_forwarded_to_origin {
    cookies_config {
      cookie_behavior = "none"
    }
    headers_config {
      header_behavior = "none"
    }
    query_strings_config {
      query_string_behavior = "none"
    }
    enable_accept_encoding_gzip   = false
    enable_accept_encoding_brotli = false
  }
}

CloudFront Distribution

上記の OriginRequestPolicy と CachePolicy を利用する以外は、一般的な CloudFront Distribution の設定になります。

resource "aws_cloudfront_distribution" "this" {
  enabled             = true
  is_ipv6_enabled     = false
  comment             = "example"
  aliases             = ["example.com"]
  default_root_object = "index.html"
  http_version        = "http2and3"

  origin {
    origin_id                = "example_origin"
    domain_name              = aws_s3_bucket.example.bucket_regional_domain_name
    origin_access_control_id = aws_cloudfront_origin_access_control.this.id
  }

  default_cache_behavior {
    allowed_methods            = ["GET", "HEAD"]
    cached_methods             = ["GET", "HEAD"]
    target_origin_id           = "example_origin"
    viewer_protocol_policy     = "redirect-to-https"
    compress                   = false
    cache_policy_id            = aws_cloudfront_cache_policy.this.id
    origin_request_policy_id   = data.aws_cloudfront_origin_request_policy.managed_cors_s3origin.id
    response_headers_policy_id = aws_cloudfront_response_headers_policy.this.id
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    acm_certificate_arn      = data.aws_acm_certificate.this.arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

  logging_config {
    include_cookies = false
    bucket          = aws_s3_bucket.logs.bucket_regional_domain_name
  }

  web_acl_id = aws_wafv2_web_acl.this.arn
}

ビルド成果物を S3 にアップロードする

一般的な Web ページでは aws s3 sync コマンドでアップロードが完了しますが、 Unity のビルド成果物に含まれる下記のような2重拡張子のファイルについて、 aws s3 sync では Content-TypeContent-Encoding が正しく設定されず、ブラウザでロードした際に起動に失敗してしまいます。

  • Build/example.framework.js.gz
  • Build/example.loader.js
  • Build/example.data.gz
  • Build/example.wasm.gz

やり方は色々あると思いますが、本環境では CI/CD に GitHub Actions を利用していたことから、ランナー上で Python スクリプトを動かしてアップロードしてしまうことにしました。
boto3 の S3 クライアントを利用することで、ファイルをアップロードする際に ExtraArgs として Content-TypeContent-Encoding を明示することが可能です。
実際のコードでは aws s3 sync --delete のように、ローカルから削除されたファイルを S3 から削除するようなこともやっていますが、アップロード部分だけを抜粋します。
content_typecontent_encoding はファイル名の末尾を見て愚直に判断しています。

s3 = boto3.client('s3')

for fullpath, content_type in upload_files:
    key = 'hogehoge' # 実際にはファイル名などから S3 上での名前を適切に採番する
    extra_args = {
        'ContentType': content_type
    }
    content_encoding = get_content_encoding(fullpath)
    if content_encoding != '':
        extra_args['ContentEncoding'] = content_encoding
    s3.upload_file(fullpath, bucket, key, ExtraArgs=extra_args)

まとめ

Unity 製 WebGL ゲームを AWS でお手軽に公開する方法についてご紹介しました。
今回利用する AWS サービスはどれもユーザーでのメンテナンスが原則不要なサービスばかりですので、公開後に追加で対応に追われることもなく気軽に構築・運用ができるものではないかと思います。
Happy Elements カカリアスタジオ エンジニア Advent Calendar 2023AWS for Games Advent Calendar 2023 には、他にも面白い記事がたくさんありますので、是非そちらもご覧ください!

脚注
  1. https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/rel ↩︎

Happy Elements

Discussion