UDN
Search public documentation:
KismetOnlineSubsystemCH
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 Home > 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 的包含/非包含部分。 在这段代码中,预处理将会根据定义的常量包含其中的两段代码。 如果 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 } }
垃圾回收概述
A lot of the tasks that the Online Subsystem performs are done asynchronously. 因此,许多任务会在它们结束后调用代理。 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 Actions(序列操作)的基类。 这个基类会处理排队在线子系统指令,管理玩家信息并清理。 它还会处理大多数输出链接调用。 如果您需要创建一个采用任何在线子系统的新的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 输出被激活,否则会激活 Failed 输出。 如果子类的 Kismet 节点同时处理了所有玩家,那么设置 bProcessedAllPlayers。
- InternalOnActivated - 这个函数是一个存根函数,在 Activated 中进行调用。 这是为了供子类扩展,因为子类不应扩展 Activated 事件。
- CleanUp - 该函数可以通过 GameKismetSceneClient 以及在 FinishedProcessPlayerIndex 内部进行调用。 该函数应该通过绑定到代理或请求一些其他类型的清除处理的子类进行扩展。 如果 IsExiting 为 true,那么您应该清除所有内容。
- NotifyGameKismetSceneClientCreated - 该函数被另一个 SeqAct_OnlineSubsystemBase 调用来表明 GameKismetSceneClient 已经创建完成。 当 SeqAct_OnlineSubsystemBase 第一个被激活的时候,它会首先尝试创建一个 GameKismetSceneClient 来插入到本地玩家的视图中。 当创建成功完成后,它会通知所有其他 SeqAct_OnlineSubsystemBase 不要尝试再次创建它。
定义
- 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 这项操作完成,它们不需要再进行这项操作。 如果 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。虚幻脚本
/** * 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 解锁成就。

函数
- InternalOnActivated - 该函数由父类 SeqAct_OnlineSubsystemBase 进行调用。 Game Center 在解锁任何诸如 OnlineSubsystem.PlayerInterface.ReadAchievements() 前会首先要求您读取成就。 Steamworks 就调用 BeginUnlockAchievement() 。
- InternalOnReadAchievementsComplete - 通过一个代理在 OnlineSubsystem.PlayerInterface 中调用这个函数。 调用它之后,它会检查是否已经在线解开了这个成就。 如果成就已经解开,并且 AlwaysUnlockAchievement 为 true,那么会调用 InternalOnUnlockAchievementComplete 。 否则,会调用 BeginUnlockAchievement() 。
- BeginUnlockAchievement - 这个函数会将调用传递给 OnlineSubsystem.PlayerInterface.UnlockAchievement() 。
- InternalOnReadAchievementsComplete - 通过一个代理在 OnlineSubsystem.PlayerInterface 中调用这个函数。 调用这个函数后,它会调用 FinishedProcessPlayerIndex() 。
- CleanUp - 它会处理所有绑定代理的清理工作。
定义
- AchievementId - 您希望解锁的成就的 Id。 它也可以通过将一个 Kismet Int Variable 附加给这个变量链接进行定义。
- AlwaysUnlockAchievement - 如果该选项为 true,那么 Kismet Sequence Action 仍然会输出 "Succeeded",即使该成就已经被成功解锁。 Game Center 专用
- DownloadedAchievements - 通过在线源下载的成就的数组。 Game Center 专用
Game Center 执行工作流
InternalOnActivated() 被调用后,会将一个代理添加给 OnlineSubsystem.PlayerInterface.ReadAchievements 完成时调用的 OnlineSubsystem.PlayerInterface 函数。 当在线子系统在线完成成就读取工作后,它会调用 InternalOnReadAchievementsComplete() 。 InternalOnReadAchievementsComplete() 会检查成就是否已经被解锁。 如果还没有解锁,那么会调用 BeginUnlockAchievement() 。 否则,如果 AlwaysUnlockAchievement 为 true,那么会调用 InternalOnUnlockAchievementComplete() 。 BeginUnlockAchievement() 会绑定到另一个解锁成就完成时调用的在线子系统代理。 然后 OnlineSubsystem.PlayerInterface.UnlockAchievement() 会被调用。 成就完成解锁后,会调用 InternalOnUnlockAchievementComplete() ,它会调用 FinishedProcessPlayerIndex() 。 在调用 CleanUp 后,所有绑定的代理都会被删除。Steamworks 执行工作流
调用 InternalOnActivated() 的时候,会调用 BeginUnlockAchievement() 。 BeginUnlockAchievement() 会绑定到另一个解锁成就完成时调用的在线子系统代理。 然后 OnlineSubsystem.PlayerInterface.UnlockAchievement() 会被调用。 成就完成解锁后,会调用 InternalOnUnlockAchievementComplete() ,它会调用 FinishedProcessPlayerIndex() 。 在调用 CleanUp 后,所有绑定的代理都会被删除。虚幻脚本
/** * 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来说,此系统允许您上传新的统计在排行榜上使用。 对Steamworks来说,此系统允许您添加,减少或设置成就的统计以供使用。

函数
- 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允许您链接此统计到附加的Kismet变量。
- SIntStat - SStat延展。 定义一个增加,减少或设置统计的值。
- SIntStat - SStat延展。 定义一个增加,减少或设置统计的值。
- EModifyMethod - 此函数设置了您希望如何修改在线统计。
- OnlineStatsWriteClass - 此函数定义了当写入在线子系统时哪个OnlineStatsWrite会归入实例。
- OnlineStatsReadClass - 此函数定义了当写入在线子系统时哪个OnlineStatsRead会归入实例。
- IntStats -要上传到在线子系统的统计定义数组。
- FloatStats -要上传到在线子系统的统计定义数组。
- ModifyMethod -定义采用何种方式来修改在线统计。 唯一的设置只能在Game Center进行,Steamworks能够增加,减少或设定。
- SessionName - 定义了对话名称, 默认为"Game"。
Game Center 执行工作流
在GameCenter,调用 InternalOnActivated() 的时候,会立即调用 SetStatValue() 。 SetStatValue() 使用 OnlineStatsWriteClass 定义的类来实例化OnlineStatsWrite。 然后迭代 IntStats 和 FloatStats 并把那些统计的值写入实例化的OnlineStatsWrite类。 然后它在线写入统计并调用 FinishedProcessPlayerIndex(). 当 CleanUp() 被调用,所有绑定的代理都被释放。 任何对象参照也同样被释放。Steamworks 执行工作流
在Steamworks中,何时调用 InternalOnActivated() 取决于 ModifyMethod 的2个函数值可被调用。 如果 ModifyMethod 为MM_Add 或 MM_Subtract, 则调用 ReadOnlineStats() ,这是因为在线统计值需要被读取来通过增加或减少其数值来修改。 否则,会调用 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() 被调用,所有绑定的代理都被释放。 任何对象参照也同样被释放。虚幻脚本
/** * 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序列动作尝试通过Steamworks读取来强迫客户端刷新其成就。
函数
- InternalOnActivated -当此函数被调用,它会绑定代理并调用 OnlineSubsystem.PlayerInterface.ReadAchievements() .
- InternalOnReadAchievementsComplete - 当 OnlineSubsystem.PlayerInterface.ReadAchievements() 完成从Steamworks读取成就时,此函数被调用。 它会调用 FinishedProcessPlayerIndex().
- CleanUp - 此函数清理任何被绑定的代理。
执行工作流
当 InternalOnActivated() 被调用,它首先会绑定 OnlineSubsystem.PlayerInterface 的代理。 当在线子系统完成从Steamworks读取成就后,该代理会被调用。 然后 OnlineSubsystem.PlayerInterface.ReadAchievements() 会被调用。 成就完成解锁后,会调用 InternalOnReadAchievementsComplete() ,它会调用 FinishedProcessPlayerIndex() 。 当 CleanUp() 被调用,任何绑定的代理都被释放。虚幻脚本
/** * 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序列动作被用来重置成就和/或为玩家保存的统计。 Kismet序列动作只能被用来做调试之用,因为它直接与Steamworks在线子系统绑定。
函数
- InternalOnActivated -如果此函数被调用,它会调用 OnlineSubsystemSteamworks.ResetStats() .
定义
- ResetAchievements - 如果此函数为true,则成就也同样会被重置。
执行工作流
当 InternalOnActivated() 被调用,它会调用 OnlineSubsystemSteamworks.ResetStats() . 然后它会调用 FinishedProcessPlayerIndex() .虚幻脚本
/** * 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序列动作允许您稍后读取Kismet内的在线统计。

函数
- InternalOnActivated - 该函数由父类 SeqAct_OnlineSubsystemBase 进行调用。 然后开始读取Steamworks的统计的调用。
- InternalOnReadOnlineStatsComplete -当从Steamworks的统计读取完成时,这个函数被调用。 它最终会调用 FinishedProcessPlayerIndex().
- CleanUp -此函数清理任何被Kismet序列动作使用的代理和对象参照。
定义
- EReadStatsMethod -该枚举定义了读取统计的不同方法。
- SLinkedVariableName -存放您需要读取的统计的ID的结构,以及链接到附属的Kismet变量的名称。
- OnlineStatsReadClass -在线读取统计类,用来读取在线统计。
- ReadStatsMethod - EReadStatsMethod 的变量声明。
- StartRankIndex -如果使用读取统计排名方法,将开始使用rank index(排名索引).
- RowCount -如使用read stats rank(读取统计排名)或rank around player方式所需行数。
- LinkedVariableNames - 输出的链接变量。
- Rank 映射为Kismet变量的排名变量。
执行工作流
当 InternalOnActivated() 被调用,它首先基于 OnlineStatsReadClass 中定义的类实例化 OnlineStatsRead 。 OnlineSubsystem.StatsInterface.FreeStats() 随后被调用来清理 OnlineStatsRead 实例。 随后代理被绑定,读取在线统计的请求完成。 读取在线统计的方法由 ReadStatsMethod 变量来定义。 这可能调用 OnlineSubsystem.StatsInterface.ReadOnlineStatsForFriends() , OnlineSubsystem.StatsInterface.ReadOnlineStatsByRank() , OnlineSubsystem.StatsInterface.ReadOnlineStatsByRankAroundPlayer() 或 OnlineSubsystem.StatsInterface.ReadOnlineStats(). 当在线统计完成时, InternalOnReadOnlineStatsComplete() 被调用,此函数负责得到统计并将它们输出到附加的Kismet顺序变量。 排名也在此设置。 PopulateLinkedVariableValues() 被调用来复制映射的属性.然后调用 FinishedProcessPlayerIndex() 。 当 CleanUp() 被调用,所有绑定的代理和对象实例都被释放。虚幻脚本
/** * 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序列动作被激活,它会弹出Game Center的成就界面。 一般这个动作会自动暂停游戏。
函数
- InternalOnActivated -当此函数被调用,它会调用 OnlineSubSystem.PlayerInterfaceEx.ShowAchievementsUI() .
执行工作流
当 InternalOnActivated() 被父类调用, SeqAct_OnlineSubsystemBase 随后会调用 OnlineSubSystem.PlayerInterfaceEx.ShowAchievementsUI() . 然后它会调用 FinishedProcessPlayerIndex() .虚幻脚本
/** * 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序列动作被激活,它会弹出Game Center的排行榜界面。 一般这个动作会自动暂停游戏。
函数
- InternalOnActivated -当此函数被调用,它会创建基于 OnlineStatsReadClass 值的 OnlineStatsRead 实例。 随后 OnlineSuppliedUIInterface.ShowOnlineStatsUI() 被调用来弹出Game Center的排行榜界面。 一个代理也被绑定,当界面关闭后,会调用 InternalOnShowOnlineStatsUIComplete 。
- InternalOnShowOnlineStatsUIComplete - 当排行榜界面关闭后,此函数被调用。
- CleanUp -此函数被调用来清空所有绑定的代理和对象参照。
定义
- OnlineStatsReadClass -您想用来读取在线统计的OnlineStatsRead类
执行工作流
当 InternalOnActivated() 被父类 SeqAct_OnlineSubsystemBase 调用,它从 OnlineStatsReadClass 定义的OnlineReadStats获得实例。 一个代理被绑定来侦测排行榜界面何时被关闭。 OnlineSuppliedUIInterface.ShowOnlineStatsUI() 随后被调用来读取和显示排行榜界面。 FinishedProcessPlayerIndex() 随后被调用。 当 InternalOnShowOnlineStatsUIComplete() 被调用,"Closed"输出被激活。 当 CleanUp() 被调用,所有绑定的代理都被释放。虚幻脚本
/** * 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的示例
