Unity 游戏开发必会——(全网最全最详细)常用 API 和类

第三章 Unity基础之API 汇总总结了Unity常用的Api,可以当做记忆文档看看 阅读详情
AI摘要

本文详细介绍了Unity游戏开发中常用的API和类。涵盖打印函数、事件函数、GameObject、MonoBehaviour等多个方面,阐述了各API和类的用法、注意点及应用场景,能帮助开发者提高开发效率,实现复杂游戏逻辑。

目录

一、引言

二、打印函数

1. Debug.Log()

2.Debug.LogWarning()

3. Debug.LogError()

三、事件函数(生命周期函数)

1.Awake()

2.Start()

3.Update()

4. FixedUpdate()

5. LateUpdate()

6.OnDestroy()

四、GameObject 游戏物体

1.GameObject.Find()

2.GameObject.SetActive()

3.AddComponent()

五、MonoBehaviour 基类

1.生命周期函数的使用

2.访问 GameObject 和其组件

六、Component 组件

1.GetComponent()

2.GetComponents()

3.GetComponentsInChildren()

七、Transform 变换组件

1.position

2.rotation

3.localPosition

4.localRotation

5.localScale

6.Translate()

7.Rotate()

八、Vector2 表示 2D 向量和点

1.基本运算

2.获取向量属性

3.应用场景

4.注意点

九、Input 访问输入系统的接口类

1.检测按键输入

2.检测鼠标输入

3.获取轴输入值

4.应用场景

5.注意点

十、Message 消息

1.SendMessage()

2.BroadcastMessage()

3.SendMessageUpwards()

4.应用场景

5.替代方案

十一、Animator 动画组件

1.动画状态机

2.控制动画播放

3.动画事件

4.应用场景

5.注意点

十二、Time 时间信息的接口类

1.时间尺度

2.时间增量

3.固定时间步长

4.应用场景

5.注意点

十三、Mathf 数学函数类

1.基本数学运算

2.三角函数

3.幂运算与开方

4.其他常用函数

5.注意点

十四、Random 生成随机数的类

1.生成随机整数

2.生成随机浮点数

3.随机旋转与随机向量

4.随机种子

5.注意点

十五、OnMouseEventFunction 鼠标回调事件

1.OnMouseEnter()

2.OnMouseExit()

3.OnMouseDown()

4.OnMouseUp()

5.OnMouseOver()

6.注意点

十六、Coroutine 协程

1.协程的基本结构

2.启动和停止协程

3.等待条件

4.应用场景

5.注意点

十七、Invoke 延时调用

1.单次延时调用

2.重复延时调用

3.注意事项

4.应用场景

十八、Rigidbody 刚体

1.添加和基本属性

2.施加力和扭矩

3.碰撞检测

4.注意点

5.应用场景

十九.结语


一、引言

        在 Unity 游戏开发过程中,丰富的 API(Application Programming Interface,应用程序编程接口)为开发者提供了强大的功能支持。熟练掌握这些常用的 API 方法和类,能够极大地提高开发效率,实现各种复杂的游戏逻辑。本文将详细介绍从一系列 Unity 开发游戏常用的 API 方法和类。


二、打印函数

        在 Unity 开发中,打印函数是非常基础且常用的调试工具,主要包括Debug.Log()、Debug.LogWarning()和Debug.LogError()。

1. Debug.Log()

  • 用法:Debug.Log()用于在控制台输出普通信息,方便开发者在开发过程中查看变量值、执行流程等信息。它可以接受各种数据类型作为参数,包括字符串、数字、对象等。例如:

public class PlayerController : MonoBehaviour
{
    public int playerHealth = 100;
    void Start()
    {
        Debug.Log("游戏开始,角色生命值为:" + playerHealth);
    }
}
  • 注意点:虽然Debug.Log()在调试时很方便,但在发布版本中,过多的日志输出可能会影响游戏性能,因此在发布前建议将不必要的Debug.Log()语句移除或进行条件编译。

  • 应用场景:在开发过程中,用于跟踪变量的变化、函数的执行顺序等。

2.Debug.LogWarning()

  • 用法:Debug.LogWarning()用于输出警告信息,当出现可能影响程序运行,但不一定会导致错误的情况时使用。例如,当资源即将耗尽、某个功能即将过时等情况。示例代码如下:

public class ResourceManager : MonoBehaviour
{
    public int resourceCount = 10;
    void Update()
    {
        if (resourceCount < 5)
        {
            Debug.LogWarning("资源数量不足,剩余:" + resourceCount);
        }
    }
}
  • 注意点:警告信息通常会以黄色显示在控制台,以便与普通日志区分开来,但开发者仍需关注这些警告,避免潜在的问题。

  • 应用场景:用于提示一些需要关注但暂时不会导致程序崩溃的情况。

3. Debug.LogError()

  • 用法:Debug.LogError()用于输出错误信息,当程序出现异常或错误时,使用它可以帮助定位问题。该信息会以红色显示在控制台,比较醒目。例如:

public class FileLoader : MonoBehaviour
{
    void Start()
    {
        try
        {
            // 模拟加载文件失败
            string filePath = "nonexistent.txt";
            System.IO.File.ReadAllText(filePath);
        }
        catch (System.Exception e)
        {
            Debug.LogError("文件加载失败:" + e.Message);
        }
    }
}
  • 注意点:当出现错误日志时,开发者应及时根据错误信息和堆栈跟踪来排查问题,确保游戏的稳定性。

  • 应用场景:在程序出现异常或错误时,输出详细的错误信息,帮助开发者快速定位和解决问题。


三、事件函数(生命周期函数)

        Unity 中的 MonoBehaviour 类提供了一系列生命周期函数,这些函数在脚本的不同阶段自动调用,合理利用它们可以更好地管理游戏对象的行为。

1.Awake()

  • 用法:Awake()在脚本实例被加载时调用,常用于初始化变量和获取组件引用。在场景中,所有对象的Awake()函数会在Start()函数之前调用。示例如下:

public class Enemy : MonoBehaviour
{
    public float speed;
    private Rigidbody rb;

    void Awake()
    {
        speed = 5f;
        rb = GetComponent<Rigidbody>();
    }
}
  • 注意点:如果一个 GameObject 上有多个脚本,它们的Awake()函数执行顺序是不确定的,因此不要在不同脚本的Awake()函数中依赖彼此的执行顺序。

  • 应用场景:用于在游戏对象创建时进行必要的初始化操作,如获取组件、设置初始参数等。

2.Start()

  • 用法:Start()在对象的第一帧更新之前调用,通常用于开始游戏时的初始化操作,如开启协程、注册事件等。例如:

public class GameManager : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(LoadLevelAsync());
    }

    IEnumerator LoadLevelAsync()
    {
        // 模拟异步加载关卡
        yield return new WaitForSeconds(3f);
        Debug.Log("关卡加载完成");
    }
}
  • 注意点:Start()函数只会在对象激活且脚本启用时调用一次。

  • 应用场景:在游戏开始时,进行一些需要在Awake()之后执行的初始化操作。

3.Update()

  • 用法:Update()每帧调用一次,是处理游戏逻辑的主要函数,如角色移动、碰撞检测等。例如,实现角色的移动控制:

public class PlayerMovement : MonoBehaviour
{
    public float moveSpeed = 5f;

    void Update()
    {
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(horizontalInput, 0f, verticalInput);
        transform.Translate(movement * moveSpeed * Time.deltaTime);
    }
}
  • 注意点:由于Update()每帧都会调用,因此其中的代码执行效率至关重要,避免在其中进行过于复杂或耗时的操作。

  • 应用场景:处理与游戏帧更新相关的逻辑,如用户输入处理、游戏对象的动态更新等。

4. FixedUpdate()

  • 用法:FixedUpdate()以固定的时间间隔调用,适合处理物理相关逻辑,确保物理模拟的稳定性。例如,控制刚体的运动:

public class CarController : MonoBehaviour
{
    private Rigidbody rb;
    public float driveForce = 1000f;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void FixedUpdate()
    {
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        rb.AddForce(transform.forward * verticalInput * driveForce);
        rb.AddTorque(transform.up * horizontalInput * driveForce);
    }
}
  • 注意点:FixedUpdate()的调用间隔由项目设置中的 “Fixed Timestep” 决定,与帧率无关。

  • 应用场景:处理物理模拟、刚体运动等需要稳定时间间隔的逻辑。

5. LateUpdate()

  • 用法:LateUpdate()在所有Update()函数调用后调用,常用于相机跟随等需要在角色移动后执行的操作。例如,实现相机跟随角色:

public class CameraFollow : MonoBehaviour
{
    public Transform target;
    public float smoothSpeed = 0.125f;
    public Vector3 offset;

    void LateUpdate()
    {
        Vector3 desiredPosition = target.position + offset;
        Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed);
        transform.position = smoothedPosition;

        transform.LookAt(target);
    }
}
  • 注意点:如果在LateUpdate()中进行过于复杂的操作,可能会导致画面卡顿,因此需要注意性能优化。

  • 应用场景:用于实现一些依赖于其他对象更新后的操作,如相机跟随、UI 元素的动态调整等。

6.OnDestroy()

  • 用法:OnDestroy()在对象被销毁时调用,可用于释放资源、取消订阅事件等。例如,在敌人被消灭时,清理相关资源:

public class Enemy : MonoBehaviour
{
    private ParticleSystem explosionEffect;

    void Start()
    {
        explosionEffect = GetComponent<ParticleSystem>();
    }

    void OnDestroy()
    {
        if (explosionEffect!= null)
        {
            explosionEffect.Play();
            Destroy(explosionEffect.gameObject, explosionEffect.main.duration);
        }
    }
}
  • 注意点:当 GameObject 被禁用时,OnDestroy()不会被调用,只有当 GameObject 被真正销毁时才会触发。

  • 应用场景:在对象生命周期结束时,进行资源释放、清理工作,避免内存泄漏等问题。


四、GameObject 游戏物体

        GameObject 是 Unity 场景中的基本对象,可挂载各种组件来实现不同功能。

1.GameObject.Find()

  • 用法:GameObject.Find()通过名称查找场景中的 GameObject。例如,查找名为 “Player” 的游戏对象:

public class GameManager : MonoBehaviour
{
    void Start()
    {
        GameObject player = GameObject.Find("Player");
        if (player!= null)
        {
            Debug.Log("找到玩家对象");
        }
        else
        {
            Debug.Log("未找到玩家对象");
        }
    }
}
  • 注意点:该方法性能开销较大,因为它需要遍历整个场景来查找指定名称的对象。不建议在Update()中频繁使用,否则可能会影响游戏性能。

  • 应用场景:在游戏初始化阶段,一次性查找特定的游戏对象。

2.GameObject.SetActive()

  • 用法:GameObject.SetActive()设置 GameObject 的激活状态,激活的物体才会在场景中显示并参与逻辑运算。例如,控制 UI 面板的显示与隐藏:

public class UIManager : MonoBehaviour
{
    public GameObject pausePanel;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            bool isActive = pausePanel.activeSelf;
            pausePanel.SetActive(!isActive);
        }
    }
}
  • 注意点:当 GameObject 被设置为非激活状态时,其所有组件(包括脚本)的Update()等函数将不会被调用。

  • 应用场景:用于控制游戏对象的显示与隐藏,以及启用或禁用一组相关的游戏对象。

3.AddComponent()

  • 用法:AddComponent()为 GameObject 添加组件,例如为游戏物体添加刚体组件:

public class ObjectInitializer : MonoBehaviour
{
    void Start()
    {
        gameObject.AddComponent<Rigidbody>();
    }
}
  • 注意点:如果 GameObject 上已经存在相同类型的组件,再次添加会抛出异常。可以先通过GetComponent()检查是否已存在该组件。

  • 应用场景:在运行时动态为游戏对象添加所需的组件,以实现特定的功能。


五、MonoBehaviour 基类

        MonoBehaviour 是所有 Unity 脚本的基类,继承自它的脚本可以使用 Unity 提供的各种功能。

1.生命周期函数的使用

        继承 MonoBehaviour 后,脚本可以使用上述提到的生命周期函数,如Awake()、Start()、Update()等,方便开发者在不同阶段控制脚本的行为。

2.访问 GameObject 和其组件

  • 访问 GameObject:通过this.gameObject可获取当前脚本所在的 GameObject。例如,在脚本中获取自身 GameObject 的名称:

public class SelfInfo : MonoBehaviour
{
    void Start()
    {
        string objectName = this.gameObject.name;
        Debug.Log("当前对象名称:" + objectName);
    }
}
  • 获取组件:使用GetComponent<Renderer>()等方法可获取 GameObject 上挂载的指定类型的组件。例如,获取渲染组件并修改其颜色:

public class ChangeColor : MonoBehaviour
{
    void Start()
    {
        Renderer renderer = GetComponent<Renderer>();
        if (renderer!= null)
        {
            renderer.material.color = Color.red;
        }
    }
}
  • 注意点:在获取组件时,要确保组件已经挂载到 GameObject 上,否则可能会返回null,导致空引用异常。

  • 应用场景:在脚本中操作自身所在的 GameObject 及其组件,实现各种游戏逻辑。


六、Component 组件

        Component 是所有组件的基类,GameObject 通过挂载 Component 实现各种功能。

1.GetComponent()

  • 用法:GetComponent()用于获取 GameObject 上挂载的指定类型的组件。例如,获取 Transform 组件:

public class TransformGetter : MonoBehaviour
{
    void Start()
    {
        Transform trans = gameObject.GetComponent<Transform>();
        if (trans!= null)
        {
            trans.position = new Vector3(1, 1, 1);
        }
    }
}
  • 注意点:如果 GameObject 上没有挂载指定类型的组件,该方法将返回null,因此在使用返回的组件前,最好进行非空判断。

  • 应用场景:在脚本中获取特定组件,以便对其进行操作和控制。

2.GetComponents()

  • 用法:GetComponents()获取 GameObject 上挂载的所有指定类型的组件,返回一个组件数组。例如,获取所有的 MeshFilter 组件:

public class MeshFilterGetter : MonoBehaviour
{
    void Start()
    {
        MeshFilter[] meshFilters = gameObject.GetComponents<MeshFilter>();
        foreach (MeshFilter mf in meshFilters)
        {
            Debug.Log("找到MeshFilter组件:" + mf.name);
        }
    }
}
  • 注意点:即使 GameObject 上只有一个指定类型的组件,该方法也会返回一个包含一个元素的数组。

  • 应用场景:当一个 GameObject 上可能挂载多个相同类型的组件,需要对它们进行统一处理时使用。

3.GetComponentsInChildren()

  • 用法:GetComponentsInChildren()获取 GameObject 及其所有子物体上挂载的指定类型的组件。例如,获取一个父物体及其所有子物体上的 Collider 组件:

public class ColliderGetter : MonoBehaviour
{
    void Start()
    {
        Collider[] colliders = gameObject.GetComponentsInChildren<Collider>();
        foreach (Collider col in colliders)
        {
            Debug.Log("找到Collider组件:" + col.name);
        }
    }
}
  • 注意点:该方法会递归查找子物体上的组件,因此如果场景层次结构较复杂,可能会有一定的性能开销。

  • 应用场景:在需要获取一个物体及其子物体上特定类型组件的情况下使用,如批量设置碰撞体的属性等。


七、Transform 变换组件

        Transform 组件用于管理 GameObject 的位置、旋转和缩放。

1.position

  • 用法:position表示物体在世界空间中的位置,是一个 Vector3 类型的值。可以通过直接赋值来改变物体的位置,例如:

public class ObjectPositioner : MonoBehaviour
{
    void Start()
    {
        transform.position = new Vector3(2, 2, 2);
    }
}
  • 注意点:修改position会直接改变物体在世界空间中的位置,如果物体有父物体,要考虑相对位置的变化。

  • 应用场景:用于精确设置物体在世界空间中的位置,如角色的出生点设置等。

2.rotation

  • 用法:rotation表示物体的旋转,以四元数形式存储,可用于精确控制物体的朝向。可以通过赋值或使用相关的旋转函数来改变物体的旋转,例如:

public class ObjectRotator : MonoBehaviour
{
    void Start()
    {
        transform.rotation = Quaternion.Euler(0, 45, 0);
    }
}
  • 注意点:四元数的运算相对复杂,直接修改时要注意其数学原理,也可以使用欧拉角(Euler Angles)进行转换,但要注意万向节死锁问题。

  • 应用场景:用于控制物体的朝向,如角色的转向、武器的瞄准方向等。

3.localPosition

  • 用法:localPosition表示物体相对于父物体的位置。例如,在一个父物体下的子物体,通过修改localPosition来调整其相对位置:

public class ChildPositioner : MonoBehaviour
{
    void Start()
    {
        transform.localPosition = new Vector3(1, 0, 0);
    }
}
  • 注意点:如果物体没有父物体,localPosition和position的值是相同的。

  • 应用场景:在处理层级关系的物体时,用于调整子物体相对于父物体的位置。

4.localRotation

  • 用法:localRotation表示物体相对于父物体的旋转。例如,设置子物体相对于父物体的旋转角度:

public class ChildRotator : MonoBehaviour
{
    void Start()
    {
        transform.localRotation = Quaternion.Euler(0, 90, 0);
    }
}
  • 注意点:同样要注意四元数运算和万向节死锁问题,且其旋转是相对于父物体的局部坐标系。

  • 应用场景:用于控制子物体相对于父物体的朝向,如车轮相对于车身的旋转等。

5.localScale

  • 用法:localScale表示物体相对于父物体的缩放比例。例如,将子物体在 x 轴方向上放大为原来的 2 倍,y 轴和 z 轴保持不变,可以这样写代码:

    public class ChildScaler : MonoBehaviour
    {
        void Start()
        {
            transform.localScale = new Vector3(2, 1, 1);
        }
    }
    

    也可以通过脚本在运行时动态改变缩放比例,比如制作一个根据游戏进度逐渐变大的特效:

    public class GrowEffect : MonoBehaviour
    {
        public float growthSpeed = 0.1f;
        void Update()
        {
            Vector3 currentScale = transform.localScale;
            currentScale += new Vector3(growthSpeed, growthSpeed, growthSpeed) * Time.deltaTime;
            transform.localScale = currentScale;
        }
    }
    

    .注意点:当物体没有父物体时,localScale 就是物体自身的实际缩放比例。
    .应用场景:在创建具有层级关系的模型时,localScale 非常有用。

6.Translate()

用法:Translate()用于移动 GameObject,它可以按照指定的方向和距离来移动对象。其参数可以是一个Vector3类型的值,表示移动的方向和距离,也可以指定移动的参考坐标系。例如,使物体沿着自身的前方向前移动一段距离:

public class MoveForward : MonoBehaviour
{
    public float speed = 5f;
    void Update()
    {
        transform.Translate(Vector3.forward * speed * Time.deltaTime);
    }
}

        如果希望物体相对于世界坐标系移动,可以传入第二个参数Space.World:

public class MoveInWorld : MonoBehaviour
{
    public float speed = 5f;
    void Update()
    {
        transform.Translate(Vector3.forward * speed * Time.deltaTime, Space.World);
    }
}

注意点:在使用Translate()时,如果物体有父物体,要注意移动是相对于父物体坐标系还是世界坐标系,不同的坐标系选择会导致不同的移动效果。另外,Translate()函数会直接修改物体的position或localPosition(取决于参考坐标系),如果在移动的同时还依赖于物体原来的位置进行其他计算,需要提前做好备份。
应用场景:广泛应用于角色的移动控制、物体的自动巡逻路径实现、UI 元素的滑动效果等。

7.Rotate()

用法:Rotate()用于旋转 GameObject,可以指定旋转的轴和角度。常见的使用方式是传入一个Vector3类型的值,分别表示绕 x 轴、y 轴、z 轴旋转的角度(单位为度),例如,使物体绕 y 轴旋转:

public class RotateAroundY : MonoBehaviour
{
    public float rotateSpeed = 90f;
    void Update()
    {
        transform.Rotate(Vector3.up * rotateSpeed * Time.deltaTime);
    }
}

        也可以指定旋转的中心点和旋转轴,通过RotateAround()函数来实现更复杂的旋转效果,例如,让一个物体围绕另一个物体旋转:

public class OrbitAround : MonoBehaviour
{
    public Transform target;
    public float orbitSpeed = 90f;
    void Update()
    {
        transform.RotateAround(target.position, Vector3.up, orbitSpeed * Time.deltaTime);
    }
}

注意点:使用Rotate()时,要注意角度的正负方向,遵循右手螺旋定则。在进行连续旋转时,要考虑累计误差的问题。对于RotateAround()函数,要确保旋转中心点的位置正确,否则可能会出现不符合预期的旋转效果。
应用场景:用于实现物体的各种旋转效果,如角色的转头动作、风扇叶片的转动、星球的自转和公转等。


八、Vector2 表示 2D 向量和点

        在 Unity 开发中,Vector2是一个用于表示二维空间中的向量和点的结构体,它在处理 2D 相关的逻辑时非常常用,比如 2D 游戏中的角色移动、坐标计算等。

1.基本运算

  Vector2支持常见的向量基本运算,包括加法、减法、乘法和除法。

  • 加法:用于将两个向量相加,得到一个新的向量。例如:
Vector2 a = new Vector2(1, 2);
Vector2 b = new Vector2(3, 4);
Vector2 c = a + b; // c 的值为 (4, 6)
  • 减法:可以计算两个向量的差值。例如:
Vector2 a = new Vector2(5, 3);
Vector2 b = new Vector2(2, 1);
Vector2 c = a - b; // c 的值为 (3, 2)

  • 乘法:分为与标量相乘和点积(内积)。与标量相乘可以缩放向量的长度;点积则返回一个标量,用于计算两个向量的夹角等信息。
    • 与标量相乘示例:
Vector2 a = new Vector2(2, 3);
float scalar = 2;
Vector2 b = a * scalar; // b 的值为 (4, 6)
  • 点积示例:
Vector2 a = new Vector2(1, 2);
Vector2 b = new Vector2(3, 4);
float dotProduct = Vector2.Dot(a, b); // dotProduct 的值为 1 * 3 + 2 * 4 = 11

  • 除法:可以将向量的每个分量除以一个标量,从而实现向量的缩放。例如:
Vector2 a = new Vector2(4, 6);
float scalar = 2;
Vector2 b = a / scalar; // b 的值为 (2, 3)

2.获取向量属性

  • 大小(长度):可以通过magnitude属性获取向量的大小。例如:
Vector2 a = new Vector2(3, 4);
float magnitude = a.magnitude; // magnitude 的值为 5(根据勾股定理计算)

  • 归一化:使用normalized属性可以将向量转换为长度为 1 的单位向量,方向与原向量相同。例如:
Vector2 a = new Vector2(3, 4);
Vector2 normalized = a.normalized; 
// normalized 的值约为 (0.6, 0.8),长度为 1

3.应用场景

        在 2D 游戏开发中,Vector2广泛应用于角色的移动控制。比如,在一个平台跳跃游戏中,可以使用Vector2来表示角色的移动方向和速度:

public class PlayerMovement2D : MonoBehaviour
{
    public float moveSpeed = 5f;
    void Update()
    {
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        Vector2 movement = new Vector2(horizontalInput, verticalInput);
        movement = movement.normalized * moveSpeed;

        transform.Translate(movement * Time.deltaTime);
    }
}

        同时,在碰撞检测、路径规划等涉及到 2D 坐标和方向计算的场景中,Vector2也发挥着重要作用。

4.注意点

        在进行向量运算时,要注意数据类型和精度问题。例如,在一些需要高精度计算的场景中,可能需要使用double类型来代替float,以避免精度损失。另外,在使用归一化向量时,如果原向量长度为 0(即零向量),normalized属性会返回零向量,这可能会导致一些逻辑错误,因此在使用前最好进行非零向量判断。


九、Input 访问输入系统的接口类

  Input类是 Unity 中用于获取用户输入信息的重要接口,通过它可以检测键盘、鼠标、手柄等设备的输入。

1.检测按键输入

  • GetKey():用于检测某个按键是否被按下。它接受一个KeyCode类型的参数,表示要检测的按键。例如:
void Update()
{
    if (Input.GetKey(KeyCode.Space))
    {
        Debug.Log("空格键被按下");
    }
}
  • GetKeyDown():检测某个按键是否在当前帧首次被按下。与GetKey()不同,GetKeyDown()只在按键按下的那一帧返回true。例如:
void Update()
{
    if (Input.GetKeyDown(KeyCode.Escape))
    {
        Debug.Log("Esc键首次被按下");
    }
}

  • GetKeyUp():检测某个按键是否在当前帧被释放。例如:
void Update()
{
    if (Input.GetKeyUp(KeyCode.LeftShift))
    {
        Debug.Log("左Shift键被释放");
    }
}

 

2.检测鼠标输入

  • GetMouseButton():检测鼠标按钮是否被按下,参数 0 代表左键,1 代表右键,2 代表中键。例如:
void Update()
{
    if (Input.GetMouseButton(0))
    {
        Debug.Log("鼠标左键被按下");
    }
}

  • GetMouseButtonDown():检测鼠标按钮是否在当前帧首次被按下。例如:
void Update()
{
    if (Input.GetMouseButtonDown(1))
    {
        Debug.Log("鼠标右键首次被按下");
    }
}

  • GetMouseButtonUp():检测鼠标按钮是否在当前帧被释放。例如:
void Update()
{
    if (Input.GetMouseButtonUp(2))
    {
        Debug.Log("鼠标中键被释放");
    }
}

3.获取轴输入值

   GetAxis()方法常用于处理摇杆或键盘方向键的输入,它返回一个范围通常在 -1 到 1 之间的值。例如,获取水平方向的输入:

void Update()
{
    float horizontalInput = Input.GetAxis("Horizontal");
    Debug.Log("水平输入值:" + horizontalInput);
}

        在 Unity 的输入设置中,可以自定义轴的名称和对应的按键或摇杆操作,使得输入处理更加灵活。

4.应用场景

        在各种类型的游戏中,Input类都是实现用户交互的基础。在动作游戏中,通过检测按键输入来控制角色的攻击、跳跃、移动等动作;在射击游戏中,利用鼠标输入来控制视角的转动和射击操作。同时,在一些模拟操作的场景中,如驾驶模拟游戏,GetAxis()方法可以用于获取方向盘或油门刹车的输入值。

5.注意点

        在不同的平台上,Input类的行为可能会有所差异。例如,在移动平台上,一些按键输入可能无法使用,需要通过虚拟按键或触摸操作来替代。另外,在处理连续输入时,要注意输入的响应频率,避免出现过于灵敏或迟钝的情况。


十、Message 消息

        Unity 中的消息系统为脚本之间的通信提供了一种便捷的方式,主要包括SendMessage()、BroadcastMessage()和SendMessageUpwards()这三个方法。

1.SendMessage()

  • 用法:SendMessage()用于向 GameObject 上所有挂载的脚本发送指定名称的消息。接收消息的脚本需要有相应名称的函数。该方法可以携带一个参数(可选),传递给接收消息的函数。例如:
// 发送消息的脚本
public class MessageSender : MonoBehaviour
{
    void Start()
    {
        gameObject.SendMessage("TakeDamage", 10);
    }
}

// 接收消息的脚本
public class HealthManager : MonoBehaviour
{
    public int currentHealth = 100;
    void TakeDamage(int damage)
    {
        currentHealth -= damage;
        Debug.Log("受到 " + damage + " 点伤害,剩余生命值:" + currentHealth);
    }
}

  • 注意点:如果 GameObject 上没有任何脚本包含指定名称的函数,SendMessage()不会产生错误,但也不会有任何效果。另外,SendMessage()是按照脚本挂载的顺序依次调用相应函数的,如果多个脚本中的同名函数有先后依赖关系,需要注意挂载顺序。

2.BroadcastMessage()

  • 用法:BroadcastMessage()与SendMessage()类似,但它会向 GameObject 及其所有子物体上挂载的脚本发送消息。例如:
// 发送消息的脚本
public class ParentMessageSender : MonoBehaviour
{
    void Start()
    {
        gameObject.BroadcastMessage("OnEnemySpawn");
    }
}

// 子物体上接收消息的脚本
public class EnemyAI : MonoBehaviour
{
    void OnEnemySpawn()
    {
        Debug.Log("敌人AI接收到生成消息");
    }
}
  • 注意点:由于BroadcastMessage()会递归地查找子物体上的脚本,因此在场景层次结构较复杂时,可能会有一定的性能开销。同时,与SendMessage()一样,如果没有找到相应的函数,也不会报错。

3.SendMessageUpwards()

  • 用法:SendMessageUpwards()向 GameObject 及其所有父物体上挂载的脚本发送消息。例如:
// 子物体上发送消息的脚本
public class ChildMessageSender : MonoBehaviour
{
    void Start()
    {
        gameObject.SendMessageUpwards("OnChildEvent");
    }
}

// 父物体上接收消息的脚本
public class ParentController : MonoBehaviour
{
    void OnChildEvent()
    {
        Debug.Log("父物体接收到子物体的事件消息");
    }
}
  • 注意点:在使用SendMessageUpwards()时,如果父物体层次较多,消息传递的路径可能会比较长,要注意可能出现的性能问题。另外,如果父物体上没有相应的函数,消息会继续向上传递,直到找到匹配的函数或到达根物体。

4.应用场景

        消息系统在游戏开发中常用于实现组件之间的解耦。例如,在一个角色扮演游戏中,当角色受到伤害时,可以通过SendMessage()通知生命值管理组件进行相应的处理;在一个包含多个敌人的场景中,使用BroadcastMessage()可以方便地通知所有敌人执行某个动作,如进入战斗状态;SendMessageUpwards()则可以用于子物体向父物体汇报状态或请求某些操作,比如在一个 UI 界面中,子元素可以通过它向父级 UI 管理器发送事件消息。

5.替代方案

        除了消息系统,还可以使用委托、事件等机制来实现脚本之间的通信。委托和事件在处理复杂的多对多通信关系时更加灵活和高效,并且可以提供更好的类型安全性。但消息系统在一些简单的场景中,使用起来更加简洁直观。


十一、Animator 动画组件

   Animator组件是 Unity 中用于管理和控制动画的重要工具,它可以处理复杂的动画状态机和动画片段之间的过渡。

1.动画状态机

   Animator组件的核心是动画状态机,它由多个动画状态和状态之间的过渡组成。可以通过 Unity 的动画窗口来创建和编辑状态机。例如,一个角色的动画状态机可能包含 “站立”“行走”“跑步”“跳跃” 等状态,以及它们之间的过渡条件,如按下某个按键或满足特定的速度条件。

2.控制动画播放

  • 设置动画参数:可以通过Animator组件的SetBool()、SetFloat()、SetInt()等方法来设置动画参数,这些参数可以用于控制动画状态之间的过渡。例如:
public class PlayerAnimatorController : MonoBehaviour
{
    private Animator animator;
    void Start()
    {
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.W))
        {
            animator.SetBool("IsWalking", true);
        }
        else
        {
            animator.SetBool("IsWalking", false);
        }
    }
}

  • 播放特定动画:可以使用Play()方法来直接播放指定名称的动画片段。例如:
public class AnimationTrigger : MonoBehaviour
{
    private Animator animator;
    void Start()
    {
        animator = GetComponent<Animator>();
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Trigger"))
        {
            animator.Play("ExplosionAnimation");
        }
    }
}

3.动画事件

        可以在动画片段中添加动画事件,当动画播放到指定帧时,会调用相应的函数。例如,在一个攻击动画中,可以在攻击动作的关键帧处添加动画事件,用于触发伤害判定或特效播放。

4.应用场景

        在各类游戏中,Animator组件广泛应用于角色动画、特效动画等的控制。在动作游戏中,通过精确控制角色的各种动作动画,如攻击、防御、闪避等,来提升游戏的战斗体验;在解谜游戏中,使用动画来展示机关的开启、关闭等过程,增强游戏的交互性和趣味性。

5.注意点

        在创建和管理动画状态机时,要保持逻辑清晰,避免状态和过渡过于复杂,导致难以调试和维护。同时,动画参数的命名和使用要规范,以确保不同脚本之间能够正确地控制动画播放。另外,动画事件的触发位置要准确,否则可能会出现逻辑错误或视觉效果不协调的问题。


十二、Time 时间信息的接口类

   Time类提供了与时间相关的信息和功能,在游戏开发中用于控制游戏的时间流逝、帧率等。

1.时间尺度

   Time.timeScale属性用于控制游戏的时间流逝速度。默认值为 1,表示正常的时间流逝速度;当设置为 0 时,游戏会暂停。例如,实现游戏的暂停功能:

public class PauseManager : MonoBehaviour
{
    public bool isPaused = false;
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            isPaused =!isPaused;
            Time.timeScale = isPaused? 0 : 1;
        }
    }
}

2.时间增量

   Time.deltaTime表示上一帧到当前帧所经过的时间(以秒为单位)。在处理游戏对象的移动、旋转等连续变化的操作时,通常会乘以Time.deltaTime,以确保在不同帧率下动作的流畅性和一致性。例如:

public class SmoothMovement : MonoBehaviour
{
    public float speed = 5f;
    void Update()
    {
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(horizontalInput, 0, verticalInput);
        transform.Translate(movement * speed * Time.deltaTime);
    }
}

3.固定时间步长

   Time.fixedDeltaTime表示FixedUpdate()函数的调用间隔,它是一个固定的值,与帧率无关。在处理物理模拟等需要稳定时间间隔的操作时,FixedUpdate()会以Time.fixedDeltaTime为间隔进行调用。

4.应用场景

   Time类在游戏开发中无处不在。除了上述的暂停功能和保证动作流畅性外,还可以用于实现倒计时、时间限制的关卡、慢动作效果等。例如,在一个赛车游戏中,可以使用Time.timeScale来实现慢动作过弯效果,增强游戏的视觉冲击力。

5.注意点

        修改Time.timeScale时要谨慎,因为它会影响到整个游戏中所有依赖时间的操作,可能会导致一些意外的结果,如音频播放速度变化等。另外,在使用Time.deltaTime和Time.fixedDeltaTime时,要理解它们的区别和适用场景,避免混淆使用导致逻辑错误。


十三、Mathf 数学函数类

   Mathf类提供了一系列常用的数学函数,在游戏开发中用于各种数学计算。

1.基本数学运算

  • 绝对值:Mathf.Abs()用于返回一个数的绝对值。例如:
float num = -5;
float absNum = Mathf.Abs(num); // absNum 的值为 5

  • 取整:Mathf.Floor()向下取整,Mathf.Ceil()向上取整,Mathf.Round()四舍五入取整。例如:
float num1 = 3.2f;
float num2 = 3.8f;
float floorNum = Mathf.Floor(num1); // floorNum 的值为 3
float ceilNum = Mathf.Ceil(num2); // ceilNum 的值为 4
float roundNum = Mathf.Round(3.5f); // roundNum 的值为 4

2.三角函数

   Mathf类提供了Sin()、Cos()、Tan()等三角函数,这些函数的参数为弧度值,常用于计算角度相关的物理运动、物体旋转等。

  • 正弦函数(Sin()):返回一个角度(以弧度为单位)的正弦值,其返回值范围在 -1 到 1 之间。例如,计算 90 度(在代码中需转换为弧度,90 * Mathf.Deg2Rad)的正弦值:
float angleInDegrees = 90;
float angleInRadians = angleInDegrees * Mathf.Deg2Rad;
float sinValue = Mathf.Sin(angleInRadians); 
// sinValue 的值接近 1(由于浮点数精度问题,可能不是精确的 1)

        在游戏中,比如制作一个摆动的钟摆,就可以利用正弦函数来控制钟摆的角度变化,实现自然的摆动效果。

  • 余弦函数(Cos()):返回一个角度(以弧度为单位)的余弦值,返回值同样在 -1 到 1 之间。例如:
float anotherAngleInDegrees = 45;
float anotherAngleInRadians = anotherAngleInDegrees * Mathf.Deg2Rad;
float cosValue = Mathf.Cos(anotherAngleInRadians); 
// cosValue 的值接近 0.707(由于浮点数精度问题,可能略有偏差)

        在一些需要计算物体之间角度关系或者根据角度来分配资源的场景中,余弦函数会非常有用。比如在一个塔防游戏中,计算防御塔攻击范围与敌人位置的角度关系,从而判断敌人是否在攻击范围内。

  • 正切函数(Tan()):返回一个角度(以弧度为单位)的正切值。例如:
float yetAnotherAngleInDegrees = 30;
float yetAnotherAngleInRadians = yetAnotherAngleInDegrees * Mathf.Deg2Rad;
float tanValue = Mathf.Tan(yetAnotherAngleInRadians); 
// tanValue 的值接近 0.577(由于浮点数精度问题,可能有一定误差)

        在处理斜坡、斜面相关的物理模拟,或者计算视角倾斜角度等方面,正切函数能发挥作用。比如在赛车游戏中,计算赛车在斜坡上的行驶角度和速度变化等。

3.幂运算与开方

    Mathf类还包含了幂运算(Pow())和开方(Sqrt())等函数,用于处理数值的指数和平方根计算。

  • 幂运算(Pow()):Mathf.Pow()接受两个参数,第一个参数是底数,第二个参数是指数,返回底数的指定指数次幂。例如:
float baseNum = 2;
float exponent = 3;
float powerResult = Mathf.Pow(baseNum, exponent); 
// powerResult 的值为 8

        在一些涉及能量计算、伤害公式计算等场景中,幂运算可能会被用到。比如游戏中的魔法伤害,根据魔法等级的不同,伤害值以一定的指数方式增长。

  • 开方(Sqrt()):Mathf.Sqrt()用于计算一个数的平方根,参数必须为非负数。例如:
float numToSqrt = 16;
float sqrtResult = Mathf.Sqrt(numToSqrt); 
// sqrtResult 的值为 4

        在计算距离、向量长度等方面,开方函数经常会被使用。比如在一个多人在线游戏中,计算玩家之间的直线距离,就可能会用到向量的模长计算,而模长计算过程中会涉及到开方运算。

4.其他常用函数

  • 最小值与最大值(Min()和Max()):Mathf.Min()返回一组数中的最小值,Mathf.Max()返回一组数中的最大值。例如:
float numA = 5;
float numB = 10;
float minValue = Mathf.Min(numA, numB); 
// minValue 的值为 5
float maxValue = Mathf.Max(numA, numB); 
// maxValue 的值为 10

        在游戏中,用于限制某个数值的范围,比如角色的生命值不能低于 0,魔法值不能超过最大值等场景。

  • 插值函数(Lerp()):Mathf.Lerp()用于在两个值之间进行线性插值,接受三个参数,分别是起始值、结束值和一个在 0 到 1 之间的插值系数。例如:
float startValue = 0;
float endValue = 10;
float t = 0.5f;
float lerpResult = Mathf.Lerp(startValue, endValue, t); 
// lerpResult 的值为 5

        在制作动画过渡效果、镜头移动的平滑过渡等方面,插值函数非常实用。比如角色在两个位置之间移动时,可以使用线性插值来实现平滑的移动效果。

5.注意点

        在使用Mathf类的函数时,要注意参数的单位和范围。例如三角函数的参数是弧度值,在使用角度时需要进行转换;Sqrt()函数的参数必须是非负数,否则会得到无效结果。另外,由于浮点数的精度问题,在进行一些精确比较和计算时,可能需要设置一定的误差范围,避免出现由于精度误差导致的逻辑错误。同时,在频繁调用这些数学函数的地方,要注意性能问题,尽量避免不必要的重复计算。


十四、Random 生成随机数的类

   Random类在 Unity 中用于生成随机数,在游戏开发中广泛应用于各种需要随机性的场景,如随机生成敌人位置、掉落物品、随机事件触发等。

1.生成随机整数

        可以使用Random.Range()方法来生成指定范围内的随机整数。它有两种重载形式,一种是接受两个整数参数,返回一个在指定范围内(包括最小值,不包括最大值)的随机整数;另一种是接受两个浮点数参数,返回一个在指定范围内(包括最小值,不包括最大值)的随机浮点数。生成随机整数的示例如下:

// 生成一个 0 到 9 之间的随机整数
int randomInt = Random.Range(0, 10); 

        在一个角色扮演游戏中,可以利用随机整数来决定敌人的等级。比如在某个地图区域,敌人的等级范围是 5 到 10 级,就可以这样生成:

int enemyLevel = Random.Range(5, 11); 

2.生成随机浮点数

        使用Random.Range()的浮点数重载形式可以生成随机浮点数。例如:

// 生成一个 0.0f 到 1.0f 之间的随机浮点数
float randomFloat1 = Random.Range(0f, 1f); 

// 生成一个 2.5f 到 5.5f 之间的随机浮点数
float randomFloat2 = Random.Range(2.5f, 5.5f); 

        在制作一些需要随机运动轨迹的特效,如粒子效果的扩散范围,就可以使用随机浮点数来控制粒子的初始位置和速度。

3.随机旋转与随机向量

  • 随机旋转:可以利用Random.rotation来获取一个随机的旋转值(四元数类型)。例如,为一个物体设置随机的初始旋转:
public class RandomRotateObject : MonoBehaviour
{
    void Start()
    {
        transform.rotation = Random.rotation;
    }
}

  • 随机向量:Random.insideUnitCircle返回一个在单位圆内的随机二维向量,Random.insideUnitSphere返回一个在单位球内的随机三维向量。比如在一个射击游戏中,为子弹添加一些随机的散射效果,可以使用Random.insideUnitCircle来生成随机的偏移向量:
public class BulletScript : MonoBehaviour
{
    public float bulletSpeed = 10f;
    void Start()
    {
        Vector2 randomOffset = Random.insideUnitCircle.normalized;
        Vector3 direction = new Vector3(randomOffset.x, 0, randomOffset.y);
        GetComponent<Rigidbody>().velocity = direction * bulletSpeed;
    }
}

4.随机种子

  Random.InitState()方法可以设置随机数生成器的种子,相同的种子会生成相同的随机数序列。这在一些需要重现随机结果的场景中非常有用,比如游戏的回放功能。例如:

// 设置随机种子为 123
Random.InitState(123); 

5.注意点

        在使用Random类时,要注意随机数的分布和范围是否符合需求。如果需要更复杂的随机分布,可能需要结合其他数学方法进行处理。另外,在网络同步的游戏中,使用随机数时要考虑到各个客户端生成的随机数一致性问题,合理设置随机种子或者采用其他同步机制。同时,频繁生成大量随机数可能会对性能产生一定影响,尤其是在性能敏感的场景中,要注意优化随机数的生成频率。


十五、OnMouseEventFunction 鼠标回调事件

        在 Unity 中,鼠标回调事件是一种方便处理鼠标与游戏对象交互的机制,通过在脚本中定义特定的方法,当鼠标对游戏对象执行相应操作时,这些方法会被自动调用。

1.OnMouseEnter()

        当鼠标指针进入游戏对象的碰撞体(Collider)时,OnMouseEnter()方法会被调用。例如,在一个 UI 按钮上,当鼠标移到按钮上时,想要改变按钮的颜色以提示用户,就可以这样实现:

using UnityEngine;
using UnityEngine.UI;

public class ButtonHighlight : MonoBehaviour
{
    private Image buttonImage;
    private Color originalColor;
    void Start()
    {
        buttonImage = GetComponent<Image>();
        originalColor = buttonImage.color;
    }

    void OnMouseEnter()
    {
        buttonImage.color = Color.grey;
    }
}

2.OnMouseExit()

        当鼠标指针离开游戏对象的碰撞体时,OnMouseExit()方法会被调用。继续上面按钮的例子,当鼠标离开按钮时,恢复按钮的原始颜色:

public class ButtonHighlight : MonoBehaviour
{
    private Image buttonImage;
    private Color originalColor;
    void Start()
    {
        buttonImage = GetComponent<Image>();
        originalColor = buttonImage.color;
    }

    void OnMouseEnter()
    {
        buttonImage.color = Color.grey;
    }

    void OnMouseExit()
    {
        buttonImage.color = originalColor;
    }
}

3.OnMouseDown()

        当鼠标左键在游戏对象的碰撞体上按下时,OnMouseDown()方法会被调用。例如,在一个场景中有一个可拾取的道具,当鼠标左键点击道具时,执行拾取操作:

public class PickupItem : MonoBehaviour
{
    void OnMouseDown()
    {
        Debug.Log("拾取道具");
        // 这里可以添加实际的拾取逻辑,如从场景中移除道具,添加到玩家背包等
    }
}

4.OnMouseUp()

        当在游戏对象的碰撞体上按下的鼠标左键被释放时,OnMouseUp()方法会被调用。可以结合OnMouseDown()和OnMouseUp()来实现更复杂的交互,比如模拟一个拖拽操作:

public class DragObject : MonoBehaviour
{
    private Vector3 offset;
    private bool isDragging = false;

    void OnMouseDown()
    {
        offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
        isDragging = true;
    }

    void OnMouseUp()
    {
        isDragging = false;
    }

    void Update()
    {
        if (isDragging)
        {
            Vector3 newPosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0)) + offset;
            transform.position = new Vector3(newPosition.x, newPosition.y, transform.position.z);
        }
    }
}

5.OnMouseOver()

        当鼠标指针在游戏对象的碰撞体上停留时,OnMouseOver()方法会在每帧被调用。例如,在一个显示提示信息的 UI 元素上,当鼠标悬停时,持续显示提示内容:

public class Tooltip : MonoBehaviour
{
    public string tooltipText;
    void OnMouseOver()
    {
        Debug.Log(tooltipText);
        // 这里可以添加显示提示框的逻辑,如创建一个UI文本框并显示提示内容
    }
}

6.注意点

        这些鼠标回调事件依赖于游戏对象上挂载的碰撞体组件,所以确保游戏对象有合适的碰撞体(如 BoxCollider、SphereCollider 等)。另外,在多个游戏对象重叠且都有鼠标回调事件时,要注意事件的响应顺序和优先级问题。同时,如果在OnMouseOver()方法中执行复杂的操作,要注意性能,避免每帧调用导致的性能开销过大。


十六、Coroutine 协程

        协程是 Unity 中一种强大的异步编程工具,它允许在不阻塞主线程的情况下执行一些需要等待的操作,如延迟执行代码、异步加载资源等。

1.协程的基本结构

        一个协程是一个带有IEnumerator返回类型的方法,方法中使用yield return语句来暂停和恢复执行。例如,一个简单的延迟打印协程:

using UnityEngine;

public class CoroutineExample : MonoBehaviour
{
    IEnumerator DelayPrint()
    {
        Debug.Log("开始协程");
        yield return new WaitForSeconds(3f);
        Debug.Log("3秒后打印");
    }
}

2.启动和停止协程

  • 启动协程:使用StartCoroutine()方法来启动一个协程。例如,在Start()方法中启动上面定义的DelayPrint协程:
public class CoroutineExample : MonoBehaviour
{
    IEnumerator DelayPrint()
    {
        Debug.Log("开始协程");
        yield return new WaitForSeconds(3f);
        Debug.Log("3秒后打印");
    }

    void Start()
    {
        StartCoroutine(DelayPrint());
    }
}
  • 停止协程:可以使用StopCoroutine()方法来停止一个协程。如果要停止的协程是通过方法名启动的,需要在启动协程时保存协程的引用。例如:
public class CoroutineControl : MonoBehaviour
{
    private Coroutine myCoroutine;
    IEnumerator Countdown()
    {
        for (int i = 10; i > 0; i--)
        {
            Debug.Log(i);
            yield return new WaitForSeconds(1f);
        }
    }

    void Start()
    {
        myCoroutine = StartCoroutine(Countdown());
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            StopCoroutine(myCoroutine);
        }
    }
}

3.等待条件

yield return语句可以搭配不同的条件来实现各种等待效果。

  • 等待固定时间:使用WaitForSeconds(),如前面的例子所示,用于等待指定的秒数。
  • 等待帧结束:yield return null表示等待当前帧结束,然后继续执行协程。在一些需要在每帧更新后执行特定操作的场景中会用到。
  • 等待另一个协程结束:可以使用yield return StartCoroutine(otherCoroutine())来等待另一个协程执行完毕。例如:
public class CoroutineChain : MonoBehaviour
{
    IEnumerator Coroutine1()
    {
        Debug.Log("Coroutine1 开始");
        yield return new WaitForSeconds(2f);
        Debug.Log("Coroutine1 结束");
    }

    IEnumerator Coroutine2()
    {
        Debug.Log("Coroutine2 开始");
        yield return StartCoroutine(Coroutine1());
        Debug.Log("Coroutine2 继续执行");
    }

    void Start()
    {
        StartCoroutine(Coroutine2());
    }
}

4.应用场景

        协程在游戏开发中有广泛的应用。在加载场景时,可以使用协程来显示加载进度条,同时异步加载资源;在制作动画效果时,通过协程实现动画的延迟播放和顺序播放;在网络通信中,等待服务器响应时不会阻塞主线程,保证游戏的流畅运行。

5.注意点

        在使用协程时,要确保正确地启动和停止协程,避免出现内存泄漏或逻辑错误。同时,过多的协程可能会对性能产生一定影响,尤其是在协程中执行复杂操作或频繁创建和销毁协程的情况下。另外,要注意yield return语句的使用,确保等待条件符合预期,否则可能导致协程无法按计划执行。


十七、Invoke 延时调用

  Invoke是 Unity 中用于实现延时调用方法的功能,它可以在指定的时间后调用一个方法,或者按照一定的时间间隔重复调用一个方法。

1.单次延时调用

        使用Invoke()方法可以在指定的秒数后调用一个无参数的方法。其语法形式为Invoke(string methodName, float time),其中methodName是要调用的方法名(以字符串形式表示),time是延迟的时间(单位为秒)。例如,在 3 秒后调用DoSomething方法:

public class InvokeExample : MonoBehaviour
{
    void Start()
    {
        Invoke("DoSomething", 3f);
    }

    void DoSomething()
    {
        Debug.Log("3秒已到,执行操作");
    }
}

2.重复延时调用

   InvokeRepeating()方法用于按照固定的时间间隔重复调用一个方法。语法为InvokeRepeating(string methodName, float time, float repeatRate),time表示首次调用前的延迟时间,repeatRate表示每次调用的时间间隔。比如,每隔 2 秒重复打印一条信息:

public class RepeatingInvokeExample : MonoBehaviour
{
    void Start()
    {
        InvokeRepeating("LogMessage", 1f, 2f);
    }

    void LogMessage()
    {
        Debug.Log("重复调用,输出信息");
    }

    void OnDisable()
    {
        CancelInvoke("LogMessage");
    }
}

        这里在OnDisable方法中使用CancelInvoke()来停止重复调用,以避免在对象禁用后仍然执行不必要的操作。

3.注意事项

  • 方法名匹配:Invoke和InvokeRepeating中传入的方法名必须与实际定义的方法名完全一致(包括大小写),否则无法正确调用方法。并且,被调用的方法必须是所在脚本中的非静态方法。
  • 性能考量:虽然Invoke和InvokeRepeating使用起来很方便,但在大量使用或者重复调用过于频繁时,可能会产生一定的性能开销。尤其是在InvokeRepeating中,如果设置的时间间隔过小,会使方法频繁执行,影响游戏性能。
  • 取消调用:对于InvokeRepeating启动的重复调用,一定要记得在合适的时候(如对象销毁、场景切换等)使用CancelInvoke来停止调用,防止出现内存泄漏或者不必要的逻辑错误。如果不传入参数,CancelInvoke会取消当前脚本中所有通过Invoke和InvokeRepeating启动的调用。

4.应用场景

  • 技能冷却:在角色扮演游戏中,当玩家释放技能后,可以使用Invoke来设置技能的冷却时间,在冷却时间结束后,允许玩家再次使用该技能。
  • 定时事件:比如在游戏中定时生成敌人、刷新道具等。可以通过InvokeRepeating按照一定的时间间隔来触发这些事件。
  • 延迟效果:在角色受到伤害后,延迟一段时间再显示血条减少的动画效果,或者在敌人死亡后,延迟播放爆炸特效等。


十八、Rigidbody 刚体

   Rigidbody组件是 Unity 中用于模拟物体物理行为的重要工具,它可以使游戏对象遵循现实世界的物理规则,如重力、碰撞、力的作用等,为游戏增添更加真实的交互体验。

1.添加和基本属性

        要使一个游戏对象具有物理行为,需要为其添加Rigidbody组件,可以在脚本中使用gameObject.AddComponent<Rigidbody>()动态添加,或者在 Unity 编辑器中手动添加。Rigidbody有一些基本属性:

  • 质量(mass):表示物体的质量,影响物体在受到力的作用时的加速度。例如,质量越大的物体,在相同的力作用下,加速度越小。
public class RigidbodyExample : MonoBehaviour
{
    private Rigidbody rb;
    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.mass = 5f; // 设置质量为5千克
    }
}

  • 阻力(drag):用于模拟物体在运动过程中受到的空气阻力或其他阻力。值越大,物体减速越快。
rb.drag = 2f; // 设置阻力为2

  • 角阻力(angularDrag):控制物体旋转时受到的阻力,影响物体的旋转速度变化。
rb.angularDrag = 0.5f; // 设置角阻力为0.5

  • 使用重力(useGravity):一个布尔值,决定物体是否受重力影响。
rb.useGravity = false; // 关闭重力

  • 运动学(isKinematic):当设置为true时,物体将不受物理引擎的控制,而是完全由脚本控制其位置和旋转。常用于需要精确控制运动的物体,如电梯、自动门等。
rb.isKinematic = true; // 设置为运动学刚体

2.施加力和扭矩

  • 施加力(AddForce):AddForce()方法用于向刚体施加一个力,使其产生加速度。可以指定力的方向和大小,力的方向通常用Vector3表示。例如,让一个物体向前运动:
public class MoveObjectWithForce : MonoBehaviour
{
    private Rigidbody rb;
    public float forceMagnitude = 10f;
    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.W))
        {
            rb.AddForce(transform.forward * forceMagnitude);
        }
    }
}

  • 施加扭矩(AddTorque):AddTorque()方法用于向刚体施加一个扭矩,使其产生旋转。例如,使一个物体绕自身的 Y 轴旋转:
public class RotateObjectWithTorque : MonoBehaviour
{
    private Rigidbody rb;
    public float torqueMagnitude = 5f;
    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.A))
        {
            rb.AddTorque(Vector3.up * torqueMagnitude);
        }
    }
}

3.碰撞检测

        当两个带有Rigidbody和碰撞体(如BoxCollider、SphereCollider等)的游戏对象发生碰撞时,可以通过以下方法检测:

  • OnCollisionEnter:当刚体进入另一个刚体的碰撞体时调用,常用于处理碰撞开始的逻辑,如产生伤害、触发事件等。
public class CollisionHandler : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Enemy"))
        {
            Debug.Log("与敌人发生碰撞");
            // 处理碰撞逻辑,如减少生命值等
        }
    }
}
  • OnCollisionStay:在刚体保持与另一个刚体的碰撞状态时,每一帧都会调用,可用于持续检测碰撞状态下的操作,如推动物体等。
void OnCollisionStay(Collision collision)
{
    if (collision.gameObject.CompareTag("PushableObject"))
    {
        // 施加力推动可推动的物体
        Rigidbody otherRb = collision.gameObject.GetComponent<Rigidbody>();
        if (otherRb!= null)
        {
            otherRb.AddForce(transform.forward * 5f);
        }
    }
}
  • OnCollisionExit:当刚体离开另一个刚体的碰撞体时调用,可用于处理碰撞结束的逻辑,如停止施加力等。
void OnCollisionExit(Collision collision)
{
    if (collision.gameObject.CompareTag("PushableObject"))
    {
        // 停止推动物体
    }
}

4.注意点

  • 性能优化:过多的刚体和复杂的物理模拟会消耗大量的性能。在使用刚体时,要合理规划,尽量减少不必要的刚体使用,对于一些不需要精确物理模拟的对象,可以考虑使用其他方式实现其行为。
  • 碰撞体设置:刚体必须搭配合适的碰撞体才能正常工作,并且碰撞体的形状和大小要根据实际需求进行调整,否则可能会出现碰撞不准确或不合理的情况。
  • 物理更新时机:刚体的物理计算是在FixedUpdate中进行的,因此在处理与刚体相关的逻辑时,要注意在FixedUpdate中进行,以确保物理模拟的稳定性和准确性。

5.应用场景

  • 动作游戏:在动作游戏中,角色的移动、跳跃、与敌人的碰撞等都可以借助刚体和物理引擎来实现,使游戏的操作和交互更加真实。
  • 赛车游戏:赛车的行驶、碰撞、漂移等效果,通过刚体和相关的物理设置能够很好地模拟出来,为玩家带来逼真的驾驶体验。
  • 解谜游戏:一些涉及物体推动、摆放的解谜游戏,利用刚体的物理特性可以增加游戏的趣味性和挑战性。

 


十九.结语

        对于 Unity 开发者来说,熟练掌握这些常用 API 是一项必备技能,但这仅仅是一个开始。随着游戏开发需求的不断变化和技术的持续演进,Unity 的 API 也在不断更新与拓展。我们需要保持学习的热情和好奇心,不断探索新的 API 功能,深入理解其原理和应用,同时结合实际项目进行实践,才能在游戏开发的道路上不断前进,创造出更加优秀、富有创意的游戏作品。希望本文对 Unity 常用 API 方法和类的详细介绍,能够为广大开发者在学习和使用 Unity 进行游戏开发时提供有益的参考和帮助,让大家在 Unity 的开发之旅中更加得心应手,实现自己的游戏开发梦想。

GameFramework框架全解析:从模块化架构到Unity游戏开发实战 在Unity游戏开发中,模块化架构是解决代码混乱、提升项目可维护性的核心设计思想。其原理在于通过高内聚、低耦合的模块划分,实现功能解耦与独立管理,从而显著提升开发效率和团队协作能力。这一技术价值在长期维护的中大型项目中尤为突出,能够有效管理资源加载、UI逻辑和对象生命周期等复杂场景。GameFramework作为一套专为Unity设计的模块化游戏框架,深度实践了这一理念,提供了资源管理、对象池、实体系统等开箱即用的核心模块。本系列教程将系统解析其设计哲学与实战应用,帮助开发者掌握从基础使用到深度定制的完整路 阅读详情

相关推荐

Unity计时器系统深度解析:从Time API到生产级TimerManager实现

在游戏开发中,计时器是控制游戏逻辑、动画和状态转换的核心基础组件。其原理基于时间增量累加与回调触发机制,通过统一管理多个计时任务来提升代码可维护性和性能。计时器的技术价值在于实现帧率无关的逻辑更新、精确的时间控制以及复杂的时序编排,广泛应用于技能冷却、UI动画、任务调度等场景。本文聚焦Unity引擎,深入剖析Time.deltaTime、Time.unscaledTime等关键API的底层机制与使用陷阱,并手把手教你构建一个支持高性能、可扩展的计时器管理系统,解决大量并发计时任务带来的性能瓶颈和内存泄漏问题

weixin_34037173的博客 376

【Unity】常用API和功能

前言 持续更新中.. 目标阅读者:Unity新手项目程序员 简单的前置:编程习惯 Unity常用API 按键Input 旧版Input void Update() { if (Input.GetKey("up")) { print("up arrow key is held"); } if (Input.GetKeyDown("down")) { pr.

PA_的博客 1万+

Unity多人游戏开发实战:网络同步架构与核心源码解析

网络游戏开发的核心在于实现多客户端间的实时状态同步,其基础原理是通过网络协议在客户端与服务器之间传递数据,确保所有玩家看到一致的游戏世界。这一技术的关键价值在于支撑了大规模多人在线互动体验,广泛应用于MMORPG、FPS、MOBA等游戏类型。在工程实践中,客户端预测与服务器调和是解决网络延迟、提升操作响应的核心技术,通过预测玩家操作并在服务器端进行权威验证与状态同步,有效平衡了流畅性与公平性。本文以Unity3D为引擎,深入剖析一套完整的多人游戏实战源码,详细解读其网络架构设计、状态同步策略及关键模块实现,

weixin_33743880的博客 488

unity常用APi

unity常用API 1、Event Function:事件函数 &nbsp; 1.Reset()&nbsp;:被附加脚本时、在游戏物体的组件上Reset会触发该事件函数 2.Start()&nbsp;:在游戏初始化时会执行一次 3.Update()&nbsp;:每一帧都会运行这个方法 4.FixedUpdate():&nbsp;会在指定帧调用该方法多少次 5.LateUpdate(...

luxifa1的博客 1187

【Unity自制手册】unity常用API大全——一篇文章足以(十万字详解)

掌握直接做游戏或者复刻demo,吃透卡拉米,随拿随用,翻阅必备,

秩沅 2万+

【unity每日一记】—物体移动的方法API的总结(GIF图文详解)

深层次探究移动API的原理

秩沅 1603

全网最详细的Unity InstantOC插件使用流程与调研

渲染管线通俗的讲就是为了使内存中的数据变为屏幕上的图像我们所做的一系列操作。绘制调用Draw Call是一个参数,这个参数反映的是在一帧的时间里,通过渲染管线渲染游戏对象的次数。在一帧的时间里出现了10个游戏物体的话(无光照),那么我们就要调用10次渲染管线进行渲染,此时的draw call为10,draw call 越大,GPU的渲染压力越大,需要的渲染时间越长,所以draw call越小越好。当两个游戏物体发生重合的时候,GPU怎么知道到底该渲染那一个游戏物体的图像到屏幕上的呢?

qq_51802524的博客 918

基于STM32的多路火灾报警系统设计

本文设计了一款基于STM32的多路火灾报警系统,利用多个传感器组合(如烟雾传感器、温度传感器等)进行数据采集与处理,精确判断火灾情况,并及时发出报警信号,保障生命与财产安全。本系统设计了一款基于STM32的多路火灾报警系统,通过结合烟雾传感器和温度传感器,提升了火灾检测的准确性和可靠性。未来,系统可以进一步集成更多传感器,如气体传感器、湿度传感器等,进一步提升火灾报警系统的智能化水平。本系统采用STM32F103单片机作为核心处理单元,结合烟雾传感器、温度传感器和报警模块,实现多通道火灾报警功能。

stm32d1219的博客 2028

超详细攻略!Unity 和Visual Studio关联不上,MonoBehaviour无法继承,unity代码api无法自动补全

我目前基本上看了全部关于这个问题的贴子,有csdn,51,脚本之家,B站,知乎,还有微软官方的说明。虽然我还没成功解决我的项目,但是我在这将主要的几种方法列出来,也许就有能解决你的问题的。我用的是2022版本,这个版本的扩展(Visual Studio Tools of Unity)在添加模块的时候就自动下载了。一般unity会自动检索到你安装的VS版本,然后选择这个版本,重启VS就可以自动补全代码了。如果发现有新版本,就尝试更新一下。如果本来就设置了VS版本,先切换到其它的,再重启,再切换回VS。

kqimo的博客 2828

MPU6050 vs BMX055 vs ICM20602:如何为你的项目选择最佳陀螺仪(附实测数据对比)

本文深度对比了MPU6050、BMX055和ICM20602三款主流六轴IMU传感器的核心性能。通过实测数据,分析了它们在精度、噪声、温漂、接口速度及资源占用上的差异,并提供了从项目需求出发的选型指南,帮助开发者根据预算、精度要求和处理器性能选择最合适的陀螺仪与加速度计组合。

h0i1j2k3l的博客 969

提高RFID检测距离以及检测速度的方法总结

摘要:提高RFID检测距离需优化阅读器功率、标签灵敏度、天线设计及频段选择,同时减少环境干扰;提升检测速度则需改进防碰撞算法、增强并行处理能力、精简数据传输。实际应用需平衡两者关系,如物流仓储采用UHF频段、高增益天线和动态Q算法实现远距离高速读取,ETC系统则通过微波频段和高速调制实现超远距离快速识别。不同场景需针对性优化硬件配置和系统参数。

start_up_go的博客 291

全网最全Unity面试题加强版之六《数据结构算法相关》

Unity面试题加强版之六数据结构算法相关 unity面试独家秘笈,码住学习,掌握轻轻松松拿Offer。 204.对数据结构了解吗?说说你常用的数据结构。 了解,数据结构是每个程序员都要会一点的。链表、列表、散列表最常用,队列和栈也经常使用。二叉树偶尔使用。 205.什么是状态机,什么是行为树 有限状态机系统:是指在不同阶段会呈现出不同的运行状态的系统,这些状态是有限的、不重叠的。这样的系统在某一时刻一定会处于其所有状态中的一个状态,此时它接收一部分允许的输入,产生一部分可能的响应,并且迁移到一部分可能的状

他在试图用代码书写这个世界,字里行间温柔尽显。勿忘初心,努力做有价值的技术文章。 1831

STC单片机使用FatFs文件系统读取SD卡指定文件内容

STC单片机使用FatFs文件系统读取SD卡指定文件内容

perseverance51博客 2744

【WPF&Unity】全网最细Unity嵌入WPF中 ---终极无敌版---

Wpf中嵌入Unity Unity无法获取焦点问题已解决!

qq_37516758的博客 2936

Motoman机器人离线编程——Motocom32开发简介

欢迎关注Jungle的Motoman机器人离线编程专栏系列文章: Motoman机器人离线编程——Motocom32开发简介 Motoman机器人离线编程——Visio Studio环境配置 Motoman机器人离线编程——Motocom32库函数的使用与封装 Motoman机器人离线编程——Motocom32参考文献及注意事项 Motoman机器人离线编程——MotocomES介绍 ...

冯Jungle的个人博客 8713

大数据利器Hadoop:从基础到实战,一篇文章掌握大数据处理精髓!

在当今大数据时代,数据量的爆炸式增长对企业和技术提出了前所未有的挑战。如何高效地存储、处理和分析这些庞大的数据集,成为了亟待解决的问题。Hadoop作为一种分布式计算框架,应运而生,为大数据处理提供了有效的解决方案。Hadoop是一个由Apache软件基金会维护的开源项目,它基于Google的分布式文件系统(Google File System,GFS)和MapReduce计算模型设计。Hadoop的主要目标是处理大规模数据集,它可以在普通硬件集群上运行,从而降低了大数据处理的成本。

邓邓子的博客 8275

Unity游戏开发集成Ulid:高性能分布式ID生成方案实践指南

在分布式系统和游戏开发中,唯一标识符(ID)的生成与管理是基础且关键的技术环节。传统方案如自增ID存在单点瓶颈,而GUID虽全局唯一但无序,导致数据库索引碎片化与查询性能下降。Ulid(通用唯一字典序可排序标识符)通过结合时间戳的排序性与随机数的唯一性,提供了去中心化、高性能的解决方案。其技术价值在于支持字典序排序、字符串紧凑且跨平台兼容,能显著优化数据查询与存储效率。在游戏开发场景中,Ulid尤其适用于处理实体标识、网络同步、存档合并等分布式数据一致性问题,有效解决ID冲突与排序难题。本文基于Unity引

weixin_33994429的博客 297

YOLOv8【第十一章:视频追踪与流处理篇·第16节】一文搞懂,DeepStream 部署入门:NVIDIA 视频分析 SDK 与 YOLOv8 结合!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。

**My Coding Family** 838

Unity引擎

简单介绍:Unity提供Manual(引擎说明书)和Scripting API(代码,类和方法可以在这里查)两个文档,开发人员可以阅读这两个文档来了解Unity开发Layout:窗口布局样式带Unity图标的表示场景。

Code Writers 863

YOLOv11改进策略【卷积层】| ICCV-2023 引入Dynamic Snake Convolution动态蛇形卷积,改进C3k2

基于拓扑几何约束的动态蛇卷积用于管状结构分割DSConv(Dynamic Snake Convolution,动态蛇形卷积)模块的设计主要是为了更好地处理管状结构的分割任务,解决传统卷积在处理细管状结构时的不足。

Limiiiing的博客 1627

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

万字详解MCP:从理论到实战,全网最全讲解,小白也能看懂!

这篇万字长文,我们将全面拆解 MCP 从概念、原理到底层实现,一步步带你理解它为什么是多智能体协作、RAG 系统、任务型对话、智能体编排等场景的「通用语言」和「执行中枢」

2401_85325726的博客 4086

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

全网第一,Unity编辑器拓展,修改URP管线设置

Unity编辑器拓展,修改URP管线设置

qq_42541751的博客 2054

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

30岁unity游戏开发程序员,失业快两个月了,目前很迷茫不知道何去何从

总看到这个。其实,30岁失业的程序员究竟有多少,当你知道数据的时候,你就会泪哭。。。很显然,现在游戏开发以及传统的程序员已经不吃香了,这不现在刚看到一知友同样31岁,2017年毕业就Unity开发到现在,不经不觉8年了,一直从事于行业,后来公司裁员,到后面无奈去了,去做上位机开发了如今每天就是根据流水设备与现场、数据库进行逻辑上的控制与数据交换工作,每天很累上班10个小时才能拿到全薪,而且没有一金以及上班6天,偶然出差要1~2个月,驻扎在现场,害,感觉就很累,几乎没有个人时间。

2401_85116933的博客 1495

Unity | 用Azure实现文字转语音(详细讲解版)

Unity | 用Azure实现文字转语音(详细讲解版)

菌菌兹的博客 2194

Unity3DTraining 之 Unit4Unity:基于 Editor Test Runner 的 Unity 单元测试实战指南

单元测试是保证游戏核心逻辑(血量、伤害、背包、AI 状态机等)稳定可靠的重要手段,但在 Unity 开发中常因生命周期、协程、物理等机制而被人忽视。本指南以仓库 `Unit4Unity` 目录下的实战案例为主线,完整演示如何在 Unity 中借助内置的 **Editor Test Runner** 编写并运行基于 NUnit 的单元测试:从被测类 `Player` 的设计、`[Test]` 测试方

gitblog_00381的博客 201

全网最新Unity-EasyAR环境配置心得(2021.11)

全网最新Unity-Vuforia(高通)环境配置心得及使用教程(2021.10)https://mp.youkuaiyun.com/mp_blog/creation/editor/120913711

PinaColadaONE的博客 5556
上一篇: Unity百游修炼(19)——粒子系统之魔法battle(详细全流程)
下一篇: Unity百游修炼(20)——见缝插针详细全流程(含完整项目及源代码)
Unity黑马王子
博客等级 码龄4年 1846粉丝 181原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值