Unity脚本执行顺序自研框架
本文章由cartzhang编写,转载请注明出处。 所有权利保留。
文章链接:http://blog.csdn.net/cartzhang/article/details/52372611
作者:cartzhang
一、关于Unity脚本执行排序
1 Unity脚本执行排序的说明
在Unity中,要控制Unity的脚本执行顺序,Unity引擎本身已经有了一个脚本排序。这个排序在编辑器中可以编辑并设置。
它里面带有默认的,根据优先级来排定执行顺序。若没有在排序的均在default time排序的间隙随机执行。也就是说,在在default time 以上列表中的优先级总是高于其他排序的。
这对于引擎本身来说,已经做的很好了。最起码把你的代码给排序了呢。
这里官方脚本执行网址:https://docs.unity3d.com/Manual/class-ScriptExecution.html
这个排序的已经可以满足基本的要求。比方说要对游戏配置进行初始化就把它排在所有其他代码的最前面就好了。问题就解决了。
2 脚本执行顺序在那里呢?
脚本的执行顺序,我之前总是以为它会全部都存在游戏的projectSettings中的DynamicManager.asset中,或在其他的TagManager.asset中,但是其实并不然。
看到每个脚本有个Meta文件,meta文件包含了脚本的执行顺序。
fileFormatVersion: 2
guid: 5715403d8bbda7e4d905d57906a392da
timeCreated: 1470808531
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
executionOrder: 0 这个就是执行顺序。越小执行的优先级月高。默认的情况下就是0。若想让脚本最先执行,可以把它设置为负值,比方说:-1200等这样数字,当然还是在脚本执行顺序中,确认一下。
二、脚本执行顺序的常用框架(单例模式)
1 入口
在Unity中,因为在代码中各自为战的处理,Awake,Start脚本,虽然可以处理上述所说的问题。
但是在大多数游戏中,还是需要把游戏的流程给全部梳理,让所有的start和Awake,甚至Update都给控制起来。
这样的框架中,必然有个main.cs最为游戏的入口,其他的大部分的脚本就都是从这里初始化和使用的。
mai.cs的基本结构:
using UnityEngine;
public class Main : MonoBehaviour
{
void Awake()
{
}
void Start()
{
GameWorld.Instance.Init(this);
}
void Update()
{
GameWorld.Instance.Update();
}
void OnDestroy()
{
GameWorld.Instance.Destroy();
}
void OnApplicationQuit()
{
}
void OnApplicationPause(bool pause)
{
}
}
2 单例模式脚本
那个GameWorld是个嘛玩意呢?
这个就是所说的单例模式了。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameWorld
{
public static GameWorld Instance { get { return SingletonProvider<GameWorld>.Instance; } }
private List<Action> frameActionList = new List<Action>();
public GameObject MainObj
{
get;
private set;
}
public Main Main
{
get;
private set;
}
public void Init(Main main)
{
this.Main = main;
this.MainObj = main.gameObject;
NotificationManager.Instance.Init();
TimerManager.Instance.Init();
NetProcessManager.Instance.Init();
}
public void Update()
{
NotificationManager.Instance.Update();
TimerManager.Instance.Update();
DelayManager.Instance.Update();
NetProcessManager.Instance.Update();
//主循环
for (int i = 0; i < frameActionList.Count; i++)
{
Action action = frameActionList[i];
if (action != null)
{
action();
}
}
}
public void Destroy()
{
NotificationManager.Instance.Destroy();
TimerManager.Instance.Destroy();
}
public void AddFrameAction(Action action)
{
if (!frameActionList.Contains(action))
{
frameActionList.Add(action);
}
}
public void RemoveFrameAction(Action action)
{
if (frameActionList.Contains(action))
{
frameActionList.Remove(action);
}
}
public Coroutine StartCoroutine(IEnumerator routine)
{
return this.Main.StartCoroutine(routine);
}
public void StopCoroutine(IEnumerator routine)
{
this.Main.StopCoroutine(routine);
}
}
这样其他的都需要在这里出发了。
其他的单例就在这初始化,更新Update,销毁Destroy等等。
简单的比方:
using System;
using System.Collections.Generic;
using UnityEngine;
public class XXManager : BaseManager
{
public static XXManager Instance { get { return SingletonProvider<XXManager>.Instance; } }
public override void Init()
{
}
public override void Update()
{
}
public override void Reset()
{
}
public override void Destroy()
{
}
public void Remove(string clockID)
{
}
public void Remove(int clockID)
{
}
}
基本就是这样的了。
然而这些都不是我想要说的。
这些单例模式优点还是蛮多的。代码容易管理、整理,在有bug的时候可以容易定位bug等等。
代码简单,明了,结构化和比较容易满足单一职责原则等等。再说,单例本身就是个很好的设计模式。
但是,我想使用另外一个尝试下。
三、自研的属性定义框架
1 什么是属性
在C#中,有个Attribute这样个的属性,打算利用这个性质来把所有相关的函数进行收集统一处理。
这样有很好处呢。
先说一点,这个只是个人的一点想法。当然已经基本实现完毕。好不好呢,这个以后再说,至于有点,好歹说是轮子也是自己制作啊。
况且,还是有优点的。
首先,属性的定义:
[System.AttributeUsage(AttributeTargets.All)]
internal class SFHCall : Attribute
{
}
这个就是个属性继承定义。无它。
2 收集所对应属性的函数
这个当然需要一个字典来存储了。
直接代码吧:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Reflection;
public class AttributeUtils
{
public static Dictionary<MonoBehaviour, List<MethodInfo>> monoRPCMethodsCache = new Dictionary<MonoBehaviour, List<MethodInfo>>();
public static void GetAllDestByProperties<T>(object []mono)
{
int length = mono.Length;
for (int i = 0; i < length; i++)
{
GetDescByProperties<T>(mono[i]);
}
}
private static void GetDescByProperties<T>(object p)
{
MonoBehaviour mo = (MonoBehaviour)p;
if (!monoRPCMethodsCache.ContainsKey(mo))
{
Cache<T>(mo);
}
return;
}
private static void Cache<T>(MonoBehaviour p)
{
List<MethodInfo> cachedCustomMethods = new List<MethodInfo>();
monoRPCMethodsCache.Add(p, cachedCustomMethods);
var type = p.GetType();
// 不会重复调用父类的方法了。
var fields2 = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (var field in fields2)
{
var objs = field.GetCustomAttributes(typeof(T), false);
if (objs.Length > 0)
{
cachedCustomMethods.Add(field);
}
}
}
}
这里需要稍微说下:
// 不会重复调用父类的方法了。
var fields2 = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
最后一个只查找当然文件中的符合要求的函数,并不会扩展到父类中。要是到父类中查找,这样就会造成大量的重复函数,这样在后面的执行中,就会浪费多倍的效率。
代码虽然没有太多注释, 但是基本都是自明其意的。所以,不再过多的阐述了。
3 函数的执行过程
函数都收集起来的。在不同的List中保存,这样就根据你自己爱好来执行先后顺序吧!!
怎么执行呢?关键在于Invoke.
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
using UnityEditor;
class SFHListener
{
static SFHListener()
{
InitialStart();
}
public static void callme()
{
Console.WriteLine("call me");
}
private static void InitialStart()
{
MonoBehaviour[] testMono = GetScriptAssetsOfType<MonoBehaviour>();
AttributeUtils.GetAllDestByProperties<SFHCall>(testMono);
int AttributeFunctionCount = AttributeUtils.monoRPCMethodsCache.Count;
if (AttributeFunctionCount < 0)
{
return;
}
foreach (var item in AttributeUtils.monoRPCMethodsCache)
{
MonoBehaviour monob = (MonoBehaviour)item.Key;
for (int iMethod = 0; iMethod < item.Value.Count; iMethod++)
{
object result = item.Value[iMethod].Invoke((object)monob, new object[] { });
if (item.Value[iMethod].ReturnType == typeof(IEnumerator))
{
monob.StartCoroutine((IEnumerator)result);
}
}
}
}
private static MonoBehaviour[] GetScriptAssetsOfType<T>()
{
// current scripts in current scene;
MonoBehaviour[] Monoscripts = (MonoBehaviour[])GameObject.FindObjectsOfType<MonoBehaviour>();
// get all monobehaviours contains current scene and also all Prefabs
//MonoBehaviour[] Monoscripts = (MonoBehaviour[])Resources.FindObjectsOfTypeAll<MonoBehaviour>();
return Monoscripts;
}
}
需要注意的是,最后一个函数中。
注释掉的代码,会查找到所有场景中和预制体的代码,这样也会有特大量的超载和重复造成的浪费。
4 最后就是调用
调用,这个超级简单。
当然也可以根据你的需要来调整。放在Awake或start中都是随你意愿啊!!
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.Reflection;
public class StartFromHere : MonoBehaviour
{
// Use this for initialization
void Start()
{
SFHListener.callme();
}
}
这里举例只写在Start中。
这样基本就说明完毕。
四、当然是源码
Github 地址:https://github.com/cartzhang/StartFromHere
当然,也非常欢迎你来提交代码。
现在代码中并乜有说可以带参数。这个估计看看,若有需要再尝试啊!!
毕竟我还要回家睡觉啊!!
——————–THE———————–END————————–
若有问题,随时联系!!
非常感谢! !!!
Unity脚本执行顺序自研框架的更多相关文章
- unity脚本执行顺序
Awake ->OnEable-> Start ->-> FixedUpdate-> Update -> LateUpdate ->OnGUI ->R ...
- unity 脚本执行顺序设置 Script Execution Order Settings
通过Edit->Project Settings->Script Execution Order打开MonoManager面板 或者选择任意脚本在Inspector视图中点击Execu ...
- 浅谈Unity的脚本执行顺序
一.添加脚本的顺序 这是一张官方的脚本顺序图 一般,当我们把脚本绑定在游戏对象上时,或者点击绑定好的脚本的reset按钮时,会调用Reset() 当我们初始化一个对象时,会先调用Awake()在调用O ...
- MonoBehaviour Lifecycle(生命周期/脚本执行顺序)
脚本执行顺序 前言 搭建一个示例来验证Unity脚本的执行顺序,大概测试以下部分: 物理方面(Physics) 渲染(Scene rendering) 输入事件(InputEvent) 流程图 Uni ...
- [转]Script标签和脚本执行顺序
Script标签和脚本执行顺序 这里详细聊聊和script标签相关的脚本执行顺序. Script标签的默认行为 几个首要特性: script标签(不带defer或async属性)的会阻止文档渲染.相关 ...
- Unity 继承MonoBehaviour脚本 执行顺序 详解
先看结果 Awake ->OnEnable-> Start ->-> FixedUpdate-> Update -> LateUpdate ->OnGUI ...
- unity脚本运行顺序具体的解释
unity脚本自带函数执行顺序例如以下:将以下脚本挂在随意物体执行就可以得到 Awake ->OnEable-> Start ->-> FixedUpdate-> Upd ...
- html/css基础篇——link和@inport详解以及脚本执行顺序探讨
先说一说两者之间的异同 两者都可以引用外部CSS的方式,现在主流浏览器两者都支持(ps:@import是CSS2.1提出的),但是存在一定的区别: 1.link是XHTML标签,除了加载CSS外,还可 ...
- unity 脚本编译顺序
根据官方的解释,它们的编译顺序如下: (1)所有在Standard Assets.Pro Standard Assets或者Plugins文件夹中的脚本会产生一个Assembly-CSharp-fil ...
随机推荐
- java(itext) 一个很简单的PDF表格生成工具
先上个效果图 因为做的项目涉及到数据预测,其中有大量打印业务来支撑实体店的运营,因为注重的是数据,要求简洁,清晰,所以写了个很简单也很实用的工具类. 如果需要编写样式或者插入背景,都可以查阅itex官 ...
- 认识与入门 MarkDown (转Te_Lee)
Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用.看到这里请不要被「标记」.「语言」所迷惑,Markdown 的语法十分简单.常用的标记符号也不 ...
- https微信分享看不到图片的坑
最近在做一个活动项目的时候一开始走的http,发现网络被劫持的特别严重,没办法,只能改走https,但是修改为https后发现在使用微信js-sdk分享的时候看不到缩略图,直接通过地址打开是可以找开图 ...
- 树莓派-(一)开箱到点亮一些坑(无屏、无wlan、无直连键鼠)
0x00.前期准备: 材料: 树莓派3b+ 板子 * 1,适配电源 * 1,网线 * 2,sd卡16G * 1,读卡器 * 1 安装时注意,3b+三个散热片贴好.小风扇接线要接对 工具: 0x01. ...
- git reset命令学习
我们在使用git进行版本管理的时候,如果遇到需要回退代码的情况,一般会用 git reset 命令,不过这个命令还有几个参数,这篇文章就来详解一下. 先来了解一下 git 大致的工作流程,配合这张图会 ...
- 跨平台移动开发phonegap/cordova 3.3全系列教程-目录
目录(更新完成后会附上源码供参考) 第一章 android平台开发 phonegap/cordova简介 1.开发环境搭建 2.helloworld 3.启动画面 4.结合asp.net/jqmboi ...
- Riverbed SteelHead 9.5.0
平台: scientific linux release 6.5 类型: 虚拟机镜像 软件包: riverbed steelhead 9.5.0 basic software Enterprise i ...
- C#之MVC3继续整理问题
1.注释验证[EmailAddress(ErrorMessage = "×")],用的MVC3框架,此处报错,找不到类“EmailAddress”,看到原文有using Syste ...
- java Vamei快速教程22 内存管理和垃圾回收
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 整个教程中已经不时的出现一些内存管理和垃圾回收的相关知识.这里进行一个小小的总结. ...
- 解决spring配置文件没有提示的问题
我们使用eclipse编辑spring配置文件时,经常没有提示,而无从下手时. 现在我们就来解决没有提示的问题. 原因是因为eclipse中没有配置xsd文件.. 步骤一:把如下头文件拷贝到你的spr ...