9.7 フレンド
9.7.1 フレンド情報の取得
LIVE ネットワークにサインインしているゲーマーであれば、他の LIVE ネットワーク上のゲーマーをフレンドとして登録できます。フレンドは、ゲームで遊ぶ知人などを登録しておくもので、Xbox 360 のシステム画面などからフレンドの状態を確認できます。例えば、フレンドが現在オンラインであるかどうかや、フレンドがプレイ中のゲームの情報などをリアルタイムで取得できます。
XNA Framework からは、サインインしているゲーマーからフレンドの情報を取得して Gamer オブジェクトとして扱うことができます。フレンドの情報を得るには、サインインしているアカウントが LIVE ネットワークに接続している必要があります。ローカルアカウントでサインインしているゲーマーにはフレンドの情報はありません。
LIVE に接続されたアカウントかどうかは IsSignedInToLive プロパティから取得します。
public bool IsSignedInToLive { get; }
ゲーマーが LIVE ネットワークに接続している場合は true、そうでなければ false を返します。Windows や Xbox 360 で実行されているゲームの場合、サインインしているゲーマーは LIVE アカウントの可能性もローカルアカウントの可能性もあります。一方、Zune で実行されているゲームは LIVE ネットワークがサポートされないため、このプロパティの結果は常に false となります。
フレンドを取得するには SignedInGamer クラスの GetFriends() メソッドを使います。LIVE アカウントではないゲーマーがこのメソッドを呼び出すと例外が発生するので注意してください。
public FriendCollection GetFriends ()
このメソッドは、フレンドリストを表す Microsoft.Xna.Framework.GamerServices.FriendCollection クラスのオブジェクトを返します。このクラスは GamerCollection クラスを継承するフレンドのゲーマーを管理するコレクションです。
public sealed class FriendCollection : GamerCollection<FriendGamer>, IDisposable
フレンドのゲーマー情報は Microsoft.Xna.Framework.GamerServices.FriendGamer クラスのオブジェクトとして取得できます。
このクラスからは Gamer クラスが提供する機能に加えて、フレンドがオンラインかどうかや、ゲームをプレイしているかどうかなどの情報を得られます。
public sealed class FriendGamer : Gamer
フレンドが現在オンラインかどうかは IsOnline プロパティから、ゲームをプレイ中かどうかは IsPlaying プロパティから、席を外しているかどうかは IsAway プロパティから、取り込み中かどうかは IsBusy プロパティから取得できます。どのプロパティも、結果を bool 型で返します。
public bool IsOnline { get; }
public bool IsPlaying { get; }
public bool IsAway { get; }
public bool IsBusy { get; }
フレンドの状態は、常に変化する可能性があります。ゲーマーサービスの Update() メソッドによる更新で、フレンドの状態が自動的に更新されます。よって、FriendGamer オブジェクトに対して特別な更新処理をかける必要はありません。FriendGamer オブジェクトのプロパティは、常に最新のフレンドの状態を提供します。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.GamerServices; public class Test : Game { public static void Main(string[] args) { using (Game game = new Test()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private SpriteBatch sprite; private SpriteFont font; private string text; public Test() { graphicsDeviceManager = new GraphicsDeviceManager(this); Components.Add(new GamerServicesComponent(this)); } protected override void LoadContent() { sprite = new SpriteBatch(GraphicsDevice); font = Content.Load<SpriteFont>("Content/TestFont"); base.LoadContent(); } protected override void Update(GameTime gameTime) { text = "Online Friends\n"; SignedInGamer gamer = Gamer.SignedInGamers[PlayerIndex.One]; if (gamer != null && gamer.IsSignedInToLive) { foreach (FriendGamer friend in gamer.GetFriends()) { if (friend.IsOnline) text += "-" + friend.Gamertag + " " + (friend.IsPlaying ? "Playing " : "") + (friend.IsAway ? "Away " : "") + (friend.IsBusy ? "Busy " : "") + "\n"; } } base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); sprite.Begin(); sprite.DrawString(font, text, Vector2.Zero, Color.Black); sprite.End(); base.Draw(gameTime); } }
コード1は、サインインしている第 1 プレイヤーの登録されているフレンドのうち、オンラインのフレンドを列挙するプログラムです。FriendGamer オブジェクトからフレンドのゲーマータグや状態を取得し、結果を text フィールドに文字列として保存しています。フレンドが退席や取り込み中など、オンラインの状態を変更すると、FriendGamer オブジェクトにも反映されます。
9.7.2 タイトル定義状態文字列
ゲーマーサービスの面白い仕掛けの 1 つに、タイトル定義状態文字列があります。これは、フレンドが実行しているゲームの内容を表す文字列で、ゲームの進行に合わせて変化する文字列です。例えば「タイトル画面」「デモ画面」「戦闘中」「ステージ(衣装選択)」など、フレンドが遊んでいるゲームによって設定されます。
public string Presence { get; }
ただし、このプロパティが返す文字列をゲーム内で描画する場合は、アルファベット以外の文字が含まれている可能性があるので注意してください。当然、日本語のゲームであれば日本語でタイトル定義状態文字列が得られます。SpriteFont オブジェクトがフォントを持たなければ例外が発生してしまいます。
9.7.3 フレンドのプロフィール
ローカルでサインインしているゲーマーのプロフィールであれば、最初にサインインした時点でローカルにプロフィールがキャッシュされているため GetProfile() メソッドですぐに結果を得ることができます。ところが、フレンドなどネットワーク上のゲーマープロフィールを GetProfile() メソッドで取得しようとすると、メソッドはゲーマープロフィールが完全にダウンロードされるまで制御を返さなくなります。ゲームの更新プロセスの中でダウンロード待ちが発生してしまうと、ゲームそのものが止まってしまいます。これは避けなければなりません。
ゲーマープロフィールを非同期で取得するには BeginGetProfile() メソッドと EndGetProfile() メソッドを使います。
public IAsyncResult BeginGetProfile ( AsyncCallback callback, Object asyncState )
public GamerProfile EndGetProfile (IAsyncResult result)
メソッドの使い方は、メッセージボックスやキーボード入力と同じです。BeginGetProfile() メソッドはプロフィールのダウンロードを待たずに IAsyncResult オブジェクトを返して制御を返します。ゲーマープロフィールは、callback パラメータに渡した AsyncCallback デリゲートが呼び出されたタイミングで EndGetProfile() メソッドを呼び出して取得します。
これらのメソッドを使う場合、非同期でプロフィールを取得中にゲーマーがサインアウトするといった予期しない振舞いに注意する必要があります。EndGetProfile() メソッド呼び出し時に、プロフィールを要求したゲーマーがサインアウトしている場合、例外が発生してしまいます。
using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.GamerServices; public class Test : Game { public static void Main(string[] args) { using (Game game = new Test()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private SpriteBatch sprite; private List<GamerProfile> profiles; public Test() { graphicsDeviceManager = new GraphicsDeviceManager(this); Components.Add(new GamerServicesComponent(this)); profiles = new List<GamerProfile>(); SignedInGamer.SignedIn += delegate(object sender, SignedInEventArgs e) { if (!e.Gamer.IsSignedInToLive) return; FriendCollection friends = e.Gamer.GetFriends(); foreach (FriendGamer friend in friends) friend.BeginGetProfile(CallbackGetProfile, friend); }; SignedInGamer.SignedOut += delegate(object sender, SignedOutEventArgs e) { profiles.Clear(); }; } private void CallbackGetProfile(IAsyncResult ar) { FriendGamer friend = (FriendGamer)ar.AsyncState; profiles.Add(friend.EndGetProfile(ar)); } protected override void LoadContent() { sprite = new SpriteBatch(GraphicsDevice); base.LoadContent(); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); Vector2 position = Vector2.Zero; sprite.Begin(); foreach (GamerProfile profile in profiles) { Texture2D icon = profile.GamerPicture; sprite.Draw(icon, position, Color.White); position.X += icon.Width; if (position.X + icon.Width > GraphicsDevice.Viewport.Width) { position.X = 0; position.Y += icon.Height; } } sprite.End(); base.Draw(gameTime); } }
コード2は、サインインしたゲーマーに登録されているフレンドのアイコンを並べます。フレンドの数が多ければ、全てのプロフィールをダウンロードするまでに時間がかかることを実感できます。もし、この処理を GetProfile() メソッドで行ってしまうと、すべてのプロフィールがダウンロードされるまでゲームが停止してしまいます。