WisdomSoft - for your serial experiences.

9.7 フレンド

LIVE ネットワークに接続しているゲーマーであれば、登録されているフレンドユーザーを得られます。ゲームは、フレンドのプロフィールや、フレンドがオンラインかどうかなどの状態を取得できます。

9.7.1 フレンド情報の取得

LIVE ネットワークにサインインしているゲーマーであれば、他の LIVE ネットワーク上のゲーマーをフレンドとして登録できます。フレンドは、ゲームで遊ぶ知人などを登録しておくもので、Xbox 360 のシステム画面などからフレンドの状態を確認できます。例えば、フレンドが現在オンラインであるかどうかや、フレンドがプレイ中のゲームの情報などをリアルタイムで取得できます。

図1
図1 フレンド

XNA Framework からは、サインインしているゲーマーからフレンドの情報を取得して Gamer オブジェクトとして扱うことができます。フレンドの情報を得るには、サインインしているアカウントが LIVE ネットワークに接続している必要があります。ローカルアカウントでサインインしているゲーマーにはフレンドの情報はありません。

LIVE に接続されたアカウントかどうかは IsSignedInToLive プロパティから取得します。

SignedInGamer クラス IsSignedInToLive プロパティ
public bool IsSignedInToLive { get; }

ゲーマーが LIVE ネットワークに接続している場合は true、そうでなければ false を返します。Windows や Xbox 360 で実行されているゲームの場合、サインインしているゲーマーは LIVE アカウントの可能性もローカルアカウントの可能性もあります。一方、Zune で実行されているゲームは LIVE ネットワークがサポートされないため、このプロパティの結果は常に false となります。

フレンドを取得するには SignedInGamer クラスの GetFriends() メソッドを使います。LIVE アカウントではないゲーマーがこのメソッドを呼び出すと例外が発生するので注意してください。

SignedInGamer クラス GetFriends() メソッド
public FriendCollection GetFriends ()

このメソッドは、フレンドリストを表す Microsoft.Xna.Framework.GamerServices.FriendCollection クラスのオブジェクトを返します。このクラスは GamerCollection クラスを継承するフレンドのゲーマーを管理するコレクションです。

Microsoft.Xna.Framework.GamerServices.FriendCollection クラス
public sealed class FriendCollection : GamerCollection<FriendGamer>, IDisposable

フレンドのゲーマー情報は Microsoft.Xna.Framework.GamerServices.FriendGamer クラスのオブジェクトとして取得できます。

このクラスからは Gamer クラスが提供する機能に加えて、フレンドがオンラインかどうかや、ゲームをプレイしているかどうかなどの情報を得られます。

Microsoft.Xna.Framework.GamerServices.FriendGamer クラス
public sealed class FriendGamer : Gamer

フレンドが現在オンラインかどうかは IsOnline プロパティから、ゲームをプレイ中かどうかは IsPlaying プロパティから、席を外しているかどうかは IsAway プロパティから、取り込み中かどうかは IsBusy プロパティから取得できます。どのプロパティも、結果を bool 型で返します。

FriendGamer クラス IsOnline プロパティ
public bool IsOnline { get; }
FriendGamer クラス IsPlaying プロパティ
public bool IsPlaying { get; }
FriendGamer クラス IsAway プロパティ
public bool IsAway { get; }
FriendGamer クラス IsBusy プロパティ
public bool IsBusy { get; }

フレンドの状態は、常に変化する可能性があります。ゲーマーサービスの Update() メソッドによる更新で、フレンドの状態が自動的に更新されます。よって、FriendGamer オブジェクトに対して特別な更新処理をかける必要はありません。FriendGamer オブジェクトのプロパティは、常に最新のフレンドの状態を提供します。

コード1 (Win, Xbox 360)
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は、サインインしている第 1 プレイヤーの登録されているフレンドのうち、オンラインのフレンドを列挙するプログラムです。FriendGamer オブジェクトからフレンドのゲーマータグや状態を取得し、結果を text フィールドに文字列として保存しています。フレンドが退席や取り込み中など、オンラインの状態を変更すると、FriendGamer オブジェクトにも反映されます。

9.7.2 タイトル定義状態文字列

ゲーマーサービスの面白い仕掛けの 1 つに、タイトル定義状態文字列があります。これは、フレンドが実行しているゲームの内容を表す文字列で、ゲームの進行に合わせて変化する文字列です。例えば「タイトル画面」「デモ画面」「戦闘中」「ステージ(衣装選択)」など、フレンドが遊んでいるゲームによって設定されます。

FriendGamer クラス Presence プロパティ
public string Presence { get; }

ただし、このプロパティが返す文字列をゲーム内で描画する場合は、アルファベット以外の文字が含まれている可能性があるので注意してください。当然、日本語のゲームであれば日本語でタイトル定義状態文字列が得られます。SpriteFont オブジェクトがフォントを持たなければ例外が発生してしまいます。

9.7.3 フレンドのプロフィール

ローカルでサインインしているゲーマーのプロフィールであれば、最初にサインインした時点でローカルにプロフィールがキャッシュされているため GetProfile() メソッドですぐに結果を得ることができます。ところが、フレンドなどネットワーク上のゲーマープロフィールを GetProfile() メソッドで取得しようとすると、メソッドはゲーマープロフィールが完全にダウンロードされるまで制御を返さなくなります。ゲームの更新プロセスの中でダウンロード待ちが発生してしまうと、ゲームそのものが止まってしまいます。これは避けなければなりません。

ゲーマープロフィールを非同期で取得するには BeginGetProfile() メソッドEndGetProfile() メソッドを使います。

Gamer クラス BeginGetProfile() メソッド
public IAsyncResult BeginGetProfile (
         AsyncCallback callback,
         Object asyncState
)
Gamer クラス EndGetProfile() メソッド
public GamerProfile EndGetProfile (IAsyncResult result)

メソッドの使い方は、メッセージボックスやキーボード入力と同じです。BeginGetProfile() メソッドはプロフィールのダウンロードを待たずに IAsyncResult オブジェクトを返して制御を返します。ゲーマープロフィールは、callback パラメータに渡した AsyncCallback デリゲートが呼び出されたタイミングで EndGetProfile() メソッドを呼び出して取得します。

これらのメソッドを使う場合、非同期でプロフィールを取得中にゲーマーがサインアウトするといった予期しない振舞いに注意する必要があります。EndGetProfile() メソッド呼び出し時に、プロフィールを要求したゲーマーがサインアウトしている場合、例外が発生してしまいます。

コード2 (Win, Xbox 360)
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 実行結果

コード2は、サインインしたゲーマーに登録されているフレンドのアイコンを並べます。フレンドの数が多ければ、全てのプロフィールをダウンロードするまでに時間がかかることを実感できます。もし、この処理を GetProfile() メソッドで行ってしまうと、すべてのプロフィールがダウンロードされるまでゲームが停止してしまいます。