二、C# 脚本编程


文档摘要

二、C# 脚本编程 二、C# 脚本编程 在 Unity3D 的世界中,C# 脚本编程是游戏逻辑实现的基石。掌握 C# 脚本,您将能够赋予游戏对象生命,控制游戏行为,并与 Unity 引擎进行深度交互。本章节将引导您从零开始,逐步深入 C# 脚本编程的核心概念和实践技巧,让您能够自如地运用 C# 脚本构建各种精彩的游戏功能。 2.1 C# 脚本基础 在深入 Unity 之前,我们需要先了解一些 C# 编程的基础知识。如果您已经熟悉 C# 或其他编程语言,可以快速回顾本节内容。 2.1.1 变量与数据类型 变量是存储数据的容器,而数据类型则定义了变量可以存储的数据种类。C# 是一种强类型语言,这意味着您在声明变量时必须指定其数据类型。

二、C# 脚本编程

二、C# 脚本编程

在 Unity3D 的世界中,C# 脚本编程是游戏逻辑实现的基石。掌握 C# 脚本,您将能够赋予游戏对象生命,控制游戏行为,并与 Unity 引擎进行深度交互。本章节将引导您从零开始,逐步深入 C# 脚本编程的核心概念和实践技巧,让您能够自如地运用 C# 脚本构建各种精彩的游戏功能。

2.1 C# 脚本基础

在深入 Unity 之前,我们需要先了解一些 C# 编程的基础知识。如果您已经熟悉 C# 或其他编程语言,可以快速回顾本节内容。

2.1.1 变量与数据类型

变量是存储数据的容器,而数据类型则定义了变量可以存储的数据种类。C# 是一种强类型语言,这意味着您在声明变量时必须指定其数据类型。

  • 常见数据类型:

    • int: 整型,用于存储整数,例如:int score = 100;

    • float: 浮点型,用于存储单精度浮点数(小数),例如:float speed = 3.5f; (注意 f 后缀)

    • double: 双精度浮点型,用于存储更高精度的浮点数,例如:double pi = 3.1415926;

    • bool: 布尔型,用于存储真或假的值,例如:bool isGameOver = false;

    • string: 字符串型,用于存储文本,例如:string playerName = "Player1";

    • char: 字符型,用于存储单个字符,例如:char initial = 'J';

    • GameObject: Unity 特有的数据类型,代表游戏场景中的游戏对象。例如:GameObject playerObject;

    • Transform: Unity 特有的数据类型,代表游戏对象的变换组件(位置、旋转、缩放)。例如:Transform playerTransform;

    • Vector3: Unity 特有的数据类型,代表三维向量(x, y, z)。例如:Vector3 direction = new Vector3(1, 0, 0);

    • Quaternion: Unity 特有的数据类型,代表旋转。例如:Quaternion rotation = Quaternion.identity;

  • 变量声明与初始化:

    数据类型 变量名; // 声明变量 数据类型 变量名 = 初始值; // 声明并初始化变量

    例如:

    int enemyCount; // 声明整型变量 enemyCount float jumpForce = 5.0f; // 声明并初始化浮点型变量 jumpForce string message = "Hello Unity!"; // 声明并初始化字符串变量 message

2.1.2 运算符

运算符用于对变量和值执行操作。C# 提供了丰富的运算符,包括:

  • 算术运算符: + (加), - (减), * (乘), / (除), % (取余)

  • 赋值运算符: = (赋值), +=, -=, *=, /=, %= (复合赋值)

  • 比较运算符: == (等于), != (不等于), > (大于), < (小于), >= (大于等于), <= (小于等于)

  • 逻辑运算符: && (逻辑与), || (逻辑或), ! (逻辑非)

  • 递增/递减运算符: ++ (递增), -- (递减)

代码示例:

int a = 10; int b = 5; int sum = a + b; // 加法 int difference = a - b; // 减法 int product = a * b; // 乘法 float quotient = (float)a / b; // 除法 (需要强制类型转换为 float 才能得到浮点结果) int remainder = a % b; // 取余 bool isEqual = (a == b); // 等于比较 bool isGreater = (a > b); // 大于比较 a++; // 递增 a (a 变为 11) b--; // 递减 b (b 变为 4)

2.1.3 控制流语句

控制流语句允许您根据条件或循环执行不同的代码块,或者重复执行代码。

  • 条件语句:

    • if 语句: 根据条件判断是否执行代码块。

      if (condition) { // 条件为真时执行的代码 }
    • if...else 语句: 条件为真时执行一个代码块,否则执行另一个代码块。

      if (condition) { // 条件为真时执行的代码 } else { // 条件为假时执行的代码 }
    • if...else if...else 语句: 多条件判断。

      if (condition1) { // 条件 1 为真时执行的代码 } else if (condition2) { // 条件 2 为真时执行的代码 } else { // 所有条件都不为真时执行的代码 }
    • switch 语句: 根据变量的值选择执行不同的代码块。

      switch (variable) { case value1: // variable 等于 value1 时执行的代码 break; case value2: // variable 等于 value2 时执行的代码 break; default: // variable 不等于任何 case 值时执行的代码 break; }
  • 循环语句:

    • for 循环: 在指定次数内重复执行代码块。

      for (初始化; 条件; 迭代器) { // 循环体代码 }
    • while 循环: 只要条件为真就重复执行代码块。

      while (condition) { // 循环体代码 }
    • do...while 循环: 先执行一次代码块,然后只要条件为真就重复执行。

      do { // 循环体代码 } while (condition);
    • foreach 循环: 遍历集合(例如数组或列表)中的元素。

      foreach (数据类型 元素变量名 in 集合) { // 针对每个元素执行的代码 }

代码示例 (控制流):

int health = 50; if (health <= 0) { Debug.Log("Game Over!"); // 使用 Debug.Log 在 Unity 控制台输出信息 } else if (health <= 20) { Debug.Log("Health is low!"); } else { Debug.Log("Health is normal."); } for (int i = 0; i < 10; i++) { Debug.Log("计数器: " + i); } int count = 0; while (count < 5) { Debug.Log("While 循环计数: " + count); count++; }

2.1.4 函数 (方法)

函数是一段组织好的、可重复使用的代码块,用于执行特定的任务。函数可以提高代码的模块化和可重用性。

  • 函数定义:

    访问修饰符 返回类型 函数名(参数列表) { // 函数体代码 return 返回值; // 如果有返回值 }
    • 访问修饰符: 例如 public, private, protected (控制函数的访问权限,稍后Unity部分会详细介绍)。

    • 返回类型: 函数执行后返回的数据类型。如果函数不返回任何值,则使用 void

    • 函数名: 函数的标识符,遵循命名规范。

    • 参数列表: 函数接受的输入参数,可以为空或包含多个参数,每个参数由数据类型和参数名组成。

    • 函数体: 包含函数执行的代码。

    • 返回值: 使用 return 语句返回函数的结果 (如果返回类型不是 void)。

  • 函数调用:

    函数名(参数值列表);

代码示例 (函数):

public class ExampleScript : MonoBehaviour // MonoBehaviour 是 Unity 脚本的基础类 { public int AddNumbers(int num1, int num2) // 定义一个公有函数 AddNumbers { int sum = num1 + num2; return sum; // 返回两数之和 } void Start() // Unity 生命周期函数,在脚本启动时执行一次 { int result = AddNumbers(5, 3); // 调用 AddNumbers 函数 Debug.Log("5 + 3 = " + result); // 输出结果 } }

2.2 Unity 中的 C# 脚本

现在,我们将重点转向 Unity 中 C# 脚本的应用。Unity 使用 C# 作为主要的脚本语言,用于控制游戏逻辑、交互和行为。

2.2.1 MonoBehaviour 类

在 Unity 中创建的 C# 脚本通常会继承自 MonoBehaviour 类。MonoBehaviour 是 Unity 脚本的基础类,它提供了许多内置的生命周期函数和属性,允许您与 Unity 引擎进行交互。

  • 创建 C# 脚本:

    1. 在 Unity 项目窗口中,选择 "Assets" 文件夹 (或其他您希望存放脚本的文件夹)。

    2. 右键单击,选择 "Create" -> "C# Script"。

    3. 输入脚本名称 (例如 "PlayerController"),按 Enter 键。

    4. 双击脚本文件,Unity 将使用默认的代码编辑器 (通常是 Visual Studio 或 Visual Studio Code) 打开脚本。

  • 默认脚本结构:

    新创建的 C# 脚本通常包含以下结构:

    using UnityEngine; // 引入 UnityEngine 命名空间,包含 Unity 核心 API public class 脚本名称 : MonoBehaviour // 继承自 MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } }
  • 常用的 MonoBehaviour 生命周期函数:

    • Awake(): 在脚本实例被创建后立即调用,无论脚本是否启用。用于初始化变量和设置初始状态。Start() 之前执行,且只执行一次。

    • OnEnable(): 在脚本启用时调用(例如,当 GameObject 被激活或脚本组件被启用时)。可能多次执行,每次脚本被启用时都会调用。

    • Start(): 在脚本第一次启用时在 Update() 函数之前调用。只执行一次,用于初始化需要在游戏开始时设置的内容。

    • FixedUpdate(): 以固定的时间间隔调用,独立于帧率。用于物理相关的更新,例如刚体运动。

    • Update(): 每帧调用一次。用于大多数游戏逻辑的更新,例如玩家输入、游戏状态检查等。

    • LateUpdate():Update() 之后每帧调用一次。用于相机跟随、后期处理等需要在所有 Update() 完成后执行的操作。

    • OnGUI(): 用于绘制 GUI 元素(不推荐用于现代 Unity UI 系统)。每帧多次调用,用于旧版 GUI 系统。

    • OnDisable(): 在脚本禁用时调用(例如,当 GameObject 被禁用或脚本组件被禁用时)。可能多次执行,每次脚本被禁用时都会调用。

    • OnDestroy(): 在脚本实例被销毁之前调用。只执行一次,用于清理资源。

2.2.2 GameObject 和 Component

Unity 场景由 GameObject (游戏对象) 组成,每个 GameObject 都可以附加不同的 Component (组件) 来实现不同的功能。C# 脚本本身也是一种 Component。

  • GameObject: 场景中的基本实体,可以理解为游戏中的 "物体"。

  • Component: 附加到 GameObject 上的模块,提供特定的功能,例如:

    • Transform: 控制 GameObject 的位置、旋转和缩放。

    • Renderer: 控制 GameObject 的渲染外观(例如 Mesh Renderer, Sprite Renderer)。

    • Collider: 定义 GameObject 的碰撞形状,用于物理碰撞检测。

    • Rigidbody: 使 GameObject 具有物理属性,可以受力、重力影响。

    • AudioSource: 播放声音。

    • Light: 提供光源。

    • Camera: 定义场景的视角。

    • UI 组件: 例如 Button, Text, Image (用于构建用户界面)。

    • 自定义 C# 脚本: 您自己编写的 C# 脚本也是 Component。

  • 获取和操作 Component:

    您可以使用 GetComponent<>() 方法从 GameObject 中获取其他 Component,并在脚本中操作它们。

    public class AccessComponents : MonoBehaviour { void Start() { // 获取当前 GameObject 的 Transform 组件 Transform transformComponent = GetComponent<Transform>(); if (transformComponent != null) // 确保组件存在 { // 修改 Transform 组件的位置 transformComponent.position = new Vector3(1, 2, 3); } else { Debug.LogError("Transform component not found!"); } // 获取当前 GameObject 的 Renderer 组件 (假设存在) Renderer rendererComponent = GetComponent<Renderer>(); if (rendererComponent != null) { // 禁用 Renderer 组件,使物体不可见 rendererComponent.enabled = false; } } }
  • 公共变量 (Public Variables) 和 Inspector 面板:

    在 MonoBehaviour 脚本中声明为 public 的变量,会在 Unity 编辑器的 Inspector 面板中显示出来。您可以在 Inspector 面板中直接修改这些变量的值,而无需重新编译脚本。这使得在编辑器中调整游戏参数非常方便。

    public class PublicVariablesExample : MonoBehaviour { public float moveSpeed = 5f; // 公共浮点型变量 moveSpeed public GameObject targetObject; // 公共 GameObject 变量 targetObject public bool isJumping = false; // 公共布尔型变量 isJumping }

    将此脚本附加到 GameObject 后,您将在 Inspector 面板中看到 Move Speed, Target Object, 和 Is Jumping 这些可编辑的属性。

2.2.3 常用 Unity API

Unity 提供了丰富的 API (应用程序编程接口) 供 C# 脚本使用,用于控制游戏逻辑、访问引擎功能等。以下是一些常用的 API:

  • Transform API: 用于操作 GameObject 的变换 (位置、旋转、缩放)。

    • transform.position: 获取或设置 GameObject 的世界坐标位置 (Vector3)。

    • transform.localPosition: 获取或设置 GameObject 相对于父物体的局部坐标位置 (Vector3)。

    • transform.rotation: 获取或设置 GameObject 的世界坐标旋转 (Quaternion)。

    • transform.localRotation: 获取或设置 GameObject 相对于父物体的局部坐标旋转 (Quaternion)。

    • transform.localScale: 获取或设置 GameObject 的局部缩放 (Vector3)。

    • transform.Translate(Vector3 translation): 按指定向量移动 GameObject。

    • transform.Rotate(Vector3 eulerAngles): 按指定欧拉角旋转 GameObject。

    • transform.LookAt(Transform target): 使 GameObject 面向目标物体。

  • Input API: 用于获取用户输入 (键盘、鼠标、触摸等)。

    • Input.GetKey(KeyCode key): 检测按键是否被按下 (持续按下)。

    • Input.GetKeyDown(KeyCode key): 检测按键是否被按下 (按下瞬间)。

    • Input.GetKeyUp(KeyCode key): 检测按键是否被释放 (释放瞬间)。

    • Input.GetAxis(string axisName): 获取轴输入值 (例如 "Horizontal" 水平轴, "Vertical" 垂直轴,通常用于方向键或摇杆)。

    • Input.GetMouseButton(int button): 检测鼠标按键是否被按下 (0: 左键, 1: 右键, 2: 中键)。

    • Input.GetMouseButtonDown(int button): 检测鼠标按键是否被按下 (按下瞬间)。

    • Input.GetMouseButtonUp(int button): 检测鼠标按键是否被释放 (释放瞬间)。

    • Input.mousePosition: 获取鼠标屏幕坐标 (Vector3)。

  • Debug API: 用于在 Unity 控制台输出调试信息。

    • Debug.Log(object message): 输出普通信息。

    • Debug.LogWarning(object message): 输出警告信息。

    • Debug.LogError(object message): 输出错误信息。

  • Time API: 用于获取时间相关信息。

    • Time.deltaTime: 上一帧到当前帧所用的时间 (以秒为单位)。非常重要,用于实现帧率独立性,使游戏在不同帧率下运行速度一致。

    • Time.time: 游戏开始运行到现在的时间 (以秒为单位)。

    • Time.fixedDeltaTime: FixedUpdate() 函数调用的固定时间间隔。

2.3 C# 脚本实践

为了更好地理解 C# 脚本在 Unity 中的应用,我们进行一些代码实践。

2.3.1 简单的移动脚本

创建一个名为 "MoveObject" 的 C# 脚本,并将其附加到一个 3D 对象 (例如 Cube)。

using UnityEngine; public class MoveObject : MonoBehaviour { public float moveSpeed = 5f; // 可在 Inspector 面板调整移动速度 void Update() { // 获取水平方向输入 (A, D 键或左右方向键) float horizontalInput = Input.GetAxis("Horizontal"); // 获取垂直方向输入 (W, S 键或上下方向键) float verticalInput = Input.GetAxis("Vertical"); // 创建移动方向向量 Vector3 moveDirection = new Vector3(horizontalInput, 0, verticalInput); // 归一化向量,使对角线移动速度与其他方向一致 moveDirection.Normalize(); // 使用 Translate 方法移动物体,并乘以 Time.deltaTime 实现帧率独立性 transform.Translate(moveDirection * moveSpeed * Time.deltaTime); } }

代码详解:

  1. public float moveSpeed = 5f;: 声明一个公共的浮点型变量 moveSpeed,用于控制移动速度,并在 Inspector 面板中显示。

  2. Input.GetAxis("Horizontal")Input.GetAxis("Vertical"): 获取 Unity 默认配置的水平和垂直轴输入值。这些轴默认绑定到 A/D, 左右方向键 和 W/S, 上下方向键。返回值范围为 -1 到 1,表示输入强度和方向。

  3. Vector3 moveDirection = new Vector3(horizontalInput, 0, verticalInput);: 根据水平和垂直输入值创建一个三维向量 moveDirection,表示移动方向。Y 轴设置为 0,表示只在水平面移动。

  4. moveDirection.Normalize();: 将 moveDirection 向量归一化。归一化后的向量长度为 1,方向不变。这样做是为了确保对角线移动速度与水平或垂直移动速度一致。如果不归一化,对角线移动速度会更快。

  5. transform.Translate(moveDirection * moveSpeed * Time.deltaTime);: 使用 transform.Translate() 方法移动物体。

    • moveDirection * moveSpeed: 计算每秒移动的距离向量。

    • * Time.deltaTime: 将每秒移动距离转换为每帧移动距离,实现帧率独立性。

实践步骤:

  1. 在 Unity 中创建一个 3D Cube 对象。

  2. 创建一个新的 C# 脚本 "MoveObject",并将上述代码复制粘贴到脚本中。

  3. 将 "MoveObject" 脚本拖拽到 Cube 对象上,使其成为 Cube 的一个组件。

  4. 运行游戏。您可以使用 A/D, W/S 或方向键控制 Cube 在水平面移动。

  5. 在 Inspector 面板中调整 Cube 的 "Move Speed" 属性,观察移动速度的变化。

2.3.2 简单的交互脚本 (点击物体)

创建一个名为 "ClickObject" 的 C# 脚本,并将其附加到一个 3D 对象 (例如 Sphere)。

using UnityEngine; public class ClickObject : MonoBehaviour { void Update() { // 检测鼠标左键是否被按下 if (Input.GetMouseButtonDown(0)) { // 从鼠标位置发射射线 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo; // 进行射线检测 if (Physics.Raycast(ray, out hitInfo)) { // 检测到碰撞物体 if (hitInfo.collider.gameObject == gameObject) // 判断碰撞物体是否是当前物体 { Debug.Log("Clicked on: " + gameObject.name); // 输出点击信息到控制台 // 在这里添加点击后的逻辑,例如改变颜色、播放声音等 GetComponent<Renderer>().material.color = Color.red; // 改变物体颜色为红色 } } } } }

代码详解:

  1. Input.GetMouseButtonDown(0): 检测鼠标左键是否被按下 (按下瞬间)。

  2. Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);: 从主摄像机 (Camera.main) 的屏幕位置 (鼠标位置) 发射一条射线。射线用于模拟鼠标点击在 3D 空间中的方向。

  3. Physics.Raycast(ray, out hitInfo): 进行射线检测。

    • ray: 要检测的射线。

    • out hitInfo: 输出参数,存储射线碰撞到的物体信息 (如果碰撞到)。

    • Physics.Raycast() 返回 true 如果射线碰撞到物体,否则返回 false

  4. if (hitInfo.collider.gameObject == gameObject): 判断射线碰撞到的 GameObject 是否是当前脚本所附加的 GameObject。

  5. GetComponent<Renderer>().material.color = Color.red;: 获取当前 GameObject 的 Renderer 组件,并将其材质的颜色设置为红色,实现点击物体后改变颜色的效果。

实践步骤:

  1. 在 Unity 中创建一个 3D Sphere 对象。

  2. 创建一个新的 C# 脚本 "ClickObject",并将上述代码复制粘贴到脚本中。

  3. 将 "ClickObject" 脚本拖拽到 Sphere 对象上。

  4. 确保场景中有一个 Camera 对象 (通常默认场景中已经存在 Main Camera)。

  5. 运行游戏。点击 Sphere 对象,Sphere 的颜色会变为红色,并在控制台输出点击信息。

2.4 高级主题 (简要介绍)

C# 脚本编程在 Unity 中还有许多高级主题,例如:

  • 协程 (Coroutines): 用于实现异步操作,例如延迟执行、动画序列等,避免阻塞主线程。

  • 事件 (Events) 和委托 (Delegates): 用于实现对象之间的松耦合通信,例如 UI 事件处理、自定义事件触发等。

  • 命名空间 (Namespaces): 用于组织代码,避免命名冲突,提高代码可维护性。

  • 面向对象编程 (OOP) 原则: 例如封装、继承、多态,用于构建更复杂、可扩展的游戏系统。

  • 数据结构和算法: 例如列表、字典、排序、搜索等,用于高效地处理游戏数据。

  • 性能优化: 例如代码优化、资源管理、批处理等,提高游戏运行效率。

这些高级主题将在后续章节或更深入的学习中逐步探索。

2.5 最佳实践

  • 代码可读性: 编写清晰、易懂的代码,使用有意义的变量名、函数名,添加必要的注释。

  • 模块化编程: 将代码分解成小的、独立的函数或类,提高代码可重用性和可维护性。

  • 注释: 适当地添加注释,解释代码的功能和逻辑,方便自己和他人理解。

  • 错误处理: 考虑可能出现的错误情况,并进行适当的处理,例如使用 if 语句检查空引用、使用 try-catch 语句捕获异常。

  • 性能意识: 编写高效的代码,避免不必要的计算和资源消耗,尤其是在 Update() 等每帧调用的函数中。

  • 版本控制: 使用版本控制系统 (例如 Git) 管理代码,方便代码协作和版本回溯。

总结

本章节详细介绍了 Unity3D 中的 C# 脚本编程基础,包括 C# 基础语法、Unity 特性结合、代码实践和一些最佳实践建议。通过学习本章节,您应该已经掌握了 C# 脚本编程的入门知识,并能够编写简单的 Unity 脚本来实现基本的游戏功能。

C# 脚本编程是 Unity 游戏开发的强大工具,不断学习和实践将使您能够构建更复杂、更精彩的游戏世界。希望本章节能为您打开 Unity 游戏开发的大门!


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U