Blog ブログ

ホロアースのサーバーサイドの技術について

ホロアースのサーバーサイドの技術について

【執筆者紹介】 「 西根幸洋さん」 ポジション:メタバース事業本部エンジニア部マネージャー 入社時期:2023 年 10 月 業界歴:Web サービスやオンラインゲームの開発・運営に 11 年従事。主にサーバーサイド C# エンジニアとして活動し、個人として Microsoft MVP for Developer Technologies(2020-2022)を受賞。 はじめに こんにちは、メタバース事業本部エンジニア部マネージャーの西根です。 ホロアースの開発の話といえば、Unity を使ったクライアント側の実装や、アバターやライブ演出などの話が注目されがちですが、今回はサーバーサイドで利用している技術の一部をご紹介させていただこうと思います。 ホロアースの開発の裏側を知りたい、開発に参加してみたいと思っている方の参考になれば幸いです。 開発に参加してみたいと思った方はこちらから是非ご応募ください! 目次 ・使用しているプログラミング言語について ・クラウド環境や各種利用サービスについて ・ Cloudflare についてについて ・ エンジニア募集中です 使用しているプログラミング言語について Go 言語を中心に、PHP、C#、TypeScript、Rust など、複数の言語を採用しています。 ホロアースのサーバーは単一のアプリケーションではなく、分割された複数のアプリケーションによって構成されており、アプリケーションごとに使用する言語が異なっています。 ▼分割されたアプリケーションの種類 ・サンドボックスゲーム ・エントランス・シティなどのコミュニケーションロビー ・アカウントや決済などを扱う基盤システム ・etc ホロアースで最初に開発を開始したサンドボックスゲームのサーバーサイドは PHP を採用しています。 その後に開発を開始したコミュニケーションロビーや基盤システムでは Go 言語を採用しており、現在では Go 言語がサーバーサイドの開発の中心になっています。 その一方では今まで使用していなかったプログラミング言語を適材適所で採用することもあります。 たとえば現在開発中の新しいシステムでは、サーバーサイドでインゲームのロジックをリアルタイムに扱う必要がありましたが、そこではサーバーサイドにも C# を採用することで Unity を扱う C# エンジニアがクライアントとサーバーを一貫して開発可能な方針を選択しました。 その他の言語については TypeScript は Cloudflare 上のリソースと連携して動作する Cloudflare の FaaS(Cloudflare Workers)で利用したり、Rust は現在開発中の通信基盤で採用しています。 クラウド環境や各種利用サービスについて ホロアースのクラウド環境は主に AWS を使用しています。AWS の主な利用サービスは EKS、ECS、EC2、Lambda、Aurora、DynamoDB、ElastiCache などです。 ストレージや CDN には S3 と CloudFront も利用していますが、それらの多くの部分は Cloudflare に移行することでコスト削減を果たしました。 モニタリングツールには Datadog、データ分析基盤には Snowflake を導入しています。 カバーは Web サービスやゲーム開発を専門とする会社ではありませんが、そういった業界の会社様と比べても見劣りがしない環境や開発体制の整備を進めています。 Cloudflare について Cloudflare は CDN サービスとして以前から有名でしたが、最近は CDN 以外にも様々なサービスが提供されています。 ホロアースでは CDN を Cloudflare に移行したことをきっかけとして、Cloudflare を積極的に活用しようとしています。現在は CDN 以外にも Cloudflare Stream、Cloudflare Images、Cloudflare Workers などを利用しています。 この中でも Cloudflare Stream は特にヘビーに利用しています。年末年始や EXPO で行ったホロアース内の同時視聴イベントは Cloudflare Stream によって実現しています。 私は Cloudflare Stream はカバーに入社してから初めて触れたのですが、動画配信が非常に簡単かつ低コストに実現できるサービスで驚きました。 動画配信サービスの開発に興味がある方は一度触ってみていただくことを是非お勧めします。 エンジニア募集中です 今回は今まであまり表にでてきていなかった、ホロアースのサーバーサイドの技術スタックをご紹介させていただきました。 ホロアースではこうした環境でプロジェクトを一緒に進めてくれる仲間をまだまだ募集しています。 以前の記事で CTO の福田がメタバースの技術領域を「世界に関する技術の総合格闘技」と表現していましたが、これは本当にその通りで、ホロアースの開発は一筋縄ではいかないことも多くありますが、そのぶんエンジニアとしてのやりがいや楽しさも非常に大きなプロジェクトです。 サーバーサイドエンジニアだけでなく、Unity エンジニアやテクニカルアーティストも募集していますので、興味がある方は是非求人ページからご応募ください。 一緒にホロアースの開発に挑戦していきましょう!

Date

24.5.17

Category

  • エンジニア
窓を壁の自由な位置に設置できるようにする

窓を壁の自由な位置に設置できるようにする

【執筆者紹介】 「 井上 翔一朗さん」 ポジション:メタバース事業本部エンジニア部クライアントチーム 入社時期:2021年2月頃 業界歴:約3年 はじめに メタバース事業本部エンジニア部クライアントチームの井上 翔一朗です。 ホロアースの開発において、主に施設や建築部分の実装を担当しています。 近年ではゲーム内にハウジング要素を持つゲームが多くなっており、そこには窓も欠かさず登場します。ですが窓は多くのゲームの場合、先に窓用の穴が開いた壁を置いてからそこに嵌めるなど決まった位置にしかつけられません。チーム内からも壁に対して自由な位置に窓を付けたい!という要望があり、工夫して解決したので紹介します。 まずは実装結果の動画からご覧ください。 https://holoearth.com/wp-content/uploads/2024/04/mado_setti.mp4 動画のように一枚の壁の自由な位置に窓を設置できるようになりました! 窓を置いた部分の壁の見た目が消えることで外が見えるようになっています。 また、コライダーも見た目に合わせて穴が開けることが可能です。そうすることで将来的に、設置した窓越しに弓矢を用いた戦闘などを行えるようになります。 どのように実現したのか解説していきます。 解決策の概要 前提 窓の設置をする前に壁を設置する必要があります。窓は壁と重なるようにしか設置できないようになっていて、回転も壁に対して正しい向きに自動で調整されます。この部分の説明は省きます。 窓は以下の情報をPrefabなどに設定して持っておく必要があります。 ・コライダー(どの壁に設置されているかを衝突で検知するため) ・窓の幅 / 高さ   処理全体の流れ ①窓はコライダーを持っており、自分が設置されている壁を取得します。 ②壁に衝突するとその壁に位置とサイズ情報を渡しつつ穴をあけるように依頼します ③依頼を受けた壁は、シェーダーの値変更とコライダー管理スクリプトを実行し穴をあけます また、窓が破壊された場合は壁に穴を元に戻すよう依頼します。 見た目に穴をあけるシェーダーと、コライダーに穴をあけるスクリプトについてはこの後それぞれ解説していきます。 今回の実装の範囲では以下の限界があります。 窓の淵が壁からはみ出すことには対応していません。そのため、窓の4つの角すべてが1枚の壁の上に載っていることをRayCastで確かめています。 一つの壁に複数の窓を開けることには対応していません。二つ目の窓を付けた際は古い窓を自動的に破壊する仕組みになっています。 シェーダーにより見た目に穴をあける 基本方針は、今描画している座標が窓に重なる座標なら描画しない。というものになります。 モデルの側面が何もない空間になりメッシュの裏側が見えてしまいますが、そこは窓側が持っている窓枠の3Dモデルで隠されて見えなくなるので問題ありません。 (左:シェーダーで窓と重なる壁を削除した状態。右:そこに窓枠を置いた状態) 描画点の座標が取得できるようにSurfaceシェーダーで作ります。 窓のスクリプトから窓の中心点の座標と、窓の大きさ(幅・高さ)を受け取っています。 判定方法 以下の二段階に分けて考えます。 ①水平平面について描画座標が窓の中にいるかどうか ②垂直方向(高さ)について描画座標が窓の中にいるかどうか ①水平平面xz平面についての直線の式 z=kx+b を用いて考えます。 真上から見たときの窓に垂直な線の傾きを k とします。そして、窓の手前と奥の縁を通る直線をそれぞれ考えると b が二つ存在します。ここではそれを b1, b2 とします。 z=kx+b1 と z=kx+b2 の二本の直線で囲まれた黄色い領域の中を描画しなければよいことになります。 つまり、今描画しようとしている座標を(x1, y1, z1)とすると k*x1+b1 < z1 < k*x1+b2 であれば水平方向においては窓の中であると言えます。 ※k は窓の y軸回転量 = rotY から求めることができます。 ※タンジェントの0は未定義ですので、tan関数の引数に0を指定しないように分岐処理を設ける必要があります。 k = tan( rotY/180f * π + 0.5π ) ※b1, b2は窓の両端の座標から求めることができます。 b = z / kx ②垂直方向次にY軸について考えます。本ゲームでは窓はY軸でのみ回転するので式はシンプルで済みます。 _windowBottomY < position.y < _windowTopY であれば垂直方向において窓の中であることが分かります。 ※_windowBottomYと_windowTopYは窓の中心点Y座標に窓の幅を±すれば求めることができます。 ①②を踏まえるとコードはこのようになります。 bool isInsideWindow(Vector3 pos) // pos:今描画しようとしている点のワールド座標 {   // xz平面で窓の中   var isInsideXZ = _k * pos.x + _b1 < pos.z && pos.z < _k * pos.x + _b2;   // 高さが窓の中   var isInsideY = _windowBottomY < pos.y && pos.y < _windowTopY;   return isInsideXZ && isInsideY; } ※実際のシェーダーは同チームの回凱によって製作されました。こちらでは分かりやすく改変して掲載しています。 ※処理負荷軽減のためにも、_k, _b1, _b2, _windowTopY,_windowBottomY は窓を設置した時点で計算しておきます。 今後の課題 現状では長方形以外の形には対応していません。 他の形にも対応させたい場合は、その形の領域を計算する式を書く必要があります。例えば円形の窓であれば、xy平面に描画座標を投影した後で、描画点と中心点の距離が半径より小さいことを確かめれば実現できます。 コライダーに穴をあける 次は、見た目ではなく当たり判定に穴をあけていきます。 壁のコライダーは一つのBoxコライダーでできています。元のコライダーを窓部分をよけるように分割した4つのBoxコライダーに置き換えることで、穴が開いていることを表現します。 方法はシンプルで、最初のコライダーを削除した後で新しく四つのBoxコライダーをアタッチしています。そのためには、それぞれのコライダーの中心座標とサイズを計算で求める必要があります。 以下の変数が登場します。 壁の大きさ Vector3 wallSize 壁に対する窓の相対座標 Vector3 windowCenter 窓の一片の長さ Vector2 windowSize 計算式は以下になります ※コライダーはローカル座標系なので回転の考慮は不要です ※壁の原点は底面の中央、窓の原点は縦横の中央にあります ▼上側のコライダー var sizeX = wallSize.x; var sizeY = wallSize.y - (windowCenter.y + windowSize.y); var colSize = new Vector3(sizeX, sizeY, windowSize.z); var posX = 0f; var posY = wallSize.y - sizeY/2f; var colCenter = new Vector3(posX , posY, windowSize.z/2f); ▼下側のコライダー var sizeX = wallSize.x; var sizeY = windowCenter.y - windowSize.y/2f; var colSize = new Vector3(sizeX, sizeY, windowSize.z); var posX = 0f; var posY = sizeY /2f; var colCenter = new Vector3(posX , posY, windowSize.z/2f); ▼左側のコライダー (x軸は壁の中心を0とするため、壁の左端の座標は - wallSize.x /2f となります) var sizeX = - wallSize.x/2f + windowCenter.x - windowSize.x/2f; var sizeY = windowSize.y; var colSize = new Vector3(sizeX, sizeY, windowSize.z); var posX = -windowSize.x/2f + sizeX/2f; var posY = windowCenter.y; var colCenter = new Vector3(posX , posY, windowSize.z/2f); ▼右側のコライダー var sizeX = wallSize.x/2f - (wallCenter.x + wallSize.x/2f); var sizeY = windowSize.y; var colSize = new Vector3(sizeX, sizeY, windowSize.z); var posX = wallSize.x/2f - sizeX/2f; var posY = windowCenter.y; var colCenter = new Vector3(posX , posY, windowSize.z/2f); これらの値を設定することで、窓の部分に被らないようにコライダーを配置します。 今後の課題 今回紹介した方法では四角形以外の穴を表現することは難しいです。 現状では四角形以外の形の窓は登場しませんが、以下のような対処法も考えられます。 積分のようにBoxコライダーの数を増やして並べることで疑似的に円形や三角形なども表現できるかもしれません。欠点としては、精度を上げようとするとBoxコライダーの数がかなり増えてしまいます。 あらかじめ三角や円形の穴が開いたコライダーを用意しておくのもいいかもしれません。四角形に穴をあけた後にその中に用意しておいたコライダーを配置します。特殊な形状のコライダの用意が手間ですが、コライダー数を少なくできる可能性があります 終わりに シェーダーとコライダー管理の組み合わせによって、窓を壁の自由な位置に配置できる体験を実現することができました!今後も魅力的なハウジング機能を作っていきたいと思います! 興味を持って頂けましたら、ぜひホロアースを遊んでみて下さい!!

Date

24.4.26

Category

  • エンジニア