見出し画像

メルクストーリアのギルド機能を大改修しました

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

こんにちは、メルクストーリア(以下、メルスト)チームのエンジニアのY.Kです。
2021年にメルストでは、ギルド機能の大改修を行いました。
リリースから7年経った今、ギルド機能を改修するにあたり気を付けた点やこだわった点などを紹介できればと思います。

改修内容について

まずは、改修前後の画面を見ていただければと思います。

旧画面

画像1

新画面

2画面に分割しました。
【ギルドマップ画面】

画像2

【ギルドバトル画面】

画像3

画面を新旧で見比べると、がらりと変わったことが伝わると思います。

旧画面は、ギルド情報やギルド設定を行う機能(画面右側)とギルドバトルを行う機能(画面左側)が1つの画面で実装されていました。
メルストのメインコンテンツの1つであるギルドバトルがかなり小さな範囲だけで行われており、操作性や視認性が低いというお声もいただいていたので、この2つの機能を別画面に分割して利便性を高めるため改修を行いました。

新画面では、ギルドの繋がりを感じてもらえるように、ギルド機能を拡張してギルドマップを作成しました。
ギルドマップには、ギルドマップ及びギルドバトルに参加しているメンバーのユニットが表示されるようになっています。ギルドマップに表示されているメンバー同士はログ(チャット機能)かスタンプでコミュニケーションを取ることができます。
ギルドイベントの前後では、ギルドマップを使って交流を楽しんでもらっているユーザー様の様子を見ることもでき大変うれしく思っています。

旧画面の左側半分で実施していたギルドバトルは、ギルド機能から切り出して1画面全てを使うように変更しました。かなり大掛かりな改修でしたが、操作性や表現を向上させることができたのではないかと思っています。
ギルドバトルのモチベーションを上げることに繋がっていると嬉しいです。

また、この改修の中で、従来のサーバーとは別にリアルタイム通信用のサーバーを構築しギルドメンバーとラグの少ないスムーズなコミュニケーションが取れるように対応を行いました。
アドベントカレンダーの記事の中で、リアルタイムサーバーに関する記事もありますので、詳しくはそちらを御覧ください。


● 『メルクストーリア – 癒術士と鐘の音色 –』における、リアルタイム通信を支えるKubernetes基盤(前編)

● 『メルクストーリア – 癒術士と鐘の音色 –』における、リアルタイム通信を支えるKubernetes基盤(後編)

● メルクストーリアのリアルタイム通信開発におけるコード共有について

● メルクストーリアのレイド用リアルタイムサーバー開発について

ギルドマップ

それぞれの画面で実装時に気を付けた点を紹介します。
まずは、ギルドマップから紹介します。

● 小物の配置
 背景は一枚絵なのですが、その上に建物を置き、次に花や木などといった
 マップ上の小物をそれぞれ1つ1つ座標を指定してオブジェクトを配置し
 ました。
 これはルール決めの問題なのですが、画面の下に行くほど手前・上に行く
 ほど奥になるようにオブジェクトの前後関係を指定してあります。

これによりマップ上を歩くユニットが自然にオブジェクトを回り込むようになりました。

■ 中央のギルドメニューの後ろ 中央のギルドメニューの後ろ

■ 手前のオブジェクトの間

● 背景の表現
 マップを左右に移動した際に、背景の空(雲)や山が遠くのものをゆっく
 り、近くのものは速く動くように対応しました。
 電車とかに乗っている時の外の景色の見え方のあれですね。
 ※ 山に注目して見てください。少しだけ地面とずれて動くようになってい
 ます。

● ユニットの移動処理
 ユニットの移動処理は NavMesh を使って実装しようと思ったのですが、
 メルストの実装との相性が良くなかったため NavMesh の利用は断念し
 移動処理は自前で実装しました。
 マップ上に細かい仮想的なマスを敷き、そのマスを利用して経路探索を
 行うことで歩行可能エリアの中で最短のルートを移動するように実装し
 ました。

● ズーム処理
 メルストで初めてピンチ操作を導入しました。
 拡大して見たい物に向けて自然に拡大・縮小されるように、2つの指で
 挟んだ中心点に目掛けて拡大・縮小されるようにこだわって対応しまし
 た。
 (波紋のようなエフェクトがタップした場所で、その2点の間に向けて
 拡大しているのが分かると思います。)

ソースコード 

csharp
async UniTask PinchZoom()
{
 float beganZoomRate = 0.0f;
 float beganDist = 0.0f;
 var tapCenterPos = Vector3.zero;
 var localPosition = Vector3.zero;
 var beganMapPosition = Vector3.zero;
 
 // 2点タッチ中はズーム処理継続
 while (Input.touchCount >= 2) {
   // タッチしている2点を取得
   var touch1 = Input.GetTouch(0);
   var touch2 = Input.GetTouch(1);

   //2点タッチ開始時の距離を記憶
   if (touch2.phase == TouchPhase.Began) {
     beganZoomRate = state.zoomRate;
     beganDist = Vector2.Distance(touch1.position, touch2.position);
     tapCenterPos = Vector3.Lerp(touch1.position, touch2.position, 0.5f);
     var worldPosition = AppCamera.instance.camera.ScreenToWorldPoint(tapCenterPos);
     localPosition = -draggablePanel.transform.InverseTransformPoint(worldPosition);
     beganMapPosition = draggablePanel.transform.localPosition;
   }
   
   if (touch1.phase == TouchPhase.Moved && touch2.phase == TouchPhase.Moved) {
     // タッチ位置の移動後、長さを再測し、開始時の距離からの相対値を取る。
     float newDist = Vector2.Distance(touch1.position, touch2.position);
     float variableRate = Mathf.Min(Mathf.Max((newDist - beganDist) * state.constantData.pinchZoomRate, -0.5f), 0.5f);

     // 開始時の指の幅と比較して拡大率を変更する
     state.SetZoomRate(beganZoomRate + variableRate);
     UpdateMapView();
     
     var centerPosition = beganMapPosition;
     float diffZoomRate = state.zoomRate - beganZoomRate;
     if (diffZoomRate < 0.0f) {
       // 拡大
       centerPosition = Vector3.Lerp(beganMapPosition, Vector3.zero, Math.Abs(diffZoomRate) * 2);
     } else if (diffZoomRate > 0.0f) {
       // 縮小
       centerPosition = Vector3.Lerp(beganMapPosition, localPosition, Math.Abs(diffZoomRate) * 2);
     }
     
     // 中心点を計算してなるべく自然に拡縮するように
     if (tapCenterPos != Vector3.zero) {
       draggablePanel.transform.localPosition = CalculateMapPositionForPinch(centerPosition);
       UpdatePanelClipRange();
     }
   }
   await UniTask.Yield();
 }
}

※ ピンチ処理の動作確認をするためには、端末に書き出す必要があるので、細かい調整に結構時間が掛かりました。

他にもちょっとした遊びも仕組んであったりするので、ぜひ探して見てください。

ギルドバトル

次はギルドバトル画面です。
まず絶対条件として、既存のギルドバトルの操作性を悪化させることがないようにと言うことを念頭に置いて開発を行いました。操作性に変更を加える場合は、運営チーム内で慎重に協議を重ねた上で実装しました。
当初画面半分で実装されていた機能を1画面全てを使って実装することになったので、ゆとりを持ったUIの配置ができるようになるだろうと思っていたのですが、そんなことはなくUIを配置する余裕が全然ありませんでした。
※ タブレット端末の場合、左右が狭くなるのでかなり圧迫感が増します。

画像4

● UI配置
 異なる画面サイズのスマートフォンやタブレットに対応できるUI配置に
 するためにはどうすれば良いか、デザイナーと連携を取りながら細かく
 調整を行いました。
 特効薬などはなく、誤操作が起こりにくくなるように泥臭く調整を重ねて
 対応しました。

● ログ表示

画像5

 右サイドにログ表示エリアを設け、ログページを開かずにギルドメンバー 
 とコミュニケーションを取ることができるようにしました。
 リアルタイムサーバーを導入したことによってコメントのやり取りをリア
 ルタイムで行うことができるようになったこともあり、ギルドバトルで連
 携を取りやすくなったと思います。

● ユニットの攻撃アニメーション表現
 背景との融和性を意識して他ギルドへのユニットの攻撃アニメーションを
 調整しました。
 攻撃するユニットがギルド間の橋から落ちないようにギルドの配置や移動
 する導線を調整したのですが、メルストには2000体ほどのユニットがいる
 ため、多くのユニットが自然に見えるように調整するのはかなり大変でし
 た。
 テンションが上がる格好いい背景をイラストレーターに描いてもらったの
 で、違和感がないように気を付けて調整しました。

● 長押し機能の実装
 技術的には大したことではないのですが、ボタン長押しによる連続祈りと
 連続応援の機能を実装しました。
 ギルドバトルで祈りや応援のボタンを連打する機会が多いためユーザー
 負荷軽減を狙って実装しました。

まとめ

ユーザーの皆様に支えられてメルストは2月でリリースから8周年を迎えることができます。
長く続いているタイトルですが、守りに入るのではなく、新しい表現にも挑戦してユーザーの皆様に喜びや驚きを届けられるように頑張っています。
ギルド機能の改修についても、慣れ親しんだ画面や機能を改修するのは、一定の不安がありましたが、残した方が良い箇所と改善した方が良い箇所を見極めた上で、新画面ならではの付加価値を追加し良い改修ができたのではないかなと感じています。

スマートフォンの端末性能も上がってきており、リリース時より表現の幅が広がっていることもあり、メルストでは古臭さを撤廃しようという取り組みを継続して行っております。
今後も新しい体験を届けられるように頑張っていきます!

--

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

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