Unity基础:C#脚本的创建与挂载------让你的游戏物体活起来

大家好,欢迎回到Unity教程系列。前面十五篇文章中,我们一直在学习场景搭建、组件操作、编辑器使用------这些都是"静态"的内容。从今天开始,我们要让游戏真正"动起来"了。而让游戏动起来的关键,就是编写C#脚本。
脚本是游戏逻辑的载体。角色的移动、敌人的AI、UI的交互、音效的播放------游戏中的一切动态行为,都来源于脚本。对于很多新手来说,第一次写脚本可能会感到紧张------别担心,这篇文章我会从最基础的操作开始,手把手带你进入Unity脚本编程的世界。
一、脚本在Unity中的角色
1.1 脚本是什么
💡 在Unity中,脚本(Script)是一种特殊的Component。当你创建一个C#脚本并将其挂载到GameObject上时,这个脚本就成为该GameObject的一个组件,拥有该组件的所有特性------可以在Inspector中查看、可以启用/禁用、可以通过GetComponent获取。
理解"脚本就是组件"这个观念非常重要。它意味着:
- 脚本继承了MonoBehaviour(而MonoBehaviour继承了Component)
- 脚本像其他组件一样,有生命周期(创建、启用、更新、禁用、销毁)
- 脚本可以通过GetComponent被其他组件访问
1.2 脚本与GameObject的关系
每个挂载了脚本的GameObject都是该脚本的一个实例。如果你将同一个脚本挂载到10个不同的GameObject上,运行时会创建10个脚本实例,每个实例有自己独立的变量值。
Player对象 ──── PlayerController脚本实例(moveSpeed=5)
Enemy_A对象 ── EnemyController脚本实例(moveSpeed=3)
Enemy_B对象 ── EnemyController脚本实例(moveSpeed=4)
二、创建脚本
2.1 在Unity中创建C#脚本
创建脚本有多种方式:
方式一:Project窗口右键
① 在Project窗口中进入Scripts文件夹
② 右键 → Create → C# Script
③ 新脚本以蓝色图标出现,名称处于可编辑状态
④ 输入脚本名称(如"MyFirstScript"),按回车确认
方式二:Assets菜单
Assets → Create → C# Script
方式三:GameObject的Add Component菜单
① 选中GameObject
② Inspector底部 → Add Component
③ 输入"New Script"
④ 点击"New Script",输入脚本名称
💡 第三种方式会同时在Project窗口的Assets根目录创建脚本,并自动将其挂载到选中的GameObject上。
2.2 脚本命名规范
⚠️ C#脚本的命名有几个重要规则:
文件名与类名必须一致:创建的脚本文件名为"MyFirstScript.cs",那么内部的类名也必须是"MyFirstScript"。如果你手动修改了文件名,必须同时修改类名,否则Unity会报编译错误。
使用PascalCase命名法 :每个单词首字母大写,如 PlayerController、EnemyAI、WeaponManager。
不使用中文和特殊字符:脚本名只能使用英文、数字和下划线,且不能以数字开头。
名称要有意义 :避免 Script1、Test2 这样的无意义命名。好的命名能让人一眼看出脚本的用途。
2.3 打开和编辑脚本
双击脚本文件 :在Project窗口中双击C#脚本文件,Unity会在你配置的代码编辑器中打开它。默认是Visual Studio,你也可以在 Edit > Preferences > External Tools 中更换为Rider或VS Code。
在Inspector中打开:如果脚本已经挂载到GameObject上,选中该物体,在Inspector中点击脚本组件名称右侧的齿轮图标或直接点击脚本名称行,都可以跳转到脚本文件。
三、脚本的基本结构
3.1 默认生成的代码模板
创建新脚本后,Unity会自动生成以下模板代码:
csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyFirstScript : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
让我们逐段理解这个模板:
3.2 using语句
csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using 语句相当于"导入命名空间",让你可以使用其他代码库中的类:
UnityEngine:Unity引擎的核心命名空间,包含MonoBehaviour、GameObject、Transform、Debug等所有Unity基础类。几乎每个脚本都需要它。System.Collections和System.Collections.Generic:C#标准库的集合类型命名空间,提供List、Dictionary、Queue等数据结构。
3.3 类定义
csharp
public class MyFirstScript : MonoBehaviour
这行代码定义了一个名为MyFirstScript的类,它继承自MonoBehaviour。
public:访问修饰符,表示这个类可以被其他代码访问。
class MyFirstScript:定义类名。
: MonoBehaviour:继承关系。MonoBehaviour是Unity中所有脚本组件的基类。继承MonoBehaviour后,你的类才能被挂载到GameObject上,才能使用Start、Update等生命周期方法。
3.4 Start方法
csharp
void Start()
{
// 初始化代码
}
Start是Unity生命周期中的一个方法,在脚本实例被启用后、第一次Update之前调用。它只被调用一次,适合用于初始化------获取组件引用、设置初始变量值、建立初始状态等。
3.5 Update方法
csharp
void Update()
{
// 每帧执行的代码
}
Update在游戏的每一帧都会被调用。它是游戏逻辑的核心方法------输入检测、移动更新、状态检查等需要持续执行的逻辑都放在Update中。
四、将脚本挂载到GameObject
4.1 挂载的方式
方式一:拖拽(最常用)
将脚本从Project窗口拖到Hierarchy中的GameObject上,或拖到Scene视图中的物体上。
方式二:拖到Inspector
选中GameObject,将脚本从Project窗口拖到Inspector的空白区域(组件列表下方)。
方式三:Add Component按钮
① 选中GameObject
② Inspector底部 → Add Component
③ 搜索脚本名称
④ 点击添加
方式四:代码中动态添加
csharp
gameObject.AddComponent<MyFirstScript>();
4.2 验证脚本挂载成功
脚本成功挂载到GameObject后,在Inspector中可以看到脚本作为一个组件出现:
- 组件名称显示为脚本的类名
- 脚本名称左侧有启用/禁用复选框
- public变量显示在Inspector中,可直接编辑
- 如果有SerializeField标记的private变量也会显示
4.3 一个物体可以有多个脚本
同一个GameObject上可以挂载任意数量的脚本组件,每个脚本独立运作:
Player
├── Transform
├── MeshRenderer
├── Rigidbody
├── PlayerController(处理输入和移动)
├── HealthComponent(管理生命值)
├── WeaponComponent(管理武器)
└── AnimationController(管理动画)
五、编写第一个脚本------让物体旋转
5.1 创建Rotator脚本
让我们写一个最简单的脚本:让物体不停地旋转。
csharp
using UnityEngine;
public class Rotator : MonoBehaviour
{
// 旋转速度,可在Inspector中调整
public float rotationSpeed = 30f;
void Update()
{
// 每帧绕Y轴旋转
// Time.deltaTime使旋转速度与帧率无关
transform.Rotate(Vector3.up * rotationSpeed * Time.deltaTime);
}
}
5.2 代码解析
public float rotationSpeed = 30f:
public关键字让这个变量在Inspector中可见可编辑float是浮点数类型rotationSpeed是变量名= 30f设置默认值
transform.Rotate(...):
transform是当前脚本所在GameObject的Transform组件引用Rotate是Transform的一个方法,让物体旋转Vector3.up是Y轴正方向的简写(即(0,1,0))* rotationSpeed乘以旋转速度* Time.deltaTime乘以帧间隔时间,使旋转速度与帧率无关
5.3 测试脚本
① 创建一个Cube
② 将Rotator脚本拖拽到Cube上
③ 点击Play按钮
④ 观察Cube在Scene视图和Game视图中旋转
⑤ 在Inspector中修改rotationSpeed的值(如改为100),观察旋转速度变化
⑥ 把Vector3.up改为new Vector3(1,1,0),观察旋转方向的变化
✅ 恭喜!你刚刚完成了第一个Unity脚本,让一个物体在你的代码控制下动了起来!
六、让物体动起来------移动脚本
6.1 键盘控制移动
csharp
using UnityEngine;
public class SimpleMover : MonoBehaviour
{
public float moveSpeed = 5f;
void Update()
{
// 获取水平输入(A/D键或左右方向键)
float horizontal = Input.GetAxis("Horizontal");
// 获取垂直输入(W/S键或上下方向键)
float vertical = Input.GetAxis("Vertical");
// 构建移动方向向量
Vector3 movement = new Vector3(horizontal, 0, vertical);
// 移动物体
transform.Translate(movement * moveSpeed * Time.deltaTime);
}
}
6.2 Input.GetAxis解析
Input.GetAxis("Horizontal") 从Unity的输入管理器获取"Horizontal"轴的值。这个值在-1到1之间平滑过渡:
- 按A键或左方向键:值逐渐变化到-1
- 按D键或右方向键:值逐渐变化到1
- 不按键:值逐渐回到0
"Vertical"轴对应W/S或上下方向键。
平滑过渡意味着物体不会"突然启动突然停止",而是有一个自然的加速和减速过程------这就是你在游戏中感受到的"手感"。
6.3 跳跃功能
csharp
using UnityEngine;
public class PlayerMover : MonoBehaviour
{
public float moveSpeed = 5f;
public float jumpForce = 10f;
private Rigidbody rb;
void Start()
{
// 获取Rigidbody组件引用
rb = GetComponent<Rigidbody>();
}
void Update()
{
// 水平移动
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(horizontal, 0, vertical);
transform.Translate(movement * moveSpeed * Time.deltaTime);
// 跳跃
if (Input.GetKeyDown(KeyCode.Space))
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
}
}
⚠️ 使用Rigidbody实现跳跃,需要在GameObject上添加Rigidbody组件。另外,上述代码没有做"在地面上才能跳"的判断------玩家可以无限在空中跳。后续章节会添加地面检测来完善。
七、脚本的调试
7.1 使用Debug.Log输出信息
当你的脚本行为不符合预期时,第一件事就是在关键位置添加Debug.Log输出:
csharp
void Start()
{
Debug.Log("脚本已启动!");
Debug.Log($"物体的初始位置是:{transform.position}");
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("玩家按下了空格键!");
}
}
输出的信息会显示在Console窗口中。不同级别的日志:
Debug.Log("信息"):普通白色消息Debug.LogWarning("警告"):黄色警告消息Debug.LogError("错误"):红色错误消息
7.2 常见编译错误
"; expected":某行代码末尾缺少分号。
"does not contain a definition":调用了不存在的方法或访问了不存在的属性。可能是拼写错误或缺少using。
"cannot convert":类型不匹配。例如把string赋值给int。
"The name 'xxx' does not exist":使用了未定义的变量。
遇到错误时,双击Console窗口中的错误消息,Unity会直接打开对应的脚本并跳转到出错行号。
八、脚本文件管理
8.1 推荐的文件组织
Assets\Scripts\
├── Player\
│ ├── PlayerController.cs
│ ├── PlayerHealth.cs
│ └── PlayerInput.cs
├── Enemies\
│ ├── EnemyAI.cs
│ └── EnemyPatrol.cs
├── Managers\
│ ├── GameManager.cs
│ └── AudioManager.cs
├── UI\
│ ├── MainMenu.cs
│ └── HealthBar.cs
└── Utils\
├── ObjectPool.cs
└── Singleton.cs
8.2 脚本重命名
⚠️ 重命名脚本要小心!在Project窗口中重命名脚本文件后,必须打开脚本文件同步修改类名。文件名和类名必须一致。
如果只修改了文件名而没改类名,Unity会显示错误:"Script class does not match file name"。
九、本篇总结
✅ 本文核心知识点回顾:
- C#脚本在Unity中是一种特殊的Component,继承自MonoBehaviour
- 脚本文件名必须与内部的类名一致
- Start()在脚本启用后调用一次(初始化),Update()每帧调用(持续逻辑)
- public变量自动显示在Inspector中,可实时编辑
- transform是脚本所在GameObject的Transform组件的快捷引用
- Input.GetAxis()获取平滑的输入值,Input.GetKeyDown()检测单次按键
- Time.deltaTime让移动速度与帧率无关
- Debug.Log()在Console窗口输出调试信息
📝 下一篇文章将深入讲解Start与Update方法------Unity脚本生命周期的初步探索。
动手任务 :
① 创建一个Rotator脚本让物体自转
② 创建一个Mover脚本用键盘控制物体移动
③ 修改rotationSpeed和moveSpeed观察Inspector变量控制的效果
④ 使用Debug.Log输出物体的实时位置
关于作者:拥有多年Unity开发经验的游戏开发者,专注Unity系统化教学与最佳实践分享。