推荐阅读
大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。
首先,来了解一下事件函数的执行顺序,下图是官方给的脚本中事件函数的执行顺序:

众所周知,Unity中某个脚本的事件函数执行顺序是Awake、Start、Update、LateUpdate等,那么不同脚本之间的事件函数的调用顺序是怎么样的呢,以及如何控制不同脚本之间的事件函数的调用顺序呢?
看完这篇文章你就能找到答案。
.meta文件
看似每个脚本之间的调用顺序是随机的,其实是有迹可循的。
痕迹就在于生成.cs文件后同步生成的.cs.meta文件:

打开.cs.meta文件:

executionOrder: 0 这个就是执行顺序。
越小执行的优先级越高。
默认的情况下就是0。若想让脚本最先执行,可以把它设置为负值。
既然知道了脚本的执行顺序,那么怎么控制脚本的执行顺序呢。
直接打开脚本的.meta文件,然后修改executionOrder的值。
越小执行的优先级越高。
在Unity中设置脚本的执行顺序:


在Project视图中随便找一个脚本,点击右上角Execution Order后弹出Project Settings视图,然后设置执行顺序即可。
脚本执行顺序工具类:
新建脚本命名为AttributeUtils.cs,双击打开脚本,编辑代码:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Reflection;
// 定义属性类 管理相关的函数
[System.AttributeUsage(AttributeTargets.All)]
public class SFHCall : Attribute
{
public SFHCall(int lev)
{
level = lev;
}
public int GetLevel()
{
return level;
}
protected int level;
}
// 定义属性类 设置函数的属性
[System.AttributeUsage(AttributeTargets.All)]
public class SFHStartCall : SFHCall
{
public SFHStartCall(int lev) : base(lev)
{
level = lev;
}
}
// 管理执行对象
public class AtrributeFlagFunction
{
public MonoBehaviour monob;
public MethodInfo methodInfo;
}
// 属性工具类
public class AttributeUtils
{
// 使用字典来存储所有的相关函数
public static SortedList myFuctionList = new SortedList();
public static void GetAllDestByProperties<T>(object[] mono) where T : SFHCall
{
int length = mono.Length;
for (int i = 0; i < length; i++)
{
Cache<T>((MonoBehaviour)mono[i]);
}
}
private static void Cache<T>(MonoBehaviour p) where T : SFHCall
{
var type = p.GetType();
// 不会重复调用父类的方法了。
var fields2 = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (var field in fields2)
{
AtrributeFlagFunction aff = new AtrributeFlagFunction();
var objs = field.GetCustomAttributes(typeof(T), false);
if (objs.Length > 0)
{
aff.monob = p;
aff.methodInfo = field;
T attr = (T)objs[0];
int value = attr.GetLevel();
if (!myFuctionList.ContainsKey(value))
{
{
List<AtrributeFlagFunction> AttributeList = new List<AtrributeFlagFunction>();
AttributeList.Add(aff);
myFuctionList.Add(value, AttributeList);
}
}
else
{
List<AtrributeFlagFunction> mlist = (List<AtrributeFlagFunction>)myFuctionList[value];
mlist.Add(aff);
}
}
}
}
}
这个脚本是用属性来获取所有的对象的函数,然后再去排列顺序去执行。
实例化属性管理工具类:
再新建脚本StartFromHere.cs,双击打开脚本,修改代码:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.Reflection;
public class StartFromHere : MonoBehaviour
{
void Start()
{
//初始化 获取所有的相关函数 并执行
InitialStart();
}
private void InitialStart()
{
MonoBehaviour[] testMono = FindObjectsOfType<MonoBehaviour>();
AttributeUtils.GetAllDestByProperties<SFHStartCall>(testMono);
for (int i = 0; i < AttributeUtils.myFuctionList.Count; i++)
{
List<AtrributeFlagFunction> mlist = (List<AtrributeFlagFunction>)AttributeUtils.myFuctionList.GetByIndex(i);
for (int j = 0; j < mlist.Count; j++)
{
AtrributeFlagFunction item = mlist[j];
MonoBehaviour monob = item.monob;
object result = item.methodInfo.Invoke((object)monob, new object[] { });
if (item.methodInfo.ReturnType == typeof(IEnumerator))
{
monob.StartCoroutine((IEnumerator)result);
}
}
}
}
}
这个脚本就是实例化脚本执行顺序工具类,需要挂载在对象上。
使用方法:
新建三个脚本,随便命名,然后编辑代码:
public class SFHTest1 : MonoBehaviour
{
[SFHStartCall(1)]
void OnStart()
{
Debug.Log("第一个执行");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SFHTest2 : MonoBehaviour
{
[SFHStartCall(2)]
void OnStart()
{
Debug.Log("第二个执行");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SFHTest3 : MonoBehaviour
{
[SFHStartCall(3)]
void OnStart()
{
Debug.Log("第三个执行");
}
}
SFHStartCall后面的参数就是执行顺序,越大越早执行,然后都挂载到对象上:

执行结果:

本篇文章实现了如何控制脚本执行的顺序。
最后使用了一个脚本执行顺序工具类来控制脚本的执行顺序。
你的点赞就是对博主的支持,有问题记得留言:
博主主页有联系方式。
博主还有跟多宝藏文章等待你的发掘哦:
| 专栏 | 方向 | 简介 |
|---|---|---|
| Unity3D开发小游戏 | 小游戏开发教程 | 分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。 |
| Unity3D从入门到进阶 | 入门 | 从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。 |
| Unity3D之UGUI | UGUI | Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。 |
| Unity3D之读取数据 | 文件读取 | 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。 |
| Unity3D之数据集合 | 数据集合 | 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。 |
| Unity3D之VR/AR(虚拟仿真)开发 | 虚拟仿真 | 总结博主工作常见的虚拟仿真需求进行案例讲解。 |
| Unity3D之插件 | 插件 | 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等 |
| Unity3D之日常开发 | 日常记录 | 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等 |
| Unity3D之日常BUG | 日常记录 | 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。 |
我怎样才能完成http://php.net/manual/en/function.call-user-func-array.php在ruby中?所以我可以这样做:classAppdeffoo(a,b)putsa+benddefbarargs=[1,2]App.send(:foo,args)#doesn'tworkApp.send(:foo,args[0],args[1])#doeswork,butdoesnotscaleendend 最佳答案 尝试分解数组App.send(:foo,*args)
我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee
我在用Ruby执行简单任务时遇到了一件奇怪的事情。我只想用每个方法迭代字母表,但迭代在执行中先进行:alfawit=("a".."z")puts"That'sanalphabet:\n\n#{alfawit.each{|litera|putslitera}}"这段代码的结果是:(缩写)abc⋮xyzThat'sanalphabet:a..z知道为什么它会这样工作或者我做错了什么吗?提前致谢。 最佳答案 因为您的each调用被插入到在固定字符串之前执行的字符串文字中。此外,each返回一个Enumerable,实际上您甚至打印它。试试
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行