使用したアセットやライブラリ

ハッピーおしゃれタイムの Steam 版を作るにあたり使用したアセットやライブラリをご紹介します。

Unity の UNet の API を参考にしたネットワークライブラリです。本当は自前のネットワークフレームワークを書こうと思っていたのですが、時間が無かったので Mirror を選択しました。シーン遷移周りにちょっとバグがあるような雰囲気がありますが、そこ以外は動いています!

ただ、Mirror らしく設計すると、全体の設計が MonoBehaviour 依存になってしまうので、個人的には使わない方向に改修したい気持ちです。

Steam での通信は、最初は FizzySteamworks を使っていましたが、Unity のスレッドで動くためにブロックしてしまったことと、Unreliable なチャンネルがうまく動いてくれなかったので、自前で Transport を書きました。そのうち公開するかもしれません。

そういえば、スタンドアロンのときに Telepathy を使う場合が多いと思うのですが、Telepathy だと TCP Socket の待ち受けで Windows の通信確認が出てしまうので、自前で以下のような空っぽの Transport を書くのをおすすめします。

OfflineTransport.cs
using Mirror; using System;
using System.Collections.Generic;
namespace Idol { public class OfflineTransport : Transport {
public override bool Available() => true;
public override void ClientConnect(string address) { }
public override bool ClientConnected() => true;
public override void ClientDisconnect() { }
public override bool ClientSend(int channelId, ArraySegment<byte> segment)
{
return true;
}
public override int GetMaxPacketSize(int channelId = 0) => int.MaxValue;
public override bool ServerActive() => true;
public override bool ServerDisconnect(int connectionId)
{
return true;
}
public override string ServerGetClientAddress(int connectionId) => "localhost";
public override bool ServerSend(List<int> connectionIds, int channelId, ArraySegment<byte> segment) => true;
public override void ServerStart() { }
public override void ServerStop() { }
public override Uri ServerUri()
{
throw new NotImplementedException();
}
public override void Shutdown() { } } }

エディタを作る上で MIDI ファイルの読み込みが必要だったので使わせてもらいました。自前で書くのめんどくさいですからねあれ……。このライブラリでデータをもらって、自前の音楽的な位置(小節・拍・Tick)やテンポや拍子を扱うライブラリで変換をかけてつかっています。

Steamworks の SDK を C# から使えるようにしてくれるラッパーです。便利。使うときは必ず公式ドキュメントのチュートリアルに従いましょう。

あと、私が昔記事を書いてました Steamworks.NET で P2P 通信してみた - chiepomme - Medium すっかり書いたことも忘れていたけれど……。

API の使い方は Steam 公式の Steamworks SDK のリファレンスがしっかり整っているので、それを見れば行けます。詰まるところは、値を受け取るための Callback<T> CallResult<T> くらいでしょうか。

あとは、スクリーンショット周りも使えますが、Unity の ARGB テクスチャを RGB テクスチャを要求する WriteScreenshot で保存するときには以下のような変換が必要です。

ClientPhoto.cs
static void SaveToSteam(Texture texture)
{
var width = texture.width;
var height = texture.height;
var argbBytes = texture.GetRawTextureData();
var rgbBytes = ConvertARGBToRGBWithYFlip(texture.width, texture.height, argbBytes);
var handle = SteamScreenshots.WriteScreenshot(rgbBytes, (uint)rgbBytes.Length, width, height);
}
static unsafe byte[] ConvertARGBToRGBWithYFlip(int width, int height, byte[] sBytes)
{
// s stands for source
// d stands for destination
const int sSize = 4;
const int dSize = 3;
var dBytes = new byte[width * height * dSize];
fixed (byte* sPointer = sBytes, dPointer = dBytes)
{
for (var dx = 0; dx < width; dx++)
{
for (var dy = 0; dy < height; dy++)
{
var sx = dx;
var sy = height - dy - 1;
var si = sx + sy * width;
var di = dx + dy * width;
var sOffset = si * sSize + 1; // 1 is for skipping alpha
var dOffset = di * dSize;
Buffer.MemoryCopy(sPointer + sOffset, dPointer + dOffset, dSize, dSize);
}
}
}
return dBytes;
}

個人的に Rx はそんなに好きではないので、Better Event として使っています。ほとんど使っていないので、普通の event にしてしまってもいいかもしれない……。

これがないともう Unity でコードを書ける気がしません!はぴおしゃでは v2 を使っています。非同期なところは全部これで書いています!使わない理由がないくらい!

普通の LineRenderer はぺらっぺらですが、これはカプセルなのでペラッペラではありません!はぴおしゃのスライドノートはこれで表示されています。

もともとはぴおしゃのローカライズは自前でツールを作ってやっていましたが、楽がしたかったのでアセットを導入しました。Google Spreadsheet との連携機能もあり便利です。(なお、 Google Spreadsheet と連携するときには Auto Update を切った方が良いです。ビルドしたときだけうまく翻訳されなくて困ったのがこれが原因だったことがありました。)

プログラムから翻訳にアクセスするときには LocalizedString を経由することが多いと思うんですが、テキスト以外にアクセスするのが、え!?という様な方法になっているので、こういう拡張メソッドを作っておくのをおすすめします。

LocalizedStringExtension.cs
using I2.Loc;
namespace Idol { public static class LocalizedStringExtension
{
public static T ToObject<T>(this LocalizedString localizedString) where T : UnityEngine.Object
{
var termData = LocalizationManager.GetTermData(localizedString.mTerm);
var languageIndex = LocalizationManager.GetAllLanguages().IndexOf(LocalizationManager.CurrentLanguage);
return LocalizationManager.GetTranslatedObject<T>(termData.GetTranslation(languageIndex));
}
}
}

あと、ボタンのスプライト入れ替えの翻訳にもそのままでは対応していないので、こういうのを作るのをおすすめします。

ButtonSpriteSwapLocalizer.cs
using I2.Loc;
using UnityEngine; using UnityEngine.UI;
namespace Idol {
public class ButtonSpriteSwapLocalizer : MonoBehaviour
{
[SerializeField]
LocalizedString Highlighted;
[SerializeField]
LocalizedString Pressed;
[SerializeField]
LocalizedString Selected;
[SerializeField]
LocalizedString Disabled;
public void OnEnable()
{
var button = GetComponent<Button>();
var spriteState = button.spriteState;
spriteState.highlightedSprite = Highlighted.ToObject<Sprite>();
spriteState.pressedSprite = Pressed.ToObject<Sprite>();
spriteState.disabledSprite = Disabled.ToObject<Sprite>();
spriteState.selectedSprite = Selected.ToObject<Sprite>();
button.spriteState = spriteState;
}
}
}

インスペクタをとても便利にしてくれる Odin Inspector とデフォルトだとシリアライズできないものもシリアライズしてくれる Odin Serializer のセットです。はぴおしゃだとマスターデータのエディタに使っています。アセットストアに売っていますが収入が 20万ドルを超えると別ライセンスになるのでお気を付けください!

3D モデルも表示したり出来ます
特定のフォルダの中だけを対象にブラウズしたりできます

Serializer まで Odin のにしてしまうと、将来的に Odin を止めたくなったときに止めにくいので、 Odin Inspector and Serializer for Unity FAQ : Can I use Odin for its editors only, without using the serialization? に従って、Serializer は切って使っています。

はぴおしゃのシェーダーはだいたいこれで書かれています。

このアセット使っているのか使っていないのかわからない……。そんなときに便利です。

はぴおしゃの鏡はこれです。Recursive を切れば VR 対応の鏡になってくれます。

Steam VR に依存したくなかったので、XR Interaction Toolkit を使いました。ちょっと癖がある部分はありますが、欲しい機能はちゃんとあるのでこれで十分でした。

もともとフォントはフォントワークスの LETS を使っているのですが、今回から翻訳が楽なように組み込みで使える mojimo-game も使い始めました。そのまま英語にするとちょっと大きな印象になるのだけが困っていますが、フォントは綺麗でとてもいいです!