Photon Unity Networking Real-time multiplayer online game development solution

Author: Zen and the Art of Computer Programming

1 Introduction

In 2019, at the GDC conference jointly organized by Intel, Facebook and other companies, it was announced that Unity Technologies would launch a new brand-Unity Game Development Platform (UGDP). The platform will include support for Unreal Engine 4, Unreal Engine 4 and the native Unity engine. Based on this platform, Unity Technologies launched Photon Services, a real-time multiplayer online game service, which has the following advantages:

  • Support Windows/Mac/Linux/IOS/Android
  • Completely free, no matter the size of your project
  • Strong scalability, can support millions of concurrent users
  • Support WebGL/HTML5/PWA
  • Can be quickly deployed to the cloud or local server
    . Based on these advantages, Unity Technologies developers have developed a complete set of Photon SDK, which mainly provides the following functions:
  • Communication between game server and client
  • Synchronization of user roles
  • Physics engine
  • sound engine
  • Buddy system
  • grouping system
  • Multi-language support
  • chat system
  • The goal of data storage
    UGDP is to become a comprehensive cloud development platform in the gaming field. The video released at the GDC conference also pointed out that more and more game creators will use this platform for development in the future. Photon Unity Networking is a complete set of real-time multiplayer online game development solutions developed based on Photon Services. You can use it to quickly build your own online virtual reality game.
    In order to further refine Photon Services, this article will gradually explain the relevant features and functions of Photon Services in the following order. Key features of Photon Services include:
  • Service model: Photon Services provides a cloud-hosted service model. Customers only need to pay the server running cost to get a good service experience;
  • Low development threshold: Photon Services provides a rich API that is simple and easy to understand, so developers can easily implement their own game logic;
  • Ultra-high concurrency: Photon Services supports millions of levels of concurrency on a single machine, and also provides a distributed cluster architecture to meet the business needs of large game teams;
  • Immersive gaming experience: Photon Services provides an immersive gaming experience, including cross-platform support (Windows/Mac/Linux/IOS/Android), WebGL/HTML5/PWA, real-time interactive chat and multi-language features;
  • Data security: Photon Services provides data storage and transmission encryption functions to ensure data integrity, privacy and availability;
  • Free trial: Photon Services provides a free trial version, developers can experience all the functions of Photon Services without charging any fees;
    next, I will describe each functional module of Photon Services in detail.

2. Explanation of basic concepts and terms

1. User role management (Authentication and Authorization)

In Photon Services, each user is assigned a unique ID. When a user connects to the server, the server ensures the user's valid identity through an authentication process. After the authentication is completed, the server will give the user a string of tokens as an identifier to distinguish different devices. This process is often called "authentication".
Through authentication and authorization mechanisms, the server can determine whether each user has permission to access certain resources. Each client needs to send a request to the server, declaring its user identity and attaching the corresponding token. If the server confirms that the token is correct, the user is allowed to access the resource.
The server's authentication and authorization module consists of two submodules:

  1. User Registration : When a user logs in for the first time, they need to create an account before they can play the game normally. User registration generally includes two aspects: user name, password, email address, and mobile phone number. The username and password are used to identify and authenticate the user's identity, and the email and mobile phone number can be used to retrieve the password or verify the user's identity.
  2. User login : Users can log in to Photon Server through username and password to complete identity authentication. After the server verifies the username and password, it generates a token and sends it to the client as an identifier to distinguish different devices.
    The authentication information after the user logs in is stored in a temporary database on the server until the user logs out.

2.Synchronization of User Roles

When a user logs into the game client, he needs to find the location and status information of other players on the game server. This process is called "role synchronization".
Photon Services provides a character synchronization framework that allows the game client to automatically obtain all player information, including location, status, etc. The client only needs to call a function to receive the latest status of all players.
The latest status of each player can be returned to the client through callbacks. In this way, the client can render a virtual world that meets the requirements based on the player's position, field of view, angle and other information.

3.Physics Engine

Another important function of character synchronization is to respond to physical events from the server in real time. For example, the server might broadcast a message indicating that a player shot an object, requiring the engine to calculate and respond to the result.
The physics engine of Photon Services is a high-performance, complete physics engine library. It is designed to support various types of game physics behaviors, including rigid body motion, collision detection, resultant forces, etc.
The physics engine can be used to create open worlds, such as cities, or it can be used to create level interiors, such as the movement of props in e-sports competitions.

4. Sound Engine

The sound engine is responsible for processing various game sound effects, including background music, sound effects, audio track switching, three-dimensional sound effects, etc. Photon Services' sound engine is implemented using the OpenAL API and supports a variety of audio formats and codecs.
The sound engine can also be used to play local sound effects files on the client to improve the performance of the game screen.

5.Friends System

Photon Services provides a friend system that allows players to establish connections and share in-game data and resources. The server records the relationships between each player and transmits them to the client. The client can use the friend system to search, add and delete friends, and invite friends to play games together.
For unique gameplay in the game, the server can also create rules-based "teams" and arrange them to compete together. Each team has a unified manager who initiates the game.

6. Group System

The group system can be used to organize and manage players in the game. Each player can join multiple groups, and each group can have multiple members. The group system can be used to create public social circles, making it easier for players to work together.
The group system can also be used to broadcast notifications into the game, letting the entire community know what's going on.

7.Multi-Language Support

Photon Services' multi-language support module helps game developers provide games in different languages. For games with different language versions, you only need to configure the language settings on the server side, and the client will automatically switch to display the corresponding language.

8. Chat System

The chat system is a real-time communication tool that allows players in the game to communicate with each other. The chat system of Photon Services allows users to quickly and directly communicate in various forms such as text, voice, graphics, expressions, etc.
The chat system can also be used to record historical data in the game, allowing players to review past experiences.

9. Data Storage

Data storage is an important function, which allows players in the game to save some customized data, such as archives, achievements, properties, etc. Photon Services provides a rich data interface, including file upload and download, data synchronization, data query and other functions.
In addition, game developers can also obtain some statistical data from the server to understand players' habits and preferences. This data can be used to optimize game play.

3. Core algorithm principles and operating steps

Below, I will elaborate on each functional module of Photon Services, introduce its key technologies and core algorithm principles, and give detailed code examples.

1.Synchronization of User Roles

Role synchronization is the most basic function of Photon Services. The algorithms involved in role synchronization are:

  1. Differential Synchronization: The server reduces network traffic and improves game performance by transmitting changes instead of absolute values;
  2. Funnel Synchronization: The server only transmits the changes of the character, reducing network consumption;
  3. Smoothing Fusion: The server uses a "time average" method to aggregate the character data of each client to eliminate the impact of mutations;

Steps:

  1. The client connects to the server;
  2. The server tells the client the initial state of the current role;
  3. The client updates the role status;
  4. The server calculates the amount of change in the character;
  5. The server transmits the change amount of the role;
  6. The client receives the change amount of the role;
  7. The client updates the role status;
  8. Repeat this cycle until the roles on both sides are synchronized.
using Photon.Realtime;
using Photon.Pun;
public class MyCharacter : MonoBehaviourPun, IPunObservable
{
    
    
   private Vector3 _position = Vector3.zero;
   
   void Start()
   {
    
    
       if (photonView.IsMine)
       {
    
    
           // We are the local player, so we can request the other players via RPC call
       }
   }
   
   [PunRPC]
   void UpdateOtherPlayerPosition(Vector3 position)
   {
    
    
       this._position = position;
       transform.position = position;
   }
   
   void Update()
   {
    
    
       photonView.RPC("UpdateOtherPlayerPosition", RpcTarget.Others, transform.position);
       
       // Do some calculations based on new role state
      ...
   }
}

The above example demonstrates how to use role synchronization. The Start() function of the client script calls the photonView.IsMine condition, which will only be executed when the client is the main character. That is to say, only the local player's character information will be requested and synchronized.
The update() function that updates the character status uses the photonView.RPC method and calls the UpdateOtherPlayerPosition function to update the character positions of other players through Remote Procedure Call. This function sends its own character location information to other clients through remote calls.
The implementation part of the character synchronization algorithm uses Vector3 as the character state type, but in fact other types can also be used, such as Quaternion, Color, etc. In addition, character synchronization can also be expanded using the PhotonView component to achieve character synchronization in more complex scenes. For example, characters can have different movement methods, attack capabilities, targeting algorithms, etc. These can all be defined with custom properties, and then the server side synchronizes them all to the client side.

2.Physics Engine

The physics engine is the core module of Photon Services and the basic module of many online games. The physics engine of Photon Services is developed based on the Bullet Physics API, which can simulate complex physical behaviors such as collisions, springs, friction, damping, etc.
Bullet Physics API is a high-performance physics engine specially designed for real-time computing. It has reliable mathematical performance and is suitable for games, virtual reality, robot control, interactive media and other fields.

Steps:

  1. Create a Rigidbody Component;
  2. Add rigid bodies to objects and assign reasonable properties such as mass, friction, resistance, etc.;
  3. Add a collision body (Collision Shape Component) to the rigid body;
  4. Use Relative Position to synchronize rigid body position and rotation;
  5. (Optional) Apply Physical Materials;
  6. (Optional) Add triggers;
using UnityEngine;
using Photon.Pun;
public class MyCubeController : MonoBehaviourPun, IPunObservable
{
    
    
   Rigidbody _rigidbody;
   
   private float speed = 5f;
    void Start()
   {
    
    
       _rigidbody = GetComponent<Rigidbody>();
       
       if (!photonView.IsMine)
       {
    
    
           // We don't need to synchronize our movement, let's just move directly with interpolation
       }
   }
   
   void Update()
   {
    
    
       if (photonView.IsMine && Input.GetKey(KeyCode.W))
       {
    
    
           _rigidbody.velocity += transform.forward * Time.deltaTime * speed;
       }
       else if (photonView.IsMine && Input.GetKey(KeyCode.S))
       {
    
    
           _rigidbody.velocity -= transform.forward * Time.deltaTime * speed;
       }
       
       if (photonView.IsMine && Input.GetKey(KeyCode.A))
       {
    
    
           _rigidbody.velocity -= transform.right * Time.deltaTime * speed;
       }
       else if (photonView.IsMine && Input.GetKey(KeyCode.D))
       {
    
    
           _rigidbody.velocity += transform.right * Time.deltaTime * speed;
       }
       
       if (_rigidbody!= null)
       {
    
    
           _rigidbody.velocity = Vector3.ClampMagnitude(_rigidbody.velocity, speed);
           
           // Apply gravity force
           _rigidbody.AddForce(new Vector3(0, -1, 0), ForceMode.Acceleration);
       }
   }
    void FixedUpdate()
   {
    
    
       if (photonView.IsMine)
       {
    
    
           transform.position = _rigidbody.position;
           transform.rotation = _rigidbody.rotation;
       }
   }
   
   [PunRPC]
   void SetVelocity(Vector3 velocity)
   {
    
    
       _rigidbody.velocity = velocity;
   }
   
   void OnTriggerEnter(Collider collider)
   {
    
    
       if (collider.GetComponent<PhotonView>() == null || collider.GetComponent<PhotonView>().IsMine)
       {
    
    
           return;
       }
        Debug.LogFormat("{0} entered trigger from {1}", gameObject.name, collider.gameObject.name);
   }
}

The above example demonstrates how to use the physics engine. After creating a rigid body component, assign reasonable mass, friction, resistance and other attributes to the rigid body. Set a speed limit to prevent your character from running too fast. Processes the character's input commands and converts them into directions of motion for the rigid body. Synchronize the state of the rigid body to the server and update its own transform in the FixedUpdate() function. The character trigger (OnTriggerEnter()) function uses remote procedure call (SetVelocity()) to send the rigid body's velocity information to other clients.
The physics engine can also be used to handle complex physical scenes, such as intersecting walls, stairs, roofs, room isolation areas, triggers, etc., which require a high degree of mathematical skills to achieve accurate simulations.

3. Sound Engine

The Photon Services sound engine module is responsible for processing game sounds, including background music, sound effects, audio track switching, three-dimensional sound effects, etc. The Photon Services sound engine is implemented using the OpenAL API and supports a variety of audio formats and codecs.

Steps:

  1. Create AudioSource component;
  2. Set parameters such as sound source type, distance model, sound effect mixing, etc.;
  3. Set the sound effect file path;
  4. Adjust sound effect parameters;
using Photon.Pun;
public class MusicManager : MonoBehaviourPun
{
    
    
   AudioSource audioSource;
    void Awake()
   {
    
    
       audioSource = GetComponent<AudioSource>();
   }
    [PunRPC]
   void PlayMusic(string name)
   {
    
    
       string path = Application.streamingAssetsPath + "/music/" + name;
       audioSource.PlayOneShot(Resources.Load<AudioClip>(path));
   }
    void OnJoinedRoom()
   {
    
    
       photonView.RPC("PlayMusic", RpcTarget.AllBuffered, "background_music");
   }
}

The above example demonstrates how to use the sound engine. Create an AudioSource component and set parameters such as sound source type, distance model, and sound effect mixing. Set the sound effect file path for AudioSource and call the PlayOneShot() method to play the sound effect. During initialization, the photonView.RPC() function is called to send background music information to all clients.
The sound engine can also be used to implement audio dynamic range (DOA) calculation, dynamic positioning, environmental sound effects, sound effect capture, sound effect editing and other functions.

4.Friends System

The buddy system is one of the advanced features of Photon Services. It provides a social function independent of the game, allowing players to connect with other players and share in-game data and resources.

Steps:

  1. Create a friends list;
  2. Get friends list;
  3. Add, delete and find friends;
  4. Set friend alias;
using Photon.Pun;
public class FriendManager : MonoBehaviourPun
{
    
    
   static List<Friend> friendsList = new List<Friend>();
    public class Friend
   {
    
    
       public string username;
       public string alias;
        public Friend(string uName, string aName)
       {
    
    
           username = uName;
           alias = aName;
       }
   }
    public bool AddFriend(string username, string alias)
   {
    
    
       foreach (Friend f in friendsList)
       {
    
    
           if (f.username == username)
           {
    
    
               return false; // Already exists
           }
       }
        friendsList.Add(new Friend(username, alias));
       return true;
   }
    public bool RemoveFriend(string username)
   {
    
    
       for (int i = 0; i < friendsList.Count; ++i)
       {
    
    
           if (friendsList[i].username == username)
           {
    
    
               friendsList.RemoveAt(i);
               return true;
           }
       }
        return false;
   }
    public bool FindFriend(string searchText, out List<Friend> foundList)
   {
    
    
       foundList = new List<Friend>();
        foreach (Friend f in friendsList)
       {
    
    
           if (f.alias.ToLower().Contains(searchText.ToLower()))
           {
    
    
               foundList.Add(f);
           }
       }
        return foundList.Count > 0;
   }
}

The above example shows the implementation of the friend system. First, create a static variable friendsList of the FriendManager class to save the friend list. The Friend class is used to encapsulate the friend's username and alias. The AddFriend() function is used to add new friends, the RemoveFriend() function is used to delete friends, and the FindFriend() function is used to search for friends.
After the client joins the room, the friend list can be sent to all clients by calling the photonView.RPC() function. Other clients can read the friend list and process it accordingly. For example, the client can display a list of friends and allow the player to select a friend to chat with.

5. Group System

The grouping system is another advanced feature of Photon Services. It provides a centralized ability to manage multiple players, allowing players to organize themselves and work together to solve problems.

Steps:

  1. create group;
  2. join group;
  3. leave group;
  4. Query group members;
using Photon.Pun;
public class GroupManager : MonoBehaviourPun
{
    
    
   const byte MaxPlayersPerGroup = 4;
    Dictionary<byte, List<int>> groups = new Dictionary<byte, List<int>>();
    public bool CreateGroup(byte groupId)
   {
    
    
       if (groups.ContainsKey(groupId))
       {
    
    
           return false; // Already exists
       }
        groups.Add(groupId, new List<int>());
       return true;
   }
    public bool JoinGroup(byte groupId)
   {
    
    
       if (photonView.IsMine && groups.ContainsKey(groupId) && groups[groupId].Count >= MaxPlayersPerGroup)
       {
    
    
           return false; // Full
       }
        int playerId = photonView.OwnerActorNr;
        if (!groups.ContainsKey(groupId))
       {
    
    
           groups.Add(groupId, new List<int>());
       }
        groups[groupId].Add(playerId);
       return true;
   }
    public bool LeaveGroup(byte groupId)
   {
    
    
       if (!groups.ContainsKey(groupId))
       {
    
    
           return false; // Not in group
       }
        groups[groupId].Remove(photonView.OwnerActorNr);
       return true;
   }
    public bool QueryGroups()
   {
    
    
       SendGroupsToMaster();
        return true;
   }
    [PunRPC]
   void ReceiveGroups(Dictionary<byte, List<int>> receivedGroups)
   {
    
    
       groups = receivedGroups;
   }
    void SendGroupsToMaster()
   {
    
    
       photonView.Rpc("ReceiveGroups", RpcTarget.MasterClient, groups);
   }
}

The above example shows the implementation of the grouping system. First, create a MaxPlayersPerGroup constant that represents the maximum number of players each group can hold. Next, create a dictionary groups to store the list of players in each group.
Create a group The CreateGroup() function checks whether the GroupId already exists. If it does not exist, it creates a new, empty group. JoinGroup() function checks whether the specified group exists and checks whether the number of group members reaches the maximum value. If you can join, add your ActorNumber to the list of players in the group. Similarly, the LeaveGroup() function removes the group it belongs to through ActorNumber. The QueryGroups() function sends group information to the host client.
When the client joins the room, the SendGroupsToMaster() function is called to send the group information to the host client. After the host client receives the group information, it calls the ReceiveGroups() function to update the group members. All clients can call the QueryGroups() function to obtain group information.
The grouping system can also be used to share game content, team ranking, decision-making consultation and other functions.

6.Multi-Language Support

Photon Services' multi-language support module can provide games in different languages. Game developers only need to configure language settings on the server side, and the client will automatically switch to display the corresponding language.

Steps:

  1. Configure server language settings;
  2. The client automatically switches languages;
using Photon.Pun;
public class LanguageManager : MonoBehaviourPun
{
    
    
   const byte DefaultLanguageCode = 0x01;
    public enum LanguageCodes : byte
   {
    
    
       English = 0x01,
       Spanish = 0x02,
       French = 0x03,
       German = 0x04,
       Italian = 0x05
   }
    byte languageCode;
    public bool ChangeLanguage(LanguageCodes langCode)
   {
    
    
       switch (langCode)
       {
    
    
           case LanguageCodes.English:
               languageCode = (byte) LanguageCodes.English;
               break;
           case LanguageCodes.Spanish:
               languageCode = (byte) LanguageCodes.Spanish;
               break;
           case LanguageCodes.French:
               languageCode = (byte) LanguageCodes.French;
               break;
           case LanguageCodes.German:
               languageCode = (byte) LanguageCodes.German;
               break;
           case LanguageCodes.Italian:
               languageCode = (byte) LanguageCodes.Italian;
               break;
       }
        SaveSettings();
       return true;
   }
    void LoadSettings()
   {
    
    
       // Read settings from file or database here
       languageCode = PlayerPrefs.GetInt("language", DefaultLanguageCode);
   }
    void SaveSettings()
   {
    
    
       // Write settings to file or database here
       PlayerPrefs.SetInt("language", languageCode);
   }
    void Start()
   {
    
    
       LoadSettings();
       photonView.RPC("ChangeLanguageRPC", RpcTarget.All, (LanguageCodes) languageCode);
   }
}

The above example shows the implementation of multi-language support. First, the default language encoding DefaultLanguageCode and the supported language encoding LanguageCodes are defined. The method to configure the server language settings is to modify the language configuration file and synchronize it to all clients. When the client starts, it calls the LoadSettings() function to read the current language settings, and notifies all clients to change the language through the photonView.RPC() function.
The switching process of client language setting is performed in the photonView.RPC() function. The server sends a "change language" command (ChangeLanguageRPC) to all clients. After receiving the command, each client calls the ChangeLanguage() function of the LanguageManager object to update the language settings. The method of saving settings can be implemented here.
Although Photon Services does not provide text translation tools, developers can use similar tools for localization development.

7. Chat System

The chat system is a real-time communication tool that allows players to communicate in the game through random text, voice, graphics, expressions, etc.

Steps:

  1. Create a chat room;
  2. Join a chat room;
  3. Leave the chat room;
  4. Send a message;
using Photon.Pun;
public class ChatManager : MonoBehaviourPun
{
    
    
   const int MessageHistoryLength = 100;
    public struct ChatMessage
   {
    
    
       public string sender;
       public string message;
        public ChatMessage(string sName, string msg)
       {
    
    
           sender = sName;
           message = msg;
       }
   }
    Dictionary<int, List<ChatMessage>> chatRooms = new Dictionary<int, List<ChatMessage>>();
    public bool JoinChatRoom(int roomId)
   {
    
    
       if (chatRooms.ContainsKey(roomId))
       {
    
    
           return false; // Already joined
       }
        chatRooms.Add(roomId, new List<ChatMessage>());
       return true;
   }
    public bool LeaveChatRoom(int roomId)
   {
    
    
       if (!chatRooms.ContainsKey(roomId))
       {
    
    
           return false; // Not in room
       }
        chatRooms.Remove(roomId);
       return true;
   }
    public bool SendMessage(int roomId, string message)
   {
    
    
       if (!chatRooms.ContainsKey(roomId))
       {
    
    
           return false; // No such room
       }
        var history = chatRooms[roomId];
       if (history.Count > MessageHistoryLength)
       {
    
    
           history.RemoveRange(0, history.Count - MessageHistoryLength);
       }
        var msg = new ChatMessage(photonView.Owner.NickName, message);
       history.Add(msg);
        SendMessageToOtherClients(roomInfo.Id, msg);
        return true;
   }
    void SendMessageToOtherClients(int roomId, ChatMessage message)
   {
    
    
       foreach (KeyValuePair<int, GameObject> kv in PhotonNetwork.CurrentRoom.Players)
       {
    
    
           if (kv.Key!= photonView.owner.ActorNumber)
           {
    
    
               kv.Value.GetPhotonView(this).RPC("ReceiveMessage",
                   RpcTarget.OthersBufferedViaServer, roomId, message);
           }
       }
   }
    [PunRPC]
   void ReceiveMessage(int roomId, ChatMessage message)
   {
    
    
       var messages = chatRooms[roomId];
       if (messages.Count > MessageHistoryLength)
       {
    
    
           messages.RemoveRange(0, messages.Count - MessageHistoryLength);
       }
        messages.Add(message);
       RenderMessagesInUI(roomId);
   }
    void RenderMessagesInUI(int roomId)
   {
    
    
       // Show messages in UI here
   }
}

The above example shows the implementation of the chat system. First, create a ChatMessage structure to save the sender and content of the chat message. A MessageHistoryLength constant is defined to limit the maximum length of message records.
Create a chat room JoinChatRoom() function checks whether a room has been joined through the room number roomId. If not, creates a new room and adds it to the chatRooms dictionary. Leave the chat roomLeaveChatRoom() function removes the room you are in through roomId.
Send a message The SendMessage() function finds whether the specified room exists through the roomId, and adds a new chat message to the message history list. Finally, call the SendMessageToOtherClients() function to send messages to other clients. After the client receives the message, it calls the ReceiveMessage() function to update the message history list, and calls the RenderMessagesInUI() function to render it on the UI.
The chat system can also be used for private chats, channel discussions, in-game chat rooms and other functions.

8. Data Storage

For online games, data storage is very important. It allows players to store game data such as saves, scores, settings, personal information, etc. The file storage interface provided by Photon Services can be used to store large file capacity at no extra charge.

Steps:

  1. File Upload;
  2. Download Document;
  3. Delete Files;
using Photon.Pun;
public class DataManager : MonoBehaviourPun
{
    
    
   public async Task UploadFile(byte[] data, string fileName)
   {
    
    
       var webRequest = UnityWebRequest.Put($"http://myserver.com/{
      
      fileName}", data);
       await webRequest.SendWebRequest();
        if (!webRequest.isHttpError &&!webRequest.isNetworkError)
       {
    
    
           Debug.Log("Upload successful!");
       }
       else
       {
    
    
           Debug.LogError("Upload failed! " + webRequest.error);
       }
   }
    public async Task DownloadFile(string fileName)
   {
    
    
       var webRequest = UnityWebRequest $"http://myserver.com/{
      
      fileName}";
       await webRequest.SendWebRequest();
        if (!webRequest.isHttpError &&!webRequest.isNetworkError)
       {
    
    
           var downloadedData = webRequest.downloadHandler.data;
           ProcessDownloadedData(downloadedData);
       }
       else
       {
    
    
           Debug.LogError("Download failed! " + webRequest.error);
       }
   }
    public bool DeleteFile(string fileName)
   {
    
    
       var filePath = Path.Combine(Application.persistentDataPath, fileName);
       File.Delete(filePath);
       return true;
   }
}

The above example shows the implementation of data storage. The file upload UploadFile() function asynchronously sends an HTTP PUT request through the UnityWebRequest object and waits for the response. File download DownloadFile() function asynchronously sends an HTTP GET request through the UnityWebRequest object, waits for the response, and reads the response data. The ProcessDownloadedData() function is used to process the downloaded data. Delete file The DeleteFile() function deletes the specified file through the local file system.
There are many areas where the file storage interface can be expanded, such as file search, file sharing, version control, permission management, etc.

Guess you like

Origin blog.csdn.net/universsky2015/article/details/132158072