Unity 常用的事件类型及其用法和线程

==========================================SendMessage***=========================================

      使用反射写一个SendMessageUpwards->https://blog.csdn.net/MikuLingSSS/article/details/83474987

       Unity里面的SendMessage是一个非常好用的方法,他的调用方式是 gameobject.SendMessage(“MethodName”,object t); 他会查找当前物体里面所有的脚本,并且根据反射查找与方法名和参数列表一致的方法并且执行,我一般会将其用在动画状态之中

如上,如果我想要在动画执行开始,或是执行过程中执行某个方法,就可以直接添加一个Method,代码中会自动帮我执行这个方法,当然,前提是我必须要有这个公共方法。

     好处:执行起来方便,调用简单,不需要我们写那么多的代码去完成一个委托或是反射

     坏处:执行花费内存比较大(毕竟你知道目标在哪,和一个个问差别还是蛮大的),如果动画状态过多,或是使用较多,一旦方法出现BUG,查找起来会很麻烦

=========================================动画中的Event==========================================

     动画中的Event和SendMessage差不多,都是查找本物体的公共方法,但是动画中的Event拥有自己的曲线,我们可以选择性的决定他什么时候,什么帧数执行

选择你需要放置事件的动画,将滚动条拉到低端

可以看到,我们可以直接控制动画的播放,让其在我们需要的地方添加执行的事件,注:当你创建事件并设置好参数时,记得点一下Apply

========================================Animation事件=============================================

       有一些老版的动画是没有办法设置Event的,这个时候我们就必须自己找到Animation组件去设置事件,方法为先打开Animation创建面板 Window => Animation ,之后选中Project面板中你想要设置事件的动画,注:有一些动画会出现一个ReadOnly的状态无法设置动画,这个时候只要选中Project中的动画,Ctrl+D复制一份,在复制体上添加事件就可以了,但是相应的你场景中的相应AnimationClip也要更换为复制体

    

与Animator Event类似,老版的动画事件也是必须要在本物体里面有相应的公共方法,否则抛出异常

========================================Invoke===================================================

        使用C#反射写一个带参数有返回值的Invoke->https://blog.csdn.net/MikuLingSSS/article/details/83422814

        这个方法我很少会用,感觉更多会用在反射里面,

调用方法为

void Start()
{
    // Invoke(string,float);
    Invoke(MethodName,timer); // 在等待timer秒后执行方法 无法使用参数,没有返回值
    
    ///  InvokeRepeating(string, timer, rate);
    ///  第二个参数是等待执行的时间,第三个参数是以后每个rate执行一次这个方法
    InvokeRepeating(MethodName,timer,rate);//等待timer秒执行方法,并且以后每隔Rate秒再次执行方法,没有参数,没有返回值

    CancelInvoke(); // 取消所有Invoke
    
    CancelInvoke(MethodName); // 取消特定的Invoke

}

void Print()
{
    debug.log("hello world"); // 类似于这样的方法
}

这里肯定会有人和我有一样的疑问,如果我多次执行InvokeRepeating的话,那会发生什么

    int a = 0;   
    void Main()
    {
        StartCoroutine(Test());
    }

    void Print1()
    {
        a += 100;// 原谅我,我实在想不到应该怎么写
    }

    private void Update()
    {
        Debug.Log(a);
    }

    IEnumerator Test()
    {
        for (int i = 0; i < 3; i++)
        {

            InvokeRepeating("Print1", 0.3f, 1f);
            yield return 1;
        }
    }

测试结果为,a += 100 的事件间隔并不是一秒,并不是代码出现了BUG,而是他们呈现了一种同步执行中,就像是下面这样

而CancelInvoke则相当于线程的Abort

void Start()
{
    for(int i =0;i < 3;i++)
    {
        Thread testThread = new Thread(()=>{ num += 100;});
        testThread.Start();
    }
}

int num = 0;

void Update()
{
    Debug.Log(a);
}

================================================Delegate=========================================

        委托:简单来说 委托就是一个类 他可以接受其他的方法作为参数 加入到自己的列表中,当执行委托时,他旗下的所有方法会被一起执行,并且参数通用↓

using ...;
public class DelegateEvent
{
    public static DelegateEventInstance{get;}
    private Delegate void Print(string info); // 声明一个委托 无返回值 stirng参数
    public Print print();//实例化
    public void AddEvent(Print e)
    {
        print += e;
    }
    public void DelEvent(Print e)
    {
        print -= e;
    }
    public void Play()
    {
        print();
    }
}
==================================================
using ...;
public class Test:Mono
{
    void Awake()
    {
        for(int i = 0;i < 10;i++)
            Instantiate<GameObject>(Resources...); // 假设这些物体上面都有一个print脚本
    }
    void Start()
    {
        DelegateEvent.Instance.Play("hello world");
    }
}
==================================================
using ...;
public class print:Mono
{
    void Awake()
    {
        Test.Instance.AddEvent(print);
    }
    public void print(string info)
    {
        Debug.Log(info);
    }
}

 

当运行之后,你会看到10个hello world,这种方法极大的简化了我们的工作流程,毕竟我们不需要保存生成的物体,也不需要他的实例化,只需要把我需要的方法在我执行之前加入进来即可

==========================================09/26 Func之类=========================================

下面的Ation代码参考Blog : https://www.cnblogs.com/cemaster/p/5996537.html

using UnityEngine;
using System;

public class EventTest : MonoBehaviour
{
    Lambda eventTest;
    Lambda action;
    void Start()
    {
        action = new Lambda();
        eventTest = new Lambda();

        // Func
        Lambda.func += OnFunc;          // Func方式访问
        Debug.Log(Lambda.func("Func->", "Func->"));

        /// Event
        eventTest.OnStart += OnEvent;   // Event方式访问,使用(System.EventArgs e)
        OnEventPlay(new EventArgs());
        /// 自定义EventArgs Event
        eventTest.te += new Lambda.Dele_ToMy(OnEvent); // Event方式访问,使用(EventArg e)->我们自己定义的
        eventTest.Play(new EventArg("EventArg_To_My", 0x000000));
        /// Action
        action.action = (string str) => { Debug.Log(str+"Lambda"); };    // ActionLambda方式增加函数
        action.action = delegate (string str) { Debug.Log(str+"delegate"); };
        action.action("Action->");
    }
    /// System.EventArgs
    public void OnEventPlay(System.EventArgs e)
    {
        eventTest.Play(this, e);
    }

    /// Event
    public void OnEvent(object sender, System.EventArgs e)
    {
        Debug.Log("(System.EventArgs e)");
    }

    /// 自己写的EventArgs
    public void OnEvent(EventArg e)
    {
        Debug.Log( e.eventArgs_Num + e.eventArgs_Str);
    }

    /// Func
    public string OnFunc(string t1, string t2)
    {
        return (t1 + t2 + ":Func").ToString();
    }


}

/// 委托类
class Lambda
{
    public static Func<string, string, string> func;

    /// 非自定义
    public delegate void Dele_To_Sys(object sender, System.EventArgs e);
    public event Dele_To_Sys OnStart;
    public void Play(object sender, System.EventArgs e)  // 注意,Event的使用只能在声明自己的本类中,所以需要这样调用
    {
        if (OnStart != null)
        {
            OnStart(sender, e);
        }
        else
        {
            Debug.Log("Not Method!!!");
        }
    }

    /// 自定义
    public delegate void Dele_ToMy(EventArg e);
    public event Dele_ToMy te;
    public void Play(EventArg e)
    {
        te(e);
    }

    /// Action
    public Action<string> action;
}

/// 自定义EventArgs
public class EventArg : System.EventArgs
{
    public string eventArgs_Str { get; set; }
    public int eventArgs_Num { get; set; }
    public EventArg(string eventArgs_Str, int eventArgs_Num)
    {
        this.eventArgs_Str = eventArgs_Str;
        this.eventArgs_Num = eventArgs_Num;
    }
}

运行结果如下:

============================================线程===============================================

       -  -这个是一时兴起加的,虽然加上后让整个标题都怪怪的 - -

Unity里面并不支持多线程,他所自带的携程 IEnumerator 其实也是顺序执行,只是在每一帧结束后会去查看携程中的条件是否满足,从根本上来说,这其实也只是一个伪线程,不信的话你可以写上这么一段

public class Test: MonoBehaviour {
    void Start () {
        StartCoroutine(true());
    }
    IEnumerator test()
    {
        while (true)
        {
            Debug.Log("11");
        }
    }
}

 

不出意外的话,Unity会直接失去响应,因为Unity发现,他的条件一直为真,所以就一直在执行,当然,是在Main线程中。

      而多线程通俗来说就是本来只有一条路,现在我给你另外一条路,你去哪里跑,无论你是堵死还是瞬间结束都与我无关。

      线程的使用是方法的调用

void Test()
{
    Thread thread = new Thread(() => {
            //方法体 匿名表达式 也叫Lambda表达式
    });
    Thread thread2 = new Thread(test1); // 注:方法没有返回值 参数必须是object类型
}

void test1(object s)
{
    while (true)
    {
        //这里不能用任何Unity相关的东西
        //Debug都不行,就算你在Editor里面不出错,但是当你打包之后有很大可能出现一个GC错误    
        //从而导致无法运行
    }
}

 

这里,就有小伙伴要问了,Unity相关的都无法使用那要他有什么用=>当然是超大量的计算啊,或是必须实时接受数据的那种

比如说Socket里面的

using ...;
class ReceiveMessage
{
    public Socket Client;
    public void Initial()
    {
        Client = New Socket
        (
            ...;//初始化
        );
        Client.Connect(new IpEndPoint(ip,Port)); 
        Thread receiveMessage =  new Thread();
        receiveMessage.IsBackGround = true;
        //设为后台线程,就是游戏进入后台运行也可以继续执行  和 Unity里面    
        //Application.RunInBackground(将Unity主线程设为后台运行)一致 
        receiveMessage.Start;                                                      
    }
    public void Receive()
    {
        while(true)
        {
            // 如果有信息传入    ↓ 就会往下执行 否则continue;
            int mesLength = client.Receive(result);
            // 执行委托中的方法  参数为result
            ReceiveEvent.Instance.play(Encoding.UTF8.GetString(result, 0, mesLength));
            //处理代码
        }
    }
}
///委托
class ReceiveEvent
{
    public static ReceiveEvent Instance{get;}
    public delegate void dele(string info);
    private dele Receive;
    public void AddEvent(dele e)
    {
        Receive += e;
    }
    public void Play()
    {
        Receive();
    }
}
///场景物体中的脚本
class Test : Mono...
{
    void Awake()
    {
        
    }

    void print(string info)
    {
        Debug.Log("来自服务器的消息:"+info);
    }
}

而线程的终结或者说是中断则是调用本线程的Abort,例如

using ...;

int main()
{
    Thread t = new Thread(()=>{
        try{
            while(true)
            {
                Console.WriteLine("Thread->Running");
                Thread.Sleep(100); // 这个是我们创建的线程阻塞100ms
            }   
        }catch(exception ex)
        {
            MessageBox.Show(ex.message); // 这个在调用Abort之后会运行
        }     
    });
    t.Start();
    Thread.Sleep(3000); // main线程阻塞3000ms
    t.Abort(); // 注意,这个是调用一个异常,就是说让线程本身爆出一个错误,
    //而且在调用之后->本线程无法再次start,
    return 0;
}

第二篇,暂时只是关于Unity使用线程,协程和InvokeRepeating的一个小实验

https://blog.csdn.net/MikuLingSSS/article/details/83350438

================================这篇Blog有点长了,以后在加可能就会新开一篇===========================

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值