Unity/Unreal Online

Connecting Unreal and Unity

Teacher VR

01/2025 - 04/2025

Unity, Unreal, C#

A student project prototype of a capture-the-flag game, where Unreal and Unity clients connect through a server using sockets.

Mechanics

  • Shoot other color team
  • Capture enemy flags and score in your zone.
  • Lobby

Game Info

It is a capture-the-flag game where sockets were used to connect clients built in both Unreal and Unity engines, transmitting information to a C# server, which then updates all connected clients.

How server works

When a client connects, the server stores it in a dictionary. The server then waits for new packets, filters them using a switch with enums, and sends the changes to all other clients.

Server main loop code

                            switch (packetType)
                            {
                                case PacketType.WELCOME:
                            
                                    //This will show the senders/clients IP Address and Port Number
                                    Console.WriteLine("Message received from " + stringSplit[1] + " ip:" + remote.ToString());
                            
                            
                                    string hi = ((int)PacketType.WELCOME) + ";Yep, you just connected!";
                            
                                    data = Encoding.ASCII.GetBytes(hi);
                            
                                    // First parameter is the data, 2nd is packet size, 3rd is any flags we want,
                                    // and 4th is destination client (which was saved when ReceiveFrom() is called)
                                    newsock.SendTo(data, data.Length, SocketFlags.None, remote);
                            
                                    IPEndPoint ePoint = (IPEndPoint)remote;
                                    isNewClient(ePoint);
                            
                                    break;
                            
                                case PacketType.UID:
                            
                                    // Asign unique ID to clients
                                    break;
                                case PacketType.UPDATE:
                            
                                    // Update all the transforms
                                    // After the switch all transforms are sent to the clients
                            
                                    break;
                                case PacketType.HIT:
                                    {
                                       // Hit event Handle
                                    }
                                    
                                case PacketType.FLAG:
                                    {
                                        int client_uniqueid = int.Parse(stringSplit[1]);
                                        int flagEvent = int.Parse(stringSplit[2]); // PickUp, Drop, Return
                                        int team = int.Parse(stringSplit[3]);
                            
                                        byte[] packetdata = new byte[4096];
                                        string data_HIT_send = ((int)PacketType.FLAG) + ";" + client_uniqueid + ";" + flagEvent + ";" + team;
                                        packetdata = Encoding.ASCII.GetBytes(data_HIT_send);

                                        SendDataToClients(packetdata);
                                        
                                        break;
                                    }
                                }
                            

How client works

Packet sending on the client side works as follows: when a client performs an action, it sends a packet to the server indicating which event has occurred.

Unity Capture Flag Code

                        // Only players can enter OnTrigger
                        private void OnTriggerEnter(Collider other)
                        {
                            NetworkGameObject player = other.GetComponent();
                      
                            if (!player)
                                return;
                      
                            if (!FlagInPosition() || !player.isLocallyOwned)
                                return;
                      
                            if (player.GetTeam() != zone_flag_.GetTeam())
                            {
                                PickFlag(player);
                            }
                            else
                            {
                                if (player.own_flag_.activeSelf)
                                    ReturnFlag(player);
                            }
                            
                        }
                        
                        public void PickFlag(NetworkGameObject nt)
                        {
                            nt.PickFlag(zone_flag_.GetTeam());
                            zone_flag_.gameObject.SetActive(false); 
                      
                            // Send a network message to notify all players about the flag pickup
                            string flagMessage = (int)PacketType.FLAG + ";" + nt.uniqueNetworkID + ";" + (int)FlagEvent.PICKUP + ";" + (int)zone_flag_.GetTeam();
                            byte[] hitPacket = Encoding.ASCII.GetBytes(flagMessage);
                      
                            NetworkManager.udpClient.Send(hitPacket, hitPacket.Length);
                        }
                          

When the client's network manager receives the event, it will be filtered using a switch with enums, and it will call the components that have been affected.

Unreal Capture Event Flag Code

                      // Network Manager class

                          switch (packetInt)
                          {
                            case EPacketType::WELCOME:
                              UE_LOG(LogTemp, Warning, TEXT("Mesage: %s"), *newString);

                              break;

                            case EPacketType::UID:
                            {
                              // Assign unique IDs to network objects
                              int32 local_ID = FCString::Atoi(*stringSplit[1]);
                              int32 global_ID = FCString::Atoi(*stringSplit[2]);
                              ETeam team = (ETeam)FCString::Atoi(*stringSplit[3]);

                              AssignUIDS(local_ID, global_ID, team);

                              break;
                            }

                            case EPacketType::UPDATE:
                            {
                              // Handle position and state updates for networked objects
                            }
                            case  EPacketType::HIT:
                            {
                              // Handle damage events

                            }
                            // Handle Flag events
                            case  EPacketType::FLAG:
                            {

                              int32 globalID = FCString::Atoi(*stringSplit[1]);
                              EFlagEvent flagEvent = (EFlagEvent)FCString::Atoi(*stringSplit[2]);
                              ETeam team = (ETeam)FCString::Atoi(*stringSplit[3]);

                              UNetworkObject* flag_player = GetNetworkObject(globalID);
                              flag_manager_->FlagEvents(flag_player, flagEvent, team);

                              break;
                            }

                        // Flag Manager class

                            void AFlagManager::FlagEvents(UNetworkObject* flagPlayer, EFlagEvent eventFlag, ETeam team) {

                              int32 t = (int32)team;
                              switch (eventFlag)
                              {
                                case EFlagEvent::PICKUP:
                            
                                    flag_zone_[t]->Activate(false);
                                    flagPlayer->PickUpFlag();
                                    break;
                                case EFlagEvent::DROP:
                                    flag_zone_[t]->Activate(true);
                                    flagPlayer->ReturnFlag();
                            
                            
                                    break;
                                case EFlagEvent::RETURN:
                                    flag_zone_[t]->Activate(true);
                                    flagPlayer->ReturnFlag();
                            
                                    points_->Score(flagPlayer->team_);
                            
                                    break;
                              }
                          }