添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在使用Unity进行Socket通信的时候使用了线程,在接收到消息后再创建物体,使用了Resource.Load,发生了错误:

UnityEngine.UnityException: Load can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

也就是说子线程不能调用Unity的方法,要在主线程执行。

找了几个解决方案,都是返回给主线程去执行方法。

Unity关于有些代码不能在除主线程外的线程使用的问题
https://blog.csdn.net/qq_34818497/article/details/79464793

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
namespace LX.Common.Threading {
    /// <summary>
    /// 线程调度,将其它线程中的代码放置在主线程中执行
    /// </summary>
    public sealed class Dispatcher : MonoBehaviour {
        private static Dispatcher instance;
        private int _lock;
        private bool _isRun;
        private Queue<Action> _queWaitAction = new Queue<Action>();
        private Queue<Action> _queExecute;
        public static void Run(Action action) {
            instance.Runner(action);
        private void Runner(Action action) {
            while (true) {
                //以原子操作的形式,将 32 位有符号整数设置为指定的值并返回原始值。
                if (0 == Interlocked.Exchange(ref _lock, 1)) {
                    //acquire lock
                    _queWaitAction.Enqueue(action);
                    _isRun = true;
                    //exist
                    Interlocked.Exchange(ref _lock, 0);
                    break;
        private void Awake() {
            instance = this;
            DontDestroyOnLoad(this.gameObject);
        private void Update() {
            if (_isRun) {
                _queExecute = null;
                //主线程不推荐使用lock关键字,防止block 线程,以至于deadlock
                if (0 == Interlocked.Exchange(ref _lock, 1)) {
                    _queExecute = new Queue<Action>(_queWaitAction.Count);
                    while (_queWaitAction.Count != 0) {
                        _queExecute.Enqueue(_queWaitAction.Dequeue());
                    //finished
                    _isRun = false;
                    //release
                    Interlocked.Exchange(ref _lock, 0);
                //not block
                if (_queExecute != null) {
                    while (_queExecute.Count != 0) {
                        _queExecute.Dequeue()();
//用法: LX.Common.Threading.Dispatcher.Run(() => {/*函数体*/})

调用的脚本把class里面的替换进去,包括改写的Awake和Update方法,修改原本的类名变量Dispatcher

完整代码,Unity是服务端:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System;
public class Socket_server : MonoBehaviour
    private static Socket_server _current;
    // Start is called before the first frame update
    void Start()
        Thread thread = new Thread(Listen);
        thread.IsBackground = true;
        thread.Start();
    /// <summary>
    /// 服务器监听消息
    /// </summary>
    void Listen()
            //建立udp服务器,参数2:udp协议以数据报的方式传输,参数3:UDP协议
            Socket udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            //为服务器绑定IP
            string ip_ = Socket_GetLocalIp();
            Debug.Log(ip_);
            IPAddress ip = IPAddress.Parse(ip_);
            EndPoint ep = new IPEndPoint(ip, 3457);
            udpServer.Bind(ep);
            //接收数据
            EndPoint endP = new IPEndPoint(IPAddress.Any, 0);
            string message;
            byte[] data = new byte[1024];
            int length = 0;
            //把数据的来源放到第二个参数上
            while (true)
                length = udpServer.ReceiveFrom(data, ref endP);
                message = Encoding.UTF8.GetString(data, 0, length);
                Debug.Log("从IP:" + (endP as IPEndPoint).Address + "取到了消息:" + message);
                Socket_server.Run(() => { makeSun(); });
        catch(System.Exception e)
            Debug.LogError(e);
            throw;
    public void makeSun()
        //生成动画
        Image sun;
        Image obj_sun = Resources.Load<Image>("sun 1");
        sun = GameObject.Instantiate(obj_sun);
        sun.GetComponent<Transform>().SetParent(GameObject.Find("Canvas").GetComponent<Transform>(), true);
    /// <summary>
    /// 获取本地的IP地址
    /// </summary>
    /// <returns></returns>
    public string Socket_GetLocalIp()
        string AddressIP = string.Empty;
        foreach (IPAddress _IPAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
            if (_IPAddress.AddressFamily.ToString() == "InterNetwork")
                AddressIP = _IPAddress.ToString();
        return AddressIP;
    private static Socket_server instance;
    private int _lock;
    private bool _isRun;
    private Queue<Action> _queWaitAction = new Queue<Action>();
    private Queue<Action> _queExecute;
    public static void Run(Action action)
        instance.Runner(action);
    private void Runner(Action action)
        while (true)
            //以原子操作的形式,将 32 位有符号整数设置为指定的值并返回原始值。
            if (0 == Interlocked.Exchange(ref _lock, 1))
                //acquire lock
                _queWaitAction.Enqueue(action);
                _isRun = true;
                //exist
                Interlocked.Exchange(ref _lock, 0);
                break;
    private void Awake()
        instance = this;
        DontDestroyOnLoad(this.gameObject);
    private void Update()
        if (_isRun)
            _queExecute = null;
            //主线程不推荐使用lock关键字,防止block 线程,以至于deadlock
            if (0 == Interlocked.Exchange(ref _lock, 1))
                _queExecute = new Queue<Action>(_queWaitAction.Count);
                while (_queWaitAction.Count != 0)
                    _queExecute.Enqueue(_queWaitAction.Dequeue());
                //finished
                _isRun = false;
                //release
                Interlocked.Exchange(ref _lock, 0);
            //not block
            if (_queExecute != null)
                while (_queExecute.Count != 0)
                    _queExecute.Dequeue()();

客户端WindowsForm窗口,udp发送消息:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
namespace WindowsFormsApp1_socket1_client
    public partial class Form1 : Form
        private Socket udpClient;//创建客户端
        private IPAddress ip;
        private EndPoint ep;
        private byte[] data = new byte[2048];//用来接收和发送数据
        private int length = 0;
        public Form1()
            InitializeComponent();
        private void button1_Click(object sender, EventArgs e)
            Socket_SendMessageToServer();
        private void Form1_Load(object sender, EventArgs e)
            Socket_UDPInit();
        public void Socket_SendMessageToServer()
            string message;
            Console.Write("Send Message");
            message = "tset";
            data = Encoding.UTF8.GetBytes(message);
                int returnnum = udpClient.SendTo(data, ep);
            catch (Exception e)
                Console.Write(e);
                throw;
        public string Socket_GetLocalIp()
            ///获取本地的IP地址
            string AddressIP = string.Empty;
            foreach (IPAddress _IPAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
                if (_IPAddress.AddressFamily.ToString() == "InterNetwork")
                    AddressIP = _IPAddress.ToString();
            return AddressIP;
        private void Socket_UDPInit()
            //建立udp客户端,参数2:udp协议以数据报的方式传输,参数3:UDP协议
            udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            //发送信息准备工作
            String a = Socket_GetLocalIp();
            ip = IPAddress.Parse(a);
            ep = new IPEndPoint(ip, 3457);
                    在使用Unity进行Socket通信的时候使用了线程,在接收到消息后再创建物体,使用了Resource.Load,发生了错误:UnityEngine.UnityException: Load can only be called from the main thread.Constructors and field initializers will be executed from the loading thread when loading a scene.Don't use this func
The Principles of Object-Oriented JavaScript 1st Edition
If you've used a more traditional object-oriented language, such as C++ or Java, JavaScript probably doesn't seem object-oriented at all. It has no concept of classes, and you don't even need to define any objects in order to write code. But don't be fooled—JavaScript is an incredibly powerful and expressive object-oriented language that puts many design decisions right into your hands.
In The Principles of Object-Oriented JavaScript, Nicholas C. Zakas thoroughly explores JavaScript's object-oriented nature, revealing the language's unique implementation of inheritance and other key characteristics. You'll learn:
–The difference between primitive and reference values
–What makes JavaScript functions so unique
–The various ways to create objects
–How to define your own constructors
–How to work with and understand prototypes
–Inheritance patterns for types and objects
The Principles of Object-Oriented JavaScript will leave even experienced developers with a deeper understanding of JavaScript. Unlock the secrets behind how objects work in JavaScript so you can write clearer, more flexible, and more efficient code.
				
类和对象;学习目标;什么是对象(object);类(class);创建类;特别注意;创建引用变量;构造方法(Constructor);问题;Constructors(构造方法);构造方法的作用;调用构造方法创建对象;对象的内存模型;定义多个构造方法;使用对象;使用对象;1、 radius变量是Cirlce类的成员变量,语句中使用radius时指的是当前对象的radius 。 2、什么是当前对象?谁调用getArea方法,谁就是getArea方法的当前对象 c1.getArea()时,c1就是getArea方法的当前对象,语句里使用的是c1的radius c2.getArea()时,c2就是getArea方法的当前对象,语句里使用的??c2的radius;练习1;练习2;练习3;练习4;成员变量的默认值;成员变量的默认值;局部变量没有默认的初值;class Circle{ double radius; Circle(double radius) { this.radius = radius; };class Circle{ private double radius;
Unity 5 From Zero to Proficiency (Intermediate): A step-by-step guide to coding your first game in C# with Unity. Why this book can help you to get started with Game Development Creating your own game can be very intimidating at the start, and quite often, regardless of your experience with games, it is sometimes difficult to find the time and motivation to get over the first barriers and to get started. Often, these barriers seem higher than they actually are. Maybe you are a teacher trying to introduce games in your classroom or a parent trying to help your child with coding, but with no previous coding or game development experience; maybe you are a hobbyist who would love to create interactive environments based on your favorite games; maybe you are a student getting started with game development but you just don't know where to start or what resources to use; or maybe you have tried online video tutorials but found them disjointed. You may be wondering: "How can I start to create my games if I have no experience of coding", or "this game engine is so complex that I just don't know where to get started". This is quite common, and you can easily overcome these issues with a step-by-step approach that gets you to progressively develop and use your skills. This is the approach that I have used successfully over the past years to take students from no knowledge of coding or game development to good levels of proficiency in Unity and coding. Of course, it takes some time and dedication; however, by following the techniques and suggestions described in this book, I can promise you that you will progress, regardless of your background, and become more comfortable with Unity and coding. Content and structure of this book In this book, the third book in the series, you will become comfortable with programming in C# by creating a simple survival game in Unity. The book includes: A list of the learning objectives at the start of each chapter. Step-by-step activitie
unity内部封装了一个很高层次的网络接口,不过有时候并不需要那么复杂的功能,只是想实现简单的TCP网络连接,可以在unity里面用C#写tcp socket。 以下TCP连接是同步模式。 建立两个unity工程,编写脚本。 using UnityEngine; using System.Collections; //引入库 using System.Net;
通常的Socket都是通过多线程的方式来实现的,多线程需要确保线程安全,而且代码量也会相对多一些,由于之前已经实现了Unity的协程功能,现在就可以通过协程来实现单线程Socket了。 首先,封装一下C#的Socket。using System; using System.Collections.Generic; using System.Linq; using System.Text;
Socketunity中实现网络功能往往需要使用socket,而socket是阻塞的,如果直接在start或者update里new出来了一个socket来用,那么在使用它的过程中当它阻塞的时候(比如accept) 整个程序就是锁死的 。 为了不产生这种影响,就需要使用非阻塞的Socket。 官方提供了一些非阻塞的方案来选择,但是这些方案都带来的相同的新问题,那就是它们都不在Unity线程中...
Load can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, instead move i
1.客户端尝试使用Disconnect方法动断开连接 Socket当中有一个专门在客户端使用的方法(Disconect方法)客户端调用该方法和服务器端断开连接服务端: 1.收发消息时判断socket是否已经断开 2.处理删除记录的socket的相关逻辑(会用到线程锁) 客户端: 动断开连接 2.自定义退出消息 让服务器收到该消息就知道是客户端想要动断开 然后服务器处理释放socket
Socket 包含本地和远程的机IP和端口号,以及网络协议。端口号0~65535 Socket中Connect、Send、Receive均为阻塞方法 书中服务端代码没有缓存Socket导致客户端重新发送信息,然后等待回复阻塞。 第二章 异步和多路复用 书中利用BeginConnect连接和BeginReceive异步接收信息。里面的回调函数是在另一个线程中。是访问不到Unity线程的东西的,会产生你更新不了Update的Text问题。如下图 服务端中,利用BeginAc.
第二章节>>现学现卖掌握快唠嗑一下。大学毕竟不全是荒度。“查找文献->综述->论文”这个“三段式”是记住了,感谢胡导师!实现思路: 服务端是需要一直监听的,所以监听需要用一个线程(thread)来单独处理,然后在线程里处理客户端的发送过来的数据。while(true)永真式保证监听的持续性,Socket里面的accept阻塞方法让该循环不至于为死循环,剩下的就是API的调用。 (代码来源:ht
本篇要是分享基于unity的客户端socket网络通信方案。关于服务器的c#-socekt搭建放在了这里《基于C#的Tcp服务端通信》。其中关于socekt粘包断包的处理放在这里分享了《C# socket粘包断包处理》。 如图所示,一共采用了两层封装来处理整个客户端的逻辑。 首先TcpClient脚本只处理最基础的连接建立,消息的发送和接收。 TcpClientMgr管理和穿件TcpClient。在利用好连接建立,消息收发的基础上再处理业务上的需求:心跳、消息等待、事件传递等。
<h3>回答1:</h3><br/>Android与Unity之间的交互可以实现在Unity运行时,调用Android原生代码来实现一些功能。在Android端可以使用 Android Java API 调用 Unity 的代码。而在Unity端,可以使用 C# 代码来调用 Android 的 Java API。这种双向调用可以实现一些比较复杂的跨平台应用,例如在Unity中展示Android硬件设备的信息或者调用相机、传感器等硬件设备来实现某些功能。 例如,在Android端上获取某个传感器的数据,然后传到Unity中,就可以借助Java Native Interface(JNI)来实现。具体实现方式如下: 1. Android端: * 创建一个Native方法,在其中读取传感器数据; * 创建一个Java类,将该Native方法和Unity相关的方法绑定; * 在Unity中调用Java类中的方法,以获取传感器数据。 2. Unity端: * 在C#代码中,调用Android的Java API,以实现数据传输; * 实现Unity的渲染逻辑,以展示从Android端传输过来的数据。 需要注意的是,为了确保代码的正确性和稳定性,一定要在调用之前好详细的测试工作。 <h3>回答2:</h3><br/>Android和Unity是两个流行的软件开发平台,它们都具有广泛的应用。将它们结合起来,可以为应用开发者带来无限的可能性。 在android与unity交互过程中,最要的问题是如何将android中的数据传递到unity中。这要分为两种情况:一种是在Unity中使用安卓API(Java代码),另一种是安卓使用Unity中的可编程元素(C#代码)。 在第一种情况下,可以使用Unity的Java接口来实现,编写类似于以下的Java代码: UnityPlayer.UnitySendMessage("GameObjectName", "MethodName", "Message"); 其中,UnitySendMessage方法将消息发送给Unity中的一个GameObject,它接受两个参数,分别是接受消息的对象名和它上面的方法名。 在第二种情况下,可以使用Unity的C#接口来实现。我们可以使用Unity中的SendMessage或BroadcastMessage方法来向Unity对象发送消息(这些对象必须具有MonoBehaviour脚本,否则将不会工作)。 在Android中使用Unity对象也同样简单。我们需要的就是在Android项目中添加UnityPlayerActivity类,在此类中使用UnityPlayer类的相关方法调用Unity导出的API。 综上所述,将安卓与Unity结合起来,可以极大地扩展应用的功能性,并为用户提供更好的体验。通过上述方法可以轻松实现android与unity交互。 <h3>回答3:</h3><br/>Android与Unity交互是现在游戏开发中非常常见的一种技术,Android作为手机操作系统的代表,提供了丰富多彩的开发接口,而Unity作为一款游戏引擎,具有着强大的游戏制作功能。两者合作可以大大提高游戏的交互性和玩法。下面我们来分别了解一下Android和Unity之间的交互方式。 首先是从Android到Unity交互。由于Android系统是Java语言开发的,而Unity则是C#开发的,所以两者之间实现交互还需要一些中间的桥梁。目前比较常用的方式是通过JNI接口,将Java层面的信息传递到C#层面的Unity中。在Java中,你需要先获取UnityPlayer的实例,然后就可以通过UnityPlayer的方法来调用C#端的函数。具体流程如下: 1. 在Unity中编写对应的C#函数,该函数必须使用静态修饰符(static)。例如: public static void UnityMethod(string str){ Debug.Log("接收到的数据为:" + str); 2. 在Java中,使用JNI接口调用C#函数。例如: // 获取UnityPlayer实例 UnityPlayer player = new UnityPlayer(); // 调用C#函数 player.UnitySendMessage("GameObject名字", "函数名字", "传递的参数"); UnitySendMessage方法中,第一个参数表示的是GameObject的名字,第二个参数表示的是C#函数的名字,第三个参数就是传递的参数。 其次是从Unity到Android的交互。在Unity中,通过AndroidJavaClass和AndroidJavaObject等API,可以实现调用Java层面的方法。具体流程如下: 1. 在Java中,编写需要调用的方法,这里的方法必须是public静态的。例如: public static void showAndroidToast(String msg){ Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); 2. 在Unity中,使用AndroidJavaClass和AndroidJavaObject等API来调用Java方法。例如: // 设置Context AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); // 调用Java方法 AndroidJavaClass cls = new AndroidJavaClass("com.example.androidTest.MainActivity"); cls.CallStatic("showAndroidToast", currentActivity, "传递的参数"); CallStatic方法中,第一个参数表示要调用的Java层面类的名称,第二个参数表示要调用的Java方法名称,接下来的参数就是要传递的参数。 综上所述,通过JNI和AndroidJavaClass等API,Android和Unity之间可以很方便地进行交互,相互传递数据和调用各自的方法。这样就能够更好地完成游戏逻辑和UI界面的制作。同时,这种交互方式也为跨平台的开发提供了很好的经验和方法。