==========================================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(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 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 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有点长了,以后在加可能就会新开一篇===========================