Unity中一些小技巧

在unity中有一些小技巧,不能像人物的移动,攻击等有好几种方式可以用来单独成章,因此就把一些内容比较少,但是比较重要的内容放在一起合成一章,内容将会持续更新

1.人物死亡动画的播放:人物死亡后,需要用到Destory()函数对其进行销毁,但是想让其播放完死亡动画后再对其进行销毁,这个时候有两种方法,第一就是利用携程函数,等待一段时间后再执行销毁函数;第二(也是最常用的)就是利用动画事件的方法,在死亡的人的控制脚本上添加一个死亡函数,随后在死亡动画的最后一帧上添加动画事件

2.时间增量Time.deltaTime:表示每帧的时间间隔,这个一般用来写在update里面,因为update刷新的帧率与电脑性能有关系,有些电脑一秒可以120帧(Time.deltaTime=1/120),有些电脑一秒60帧(Time.deltaTime=1/60

 Vector2 position = transform.position;
  position.x = position.x + speed * horizontal;//这里的 horizonta横向轴
  position.y = position.y + speed * vertical ;
  transform.position = position;

通过这个公式可以知道在(只看x)如果update一秒刷新120帧(假如速度为10),那么 一秒后游戏对象的x坐标增加了120个单位,如果update一秒刷新60帧那么 一秒后游戏对象的x坐标增加了60个单位,这样就会导致相同的一秒钟,不同电脑上,运行的距离不一样

如果增加Time.deltaTime

 Vector2 position = transform.position;
  position.x = position.x + speed *Time.deltaTime* horizontal;//这里的 horizonta横向轴
  position.y = position.y + speed*Time.deltaTime * vertical ;
  transform.position = position;

 这样的话在120帧的update中Time.deltaTime=1/120,position.x = position.x + (10*1/120* horizontal);这样执行120次,一秒后游戏对象的x坐标增加了10个单位,在60帧的update中Time.deltaTime=1/60,position.x = position.x + (10*1/60* horizontal);这样执行60次,一秒后游戏对象的x坐标增加了10个单位,这样就控制了不同的刷新率但是一秒钟的移动距离是相同的

总结:这里 speed *Time.deltaTime就把每帧移动距离变成了每秒的移动距离

3.玩家的二段跳跃 :实现玩家二段跳跃,一般来说是给玩家脚底添加一个碰撞器然后判断是否接触地面,接触地面时设置重置跳跃次数

在制作二段跳之前,首先要设置一个跳跃次数,要在跳跃次数大于0的情况下才能执行跳跃,跳跃完成后跳跃次数减一,代码如下

 public float jumpspeed;//跳跃速度

 public int jumpCount=2;//跳跃次数

 void Update()
    {
        if(jumpCount > 0)
        {
            Jump();
        }

    }

void Jump()//跳跃函数
    {      
        if (Input.GetKeyDown(KeyCode.K))
        {
            Vector2 jumpVel = new Vector2(M_Rigidbody2D.velocity.x, jumpspeed);
            M_Rigidbody2D.velocity = jumpVel;
            jumpCount--;//完成一次跳跃后,跳跃次数减一
        }   
    }

区别就是在于重置跳跃次数函数, 目前来说检测是否接触地面的方法有两种(不用射线):

第一种(使用标签)将脚底的碰撞器勾选为一个触发器,将地面的标签设置为Ground,随后在进入触发器的函数中重置跳跃次数,代码如下

private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "Ground")
        {
            jumpCount = 2;
        }
    }

第二种(使用图层)将地面的图层设置为Ground在start中获取到脚底的碰撞器,再写一个判断重置函数,如果碰到地面图层就重置,代码如下:

private bool isGround;//判断是否接触地面

private BoxCollider2D myFeet;//脚底的碰撞器
void Start()
{
myFeet = GetComponent<BoxCollider2D>();
}

void CheckTouch()//这个需要在update中调用

{

  //这里如果碰到地面图层就返回true

  isGround = myFeet.IsTouchingLayers(LayerMask.GetMask("Ground"));

  if(isGround)

  {

    jumpCount=2;

  }

}

4.单例模式: 单例模式通俗来讲目的就是一个类只能有一个实例(也就是它只能被实例化一次,只存在一个对象),在unity中的使用场景经常是用来制作UI比如血条,代码如下:

   public static UIHealthBar Instance { get; private set; } //这里是使用快捷属性的方法     
   private void Awake()
    {

        //这里是关键,相当于是把当前的对象赋值给Instance字段(自动创建的),这时Instance

        就相当于是当前类的对象,并且只有一个
        Instance = this;//设置静态实例为当前对象,这个this就与之前的gameobject差不多
    }

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

5.游戏对象的遮罩问题(2D图形渲染顺序):首先要清楚unity中存在排序图层 (Sorting Layer) 和图层中的顺序 (Order in Layer)

注意在创建游戏对象后,unity默认游戏对象的排序图层为为default,图层中的顺序为0,这个时候就会出现后创建的在上方的情况,即使在层级窗口改变游戏对象的上下顺序也不行,应该更改排序图层或者图层顺序即可

6.2D人物卡墙,旋转问题:在游戏中如果设置一堵墙,当玩家跳跃起来按住方向键不松手时,人物会卡在墙上,直到松开方向键,才会掉落;有时人物还会出现旋转

(1)卡墙:首先创建一个2d的物理材质创建-》2D-》物理材质2D

 再将物理材质的摩擦系数改为0,随后将该物理材质添加给碰撞器即可

 (2)关于人物在墙边缘会旋转的情况:只需要在人物的Rigidbody 2D 添加约束实现,将z轴冻结即可

7.关于 gameObject.CompareTag和gameObject.tag:这两者的在使用的效果是一样的,但是只是会有运行所花的时间区别,前者必后者所花时间少,因此更建议使用前者,至于两者时间不同的原因在于CompareTag是GameObject中定义的一个方法可以直接进行比较而.tag作为GameObject中的属性先要get,set一遍然后再通过字符串进行比较,所以多花了时间。

8.游戏对象和组件用代码激活的方式:在脚本中,用代码激活游戏对象和激活游戏对象上组件是有区别的,激活游戏对象时gameobject.SetActive(true),激活组件时,以碰撞器为例gameobject.Ge tComponent<BoxCollider>().enabled=true;

9.暂停游戏:暂停游戏可以使用Time.timeScale=0;也就是让时间缩放等于0,同理想要制作慢放或者快放的效果设置Time.timeScale即可

10.人物发生抖动:当在Upadte中使用Translate和MoveTowards时,人物和一些建筑碰撞就会出现人物抖动的情况,这是因为Unity把物理运动时放在FixUpdate中计算的,有因为FixUpdate是要比Update先计算的,如果把Translate和MoveTowards放在Update中,就会出现在执行update那一帧的移动后,人物的图片通过电脑渲染已经进入墙体一部分(Translate和MoveTowards是改变物体位置),在进行下一次FixUpdate时计算物理运动就会把人物弹出墙外,导致人物出现抖动

11.UI显示文字模糊:当制作UI文字显示得很模糊时,可以通过改变文本组件的Scale和Font Size的大小调整,Scale越小,Font Size越大,文字越清晰

12.加载文件中的资源:Resources这是一个Unity提供的类,它有一个Load()方法,该方法是用来加载Resources文件中的的资源用的,使用时需要在Assets中创建一个名为Resources的文件夹,然后将需要使用的资源放在该文件夹下,Resource.Load();//这里参数就是文件的名称,但是要注意这里的返回值是GameObject,如果需要其它类型比如Spirte类型,就需要加泛型Resource.Load<Spirte>(文件名);

13.Animator和Animation的简单区别:

Animation是一种简单的动画组件,先简单介绍使用上的区别,这里想要播放动画时直接在代码中

Animation(对象).clip=AnimationClip(对象),这一步就相当于把Animation中的动画赋值为想要播放的动画clip,然后Animation(对象).Play();播放动画;还有中方式就是Animator.Play("动画名称");注意这里的动画名称是要在Animator动画器中存在的。

 Animator比起animation就复杂了,但也更强大了,Animator一般用于骨骼动画人型,目前就不详细介绍,这是控制器在控制器中可以看见一些状态,比如stop,run等,这些动画就可以通过通过控制参数控制切换,在控制器中还存在一个混合树,和子状态机这两个的东西,混合树的作用是,这两者我理解的是比如人物在进行移动时有走,跑等动作用将这些需要融合的动画就可以放在混合树里面,子状态机可以理解为是不需要融合的动作,比如一段攻击,二段攻击,跳跃和下落等就可以放在子状态机中,混合树是通过参数设置播放对应的AnimationClip,等以后涉及到动画篇时再详细讲解

14.Vector2向量的一些基本用法:二维向量和三维的用法大概一致;例如有一个Vector2 V2=new Vector2(1,1);常用的就是求V2的模长,和把V2单位化,都有两种方法

  (1)求模长:V2.magnitude(模长);V2.sqrMagnitude(模场的平方); V2.SqrMagnitude()(这个也是求模长的平方);前两个和第三个的区别就是,前两个是Vector2的成员变量,第三个是Vector2的方法,前两个不会改变原本V2的值,第三个会改变V2的值

     (2)求单位化:V2.normalized;V2.Normalize();单位化同理

15.OnTriggerEnter2D触发器函数:在使用触发器函数时要注意有两点,1.collison碰撞的物体对象上必须有刚体组件,2.这里的collider(也就是碰撞的区域)无论是否勾选了IsTrigger都会触发这个函数

16.输入检测:轴可以在编辑-》项目设置-》输入管理器中可以查看并修改轴的参数

//连续检测的意思就是如果玩家一直在触发某个按键,那么它就会持续返回值

//连续检测(移动),这里返回的是值

Input.GetAxis("Horizontal")//水平方向的轴,返回值为-1到1的连续,也就是返回值是渐变的

Input.GetAxis("Vertical")//垂直方向的轴值

Input.GetAxisRaw("Horizontal")//水平方向的边界轴值,返回值就只用-1和1,不是连续的

Input.GetAxisRaw("Vertical")//垂直方向的边界轴值

Input.GetAxis("Mouse X")//鼠标水平移动增量,返回值是连续的

Input.GetAxis("Mouse Y")//鼠标垂直移动增量

//连续检测(事件),这里返回的是true或者false

//下面两者的区别就在于GetButton的参数是虚拟轴的名称(可定义的),GetKey的参数:

//1.要么的键的名称,2.要么是KeyCode的枚举值,详情见图,下面同理

Input.GetButton("Fire1")//鼠标右键

Input.GetButton("Fire2")//鼠标左键

Input.GetKey("up")//键盘的↑方向键

Input.GetKey(KeyCode.UpArrow)//键盘的↑方向键

//间隔检测事件就在按键按下后检测一次,也就是只返回一个值

//间隔检测(事件),这里返回的是true或者false

Input.GetButtonDown("Jump")

Input.GetKeyDown(KeyCode.Q)//这是键

Input.GetButtonUp("Squat")//这是自定义的轴

Input.anyKeyDown//任意键

Input.GetMouseButton(0)//鼠标左键,1是右键,2是鼠标中轮

17.时间的输出:

Time.deltaTime;//完成上一帧所用的时间(以秒为单位)

Time.fixedDeltaTime;//执行物理或者其他固定帧率更新的时间间隔(因此是固定的)

Time.time;//游戏开始以来的总时间,会受Time.timeScale的影响

Time.fixedTime;//自游戏启动以来的总时间(以物理或者其他固定帧率更新的时间间隔累计计算的)

Time.realtimeSinceStartup;//游戏开始以来的实际时间,不会受到Time.timeScale影响

Time.smoothDeltaTime;//经过平滑处理的Time.deltaTime的时间

Time.timeScale;//时间缩放,可以用来慢放动作

Time.timeSinceLevelLoad;//当前关卡所花的时间

Time.time在游戏暂停的时候不增长,结束暂停后会把这段暂停的时间加上去,Time.realtimeSinceStartup不管暂不暂停都增长。

18.复杂地形创建碰撞体:可以使用2D 边缘组件

19.运动拖尾效果:制作尾部流光特效可以使用拖尾渲染器组件

20.判断人物是否接触地面:可以直接在人物脚本上写OnCollisionEnter2D函数,在该函数中重置是否接触地面的布尔变量

21.玩家输入:获取玩家的输入最好放在Update中完成

22.人物翻转有几种方式:第一直接在transform中调用rotation进行旋转,第二在Sprite Renderer中调用翻转

23.隐藏字段:可以使用[HideInInspector]将public字段在检查器中隐藏

24.销毁物体:销毁物体Destroy函数,1参数(游戏对象),2参数(游戏对象,多少秒销毁)Destroy和DestroyImmediate,的区别在于,一个是调用后的下一帧执行,一个是在当前调用的帧中立马执行

25.跨越平台跳跃:人物可以从下往上跳上平台使用的是PlatForm Effector2D组件,记得把碰撞器勾选由效果器触发(将旋转偏移为0表面弧度180度就会出现下方可以跳上去),如果想要下来有两种方式1.将表面弧度设置为90度,2.将旋转偏移设置为180

26.抖动函数:PingPong函数用来实现数值在设定的范围内来回变动,Mathf.PingPong(Time.time),最大范围),第一个参数相当于x轴,第二个相当于y轴,x需要不断变化y才能不断变化,想象成cos函数

27.获取相机:可以在代码中使用Camera.main获取摄像机(摄像机的标签要是Camera)

28.控制声音仅播放一次:控制声音在播放时不再播放(也就是持续按下才会播放)可以使用if(!audioSource.isPlaying){audioSource.Play()};

29.任何动画切换:动画切换,当一个动画可以由仍以状态切换过去时,就可以从anystate状态连接过去,但是不能连接会任何状态

30.UI锚点:UI中Anchor锚点是用来保持图片的中心轴点与锚点的距离不变的,锚点的边的作用是让图片的边与锚点的边保持不变的

31.UI输入框:UI中文本的输入框使用的是InputField组件,其子物体PlaceHolder中的Text组件内容是文本框默认显示,子物体Text中的Text组件内容则是获取用户输入的内容,InputField组件中的Text也会显示子物体子物体Text中的Text组件内容

32.UI单选框:UI中Toggle组件是单选框,可以使用Toggle Group组件讲将多个单选框组合成一个相互影响的单选框

33.UI技能冷却效果:Sprite Render组件中的绘制模式简单:缩放图片最常规的变形方式;已切片:上下边框只会边长,左右边框只会变高,边框里面的内容变形;已平铺:图片会根据内容进行平铺。Image组件中的图像类型类似,有个已填充:如果不设置就和简单一样,,其中的FillMethod配合Fill Amount可以用来做CD效果的UI

34.UI中Canvas的三种渲染模式:Canvas的三种渲染模式分别是Screen Space -Overlay(屏幕空间—覆盖),Screen Space - Camera(屏幕空间—摄像机),World Space(世界空间)

        (1)Screen Space -Overlay:可以理解为画布紧贴着摄像机的镜头,也就是说在该画布上的所有内容都会在游戏对象之前,都会把游戏对象覆盖(那两个都在该画布上的,Sort Order 值越大越靠上,如果Sort Order 值一样,那在Hierarchy 层级中越靠下的会越显示在上方,这与渲染顺序有关)

       (2)Screen Space - Camera:它就是相当于在摄像机和UI画布之间嗨哟一段距离,当物体在摄像机和UI画布之间时,游戏物体就会在UI前面,当游戏物体在UI后面是,当然也就会被UI遮盖

       (3)World Space:这就相当于是一个跟随游戏物体的UI,它可以不是全屏大小的,比如玩家的头上需要显示血量,或者NPC头上需要显示对话框,这种就不能用全屏的UI来做,就相当于是一个局部UI

35.延时调用方法:1.协程,2.Invoke(“方法名”,多少秒后调用);3.InvokeRepeating(“方法名”,多少秒后开始第一次调用,间隔多少秒后重复调用),Invoke和协程的区别在于,用Invoke方法调用的函数不能带参数,协程就能够调用方法时携带参数

36.UGUI局部坐标轴:在UGUI中,切换成局部坐标后旋转物体,虽然游戏物体旋转了,可是坐标轴还是没有改变,可以点击Rect Transform中的这个就能看见

 37.设置子物体到UGUI上:在使用transform.SetParent时要注意,要是想要将子物体(没在UGUI上),设置到UGUI上,如果子物体的缩放已经合适,那么transform.SetParent(父物体,false)这里需要有false,不要让它再次缩放

38.鼠标点击事件和按钮重合:当游戏中有按钮和鼠标点击触发事件事,不想点击按钮时触发点击点击事件,这个时候可以就用EventSystem.current.IsPointerOverGameObject()来判断是否点击到UI上(这里指按钮),如果点击到按钮就可以不用触发点击事件,当存在背景再ui上时,可以取消勾选取消背景对鼠标点击事件的检测

39.Unity中使用VS不出现提示功能:Unity使用VS编译器出现了生命周期函数不能提示的功能,这个时候可以打开你项目所在的文件夹找到这三个文件并删除,再重新用unity打开项目即可

 40.加载场景:加载场景可以用UnityEngine.SceneManagement.SceneManager.LoadScene(0);或者书写头文件using UnityEngine.SceneManagement;随后就能SceneManager.LoadScene(0);或者利用SceneManager.LoadScene(SceneManager.GetActiveScene().name);该方法可以重新加载当前激活场景(用于游戏失败后的再玩一次)

41.JSON存取读取文档的坑:在进行读档存档时,Unity使用自带的JsonUtility反序列化的时候,如果对象继承自MonoBehavior或者ScriptableObject,使用JsonUtility.FromJson的时候就会报错ArgumentException: Cannot deserialize JSON to new instances of type ‘xxx.',这个时候有两种解决办法,1.该类继承的MonoBehavior删除掉(因为这个类只用来保存数据,不会用来实例化对象(因为在unity中脚本挂载在游戏对象上的关键是需要继承自MonoBehavior类),可以删掉) 2.使用JsonUtility.FromJsonOverwrite(Json的字符串,存储在哪个对象上(需要类保持一致))

42.不能用new对MonoBehavior声明对象:在Unity中,继承于MonoBehavior的对象,要避免使用new关键字来创建,而必须使用AddComponent或Instantiate函数来创建,这种对象也要尽量避免使用构造函数,对应的初始化工作要在对应的Awake和Start函数中进行

43.贝塞尔曲线(贝兹曲线)的算法可以参考这两篇文章

https://www.cnblogs.com/mmc1206x/p/3709188.htmlunity 贝塞尔曲线算法_Rock-G的博客-CSDN博客

44.有关渲染的周期函数:OnDrawGizmos和OnDrawGizmosSelected也是声明周期函数,前者每一帧都会调用,后者是鼠标选中后才会执行,这两者的内容都是在Gizmos中渲染的(Scene中可见,不同运行游戏直接可见)

45.遍历子物体:除了用for(int i=0;i<transform.childCount;i++){transform.GetChild(i);}可以达到以外,使用Foreach(Transform Item in transform )该方法也可以遍历一个游戏对象的子物体

46.实例化的几种重载方式:

Instantiate(Object original);

Instantiate(Object original, Transform parent);

Instantiate(Object original, Transform parent, bool instantiateInWorldSpace);

Instantiate(Object original, Vector3 position, Quaternion rotation);

Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent);

返回值:被复制物体的复制体对象

其中参数: bool instantiateInWorldSpace决定Vector3 position是全局位置还说局部位置,true为全局,false为局部,Quaternion rotation为旋转状态

47.射线函数:这里需要分为2D和3D

      (1)在2D中使用Physics2D.Raycast(起点,方向,距离,检测层);(这种重载方式最常用,如果不带检测层,就会检测到自身的collider)为发射射线的方法,返回值为PhysicsHit2D(这是一个带有碰撞信息的对象),需要被检测物体带有collider才能被检测到,检测层可以LayerMask.GetMask(targetLayerName)

      (2)在3D中使用Physics.Raycast()具体使用方法参考文档,它的返回值是bool类型

48. 避免射线和trigger物体进行检测的方法:在进行射线检测时,射线会与collider进行碰撞检测(Trigger)也会检测,如果不想检测trigger的话,就在项目设置-》物理里面把这个取消勾选

49. 画线组件:使用LineRender组件可以在游戏中画线,其中对象.SetPosition(new Vector3[]{起点,终点});方法就是用来画线的

50.局部坐标转世界坐标:将局部(自身)坐标系转到世界坐标系transform.TransformDirection(局部坐标)

51.播放动画片段的方法(不用任何逻辑来链接而直接跳转动画):animator.CrossFade(动画,过渡时间),animator.Play(“动画”),这两者的区别就是前者有过渡时间,后者直接切换

52.Time时间类的常用属性

(1)Time.timeScale时间的缩放,1是默认值,为0可用于游戏的暂停,为2可用于游戏的快进

(2)Time.time从游戏的第一帧开始计算,运行的总时间,会受Time.timeScale的影响和如果在FixedUpdate  内部调用时会变成Time.fixedTime

(3)Time.unscaledTime 从游戏的第一帧开始计算,同上,不会受Time.timeScale的影响

(4)Time.realtimeSinceStartup 游戏开始后所运行的真实时间,启动游戏就开始计算,不会受Time.timeScale的影响

(5)Time.fixedTime从游戏的第一帧开始计算,运行的总时间(由Time.fixedDeltaTime累加),会受Time.timeScale的影响

(6)Time.fixedUnscaledTime从游戏的第一帧开始计算,运行的总时间(由Time.fixedDeltaTime累加),不会受Time.timeScale的影响

(7)Time.timeSinceLevelLoad当前场景加载完开始计算,表示加载完场景到目前为止运行的时间,会受Time.timeScale的影响

(8)Time.deltaTime 上一帧到当前帧的时间间隔会受Time.timeScale的影响和如果在FixedUpdate 内部调用时会变成Time.fixedDeltaTime

(9)Time.fixedDeltaTime固定时间增量,不会受Time.timeScale的影响

注意:1.Time.timeScale不会影响Update和LateUpdate,因为Time.timeScale不会影响和帧率有关的事情, Update和LateUpdate是受到帧率影响而不是受到时间影响

           2.Time.timeScale会影响FixedUpdate,因为FixedUpdate是固定时间内运行固定的次数,不会影响时间间隔(fixedupdate的内部实现是用的计时器)

           3.Time.timeScale是会作用于整个游戏,不止当前的场景

           4.Time.timeScale的取值范围为(1~100)

53.控制一段时间不能连续输入的方法(这是举例参考,写法很多): 

 54.避免游戏物体被天空盒颜色影响的方法:在游戏的制作过程中,可以在相机的游戏对象上添加skybox组件,再把天空盒赋值给它,这样就可以避免游戏物体的渲染颜色被天空盒颜色所影响

 55.滚动视图的使用:创建UI时,可以直接创键滚动视图,其中的结构是由三个组成1.ViewPort加两个滑动条

 这两个为滚动条,可以参照Slider;ViewPort因为带有Mask组件因此起遮罩作用,只显示规定区域内的内容;ViewPort下面有个Content,这个就顾名思义,就是滑动框里面的内容,其越大越大内容就会显示越多,滑动范围也就越大(因此都会把需要滑动的内容放在这里面,经常结合Grid一起使用);介绍完这里两个后就介绍

 56.制作小型排行榜:使用dreamlo网站可以制作小型的排行榜功能,先解释下dreamlo的内容,添加用不到共有code(获取会用到)

 57.协程的使用注意:协程的开启必须必须要在激活的物体上进行

 58.在Scene窗口中画线:可以在update中使用Debug.DrawRay()方法就可以在scene窗口中绘制线,一般是配合射线一起使用,观察射线的状态

 59.使用代码为按钮添加点击事件: 给按钮添加点击事件时,除了使用拖曳的方式,还可以使用代码注册事件Button对象.onClick.AddListener(点击事件触发的函数);

60.List和数组转换:List和数组可以直接相互转换,方法参考ToList()和ToArray(),除了List和数组可以相互转换,string也可以和数组相互转换参考ToCharArray()和new string(数组)

61.关于配置文件ScriptableObject类的使用方法:参考该篇博客,写得相当详细,这里我就不再赘述。http://imyhq.com/game/10973.html#__1

62.判断项目使用平台: 在unity项目中,可能会涉及到不同平台的使用,例如在pc端,安卓端等甚至在编辑器环境中,都算是不同平台,如果在某些设置,或者数据上有差异的情况下就可以使用

#if UNITY_ANDROID

     Debug.Log( "Android" );//设置安卓平台

#endif

#if UNITY_IPHONE

     Debug.Log( "IOS" );//这是IOS平台

#endif

以下是可以参考的对应的平台使用的关键字 

63.UI自适应在ui自适应中,位置的自适应使用锚点进行定位,大小的自适应除了用锚点的边做自适应外还可以用Canvas Scaler中的UI Scale Mode的scale with screen size可以做ui大小的自适应,UI Scale Mode的三种模式分别为:

(1)constant pixel size:ui固定像素,也就是说ui的像素是固定的,相同的ui,在不同分别率的屏幕上,大小不一样(ui像素固定,屏幕分辨率越大ui越小)

(2)scale with screen size :按照屏幕比例缩放,也就是ui大小随屏幕大小自适应

(3)constant physical size :按照固定尺寸,也就是ui元素的尺寸始终保持不变

64.Dictionary的扩展:

因为字典的取值有两种方式,1.索引器取值[],2.TryGetValue();这两者的区别就在于使用 [] 运算符在确保键存在的情况下,可以更直接地获取字典中的值,但如果键不存在,将引发异常。而 TryGetValue 方法则提供了一种更安全的获取字典值的方式,无论键存在与否都不会引发异常。

但是TryGetValue 的取值会比较繁琐因此就可以对其进行扩展

c#中可以用类DictoryExtension类(需要静态)来对Dictionry自动进行扩展(扩展的方法也需要是静态的),也就是说可以使用Dictionry对象来使用DictionryExtension类中的静态方法,举个例子在Dictory类中有对象.TryGetValue方法,但是使用时,你需要先定义一个对象,再用该对象去接收TryGetValue方法返回的值,也就是需要

Int a;

对象.TryGetValue(键,out a);

这样就显得较为麻烦,因此就可以在DictoryExtension扩展类中写一个静态方法,就像这样

public static void TryGet<Tkey, Tvalue>(this Dictionary<Tkey, Tvalue> dict, Tkey key)

{

Tvalue value;

dict.TryGetValue(key, out value);

return value ;

}//注意这里的this的意思是当前的对象实例,也就是方法的调用者,因此在调用该方法时不用传递

//第一个参数,调用者对象就默认作为了第一个参数,并且泛型也是根据第一个参数默认指定,不需要自己指定

这样就可以直接通过Dictory对象.TryGet(键)就可以直接调用,而不用像上方那样麻烦

65.字符串类型转枚举类型:使用的是这个静态方法

public static object Parse(Type enumType, string value);

示例:

66.使用CanvasGroup控制整个页面对一个整体Panel下的UI元素进行控制时,可以使用Canvas Group组件,Canvas Group可以影响该组UI元素的部分性质,而不需要费力的对该组UI下的每个元素进行逐一得得调整。Canvas Group是同时作用于该组件UI下的全部元素。具体使用方法可以参考文档

67.解析json对象中有枚举类型:在解析json时,把json反序列化成对象时,如果对象中有成员有枚举类型的,就序列化不能序列化,因此就需要先创建个字符串来接收解析后的json,随后再把字符串转变为枚举类型,这个时候就需要把json解析成的类(带有枚举类型的变量)实现ISerializationCallbackReceiver这个接口(注意该接口和jsonutility对应)

法2:对象中存在枚举类型时,可以用回调函数解决问题。还有个方法,就是在写json 文件时,枚举类型的变量就不要写成字符串类型,可以直接写int类型,也就是写出需要的枚举类型对应的整数是多少,这样在转换时就可以直接进行转换

68.动画过渡:动画过渡中的Has Exit Time(是否开启退出时间)和Transition Duration(过渡时间)的理解在动画过渡中,Has Exit Time使用来控制动画是否播放到指定地点再跳转的,简单点来说,如果勾选Has Exit Time,那么当前动画必须要播放到Exit Time(这个是一个百分比)然后才进行跳转到目标动画,如果不勾选Has Exit Time,那么只要满足过渡条件就直接跳转到目标动画(比如射击游戏人物再换子弹时,换子弹动画必须播放完整才能跳转到目标动画,因此就需要勾选Has Exit Time,设置 Exit Time到指定百分比然后再跳转到目标动画;像控制人物跑到跳时,就需要立刻转换,因此就不要勾选Has Exit Time)。Transition Duration就是动画切换的过渡时间,有%和秒两个单位,Transition Duration为0的话就相当于两个动画直接切换,没有融合。注意,这个Transition Duration效果是在动画切换时就会发生。

因此可以看出在勾选了Has Exit Time,Transition Duration就发生在Exit Time之后

经过实验结论就是如果没勾选Has Exit Time,动画就可以随时发生切换,因此Transition Duration就会发生在你切换动画的时候(也就是Conditions的时候)

69.FindObjectOfType方法:该方法用于查找场景中第一个指定类型的组件,返回对该组件的引用。

比如Camera camera = FindObjectOfType<Camera>();注意,FindObjectOfType方法的查找范围是当前场景中的所有游戏对象,因此它的查找效率并不高,且会被场景中所有对象的组件所影响。\

70.预制件:预制件本质上是一种蓝图或者模板,当你在场景中使用一个预制件创建一个游戏对象时,它会根据预制件的设置自动创建一个该类型的实例化对象。注意,当使用预制件时,有两种方式,第一就是创建一个公共变量,然后用拖曳方式赋值该公共变量(拖拽功能只是把预制件的引用赋值给了这个变量(拖曳赋值就是将引用赋值给变量,无论是预制件的引用还是游戏对象的引用))(与游戏场景中的游戏对象拖曳给公共变量用法上存在区别,还需一步实例化),最后用Instantiate(prefab)实例化出对象;第二也是创建一个变量,但是可以不是公共变量,随后使用Resources.Load的方法赋值该变量

71.向下转型:C#中,子类对象可以直接转换为父类的类型,但是父类对象不能直接转换为子类的类型这是因为子类对象包含了比父类对象更多的成员和方法,直接转换会造成信息的丢失或类型不匹配的问题。但是,通过向下转型(Downcasting)可以将一个父类对象转换为子类对象。向下转型涉及到类型转换和类型检查两个过程。类型转换使用 as 关键字,语法为:

childClass obj = parentObj as childClass;

其中,parentObj 是一个父类对象,childClass 是一个子类类型。如果对象 parentObj 实际上是 childClass 的实例,则转换成功,并返回 childClass 类型的子类对象。否则,转换失败并返回 null。

需要注意的是,在进行向下转型之前,需要使用 is 运算符进行类型检查。类型检查使用 is 关键字,语法为:

if (parentObj is childClass)

{

    // 进行向下转型操作

}

这样可以确保在进行转型操作时不会出现类型错误或引发异常。向下转型是一种比较危险的操作,在实际应用中应该谨慎使用。

72.Instantitae方法:Instantiate() 方法的返回值是实例化出来的对象。通常情况下,我们将返回值赋给一个同类型的变量。例如:

GameObject newObject = Instantiate(prefab, position, rotation);

在这个例子中,实例化方法 Instantiate() 的返回值是一个 GameObject 类型的对象(这里可以用as进行转型,不转型也可以因为它内部源码时调用的泛型)。我们将返回值赋给变量 newObject,使其引用实例化出来的物体。需要注意的是,Instantiate() 方法并不会改变被实例化的原始预制体。当实例化出一个对象时,实际上是通过复制预制体的模型、材质、脚本和其他组件来完成的。因此,我们可以在任何时候销毁实例化出来的对象,而不会影响预制体本身。

73.相交球函数:用来获该球范围内的碰撞器(获得了一个圆范围的碰撞体后就可以使用刚体的AddExplosionForce给各个碰撞体施加一个爆炸力,实现爆炸效果

2D相交球函数,这个重载是返回如果圆形范围内有任意一个碰撞体与之重叠,那么该方法将返回与其碰撞的第一个碰撞体(可以用这个来检查2D游戏中敌人的自动巡回是否撞墙)

74.连续攻击代码的书写:待定,找test项目中的ContinueAttack脚本

75.控制音量关系:AudioMixer可以用来控制全局音量(master),背景音量和其它音量的关系(具体用法参考文档)

76.VideoPlayer的播放完视频事件:播放视频完成后的循环事件

VideoPlayer.loopPointReached 是 Unity 中 VideoPlayer 组件提供的一个事件,当视频播放到设定的循环点时就会触发该事件。其具体使用方法为通过 AddListener() 方法将事件处理函数注册到该事件上,例如:

// 获取 VideoPlayer 组件

VideoPlayer videoPlayer = GetComponent<VideoPlayer>();

// 注册循环事件处理函数

videoPlayer.loopPointReached += EndReached;

// 处理函数

void EndReached(VideoPlayer vp)

{

    vp.Play();  // 循环播放视频

}

在上述示例中,我们通过 GetComponent<VideoPlayer>() 获取了该物体中的 VideoPlayer 组件,然后通过 += 运算符将 EndReached() 方法注册到 loopPointReached 事件上。当视频播放到循环点时,就会触发该事件,并调用 EndReached() 方法。在 EndReached() 方法中,我们只是简单地再次调用 Play() 方法实现循环播放。你也可以在该方法内实现其他的操作,如切换到其他视频等。

使用videoplayer播放视频:使用videoplayer组件加raw image再创建Render Texture就可以实现

77.停止玩家输入:停止玩家输入可以是在输入语句前加入条件判断语句,如果满足停止输入条件就return

78.动画的应用:在动画中,除了可以在指定帧添加动画事件调用函数,还可以在指定帧修改游戏对象的参数等如scale,碰撞器的大小等,达到指定的效果

79.敌人死亡后取消碰撞:在人物玩家死亡后,在播放死亡效果时,要取消玩家角色和敌人之间的碰撞就可以使用Physics2D.IgnoreLayerCollision(),通过图层来取消碰撞。

80.位移运算符:在了解LayerMask类之前,先了解C#中的位移运算符,<<和>>,这里就不讲原理,就讲下使用效果,如X<<N,这句话的意思就是X向左或向左移动N位(其中X是二进制表示)(此处,X的类型只能是int,uint、long或ulong,N的类型只能是int),比如5<<2,该运算过程就是5转换为二进制为101,将101向左一动2位,右边用0补充,因此得到的结果为10100(二进制),转换为10进制就为20,右移运算符同理因此就有

81.LayerMask:LayerMask类中的图层(用来碰撞检测),其在每个图层都有个属于自己的Index索引,但是如果想要使用该图层时,不能直接使用该图层的索引(因为 LayerMask 是位掩码表示的(因此 LayerMask的值就为1<<Index)),需要使用图层的位掩码。因此LayerMask有三个静态函数

(1)LayerToName(图层的Index返回图层名)

(2)NameToLayer(根据图层名字返回图层索引Index)

例:int enemyLayerMask = 1 << LayerMask.NameToLayer("Enemy")

多层:int layerMask =(1<<10)|(1<<9);

(3)GetMask(直接根据图层名字返回图层的位掩码,而且还可以获取多个层的层掩码)

例:int layerMask = LayerMask.GetMask("Layer1", "Layer2", "Layer3");

注意gameObject.layer获取的是游戏对象的图层的索引Index

除了指定射线检测指定层外,还可以通过~(1 <<10)这样的方式来指定射线除了不检测索引Index为10的图层,其它图层都检测(设置Layer Collision Matrix似乎也能实现 )

82.四元数和欧拉角转四元数:

83.让敌人始终看向玩家:

(1) 1.在3D中,可以用敌人坐标减去玩家坐标,再把该向量赋值给敌人的forward向量(前提是forward向量刚好在敌人的视线正前方),如果forward没在正前方,那么就可以创建一个父物体,将敌人放在父对象中,并把敌人的视线正前方调增到和父物体的forward一致,随后直接给父物体的forward赋值即可。

          2.还可以用  transform.LookAt(target.position);方法(其实原理就是改变该游戏对象的forward向量)

        3.还有Vector3 direction = target.position - transform.position; // 计算向量

     transform.rotation = Quaternion.LookRotation(direction); // 应用旋转

该方法也是并使物体的正面(Z 轴)朝向目标向量 direction,不过它第二个参数默认为该物体的up向量,若指定第二个参数upwards,则物体的right方向将对齐至‘第一个参数’与‘第二个参数’的叉乘方向。

(2)1.在2D中,同理不过是给敌人的right向量进行赋值,如果right向量和敌人视线不一致,同上方一样。

        2.2D中还有另一种方法,计算角度,随后旋转敌人

因此代码就可以这样写(放在update中)(蓝色部分为代码)

//计算敌人和玩家的向量,target为玩家

Vector2 direction = (target.position - transform.position).normalized;

//这里注意如果按照上面如图敌人的初始朝向,那么这里的direction.y, direction.x应该交换//下顺序Mathf.Atan2()和 Mathf.Atan()的区别是前面一个是需要两个参数为x,y;第二个//就一个参数y/x,第二个不能避免分母为0。 Mathf.Atan2(direction.y, direction.x) 只能计算//出弧度,需要 Mathf.Rad2Deg才能计算出角度。

float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;

// Quaternion.AngleAxis(angle, Vector3.forward)的意思是围绕Z轴,旋转到angle这个度数(与//旋转angel度数有区别,可以理解为直接在inspector中设置rotation一样)

//然后赋值给敌人的rotation,让其旋转;注意这里因为unity中z轴正方向朝向屏幕里面。//因此angel为正时(angel的正负由x,y决定,具体参考正切函数),从屏幕外=>内看就会是//逆时针旋转了angel度,(但是从屏幕内=>外看就会是顺时针旋转了angel度,)因此,

//这里要么在angel加-号,要么Vector3.forward加-号,保证旋转的正常

transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);//其实这个过程就可以理解//为在inspector中输入数值设置一样,和上一次的朝向没有关系(也就是不是旋转angel度,//而是旋转到angle度)(这样理解也就不会出现在update中一直旋转angel的情况了)。

84.异步加载:异步加载LoadSceneAsync;1.为什么要使用异步加载呢?这就要相比于同步加载LoadScene来说,当你需要加载下一个游戏场景的时候,如果下一个场景资源很多,如果使用同步加载的方式就会在你调用LoadScene的下一帧执行加载场景的命令,直到加载完才会在开启下一帧,这就会导致游戏出现卡顿的情况,影响游戏体验,而异步加载就解决了这一问题,不需要在某一帧执行加载完场景的命令,在开启异步加载后也不会影响其它帧的执行,可以直到异步加载完后再跳转场景,参考Unity 异步加载场景_unity异步加载场景_shadowsghost的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/shadowsghost/article/details/126462985同步加载与异步加载的区别_异步加载和同步加载的区别_忆弥的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_43692768/article/details/110147715

怎么实现异步加载进度条的制作参考 

【Unity】场景异步加载的进度条制作_Zok93的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/sinat_20559947/article/details/50000455

或者public class LoadGame : MonoBehaviour {

    public Slider processView;

// Use this for initialization

void Start () {

        LoadGameMethod();

        

}

// Update is called once per frame

void Update () {

    }

    public void LoadGameMethod()

    {

        StartCoroutine(StartLoading_4(2));

    }

    private IEnumerator StartLoading_4(int scene)

    {

        int displayProgress = 0;

        int toProgress = 0;

        AsyncOperation op = SceneManager.LoadSceneAsync(scene);

        op.allowSceneActivation = false;

        while (op.progress < 0.9f)

        {

            toProgress = (int)op.progress * 100;

            while (displayProgress < toProgress)

            {

                ++displayProgress;

                SetLoadingPercentage(displayProgress);

                yield return new WaitForEndOfFrame();

            }

        }

        toProgress = 100;

        while (displayProgress < toProgress)

        {

            ++displayProgress;

            SetLoadingPercentage(displayProgress);

            yield return new WaitForEndOfFrame();

        }

        op.allowSceneActivation = true;

    }

    private void SetLoadingPercentage(float v)

    {

        processView.value = v / 100;

  }

85.人物和道具关于触发器的优化:人物和道具的触发效果,可以在控制人物的脚本中通过在Trigger函数使用switch语句判断碰撞到的collider的tag,然后case执行效果,这样道具就只用添加collider,不需要再给道具添加效果脚本

86.以局部坐标系添加力:Rigidbody.AddRelativeForce()是以自身坐标系给刚体添加力

87.安卓端不能像PC端读取json,www或者unitywebrequest:

Unity 安卓发布配置文件读取问题_牛神自的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_41155760/article/details/91489618

88.以规定进制进行Convert:Convert.ToInt32(a, 2);意思是表示字符串a是2进制数,Convert.ToString(e, 2)意思是将e转化为2进制字符串

89.Dotween的tweener的应用:

tweener.PlayForward();//这个是重新播放,相当于从头开始播放视频          

 tweener.Play();//该函数是继续播放的意思,就行继续播放视频

(注意这里是用的tweener对象来调用函数)

90. 版本控制,集中式和分布式:版本控制分布式(git)和集中式(svn)最大的区别在于当服务器出现故障时,svn就不能进行上传和下载,也就是可以进行更改但是只能更改,不能进行上传,看到自己的更改记录,gti是在本地有个仓库,还有个远程仓库(服务器),就算服务器出问题,开发人员照样可以进行更改提交(commit),同时也会有提交记录,只是无法将本地仓库的内容push到远程仓库。因此可以说svn就一个提交,但是git有两个(一个commit提交到本地库,一个push将本地仓库上传到远程仓库)

91.懒汉单例模式:在进行单例模式书写时,如果进行懒汉单例模式的写法,需要注意在静态方法中,不能通过this和ganmeobject来获取组件,可以通过GameObject.Find()来获取(因为该方法也是静态方法)

92.文本在unity的存在形式:文本在unity中是TextAsset类型,读取文本除了IO流的形式,也可以用Resources的方式读取

93.解析json成数组:JsonUtility相比于litjson,JsonUtility不能将json解析成数组,它只能解析成对象,而litjson可以将json文件解析成数组。

94.动态解析json格式:如果想要动态解析json文件格式时,可以使用LitJson中的jsondata类型来接收,随后通过json数据的重新区分符来区分即可(通过jsondata[“”]来访问)

95.文本框自适应:有两种方法

1.使用Content Szie Fitter组件挂载在文本对象上,把Hrozantal Fit和Vertical Fit都设置为PreFerren Size,这就有文本框自适应效果,接着就可以添加文本框背景,需要添加到其子物体上,这时就会出现因为是子物体的情况,背景就把文本遮盖住了,因此还需要添加一个文本对象和父对象的文本保持一致即可(保持一致需要用到代码控制)类似于这种结构

2.直接使用在文本框的背景图上添加Content Szie Fitter并设置最佳适应,再添加Vertical Layout Group(或者Hrizental Layout Group);随后再为文本框背景图添加一个文本子物体,添加Content Szie Fitter并设置最佳适应即可

96.UI跟随鼠标:

法1:直接将Input.mousePosition的鼠标坐标赋值给UI的坐标,

法2.用 RectTransformUtility.ScreenPointToLocalPointInRectangle()函数先将Input.mousePosition鼠标屏幕坐标转换为rect transform坐标,随后再将UI坐标赋值,类似于这样

97.富文本:要想要控制文本指定内容的指定效果,比如指定颜色,就需要用到富文本

98.区别鼠标点击的左右键:当使用鼠标点击的回调事件时,如果要区分是鼠标左中右哪一个触发的,可以在eventData.button中获取到该数据,该数据是属于PointerEventData.InputButton的一个枚举类型

99.Unity读取文件:Unity 读取文件 TextAsset读取配置文件方式:1 支持文件类型:.txt .html .html .xml .bytes .json .csv .yaml .fnt

100.OnTriggerEnter2D的坑:在OnTriggerEnter2D函数中不要用else if进行判断,好像会失灵

101.按照指定行读取文本:如果要读取IO流时,想要从指定行读取的话需要自己构建函数,返回指定行数的字符串

102.各个路径的区别:这里只讲路径存放位置,没有说其使用范围

Application.dataPath默认路径的Unity中的Assets

Application.streamingAssetsPath 的默认路径是Unity中的Assets中的streamingAssets文件夹(需要自己创建)

Application.persistentDataPath是直接放在本地电脑路径C:\Users\User\AppData\LocalLow\DefaultCompany\projectname\data(因此是用来放持久化数据的)

103.插件Avpro的坑:在使用avpro控制视频播放时,用slider来反映和控制视频进度,如果直接

这样的话就会出现矛盾,到底是视频进度控制slider还是slider控制视频进度,就会出现卡顿;解决办法就是设置一个中间值,正常播放视频时,总是把视频进度赋值给中间值,随后将中间值赋值给slider(也就是视频进度控制slider),当拖动slider进行改变时,就判断slider的值是否等于中间值,如果不等于就让slider的值控制中间值

104.给动态创建的按钮添加事件:在动态创建按钮如果想要动态添加点击函数,就有可能导致数组越界的问题动态添加按钮点击事件导致数组越界的问题通常是由于闭包引起的。在你的循环中,你使用了一个委托(delegate)创建了一个闭包,但是在闭包中,循环变量i的值会随着循环的进行而改变。由于闭包会在循环结束后才执行,所以在添加按钮点击事件时,所有的闭包都会引用循环结束时的i的值,而不是每次迭代时的不同值。解决这个问题的一种常见方法是创建一个临时变量,将循环变量i的值复制给临时变量,并在委托中使用临时变量。

在修改后的代码中,创建了一个名为index的临时变量,并将循环变量i的值复制给它。在委托中使用的是临时变量index,确保每个按钮的点击事件都引用了正确的索引值。通过这种方式,你应该能够避免数组越界的问题。

105.Layout和Content Size Fitter的注意事项:如果使用layout和content size fitter组件实现父对象随子对象内容多少而变化大小的话如果父物体在扩大到一定范围后不变后考虑一下约束是否设置正确

106.碰撞检测失效:在使用了navmesh时,如果在检查了代码等没问题的情况下,在碰撞后不发生碰撞检测就可以考虑下是不是刚体组件勾选了Iskinematic,如果勾选了那么就只能进行触发检测了,不能进行碰撞检测。(使用navmesh时刚体勾选使用重力和Iskinematic会在碰撞检测或者其它地方出奇怪的bug具体原因还不清楚)

107.Stopwatch类测试性能:可以使用Stopwatch类对代码段进行性能测试

  1. Stopwatch timer=Stopwatch.StartNew();//声明计时器

                 timer.Start();//开启计时器

                 //执行代码

                 Timer.Stop()//停止计时器

                 Timer.Elapsed();//返回计时器信息

108.输入监听应该放在update中:当做角色控制时,都应该是在update中监听输入和计算出移动量等,真正移动或者控制玩家的代码应该是在fixupdate中

 109.旋转控制:控制物体旋转transform.Rotate transform.RotateAround可以指定旋转的点和轴

 110.静态类进行扩展方法:使用静态类可以进行方法的扩展https://zhuanlan.zhihu.com/p/472276774

 111.打图集图片重合的问题:打包图集时如果一张照片会出现重叠时,可能是照片的打包方式设置为了紧密型,可以设置为全矩形就能解决

 112.多线程报错:在unity中我们使用多线程时。用子线程调用主线程时。用到unity的东西时就会报如下的错误。

CompareBaseObjectsInternal 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中的API不能在多线程中使用,因此注意例如加载资源Load函数等如果想要加载大量资源就不能开启多线程加载,直接使用异步加载的load解决

https://www.ngui.cc/article/show-828516.html?action=onClick

https://www.taikr.com/article/3932

113.Resources.load<T>加载不确定类型资源:使用Resources.load<T>加载资源时,如果T无法确定,用泛型代替就会报错,解决思路用Resources.load方法加载成object对象,随后进行向下转型。还可以用Function委托,将Resources.load<T>方法在具体的实现类中实现

114. 设置渲染遮罩:使用transform.SetSiblingIndex()方法可以设置物体的渲染遮罩

115.Dotween的正/倒播:Dotwenn想要进行倒播时必须先进行一次正播后才能进行倒播(也就是必须PlayForwardPlayBackwards结合使用才行

116.不确定类型又想调用泛型方法:在不确定泛型类型的情况下,又想调用泛型方法就需要用到反射

117.可以用Texture创建Sprite 

118. 使用Awake实例化的问题:当脚本A要调用脚本B的方法时,由于脚本B可能还没激活就导致不能Awake,因此就可以将脚本B的初始化放在初始化方法中,随后在脚本A调用脚本B方法前手动调用脚本B的初始化方法

在Unity中,`Awake` 方法是在对象被激活或加载时被调用的,因此在对象未激活时调用 `Awake` 方法是不直接支持的。然而,你可以通过使用 `Awake` 方法之外的其他方式来实现类似的功能。

一个可能的方法是使用自定义的初始化方法,而不是直接依赖于 `Awake`。以下是一个示例:

```csharp
using UnityEngine;

public class ScriptB : MonoBehaviour
{
    private bool isInitialized = false;

    // 自定义的初始化方法
    public void CustomInitialize()
    {
        // 在这里进行初始化操作
        Initialize();
    }

    void Initialize()
    {
        // 初始化变量和执行其他操作
        isInitialized = true;
    }

    public void Func1()
    {
        // 在这里调用需要在初始化后执行的逻辑
        if (!isInitialized)
        {
            Debug.LogError("ScriptB is not initialized yet!");
            return;
        }

        // 继续执行其他操作
    }
}
```

然后,在脚本A中,你可以这样调用:

```csharp
using UnityEngine;

public class ScriptA : MonoBehaviour
{
    public ScriptB scriptB;

    void OnEnable()
    {
        // 检查 ScriptB 是否为空
        if (scriptB != null)
        {
            // 调用 ScriptB 的自定义初始化方法
            scriptB.CustomInitialize();

            // 调用 ScriptB 的 func1 方法
            scriptB.Func1();
        }
    }
}
```

在这个例子中,我将 `Awake` 方法中的初始化逻辑移至了 `CustomInitialize` 方法中,并在需要时调用这个方法。这样你就可以在脚本A中在物体激活之前执行初始化逻辑。当然,前提是你有控制脚本A何时激活物体的能力。

  • 0
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity开发,有一些小技巧可以提高效率和方便开发。以下是一些常用的Unity开发小技巧: 1. 锁定角度和移动对象:在旋转对象时按住Ctrl/Cmd键可以锁定角度,同样的方法也适用于移动对象。要修改锁定的默认值,可以到Edit->SnapSettings进行修改。 2. 对齐锁定技巧:当移动对象时,按住V键可以启用节点锁定,可以将两个点简单对齐,对于关卡对象排列有帮助。 3. 直接开启说明文件:在组件选单内点击蓝色的问号图标,可以直接打开说明文件。 4. 在Play模式下修改数值:如果在Play模式下调整数值后发现不合适,可以点击组件列表右上方的小齿轮,然后选择CopyComponent将组件复制一份,在退出Play模式后再点击小齿轮选择PasteComponent将复制的组件粘贴回去。 5. 使用Layers按钮管理显示或隐藏对象:使用Layers按钮可以管理显示或隐藏对象。例如,可以将一些只在编辑初期使用的坐标对象归类到一个Layer,当要隐藏这些对象时,只需点击Layers按钮,然后将该图层右边的眼睛图标取消勾选即可。 6. 在Profiler切换图表显示:在Profiler,可以点击Drawcalls、Scripts、Rendering、VSync等名称左边的小方块来切换是否显示相应图表。 7. 调整Playmode tint颜色:初学者在Play模式下修改东西后,可能在停止播放后发现所有的修改都恢复到调整前。为了避免这种困扰,可以在Preferences设置的Colours/Colors调整Playmode tint颜色,这样你就可以很容易地分辨是否在播放模式。 这些小技巧可以帮助开发者更高效地使用Unity进行开发和调试。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Unity 开发的十个实用小技巧](https://blog.csdn.net/weixin_46052359/article/details/115072925)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值