初始化obs/Unity仓库

This commit is contained in:
2026-05-03 14:06:26 +08:00
commit 0d9e5282f3
95 changed files with 14419 additions and 0 deletions
+485
View File
@@ -0,0 +1,485 @@
# 一. 射线检测 (Ray Detection)
## 1. 射线对象 (Ray Object)
### 1.1 3D世界中的射线 (Ray in a 3D World)
射线是一条具有**初始点**和**方向**的直线(注意:方向不是终点)。 声明射线时,需要传入初始点和方向。
C#
```
// Ray(Vector3 origin, Vector3 direction)
Ray ray = new Ray(origin, direction);
// 示例:从原点 (0,0,0) 指向右方 (1,0,0) 的射线
Ray rayFromOriginToRight = new Ray(Vector3.zero, Vector3.right);
```
### 1.2 摄像机发出的射线 (Ray from Camera)
这是从屏幕上的一个点(例如鼠标位置)作为起点,沿着摄像机视口方向发出的一条射线。 其初始点即为摄像机位置,方向由屏幕坐标转换而来。
C#
```
// 从主摄像机发出,基于当前鼠标在屏幕上的位置
Ray rayFromCamera = Camera.main.ScreenPointToRay(Input.mousePosition);
```
> **注意**: 单独的射线没有实际作用,需要结合射线检测才有意义。
---
## 2. 射线检测 (Raycasting)
### 2.1 最原始的射线检测 (Basic Raycast)
这种检测方法返回一个布尔值 (`bool`),用于判断射线是否碰撞到任何物体。
**参数说明:**
1. `ray`: 发出的射线。
2. `maxDistance`: 检测的最大距离。
3. `layerMask`: 指定检测的层级 (Layer)。
4. `queryTriggerInteraction`: 是否与触发器 (Trigger) 交互。
- `UseGlobal`: 使用全局设置。
- `Collide`: 检测触发器。
- `Ignore`: 忽略触发器 (默认值,如果不填,则使用 `UseGlobal`)。
<!-- end list -->
C#
```
// 示例:检测名为 "Player" 的层级,最大距离1000,忽略触发器
if (Physics.Raycast(ray, 1000f, 1 << LayerMask.NameToLayer("Player"), QueryTriggerInteraction.Ignore))
{
// 射线碰到了 "Player" 层上的物体
}
```
### 2.2 获得相交的单个物体的信息 (Getting Information about a Single Hit)
此方法同样返回 `bool` 值,但能通过 `out` 参数获取碰撞到的单个物体的详细信息。
**参数说明:**
1. `ray`: 发出的射线。
2. `out RaycastHit hitInfo`: `out` 关键字表示此参数会传出碰撞信息。
3. `maxDistance`: 检测的最大距离。
4. `layerMask`: 指定检测的层级。
5. `queryTriggerInteraction`: 是否与触发器交互。
<!-- end list -->
C#
```
RaycastHit hit; // 用来存储碰撞信息
if (Physics.Raycast(ray, out hit, 1000f, 1 << LayerMask.NameToLayer("Enemy"), QueryTriggerInteraction.Collide))
{
// 射线碰到了物体,碰撞信息存储在 'hit' 中
// hit.collider: 碰到的碰撞体
// hit.point: 碰撞点坐标
// hit.normal: 碰撞点处的法线向量
// hit.distance: 射线起点到碰撞点的距离
// GameObject hitObject = hit.collider.gameObject; // 获取碰撞到的游戏对象
}
```
### 2.3 获得相交的多个物体 (Getting Information about Multiple Hits)
此方法返回一个 `RaycastHit` 类型的数组。如果射线没有碰撞到任何物体,则数组长度为0。
**参数说明:**
1. `ray`: 发出的射线。
2. `maxDistance`: 检测的最大距离。
3. `layerMask`: 指定检测的层级。
4. `queryTriggerInteraction`: 是否与触发器交互。
<!-- end list -->
C#
```
RaycastHit[] hits;
hits = Physics.RaycastAll(ray, 1000f, 1 << LayerMask.NameToLayer("Obstacle"));
if (hits.Length > 0)
{
foreach (RaycastHit hit in hits)
{
// 处理每一个碰撞到的物体
// Debug.Log("Hit: " + hit.collider.name);
}
}
```
> **`RaycastHit` 结构体**: 在射线检测中非常关键。它记录了大量信息,例如:
>
> - `collider`: 射线命中的碰撞体,通过它可以获取整个游戏对象。
> - `point`: 射线检测到的碰撞点。
> - `normal`: 射线与表面相交点的法线向量。
> - `distance`: 射线起点到相交点的距离。
---
# 二. 范围检测 (Overlap Detection)
范围检测是在一个固定的区域内,检测其中所有携带碰撞体 (Collider) 的物体。 Unity 中主要有三种范围检测:
- 盒状范围检测 (`OverlapBox`)
- 球形范围检测 (`OverlapSphere`)
- 胶囊体范围检测 (`OverlapCapsule`)
这些方法都存储在 `Physics` 类中,参数大致相同,只是根据形状不同,需要传入不同的点来构造形状。
## 1. 盒状范围检测 (OverlapBox)
`Physics.OverlapBox()`
**参数说明:**
1. `center`: 立方体的中心点。
2. `halfExtents`: 立方体三个轴向的半尺寸 (大小的一半)。
3. `orientation`: 立方体的旋转角度 (四元数 `Quaternion`)。
4. `layerMask`: **检测指定层级**。
5. `queryTriggerInteraction`: 是否忽略触发器。
**返回值**: 范围内的所有碰撞体 (`Collider[]`) 数组。
### 重点参数四:检测指定层级 (LayerMask)
Unity 中的 Layer共有32个 (0-31),这刚好可以用一个32位的二进制数来表示。 `0000 0000 0000 0000 0000 0000 0000 0000`
**使用 `1 << LayerMask.NameToLayer("LayerName")` 创建层掩码:** `LayerMask.NameToLayer("LayerName")` 函数会返回指定层名称在 Layer 中的索引序号。 将数字 `1` 左移这个索引号的位数,会在32位数字中得到一个全新的数字。例如,如果 "UI" 层是第 5 层 (索引为5): `1 << 5` 结果为 `0000 0000 0000 0000 0000 0000 0010 0000` (第5位为1)
在这个32位的二进制掩码中,`1` 代表需要检测该层级,`0` 代表不需要。
**检测多个层级:** 使用按位或 (`|`) 操作可以方便地组合多个层级进行检测。 例如,要检测第0层、第2层和第5层: `0000 0000 0000 0000 0000 0000 0010 0101` (二进制表示)
C#
```
int layerMask = (1 << LayerMask.NameToLayer("第一层")) |
(1 << LayerMask.NameToLayer("第二层")) |
(1 << LayerMask.NameToLayer("第五层"));
// 或者,如果知道层索引:
// int layerMask = (1 << 0) | (1 << 2) | (1 << 5);
```
### 位运算在 LayerMask 中的应用
- **按位与 (`&`)**:
- 规则:两个位都为 `1` 时,结果才为 `1`。
- 用途:检测某个特定层是否存在于当前的层掩码中。
- 示例:
- 掩码: `0000 ... 0010 0101` (检测0, 2, 5层)
- 待检测层 (例如第0层): `0000 ... 0000 0001` (1 &lt;&lt; 0)
- `(掩码 & 待检测层)` 结果不为0,证明该层在掩码中。
- **按位或 (`|`)**:
- 规则:只要有一个位为 `1`,结果就为 `1`。
- 用途:将多个层添加到层掩码中。
- **按位异或 (`^`)**:
- 规则:两个位相同为 `0`,不同为 `1`。
- 用途:动态切换某个层级的检测状态(添加或移除)。
- 示例:
- 原始掩码 (检测第1层和第3层): `0000 ... 1010`
- 操作层 (第3层): `0000 ... 1000`
- `原始掩码 ^ 操作层` 结果: `0000 ... 0010` (第3层的状态被翻转,现在只检测第1层)
### Layer 和 LayerMask 的区别是什么?
- **Layer**: 指的是 Unity 编辑器中为 GameObject 分配的单个层级(例如 "Player", "Enemy", "UI")。每个 GameObject 只能属于一个 Layer。Layer 本身是一个整数索引 (0-31)。
- **LayerMask**: 是一个32位的整数,用作位掩码 (bitmask)。它的每一位对应一个 Layer。通过设置 LayerMask 中的特定位为1,可以指定射线检测、范围检测等操作应该作用于哪些 Layer。一个 LayerMask 可以同时代表多个 Layer。
---
# 三. RPG游戏学习 (RPG Game Study)
## 1. 导航系统 (Navigation System)
- (内容待补充)
## 2. 鼠标点击, 角色移动 (Mouse Click, Character Movement)
- (内容待补充)
---
# 四. 对象池技术 (Object Pooling)
目前构造的对象池比较简单。这个对象池是一个单例类。我们需要明确:
- 对象池存储的物体是什么类型。
- 对象池存储的物体初始个数是多少。
然后构造一个队列 (Queue) 来管理这些对象。
一个典型的对象池实现:
1. 在 `Awake()` 方法中完成单例类的构造。
2. 实现三个核心方法:
- **初始化填充对象池**: 在开始时创建并存储一定数量的对象。
- **从对象池中获取对象**: 当需要对象时,从池中取出一个。
- **将对象返回对象池**: 当对象不再使用时,将其返还给池中以备复用。
---
# 五. LineRenderer (画线组件)
`LineRenderer` 组件允许在 Unity 场景中绘制线段。它主要包含编辑器设置和代码控制两大部分。代码控制是重点,通常在脚本中动态实现。
## 1. 编辑器部分 (Editor Properties)
_(图片引用)_
1. **Loop**: 线段的开头和结束是否闭合形成循环。
2. **Positions**: 控制线段的顶点个数和每个顶点的坐标。
3. **Width**: 线段的粗细 (可以是一个曲线,使粗细沿长度变化)。
4. **Color**: 线段的颜色 (可以使用渐变色)。
5. **Corner Vertices**: 角顶点个数。数值越大,线段转角处越圆润。
6. **End Cap Vertices**: 末端顶点个数。数值越大,线段末端越圆润。
7. **Alignment**: 对齐方式 (View, TransformZ)。
8. **Texture Mode**: 纹理模式 (Stretch, Tile, DistributePerSegment, Shape)。
9. **Generate Lighting Data**: 是否生成光照数据,即光照是否对线段产生影响。
10. **Use World Space**: 是否使用世界坐标。
- 若勾选,移动 LineRenderer 所在的 GameObject 时,线段本身不会随之移动。
- 若不勾选 (使用本地坐标),线段会随 GameObject 移动。
## 2. 代码部分 (Scripting)
首先,需要获取 `LineRenderer` 组件的引用。
C#
```
LineRenderer lineRenderer;
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
}
```
**常用代码操作:**
1. **首尾相连 (Looping)**:
C#
```
lineRenderer.loop = true; // 或者 false
```
2. **设置开始和结束宽度**:
C#
```
lineRenderer.startWidth = 0.1f;
lineRenderer.endWidth = 0.5f;
// 也可以使用曲线来控制宽度
// lineRenderer.widthCurve = new AnimationCurve(new Keyframe(0, 0.1f), new Keyframe(1, 0.5f));
```
3. **设置开始和结束颜色**:
C#
```
lineRenderer.startColor = Color.red;
lineRenderer.endColor = Color.blue;
// 也可以使用 Gradient 来控制颜色
// Gradient gradient = new Gradient();
// gradient.SetKeys(
// new GradientColorKey[] { new GradientColorKey(Color.red, 0.0f), new GradientColorKey(Color.blue, 1.0f) },
// new GradientAlphaKey[] { new GradientAlphaKey(1.0f, 0.0f), new GradientAlphaKey(1.0f, 1.0f) }
// );
// lineRenderer.colorGradient = gradient;
```
4. **设置材质 (Material)**:
C#
```
public Material lineMaterial; // 在 Inspector 中指定
// ...
lineRenderer.material = lineMaterial;
```
5. **设置顶点 (Positions)**:
- 设置顶点数量:
C#
```
lineRenderer.positionCount = 2; // 例如,一条直线需要2个点
```
- 设置单个顶点坐标:
C#
```
// lineRenderer.SetPosition(int index, Vector3 position);
lineRenderer.SetPosition(0, new Vector3(0, 0, 0));
lineRenderer.SetPosition(1, new Vector3(1, 1, 0));
```
- 通过数组一次性设置所有顶点坐标:
C#
```
Vector3[] positions = new Vector3[] {
new Vector3(0, 0, 0),
new Vector3(1, 1, 0),
new Vector3(2, 0, 0)
};
lineRenderer.SetPositions(positions);
```
---
# 六. 四元数 (Quaternions)
- (内容待补充,用于表示旋转)
---
# 七. 组件和物体的启用和禁用 (Component and GameObject Enabling/Disabling)
## 1. GameObject 的启用和禁用
### 1.1 激活 GameObject
C#
```
// 'this' 指向当前脚本所在的 GameObject
this.gameObject.SetActive(true);
```
### 1.2 禁用 GameObject
C#
```
this.gameObject.SetActive(false);
```
### 1.3 查看 GameObject 状态
C#
```
// 自身是否被设置为激活 (不受父物体影响)
bool isActiveSelf = gameObject.activeSelf;
// 在层级视图中实际的激活状态 (受父物体影响)
bool isActiveInHierarchy = gameObject.activeInHierarchy;
```
### 1.4 `activeSelf` 和 `activeInHierarchy` 的区别
- `gameObject.activeSelf`: 表示该 GameObject 自身是否被设置为激活状态。如果它被设置为 `false`,即使其所有父对象都激活,它依然是非激活的。
- `gameObject.activeInHierarchy`: 表示该 GameObject 在场景中是否真实处于激活状态。如果它自身 `activeSelf` 为 `true`,但其任何一个父对象 `activeSelf` 为 `false` (导致父对象 `activeInHierarchy` 为 `false`),那么该 GameObject 的 `activeInHierarchy` 也会是 `false`。
## 2. 组件的启用和禁用
### 2.1 激活组件
C#
```
// 对于当前脚本组件本身
this.enabled = true;
// 对于其他组件 (例如 Collider),需要先获取其引用
Collider myCollider = this.GetComponent<Collider>();
if (myCollider != null)
{
myCollider.enabled = true;
}
```
### 2.2 禁用组件
C#
```
// 对于当前脚本组件本身
this.enabled = false;
// 对于其他组件
Collider myCollider = GetComponent<Collider>();
if (myCollider != null)
{
myCollider.enabled = false;
}
```
### 2.3 检查组件的启用状态
C#
```
// 当前脚本组件的状态
bool isScriptEnabled = this.enabled;
// 其他组件的状态
Collider myCollider = GetComponent<Collider>();
bool isColliderEnabled = false;
if (myCollider != null)
{
isColliderEnabled = myCollider.enabled;
}
```
## 3. 相关的生命周期事件 (Lifecycle Events)
### 3.1 组件相关的生命周期方法:
C#
```
void OnEnable()
{
// 当组件被启用时调用。
// 这也包括其所在的 GameObject 从非激活状态变为激活状态时。
// 注意: 如果 GameObject 首次激活,OnEnable 会在 Awake 和 Start 之后(对于脚本而言)或之前(对于某些内置组件)被调用,
// 具体取决于脚本执行顺序和组件类型。通常在 Awake 之后,Start 之前或之后。
// 严格来说,是 Awake -> OnEnable -> Start
}
void OnDisable()
{
// 当组件被禁用时调用。
// 这也包括其所在的 GameObject 从激活状态变为非激活状态时。
// 不会触发 OnDestroy()。
}
```
### 3.2 GameObject 相关的生命周期影响:
- 调用 `gameObject.SetActive(true)`:
- 会触发该 GameObject 及其所有激活的子物体上所有激活组件的 `OnEnable()` 方法。
- 如果该 GameObject 是首次被激活 (之前从未激活过或被实例化后首次激活),还会先调用其上所有组件的 `Awake()`,然后是 `OnEnable()`,接着是 `Start()`。
- 调用 `gameObject.SetActive(false)`:
- 会触发该 GameObject 及其所有激活的子物体上所有激活组件的 `OnDisable()` 方法。
- **不会**触发 `OnDestroy()`。
---
# 八. MonoBehaviour 中的重要内容和 GameObject 的重要内容 (Key Aspects of MonoBehaviour and GameObject)
- (内容待补充)