Changes

→‎Per-screen data: expand info + example, add new features in SMAPI 3.9
Line 139: Line 139:  
[[Multiplayer|Split-screen multiplayer]] swaps the entire game state between each player, so each mod runs across every player. For example, with four local players the [[Modding:Modder Guide/APIs/Events#GameLoop.UpdateTicked|<tt>UpdateTicked</tt>]] event would be raised four times per tick. The game manages its own state automatically (e.g. <tt>Game1.activeClickableMenu</tt>), but if your mod stores info in its own fields you may need to handle those yourself.
 
[[Multiplayer|Split-screen multiplayer]] swaps the entire game state between each player, so each mod runs across every player. For example, with four local players the [[Modding:Modder Guide/APIs/Events#GameLoop.UpdateTicked|<tt>UpdateTicked</tt>]] event would be raised four times per tick. The game manages its own state automatically (e.g. <tt>Game1.activeClickableMenu</tt>), but if your mod stores info in its own fields you may need to handle those yourself.
   −
That can be tricky, so SMAPI provides <code>PerScreen&lt;T&gt;</code> to take care of it. A <code>PerScreen&lt;T&gt;</code> field manages a separate value for each local screen.
+
SMAPI provides <code>PerScreen&lt;T&gt;</code> to make that easy. You use it by creating '''readonly''' fields for your values (which can be any value from <tt>int</tt> to entire class instances):
 +
<source lang="C#">
 +
private readonly PerScreen<int> LastPlayerId = new PerScreen<int>(); // defaults to 0
 +
private readonly PerScreen<SButton> LastButtonPressed = new PerScreen<SButton>(createNewState: () => SButton.None); // defaults to the given value
 +
</source>
   −
For example, this mod would keep track of the last button pressed by each player:
+
Then you can just use its properties and methods:
 +
 
 +
{| class="wikitable"
 +
|-
 +
! member
 +
! description
 +
|-
 +
| <code>Value</code>
 +
| Get or set the value for the current screen. If needed, this creates the value automatically.
 +
|-
 +
| <code>GetActiveValues()</code>
 +
| Get the screen IDs and values for every active screen which has a value.
 +
|-
 +
| <code>GetValueForScreen(screenId)</code><br /><code>SetValueForScreen(screenId, value)</code>
 +
| Get or set the value for a specific screen ID, creating it if needed.
 +
|-
 +
| <code>ResetAllScreens</code>
 +
| Clear the values saved for every screen.
 +
|}
 +
 
 +
===Example===
 +
For example, this mod plays a simple game: the main player presses a key, and farmhands need to guess which key was pressed.
    
<source lang="C#">
 
<source lang="C#">
Line 147: Line 172:  
{
 
{
 
     private readonly PerScreen<SButton> LastButtonPressed = new PerScreen<SButton>();
 
     private readonly PerScreen<SButton> LastButtonPressed = new PerScreen<SButton>();
 
+
    private readonly PerScreen<int> Score = new PerScreen<int>();
    
     public override void Entry(IModHelper helper)
 
     public override void Entry(IModHelper helper)
Line 156: Line 181:  
     private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
 
     private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
 
     {
 
     {
         this.LastButtonPressed.Value = e.Button;
+
         // main player changes key
 +
        if (Context.IsMainPlayer)
 +
        {
 +
            this.LastButtonPressed.Value = e.Button;
 +
            this.Monitor.Log("The main player changed the key. Can you guess it?", LogLevel.Info);
 +
        }
 +
 
 +
        // farmhands try to guess the key
 +
        else
 +
        {
 +
            SButton correctButton = this.LastButtonPressed.GetValueForScreen(0);
 +
            if (correctButton != SButton.None)
 +
            {
 +
                if (e.Button == correctButton)
 +
                {
 +
                    this.Score.Value++;
 +
                    this.LastButtonPressed.SetValueForScreen(0, SButton.None);
 +
                    this.Monitor.Log($"Player #{Context.ScreenId + 1} correctly guessed the key: {e.Button}! Their score is now {this.Score.Value}.", LogLevel.Info);
 +
                }
 +
                else
 +
                    this.Monitor.Log($"Player #{Context.ScreenId + 1} guessed incorrectly: {e.Button}.", LogLevel.Debug);
 +
            }
 +
        }
 
     }
 
     }
 
}
 
}
</source>
  −
  −
If you haven't set a value for the current player, <code>PerScreen&lt;T&gt;</code> will return the default value for the type (e.g. 0 for <tt>int</tt>). You can change that by specifying your own default-value logic:
  −
  −
<source lang="C#">
  −
private readonly PerScreen<SButton> LastButtonPressed = new PerScreen<SButton>(createNewState: () => SButton.None);
   
</source>
 
</source>
  
translators
8,404

edits