赞
踩
与c#相同,class不写public,则默认同一程序集(internal)中可以访问,在unity中,程序集表现为项目,即同一项目可以互相访问
类里的成员默认与c#同样相同,都是private
在C#中,将字段和方法都设为私有(private
)并使用static
修饰符并不是“多此一举”,而是根据具体的设计需求和场景来决定的。下面我会详细解释为什么在某些情况下使用static
是有意义的,即使字段和方法都是私有的。
全局访问性:静态字段属于类本身,而不是类的任何实例。这意味着,无论创建了多少个类的实例,静态字段都只有一份拷贝,并且可以在类的所有实例之间共享。即使字段是私有的,它仍然可以在类的内部被所有静态和非静态方法访问,这有助于在类的不同部分之间共享数据。
内存管理:由于静态字段只存在于内存中的一份拷贝,因此它们有助于减少内存的使用,特别是在需要跨多个实例共享数据时。
单例模式:在实现单例模式时,静态字段常用于存储类的唯一实例。虽然在这种情况下,你可能会将访问该实例的方法设为公共的,但实例本身通常是私有的静态字段。
如果是建立在类不是静态类,可以实例的基础上,静态私有就有意义
而如果类本身是静态类,private static和private就没区别了,但是,在C#中,如果类本身是静态类(static class
),那么该类中的所有成员(包括字段、方法、属性等)都必须是静态的(static
),因为静态类不能被实例化,
在这种情况下,private static
和private
在静态类内部确实在语法上看起来没有区别,因为所有成员都已经是隐式静态的。但是,从语义和代码清晰度的角度来看,使用private static
明确指定成员的静态性质仍然是一个好习惯。
通常意味着它可以在同一个Unity项目中的任何脚本中被访问,但如果你想从另一个Unity项目或DLL中访问它,则不可能了,如果是写了public,则还需要确保该程序集(或DLL)被正确地引用或加载。
SimpleMove
和Move
方法来简化角色的移动控制。RaycastHit
结构体包含了一系列有用的属性,这些属性提供了关于射线与物体相交的详细信息,包括但不限于:
执行
if(Input.GetMouseButtonDown(0))
{
print("ButtonDown");
}
if (Input.GetMouseButton(0))
{
print("Button");
}
if (Input.GetMouseButtonUp(0))
{
print("ButtonUp");
}
3D模型转换成2D并呈现在屏幕上的过程,在计算机图形学中,通常被称为图形渲染管线(Graphics Rendering Pipeline)。这个过程涉及多个步骤,包括建模、变换、视图变换、投影变换、裁剪、光栅化、着色和纹理映射、深度测试和混合、帧缓冲等。以下是这些步骤的详细解释:
首先,3D模型在游戏或应用程序中以模型的形式存在,这些模型由顶点(vertices)和面(faces)组成,可以是简单的几何形状,也可以是复杂的多边形网格。
3D模型需要经过一系列的变换,以确定它们在游戏世界中的位置、方向和大小。这些变换包括平移(translation)、旋转(rotation)和缩放(scaling)。
接下来,需要将3D世界中的坐标转换为相对于摄像机(或观察者)的视图空间坐标。这通常通过一个视图矩阵(View Matrix)来完成,它描述了摄像机在世界空间中的位置和方向。
这个摄像机对象并不是物理上的新相机,而是用于渲染过程中的一个虚拟对象,它定义了如何从特定位置和角度观察3D场景。
通过这些属性和功能,摄像机对象能够将3D世界中的坐标转换到相对于摄像机视图的坐标系统中。这样,后续的投影变换才能正确地将这些坐标映射到2D屏幕上,形成我们最终看到的图像。
在这个过程中,3D模型仍然是数据意义上的3D模型,但它们的表示方式已经转换为了从摄像机视角观察的结果,这是通过视图变换和后续的渲染管线步骤共同实现的。
为了将3D场景投影到2D屏幕上,需要进行投影变换。这可以通过正投影(Orthographic Projection)或透视投影(Perspective Projection)来实现。
可以理解为在视图变换之后,对3d模型的数据进行2d化的处理
在投影变换之后,一些位于摄像机视野之外的物体或部分会被裁剪掉,因为它们不会出现在最终的2D图像中。
经过投影变换的3D物体现在需要转换为2D图像上的像素。这个过程称为光栅化(Rasterization),它将3D物体的边缘映射到屏幕上的像素网格,并确定哪些像素属于该物体。
在确定了哪些像素属于特定物体后,接下来是着色(Shading)和纹理映射(Texture Mapping)。
所有处理过的像素会存储在帧缓冲(Frame Buffer)中,然后显示器会读取这些像素数据并在屏幕上显示出来。
为了提高渲染效率和性能,开发者会进行各种优化,如减少绘制调用、使用级联阴影映射、实施遮挡剔除、使用LOD技术等。
虽然不直接涉及渲染,但用户输入(如键盘、鼠标或游戏手柄)是游戏体验的重要组成部分。用户的输入会影响游戏逻辑,进而影响渲染管线中的视图变换和其他渲染决策。
射线(Ray)是从一个点出发沿直线方向无限延伸的线段。在Unity中,我们可以使用Ray
类来创建这样的射线,并通过Physics.Raycast
、Physics.RaycastAll
或Physics.RaycastNonAlloc
等方法来检测这条射线是否与场景中的物体相交。
创建射线:首先,你需要定义一个射线的起点和方向。通常,起点是相机位置或玩家角色的眼睛位置,方向是从起点到鼠标点击的屏幕位置在3D空间中的向量。
发送射线:使用Physics.Raycast
等函数将射线发送到场景中,并获取碰撞信息(如果有的话)。
处理碰撞:如果射线与物体相交,Physics.Raycast
会返回一个布尔值true
以及一个RaycastHit
结构体,其中包含了碰撞点的位置、碰撞的物体、碰撞的法线等信息。
- using UnityEngine;
-
- public class RaycastExample : MonoBehaviour
- {
- void Update()
- {
- // 创建从相机到鼠标位置在屏幕上的射线
- Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
-
- // 声明RaycastHit用于存储碰撞信息
- RaycastHit hit;
-
- // 发送射线,并检测是否有碰撞,最大距离为100
- if (Physics.Raycast(ray, out hit, 100f))
- {
- // 如果有碰撞,打印碰撞物体的名称
- Debug.Log("Hit: " + hit.collider.gameObject.name);
-
- // 可选:你可以在这里添加更多代码来处理碰撞,比如显示UI提示等
- }
- }
- }
Physics.Raycast
方法的layerMask
参数来限制射线检测的层。Physics.RaycastNonAlloc
等优化手段来减少内存分配。Camera.main
:这是Unity提供的一个快捷方式,用于访问场景中被标记为“MainCamera”的相机。如果你的场景中只有一个相机,或者你有意地将某个相机设置为“MainCamera”,那么Camera.main
就会引用这个相机。(默认的初始相机即使改了名也可以main出来)
ScreenPointToRay()
:这是Camera
类的一个方法,它接受一个屏幕上的点(通常是一个Vector3
,但在这个上下文中,由于屏幕是二维的,我们通常只关心x和y坐标,而z总是0),并返回一个从相机位置出发,穿过该屏幕点的射线。
Input.mousePosition
:这是Unity的Input
类的一个属性,它返回一个Vector3
,表示鼠标当前在屏幕上的位置。然而,这个Vector3
的z分量总是0,因为屏幕是二维的。
将这三者结合起来,Camera.main.ScreenPointToRay(Input.mousePosition)
就创建了一条从主相机的位置出发,穿过当前鼠标在屏幕上的位置的射线。(仅仅是使这样的一条射线存在)
补充:
Camera.main
是通过以下步骤来确定哪个相机是主相机的:
null
。ScreenPointToRay
public static T Instantiate<T>(T original)
public static T Instantiate<T>(T original, Vector3 position, Quaternion rotation)
public static T Instantiate<T>(T original, Vector3 position, Quaternion rotation, Transform parent)
public static T Instantiate<T>(T original, Transform parent)
public static T Instantiate<T>(T original, Transform parent, bool worldPositionStays)
Awake是在脚本实例后调用,而Start是在Update第一次唤醒,的前一刻才开始调用
Awake
方法中给变量赋值,并且该变量之前已经在Inspector中或通过脚本直接赋值了,那么Awake
方法中的赋值会覆盖之前的值。Start
方法中给变量赋值,并且该变量之前已经在Awake
方法中赋值了,那么Start
方法中的赋值会覆盖Awake
方法中的值,因为Start
方法在Awake
方法之后调用。在迭代器块中(即实现了 IEnumerable
或 IEnumerator
接口的方法中),yield return
用于逐个返回集合中的元素,而不需要显式地创建集合的完整副本。每次调用 MoveNext()
方法时,迭代器都会执行到下一个 yield return
语句,并返回该语句后面的值。然后,迭代器的状态会被保存,以便在下次调用 MoveNext()
时从该点继续执行。
- public IEnumerable<int> GetNumbers()
- {
- for (int i = 0; i < 5; i++)
- {
- yield return i; // 逐个返回数字
- }
- }
-
- // 使用示例
- foreach (var number in GetNumbers())
- {
- Console.WriteLine(number); // 输出 0, 1, 2, 3, 4
- }
在 Unity 中,yield return
被用于协程中,以实现非阻塞的等待。协程方法返回一个 IEnumerator
,并且在方法体内使用 yield return
来暂停执行,直到满足某个条件(如等待一段时间、等待直到下一帧等)。Unity 的游戏循环会在适当的时候恢复协程的执行。
- IEnumerator MyCoroutine()
- {
- Debug.Log("开始协程");
- yield return new WaitForSeconds(1f); // 等待1秒
- Debug.Log("等待了1秒后继续");
- // 可以继续添加更多的 yield return 语句
- }
-
- void Start()
- {
- StartCoroutine(MyCoroutine()); // 启动协程
- }
在上面的例子中,MyCoroutine
方法是一个协程,它首先打印出“开始协程”,然后使用 yield return new WaitForSeconds(1f);
暂停执行1秒。在这1秒内,Unity 的游戏循环会继续运行,处理其他事件和更新。1秒后,Unity 会自动恢复 MyCoroutine
协程的执行,并打印出“等待了1秒后继续”。
yield return
语句,当前方法的执行就会暂停,控制权会返回到调用者。yield return
语句之后添加任何代码,因为那些代码将永远不会被执行(除非在另一个迭代周期中)。但在协程中,你可以在 yield return
之后添加代码,这些代码将在下一次协程恢复执行时运行。IEnumerator
是 C# 中的一个接口,它定义了一个简单的迭代机制,用于按顺序访问集合中的元素。然而,在 Unity 的上下文中,IEnumerator
有一个额外的用途,即作为协程的基础。
在标准的 C# 集合框架中,IEnumerator
和 IEnumerable
接口一起工作,允许对集合进行迭代。IEnumerable
接口要求实现一个 GetEnumerator()
方法,该方法返回一个 IEnumerator
对象。然后,你可以使用 IEnumerator
的 MoveNext()
方法来遍历集合中的元素,并使用 Current
属性来访问当前元素。
- foreach (var item in collection) // 背后使用了 IEnumerable 和 IEnumerator
- {
- // 处理 item
- }
在 Unity 中,IEnumerator
被赋予了额外的含义,用于实现协程。Unity 的协程是一种允许你编写非阻塞代码的机制,这些代码可以在未来的某个时间点继续执行,而不会阻塞游戏的主循环。
当你定义一个返回 IEnumerator
的方法时,这个方法就可以被当作协程来执行。在 Unity 中,你通过调用 StartCoroutine(IEnumerator routine)
方法来启动协程,其中 routine
是你想要作为协程执行的方法的调用(注意,你实际上是传递了方法的返回值,即一个 IEnumerator
对象)。
在协程方法中,你可以使用 yield return
语句来暂停协程的执行。Unity 会记住协程的当前状态,并在适当的时候(如下一帧或指定的等待时间之后)恢复协程的执行。yield return
后面可以跟多种类型,比如 WaitForSeconds
、WaitForFixedUpdate
、null
(表示下一帧继续)等,以实现不同的等待效果。
- IEnumerator MyCoroutine()
- {
- // 做一些事情...
- yield return new WaitForSeconds(1f); // 等待1秒
- // 继续做一些事情...
- }
-
- void Start()
- {
- StartCoroutine(MyCoroutine()); // 启动协程
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。