当前位置: 首页 > news >正文

仿星露谷物语开发总结VIP(Unity高级编程知识)

1、生命周期函数

(1)有哪些生命周期函数

Awake/OnEnable/Start/Update/FixedUpdate/LateUpdate

(2)生命周期函数的时间点/次数/顺序

函数名

触发时间点

顺序

Awake

脚本实例被加载时,1次

最早

OnEnable

对象被激活时,多次

Awake之后

Start

Update第一次调用前,1次

OnEnable之后

Update

每帧调用一次,多次

Start之后

FixedUpdate

固定时间间隔

LateUpdate

Update之后调用,多次

Update之后

(3)生命周期函数中可以返回IEnumerator类型的是

Start函数,定义为:

private IEnumerator Start(){}

2、物体GameObject相关

(1)当前对象

gameObject或transform.gameObject

(2)实例化物体

Instantiate(GameObject物体,  世界坐标的Vector3位置,旋转方向, 父对象Transform)

后面2个参数可以省略

(3)销毁物体

Destroy(GameObject物体)

(4)查找对象的常用方法

1)通过名称找别的对象:GameObject.Find(“name”)

2)通过标签找别的对象:GameObject.FindWithTag(“Player”)

3)查找一级子节点对象:transform.Find(“name”).gameObject

(5)返回实例ID

GameObject实例.GetInstanceID()

(6)判断自身的激活状态

activeSelf属性

3、transform组件

(1)获取对象的世界坐标位置

transform.position

(2)获取对象的父对象

transform.parent.gameObject

(3)查看对象下的子对象的Transform

transform.Find(名称) 返回Transform类型

(4)物体移动

transfrom.Translate(dir * speed * Time.deltaTime)

4、外部输入

(1)鼠标左键点击一下

Input.GetMouseButtonDown(0)

(2)鼠标左键持续点击

Input.GetMouseButton(0)

(3)按一次键盘A

Input.GetKeyDown(KeyCode.A)

(4)持续按键盘A

Input.GetKey(KeyCode.A)

(5)获取水平方向轴值

float horizontal = Input.GetAxis("Horizontal");

(6)获取垂直方向轴值

float vertical = Input.GetAxis("Vertical");

(7)鼠标的位置

Input.mousePosition

(8)鼠标拖拽的方法

类实现IBeginDragHandler、IDragHandler、IEndDragHandler接口,

然后在OnBeginDrag、OnDrag、OnEndDrag函数中实现功能

(9)鼠标滑过的方法

类实现IPointerEnterHandler, IPointerExitHandler接口,

然后在OnPointerEnter、OnPointerExit函数中实现功能

(10)鼠标点击的方法

类实现IPointerClickHandler接口

然后在OnPointerClick函数中实现功能

5、声音

(1)播放简单流程

首先,有一个Audio Listener组件,一般挂在Main Camera下面。

然后,给对象放Audio Source组件,然后配置声音Clip

接着,通过AudioSource player = GetComponent<AudioSource>()获取组件对象

最后,播放音乐

  1. 背景音乐:player.clip = xx; player.Play()
  2. 音效:player.PlayOneShot(yy)

6、碰撞

(1)两个物体碰撞的前提条件

2个条件:

  1. 两个物体都有碰撞器,比如BoxCollider2D组件
  2. 其中一个物体有RigidBody组件

(2)获取碰撞物体

private void OnCollisionEnter(Collision collision){

       collision.gameObject

}

(3)设置碰撞体不碰撞

在Project Settings中设置碰撞矩阵,设置Layer之间不碰撞

7、标签

(1)自定义新标签方法

使用PropertyAttribute定义新的标签属性

使用PropertyDrawer告诉Unity如果在Inspector中渲染这些属性

(2)[SerializeField]

序列化私有属性

(3)[System.Serializable]

序列化自定义对象

(4)[HideInInspector]

Inspector中隐藏字段,减少视觉混乱

(5)[RequireComponent(typeof(<T>))]

当前脚本执行的前置组件,如果没有会自动添加

(6)[ExecuteAlways]

在编辑模式和播放模式下都能够运行

(7)[ContextMenu]

编辑器模式下工作,右键出现在菜单中,点击会执行对应函数。

8、组件

(1)类和组件的关系

类继承了MonoBehaviour时就变成了组件。

(2)查找组件的常用方法

1)当前对象的组件:GetComponent<T>()

2)当前对象及子对象的组件:GetComponentInChildren<T>()

3)对象父对象链的组件:GetComponentInParent<T>()

4)整个场景中按照类型找组件:FindObjectOfType<T>()

(3)Image组件

(3.1)Type参数:Simple/Sliced/Tiled/Filled

1)Simple:普通图片显示

2)Sliced:切片,将图片分为核心和边缘两部分,核心可以缩放,边缘不进行缩放

3)Tiled:图片正常大小显示,然后调整w/h值会出现剪切、堆叠的效果

4)Filled:适合做特效的填充模式

(3.2)Preserve Aspect属性

图像将保持其原始宽高比进行缩放

(4)Sorting Group组件作用

优化2D游戏中的渲染顺序和性能。

将一组Sprite Renderer作为整体一起绘制从而减少绘制次数,提高渲染效率。

比如角色由身体、衣服等多个部分组成,每个部分都有自己的Sprite Renderer,那么给这个角色这个角色添加一个Sorting Group可以帮助优化这些部分的渲染。

(5)UI布局组件

组件名称

功能

用途

AspectRatioFitter(纵横比适配器)

保持对象的固定宽高比,影响单个元素

图片显示时防止拉伸变形

Canvas Scaler

调整UI元素的缩放比例,影响整个画布

根据不同屏幕尺寸自动调整UI元素的大小

Vertical Layout Group(垂直布局组)

将子元素按照垂直方向布局,并设置间距、对齐方式

自动排列按钮、文本等UI控件

ContentSizeFitter(内容尺寸适配器)

根据子元素的内容调整父容器的大小

子元素数量或大小变化时自动调整父容器的大小

(6)TextMeshPro组件

比Text组件更优秀的文本组件

(7)渲染模式screen space-overlay/screen space-camera/world space

Overlay:覆盖在所有内容上,不随摄像机变化,比如菜单栏

Camera:随着摄像机移动,但是保持一定距离不变,比如跟随角色的生命值条

World space:作为游戏对象,比如商店的招牌

(8)RigidBody组件作用

模拟重力、碰撞、力的作用等物理效果

(9)RigidBody的Body Type选项Dynamic/Kinematic/Static

Dynamic:完全受物理引擎控制,受重力、力和碰撞影响。可与其他物体发生物理交互。一般用于角色、敌人、可移动的道具。

Kinematic:不受重力和力影响,位置由Transform或脚本控制(rigidbody2D.MovePosition执行移动)。仍可参与碰撞检测,但不会被物理力推动。一般用于平台(如移动的传送带)、NPC路径移动

Static:完全静止,不受物理影响。仅作为碰撞体存在,不会移动或旋转。一般用于地形、墙壁、固定障碍物。

9、时间Time

(1)一帧的时间

Time.deltaTime

(2)游戏暂停和恢复

Time.timeScale=0f;

Time.timeScale=1f;

(3)时间的数据类型

float

10、事件Event

(1)超过16个参数的事件处理流程

1)定义委托:public delegate void delegateFunc(参数)

2)定义EventHandler类/Event/触发函数

public static class EventHandler{  // 定义类

       public static event delegateFunc xxEvent;  // 定义事件

       public static void callXXEvent(参数){  // 定义触发函数

              if(xxEvent != null){

       xxEvent(参数)

}

}

}

3)订阅事件:EventHandler.xxEvent += func(参数)

4)发布事件:EventHandler.callXXEvent(参数)

(2)小于16个参数的事件处理流程

没有定义委托这一步

将定义事件换成public static event Action<参数> xxEvent;

其他的和上一个处理流程一致。

(3)OnDisable未取消订阅事件的影响

首先,会导致内存泄漏。若订阅事件的对象被销毁,而事件仍保留对该对象方法的引用,这个对象就无法被垃圾回收。长此以往,内存占用会不断增加。

其次,会造成空引用异常。当订阅事件的对象已被销毁,但事件触发时,仍会尝试调用该对象的方法,这就会引发NullReferenceException,导致游戏崩溃。

然后,会造成方法重复调用。若对象反复启用和禁用,每次启用时都会进行事件订阅。要是没有在禁用时取消订阅,同一方法就会被多次订阅,事件触发时该方法会被多次调用。

11、动画

(1)Animator.StringToHash方法

将字符串转换为哈希值,用于AnimatorController中引用的参数、触发器,减少字符串比较提高性能。

使用方法:

Animator animator = GetComponent<Animator>();

int jumpTrigger = Animator.StringToHash(“Jump”);

animator.SetTrigger(jumpTrigger);

(2)使用触发器触发动画的常见方法

Animator animator = GetComponent<Animator>();

animator.SetTrigger(“xx”);

(3)Animator组件中的Apply Root Motion属性的作用

动画中的位移能够直接应用到角色。

如果不勾选该选项,动画中向前跑则只能原地跑了。

(4)获取当前动画信息

animator.GetCurrentAnimatorStateInfo

可以获取动画名称、持续时间 、当前进度、是否循环等信息

(5)Has Exit Time属性

是动画状态机中两个状态之间过渡的属性。启用该选项,当前动画必须播放到一定比例或时间后才能触发过渡。反选该选项,过渡可以立即生效,无需等待当前动画完成。

12、Tilemap地图系统

(1)创建Tilemap流程的4步

1)在Hierarchy下创建Tilemap,此时会出现Grid和Tilemap对象,可创建多个Tilemap对象进行分层

2)打开Tile Palette面板,把图片拖进去后生成对应的palette

3)使用Bush把palette绘制到Tilemap地图上

4)给Tilemap对象添加Tilemap Collider组件,同时使用Composite Collider优化碰撞体

(2)Grid类的含义

1)它是地图系统的“容器”

2)控制了所有Tilemap子对象的对齐和布局规则

3)一个场景中通常是一个Grid对象

(3)Tilemap类的含义

1)用于绘制瓦片的地图层

2)每个Tilemap是一个网格化的地图层,用于放置Tile(瓦片)

3)一个Grid下可以有多个Tilemap子对象(比如背景层、碰撞层)

(4)世界坐标和网格坐标互转的方法

世界 -> 网格:Grid实例(或Tilemap实例).WorldToCell( worldPosition);

网格 -> 世界:Grid实例(或Tilemap实例).CellToWorld( gridPosition);

(5)Tilemap函数

(5.1)清空所有瓦片

Tilemap实例.ClearAllTiles()

(5.2)设置瓦片

Tilemap实例.setTile(gridPosition, tile实例);

(6)获取网格的世界坐标位置

Grid类. GetCellCenterWorld(gridposX, gridPosY, gridPosZ);

13、Camera

(1)角色在边界显示一半黑屏的解决方案

1)角色由Cinemachine负责跟随显示,需要给该对象添加Cinemachine confiner组件。

2)在该组件中配置Bounding Shape的碰撞体

3)然后在Cinemachine组件中指定extension为cinemachine confiner组件。

14、数据存储

(1)ScriptableObject的功能和文件形式

SO是数据存储的容器。

文件为.asset的格式

(2)ScriptableObject的使用流程

1)创建类继承自ScriptableObject类,类中定义好属性信息

2)在CreateAssetMenu标签中指定fileName和menuName信息

3)在Asset下右击根据menuName和fileName选中选项进行创建

4)在创建的资源中定义好属性信息

5)在别的类的SO字段中拖入该ScriptableObject进行数据读取

(3)自定义类型数据写入文件的类型转换

自定义类型转为可序列化的,

比如Vector3类型不能使用,需要创建自定义的Vector3Serializable,该类型需要通过[System.Serializable]进行标记

15、坐标空间

(1)对比世界坐标/视口坐标/屏幕坐标/网格坐标的英文和作用

坐标空间

作用

世界坐标World Space

游戏世界中物体真实位置

视口坐标Viewport Space

相对于摄像机的比例坐标,左下角是(0,0),右上角为(1,1)

屏幕坐标Screen Space

像素坐标,比如分辨率为1920*1080,左下角是(0,0),右上角为(1920,1080)

网格坐标GridPosition

基于Tilemap的地图系统,左上角是(0,0)

(2)世界坐标转视口坐标、屏幕坐标的方法

转视口坐标:Camera.main.WorldToViewportPoint(worldPosition)

转屏幕坐标:Camera.main.WorldToScreenPoint(worldPosition)

(3)屏幕转世界坐标方法

Camera.main.ScreenToWorldPoint(screenPosition)

(4)鼠标操作屏幕在哪个坐标空间

屏幕坐标

16、系统函数

(1)判断当前是否在播放模式

Application.IsPlaying(gameObject)返回bool类型

(2)退出游戏及执行环境

Application.Quit();

运行模式下执行,编辑器模式下不成功

17、编辑器函数

(1)代码中变更SO数据通知编辑器变更

EditorUtility.SetDirty(SO对象);

(2)定义编辑器模式下执行的代码

#if UNITY_EDITOR

#endif

18、UI

(1)全透明颜色

Color.clear

(2)Pixels Per Unit

每个单位像素数量

在U3D空间中,一个单位通常被设计为一米。

(3)2D纹理类

Texture2D

(4)获取纹理中的像素信息的使用方法

Texture2D实例.GetPixels(),返回Color[]类型

(5)设置纹理中的像素信息的使用方法

Texture2D类型.SetPixels(Color[]类型数据);

Texture2D类型.Apply();   // 使之生效

(6)纹理(Texture)和材质(Material)的区别

纹理 是粒子的 “皮肤”,定义外观细节。
材质 是粒子的 “渲染规则”,决定如何显示纹理。

(7)Texture类的filterMode属性

使用方法:对纹理在缩放时的采样方式进行控制。

Point(最近邻):纹理在缩放过程中,会保留清晰的像素边缘,比较适合像素艺术风格的游戏

Bilinear(双线性):通过对相邻像素进行平均处理,使纹理边缘看起来更加平滑

Trilinear(三线性):和双线性类似,但在处理Mipmap层级之间的过渡时效果更好

19、粒子系统

(1)怎么产生粒子特效

Hierarchy下创建一个物体,该物体添加Particle System组件,配置相关参数

该物体放到哪里,哪里就会有相应的粒子特效。

(2)重要参数

参数名

作用

Duration

整个粒子系统的存在时间

Start Lifetime

单个粒子的存活时间

Gravity Modifier

重力对粒子的影响程度,正值向下,负值向上

Simulation Space

粒子系统的坐标空间,

Local:局部空间,跟着发射器一起动

World:世界空间,粒子一旦生成就脱离发射器的影响

Emission-Rate Over Time

每秒发射的粒子数

Emission-Bursts

一次性发射粒子的数量

Shape – Shape

粒子发射器的形状

Texture Sheet Animation

将一个纹理图集应用到粒子系统上,一般是选图片

Renderer

渲染方式,一般选图片方式,并且设置Sorting Layer

20、协程

(1)协程用法最简单写法

构造协程方法:

IEnumator routerFunc(){

       // do something

       yield return new WaitForSeconds(2f);

}

使用方法:

StartCoroutine(routerFunc());

(2)yield return null的含义

协程暂停执行直到下一帧

100、难点解读

(1)GetAxis和GetAxisRaw的异同

相同:都是获取输入轴上的值

不同:

特性

GetAxis

getAxisRaw

返回值范围

[-1,1]浮点数

只有-1,0,1

是否平滑

是(有加速/减速效果)

无,立即切换

输入延迟

常用场景

平滑移动、模拟摇杆控制

精确控制

(2)角色移动的方法差异

组件

方法

优点

缺点

Transform组件

修改transform.position

简单

没有物理特性

Transform组件

transform.Translate(Vector3.forward * speed * Time.deltaTime);

Rigidbody组件

Rigidbody对象.velocity=dir * speed

直接控制速度

干扰正常物理模拟

Rigidbody组件

Rigidbody对象.AddForce(dir*力量)

模拟真实推力

Rigidbody组件

rb.MovePosition(pos)

保持物理特性的平滑

CharacterController组件

characterController.Move(moveDirection * speed * Time.deltaTime);

简单碰撞检测及爬坡

物理特性功能有限

(3)gameObject和GameObject的区别

1)gameObject

MonoBehaviour类自动提供的一个属性,指向当前脚本的GameObject实例。

比如gameObject.name可以获取实例名称

比如gameObject.AddComponent<XX>()可以添加组件

2)GameObject:

Unity引擎的一个类,用于创建和操作游戏对象

比如GameObject.Find来查找对象

(4)onXxDrag、OnPointerXx、OnPointerClick函数是否出现在自身脚本的区别

如果对象脚本中包含这两个函数,则可以在函数中获取对象的属性。

如果要获取其他对象的属性,则需要使用PointerEventData的pointerCurrentRaycast.gameobject获取对方物体的信息

(5)2D场景中检测碰撞方法对比

方法名称

功能

是否分配内存

使用建议

Physics2D.OverlapBoxAll(...)

检测矩形区域内所有Collider2D

分配新数组

简单易用,适合不频繁调用

Physics2D.OverlapBoxNonAlloc(...)

检测矩形区域内所有Collider2D

不分配新内存

高频调用时推荐,避免GC

Physics2D.OverlapPointAll(...)

检测某一点上的所有Collider2D

分配新数组

点击检测、鼠标拾取等

(6)AnimationClip和AnimationControl的区别

AnimationClip是一个具体的动画数据文件,存储了对象在时间线上的属性变化(比如位置、旋转、缩放、材质参数等)。

AnimationControl是一个状态机工具,用于管理多个AnimationClip之间的逻辑和过渡(如切换条件、混合、层级)。

101、C#知识

(1)Invoke方法

延迟执行某个不带参数的方法

Invoke("MyMethod", 2f);   // 2秒后调用 MyMethod 方法

(2)委托函数

定义:一种类型安全的函数指针。 声明方式:public delegate 返回类型 委托名(参数类型 参数名, ...);  // 就是在返回类型之前增加了delegate关键字。

定义:委托对象使用+运算符合并相同类型的委托。
作用:创建一个委托要调用方法的列表,然后依次执行。

委托机制允许委托类型方法的参数作为参数传递给其他方法。

(3)为什么不用Delegate构造事件

Delegate对象在订阅事件时直接用=而不是+=,则会影响其他订阅的服务。

Event是一种特殊的delegate,限制订阅者只能通过+=进行订阅,否则编译报错。

(4)Event触发时为什么要判空

如果没有订阅者,则Event实例没有初始化,此时触发会报未引用的异常。

(5)virtual和abstract函数的区别

Virtual:虚拟的模板,提供了一个默认的实现,允许派生类根据需要进行重写(override),也可以不重写。

Abstract:抽象的概念,仅声明了方法,没有实现,强制非抽象派生类必须提供实现。

(6)Dictionary获取值的方法

Dictionary实例.TryGetValue(key, out value)

(7)获取类型的默认值

default()函数

(8)类型转换函数

as

(9)Mathf. Approximately方法

Mathf.Approximately(float a, float b)

作用: 用于比较两个浮点数是否“近似相等”。

由于浮点数在计算机中的精度问题(如 0.1f + 0.2f != 0.3f),直接使用 == 比较两个浮点数可能会导致误差。Mathf.Approximately 提供了一种更安全的方式来判断两个浮点数是否足够接近,可以认为是“相等”。

(10)Mathf.MoveTowards方法

Mathf.MoveTowards(float current, float target, float maxDelta)

作用: 将一个值从当前值向目标值移动,但不会超过指定的最大步长。 这个方法常用于平滑地改变某个值(比如位置、角度、速度等),避免一次性跳变到目标值。

参数:

current: 当前值。

target: 目标值。

maxDelta: 每次调用允许移动的最大步长(正值)。

102、架构设计

(1)角色移动的动画设计

1)角色分多个身体部位,每个身体部位一个Animator,动画入参保持一致。

2)在Update中计算移动速度及身体部位的状态值,同时触发动画事件实现走路/跑步。

3)在FixedUpdate中通过Rigidbody组件的MovePosition方法实现移动。

(2)实现树木淡入淡出的设计

1)给树木添加Box Collider组件,并设置为trigger

2)添加自定义的Fader脚本组件,在该脚本中提供淡入淡出的函数,通过SpriteRenderer组件逐步改变透明度实现淡入淡出。

3)给角色添加另一个TriggerFade自定义组件,在OnTriggerEnter函数中触发每个树木的淡入淡出函数

(3)从库存栏中选中Item拖到游戏世界中的思路

1)创建拖拽用的item预制体

2)开始拖Item时,创建预制体实例,把该Item的信息赋值到预制体实例上

3)拖的过程中,让预制体实例的position等于Input.mousePosition

3)拖结束时,销毁预制体实例,并在游戏世界item们的父transform中创建item实例

(4)在库存栏中交换两个item的思路

1)开始拖Item时获取该slotNumber索引值

2)结束拖时获取停止时的toSlotNumber索引值z

3)在库存列表中交换两个索引值对应的信息

4)触发更新库存信息事件

(5)idle状态动画arm部位替换为carry模式的思路

1)获取所有的Animator组件,找到所有arm部位的动画clip,全部映射到carry模式的动画clip。

2)通过动画覆盖控制器让映射生效,此时Animator状态机中都是carry模式的动画了

3)通过isCarry的值调整动作Event的参数,使得动画发生变更

(6)保存游戏状态的思路

提供状态保存的接口,包括类型ID、数据结构、注册/注销接口、保存/恢复接口。

每个类型的状态需要继承该接口,

编写SaveLoadManager类,接收注册的类的数据,统一操作每个类型的保存和恢复,

在合适的位置(比如切换场景)执行SaveLoadManager的保存和恢复。

(7)保存地面属性的思路

在Hierarchy中增加GridProperties对象,其下是各个地面属性Tilemap对象。

生成一个脚本,当该脚本Disable时,会扫描tilemap组件并保存到gridProperties的SO实例中。

然后GridProperties对象下每个地面属性tilemap对象都挂载该脚本,并在脚本组件中指定地面属性信息为自己的属性。

(8)Dug时地面图形变化思路

根据当前Dug的grid的上下左右计算当前grid的图片,

然后再调整上下左右4个grid的图片

(9)对象池的设计思路

对象池用于快速产生预制体对象,提高性能。

设计Dictionary<int, Queue<GameObject>> poolDictionary用于存储对象池,int是预制体的id,Queue<GameObject>是预制体实例化之后的队列。

每次获取对象时,从队首获取实例,同时队尾再增加一个实例。

同时将获取的实例进行position,rotation等设置。

(10)农作物生长的思路

创建一个CropDetails类,记录农作物每个阶段的时间、图片等信息。

然后创建SO配置所有农作物的Details信息。

农作物接收天数变化的事件,然后根据每个阶段的时间更换对应的图片。

(11)A*算法构建路径的思路

G为起点到当前点的路径值,H为当前点到终点的预测值,F=G+H

每次取节点四周最小的F值的点放入openPathList,走过的点放入closedPathList中

然后每次取openPathList中最小F的点计算四周的F值,直到走到终点

可以在节点中标记obstacle属性以及isPath属性,对于非isPath的属性可以在F中加入惩罚系数。

(12)NPC移动的思路

计算每秒钟需要达到的位置,然后每秒钟接收到事件后移动到对应位置。

(13)NPC跨场景移动的思路

提前准备好场景间移动的起/终点数据,每个场景的起/终点一条路径,多条路径组成一个场景间的数据,多个场景间的数据汇总配置到SO容器中。

路径中的起/终点取NPC的位置数据,则值特别大,否则会固定的数据。

在每个场景中分别进行NPC移动。

(14)基于对象池播放声音的方法

常见Sound组件放到对象池中。

Sound组件需要输入音乐名称以配置AudioClip

在OnEnable中播放音乐,在OnDisable中停止音乐(协程方法,一段时间后停止)

外部播放音乐只需要启用/关闭Sound组件即可。

103、Unity按钮操作

(1)Tilemap左右翻转/旋转

左右翻转:Shift + [

旋转:]

(2)同时打开2个场景并激活第2个场景的方法

第2个场景以附加形式打开,右击第2个场景选择”Open Scene Additive”。

第2个场景右击选择“Set Active Scene”。

(3)Inspector中选中Anchor的方法

按住Shift + Alt进行选取

(4)Screen界面中聚焦某个物体的方法

在Hierarchy中选中某个物体按F键

(5)预制体创建变体

预制体右键 Create -> Prefab Variant

(6)Animator界面移动界面

按住Alt键后左键移动

200、常用代码片段

(1)鼠标控制移动的方法

void Update()

    {

        if (Input.GetMouseButtonDown(0))

        {

            // 按下鼠标左键发射射线

            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            // 声明一个碰撞信息类

            RaycastHit hitInfo;

            // 碰撞检测

            bool res = Physics.Raycast(ray, out hitInfo);

            // 如果碰撞到的情况下,hitInfo就有内容了

            if (res)

            {

                transform.position = hitInfo.point;

            }

        }

}

(2)单例模式类

public abstract class SingletonMonobehaviour<T> : MonoBehaviour where T: MonoBehaviour

{

    private static T instance;

    public static T Instance {  get { return instance; } }

    protected virtual void Awake()

    {

        if (instance == null)

        {

            instance = this as T;

        }

        else

        {

            Destroy(gameObject);

        }

    }

}

(3)AnimatorOverrideController动画重写控制器的2种使用方法

1)一般使用方法

定义变量:AnimatorOverrideController animatorOverrideController;

初始化变量:animatorOverrideController = new AnimatorOverrideController(animator.runtimeAnimatorController);

animator.runtimeAnimatorController = animatorOverrideController;

通过上面的赋值,后续对animatorOverrideController的修改将直接作用于animator.runtimeAnimatorController(运行态的动画控制器)。

运行中更新动画:animatorOverrideController["shot"] = weaponAnimationClip[weaponIndex];

2)每帧更新多个动画剪辑

使用AnimatorOverrideController.ApplyOverrides方法,它能够将动画器剪辑绑定重新分配的数量减少到每次调用只有一个。

定义新老动画的映射:List<KeyValuePair<AnimationClip, AnimationClip>> clipOverrides;

初始化:animatorOverrideController.GetOverrides(clipOverrides);

运行中更新动画:animatorOverrideController.ApplyOverrides(clipOverrides);

其中ApplyOverrides函数:对该动画器重写控制器应用写列表

GetOverrides函数:获取该动画器重写控制器中当前定义的动画剪辑重写的列表。

(4)计算一秒钟的方法

private void GameTick()

{

    gameTick += Time.deltaTime;

    if(gameTick >= 0.012f)

    {

        gameTick -= 0.012f;  // 减完重新开始

        UpdateGameSecond();

    }

}

(5)附加方式加载Scene并设置Active的方法

SceneManager.LoadSceneAsync(xx, LoadSceneMode.Additive);
SceneManager.SetActiveScene(xxScene);

(6)获取GUID的方法

string _gUID = System.Guid.NewGuid().ToString();

(7)调整给定点位置以适应屏幕像素密度的方法

RectTransformUtility.PixelAdjustPoint(Vector2 point, Transform elementTransform, Canvas canvas);

  1. point:需要调整的点的屏幕坐标位置
  2. elementTransform:与point点关联的Transform或RectTransform。这个参数用于确定如何根据其父级对象和画布来调整点的位置
  3. canvas:用于获取当前画布的缩放信息和其他相关属性。Canvas决定了UI元素如何被渲染以及它们相对于屏幕的比例关系。

(8)在指定2D矩形区域查找所有包含特定组件的游戏对象

public static bool GetComponentsAtBoxLocation<T>(out List<T> listComponentsAtBoxPosition, Vector2 point, Vector2 size, float angle)

    {

        bool found = false;

        List<T> componentList = new List<T>();

        Collider2D[] collider2DArray = Physics2D.OverlapBoxAll(point, size, angle);

        // Loop through all colliders to get an object of type T

        for(int i = 0; i < collider2DArray.Length; i++)

        {

            T tComponent = collider2DArray[i].gameObject.GetComponentInParent<T>();

            if(tComponent != null)

            {

                found = true;

                componentList.Add(tComponent);

            }

            else

            {

                tComponent = collider2DArray[i].gameObject.GetComponentInChildren<T>();

                if(tComponent != null)

                {

                    found = true;

                    componentList.Add(tComponent);

                }

            }

        }

        listComponentsAtBoxPosition = componentList;

        return found;

    }

(9)保存/加载文件

【保存到文件】

public void SaveDataToFile()

{

    BinaryFormatter bf = new BinaryFormatter();

    FileStream file = File.Open(Application.persistentDataPath + "/WildHopeCreek.data" , FileMode.Create);

    bf.Serialize(file, <数据内容>);

    file.Close();

 }

【加载文件】

public void LoadDataFromFile()

{

    BinaryFormatter bf = new BinaryFormatter();

    if(File.Exists(Application.persistentDataPath + "/WildHopeCreek.data"))

    {

        FileStream file = File.Open(Application.persistentDataPath + "/WildHopeCreek.data", FileMode.Open);

        gameSave = (GameSave)bf.Deserialize(file);  // 转换数据类型

        file.Close();

    }

 }

(10)AnimationClip类使用示例

using UnityEngine;

using UnityEditor; // 仅在编辑器环境使用

public class AnimationClipExample : MonoBehaviour

{

    [ContextMenu("Create Animation")]

    public void CreateSimpleAnimation()

    {

        // 创建新的动画剪辑

        AnimationClip clip = new AnimationClip();

        clip.name = "SimpleFloat";

        clip.frameRate = 60;

        // 设置循环模式

        AnimationClipSettings settings = AnimationUtility.GetAnimationClipSettings(clip);

        settings.loopTime = true;

        AnimationUtility.SetAnimationClipSettings(clip, settings);

        // 创建位置Y轴的动画曲线

        AnimationCurve curve = new AnimationCurve();

        curve.AddKey(0f, 0f);     // 时间0,Y=0

        curve.AddKey(1f, 1f);     // 时间1,Y=1

        curve.AddKey(2f, 0f);     // 时间2,Y=0

        curve.postWrapMode = WrapMode.Loop; // 设置循环模式

        // 将曲线应用到Y轴位置

        clip.SetCurve("", typeof(Transform), "localPosition.y", curve);

        // 保存动画文件到项目

        AssetDatabase.CreateAsset(clip, "Assets/SimpleFloat.anim");

        AssetDatabase.SaveAssets();

        Debug.Log("动画创建成功!路径:Assets/SimpleFloat.anim");

    }

}

【使用步骤】

1)将此脚本挂载到任意GameObject上

2)在Inspector面板右键点击脚本组件

3)选择“Create Animation”菜单项

4)项目中会自动生成"SimpleFloat.anim"动画文件

【查看效果】

1)创建Animator Controller

2)将生成的动画文件拖入控制器

3)给物体添加Animator组件并关联控制器

4)运行游戏即可看到物体上下浮动效果

http://www.dtcms.com/a/265567.html

相关文章:

  • RabbitMQ 通过HTTP API删除队列命令
  • 【RK3568+PG2L50H开发板实验例程】Linux部分/FPGA FSPI 通信案例
  • 【机器学习深度学习】什么是下游任务模型?
  • laravel基础:php artisan make:model Flight --all 详解
  • 【PaddleOCR】OCR文本检测与文本识别数据集整理,持续更新......
  • 【QT】QWidget控件详解 || 常用的API
  • 蓝桥杯C++组算法知识点整理 · 考前突击(中)【小白适用】
  • Java调用百度地图天气查询服务获取当前和未来天气-以贵州省榕江县为例
  • 【字节跳动】数据挖掘面试题0006:SVM(支持向量机)详细原理
  • JVM类加载过程
  • 车载电子电气架构 --- 从车窗演进看车联网的需求、发展与选择
  • 2025年游戏鼠标推荐,游戏鼠标推荐,打CSGO(罗技、雷蛇、卓威、ROG、漫步者、赛睿、达尔优)
  • 前端-HTML-day2
  • 从生活实例看:点积、内积和矩阵乘法如何玩转机器学习
  • 物联网MQTT协议与实践:从零到精通的硬核指南
  • I/O 进程 7.2
  • Mysql锁机制与优化实践以及MVCC底层原理剖析
  • TensorFlow 安装使用教程
  • 6. 常见K线形态(楔形与旗形)
  • Laravel8中调取腾讯云文字识别OCR
  • 中文语境下的视频生成革命:百度 MuseSteamer 的“产品级落地”启示录
  • 手机内存融合是什么意思
  • Redis 的特性、工作机制与性能优化全解(含搭建实战教程)
  • 用 vLLM 在两张 RTX 3090 上部署 Qwen2.5-14B BF16全量大模型的完整过程
  • 替换springboot打好jar包中的class文件
  • Python 异步爬虫(aiohttp)高效抓取新闻数据
  • 前端开发中的 Base64 图片革命:从链接到嵌入的性能优化
  • Go爬虫实时性能监控方案
  • 利用人名语言分类案例演示RNN、LSTM和GRU的区别(基于PyTorch)
  • 【学习线路】机器学习线路概述与内容关键点说明