Photon Fusion 2 Network Properties Explained
This page accompanies the Photon Fusion 2 Network Properties Explained — How to Sync Player Data in Unity video and explains how to synchronize persistent player data in Photon Fusion Shared Mode.
Use this page when implementing synchronized values such as player colors, health, names, scores, equipment, character selections, animation state, or other data that every connected client must observe consistently.
If you want to dive deeper into Network Properties, take a look here.
Overview
Networked properties are Fusion state values that are replicated between peers.
When the State Authority changes a Networked property, Fusion includes the new value in the object’s synchronized state. Other clients then receive that value and can update their local presentation.
This tutorial builds a small Shared Mode example in which:
- players spawn at different positions;
- each player receives an initial color;
- the selected color is stored as a Networked property;
- players can change their own color during gameplay;
- all connected clients observe the change;
[OnChangedRender]updates the local Unity visuals.
The tutorial also demonstrates an important optimization: synchronizing a one-byte color index instead of the complete color value.
Video Timeline
| Time | Section |
|---|---|
| 00:00 | Introduction |
| 00:22 | Setting up the project |
| 01:18 | Getting the Fusion SDK |
| 03:02 | Creating the player prefab |
| 04:43 | Implementing the player spawner |
| 07:44 | Spawning players in Shared Mode |
| 08:15 | Synchronizing player positions |
| 08:45 | Creating NetworkedPlayerColor |
| 10:29 | Using the [Networked] attribute |
| 13:30 | Applying synchronized colors |
| 14:16 | Building and testing multiplayer |
| 15:37 | Changing colors dynamically |
| 16:23 | Using [OnChangedRender] |
| 17:00 | Optimization and summary |
What Are Networked Properties?
A Networked property is a value stored as part of a Fusion NetworkObject’s replicated state.
Examples include:
- player health;
- player color;
- display name;
- score;
- ammunition;
- selected weapon;
- character skin;
- ready state;
- current team;
- game phase.
A Networked property is declared inside a NetworkBehaviour using the [Networked] attribute.
Example:
C#
[Networked]
public float Health { get; set; }
When the correct authority changes Health, Fusion synchronizes the new value to the other clients.
Networked State Compared with Local State
A normal field exists only inside the local Unity process:
C#
private float _health;
Changing it does not automatically notify other Fusion clients.
A Networked property is part of the object’s replicated state:
C#
[Networked]
public float Health { get; set; }
Use a Networked property when the current value must be known by other peers or late joiners.
Use a normal local field for values that exist only on one client, such as:
- cached component references;
- local UI objects;
- temporary visual effects;
- local input buffers;
- camera references;
- non-networked animation helpers.
Networked Properties Represent State
Networked properties are intended for persistent current state.
For example:
Player color = Blue
A client joining later can receive that current value.
This differs from an RPC, which represents a message or event that happened at a specific moment.
Use Networked properties for values that answer:
What is the object’s current state?
Use RPCs for actions that answer:
What event just happened?
Authority and Networked Properties
Only the peer with the appropriate State Authority should modify a Networked property.
A client can technically assign a value locally on an object it does not control, but that change does not become the authoritative synchronized state.
It may appear temporarily as a local prediction and later be replaced by the actual State Authority value.
Before changing a Networked property, check:
C#
Object.HasStateAuthority
Example:
C#
if (Object.HasStateAuthority)
{
Health = 100f;
}
State Authority in Shared Mode
In Shared Mode, the client that spawns an object generally receives State Authority over it unless authority is configured or transferred differently.
For player objects, each client commonly spawns and controls its own player.
This means:
- Player 1 writes Player 1’s Networked properties;
- Player 2 writes Player 2’s Networked properties;
- proxies display the synchronized results.
The exact authority model depends on the project’s spawning and object configuration.
State Authority Compared with Input Authority
State Authority controls who may write the object’s Networked state.
Input Authority identifies the player associated with the object’s input.
| Authority Type | Purpose |
|---|---|
| State Authority | Writes authoritative Networked properties. |
| Input Authority | Identifies the player whose input is associated with the object. |
In the tutorial:
HasStateAuthoritydetermines whether the client may changeColorIndex;Object.InputAuthority.PlayerIdis used to derive the initial color.
Do not treat Input Authority alone as permission to write Networked state.
Supported Networked Types
Fusion supports specific network-compatible data types.
Common examples include:
byte;int;float;boolorNetworkBool;- enums;
Vector2;Vector3;Quaternion;Color;Color32;PlayerRef;NetworkString;- Fusion Networked collections;
- structs implementing
INetworkStruct.
Complex Unity objects cannot be synchronized directly.
Do not attempt to synchronize:
GameObject;Transform;SpriteRenderer;Image;Material;- arbitrary MonoBehaviours;
- scene component references.
Synchronize the data required to reconstruct their state locally.
Synchronize Data, Not Unity Components
Suppose a player UI contains a Unity Image.
Do not synchronize the Image component.
Instead, synchronize a simple value such as:
C#
[Networked]
public byte ColorIndex { get; set; }
Each client uses the index to update its own local Image, SpriteRenderer, or material.
This keeps Networked state compact and separates networking from presentation.
Project Setup
Begin with a new Unity scene and install the current Fusion SDK.
The basic setup process is:
- Create or open a Unity project.
- Import Photon Fusion.
- Create a Fusion application in the Photon Dashboard.
- Copy its App ID.
- Add the App ID to the Fusion project settings.
- Verify that the project compiles.
Adding Basic Fusion Networking
Fusion provides a setup tool for creating a simple connection flow.
Open:
Tools > Fusion > Scene > Setup Networking in Scene
This creates objects such as:
Prototype Runner;Prototype Network Start.
When the scene starts, the sample connection UI allows you to run the project in different network modes.
For this tutorial, use Shared Mode.
Creating the Player Prefab
Create the player hierarchy:
Player
└── Graphics
└── Body
The root object contains the network and gameplay components.
The child objects contain the visual representation.
The example uses simple 2D shapes, but the same architecture applies to:
- 3D characters;
- sprites;
- vehicles;
- avatars;
- custom models.
Making the Player Networked
Add the following components to the player root:
NetworkObject;NetworkTransform;NetworkedPlayerColor.
NetworkObject gives the prefab a Fusion network identity.
NetworkTransform synchronizes its position and rotation.
After configuring the object:
- Create a Prefabs folder.
- Drag the player into the folder.
- Delete the temporary scene instance.
- Ensure Fusion recognizes the prefab as a network prefab.
The player will be spawned at runtime.
NetworkTransform
NetworkTransform synchronizes transform state without requiring a custom Networked position property.
For this example, it handles:
- player position;
- player rotation where applicable;
- proxy interpolation.
The player color is synchronized separately through NetworkedPlayerColor.
Creating the Player Spawner
Create:
PlayerSpawner
The spawner inherits from:
C#
SimulationBehaviour
and implements:
C#
IPlayerJoined
IPlayerJoined provides a callback when a player enters the Fusion session.
PlayerSpawner Fields
The spawner needs:
- a player prefab;
- an array of spawn points.
Example:
C#
using Fusion;
using UnityEngine;
public class PlayerSpawner :
SimulationBehaviour,
IPlayerJoined
{
[SerializeField]
private NetworkObject _playerPrefab;
[SerializeField]
private Transform[] _spawnPoints;
public void PlayerJoined(PlayerRef player)
{
}
}
Spawning Only the Local Player
Every peer can receive the player-joined callback.
The tutorial checks whether the joining player is the local player:
C#
if (player != Runner.LocalPlayer)
{
return;
}
That peer then spawns its own player object.
This avoids every client spawning duplicate objects for the same player.
Selecting a Spawn Point
Use the player index to select a spawn point:
C#
int spawnIndex =
player.AsIndex %
_spawnPoints.Length;
The modulo operation ensures that the value stays within the array bounds.
For example, with three spawn points:
| Player Index | Spawn Point |
|---|---|
| 0 | 0 |
| 1 | 1 |
| 2 | 2 |
| 3 | 0 |
Spawning the Player
Spawn through the Fusion runner:
C#
public void PlayerJoined(PlayerRef player)
{
if (player != Runner.LocalPlayer)
{
return;
}
if (_spawnPoints == null ||
_spawnPoints.Length == 0)
{
Debug.LogError(
"No player spawn points configured."
);
return;
}
int spawnIndex =
player.AsIndex %
_spawnPoints.Length;
Transform spawnPoint =
_spawnPoints[spawnIndex];
Runner.Spawn(
_playerPrefab,
spawnPoint.position,
spawnPoint.rotation,
player
);
}
The final PlayerRef argument assigns the spawned object’s Input Authority.
Because the local Shared Mode client performs the spawn, it also normally receives State Authority over the new object.
Adding the Spawner to Prototype Runner
Attach PlayerSpawner to:
Prototype Runner
This is important because the runner hosts the simulation callbacks and player-join events.
Assign:
- the player prefab;
- all available spawn points.
When a player joins, the runner calls PlayerJoined.
Testing Basic Spawning
Before implementing color synchronization:
- Enter Play Mode.
- Start a Shared Mode client.
- Confirm that the local player spawns.
- Start a second client.
- Confirm that both players exist.
- Verify that
NetworkTransformsynchronizes movement or position.
Fix spawning or transform issues before adding custom Networked properties.
Creating NetworkedPlayerColor
Create:
NetworkedPlayerColor
The script inherits from:
C#
NetworkBehaviour
It contains:
- an array of body-part renderers;
- an array of predefined colors;
- a Networked color index;
- initialization logic;
- local input;
- a render-side change callback.
Why Synchronize a Color Index?
Fusion can synchronize Color and Color32.
However, the sample uses a predefined palette.
Instead of synchronizing the complete color value, synchronize only the selected index.
Approximate value sizes:
| Data | Approximate Size |
|---|---|
Color |
Four floating-point channels |
Color32 |
Four byte channels |
byte index |
One byte |
If the available colors already exist in the same order on every client, the index is sufficient.
Example:
0 = Red
1 = Blue
2 = Green
3 = Yellow
Synchronizing 2 tells every client to use Green.
Declaring the Networked Property
C#
[Networked]
public byte ColorIndex { get; set; }
Fusion Networked properties are commonly declared as auto-properties.
Do not implement a custom backing field unless the Fusion API explicitly supports the chosen pattern.
Adding OnChangedRender
Add a render-side change callback:
C#
[Networked,
OnChangedRender(nameof(OnPlayerColorChanged))]
public byte ColorIndex { get; set; }
When the rendered value changes, Fusion calls:
C#
private void OnPlayerColorChanged()
{
ApplyPlayerColor();
}
This keeps visual updates separate from the Networked value itself.
Complete NetworkedPlayerColor Example
C#
using Fusion;
using UnityEngine;
public class NetworkedPlayerColor :
NetworkBehaviour
{
[SerializeField]
private SpriteRenderer[] _bodyParts;
[SerializeField]
private Color[] _playerColors;
[Networked,
OnChangedRender(nameof(OnPlayerColorChanged))]
public byte ColorIndex { get; set; }
public override void Spawned()
{
if (_playerColors == null ||
_playerColors.Length == 0)
{
Debug.LogError(
"No player colors configured.",
this
);
return;
}
if (Object.HasStateAuthority)
{
int playerId =
Object.InputAuthority.PlayerId;
int initialIndex =
playerId %
_playerColors.Length;
ColorIndex =
(byte)initialIndex;
}
ApplyPlayerColor();
}
private void Update()
{
if (!Object.HasStateAuthority)
{
return;
}
if (!Input.GetKeyDown(KeyCode.W))
{
return;
}
if (_playerColors == null ||
_playerColors.Length == 0)
{
return;
}
int nextIndex =
(ColorIndex + 1) %
_playerColors.Length;
ColorIndex =
(byte)nextIndex;
}
private void OnPlayerColorChanged()
{
ApplyPlayerColor();
}
private void ApplyPlayerColor()
{
if (_playerColors == null ||
_playerColors.Length == 0)
{
return;
}
if (ColorIndex >=
_playerColors.Length)
{
return;
}
Color currentColor =
_playerColors[ColorIndex];
foreach (
SpriteRenderer bodyPart
in _bodyParts)
{
if (bodyPart == null)
{
continue;
}
bodyPart.color =
currentColor;
}
}
}
Initializing the Color in Spawned
Spawned() is Fusion’s network-object initialization callback.
It is called when the NetworkObject becomes available to the local peer.
For the authoritative player:
C#
if (Object.HasStateAuthority)
{
int playerId =
Object.InputAuthority.PlayerId;
int colorIndex =
playerId %
_playerColors.Length;
ColorIndex =
(byte)colorIndex;
}
This gives each player a starting color derived from their player ID.
Why Apply the Color in Spawned?
The callback associated with [OnChangedRender] responds to rendered changes.
The object should also apply its current visual state when it initially spawns.
Calling:
C#
ApplyPlayerColor();
from Spawned() ensures the current synchronized value is reflected immediately.
This is particularly important for:
- proxies;
- late joiners;
- objects whose Networked state was initialized before the local view existed.
Changing the Color
The sample uses the W key:
C#
private void Update()
{
if (Object.HasStateAuthority &&
Input.GetKeyDown(KeyCode.W))
{
int nextIndex =
(ColorIndex + 1) %
_playerColors.Length;
ColorIndex =
(byte)nextIndex;
}
}
Only the player with State Authority can change the value.
When ColorIndex changes:
- Fusion records the new Networked state.
- Other clients receive it.
OnPlayerColorChanged()executes during rendering.- Each client updates its local SpriteRenderers.
Why Use Update for the Key Press?
The example uses Unity’s Input.GetKeyDown, which is a rendered-frame input API.
This is acceptable for a simple visual demonstration.
For gameplay-critical input, use Fusion’s normal input collection and consume the result during FixedUpdateNetwork().
A color-selection menu could also call a public method rather than reading a keyboard key directly.
Public Color Selection Method
A reusable implementation can expose:
C#
public void SelectColor(byte colorIndex)
{
if (!Object.HasStateAuthority)
{
return;
}
if (colorIndex >=
_playerColors.Length)
{
return;
}
ColorIndex = colorIndex;
}
A local UI button can then request a specific color.
For competitive or restricted customization, validate the selection on the appropriate authority.
Applying the Color Locally
The visual method reads the synchronized index:
C#
private void ApplyPlayerColor()
{
if (ColorIndex >=
_playerColors.Length)
{
return;
}
Color currentColor =
_playerColors[ColorIndex];
foreach (
SpriteRenderer bodyPart
in _bodyParts)
{
bodyPart.color =
currentColor;
}
}
The SpriteRenderers are not Networked.
Every client updates its own renderers using the same synchronized data.
Multiple Body Parts
The example uses an array so one color can be applied to several character parts.
Examples include:
- body;
- arms;
- helmet;
- weapon accent;
- outline;
- UI marker.
The array allows the same Networked value to drive several local visual components.
Shared Color Palette
Every client must use the same palette order.
For example, if index 1 is Blue on one client but Green on another, the Networked property remains synchronized but the visual result differs.
Keep the color palette consistent through:
- the same prefab;
- a shared ScriptableObject;
- a versioned configuration asset;
- build validation.
Color Index Validation
Always validate the index before reading the array:
C#
if (ColorIndex >= _playerColors.Length)
{
return;
}
This prevents an out-of-range exception if:
- the palette changes;
- invalid state is received;
- a prefab is misconfigured;
- a value is set through debugging tools.
Networked Property Optimization
Synchronize the smallest value that fully represents the required state.
Examples:
| Requirement | Possible Networked Value |
|---|---|
| Predefined player color | byte palette index |
| Selected skin | byte or ushort skin index |
| Team | small enum |
| Ready state | NetworkBool |
| Weapon selection | compact weapon ID |
| Current animation state | byte enum or compact parameter |
| Player name | fixed-capacity NetworkString |
| Health | quantized integer where precision permits |
Do not synchronize a large structure when a compact identifier produces the same result.
Derived State
Some values do not need to be synchronized because every client can derive them.
For example, if a player’s display outline is always brighter than their synchronized base color, synchronize only the base color index and derive the outline locally.
Avoid storing both:
BaseColor
OutlineColor
when one can be calculated from the other.
Avoid Writing Unchanged Values
Before assigning frequently changing Networked state, check whether the value actually differs.
Example:
C#
if (ColorIndex != requestedIndex)
{
ColorIndex = requestedIndex;
}
Fusion handles state replication efficiently, but avoiding unnecessary writes makes the code’s intent clearer and can reduce avoidable change activity.
Networked Property Change Flow
The complete color flow is:
- The authoritative player presses
W. ColorIndexchanges.- Fusion stores the new object state.
- Fusion sends the change to interested peers.
- Proxy render state updates.
[OnChangedRender]invokes the callback.ApplyPlayerColor()changes local SpriteRenderers.
Late Joiners
Networked properties represent current state.
A player joining after another player has changed color receives the current ColorIndex.
The late joiner does not need the history of every color change.
They only need the latest value.
This is one of the main differences between Networked state and an RPC-only implementation.
Testing with Multiple Clients
Test the project with at least two instances.
Suggested process:
- Create a standalone build.
- Start one client from the build.
- Start another client in the Unity Editor.
- Connect both clients to the same Shared Mode session.
- Confirm that each player spawns at a different point.
- Confirm that each player has an initial color.
- Press
Won Player 1. - Verify that only Player 1 changes color.
- Confirm that Player 2 sees the new color.
- Press
Won Player 2. - Confirm that the change appears on both clients.
Testing Authority
Verify that a client cannot change a remote player’s synchronized color.
Each local client should modify only the object for which it has State Authority.
Add temporary logging if required:
C#
Debug.Log(
$"Player {Object.InputAuthority} " +
$"State Authority: " +
$"{Object.HasStateAuthority}"
);
Testing Late Joiners
- Start Player 1.
- Change Player 1’s color.
- Start Player 2 afterwards.
- Join the same session.
- Verify that Player 2 sees Player 1’s current color.
This confirms that the value exists as synchronized state rather than only as a local event.
Player Color as an Example Pattern
The same architecture can be reused for other player data.
Health
C#
[Networked,
OnChangedRender(nameof(OnHealthChanged))]
public float Health { get; set; }
The callback updates a local health bar.
Player Name
C#
[Networked]
public NetworkString<_32>
PlayerName { get; set; }
The local UI displays the value above the player.
Selected Skin
C#
[Networked,
OnChangedRender(nameof(OnSkinChanged))]
public byte SkinIndex { get; set; }
The callback activates the correct model or material.
Score
C#
[Networked]
public int Score { get; set; }
The scoreboard reads the current synchronized value.
Change Callbacks and Presentation
Use [OnChangedRender] for visual updates that should occur when a rendered Networked value changes.
Examples include:
- changing a material;
- updating a health bar;
- changing player name text;
- displaying team colors;
- updating an equipment model;
- switching an animation layer.
Do not use a render callback as the authoritative place for gameplay state changes.
The callback is for presentation based on state that has already changed.
Gameplay Logic and Render Logic
| Logic | Recommended Location |
|---|---|
| Change authoritative health | FixedUpdateNetwork(), RPC validation, or authoritative gameplay system |
| Apply health-bar fill | Render() or [OnChangedRender] |
| Change selected weapon ID | Authoritative simulation logic |
| Enable local weapon model | Render callback |
| Change player color index | State Authority |
| Apply color to SpriteRenderers | Render callback |
Keep state mutation separate from visual application.
Networked Collections
For multiple synchronized values, Fusion provides Networked collections such as:
NetworkArray;NetworkDictionary;NetworkLinkedList.
Use them for bounded collections such as:
- inventory slots;
- lobby player data;
- recent events;
- team rosters.
Do not use a collection when one compact property is sufficient.
Recommended Implementation Workflow
- Decide which value represents the authoritative state.
- Determine which peer should own State Authority.
- Choose the smallest supported Networked type.
- Declare the property in a
NetworkBehaviour. - Change it only from the correct authority.
- Use
Spawned()to apply initial state. - Use
[OnChangedRender]orRender()for visual updates. - Keep Unity component references local.
- Test proxies and late joiners.
- Inspect state size and change frequency.
Key Concepts
| Concept | Description |
|---|---|
| Networked property | Value stored as part of Fusion’s replicated object state. |
[Networked] |
Attribute declaring a Fusion-synchronized property. |
NetworkBehaviour |
Base class that provides Fusion network-object functionality. |
| State Authority | Peer allowed to write authoritative Networked state. |
| Input Authority | Player associated with the object’s input. |
NetworkObject |
Gives a GameObject a Fusion network identity. |
NetworkTransform |
Synchronizes object position and rotation. |
SimulationBehaviour |
Fusion behaviour used for runner-level simulation callbacks. |
IPlayerJoined |
Interface receiving player-join callbacks. |
PlayerRef |
Fusion identifier for a connected player. |
Spawned() |
Initialization callback for a spawned NetworkObject. |
[OnChangedRender] |
Invokes a render-side callback when a property’s rendered value changes. |
| Proxy | Local representation of an object controlled by another peer. |
| Late joiner | Client that enters after Networked state has already been established. |
Best Practices
Use this checklist when working with Networked properties:
- Synchronize state, not Unity component references.
- Modify Networked properties only from the correct State Authority.
- Use
PlayerRefto identify players. - Use compact IDs or indexes for predefined data.
- Apply synchronized state to local visuals.
- Use
Spawned()for initial visual setup. - Use
[OnChangedRender]for presentation changes. - Keep gameplay mutations out of render callbacks.
- Validate array indexes before using them.
- Keep configuration arrays consistent on all clients.
- Avoid synchronizing values that can be derived locally.
- Avoid writing values that have not changed.
- Test with multiple clients.
- Test late joining.
- Verify authority before accepting local input.
- Use Networked collections only when several bounded values are required.
Common Pitfalls
| Pitfall | Why It Is a Problem |
|---|---|
| Changing a property without State Authority | The value is not accepted as authoritative synchronized state. |
Synchronizing a GameObject or SpriteRenderer |
Unity object references are not general Networked property data. |
| Synchronizing a complete color when an index is sufficient | Uses more Networked state than necessary. |
Forgetting to inherit from NetworkBehaviour |
The script cannot declare Fusion Networked properties correctly. |
Using a normal field instead of [Networked] |
Other peers do not receive the value. |
Not applying the value in Spawned() |
Initial or late-join visuals may not reflect current state immediately. |
Updating gameplay state from [OnChangedRender] |
Render callbacks are presentation-side and not authoritative simulation logic. |
| Using different color-array orders on clients | The same index produces different visual results. |
| Not checking palette length | Modulo or array access can fail when no colors are configured. |
| Letting every client spawn every player | Duplicate player objects can be created. |
Assuming the final Runner.Spawn argument grants State Authority |
It assigns Input Authority; Shared Mode State Authority comes from the spawning and authority rules. |
| Reading local input on proxy objects | A client may attempt to modify remote player representations. |
| Using an RPC alone for current color | Late joiners would not automatically receive the current selection. |
Summary
Networked properties synchronize persistent object state between Fusion clients.
In the tutorial, each player stores a Networked ColorIndex. The client with State Authority changes the index, Fusion replicates it, and [OnChangedRender] updates the local SpriteRenderers on every peer.
Synchronizing a compact palette index is more efficient than synchronizing the complete color value when every client already has the same predefined palette.
The same pattern applies to health, names, scores, skins, equipment, ready states, and other player data:
- Synchronize a compact authoritative value.
- Let the correct authority change it.
- Reconstruct the Unity presentation locally.
Last updated on
Back to top- Overview
- Video Timeline
- What Are Networked Properties?
- Networked State Compared with Local State
- Networked Properties Represent State
- Authority and Networked Properties
- State Authority in Shared Mode
- State Authority Compared with Input Authority
- Supported Networked Types
- Synchronize Data, Not Unity Components
- Project Setup
- Adding Basic Fusion Networking
- Creating the Player Prefab
- Making the Player Networked
- NetworkTransform
- Creating the Player Spawner
- PlayerSpawner Fields
- Spawning Only the Local Player
- Selecting a Spawn Point
- Spawning the Player
- Adding the Spawner to Prototype Runner
- Testing Basic Spawning
- Creating NetworkedPlayerColor
- Why Synchronize a Color Index?
- Declaring the Networked Property
- Adding OnChangedRender
- Complete NetworkedPlayerColor Example
- Initializing the Color in Spawned
- Why Apply the Color in Spawned?
- Changing the Color
- Why Use Update for the Key Press?
- Public Color Selection Method
- Applying the Color Locally
- Multiple Body Parts
- Shared Color Palette
- Color Index Validation
- Networked Property Optimization
- Derived State
- Avoid Writing Unchanged Values
- Networked Property Change Flow
- Late Joiners
- Testing with Multiple Clients
- Testing Authority
- Testing Late Joiners
- Player Color as an Example Pattern
- Change Callbacks and Presentation
- Gameplay Logic and Render Logic
- Networked Collections
- Recommended Implementation Workflow
- Key Concepts
- Best Practices
- Common Pitfalls
- Summary