UDN
Search public documentation:
KismetOnlineSubsystemJP
English Translation
中国翻译
한국어
Interested in the Unreal Engine?
Visit the Unreal Technology site.
Looking for jobs and company info?
Check out the Epic games site.
Questions about support via UDN?
Contact the UDN Staff
中国翻译
한국어
Interested in the Unreal Engine?
Visit the Unreal Technology site.
Looking for jobs and company info?
Check out the Epic games site.
Questions about support via UDN?
Contact the UDN Staff
UE3 ホーム > Unreal Development Kit Gems > Kismet オンライン サブシステム
Kismet オンライン サブシステム
UDK での前回テスト 2012年1月
PC 対応
iOS 対応
概要
Kismet は、ノードを使用してゲームプレイ ロジックを構築することができるように設計されているビジュアル スクリプティング システムです。プロトタイプの段階から生産段階まで一貫してとても役に立つツールです。ゲームプレイ ロジックのうちいくつかが Kismet 内に存在するため、Kismet から Game Center (ゲームセンター) や Steam Works にアクセスできるようにしたくなるのは当然なことです。この開発キット文書では、新たな Kismet ノードを追加して、Game Center および Steam Works と接続できるようにします。

UnrealScript のプリプロセシングについての説明
UnrealScript のプリプロセッサの機能は、C++ などの他の言語におけるプリプロセッサの機能と非常によく似ています。プリプロセッサは、if 文および else 文に基づいて UnrealScript に含める / から除外する部分および定数を定義する非常に簡単なスクリプトです。 次のコードのスニペットでは、コードが 2 つのセクションに分かれています。それらセクションは、定義されている定数に基づいてプリプロセッサに含まれることになります。 USE_STEAMWORKS が定義されている場合は、if 文内にあるすべてがコンパイルに含まれます。反対に、 USE_STEAMWORKS が定義されていない場合は、else 文内にあるすべてがコンパイルに含まれます。Game Center と Steam Works の機能が異なるため、コードが動くためには異なる経路が必要となりました。
/** * Called when this sequence action should do something attached with a local user num */ protected function InternalOnActivated() { local OnlineSubsystem OnlineSubsystem; // Connect to the online subsystem and link up the achievement delegates OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem != None && OnlineSubsystem.PlayerInterface != None) { // ========== // SteamWorks // ========== `if(`isdefined(USE_STEAMWORKS)) // Begin unlocking the achievement BeginUnlockAchievement(); // ========== // GameCenter // ========== `else // Game Center requires you to read the achievements list first, and then unlock the achievement. // Assign the read achievements delegate OnlineSubsystem.PlayerInterface.AddReadAchievementsCompleteDelegate(ProcessingPlayers[0].LocalUserNum, InternalOnReadAchievementsComplete); // Read all achievements OnlineSubsystem.PlayerInterface.ReadAchievements(ProcessingPlayers[0].LocalUserNum); `endif } }
ガーベジコレクションについての説明
オンライン サブシステムが実行する多数のタスクは非同期で実行されます。したがって、多数のタスクは、タスク終了時に呼び出されるデリゲートを使用します。Kismet ノード自体は、デリゲートにバインドされています。デリゲートが、適切にクリーンアップされなければ、レベルがガーベジコレクトされずにメモリから除去されなくなります。 GameKismetSceneClient というインタラクションが使用されることによって、ゲームのセッション終了が検知され、Kismet ノードすべてが自身をクリーンアップするように求められます。
/** * A GameKismetSceneClient is an interaction which watches when a game session ends. This is required to clean up all of the Kismet nodes that may have * bindings to the OnlineSubsystems * * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved. */ class GameKismetSceneClient extends Interaction; /** * Called when the current game session has ended. Cleans up all OnlineSubsystemBase Kismet nodes */ function NotifyGameSessionEnded() { local Sequence GameSequence; local array<SequenceObject> SequenceObjects; local SeqAct_OnlineSubsystemBase SeqAct_OnlineSubsystemBase; local int i; local WorldInfo WorldInfo; // Get the world info WorldInfo = class'WorldInfo'.static.GetWorldInfo(); if (WorldInfo == None) { return; } // Clean up all of the online subsystem base Kismet nodes GameSequence = WorldInfo.GetGameSequence(); if (GameSequence != None) { GameSequence.FindSeqObjectsByClass(class'SeqAct_OnlineSubsystemBase', true, SequenceObjects); if (SequenceObjects.Length > 0) { for (i = 0; i < SequenceObjects.Length; ++i) { SeqAct_OnlineSubsystemBase = SeqAct_OnlineSubsystemBase(SequenceObjects[i]); if (SeqAct_OnlineSubsystemBase != None) { SeqAct_OnlineSubsystemBase.CleanUp(true); } } } } } defaultproperties { }
Steam Works および Game Center Kismet ノード
SeqAct_OnlineSubsystemBase
SeqAct_OnlineSubsystemBase は、他すべての Kismet Sequence Action のベースとなる抽象クラスです。この基本クラスは、オンラインサブシステムの命令のキュー、および、プレイヤー情報の整理、クリーンアップを扱います。また、出力リンクのコールのほとんども処理します。オンライン サブシステムのいずれかを利用する Kismet Sequence Action を新たに作成する必要がある場合は、このクラスをサブクラス化するのが最善です。Kismet Sequence Action がアクティベートされると、アタッチされている Kismet Variable Players ノードに基づいて、作用する必要があるプレイヤーの配列をコンパイルします。さらに、順次、プレイヤーの配列を処理します。この Kismet Sequence Action は、ただちに Out に出力します。Kismet Sequence Action がプレイヤーの処理にビジーである場合は、ただちに Busy (ビジー) に出力します。サブクラス化された Kismet Sequence Action の結果に応じて、その後、Succeeded (成功) あるいは Failed (失敗) に出力する可能性があります。関数
- Activated - Kismet Sequence Action が別の Kismet Sequence ノードによってアクティベートされる場合に、このイベントが呼び出されます。
- FinishedProcessPlayerIndex - プレイヤーのためにすべきことを処理し終えた時に、この関数がサブクラスから呼び出されます。 bWasSuccessful が true の場合は、Succeeded 出力がアクティベートされます。true でない場合は、Failed 出力がアクティベートされます。サブクラス化された Kismet ノードによってすべてのプレイヤーが一気に処理された場合に、bProcessedAllPlayers をセットします。
- InternalOnActivated - この関数は、Activated 内で呼び出されるスタブ関数です。この関数はサブクラスが拡張するためのものです。サブクラスは、Activated イベントを拡張してはならないためです。
- CleanUp - この関数は、GameKismetSceneClient からと、FinishedProcessPlayerIndex 内から呼び出されます。この関数は、デリゲートにバインドしているサブクラス、または、他の種類のクリーンアップを必要とするサブクラスによって拡張されるべきです。IsExiting が true の場合は、あらゆるものをクリーンアップします。
- NotifyGameKismetSceneClientCreated - この関数は、他の SeqAct_OnlineSubsystemBase に呼び出されて、GameKismetSceneClient がすでに作成されていることを示します。SeqAct_OnlineSubsystemBase が最初にアクティベートされると、ローカルのプレイヤーのビューポートに挿入すべき GameKismetSceneClient をまず作成しようとします。それに成功すると、他のすべての SeqAct_OnlineSubsystemBase に対して、GameKismetSceneClient をもう作成しないように命じます。
定義
- SProcessingPlayer - この構造体は、処理されているプレイヤー、または、処理がペンディングされているプレイヤーを定義します。
- IsProcessing - これが true の場合は、Kismet ノードがタスク処理にビジーであるため、この Kismet ノードへのリクエストがすべて直ちに拒否されます。
- ProcessingPlayers - 現在処理されているプレイヤーの配列です。プレイヤーは順次処理されます。各プレイヤーの処理が完了すると、この配列から最初のエントリーを削除し、さらに、この配列が空になるまで次のエントリーを処理します。
- HasCreatedSceneClient - これが false の場合は、この Kismet Sequence Action がアクティベートされると、GameKismetSceneClient を作成しようとするとともに、それをローカルのプレイヤーのビューポートに挿入しようとします。これは、ゲームのセッションが終了した時を捉えてクリーンアップが実行されるようにするためのものです。これが true の場合は、この Kismet Sequence Action は上記の実行を試みなくなります。
実行フロー
この Kismet Sequence Action が他の Kismet Sequence ノードによってアクティベートされると、 Activated() がまず呼び出されます。 Activated() は、 HasCreatedSceneClient の値を調べることによって、 GameKismetSceneClient を作成する必要があるか否かをチェックします。その必要がある場合は、ローカルのプレイヤーのコントローラをグラブして、新たな GameKismetSceneClient をビューポートに挿入します。これに成功すると、他すべての SeqAct_OnlineSubsystemBase に対して、 GameKismetSceneClient の挿入が完了したため、これ以上その必要がないことを通知します。 IsProcessing が true の場合は、Busy の出力がアクティベートされて、関数が停止します。そうでない場合は、アタッチされている Kismet Player Variable (プレイヤー変数) に基づいて、プレイヤーのリストをコンパイルします。Kismet Player Variable にリストされている各プレイヤーについて、 ProcessingPlayers (処理プレイヤー) 配列を、プレイヤーインデックスと一意なプレイヤー ID で満たします。 ProcessingPlayers 配列がサイズをもっている場合は、 IsProcessing が true にセットされ、 InternalOnActivated() が呼び出されます。さらに、Out の出力がアクティベートされます。 サブクラスがプレイヤーの処理を終えると、 FinishedProcessPlayerIndex() が呼び出されます。 bProcessedAllPlayers が true の場合は、 ProcessingPlayers 配列が空になります。そうでない場合は、 ProcessingPlayers 配列の最初のエントリーのみが削除されます。 ProcessingPlayers 配列が空でない場合は、 InternalOnActivated() を再び呼び出すことによって、最初のエントリーが処理されます。そうでない場合で、 bWasSuccessful が true の場合は、"Succeeded" の出力がアクティベートされます。それ以外は、Failed の出力がアクティベートされます。 CleanUp() が呼び出され、 IsExiting が true の場合は、 ProcessingPlayers 配列が空になります。 NotifyGameKismetSceneClientCreated() が呼び出されると、 HasCreatedSceneClient が true にセットされます。UnrealScript
/** * An OnlineSubsystemBase action is an abstract Action which is used to call other online subsystem functions which require local user nums * * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved. */ class SeqAct_OnlineSubsystemBase extends SequenceAction abstract HideDropDown; struct SProcessingPlayer { var byte LocalUserNum; var UniqueNetId LocalNetId; }; var private bool IsProcessing; var protected array<SProcessingPlayer> ProcessingPlayers; var protected bool HasCreatedSceneClient; /** * Called when this event is activated. */ event Activated() { local SeqVar_Player SeqVar_Player; local bool AllPlayers; local array<int> PlayerIndices, ExistingPlayerIndices; local WorldInfo WorldInfo; local PlayerController PlayerController; local LocalPlayer LocalPlayer; local int i; local SProcessingPlayer ProcessingPlayer; local OnlineSubSystem OnlineSubSystem; local Sequence GameSequence; local array<SequenceObject> SequenceObjects; local SeqAct_OnlineSubsystemBase SeqAct_OnlineSubsystemBase; // Check if we need to insert the GameKismetSceneClient to watch for garbage collection events if (!HasCreatedSceneClient) { // Insert the game kismet scene client WorldInfo = GetWorldInfo(); if (WorldInfo != None) { // Get the local player controller PlayerController = WorldInfo.GetALocalPlayerController(); if (PlayerController != None) { LocalPlayer = LocalPlayer(PlayerController.Player); if (LocalPlayer != None && LocalPlayer.ViewportClient != None) { LocalPlayer.ViewportClient.InsertInteraction(new (LocalPlayer.ViewportClient) class'GameKismetSceneClient'); } } } // Flag all SeqAct_OnlineSubsystemBase that the game kismet scene client has been created GameSequence = WorldInfo.GetGameSequence(); if (GameSequence != None) { GameSequence.FindSeqObjectsByClass(class'SeqAct_OnlineSubsystemBase', true, SequenceObjects); if (SequenceObjects.Length > 0) { for (i = 0; i < SequenceObjects.Length; ++i) { SeqAct_OnlineSubsystemBase = SeqAct_OnlineSubsystemBase(SequenceObjects[i]); if (SeqAct_OnlineSubsystemBase != None) { SeqAct_OnlineSubsystemBase.NotifyGameKismetSceneClientCreated(); } } } } } if (IsProcessing) { // Activate the Busy output ActivateOutputLink(1); return; } // Get the online subsystem OnlineSubSystem = class'GameEngine'.static.GetOnlineSubSystem(); if (OnlineSubSystem == None || OnlineSubSystem.PlayerInterface == None) { return; } // Get the player indices ForEach LinkedVariables(class'SeqVar_Player', SeqVar_Player) { if (SeqVar_Player.bAllPlayers) { AllPlayers = true; break; } else { PlayerIndices.AddItem(SeqVar_Player.PlayerIdx); } } // Find the players that need to be processed WorldInfo = GetWorldInfo(); if (WorldInfo != None) { // If all players is true, then iterate for each player if (AllPlayers) { ForEach WorldInfo.AllControllers(class'PlayerController', PlayerController) { LocalPlayer = LocalPlayer(PlayerController.Player); if (LocalPlayer != None) { // Create the processing player struct ProcessingPlayer.LocalUserNum = class'UIInteraction'.static.GetPlayerIndex(LocalPlayer.ControllerId); OnlineSubSystem.PlayerInterface.GetUniquePlayerId(ProcessingPlayer.LocalUserNum, ProcessingPlayer.LocalNetId); // Append the processing player struct to the processing players array ProcessingPlayers.AddItem(ProcessingPlayer); } } } // Otherwise iterate for each player index else if (PlayerIndices.Length > 0) { // Get all of the existing player indices first ForEach WorldInfo.AllControllers(class'PlayerController', PlayerController) { LocalPlayer = LocalPlayer(PlayerController.Player); if (LocalPlayer != None) { ExistingPlayerIndices.AddItem(class'UIInteraction'.static.GetPlayerIndex(LocalPlayer.ControllerId)); } } for (i = 0; i < PlayerIndices.Length; ++i) { if (ExistingPlayerIndices.Find(PlayerIndices[i]) != INDEX_NONE) { // Create the processing player struct ProcessingPlayer.LocalUserNum = PlayerIndices[i]; OnlineSubSystem.PlayerInterface.GetUniquePlayerId(ProcessingPlayer.LocalUserNum, ProcessingPlayer.LocalNetId); // Append the processing player struct to the processing players array ProcessingPlayers.AddItem(ProcessingPlayer); } } } // Process the first one if (ProcessingPlayers.Length > 0) { // Set processing to true IsProcessing = true; // Activate InternalOnActivated(); } } // Activate the Out output ActivateOutputLink(0); } /** * Called when the Kismet node has finished processing for this player * * @param bWasSuccessful True if the Kismet node was successful in what it was doing * @param bProcessedAllPlayers True if this Kismet node processed all players in one go, rather then doing one player at a time */ protected function FinishedProcessPlayerIndex(bool bWasSuccessful, optional bool bProcessedAllPlayers) { // Perform clean up of this Kismet node CleanUp(); // Pop the first processing player index if (ProcessingPlayers.Length > 0) { // If processed all players, then remove all of them now if (bProcessedAllPlayers) { ProcessingPlayers.Remove(0, ProcessingPlayers.Length); } // Otherwise we've only processed one, so pop it off the top else { ProcessingPlayers.Remove(0, 1); } } // If there is still more player indices to process, process the next one if (ProcessingPlayers.Length > 0) { InternalOnActivated(); } // Otherwise, this Kismet node has finished processing and should activate one of the outputs else { IsProcessing = false; ForceActivateOutput((bWasSuccessful) ? 2 : 3); } } /** * Called when this sequence action should do something attached with a local user num */ protected function InternalOnActivated(); /** * Called when this Kismet node should clean up all variable or delegate references so that garbage collection can occur * * @param IsExiting If exiting, then clean up everything */ function CleanUp(optional bool IsExiting) { // Clear processing players array if (IsExiting) { ProcessingPlayers.Remove(0, ProcessingPlayers.Length); } } /** * Called when a Kismet node has created the GameKismetSceneClient which is used to catch when the game session has finished and when game clean up should occur */ function NotifyGameKismetSceneClientCreated() { HasCreatedSceneClient = true; } defaultproperties { `if(`isdefined(USE_STEAMWORKS)) ObjCategory="Steamworks" `elseif(`isdefined(USE_GAMECENTER)) ObjCategory="Game Center" `endif ObjColor=(R=0,G=63,B=127,A=255) bAutoActivateOutputLinks=false VariableLinks.Empty VariableLinks(0)=(ExpectedType=class'SeqVar_Player',LinkDesc="Player") OutputLinks(1)=(LinkDesc="Busy") OutputLinks(2)=(LinkDesc="Succeeded") OutputLinks(3)=(LinkDesc="Failed") }
SeqAct_UnlockAchievement
この Kismet Sequence Action は、アタッチされている ID またはプロパティパネルで定義されている ID に基づいて、Achievement (成績) のロックを解除します。

関数
- InternalOnActivated - この関数は、親クラスの SeqAct_OnlineSubsystemBase によって呼び出されます。Game Center では、ロックを解除する前にまず、Achievements を読み取ることが必要となります。そのため、 OnlineSubsystem.PlayerInterface.ReadAchievements() が呼び出されます。Steamworks では、単に BeginUnlockAchievement() が呼び出されます。
- InternalOnReadAchievementsComplete - この関数は、デリゲートを介して OnlineSubsystem.PlayerInterface から呼び出されます。呼び出されると、オンラインで Achievement が達成されているか否かをチェックします。達成されていて、 AlwaysUnlockAchievement が true の場合は、 InternalOnUnlockAchievementComplete が呼び出されます。そうでない場合は、 BeginUnlockAchievement() が呼び出されます。
- BeginUnlockAchievement - この関数は、 OnlineSubsystem.PlayerInterface.UnlockAchievement() にコールを送ります。
- InternalOnUnlockAchievementComplete - この関数は、デリゲートを介して、 OnlineSubsystem.PlayerInterface から呼び出されます。この関数は、呼び出されると、 FinishedProcessPlayerIndex() を呼び出します。
- CleanUp - この関数は、バインドされているデリゲートすべてのクリーンアップを処理します。
定義
- AchievementId - ロック解除したい Achievement の ID です。これは、Kismet Int Variable を変数リンクにアタッチさせることによっても定義することができます。
- AlwaysUnlockAchievement - true の場合は、Achievement がすでにロック解除されていても、Kismet Sequence Action が Succeeded を出力します。 Game Center のみ
- DownloadedAchievements - オンラインのソースからダウンロードされた Achievement の配列です。 Game Center のみ
Game Center の実行フロー
InternalOnActivated() が呼び出されると、デリゲートが OnlineSubsystem.PlayerInterface に追加されます。*OnlineSubsystem.PlayerInterface* は、 OnlineSubsystem.PlayerInterface.ReadAchievements が完了した時に呼び出されます。 オンラインサブシステムが、オンラインの Achievement の読み取りを終えると、 InternalOnReadAchievementsComplete() を呼び出します。 InternalOnReadAchievementsComplete() は、Achievement がすでにロック解除されているか否かを確認します。ロック解除されている場合は、 BeginUnlockAchievement() が呼び出されます。そうでないならば、 AlwaysUnlockAchievement が true の場合は、 InternalOnUnlockAchievementComplete() が呼び出されます。 *BeginUnlockAchievement() は、Achievement のロック解除が完了した時に呼び出される他のオンラインサブシステム デリゲートにバインドされます。さらに、 OnlineSubsystem.PlayerInterface.UnlockAchievement() が呼び出されます。 Achievement がロック解除を終えると、 InternalOnUnlockAchievementComplete() が呼び出されます。これは、 FinishedProcessPlayerIndex() を呼び出します。 CleanUp が呼び出されると、バインドされているデリゲートすべてが削除されます。Steamworks の実行フロー
InternalOnActivated() が呼び出されると、 BeginUnlockAchievement() が呼び出されます。 BeginUnlockAchievement() は、Achievement のロック解除が完了した時に呼び出される他のオンラインサブシステム デリゲートにバインドされます。さらに、 OnlineSubsystem.PlayerInterface.UnlockAchievement() が呼び出されます。 Achievement がロック解除を終えると、 InternalOnUnlockAchievementComplete() が呼び出されます。これは、 FinishedProcessPlayerIndex() を呼び出します。 CleanUp が呼び出されると、バインドされているデリゲートすべてが削除されます。UnrealScript
/** * An UnlockAchievement Action is used to unlock an achievement. * * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved. */ class SeqAct_UnlockAchievement extends SeqAct_OnlineSubsystemBase; // Achievement Id that you want to unlock var() int AchievementId; // ========== // GameCenter // ========== `if(`isdefined(USE_GAMECENTER)) // If true, then achivements that have already been unlocked, will still output the success link var() bool AlwaysUnlockAchievement; // Array of all downloads achievements var protected array<AchievementDetails> DownloadedAchievements; `endif /** * Called when this sequence action should do something attached with a local user num */ protected function InternalOnActivated() { local OnlineSubsystem OnlineSubsystem; // Connect to the online subsystem and link up the achievement delegates OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem != None && OnlineSubsystem.PlayerInterface != None) { // ========== // SteamWorks // ========== `if(`isdefined(USE_STEAMWORKS)) // Begin unlocking the achievement BeginUnlockAchievement(); // ========== // GameCenter // ========== `else // Game Center requires you to read the achievements list first, and then unlock the achievement. // Assign the read achievements delegate OnlineSubsystem.PlayerInterface.AddReadAchievementsCompleteDelegate(ProcessingPlayers[0].LocalUserNum, InternalOnReadAchievementsComplete); // Read all achievements OnlineSubsystem.PlayerInterface.ReadAchievements(ProcessingPlayers[0].LocalUserNum); `endif } } // ========== // GameCenter // ========== `if(`isdefined(USE_GAMECENTER)) /** * Called when the async achievements read has completed. * * @param TitleId The title id that the read was for (0 means current title) */ protected function InternalOnReadAchievementsComplete(int TitleId) { local OnlineSubsystem OnlineSubsystem; local int AchievementIndex; // Ensure we have an online subsystem, and an associated player interface OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem == None || OnlineSubsystem.PlayerInterface == None) { return; } // Read the achievements into the downloaded achievements array OnlineSubsystem.PlayerInterface.GetAchievements(ProcessingPlayers[0].LocalUserNum, DownloadedAchievements, TitleId); // Grab the achievement index AchievementIndex = DownloadedAchievements.Find('Id', AchievementId); // Unlock the achievement if (AchievementIndex != INDEX_NONE) { // We haven't unlocked it yet, so start the unlock process if (!DownloadedAchievements[AchievementIndex].bWasAchievedOnline) { BeginUnlockAchievement(); } // We've already unlocked it, so finish else if (AlwaysUnlockAchievement) { InternalOnUnlockAchievementComplete(true); } } } `endif /** * Called when unlocking the achievement should begin * * @param AchievementId Which achievement to unlock * @param LocalUserNum Local user index */ protected function BeginUnlockAchievement() { local OnlineSubsystem OnlineSubsystem; // Grab the online subsystem OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem == None || OnlineSubsystem.PlayerInterface == None) { return; } // Assign the unlock achievement complete delegate OnlineSubsystem.PlayerInterface.AddUnlockAchievementCompleteDelegate(ProcessingPlayers[0].LocalUserNum, InternalOnUnlockAchievementComplete); // Start the unlocking process OnlineSubsystem.PlayerInterface.UnlockAchievement(ProcessingPlayers[0].LocalUserNum, AchievementId); } /** * Called when the achievement unlocking has completed * * @param bWasSuccessful True if the async action completed without error, false if there was an error */ protected function InternalOnUnlockAchievementComplete(bool bWasSuccessful) { // Finished unlocking this achievement FinishedProcessPlayerIndex(bWasSuccessful); } /** * Called when this Kismet node should clean up all variable or delegate references so that garbage collection can occur * * @param IsExiting If exiting, then clean up everything */ function CleanUp(optional bool IsExiting) { local OnlineSubsystem OnlineSubsystem; local int i, InPlayerIndex; // Grab the online subsystem and remove all delegate binds OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem != None && OnlineSubsystem.PlayerInterface != None) { // There can only be 4 maximum split screen local players for (i = 0; i < 4; ++i) { // Get the player index InPlayerIndex = class'UIInteraction'.static.GetPlayerIndex(i); if (InPlayerIndex >= 0) { // Clear the delegates // ========== // GameCenter // ========== `if(`isdefined(USE_GAMECENTER)) OnlineSubsystem.PlayerInterface.ClearReadAchievementsCompleteDelegate(InPlayerIndex, InternalOnReadAchievementsComplete); `endif OnlineSubsystem.PlayerInterface.ClearUnlockAchievementCompleteDelegate(InPlayerIndex, InternalOnUnlockAchievementComplete); } } } // Call the super just in case we are exiting Super.CleanUp(IsExiting); } defaultproperties { // ========== // GameCenter // ========== `if(`isdefined(USE_GAMECENTER)) AlwaysUnlockAchievement=true `endif ObjName="Unlock Achievement" VariableLinks(1)=(ExpectedType=class'SeqVar_Int',LinkDesc="Achievement Id",PropertyName=AchievementId) }
Kismet の見本

SeqAct_ModifyOnlineStat
Modify Online Stat は、オンライン サブシステムによって使用される統計をオンラインで修正することを可能にする Kismet Sequence Action です。Game Center では、このシステムによって、Leaderboard (成績表) で使用する新たな統計をアップロードすることができます。Steamworks では、このシステムによって、Achievement (成績) のために使用する統計を、足したり、引いたり、セットしたりすることができます。

関数
- InternalOnActivated - 親クラスの SeqAct_OnlineSubsystemBase から呼び出されます。Game Center では、常に、 SetStatValue() を呼び出すにすぎません。Steamworks では、 ModifyMethod の値によって、 ReadOnlineStats() (既存の統計を足したり引いたりする場合) か、 SetStatValue() のどちらかを呼び出すことができます。
- ReadOnlineStats - 既存の統計を足したり引いたりする場合に、Steamworks のバージョンでのみ呼び出されます。この関数は、オンライン統計を読み込み、それが終わると、 InternalOnReadOnlineStatsComplete() を呼び出します。
- InternalOnReadOnlineStatsComplete - 統計の読み込みが終わると、その統計に値が加わるか、引かれます。さらに、その結果が Steamworks に送信されます。
- SetStatValue - この関数は、統計をセットし、Game Center または Steamworks のいずれかに送信します。
- InternalOnFlushOnlineStatsComplete - この関数は、統計がオンライン データベースに書き込まれたときに、Steamworks バージョンのみで呼び出されます。
- CleanUp - この関数は、Kismet ノードが自身をクリーンアップした場合に呼び出されます。
定義
- SStat - ベースとなる構造体の定義です。StatId は、Game Center または Steamworks 上で stat (統計) id を参照します。 LinkedVariableName によって、この stat id を、 アタッチされている Kismet Variable (変数) にリンクさせることができます。
- SIntStat - SStat の拡張です。統計を加える / 引く / セットする 値を定義します。
- SFloatStat - SStat の拡張です。統計を加える / 引く / セットする 値を定義します。
- EModifyMethod - オンライン統計を修正する方法を設定します。
- OnlineStatsWriteClass - オンライン サブシステムに書きだす時にどの OnlineStatsWrite をインスタンス化するかを定義します。
- OnlineStatsReadClass - オンライン サブシステムに書きだす時にどの OnlineStatsRead をインスタンス化するかを定義します。
- IntStats - オンライン サブシステムにアップロードするための統計の定義の配列です。
- FloatStats - オンライン サブシステムにアップロードするための統計の定義の配列です。
- ModifyMethod - オンライン統計を修正する方法を定義します。Game Center ではセットだけが可能です。Steamworks では、足す / 引く / セットが可能です。
- SessionName - セッション名を定義します。デフォルトでは Game となっています。
Game Center の実行フロー
Game Center では、 InternalOnActivated() が呼び出されると、ただちに SetStatValue() が呼び出されます。 SetStatValue() が、 OnlineStatsWriteClass で定義されているクラスを使用して、OnlineStatsWrite をインスタンス化します。さらに、 IntStats と FloatStats をイタレートして、それらの統計の値をインスタンス化された OnlineStatsWrite クラスに書き出します。さらに、これらの統計をオンラインで書き出し、 FinishedProcessPlayerIndex() を呼び出します。 CleanUp() が呼び出されると、バインドされているすべてのデリゲートが解放されます。あらゆるオブジェクトの参照も解放されます。Steamworks の実行フロー
Steamworks では、 InternalOnActivated() が ModifyMethod の値に応じて呼び出されると、2 つの関数が呼び出されます。 ModifyMethod が MM_Add または MM_Subtract の場合は、 ReadOnlineStats() が呼び出されます。なぜならば、オンライン統計値を読み取ることによって、足すか引くかして修正できるようにする必要があるためです。 ModifyMethod がそのような値でない場合は、 SetStatValue() が呼び出されます。 ReadOnlineStats() が呼び出されると、デリゲートがバインドされ、OnlineStatsRead のインスタンスが作成されます。さらに*OnlineSubsystem.StatsInterface.ReadOnlineStats()* が呼び出されます。オンライン統計の読み取りが終了すると、 InternalOnReadOnlineStatsComplete が呼び出されます。さらに、 InternalOnReadOnlineStatsComplete が IntStats 配列と FloatStats 配列をイタレートして、統計値を修正します。修正された統計は、OnlineStatsWrite インスタンスに書き出され、さらに、 OnlineSubsystem.StatsInterface.WriteOnlineStats() が呼び出されて、Steamworks に送信されます。 OnlineSubsystem.StatsInterface.WriteOnlineStats() が渡された場合、デリゲートがバインドされ、 OnlineSubsystem.StatsInterface.FlushOnlineStats() が呼び出されます。オンライン統計値がフラッシュされると、 InternalOnFlushOnlineStatsComplete() が呼び出されます。さらに、この関数は、 FinishedProcessPlayerIndex() を呼び出します。 SetStatValue() は、呼び出されると、 IntStats 配列と FloatStats 配列をイタレートして、統計値をセットします。修正された統計は、OnlineStatsWrite インスタンスに書き出され、さらに、 OnlineSubsystem.StatsInterface.WriteOnlineStats() が呼び出されて、Steamworks に送信されます。 OnlineSubsystem.StatsInterface.WriteOnlineStats() が渡された場合、デリゲートがバインドされ、 OnlineSubsystem.StatsInterface.FlushOnlineStats() が呼び出されます。オンライン統計値がフラッシュされると、 InternalOnFlushOnlineStatsComplete() が呼び出されます。さらに、この関数は、 FinishedProcessPlayerIndex() を呼び出します。 CleanUp() が呼び出されると、バインドされているすべてのデリゲートが解放されます。あらゆるオブジェクトの参照も解放されます。Unrealscript
/** * An Modify Online Stat Action is used to modify and upload stats. * * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved. */ class SeqAct_ModifyOnlineStat extends SeqAct_OnlineSubsystemBase; // Stat base struct struct SStat { var() Name LinkedVariableName; var() int StatId; }; // Integer based stat struct SIntStat extends SStat { var() int Value; }; // Float based stat struct SFloatStat extends SStat { var() float Value; }; // What method does the user want to modify the stat enum EModifyMethod { MM_Set<DisplayName=Set>, // ========== // Steamworks // ========== `if(`isdefined(USE_STEAMWORKS)) MM_Add<DisplayName=Add>, MM_Subtract<DisplayName=Subtract>, `endif }; // Online stats write class associated with the stats var() class<OnlineStatsWrite> OnlineStatsWriteClass; // ========== // Steamworks // ========== `if(`isdefined(USE_STEAMWORKS)) // Online stats read class associated with the stats var() class<OnlineStatsRead> OnlineStatsReadClass; `endif // Array of integer based stats var() array<SIntStat> IntStats; // Array of float based stats var() array<SFloatStat> FloatStats; // Method to modify the stat var() EModifyMethod ModifyMethod; // Session name var() Name SessionName; // Online stats write object instance var protected OnlineStatsWrite OnlineStatsWrite; // Online stats read object instance var protected OnlineStatsRead OnlineStatsRead; /** * Called when this sequence action should do something attached with a local user num */ protected function InternalOnActivated() { // Abort if the online stats write class is none // Abort if there are no column ids if (OnlineStatsWriteClass == None || IntStats.Length <= 0) { return; } // ========== // Steamworks // ========== `if(`isdefined(USE_STEAMWORKS)) // User wants to modify the stats using add or subtract if (ModifyMethod == MM_Add || ModifyMethod == MM_Subtract) { ReadOnlineStats(); } // User wants to modify the stats by setting the value else { `endif SetStatValue(); // ========== // Steamworks // ========== `if(`isdefined(USE_STEAMWORKS)) } `endif } // ========== // Steamworks // ========== `if(`isdefined(USE_STEAMWORKS)) /** * Called when the user wants to modify the stats using increment or decrement */ protected function ReadOnlineStats() { local OnlineSubsystem OnlineSubsystem; local array<UniqueNetId> PlayerIds; // Attempt to read the online stats OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None) { OnlineSubsystem.StatsInterface.AddReadOnlineStatsCompleteDelegate(InternalOnReadOnlineStatsComplete); PlayerIds.AddItem(ProcessingPlayers[0].LocalNetId); OnlineStatsRead = new () OnlineStatsReadClass; if (OnlineStatsRead != None) { OnlineSubsystem.StatsInterface.ReadOnlineStats(PlayerIds, OnlineStatsRead); } } } /** * Called when reading the online stats has finished * * @param bWasSuccessful True if reading the online stats succeeded */ function InternalOnReadOnlineStatsComplete(bool bWasSuccessful) { local OnlineSubsystem OnlineSubsystem; local int i, ReadIntStatValue; local float ReadFloatStatValue; local SeqVar_Int SeqVar_Int; local SeqVar_Float SeqVar_Float; // If reading the stats is successful if (bWasSuccessful && OnlineStatsRead != None) { // Write stats OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None) { // Instance the stats write object OnlineStatsWrite = new () OnlineStatsWriteClass; if (OnlineStatsWrite != None) { // Modify the int stats write values for (i = 0; i < IntStats.Length; ++i) { OnlineStatsRead.GetIntStatValueForPlayer(ProcessingPlayers[0].LocalNetId, IntStats[i].StatId, ReadIntStatValue); // If this state has a variable name, then perform a look up if (IntStats[i].LinkedVariableName != '' && IntStats[i].LinkedVariableName != 'None') { ForEach LinkedVariables(class'SeqVar_Int', SeqVar_Int) { if (SeqVar_Int.VarName == IntStats[i].LinkedVariableName) { switch (ModifyMethod) { case MM_Add: OnlineStatsWrite.SetIntStat(IntStats[i].StatId, ReadIntStatValue + SeqVar_Int.IntValue); break; case MM_Subtract: OnlineStatsWrite.SetIntStat(IntStats[i].StatId, ReadIntStatValue - SeqVar_Int.IntValue); break; default: break; } } } } // Otherwise use the value defined in the struct else { switch (ModifyMethod) { case MM_Add: OnlineStatsWrite.SetIntStat(IntStats[i].StatId, ReadIntStatValue + IntStats[i].Value); break; case MM_Subtract: OnlineStatsWrite.SetIntStat(IntStats[i].StatId, ReadIntStatValue - IntStats[i].Value); break; default: break; } } } // Modify the float stats write values for (i = 0; i < FloatStats.Length; ++i) { OnlineStatsRead.GetFloatStatValueForPlayer(ProcessingPlayers[0].LocalNetId, FloatStats[i].StatId, ReadFloatStatValue); // If this state has a variable name, then perform a look up if (FloatStats[i].LinkedVariableName != '' && FloatStats[i].LinkedVariableName != 'None') { ForEach LinkedVariables(class'SeqVar_Float', SeqVar_Float) { if (SeqVar_Float.VarName == FloatStats[i].LinkedVariableName) { switch (ModifyMethod) { case MM_Add: OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, ReadFloatStatValue + SeqVar_Float.FloatValue); break; case MM_Subtract: OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, ReadFloatStatValue - SeqVar_Float.FloatValue); break; default: break; } } } } // Otherwise use the value defined in the struct else { switch (ModifyMethod) { case MM_Add: OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, ReadFloatStatValue + FloatStats[i].Value); break; case MM_Subtract: OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, ReadFloatStatValue - FloatStats[i].Value); break; default: break; } } } // Send the write request to the stat handler if (OnlineSubsystem.StatsInterface.WriteOnlineStats(SessionName, ProcessingPlayers[0].LocalNetId, OnlineStatsWrite)) { // Add the flush delegate OnlineSubsystem.StatsInterface.AddFlushOnlineStatsCompleteDelegate(InternalOnFlushOnlineStatsComplete); // Flush the online stats OnlineSubsystem.StatsInterface.FlushOnlineStats(SessionName); } // Activate the failed output link else { ForceActivateOutput(3); } } } } else { FinishedProcessPlayerIndex(bWasSuccessful); } } `endif /** * Called when this Kismet node should just set the stats value */ protected function SetStatValue() { local OnlineSubsystem OnlineSubsystem; local int i; local SeqVar_Int SeqVar_Int; local SeqVar_Float SeqVar_Float; // Write stats OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None) { // Instance the stats write object OnlineStatsWrite = new () OnlineStatsWriteClass; if (OnlineStatsWrite != None) { // Set the int stats write values for (i = 0; i < IntStats.Length; ++i) { // If this state has a variable name, then perform a look up if (IntStats[i].LinkedVariableName != '' && IntStats[i].LinkedVariableName != 'None') { ForEach LinkedVariables(class'SeqVar_Int', SeqVar_Int) { if (SeqVar_Int.VarName == IntStats[i].LinkedVariableName) { OnlineStatsWrite.SetIntStat(IntStats[i].StatId, SeqVar_Int.IntValue); break; } } } // Otherwise use the value defined in the struct else { OnlineStatsWrite.SetIntStat(IntStats[i].StatId, IntStats[i].Value); break; } } // Modify the float stats write values for (i = 0; i < FloatStats.Length; ++i) { // If this state has a variable name, then perform a look up if (FloatStats[i].LinkedVariableName != '' && FloatStats[i].LinkedVariableName != 'None') { ForEach LinkedVariables(class'SeqVar_Float', SeqVar_Float) { if (SeqVar_Float.VarName == FloatStats[i].LinkedVariableName) { OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, SeqVar_Float.FloatValue); } } } // Otherwise use the value defined in the struct else { OnlineStatsWrite.SetFloatStat(FloatStats[i].StatId, FloatStats[i].Value); } } // Send the write request to the stat handler if (OnlineSubsystem.StatsInterface.WriteOnlineStats(SessionName, ProcessingPlayers[0].LocalNetId, OnlineStatsWrite)) { // ========== // SteamWorks // ========== `if(`isdefined(USE_STEAMWORKS)) // Add the flush delegates OnlineSubsystem.StatsInterface.AddFlushOnlineStatsCompleteDelegate(InternalOnFlushOnlineStatsComplete); `endif // Flush the online stats OnlineSubsystem.StatsInterface.FlushOnlineStats(SessionName); // ========== // GameCenter // ========== `if(`isdefined(USE_GAMECENTER)) // Clear all object refs OnlineStatsWrite = None; OnlineStatsRead = None; // Handle process player index FinishedProcessPlayerIndex(true); `endif } // Activate the failed output link else { ForceActivateOutput(3); } } } } `if(`isdefined(USE_STEAMWORKS)) /** * Called when the stats flush operation has completed * * @param SessionName the name of the session having stats flushed for * @param bWasSuccessful true if the async action completed without error, false if there was an error */ function InternalOnFlushOnlineStatsComplete(Name InSessionName, bool bWasSuccessful) { // Clear all object refs OnlineStatsWrite = None; OnlineStatsRead = None; // Handle process player index FinishedProcessPlayerIndex(bWasSuccessful); } `endif /** * Called when this Kismet node should clean up all variable or delegate references so that garbage collection can occur * * @param IsExiting If exiting, then clean up everything */ function CleanUp(optional bool IsExiting) { // ========== // SteamWorks // ========== `if(`isdefined(USE_STEAMWORKS)) local OnlineSubsystem OnlineSubsystem; local int i, InPlayerIndex; // Grab the online subsystem and remove all delegate binds OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None) { // There can only be 4 maximum split screen local players for (i = 0; i < 4; ++i) { // Get the player index InPlayerIndex = class'UIInteraction'.static.GetPlayerIndex(i); if (InPlayerIndex >= 0) { // Clear the delegates OnlineSubsystem.StatsInterface.ClearFlushOnlineStatsCompleteDelegate(InternalOnFlushOnlineStatsComplete); OnlineSubsystem.StatsInterface.ClearReadOnlineStatsCompleteDelegate(InternalOnReadOnlineStatsComplete); } } } `endif // Clear the online stats write object OnlineStatsWrite = None; // Clear the online stats read object OnlineStatsRead = None; // Call the super just in case we are exiting Super.CleanUp(IsExiting); } defaultproperties { SessionName="Game" ObjName="Modify Online Stat" VariableLinks(1)=(ExpectedType=class'SeqVar_Int',LinkDesc="Stat Values") }
Kismet の見本

Steam Works 専用 Kismet ノード
SeqAct_RefreshAchievements
この Kismet Sequence Action は、Steam Works から読み取ることによって Achievement をリフレッシュするように、クライアントに強制しようとします。
関数
- InternalOnActivated - この関数が呼び出されると、デリゲートにバインドするとともに、 OnlineSubsystem.PlayerInterface.ReadAchievements() を呼び出します。
- InternalOnReadAchievementsComplete - OnlineSubsystem.PlayerInterface.ReadAchievements() が Steam Works から Achievement の読み取りを終了した時に、この関数が呼び出されます。この関数は FinishedProcessPlayerIndex() を呼び出します。
- CleanUp - この関数は、バインドされていたあらゆるデリゲートをクリーンアップします。
実行フロー
InternalOnActivated() は、呼び出されると、まず、 OnlineSubsystem.PlayerInterface 内のデリゲートにバインドします。このデリケートは、オンラインサブシステムが Steam Works から Achievement の読み取りを終了した時に、呼び出されます。さらに、 OnlineSubsystem.PlayerInterface.ReadAchievements() が呼び出されます。Achievement の読み取りが終了すると、 InternalOnReadAchievementsComplete() が呼び出されます。この関数は、さらに、 FinishedProcessPlayerIndex() を呼び出します。 CleanUp() が呼び出されると、バインドされていたあらゆるデリゲートが開放されます。UnrealScript
/** * A Refresh Achievements Action is used to refresh all achievements. * * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved. */ class SeqAct_RefreshAchievements extends SeqAct_OnlineSubsystemBase `if(`notdefined(USE_STEAMWORKS)) abstract HideDropDown; `else ; /** * Called when this sequence action should do something attached with a local user num */ protected function InternalOnActivated() { local OnlineSubSystem OnlineSubSystem; OnlineSubSystem = class'GameEngine'.static.GetOnlineSubSystem(); if (OnlineSubSystem != None && OnlineSubSystem.PlayerInterface != None) { // Bind the delegate OnlineSubSystem.PlayerInterface.AddReadAchievementsCompleteDelegate(ProcessingPlayers[0].LocalUserNum, InternalOnReadAchievementsComplete); // Read all achievements OnlineSubsystem.PlayerInterface.ReadAchievements(ProcessingPlayers[0].LocalUserNum); } } /** * Called when the async achievements read has completed * * @param TitleId the title id that the read was for (0 means current title) */ function InternalOnReadAchievementsComplete(int TitleId) { FinishedProcessPlayerIndex(true); } /** * Called when this Kismet node should clean up all variable or delegate references so that garbage collection can occur * * @param IsExiting If exiting, then clean up everything */ function CleanUp(optional bool IsExiting) { local OnlineSubsystem OnlineSubsystem; local int i, InPlayerIndex; // Grab the online subsystem and remove all delegate binds OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem != None && OnlineSubsystem.PlayerInterface != None) { // There can only be 4 maximum split screen local players for (i = 0; i < 4; ++i) { // Get the player index InPlayerIndex = class'UIInteraction'.static.GetPlayerIndex(i); if (InPlayerIndex >= 0) { // Clear the delegates OnlineSubsystem.PlayerInterface.ClearReadAchievementsCompleteDelegate(InPlayerIndex, InternalOnReadAchievementsComplete); } } } // Call the super just in case we are exiting Super.CleanUp(IsExiting); } `endif defaultproperties { ObjName="Refresh Achievements" }
SeqAct_ResetAchievements
この Kismet Sequence Action は、プレイヤーのために保存されている統計または Achievement (またはその両方) をリセットするために使用されます。この Kismet Sequence Action は、Steam Works オンライン サブシステムに直接バインドされているため、デバッグ用にのみ使用されるべきです。
関数
- InternalOnActivated - この関数は、呼び出されると、 OnlineSubsystemSteamworks.ResetStats() を呼び出します。
定義
- ResetAchievements - これが true の場合は、Achievement もリセットされます。
実行フロー
InternalOnActivated() が呼び出されると、 OnlineSubsystemSteamworks.ResetStats() を呼び出します。さらに、 OnlineSubsystemSteamworks.ResetStats() は FinishedProcessPlayerIndex() を呼び出します。UnrealScript
/** * A Reset Achievements Action is used to reset all of the achievements earnt by the player. * * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved. */ class SeqAct_ResetAchievements extends SeqAct_OnlineSubsystemBase `if(`notdefined(USE_STEAMWORKS)) abstract HideDropDown; `else ; // If true, then achievements are also reset. Otherwise only stats are reset. var() const bool ResetAchievements; /** * Called when this sequence action should do something attached with a local user num */ protected function InternalOnActivated() { local OnlineSubsystemSteamworks OnlineSubsystemSteamworks; local bool bWasSuccessful; OnlineSubsystemSteamworks = OnlineSubsystemSteamworks(class'GameEngine'.static.GetOnlineSubsystem()); if (OnlineSubsystemSteamworks != None) { bWasSuccessful = OnlineSubsystemSteamworks.ResetStats(ResetAchievements); } else { bWasSuccessful = false; } FinishedProcessPlayerIndex(bWasSuccessful); } `endif defaultproperties { `if(`isdefined(USE_STEAMWORKS)) ResetAchievements=true `endif ObjName="Reset Achievements/Stats" }
Kismet の見本

SeqAct_ReadOnlineStat
この Kismet Sequence Action によって、後に Kismet 内で使用することになるオンライン統計を読み取ることができます。

関数
- InternalOnActivated - この関数は、親クラスの SeqAct_OnlineSubsystemBase によって呼び出されます。この関数は、さらに、Steam Works から統計を読み取るためのコールを開始します。
- InternalOnReadOnlineStatsComplete - この関数は、Steam Woks から統計を読み取り終わると呼び出されます。この関数は最終的に FinishedProcessPlayerIndex() を呼び出します。
- CleanUp - この関数は、このKismet Sequence Action によって使用されるあらゆるデリゲートとオブジェクト参照をクリーンアップします。
定義
- EReadStatsMethod - 統計を読み取るための各種方法を定義する列挙型変数です。
- SLinkedVariableName - 読み取りたい統計のための ID、および、アタッチされている Kismet Variable (変数) へリンクする名前を保持する構造体です。
- OnlineStatsReadClass - オンライン統計を読み取るために使用する Online Read Stats クラスです。
- ReadStatsMethod - EReadStatsMethod の変数宣言です。
- StartRankIndex - read stats rank メソッド を使用する場合に、どの開始ランクインデックスを使用するかを保持します。
- RowCount - read stats rank メソッド または rank around player メソッドを使用する場合に、読み取る行数を保持します。
- LinkedVariableNames - 出力先のリンクされている変数です。
- Rank - Kismet Variable (変数) にマッピングされているランク化された変数です。
実行フロー
InternalOnActivated() は、呼び出されると、まず、 *OnlineStatsReadClass で定義されているクラスに基づいて OnlineStatsRead のインスタンス化を行います。さらに、 OnlineSubsystem.StatsInterface.FreeStats() が呼び出され、 OnlineStatsRead インスタンスがクリアされます。さらに、デリゲートがバインドされ、オンライン統計を読み取るためのリクエストが実行されます。オンライン統計を読み取るためのメソッドは、 ReadStatsMethod 変数によって定義されています。これによって、 OnlineSubsystem.StatsInterface.ReadOnlineStatsForFriends() 、 OnlineSubsystem.StatsInterface.ReadOnlineStatsByRank() 、 OnlineSubsystem.StatsInterface.ReadOnlineStatsByRankAroundPlayer() 、 OnlineSubsystem.StatsInterface.ReadOnlineStats() が呼び出される可能性があります。 InternalOnReadOnlineStatsComplete() は、オンライン統計が完了した時に呼び出されます。この関数の役割は、統計を取得し、それらをアタッチされている Kismet Sequence Variables に出力することです。ランクもここにセットされます。 PopulateLinkedVariableValues() が呼び出されて、マッピングされた統計プロパティをコピーします。さらに、 FinishedProcessPlayerIndex() が呼び出されます。 CleanUp() が呼び出されると、バインドされていたすべてのデリゲートとオブジェクト インスタンスが解放されます。UnrealScript
/** * A Read Online Stat Action is used to read online stats. * * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved. */ class SeqAct_ReadOnlineStat extends SeqAct_OnlineSubsystemBase // ========== // GameCenter // ========== `if(`isdefined(USE_GAMECENTER)) abstract HideDropdown; `else ; // enum EReadStatsMethod { RST_ReadAll<DisplayName=Read all stats>, RST_ReadFriendsOnly<DisplayName=Read stats of friends>, RST_ReadByRank<DisplayName=Read stats by rank>, RST_ReadByRankAroundPlayer<DisplayName=Read stats around player> }; // struct SLinkedVariableName { var() int StatId; var() Name LinkedVariableName; }; // Class to use which reads the stats var() class<OnlineStatsRead> OnlineStatsReadClass; // Method to read stats var() EReadStatsMethod ReadStatsMethod; // If use a read stats ranked method, which starting rank index to use var() int StartRankIndex; // How many rows to read if using read stats rank or rank around player methods var() int RowCount; // Linked variables to output to var() array<SLinkedVariableName> LinkedVariableNames; // Online stats read object instance var protected OnlineStatsRead OnlineStatsRead; // Rank variable mapped to a Kismet variable var int Rank; /** * Called when this sequence action should do something attached with a local user num */ protected function InternalOnActivated() { local OnlineSubSystem OnlineSubSystem; local array<UniqueNetId> PlayerIds; // Abort if the online stats read class is none // Abort if there are no column ids if (OnlineStatsReadClass == None || LinkedVariableNames.Length <= 0) { return; } // Request stats OnlineSubSystem = class'GameEngine'.static.GetOnlineSubSystem(); if (OnlineSubSystem != None && OnlineSubSystem.StatsInterface != None) { // Instance the online stats OnlineStatsRead = new () OnlineStatsReadClass; if (OnlineStatsRead != None) { // Free the stats OnlineSubsystem.StatsInterface.FreeStats(OnlineStatsRead); // Bind the read online stats delegate OnlineSubsystem.StatsInterface.AddReadOnlineStatsCompleteDelegate(InternalOnReadOnlineStatsComplete); switch (ReadStatsMethod) { // Read stats for friends only case RST_ReadFriendsOnly: OnlineSubsystem.StatsInterface.ReadOnlineStatsForFriends(ProcessingPlayers[0].LocalUserNum, OnlineStatsRead); break; // Read stats by rank only case RST_ReadByRank: OnlineSubsystem.StatsInterface.ReadOnlineStatsByRank(OnlineStatsRead, StartRankIndex, RowCount); break; // Read stats around player case RST_ReadByRankAroundPlayer: OnlineSubsystem.StatsInterface.ReadOnlineStatsByRankAroundPlayer(ProcessingPlayers[0].LocalUserNum, OnlineStatsRead, RowCount); break; // Read all stats case RST_ReadAll: default: PlayerIds.AddItem(ProcessingPlayers[0].LocalNetId); OnlineSubsystem.StatsInterface.ReadOnlineStats(PlayerIds, OnlineStatsRead); break; } } } } /** * Notifies the interested party that the last stats read has completed * * @param bWasSuccessful true if the async action completed without error, false if there was an error */ function InternalOnReadOnlineStatsComplete(bool bWasSuccessful) { local int i; local SeqVar_Int SeqVar_Int; local SeqVar_Float SeqVar_Float; if (OnlineStatsRead != None) { // Output the stat id for the processing player if (LinkedVariableNames.Length > 0) { for (i = 0; i < LinkedVariableNames.Length; ++i) { if (LinkedVariableNames[i].LinkedVariableName != '' && LinkedVariableNames[i].LinkedVariableName != 'None') { // Copy the int stats over to the sequence variable ForEach LinkedVariables(class'SeqVar_Int', SeqVar_Int) { if (SeqVar_Int.VarName == LinkedVariableNames[i].LinkedVariableName) { OnlineStatsRead.GetIntStatValueForPlayer(ProcessingPlayers[0].LocalNetId, LinkedVariableNames[i].StatId, SeqVar_Int.IntValue); } } // Copy the float stats over to the sequence variable ForEach LinkedVariables(class'SeqVar_Float', SeqVar_Float) { if (SeqVar_Float.VarName == LinkedVariableNames[i].LinkedVariableName) { OnlineStatsRead.GetFloatStatValueForPlayer(ProcessingPlayers[0].LocalNetId, LinkedVariableNames[i].StatId, SeqVar_Float.FloatValue); } } } } } // Output rank Rank = OnlineStatsRead.GetRankForPlayer(ProcessingPlayers[0].LocalNetId); // Populate the linked variables PopulateLinkedVariableValues(); } // Finished processing players FinishedProcessPlayerIndex(bWasSuccessful); // Remove reference to online stats read OnlineStatsRead = None; } /** * Called when this Kismet node should clean up all variable or delegate references so that garbage collection can occur * * @param IsExiting If exiting, then clean up everything */ function CleanUp(optional bool IsExiting) { local OnlineSubsystem OnlineSubsystem; local int i, InPlayerIndex; // Grab the online subsystem and remove all delegate binds OnlineSubsystem = class'GameEngine'.static.GetOnlineSubsystem(); if (OnlineSubsystem != None && OnlineSubsystem.StatsInterface != None) { // There can only be 4 maximum split screen local players for (i = 0; i < 4; ++i) { // Get the player index InPlayerIndex = class'UIInteraction'.static.GetPlayerIndex(i); if (InPlayerIndex >= 0) { // Clear the delegates OnlineSubsystem.StatsInterface.ClearReadOnlineStatsCompleteDelegate(InternalOnReadOnlineStatsComplete); } } } // Clear the online stats read object OnlineStatsRead = None; // Call the super just in case we are exiting Super.CleanUp(IsExiting); } `endif defaultproperties { ObjName="Read Online Stat" // ========== // SteamWorks // ========== `if(`isdefined(USE_STEAMWORKS)) StartRankIndex=1 RowCount=50 `endif VariableLinks(1)=(ExpectedType=class'SeqVar_Int',LinkDesc="Stat Values",bWriteable=true) VariableLinks(2)=(ExpectedType=class'SeqVar_Int',LinkDesc="Rank",bWriteable=true,PropertyName=Rank) }
Kismet の見本

Game Center 専用 Kismet ノード
SeqAct_Mobile_ShowAchievementsUI
この Kismet Sequence Action がアクティベートされると、Game Center の Achievement の UI が表示されます。通常、これによってゲームが自動的にポーズされます。
関数
- InternalOnActivated - この関数は、呼び出されると、 OnlineSubSystem.PlayerInterfaceEx.ShowAchievementsUI() を呼び出します。
実行フロー
InternalOnActivated() が親クラスの SeqAct_OnlineSubsystemBase によって呼び出されると、これはさらに、 OnlineSubSystem.PlayerInterfaceEx.ShowAchievementsUI() を呼び出します。さらに、 OnlineSubsystemSteamworks.ResetStats() は FinishedProcessPlayerIndex() を呼び出します。UnrealScript
/** * A Mobile Show Achievements UI Action is used to display the achievements UI. * * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved. */ class SeqAct_Mobile_ShowAchievementsUI extends SeqAct_OnlineSubsystemBase `if(`notdefined(USE_GAMECENTER)) abstract HideDropDown; `else ; /** * Called when this sequence action should do something attached with a local user num */ protected function InternalOnActivated() { local OnlineSubsystem OnlineSubsystem; // Get the online sub system OnlineSubSystem = class'GameEngine'.static.GetOnlineSubsystem(); // Check that the online subsystem and player interface ex is accessible if (OnlineSubSystem != None && OnlineSubSystem.PlayerInterfaceEx != None) { OnlineSubSystem.PlayerInterfaceEx.ShowAchievementsUI(ProcessingPlayers[0].LocalUserNum); FinishedProcessPlayerIndex(true); } else { FinishedProcessPlayerIndex(false); } } `endif defaultproperties { ObjName="Show Achievements UI" }
Kismet の見本

SeqAct_Mobile_ShowLeaderboardsUI
この Kismet Sequence Action がアクティベートされると、Game Center の Leaderboards の UI が表示されます。通常、これによって、ゲームが自動的にポーズされます。
関数
- InternalOnActivated - この関数は、呼び出されると、 OnlineStatsReadClass の値に基づいて OnlineStatsRead のインスタンスを作成します。さらに、 OnlineSuppliedUIInterface.ShowOnlineStatsUI() が呼び出されて、Game Center の Leaderboards の UI が表示されます。UI が閉じられる際に InternalOnShowOnlineStatsUIComplete を呼び出すデリゲートもバインドされます。
- InternalOnShowOnlineStatsUIComplete - この関数は、Leaderboards の UI が閉じられる際に呼び出されます。
- CleanUp - この関数は、すべてのバインドされているデリゲートとオブジェクト参照を解放するために呼び出されます。
定義
- OnlineStatsReadClass - オンライン統計を読み取るために使用する OnlineStatsRead のクラスです。
実行フロー
InternalOnActivated() が、親クラスの SeqAct_OnlineSubsystemBase に呼び出されると、 OnlineStatsReadClass によって定義されている OnlineReadStats をインスタンス化します。デリゲートがバインドされて、Leaderboards のUI が閉じられる時を検知します。さらに、 OnlineSuppliedUIInterface.ShowOnlineStatsUI() が呼び出され、Leaderboards の UI を読み取り表示します。さらに、 FinishedProcessPlayerIndex() が呼び出されます。 InternalOnShowOnlineStatsUIComplete() が呼び出されると、Closed の出力がアクティベートされます。 CleanUp() が呼び出されると、バインドされているすべてのデリゲートが解放されます。UnrealScript
/** * A Mobile Show Leaderboards UI Action is used to display the leaderboards UI. * * Copyright 1998-2012 Epic Games, Inc. All Rights Reserved. */ class SeqAct_Mobile_ShowLeaderboardsUI extends SeqAct_OnlineSubsystemBase `if(`notdefined(USE_GAMECENTER)) abstract HideDropDown; `else ; var() const class<OnlineStatsRead> OnlineStatsReadClass; /** * Called when this sequence action should do something attached with a local user num */ protected function InternalOnActivated() { local OnlineSubsystem OnlineSubsystem; local OnlineSuppliedUIInterface OnlineSuppliedUIInterface; local array<UniqueNetId> PlayerIds; local OnlineStatsRead OnlineStatsRead; local int i; // If this class is none, then abort if (OnlineStatsReadClass == None) { return; } // Get the online sub system OnlineSubSystem = class'GameEngine'.static.GetOnlineSubsystem(); // Check that the online subsystem is accessible if (OnlineSubSystem != None) { // Create the PlayerIds array from all the processing players array for (i = 0; i < ProcessingPlayers.Length; ++i) { PlayerIds.AddItem(ProcessingPlayers[i].LocalNetId); } // Get the online supplied UI interface OnlineSuppliedUIInterface = OnlineSuppliedUIInterface(OnlineSubSystem.GetNamedInterface('SuppliedUI')); if (OnlineSuppliedUIInterface != None) { // Instance the online stats read class OnlineStatsRead = new () OnlineStatsReadClass; if (OnlineStatsRead != None) { // Bind to the online stats UI delegate OnlineSuppliedUIInterface.AddShowOnlineStatsUICompleteDelegate(InternalOnShowOnlineStatsUIComplete); // Show the online stats UI OnlineSuppliedUIInterface.ShowOnlineStatsUI(PlayerIds, OnlineStatsRead); FinishedProcessPlayerIndex(true, true); } } } else { FinishedProcessPlayerIndex(false); } } /** * Delegate fired when the supplied stats UI is closed */ function InternalOnShowOnlineStatsUIComplete() { // Activate the fourth output ForceActivateOutput(4); } /** * Called when this Kismet node should clean up all variable or delegate references so that garbage collection can occur * * @param IsExiting If exiting, then clean up everything */ function CleanUp(optional bool IsExiting) { local OnlineSubsystem OnlineSubsystem; local OnlineSuppliedUIInterface OnlineSuppliedUIInterface; Super.CleanUp(IsExiting); if (IsExiting) { // Get the online sub system OnlineSubSystem = class'GameEngine'.static.GetOnlineSubsystem(); // Check that the online subsystem is accessible if (OnlineSubSystem != None) { // Get the online supplied UI interface OnlineSuppliedUIInterface = OnlineSuppliedUIInterface(OnlineSubSystem.GetNamedInterface('SuppliedUI')); if (OnlineSuppliedUIInterface != None) { OnlineSuppliedUIInterface.ClearShowOnlineStatsUICompleteDelegate(InternalOnShowOnlineStatsUIComplete); } } } } `endif defaultproperties { ObjName="Show Leaderboards UI" OutputLinks(4)=(LinkDesc="Closed") }
Kismet の見本
