GS2-Friend
ゲーム専用のソーシャルグラフを形成するための機能を提供します。 ソーシャルグラフはプラットフォーマーも提供している機能がありますが、ゲーム内で独自に形成することにも価値があります。
プラットフォーム全体で使用されるフレンド関係は登録を行うのに慎重になりがちです。 なぜなら「私はこの人と今遊んでるゲームは一緒に遊びたいけれど、他のゲームを一緒に遊びたいとは思っていない」というケースが存在するためです。 プラットフォーマーが提供するソーシャルグラフはリアルに近い関係性を反映したもので、ゲーム固有のソーシャルグラフはそのゲーム内の関係性を反映したものとして扱うと、プレイヤーはより気軽にフレンド機能を利用してくれます。
フレンド
フレンドになるためには、双方向の同意関係が必要です。
フレンド関係を構築したいいずれかのプレイヤーが、相手にフレンドリクエストを送信し、フレンドリクエストを受け取ったプレイヤーがそれに同意することでフレンド関係が成立します。
フォロー
フォローは相手の同意なく、関係を構築することができます。
フォローされている人は自分が誰にフォローされているかを一覧として知る術はありません。 この仕様はソーシャルネットワークのフォローの仕様を想像していると違和感があるかもしれません。 しかし、ゲーム内でフォロー機能を実装している多くのゲームの仕様をみていて、その必要性がないと判断しました。
自分をフォローしてくれているプレイヤーが自分のゴーストキャラクターと一緒に冒険に出た場合、 冒険で得た報酬を分け与えてくれるだけで十分で、誰が一緒に冒険してくれたのかにはあまり興味がなかっためです。
一覧で取得することはできませんが、冒険で得た報酬を分け与える処理として GS2-Inbox に報酬付きメッセージを届ける際に メッセージのペイロードに、一緒に冒険に出たプレイヤーのユーザーIDを載せることで、プロフィールを伝えること自体は可能です。
プロフィール
GS2-Friend はプレイヤーのプロフィールを保持する領域を提供します。 プロフィールには任意の値を保持でき、スペースが3箇所存在します。
- 他プレイヤーが自由に参照できる「パブリックプロフィール」
- フォローしているプレイヤーが参照できる「フォロワープロフィール」
- フレンドが参照できる「フレンドプロフィール」
それぞれ、用途に合わせて使い分けることが可能です。
ブラックリスト
ゲームを長期間プレイしていると、不快に感じるプレイヤーがいるかもしれません。 そのようなプレイヤーをリスト化して永続化する機能です。
あくまで永続化する機能が存在するだけで、ここに追加するだけでは何も機能は果たしません。 GS2-Matchmaking のマッチメイキング条件にリストを渡すなど別途ここに記録したリストを必要に応じて使用する必要があります。
実装例
プロフィールの更新
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Profile(
).UpdateProfileAsync(
publicProfile: "public",
followerProfile: "follower",
friendProfile: "friend"
);
var item = await result.ModelAsync();
const auto Domain = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Profile(
);
const auto Future = Domain->UpdateProfile(
"public", // publicProfile
"follower", // followerProfile
"friend" // friendProfile
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
// obtain changed values / result values
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;
const auto Result = Future2->GetTask().Result();
自分のプロフィールを取得
var item = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Profile(
).ModelAsync();
const auto Domain = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Profile(
);
const auto item = Domain.Model();
他人の公開プロフィールを取得
var item = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).User(
userId: "user-0001"
).PublicProfile(
).ModelAsync();
const auto Domain = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->User(
"user-0001" // userId
)->PublicProfile(
);
const auto item = Domain.Model();
フレンドリクエストを送信
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).SendRequestAsync(
targetUserId: "user-0002"
);
const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->SendRequest(
"user-0002"
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
送信したフレンドリクエストの一覧を取得
var items = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).SendRequestsAsync(
).ToListAsync();
const auto It = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->SendRequests(
);
TArray<Gs2::UE5::Friend::Model::FEzFriendRequestPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}
受信したフレンドリクエストの一覧を取得
var items = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).ReceiveRequestsAsync(
).ToListAsync();
const auto It = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->SendRequests(
);
TArray<Gs2::UE5::Friend::Model::FEzFriendRequestPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}
フレンドリクエストを許可
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).ReceiveFriendRequest(
fromUserId: "user-0002"
).AcceptAsync(
);
const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->ReceiveFriendRequest(
nullptr, // targetUserId
"user-0002" // fromUserId
)->Accept(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
フレンドリクエストを拒否
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).ReceiveFriendRequest(
fromUserId: "user-0002"
).RejectAsync(
);
const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->ReceiveFriendRequest(
nullptr, // targetUserId
"user-0002" // fromUserId
)->Reject(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
フレンドの一覧を取得
var items = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).FriendsAsync(
).ToListAsync();
const auto It = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Friends( // withProfile
);
TArray<Gs2::UE5::Friend::Model::FEzFriendUserPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}
フレンドの削除
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Friend(
withProfile: null
).FriendUser(
targetUserId: "user-0002"
).DeleteFriendAsync(
);
const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Friend(
nullptr // withProfile
)->FriendUser(
"user-0002" // targetUserId
)->DeleteFriend(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
フォロー
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).FollowUser(
targetUserId: "user-0002",
withProfile: true
).FollowAsync(
);
const auto Future = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->FollowUser(
"user-0002", // targetUserId
true // withProfile
)->Follow(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
フォロワーの一覧を取得
var items = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).FollowsAsync(
).ToListAsync();
const auto It = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Follows(
);
TArray<Gs2::UE5::Friend::Model::FEzFollowUserPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}
アンフォロー
var result = await gs2.Friend.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).FollowUser(
targetUserId: "user-0002",
withProfile: true
).UnfollowAsync(
);
const auto Domain = Gs2->Friend->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->FollowUser(
"user-0002", // targetUserId
false // withProfile
);
const auto Future = Domain->Unfollow(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}