Unity 製 WebGL ゲームを AWS でお手軽公開
この記事は「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 での環境構築についてのお話を紹介したいと思います。
なお、今回紹介する方法で公開されたゲームたちは、 以下のリンクからどなたでも遊べますので、是非こちらもご覧ください。
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-Type
と Content-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-Type
や Content-Encoding
を明示することが可能です。
実際のコードでは aws s3 sync --delete
のように、ローカルから削除されたファイルを S3 から削除するようなこともやっていますが、アップロード部分だけを抜粋します。
content_type
や content_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 2023 や AWS for Games Advent Calendar 2023 には、他にも面白い記事がたくさんありますので、是非そちらもご覧ください!
Discussion