Unity 一起看+弹幕 Socket通信

效果演示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实现功能

  • 发送弹幕
  • 连接通信
  • 共同更换视频

服务端

实现通信功能,我选择的是Scoket通信。

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace ChatRoomService
{
    
    
    class Service
    {
    
    
        Socket socketSevice ;
        List<Socket> userList;//用户组
        public Service()
        {
    
    
           socketSevice = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
           userList = new List<Socket>();
        }

        public void  Start()
        {
    
    
            socketSevice.Bind(new IPEndPoint(IPAddress.Any,10000));//这里设置任何ip都可以连接
            socketSevice.Listen(10);
            Console.WriteLine("服务器启动成功");

            //开启接受连接,用多线程
            Thread accThread = new Thread(Accept);
            accThread.IsBackground = true;
            accThread.Start();
        }

        private void Accept()
        {
    
    
            //接受连接
            Socket clientSocket = socketSevice.Accept();
            userList.Add(clientSocket);
            //打印已经连接IP地址
            Console.WriteLine(IPToAddress(clientSocket)+"连接进来了");

            //
            Thread RecvThread = new Thread(ReceMessage);
            RecvThread.IsBackground = true;
            RecvThread.Start(clientSocket);

            Accept();//递归
        }
        //接收客户端信息
        private void ReceMessage(Object obj)
        {
    
    
            Socket client = obj as Socket;
            byte[] strByte = new byte[1024];//设定接受字符的长度
            string str = "";
            try
            {
    
    
                int len = client.Receive(strByte);//接受用户发送的内容
                str = Encoding.UTF8.GetString(strByte, 0, len);
                if (str != "")
                {
    
    
                    Broadcast(str,client);//广播给用户
                    Console.WriteLine(str);
                }
            }
            catch (Exception e)
            {
    
     
                Console.WriteLine(IPToAddress(client)+"退出");
                userList.Remove(client);
                Thread.CurrentThread.Abort();//退出时掐死线程,不然递归反弹
            }
            ReceMessage(client); //使用递归
        }

        /// <summary>
        /// 广播信息
        /// </summary>
        /// <param name="useStr">传入收到的传输的内容</param>
        /// <param name="obj">传送信息的客户</param>
        private void Broadcast(string userStr,object obj)
        {
    
    
            Socket clientSend = obj as Socket; //当前发送信息的客户
            foreach (Socket client in userList)
            {
    
    
                if (client != clientSend)//将信息广播给其他用户
                {
    
    
                    client.Send(Encoding.UTF8.GetBytes(userStr));
                }
            }
        }

        //转换出连来客户的IP地址
        private string IPToAddress(Socket soket)
        {
    
    
            return (soket.RemoteEndPoint as IPEndPoint).Address.ToString();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ChatRoomService
{
    
    
    class Program
    {
    
    
        static void Main(string[] args)
        {
    
    
            Service ss = new Service();
            ss.Start();
            Console.ReadLine();
        }
    }
}

客户端

UI界面

在这里插入图片描述

Control

我将UI都放在Control空物体下,方便添加脚本统一管理。
RawIamge:用来播放视频,添加VideoPlayer组件
在这里插入图片描述
这里的TargetTexture是一个RenderTexture
在这里插入图片描述
记住这里的RenderTexture和RawImage大小设置成一样

SendMessage :是一个空物体,子物体是一个InputFiled用来读取输入的信息,还有一个Button用来发送信息
Last:上一集按钮
Next:下一集按钮
Together:一起看按钮

Panel

我们发送弹幕,弹幕都是在视频的上端,所以我就加一个Panel让在RawImage的上端位置,并且弹幕是从头出来才显示,到达另一端逐渐消失,所以为了达到这个效果
我的想法是每发送一条弹幕,我就生成一个Text从Panel右到左移动,所以Panel我家一个Mask组件达到遮罩效果,并且将生成Text做一个对象池,减小性能的消耗。并且Panel的Corol的透明度需要设置透明,但是不能为0,因为我要将Text成为Panel的子物体,方便管理,如果设置为0,Text就看不到了。这里必须将Text设置成Panel的子物体,因为Mask组件只能遮罩子物体。
我将Text设置成了预制体。在这里插入图片描述

脚本

MessageFire

这个脚本最简单,我们给message这个预制体
在这里插入图片描述
我这个Message初始化位置就在这里,这样Message向左移动直接就会显示出来

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MessageFire : MonoBehaviour
{
    
    
    private int StartPosX = 1300;//初始化Message的位置,当然这个需要你根据你的项目自行设置
    private int EndPosX = -1300;//同理这个是消失位置,x坐标达到-1300就会返回对象池中
    private float speed = 150f;//设置的速度,自定义
    
    
    void Start()
    {
    
    
        transform.localPosition=new Vector3(StartPosX,0,0);//当我们从对象池拿到后初始化他的位置
    }

    // Update is called once per frame
    void Update()
    {
    
    
    	//Message移动,到终点返回对象池
        transform.localPosition=new Vector3(transform.localPosition.x-speed*Time.deltaTime,0,0);
        if(transform.localPosition.x<EndPosX)
            BarrageManager.GetInstace().RecyleMessageText(gameObject.GetComponent<Text>());
    }
}

对象池BarrageManager

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class BarrageManager : MonoBehaviour
{
    
    
    
     private int index = 0;//返回对对象池中的第index个对象
     private GameObject message;//预制体
     private List<GameObject> messageLists=new List<GameObject>();
     private static BarrageManager Instance;//单例模式
    
    public static BarrageManager GetInstace 
    {
    
    
        get
        {
    
    
            return Instance;
        }
    }

    private void Awake()
    {
    
    
        Instance = this;
        message = Resources.Load<GameObject>("Prefab/Message");//获取预制体
    }

    private void Start()
    {
    
    
        InitiMessageText();//初始化对象池
    }

    
    /// <summary>
    /// 返回一个Message
    /// </summary>
    /// <returns></returns>
    public GameObject GetMessageText()
    {
    
    
        //这里我是按照对象池顺序,每次返回一个index就自加,这样就会每次判断对象池的下一个,这样我基本只会遍历判断一次就可以拿到没有显示的Message
        //不是从头遍历,这样太消耗性能
        for (int i = index% messageLists.Count; i < messageLists.Count; i++)
        {
    
    
            if (!messageLists[i].activeSelf)
            {
    
    
                //这里是判断i是否和index相等,按道理我们的i和index相同,但是有可能对象池没有禁用的,没有可以拿的,
                //但是我们已经将index设置为0,但是对象池添加了10个,
                //我们拿到的是第11个,下标i=10,所以导致i!=index,所以当不相等时我们将index=i
                if (index != i)
                    index = i;
                index++;
                if (index == messageLists.Count)//默认一般情况当index等于对象池的个数就从头开始
                    index = 0;
                return messageLists[i];
            }
        }
        
        InitiMessageText();

        return GetMessageText();//我们采用递归方式来进行返回一个Message
    }

    /// <summary>
    /// 回收Message
    /// </summary>
    /// <param name="messagetext">我们的回收的是一个Text</param>
    public void RecyleMessageText(Text messagetext)
    {
    
    
        messagetext.gameObject.transform.localPosition=Vector3.zero;
        messagetext.gameObject.SetActive(false);
    }
    
    
    
    /// <summary>
    /// 初始化10个MessageText做话框
    /// </summary>
    void InitiMessageText()
    {
    
    
        for (int i = 0; i < 10; i++)
        {
    
    
            GameObject text=Instantiate(message);
            text.transform.SetParent(transform);
            text.SetActive(false);
            messageLists.Add(text);
        }
    }
}

ChatRoom客户端连接

//这里我们还没有连接,我们需要生成一个ChatRoom才会连接,所以我们当点击一起看按钮就会实现连接功能

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

namespace PXZ
{
    
    
    public class ChatRoom
    {
    
    
        public string message = "";//接收到的信息,方便我们判断是否更换视频
        public Socket clientSocket;//当我们关闭程序关闭连接

        public ChatRoom()
        {
    
    
            clientSocket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
        }

        public void Connected(string IP, int port)
        {
    
    
            clientSocket.Connect(IP, port);
            
            Thread RecThread=new Thread(RecvMessage);
            RecThread.IsBackground = true;
            RecThread.Start();
        }

        //发送信息
        public void Send(string str)
        {
    
    
            if(str!="")//防止发空信息
                clientSocket.Send(Encoding.UTF8.GetBytes(str));
        }

        //接受信息
        private void RecvMessage()
        {
    
    
            try
            {
    
    
                byte[] strByte=new byte[1024];
                int len = clientSocket.Receive(strByte);
                message = Encoding.UTF8.GetString(strByte, 0, len);
            }
            catch (Exception e)
            {
    
    
                Console.WriteLine(e);
                Thread.CurrentThread.Abort();
                throw;
            }
            RecvMessage();
        }
    }
}

ViewVideo

using System;
using System.Collections;
using System.Collections.Generic;
using PXZ;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
using UnityEngine.Video;

public class ViewVideo : MonoBehaviour
{
    
    
    #region 单例模式

    private static ViewVideo Instance;

    public static ViewVideo GetInstance
    {
    
    
        get
        {
    
    
            return Instance;
        }
    }

    #endregion
    
    
    //添加更换视频的片段
    public List<VideoClip> clips=new List<VideoClip>();

    
    public bool connect = false;//查看时候连接,因为连接和不连接我们的按钮发送的信息不同
    private int index = 0;//视频下标
    
    //都是Control的子物体获取组件
    private VideoPlayer videoPlayer;
    private Button last;
    private Button next;
    private Button send;
    private Button togerher;
    public ChatRoom client;
    private void Awake()
    {
    
    
        Instance = this;
        
        videoPlayer = transform.GetChild(0).GetComponent<VideoPlayer>();
        last = transform.GetChild(2).GetComponent<Button>();
        next = transform.GetChild(3).GetComponent<Button>();
        togerher = transform.GetChild(4).GetComponent<Button>();
        last.onClick.AddListener(LastButtonOnclik);
        next.onClick.AddListener(NextButtonOnclik);
        togerher.onClick.AddListener(TogetherButtonOnclik);

    }


    private void Update()
    {
    
    
        //当我们连接后才执行
        if (connect)
        {
    
    
            //判断我们接受的信息不空也不是1和0我们就认为是一条弹幕,直接发送弹幕函数
            if (client.message != "" && client.message!="1"&& client.message!="0")
            {
    
    
               SendMessageFire.Instace.TextFire(client.message);
            }
            
            //这里我们收到的信息是1和0后我们就默认是更换视频
            if (client.message == "1" || client.message=="0")
            {
    
    
                ViewVideo.GetInstance.ViewPlayerChange(Convert.ToInt32(client.message));
            }
            client.message = "";//再讲我们收到的信息设置为为空,防止两个判断一致发生
        }
    }

    private void OnDestroy()
    {
    
    
        //关闭连接
        if (client != null)
        {
    
    
            client.clientSocket.Close();
        }
    }

    /// <summary>
    /// 我们这里更换视频有两种情况,当我们点击一起看连接后,和一种自己看
    /// 当连接后我们发送一条1,接收方收到后就会根据信息来判断是不是更换视频了
    /// 不连接啧index+1并更换视频
    /// </summary>
    private void NextButtonOnclik()
    {
    
    
        if (connect)
        {
    
    
            client.Send("1");
        }
        index++;
        if (index == clips.Count)
            index = 0;
        videoPlayer.clip = clips[index];
    }

    private void LastButtonOnclik()
    {
    
    
        if(connect)
        {
    
    
            client.Send("0");
        }
        index--;
        if (index < 0)
            index = clips.Count - 1;
        videoPlayer.clip = clips[index];
    }

    /// <summary>
    /// 我们需要判断是client是否为空,防止点击多次Together按钮生成多个client;
    /// </summary>
    private void TogetherButtonOnclik()
    {
    
    
        if (client == null)
        {
    
    
            client=new ChatRoom();
            client.Connected("127.0.0.1",10000);
            connect = true;
        }
    }
    
    /// <summary>
    /// 这个是连接成功后更换视频的函数
    /// </summary>
    /// <param name="x">判断接收的是1还是0</param>
    public void ViewPlayerChange(int x)
    {
    
    
            if (x == 1)
            {
    
    
                index++;
                if (index == clips.Count)
                    index = 0;
            }

            if (x == 0)
            {
    
    
                index--;
                if (index < 0)
                    index = clips.Count - 1;
            }
            videoPlayer.clip = clips[index];
    }
}

SendMessageFire

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SendMessageFire : MonoBehaviour
{
    
    

    #region 单例模式

    private static SendMessageFire instace;

    public static SendMessageFire Instace
    {
    
    
        get
        {
    
    
            return instace;
        }
    }

    #endregion
    
    //获取组件
    private InputField messageInput;
    private Button sendButton;
    private string message;

    
    private void Awake()
    {
    
    
        instace = this;
        messageInput = transform.GetChild(0).GetComponent<InputField>();
        sendButton = transform.GetChild(1).GetComponent<Button>();
        sendButton.onClick.AddListener(SendButtonOnclik);
    }

    

    /// <summary>
    /// 判断是否连接服务器,连接的话就给服务器发送信息,没有就直接发送一条弹幕
    /// </summary>
    void SendButtonOnclik()
    {
    
    
        message = messageInput.text;
        messageInput.text = "";
        if (message != "")
        {
    
    
            if (ViewVideo.GetInstance.connect)
            {
    
    
                ViewVideo.GetInstance.client.Send(message);
            }
            
            TextFire(message);
        }
    }

    /// <summary>
    /// 发送弹幕函数
    /// </summary>
    /// <param name="str">发送的信息</param>
    public void TextFire(string str)
    {
    
    
        GameObject messageObject = BarrageManager.GetInstace.GetMessageText();
        Text messageText= messageObject.GetComponent<Text>();
        messageText.text = str;
        messageObject.SetActive(true);
    }
}

脚本添加

Control添加ViewVideo脚本,并给Clips初始化添加视频
在这里插入图片描述

SendMessage
在这里插入图片描述
Panel
在这里插入图片描述

问题

  • 当两人观看视频不一致的时候,两者点击一起看,再点击上一集或下一集,但是无法保持观看视频一致
  • 当两人连接一起看后,一旦一方断开连接关闭程序,服务器就会崩坏。
  • 两人一起看,两人都可以更换视频,没有做到只有管理员才能更换视频。没有管理员功能
    但是总体功能都已经实现

猜你喜欢

转载自blog.csdn.net/m0_48554728/article/details/121522807