Unity是全球最流行的游戏引擎之一,而C#是Unity开发的主要编程语言。掌握C#脚本编写是成为Unity开发者的关键一步。本文将全面介绍Unity中C#脚本编写的核心概念、最佳实践和高级技巧,帮助你高效地开发游戏逻辑和交互系统。
1. Unity脚本基础
1.1 创建第一个脚本
- 在Unity项目中右键点击Assets窗口
- 选择Create > C# Script
- 命名为”PlayerController”
- 双击脚本在Visual Studio或其他编辑器中打开
using UnityEngine;
public class PlayerController : MonoBehaviour
{
// 在Inspector中显示的公共变量
public float moveSpeed = 5f;
public float jumpForce = 10f;
private Rigidbody rb;
private bool isGrounded;
// 初始化
void Start()
{
rb = GetComponent<Rigidbody>();
}
// 每帧调用
void Update()
{
float moveInput = Input.GetAxis("Horizontal");
rb.velocity = new Vector3(moveInput * moveSpeed, rb.velocity.y, 0);
if (Input.GetButtonDown("Jump") && isGrounded)
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
isGrounded = false;
}
}
// 碰撞检测
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = true;
}
}
}
1.2 脚本生命周期
Unity脚本遵循特定的执行顺序:
- Awake():脚本实例被创建时调用(即使脚本未启用)
- OnEnable():对象启用时调用
- Start():第一次Update前调用
- FixedUpdate():固定时间间隔调用(物理更新)
- Update():每帧调用
- LateUpdate():所有Update执行后调用
- OnDisable():对象禁用时调用
- OnDestroy():对象销毁前调用
2. 常用Unity API
2.1 游戏对象操作
// 创建新游戏对象
GameObject newObj = new GameObject("NewObject");
// 实例化预制体
public GameObject prefab;
Instantiate(prefab, transform.position, Quaternion.identity);
// 查找对象
GameObject player = GameObject.Find("Player");
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
// 访问和修改组件
Renderer rend = GetComponent<Renderer>();
rend.material.color = Color.red;
// 激活/禁用对象
gameObject.SetActive(false);
2.2 输入系统
// 键盘输入
float horizontal = Input.GetAxis("Horizontal"); // 平滑输入值[-1,1]
float rawInput = Input.GetAxisRaw("Horizontal"); // 离散值(-1,0,1)
// 鼠标输入
if (Input.GetMouseButtonDown(0)) // 左键点击
{
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
// 新输入系统(需要安装Input System包)
using UnityEngine.InputSystem;
public class PlayerInputExample : MonoBehaviour
{
private PlayerInputActions inputActions;
private void Awake()
{
inputActions = new PlayerInputActions();
inputActions.Player.Jump.performed += ctx => Jump();
}
private void OnEnable() => inputActions.Enable();
private void OnDisable() => inputActions.Disable();
private void Jump()
{
// 跳跃逻辑
}
}
2.3 物理系统
// 施加力
Rigidbody rb = GetComponent<Rigidbody>();
rb.AddForce(Vector3.forward * 10f, ForceMode.Impulse);
// 射线检测
if (Physics.Raycast(transform.position, transform.forward, out RaycastHit hit, 10f))
{
Debug.Log($"Hit: {hit.collider.name}");
}
// 2D物理
Rigidbody2D rb2D = GetComponent<Rigidbody2D>();
rb2D.AddForce(Vector2.up * 5f, ForceMode2D.Impulse);
// 碰撞检测
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Collectible"))
{
Destroy(other.gameObject);
}
}
3. 高级脚本技术
3.1 协程(Coroutines)
// 简单协程
IEnumerator FadeOut()
{
Renderer rend = GetComponent<Renderer>();
Color color = rend.material.color;
for (float alpha = 1f; alpha >= 0; alpha -= 0.1f)
{
color.a = alpha;
rend.material.color = color;
yield return new WaitForSeconds(0.1f);
}
gameObject.SetActive(false);
}
// 启动协程
StartCoroutine(FadeOut());
// 带参数的协程
IEnumerator MoveToPosition(Vector3 targetPos, float duration)
{
Vector3 startPos = transform.position;
float elapsed = 0f;
while (elapsed < duration)
{
transform.position = Vector3.Lerp(startPos, targetPos, elapsed/duration);
elapsed += Time.deltaTime;
yield return null;
}
transform.position = targetPos;
}
3.2 事件系统
// 自定义事件
public class GameEvents : MonoBehaviour
{
public static GameEvents current;
private void Awake()
{
current = this;
}
public event Action<int> onScoreUpdated;
public void ScoreUpdated(int score)
{
onScoreUpdated?.Invoke(score);
}
}
// 事件订阅
public class ScoreDisplay : MonoBehaviour
{
private Text scoreText;
private void OnEnable()
{
GameEvents.current.onScoreUpdated += UpdateScoreDisplay;
}
private void OnDisable()
{
GameEvents.current.onScoreUpdated -= UpdateScoreDisplay;
}
private void UpdateScoreDisplay(int score)
{
scoreText.text = $"Score: {score}";
}
}
3.3 脚本间通信
// 1. 直接引用
public class PlayerHealth : MonoBehaviour
{
public int health = 100;
public void TakeDamage(int damage)
{
health -= damage;
}
}
// 在其他脚本中
PlayerHealth playerHealth = FindObjectOfType<PlayerHealth>();
playerHealth.TakeDamage(10);
// 2. 使用SendMessage
gameObject.SendMessage("TakeDamage", 10, SendMessageOptions.DontRequireReceiver);
// 3. 单例模式
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
}
4. 编辑器扩展
4.1 自定义Inspector
using UnityEditor;
[CustomEditor(typeof(PlayerController))]
public class PlayerControllerEditor : Editor
{
public override void OnInspectorGUI()
{
PlayerController controller = (PlayerController)target;
// 默认属性
DrawDefaultInspector();
// 自定义按钮
if (GUILayout.Button("Reset Position"))
{
controller.transform.position = Vector3.zero;
}
// 条件显示
if (controller.moveSpeed > 10f)
{
EditorGUILayout.HelpBox("高速移动可能导致问题!", MessageType.Warning);
}
}
}
4.2 属性特性
// 常用属性特性
[Header("移动设置")]
[Range(0, 20)] public float moveSpeed = 5f;
[Tooltip("跳跃力度")] public float jumpForce = 10f;
[SerializeField] private int health; // 强制显示私有变量
[HideInInspector] public bool debugMode; // 隐藏公共变量
[Space(20)] // 添加空白
[TextArea(3, 5)] public string description;
4.3 自定义Gizmos
// 在场景视图中绘制辅助图形
private void OnDrawGizmos()
{
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(transform.position, 1f);
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, transform.position + transform.forward * 2f);
}
// 仅在选中时绘制
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireCube(transform.position, Vector3.one * 2f);
}
5. 性能优化
5.1 缓存组件引用
// 不好的做法:每帧获取组件
void Update()
{
GetComponent<Rigidbody>().AddForce(Vector3.up * 10f);
}
// 好的做法:缓存引用
private Rigidbody rb;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
rb.AddForce(Vector3.up * 10f);
}
5.2 对象池系统
public class ObjectPool : MonoBehaviour
{
public GameObject prefab;
public int poolSize = 10;
private Queue<GameObject> pool = new Queue<GameObject>();
private void Awake()
{
for (int i = 0; i < poolSize; i++)
{
GameObject obj = Instantiate(prefab);
obj.SetActive(false);
pool.Enqueue(obj);
}
}
public GameObject GetObject()
{
if (pool.Count > 0)
{
GameObject obj = pool.Dequeue();
obj.SetActive(true);
return obj;
}
// 必要时扩展池
GameObject newObj = Instantiate(prefab);
return newObj;
}
public void ReturnObject(GameObject obj)
{
obj.SetActive(false);
pool.Enqueue(obj);
}
}
5.3 优化Update方法
// 使用Time.deltaTime进行帧率无关的移动
void Update()
{
transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
}
// 减少不必要的每帧计算
private float nextActionTime;
public float interval = 1f;
void Update()
{
if (Time.time > nextActionTime)
{
nextActionTime = Time.time + interval;
PerformExpensiveOperation();
}
}
6. 常用设计模式
6.1 状态模式
public interface IPlayerState
{
void EnterState(PlayerController player);
void Update(PlayerController player);
void ExitState(PlayerController player);
}
public class IdleState : IPlayerState
{
public void EnterState(PlayerController player)
{
player.animator.Play("Idle");
}
public void Update(PlayerController player)
{
if (Input.GetAxis("Horizontal") != 0)
{
player.ChangeState(new RunState());
}
}
public void ExitState(PlayerController player) { }
}
public class PlayerController : MonoBehaviour
{
private IPlayerState currentState;
private void Start()
{
ChangeState(new IdleState());
}
private void Update()
{
currentState.Update(this);
}
public void ChangeState(IPlayerState newState)
{
currentState?.ExitState(this);
currentState = newState;
currentState.EnterState(this);
}
}
6.2 观察者模式
public class AchievementSystem : MonoBehaviour
{
private void OnEnable()
{
PlayerHealth.onPlayerDamaged += CheckDamageAchievement;
Enemy.onEnemyKilled += CheckKillAchievement;
}
private void OnDisable()
{
PlayerHealth.onPlayerDamaged -= CheckDamageAchievement;
Enemy.onEnemyKilled -= CheckKillAchievement;
}
private void CheckDamageAchievement(float damage)
{
// 检查伤害相关成就
}
private void CheckKillAchievement(Enemy enemy)
{
// 检查击杀相关成就
}
}
7. 脚本调试技巧
7.1 Debug工具
// 基本调试输出
Debug.Log("普通信息");
Debug.LogWarning("警告信息");
Debug.LogError("错误信息");
// 条件编译
[System.Diagnostics.Conditional("UNITY_EDITOR")]
void DebugMessage(string message)
{
Debug.Log(message);
}
// 可视化调试
Debug.DrawRay(transform.position, transform.forward * 10f, Color.red, 1f);
Debug.DrawLine(startPos, endPos, Color.green, 2f);
7.2 断点调试
- 在Visual Studio中点击代码行号左侧设置断点
- 在Unity中点击”Attach to Unity”按钮
- 运行游戏,当执行到断点时会暂停
- 使用调试工具查看变量值和调用堆栈
7.3 性能分析
// 代码块性能测量
void Update()
{
using (new UnityEngine.Profiling.ProfilerMarker("CustomUpdate").Auto())
{
// 需要测量的代码
}
}
// 在Profiler窗口中查看结果
8. 总结
本文全面介绍了Unity中C#脚本编写的关键知识和技术:
- 基础结构:理解脚本生命周期和核心Unity API
- 交互系统:掌握输入处理和物理交互
- 高级技术:协程、事件系统和脚本通信
- 编辑器扩展:自定义Inspector和开发工具
- 性能优化:对象池、缓存和Update优化
- 设计模式:状态模式和观察者模式的应用
- 调试技巧:日志输出和性能分析
通过掌握这些技术,你将能够编写高效、可维护的Unity游戏脚本。记住,优秀的Unity开发者不仅需要理解编程概念,还需要熟悉Unity引擎的特性和最佳实践。不断实践和探索新的技术,你的游戏开发技能将不断提升。