見出し画像

『あんさんぶるスターズ!!Music』におけるUnityでのSNSを活用したキャンペーンの実装

この記事は「Cacalia Studio アドベントカレンダー 2021 エンジニア版」の4日目の記事です。

はじめに

『あんさんぶるスターズ!!Music』開発エンジニアのT.Tです。
本記事ではあんスタ!!Musicで定期的に実施している「MV衣装お試しキャンペーン」における、SNSの活用とその実装についてご紹介します

あんさんぶるスターズ!!Music「MV衣装お試しキャンペーン」

あんスタ!!Musicはアイドルたちのハイクオリティな3DCGを駆使したMVを楽しむことができるリズムゲームアプリです。自分のお気に入りのアイドルに好きな衣装を着せてMVを楽しむことができるのも魅力としています。

画像1

この「MV衣装」は、スカウトやイベントで獲得したアイドルのカードを育成することで開放することができます。
開放していないMV衣装は基本使用することができませんが、それをお試しで着せ替えし放題でMVを楽しめるのがこの「MV衣装お試しキャンペーン」です。

画像2

「MV衣装お試しキャンペーン」におけるSNSの活用

「MV衣装お試しキャンペーン」は、ただ好きに着せ替え放題なだけではなく、お気に入りの編成をSNSでシェアすることができるキャンペーンとなっています。

アプリ内のボタンから以下のようにSNSに投稿することができます。

画像3

ハッシュタグを入れることでトレンド入りも果たし、個人で好きな編成を楽しむキャンペーンでありながらシェアという形で大きな盛り上がりとなりました。また編成の画像もシェアすることで、ハッシュタグから他の人がシェアした編成を眺めているだけでも楽しいキャンペーンになったのではないかと思っています。

そしてシェアされた投稿を見たユーザーは、

● ディープリンクを用いたURLをクリックすることによるアプリを起動
● 表示されたIDによる検索
● QRコードでのIDの読み取り(※QRコードは(株)デンソーウェーブの登録商標です)

によって実際にその編成でアプリ内でMVを楽しむことができます。

画像4

画像5

SNSシェア機能の実装について

それではここからは本機能のUnityでの実装方法について紹介していきます。
※ディープリンクを用いたURLについては紹介を省かせていただきます

シェア用のスクリーンショットの準備

「MV衣装お試しキャンペーン」では、シェアされる画像はアプリ内では表示せず裏で生成しています。
と言っても、Cameraのdepthを調整して見えないところに画面を描画してそれを画像にしているだけなので、アプリ内で表示する場合でも処理は変わらないはずです。

また、投稿される画像は同じ解像度・同じ見た目にして、シェアされて並んだ場合の見た目を良くしたいというちょっとしたこだわりがあったため、Unity標準のScreenCapture.CaptureScreenshotは使わない実装としています。

もちろん、スクリーンショットではなく事前に準備した画像をSNSにシェアする場合は不要な手順にはなります。

実装

先に大まかな実装を書いておくと、

● Camera#depthを調整して見えないようにスクショしたい画面を描画
Texture2D#ReadPixelsでスクショしたいピクセルを取得
● RenderTexture経由でリサイズしてpngで保存

という流れになっています。

以下サンプルコードです。
今回はデザイナーさんから1200*675でリサイズしてくださいとの指定があったのでそのようにしています。

csharp
[RequireComponent(typeof(Camera))]
public class ScreenShotManager : MonoBehavior
{
   string _screenShotSavedPath;
   public void TakeScreenShot(string savedPath)
   {
       _screenShotSavedPath = savedPath;
   }
   void OnPostRender()
   {
       if (string.IsNullOrEmpty(_screenShotSavedPath))
       {
           return;
       }
       float screenShotWidth = Screen.width;
       float screenShotHeight = Screen.height;
       if (screenShotWidth / screenShotHeight < 16f / 9)
       {
           // 縦長端末なので調整
           screenShotHeight = screenShotWidth * (9f / 16);
       }
       else
       {
           // 横長端末なので調整
           screenShotWidth = screenShotHeight * (16f / 9);
       }
       var tempTexture = new Texture2D((int)screenShotWidth, (int)screenShotHeight);
       // 画面の中心を基準に(screenShotWidth, screenShotHeight)の領域を取得
       tempTexture.ReadPixels(
           new Rect(Screen.width / 2f - screenShotWidth / 2f, Screen.height / 2f - screenShotHeight / 2f,
           screenShotWidth, screenShotHeight), 0, 0);
       tempTexture.Apply();
       
       // 1200*675のRenderTextureにtempTextureを投影
       var renderTexture = RenderTexture.GetTemporary(1200, 675);
       Graphics.Blit(tempTexture, renderTexture);
       
       // 投影したRenderTextureをアクティブに指定
       var original = RenderTexture.active;
       RenderTexture.active = renderTexture;
       
       // 投影したRenderTextureから1200*675で読み取り
       var resultTexture = new Texture2D(1200, 675);
       resultTexture.ReadPixels(new Rect(0, 0, 1200, 675), 0, 0);
       resultTexture.Apply();
       RenderTexture.active = original;
       
       // 保存
       File.WriteAllBytes(_screenShotSavedPath, resultTexture.EncodeToPNG());
       _screenShotSavedPath = null;
       
       // 一時的に使ったものの破棄
       RenderTexture.ReleaseTemporary(renderTexture);
       DestroyImmediate(tempTexture);
       DestroyImmediate(resultTexture);
   }
}

このtexture2D.ReadPixelsはUnityのイベント関数であるOnPostRenderで呼ぶ必要があります。
そしてこのOnPostRender()はCameraにアタッチされたスクリプトで発火される関数なので、RequireComponent属性を付けています。なので、このスクリプトはCameraがあるオブジェクトにアタッチする必要があります。

画像解像度の調整については、調整をせずに単純に画面のスクリーンショットとした場合は以下のような画像ができあがります。

● 横長端末の場合(iPhone Xなど)

画像6

● 縦長端末の場合(iPadなど)

画像7

(上下に空白が入っています)

● 調整済みの場合

画像8

綺麗に上下の不要部分を切ることができました。

SocialConnectorでSNS共有

SNSへのシェアについては、SocialConnectorというプラグインを利用させていただいています。
かなり簡単にSNSシェアができるプラグインですが、すでにPublic archiveなリポジトリとなっているため同様の他のプラグインを探した方がいいかもしれません。

以下サンプルコードです。ScreenShotManager.csとは別のスクリプトとしています。

csharp
static readonly string SharedFilePath = Path.Combine(UnityEngine.Application.persistentDataPath, "shared_picture.png");

[SerializeField] ScreenShotManager _screenShotManager;

async UniTask Share()
{
   if (File.Exists(SharedFilePath))
   {
       File.Delete(SharedFilePath);
   }
   
   _screenShotManager.TakeScreenShot(SharedFilePath);
   // 保存に失敗した場合無限ループになるので5秒で切る
   await UniTask.WhenAny(UniTask.Delay(5000), UniTask.WaitUntil(() => File.Exists(SharedFilePath)));
   if(File.Exists(SharedFilePath))
   {
       SocialConnector.SocialConnector.Share(シェアしたい文言, "", SharedFilePath);
   }
}

シェア用画像がユーザーの端末に残り続けてしまうので、どこかしらで消す処理は書いておいた方が良さそうです。

QRコードの作成・読み取りの実装

QRコードの作成・読み取りにはZXingというプラグインを利用させていただいています。

● 作成

csharp
using UnityEngine;
using ZXing;
using ZXing.QrCode;

public static class QRCodeGenerator
{
   public static Texture2D Generate(string text, int size)
   {
       var barcodeWriter = new BarcodeWriter
       {
           Format = BarcodeFormat.QR_CODE,
           Options = new QrCodeEncodingOptions
           {
               Height = size,
               Width = size
           }
       };

       var pixels = barcodeWriter.Write(text);

       var qrCodeTexture = new Texture2D(size, size);
       qrCodeTexture.SetPixels32(pixels);
       qrCodeTexture.Apply();

       return qrCodeTexture;
   }
}

● 読み取り
 WebCamTextureの取得についてはカメラの権限の扱いなどもあり少し複雑なので今回の説明からは省かせていただきます。

csharp
using System;
using System.Collections.Generic;
using UnityEngine;
using UniRx.Async;
using ZXing;
using ZXing.Common;

public static class QRCodeReader
{
   public static async UniTask Play(WebCamTexture webCamTexture, Action<string> completion)
   {
       var text = await Play(webCamTexture);
       completion?.Invoke(text);
   }
   static async UniTask<string> Play(WebCamTexture webCamTexture)
   {
       var barcodeReader = new BarcodeReader
       {
           AutoRotate = true,
           Options = new DecodingOptions
           {
               PossibleFormats = new List<BarcodeFormat> {BarcodeFormat.QR_CODE},
           }
       };

       string decodedText = null;

       while (webCamTexture.isPlaying)
       {
           var result = barcodeReader.Decode(webCamTexture.GetPixels32(), webCamTexture.width,
               webCamTexture.height);
           if (result != null)
           {
               decodedText = result.Text;
               break;
           }
           await UniTask.WhenAny(UniTask.DelayFrame(5), UniTask.WaitWhile(() => webCamTexture.isPlaying));
       }
       return decodedText;
   }
}

おわりに

本記事では『あんさんぶるスターズ!!Music』の「MV衣装お試しキャンペーン」を例に、SNSを活用したキャンペーンとその実装について紹介させていただきました。

実装については外部で提供されているプラグインを利用させていただき、比較的簡単に機能を実現することができました。
利用させていただいたプラグインについては、アプリ内の権利表記にライセンスを掲載しています。

昨今のソーシャルゲームの展開においてはSNSを活用して盛り上がりを作ることも、コンテンツを楽しんでいただくうえで重要な要素になってきています。
本記事が同じくアプリ内でSNSを活用したいと考えておられる方の一助となれば幸いです。

--

Happy Elements カカリアスタジオでは
いっしょに「熱狂的に愛されるコンテンツ」をつくっていただけるメンバーを大募集中です!

もし弊社にご興味持っていただけましたら、是非一度
下記採用サイトをご覧ください!