Unity基础
目录
06. Unity基础
Unity引擎完全指南 - 从C#开发者到Unity游戏开发
📌 本章导航
Unity环境和编辑器界面
Unity Hub和Unity编辑器
Unity是目前最受欢迎的游戏开发引擎之一,它提供了一个完整的开发环境,让开发者可以创建2D和3D游戏。对于C#开发者来说,Unity提供了一个熟悉的编程环境,因为Unity的脚本系统主要使用C#。
Unity Hub安装和项目管理:
Unity Hub是Unity的中央管理工具,用于:
- 管理Unity版本
- 创建和管理项目
- 访问Unity Learn资源
- 管理Unity账户
Unity编辑器界面:
Unity编辑器包含以下主要窗口:
- Scene视图: 3D/2D场景的可视化编辑器
- Game视图: 游戏运行时的视图
- Hierarchy窗口: 场景中所有游戏对象的层次结构
- Project窗口: 项目资源管理
- Inspector窗口: 选中对象的属性和组件
- Console窗口: 调试信息和错误输出
项目结构
// Unity项目的典型目录结构
Assets/
├── Scripts/ // C#脚本文件
├── Scenes/ // 场景文件
├── Prefabs/ // 预制件
├── Materials/ // 材质
├── Textures/ // 纹理
├── Models/ // 3D模型
├── Audio/ // 音频文件
├── Animations/ // 动画文件
└── Plugins/ // 插件
Unity编辑器基本操作
在Unity中,你需要熟悉以下基本操作:
- 场景导航:
- 鼠标右键 + WASD: 移动视图
- 鼠标中键: 平移视图
- 滚轮: 缩放视图
- F键: 聚焦选中对象
- 对象操作:
- W: 移动工具
- E: 旋转工具
- R: 缩放工具
- Q: 选择工具
创建第一个Unity项目
// 1. 打开Unity Hub
// 2. 点击"New Project"
// 3. 选择模板: 3D, 2D, 或Universal
// 4. 设置项目名称和位置
// 5. 点击"Create Project"
MonoBehaviour生命周期
MonoBehaviour基础
MonoBehaviour是Unity中所有脚本组件的基类。它定义了组件在游戏运行时的行为生命周期。
using UnityEngine;
// 继承MonoBehaviour的脚本
public class PlayerController : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Debug.Log("PlayerController Start");
// 初始化代码
}
// Update is called once per frame
void Update()
{
// 每帧执行的代码
HandleInput();
}
// FixedUpdate is called at regular intervals
void FixedUpdate()
{
// 物理更新代码
ApplyPhysics();
}
// LateUpdate is called after Update
void LateUpdate()
{
// 后续更新代码,通常用于跟随相机等
FollowTarget();
}
private void HandleInput()
{
// 处理输入
}
private void ApplyPhysics()
{
// 应用物理
}
private void FollowTarget()
{
// 跟随目标
}
}
完整的生命周期方法
using UnityEngine;
public class LifecycleDemo : MonoBehaviour
{
// 1. 脚本实例创建时调用(在对象被创建时)
private void Awake()
{
Debug.Log("1. Awake - 对象创建时调用");
// 初始化变量
// 设置单例模式
// 不能调用StartCoroutine
}
// 2. 脚本启用时调用(在Start之前)
private void OnEnable()
{
Debug.Log("2. OnEnable - 脚本启用时调用");
// 订阅事件
// 注册回调
}
// 3. 在所有Awake后调用,用于初始化
void Start()
{
Debug.Log("3. Start - 初始化代码");
// 只执行一次
// 依赖于其他对象的初始化
}
// 4. 每帧更新(渲染前)
void Update()
{
Debug.Log("4. Update - 每帧更新");
// 处理输入
// UI更新
// 游戏逻辑
}
// 5. 固定时间间隔更新(物理更新)
void FixedUpdate()
{
Debug.Log("5. FixedUpdate - 物理更新");
// 物理计算
// 刚体操作
// 确保物理计算的一致性
}
// 6. 渲染后更新
void LateUpdate()
{
Debug.Log("6. LateUpdate - 渲染后更新");
// 相机跟随
// 后处理
}
// 7. 碰撞检测
void OnCollisionEnter(Collision collision)
{
Debug.Log($"碰撞检测: {collision.gameObject.name}");
}
// 8. 触发器检测
void OnTriggerEnter(Collider other)
{
Debug.Log($"触发检测: {other.gameObject.name}");
}
// 9. 脚本禁用时调用
private void OnDisable()
{
Debug.Log("7. OnDisable - 脚本禁用时调用");
// 取消订阅事件
// 清理资源
}
// 10. 对象销毁时调用
private void OnDestroy()
{
Debug.Log("8. OnDestroy - 对象销毁时调用");
// 清理资源
// 保存数据
// 取消订阅
}
// 11. GUI渲染(OnGUI已被弃用,但了解其用途)
void OnGUI()
{
// 用于调试GUI
GUI.Label(new Rect(10, 10, 200, 20), "Debug Info");
}
}
生命周期最佳实践
using UnityEngine;
public class BestPracticeExample : MonoBehaviour
{
[Header("移动设置")]
public float moveSpeed = 5.0f;
public float jumpForce = 10.0f;
[Header("游戏状态")]
public int health = 100;
public int maxHealth = 100;
private Rigidbody rb;
private Animator animator;
private bool isGrounded = true;
// Awake: 初始化组件引用和单例模式
void Awake()
{
// 获取组件引用
rb = GetComponent<Rigidbody>();
animator = GetComponent<Animator>();
// 单例模式示例
if (FindObjectsOfType<BestPracticeExample>().Length > 1)
{
Destroy(gameObject);
}
}
// Start: 依赖其他对象的初始化
void Start()
{
// 依赖其他对象的初始化
InitializePlayer();
}
// Update: 输入处理和游戏逻辑
void Update()
{
HandleInput();
UpdateUI();
}
// FixedUpdate: 物理相关的更新
void FixedUpdate()
{
ApplyMovement();
}
// OnEnable/OnDisable: 事件订阅/取消订阅
private void OnEnable()
{
// 订阅事件
GameEvents.OnPlayerDeath += OnPlayerDeath;
}
private void OnDisable()
{
// 取消订阅事件
GameEvents.OnPlayerDeath -= OnPlayerDeath;
}
// OnDestroy: 清理资源
private void OnDestroy()
{
// 清理资源
if (rb != null)
{
rb.velocity = Vector3.zero;
}
}
private void InitializePlayer()
{
health = maxHealth;
if (animator != null)
{
animator.SetInteger("Health", health);
}
}
private void HandleInput()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
Jump();
}
}
private void ApplyMovement()
{
if (rb != null)
{
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
rb.velocity = new Vector3(movement.x * moveSpeed, rb.velocity.y, movement.z * moveSpeed);
}
}
private void Jump()
{
if (rb != null)
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
isGrounded = false;
}
}
private void UpdateUI()
{
// UI更新逻辑
}
private void OnPlayerDeath()
{
// 玩家死亡处理
Debug.Log("Player died!");
}
}
场景和游戏对象
游戏对象 (GameObject) 基础
GameObject是Unity中所有实体的基础,它本身只是一个容器,通过附加不同的组件来实现具体功能。
using UnityEngine;
public class GameObjectDemo : MonoBehaviour
{
// 创建游戏对象
void CreateGameObjects()
{
// 1. 创建空游戏对象
GameObject emptyObject = new GameObject("EmptyObject");
// 2. 创建带组件的游戏对象
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "PlayerCube";
// 3. 从预制件创建
// GameObject prefab = Resources.Load<GameObject>("Prefabs/PlayerPrefab");
// GameObject instance = Instantiate(prefab);
// 4. 从场景中查找
GameObject player = GameObject.Find("Player");
GameObject[] allObjects = GameObject.FindGameObjectsWithTag("Enemy");
}
// 游戏对象操作
void ManipulateGameObjects()
{
GameObject target = new GameObject("Target");
// 设置位置
target.transform.position = new Vector3(10, 0, 0);
// 设置旋转
target.transform.rotation = Quaternion.Euler(0, 45, 0);
// 设置缩放
target.transform.localScale = Vector3.one * 2;
// 设置父对象
target.transform.SetParent(this.transform);
// 激活/禁用
target.SetActive(false);
target.SetActive(true);
// 销毁对象
// Destroy(target); // 延迟一帧后销毁
// DestroyImmediate(target); // 立即销毁(仅在编辑器中)
}
// 游戏对象查找
void FindGameObjects()
{
// 通过名称查找
GameObject player = GameObject.Find("Player");
// 通过标签查找
GameObject enemy = GameObject.FindGameObjectWithTag("Enemy");
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
// 通过组件查找
PlayerController playerController = FindObjectOfType<PlayerController>();
PlayerController[] allPlayers = FindObjectsOfType<PlayerController>();
}
}
场景管理
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneManagementDemo : MonoBehaviour
{
// 场景加载
public void LoadScene(string sceneName)
{
// 同步加载
SceneManager.LoadScene(sceneName);
// 异步加载
StartCoroutine(LoadSceneAsync(sceneName));
}
// 异步场景加载
System.Collections.IEnumerator LoadSceneAsync(string sceneName)
{
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
while (!asyncLoad.isDone)
{
float progress = Mathf.Clamp01(asyncLoad.progress / 0.9f);
Debug.Log($"加载进度: {progress * 100}%");
yield return null;
}
}
// 场景切换
public void SwitchToScene(int sceneIndex)
{
SceneManager.LoadScene(sceneIndex);
}
// 添加场景
public void LoadSceneAdditive(string sceneName)
{
SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
}
// 场景信息
void SceneInfo()
{
Scene currentScene = SceneManager.GetActiveScene();
Debug.Log($"当前场景: {currentScene.name}");
Debug.Log($"场景索引: {currentScene.buildIndex}");
Debug.Log($"场景是否有效: {currentScene.isLoaded}");
int sceneCount = SceneManager.sceneCount;
Debug.Log($"加载的场景数量: {sceneCount}");
}
// 场景卸载
public void UnloadScene(string sceneName)
{
SceneManager.UnloadSceneAsync(sceneName);
}
}
预制件 (Prefab) 系统
using UnityEngine;
public class PrefabDemo : MonoBehaviour
{
[Header("预制件引用")]
public GameObject playerPrefab;
public GameObject enemyPrefab;
public GameObject bulletPrefab;
[Header("生成设置")]
public Transform spawnPoint;
public float spawnInterval = 2.0f;
private System.Collections.IEnumerator spawnCoroutine;
void Start()
{
// 开始自动生成敌人
spawnCoroutine = SpawnEnemies();
StartCoroutine(spawnCoroutine);
}
// 实例化预制件
GameObject SpawnObject(GameObject prefab, Vector3 position, Quaternion rotation)
{
if (prefab != null)
{
GameObject instance = Instantiate(prefab, position, rotation);
instance.name = prefab.name + "_Instance";
return instance;
}
return null;
}
// 生成敌人
System.Collections.IEnumerator SpawnEnemies()
{
while (true)
{
if (enemyPrefab != null && spawnPoint != null)
{
Vector3 spawnPos = spawnPoint.position;
GameObject enemy = SpawnObject(enemyPrefab, spawnPos, Quaternion.identity);
if (enemy != null)
{
// 设置敌人属性
EnemyController enemyCtrl = enemy.GetComponent<EnemyController>();
if (enemyCtrl != null)
{
enemyCtrl.SetDifficulty(1.0f);
}
}
}
yield return new WaitForSeconds(spawnInterval);
}
}
// 生成子弹
public void SpawnBullet(Vector3 position, Vector3 direction)
{
if (bulletPrefab != null)
{
GameObject bullet = Instantiate(bulletPrefab, position, Quaternion.identity);
BulletController bulletCtrl = bullet.GetComponent<BulletController>();
if (bulletCtrl != null)
{
bulletCtrl.SetDirection(direction);
}
}
}
// 预制件操作
void PrefabOperations()
{
// 检查对象是否为预制件实例
if (PrefabUtility.IsPartOfPrefabInstance(this.gameObject))
{
Debug.Log("这是一个预制件实例");
}
// 获取预制件源
GameObject prefabSource = PrefabUtility.GetCorrespondingObjectFromSource(this.gameObject) as GameObject;
}
void OnDestroy()
{
// 停止生成协程
if (spawnCoroutine != null)
{
StopCoroutine(spawnCoroutine);
}
}
}
// 敌人控制器示例
public class EnemyController : MonoBehaviour
{
public float health = 100f;
public float speed = 2.0f;
public int damage = 10;
public void SetDifficulty(float multiplier)
{
health *= multiplier;
speed *= multiplier;
damage = Mathf.RoundToInt(damage * multiplier);
}
public void TakeDamage(float damage)
{
health -= damage;
if (health <= 0)
{
Die();
}
}
void Die()
{
// 播放死亡效果
Debug.Log($"{gameObject.name} died!");
Destroy(gameObject);
}
}
// 子弹控制器示例
public class BulletController : MonoBehaviour
{
public float speed = 10.0f;
public float damage = 20.0f;
public float lifeTime = 5.0f;
private Vector3 direction;
public void SetDirection(Vector3 dir)
{
direction = dir.normalized;
}
void Start()
{
// 设置子弹生命周期
Destroy(gameObject, lifeTime);
}
void Update()
{
// 移动子弹
transform.position += direction * speed * Time.deltaTime;
}
void OnTriggerEnter(Collider other)
{
// 检测碰撞
if (other.CompareTag("Enemy"))
{
EnemyController enemy = other.GetComponent<EnemyController>();
if (enemy != null)
{
enemy.TakeDamage(damage);
}
Destroy(gameObject); // 销毁子弹
}
}
}
组件系统
组件基础
组件是Unity中功能的基本单元,每个GameObject都可以附加多个组件来实现不同的功能。
using UnityEngine;
public class ComponentDemo : MonoBehaviour
{
// 常用组件引用
private Transform transform; // 继承自MonoBehaviour,自动可用
private Rigidbody rb;
private Renderer renderer;
private Collider collider;
private AudioSource audioSource;
private Animator animator;
void Awake()
{
// 获取组件的几种方式
rb = GetComponent<Rigidbody>();
renderer = GetComponent<Renderer>();
collider = GetComponent<Collider>();
audioSource = GetComponent<AudioSource>();
animator = GetComponent<Animator>();
// 获取组件(如果不存在则添加)
rb = GetComponent<Rigidbody>() ?? gameObject.AddComponent<Rigidbody>();
// 获取多个组件
Renderer[] renderers = GetComponents<Renderer>();
// 获取子对象的组件
Renderer childRenderer = GetComponentInChildren<Renderer>();
Renderer[] childRenderers = GetComponentsInChildren<Renderer>();
// 获取父对象的组件
PlayerController parentController = GetComponentInParent<PlayerController>();
}
// 组件操作
void ComponentOperations()
{
// 启用/禁用组件
if (renderer != null)
{
renderer.enabled = false; // 禁用渲染
renderer.enabled = true; // 启用渲染
}
// 检查组件是否存在
if (GetComponent<Rigidbody>() != null)
{
Debug.Log("物体有刚体组件");
}
// 添加组件
if (GetComponent<MeshFilter>() == null)
{
gameObject.AddComponent<MeshFilter>();
}
// 移除组件
// Destroy(GetComponent<Renderer>()); // 通过销毁组件对象来移除
}
// 自定义组件示例
void CustomComponentExample()
{
// 添加自定义组件
HealthComponent health = gameObject.AddComponent<HealthComponent>();
health.maxHealth = 200;
health.currentHealth = 200;
}
}
// 自定义组件示例
public class HealthComponent : MonoBehaviour
{
[Header("生命值设置")]
public int maxHealth = 100;
public int currentHealth;
[Header("生命值UI")]
public UnityEngine.UI.Slider healthBar;
void Start()
{
currentHealth = maxHealth;
UpdateHealthBar();
}
public void TakeDamage(int damage)
{
currentHealth = Mathf.Max(0, currentHealth - damage);
UpdateHealthBar();
if (currentHealth <= 0)
{
Die();
}
}
public void Heal(int amount)
{
currentHealth = Mathf.Min(maxHealth, currentHealth + amount);
UpdateHealthBar();
}
void UpdateHealthBar()
{
if (healthBar != null)
{
healthBar.value = (float)currentHealth / maxHealth;
}
}
void Die()
{
Debug.Log($"{gameObject.name} died!");
// 触发死亡事件
SendMessage("OnDeath", this, SendMessageOptions.DontRequireReceiver);
}
}
组件间通信
using UnityEngine;
// 主控制器组件
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class PlayerController : MonoBehaviour
{
[Header("移动设置")]
public float moveSpeed = 5.0f;
public float jumpForce = 10.0f;
private Rigidbody rb;
private HealthComponent health;
private WeaponComponent weapon;
void Start()
{
rb = GetComponent<Rigidbody>();
health = GetComponent<HealthComponent>();
weapon = GetComponent<WeaponComponent>();
// 如果组件不存在,创建它们
if (health == null)
{
health = gameObject.AddComponent<HealthComponent>();
}
if (weapon == null)
{
weapon = gameObject.AddComponent<WeaponComponent>();
}
}
void Update()
{
HandleMovement();
HandleInput();
}
void HandleMovement()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(horizontal, 0, vertical).normalized;
rb.velocity = new Vector3(movement.x * moveSpeed, rb.velocity.y, movement.z * moveSpeed);
}
void HandleInput()
{
if (Input.GetButtonDown("Jump"))
{
Jump();
}
if (Input.GetButtonDown("Fire1"))
{
weapon?.Shoot();
}
}
void Jump()
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
// 接收来自其他组件的消息
void OnDamageTaken(int damage)
{
health?.TakeDamage(damage);
}
void OnHealthChanged(int current, int max)
{
Debug.Log($"Health changed: {current}/{max}");
}
}
// 武器组件
public class WeaponComponent : MonoBehaviour
{
[Header("武器设置")]
public float damage = 25.0f;
public float fireRate = 0.5f;
public float range = 100.0f;
private float lastFireTime = 0f;
private PlayerController player;
void Start()
{
player = GetComponent<PlayerController>();
}
public void Shoot()
{
if (Time.time - lastFireTime >= fireRate)
{
lastFireTime = Time.time;
// 射线检测
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
RaycastHit hit;
if (Physics.Raycast(ray, out hit, range))
{
// 检查是否击中敌人
EnemyController enemy = hit.collider.GetComponent<EnemyController>();
if (enemy != null)
{
enemy.TakeDamage((int)damage);
}
}
// 播放射击效果
OnShoot();
}
}
void OnShoot()
{
Debug.Log("Weapon fired!");
// 播放音效、粒子效果等
}
}
// 碰撞检测组件
public class CollisionDetector : MonoBehaviour
{
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
// 通知玩家控制器受到伤害
PlayerController player = GetComponent<PlayerController>();
if (player != null)
{
player.SendMessage("OnDamageTaken", 10, SendMessageOptions.DontRequireReceiver);
}
}
}
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("PowerUp"))
{
// 激活道具效果
ActivatePowerUp(other.gameObject);
}
}
void ActivatePowerUp(GameObject powerUp)
{
Debug.Log($"Collected power-up: {powerUp.name}");
Destroy(powerUp);
// 通知其他组件
SendMessage("OnPowerUpCollected", powerUp.name, SendMessageOptions.DontRequireReceiver);
}
}
组件属性面板
using UnityEngine;
// 使用属性特性自定义Inspector面板
public class CustomInspectorDemo : MonoBehaviour
{
[Header("基本设置")]
public string playerName = "Player";
[Range(1, 100)]
public int level = 1;
[Tooltip("玩家移动速度")]
public float moveSpeed = 5.0f;
[Header("颜色设置")]
public Color playerColor = Color.blue;
public Gradient healthGradient;
[Header("音效设置")]
public AudioClip[] soundEffects;
[Range(0f, 1f)]
public float volume = 1.0f;
[Header("特殊能力")]
[TextArea(3, 10)]
public string specialAbilityDescription = "描述特殊能力";
[Space(10)]
[Header("调试设置")]
public bool debugMode = false;
[ConditionalField("debugMode")]
public int debugValue = 0;
// 自定义属性
[System.NonSerialized]
private int internalScore = 0;
public int Score
{
get { return internalScore; }
set { internalScore = Mathf.Max(0, value); }
}
// 只读属性显示
[System.NonSerialized]
private float calculatedValue = 0f;
void Update()
{
calculatedValue = level * moveSpeed;
}
// 在Inspector中显示只读值
private void OnValidate()
{
level = Mathf.Clamp(level, 1, 100);
moveSpeed = Mathf.Max(0.1f, moveSpeed);
}
}
// 自定义属性特性
public class ConditionalFieldAttribute : PropertyAttribute
{
public string ConditionFieldName { get; private set; }
public ConditionalFieldAttribute(string conditionFieldName)
{
ConditionFieldName = conditionFieldName;
}
}
// 使用标签和层
public class TagLayerDemo : MonoBehaviour
{
[Header("目标设置")]
public string targetTag = "Player";
public LayerMask collisionLayers = -1; // 所有层
void Start()
{
// 使用标签查找
GameObject target = GameObject.FindGameObjectWithTag(targetTag);
// 使用层检测
Collider[] hitColliders = Physics.OverlapSphere(transform.position, 5f, collisionLayers);
foreach (var hitCollider in hitColliders)
{
Debug.Log($"检测到: {hitCollider.name}");
}
}
}
Transform和空间变换
Transform组件详解
Transform组件是Unity中每个GameObject都有的组件,它管理对象的位置、旋转和缩放。
using UnityEngine;
public class TransformDemo : MonoBehaviour
{
[Header("目标设置")]
public Transform target;
public float moveSpeed = 5.0f;
public float rotateSpeed = 60.0f;
void Update()
{
if (target != null)
{
MoveToTarget();
LookAtTarget();
}
}
// 位置操作
void PositionOperations()
{
// 设置绝对位置
transform.position = new Vector3(10, 5, 0);
// 相对当前位置移动
transform.position += Vector3.forward * Time.deltaTime * moveSpeed;
// 相对自身坐标系移动
transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);
// 相对世界坐标系移动
transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed, Space.World);
// 相对本地坐标系移动
transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed, Space.Self);
}
// 旋转操作
void RotationOperations()
{
// 设置绝对旋转(欧拉角)
transform.eulerAngles = new Vector3(0, 45, 0);
// 设置绝对旋转(四元数)
transform.rotation = Quaternion.Euler(0, 45, 0);
// 相对当前旋转增加
transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime);
// 绕指定轴旋转
transform.RotateAround(Vector3.zero, Vector3.up, 30 * Time.deltaTime);
// 朝向指定点
if (target != null)
{
transform.LookAt(target);
}
}
// 缩放操作
void ScaleOperations()
{
// 设置绝对缩放
transform.localScale = Vector3.one * 2;
// 相对缩放
transform.localScale += Vector3.one * 0.1f * Time.deltaTime;
}
// 移动到目标
void MoveToTarget()
{
// 线性插值移动
transform.position = Vector3.Lerp(transform.position, target.position,
moveSpeed * Time.deltaTime);
// 平滑移动
transform.position = Vector3.SmoothDamp(transform.position, target.position,
ref velocity, 0.1f);
}
// 朝向目标
void LookAtTarget()
{
if (target != null)
{
// 简单位置朝向
Vector3 direction = target.position - transform.position;
transform.rotation = Quaternion.LookRotation(direction);
// 平滑朝向
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation,
rotateSpeed * Time.deltaTime);
}
}
private Vector3 velocity = Vector3.zero; // 用于SmoothDamp
// 空间变换示例
void SpaceTransformations()
{
// 世界坐标和本地坐标的转换
Vector3 worldPos = transform.position; // 世界位置
Vector3 localPos = transform.localPosition; // 本地位置
// 坐标系转换
Vector3 worldToTarget = target.position - transform.position;
Vector3 localToTarget = transform.InverseTransformPoint(target.position);
// 方向转换
Vector3 worldForward = transform.forward; // 世界空间的前方
Vector3 localForward = transform.TransformDirection(Vector3.forward); // 本地转世界方向
// 验证转换
Debug.Log($"世界位置: {worldPos}, 本地位置: {localPos}");
Debug.Log($"世界到目标: {worldToTarget}, 本地到目标: {localToTarget}");
}
}
高级Transform操作
using UnityEngine;
public class AdvancedTransform : MonoBehaviour
{
[Header("变换设置")]
public Transform[] childObjects;
public float orbitRadius = 5.0f;
public float orbitSpeed = 30.0f;
void Start()
{
// 获取所有子对象
childObjects = new Transform[transform.childCount];
for (int i = 0; i < transform.childCount; i++)
{
childObjects[i] = transform.GetChild(i);
}
}
void Update()
{
OrbitMovement();
ParentChildOperations();
}
// 环绕运动
void OrbitMovement()
{
for (int i = 0; i < childObjects.Length; i++)
{
if (childObjects[i] != null)
{
float angle = Time.time * orbitSpeed * Mathf.Deg2Rad + (i * Mathf.PI * 2 / childObjects.Length);
Vector3 orbitPos = new Vector3(
Mathf.Cos(angle) * orbitRadius,
0,
Mathf.Sin(angle) * orbitRadius
);
childObjects[i].position = transform.position + orbitPos;
childObjects[i].LookAt(transform);
}
}
}
// 父子关系操作
void ParentChildOperations()
{
// 设置父对象
// childObject.SetParent(parentObject);
// 设置父对象并保持世界位置不变
// childObject.SetParent(parentObject, false); // 保持本地位置
// childObject.SetParent(parentObject, true); // 保持世界位置
// 解除父关系
// childObject.SetParent(null);
// 获取父对象
Transform parent = transform.parent;
// 获取根对象
Transform root = transform.root;
// 获取层级深度
int depth = GetTransformDepth(transform);
}
int GetTransformDepth(Transform t)
{
int depth = 0;
while (t.parent != null)
{
depth++;
t = t.parent;
}
return depth;
}
// 批量变换操作
public void BatchTransformOperation(System.Action<Transform> operation)
{
operation(transform);
for (int i = 0; i < transform.childCount; i++)
{
Transform child = transform.GetChild(i);
BatchTransformOperationRecursive(child, operation);
}
}
void BatchTransformOperationRecursive(Transform t, System.Action<Transform> operation)
{
operation(t);
for (int i = 0; i < t.childCount; i++)
{
Transform child = t.GetChild(i);
BatchTransformOperationRecursive(child, operation);
}
}
// 变换重置
public void ResetTransform()
{
transform.position = Vector3.zero;
transform.rotation = Quaternion.identity;
transform.localScale = Vector3.one;
}
// 变换复制
public void CopyTransformFrom(Transform source)
{
if (source != null)
{
transform.position = source.position;
transform.rotation = source.rotation;
transform.localScale = source.localScale;
}
}
// 相对变换
public void ApplyRelativeTransform(Vector3 relativePos, Quaternion relativeRot, Vector3 relativeScale)
{
transform.position += transform.TransformDirection(relativePos);
transform.rotation = transform.rotation * relativeRot;
transform.localScale = Vector3.Scale(transform.localScale, relativeScale);
}
}
// 变换工具类
public static class TransformUtils
{
// 在指定对象的局部空间中查找最近的点
public static Vector3 GetClosestPointOnObject(Transform transform, Vector3 worldPoint)
{
Renderer renderer = transform.GetComponent<Renderer>();
if (renderer != null)
{
return renderer.bounds.ClosestPoint(worldPoint);
}
return transform.position;
}
// 获取对象的边界框
public static Bounds GetRendererBounds(GameObject obj)
{
Renderer[] renderers = obj.GetComponentsInChildren<Renderer>();
if (renderers.Length == 0) return new Bounds();
Bounds bounds = renderers[0].bounds;
for (int i = 1; i < renderers.Length; i++)
{
bounds.Encapsulate(renderers[i].bounds);
}
return bounds;
}
// 检查两个对象是否重叠
public static bool CheckOverlap(GameObject obj1, GameObject obj2)
{
Bounds bounds1 = GetRendererBounds(obj1);
Bounds bounds2 = GetRendererBounds(obj2);
return bounds1.Intersects(bounds2);
}
// 获取对象的中心点
public static Vector3 GetCenterPoint(GameObject obj)
{
Bounds bounds = GetRendererBounds(obj);
return bounds.center;
}
}
Transform动画和插值
using UnityEngine;
public class TransformAnimation : MonoBehaviour
{
[Header("动画设置")]
public AnimationCurve positionCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
public float animationDuration = 2.0f;
public bool loopAnimation = true;
private Vector3 startPos;
private Quaternion startRot;
private Vector3 endPos;
private Quaternion endRot;
private float animationTime = 0f;
private bool isAnimating = false;
void Start()
{
startPos = transform.position;
startRot = transform.rotation;
endPos = startPos + Vector3.right * 5f;
endRot = startRot * Quaternion.Euler(0, 90, 0);
}
void Update()
{
if (isAnimating)
{
AnimateTransform();
}
}
// 开始动画
public void StartAnimation()
{
animationTime = 0f;
isAnimating = true;
}
// 动画更新
void AnimateTransform()
{
animationTime += Time.deltaTime;
float progress = Mathf.Clamp01(animationTime / animationDuration);
// 使用动画曲线
float curvedProgress = positionCurve.Evaluate(progress);
// 位置插值
transform.position = Vector3.Lerp(startPos, endPos, curvedProgress);
// 旋转插值
transform.rotation = Quaternion.Slerp(startRot, endRot, curvedProgress);
if (progress >= 1f)
{
if (loopAnimation)
{
// 循环动画:交换起始和结束位置
Vector3 tempPos = startPos;
Quaternion tempRot = startRot;
startPos = endPos;
startRot = endRot;
endPos = tempPos;
endRot = tempRot;
animationTime = 0f;
}
else
{
isAnimating = false;
}
}
}
// 平滑跟随
public void SmoothFollow(Transform target, float smoothTime = 0.3f)
{
if (target != null)
{
transform.position = Vector3.SmoothDamp(transform.position,
target.position,
ref velocity,
smoothTime);
}
}
// 弹簧效果
public void SpringFollow(Transform target, float stiffness = 10f, float damping = 1f)
{
if (target != null)
{
Vector3 force = (target.position - transform.position) * stiffness;
velocity += force * Time.deltaTime;
velocity *= Mathf.Exp(-damping * Time.deltaTime); // 阻尼
transform.position += velocity * Time.deltaTime;
}
}
private Vector3 velocity = Vector3.zero;
// 旋转动画
public void RotateOverTime(Vector3 axis, float degreesPerSecond)
{
transform.Rotate(axis, degreesPerSecond * Time.deltaTime);
}
// 缓动函数
public static class Easing
{
public static float EaseInQuad(float t) => t * t;
public static float EaseOutQuad(float t) => t * (2 - t);
public static float EaseInOutQuad(float t) => t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
public static float EaseInCubic(float t) => t * t * t;
public static float EaseOutCubic(float t) => 1 + (--t) * t * t;
}
}
输入系统
传统输入系统
Unity提供了多种输入检测方式,从简单的按键检测到复杂的输入管理。
using UnityEngine;
public class InputSystemDemo : MonoBehaviour
{
[Header("移动设置")]
public float moveSpeed = 5.0f;
public float mouseSensitivity = 2.0f;
[Header("输入轴映射")]
public string horizontalAxis = "Horizontal";
public string verticalAxis = "Vertical";
public string jumpButton = "Jump";
public string fireButton = "Fire1";
private float mouseX = 0f;
private float mouseY = 0f;
private bool isJumping = false;
void Update()
{
HandleKeyboardInput();
HandleMouseInput();
HandleGamepadInput();
HandleTouchInput();
}
// 键盘输入处理
void HandleKeyboardInput()
{
// 按键按下(每帧检测)
if (Input.GetKeyDown(KeyCode.Space))
{
Jump();
}
// 按键持续按下
if (Input.GetKey(KeyCode.LeftShift))
{
moveSpeed *= 2f; // 加速
}
// 按键抬起
if (Input.GetKeyUp(KeyCode.Space))
{
isJumping = false;
}
// 轴输入(推荐用于平滑移动)
float horizontal = Input.GetAxis(horizontalAxis);
float vertical = Input.GetAxis(verticalAxis);
Vector3 movement = new Vector3(horizontal, 0, vertical) * moveSpeed * Time.deltaTime;
transform.Translate(movement);
// 获取特定按键状态
bool jumpPressed = Input.GetButton(jumpButton);
bool firePressed = Input.GetButton(fireButton);
}
// 鼠标输入处理
void HandleMouseInput()
{
// 鼠标按键
if (Input.GetMouseButtonDown(0)) // 左键
{
Debug.Log("左键按下");
}
if (Input.GetMouseButton(1)) // 右键持续按下
{
// 相机旋转
mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
transform.Rotate(Vector3.down, mouseX);
Camera.main.transform.Rotate(Vector3.right, -mouseY);
}
// 鼠标滚轮
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Abs(scroll) > 0.01f)
{
Debug.Log($"滚轮滚动: {scroll}");
}
// 鼠标位置
Vector3 mousePos = Input.mousePosition;
Vector3 worldPos = Camera.main.ScreenToWorldPoint(
new Vector3(mousePos.x, mousePos.y, Camera.main.nearClipPlane));
}
// 手柄输入处理
void HandleGamepadInput()
{
// 手柄轴
float leftStickX = Input.GetAxis("Left Stick X");
float leftStickY = Input.GetAxis("Left Stick Y");
float rightStickX = Input.GetAxis("Right Stick X");
float rightStickY = Input.GetAxis("Right Stick Y");
// 手柄按钮 (通常为0-19)
if (Input.GetButtonDown("joystick button 0")) // A/X按钮
{
Debug.Log("A按钮按下");
}
// 触发器
float leftTrigger = Input.GetAxis("Left Trigger");
float rightTrigger = Input.GetAxis("Right Trigger");
}
// 触摸输入处理(移动端)
void HandleTouchInput()
{
if (Input.touchSupported && Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
switch (touch.phase)
{
case TouchPhase.Began:
Debug.Log("触摸开始");
break;
case TouchPhase.Moved:
Debug.Log($"触摸移动: {touch.deltaPosition}");
break;
case TouchPhase.Ended:
Debug.Log("触摸结束");
break;
case TouchPhase.Canceled:
Debug.Log("触摸取消");
break;
}
// 多点触控
if (Input.touchCount >= 2)
{
Touch touch1 = Input.GetTouch(0);
Touch touch2 = Input.GetTouch(1);
// 计算两点间距离,用于缩放
float currentDist = Vector2.Distance(touch1.position, touch2.position);
if (touch1.phase == TouchPhase.Moved || touch2.phase == TouchPhase.Moved)
{
// 实现缩放逻辑
}
}
}
}
void Jump()
{
if (!isJumping)
{
isJumping = true;
// 实现跳跃逻辑
Debug.Log("跳跃!");
}
}
}
新输入系统 (New Input System)
Unity的新输入系统提供了更灵活和强大的输入管理。
// 注意:新输入系统需要导入Package: "Input System"
// 以下代码展示概念,实际使用需要设置Input Actions
/*
using UnityEngine;
using UnityEngine.InputSystem;
public class NewInputSystemDemo : MonoBehaviour
{
[Header("移动设置")]
public float moveSpeed = 5.0f;
private PlayerInput playerInput;
private InputAction moveAction;
private InputAction jumpAction;
private InputAction fireAction;
void Awake()
{
playerInput = new PlayerInput();
}
void OnEnable()
{
moveAction = playerInput.Player.Move;
jumpAction = playerInput.Player.Jump;
fireAction = playerInput.Player.Fire;
moveAction.Enable();
jumpAction.Enable();
fireAction.Enable();
jumpAction.performed += _ => Jump();
fireAction.performed += _ => Fire();
}
void OnDisable()
{
moveAction.Disable();
jumpAction.Disable();
fireAction.Disable();
}
void Update()
{
// 获取移动向量
Vector2 moveInput = moveAction.ReadValue<Vector2>();
Vector3 movement = new Vector3(moveInput.x, 0, moveInput.y) * moveSpeed * Time.deltaTime;
transform.Translate(movement);
}
void Jump()
{
Debug.Log("跳跃!");
}
void Fire()
{
Debug.Log("开火!");
}
}
*/
输入管理器
using UnityEngine;
// 输入管理器单例
public class InputManager : MonoBehaviour
{
public static InputManager Instance { get; private set; }
[Header("移动控制")]
public string horizontalAxis = "Horizontal";
public string verticalAxis = "Vertical";
[Header("动作控制")]
public string jumpButton = "Jump";
public string fireButton = "Fire1";
public string interactButton = "Interact";
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
// 获取轴输入
public float GetHorizontalAxis() => Input.GetAxis(horizontalAxis);
public float GetVerticalAxis() => Input.GetAxis(verticalAxis);
public Vector2 GetMovementVector() => new Vector2(GetHorizontalAxis(), GetVerticalAxis());
// 获取按钮状态
public bool IsJumpPressed() => Input.GetButtonDown(jumpButton);
public bool IsFirePressed() => Input.GetButtonDown(fireButton);
public bool IsInteractPressed() => Input.GetButtonDown(interactButton);
// 获取持续按钮状态
public bool IsJumpHeld() => Input.GetButton(jumpButton);
public bool IsFireHeld() => Input.GetButton(fireButton);
// 获取按钮抬起状态
public bool IsJumpReleased() => Input.GetButtonUp(jumpButton);
// 复合输入检测
public bool IsMoving() => Mathf.Abs(GetHorizontalAxis()) > 0.1f || Mathf.Abs(GetVerticalAxis()) > 0.1f;
public bool IsSprinting() => Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
public bool IsCrouching() => Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
// 鼠标输入
public Vector2 GetMouseDelta() => new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
public bool IsLeftMousePressed() => Input.GetMouseButtonDown(0);
public bool IsRightMousePressed() => Input.GetMouseButtonDown(1);
public Vector3 GetMousePosition() => Input.mousePosition;
// 触摸输入
public bool IsTouchSupported() => Input.touchSupported;
public int GetTouchCount() => Input.touchCount;
public Touch? GetTouch(int index)
{
if (Input.touchCount > index)
return Input.GetTouch(index);
return null;
}
// 输入设备检测
public string[] GetConnectedInputDevices()
{
return Input.GetJoystickNames();
}
public bool IsGamepadConnected()
{
string[] joysticks = Input.GetJoystickNames();
return joysticks.Length > 0 && !string.IsNullOrEmpty(joysticks[0]);
}
}
// 使用输入管理器的示例
public class PlayerWithInputManager : MonoBehaviour
{
[Header("移动设置")]
public float moveSpeed = 5.0f;
public float rotateSpeed = 60.0f;
void Update()
{
HandleMovement();
HandleActions();
}
void HandleMovement()
{
Vector2 moveInput = InputManager.Instance.GetMovementVector();
Vector3 movement = new Vector3(moveInput.x, 0, moveInput.y) * moveSpeed * Time.deltaTime;
transform.Translate(movement);
if (InputManager.Instance.IsMoving())
{
// 根据移动方向旋转角色
if (moveInput.magnitude > 0.1f)
{
float targetAngle = Mathf.Atan2(moveInput.x, moveInput.y) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Slerp(
transform.rotation,
Quaternion.Euler(0, targetAngle, 0),
rotateSpeed * Time.deltaTime
);
}
}
}
void HandleActions()
{
if (InputManager.Instance.IsJumpPressed())
{
Jump();
}
if (InputManager.Instance.IsFirePressed())
{
Fire();
}
if (InputManager.Instance.IsInteractPressed())
{
Interact();
}
}
void Jump()
{
Debug.Log("玩家跳跃!");
}
void Fire()
{
Debug.Log("玩家开火!");
}
void Interact()
{
Debug.Log("玩家交互!");
}
}
Time和帧率控制
Time类详解
Time类提供了游戏时间相关的各种信息和控制。
using UnityEngine;
public class TimeSystemDemo : MonoBehaviour
{
[Header("时间设置")]
public float gameSpeed = 1.0f;
public float slowMotionFactor = 0.1f;
[Header("计时器")]
public float timerDuration = 10.0f;
private float timer = 0f;
private bool timerRunning = false;
void Start()
{
PrintTimeInfo();
}
void Update()
{
UpdateTimer();
HandleTimeControls();
// 基于时间的移动(重要:使用Time.deltaTime)
transform.Translate(Vector3.forward * 5f * Time.deltaTime);
}
// 时间信息
void PrintTimeInfo()
{
Debug.Log($"=== 时间信息 ===");
Debug.Log($"当前时间: {Time.time:F2}s");
Debug.Log($"帧时间: {Time.deltaTime:F4}s");
Debug.Log($"固定时间步长: {Time.fixedDeltaTime:F4}s");
Debug.Log($"时间缩放: {Time.timeScale:F2}");
Debug.Log($"帧率: {1.0f / Time.deltaTime:F1} FPS");
}
// Time类的主要属性
void TimeProperties()
{
// 从游戏开始的总时间
float totalTime = Time.time;
// 上一帧到当前帧的时间(秒)
float deltaTime = Time.deltaTime;
// 固定更新的时间间隔
float fixedDeltaTime = Time.fixedDeltaTime;
// 时间缩放因子(用于慢动作等效果)
float timeScale = Time.timeScale;
// 未缩放的时间(不受timeScale影响)
float unscaledTime = Time.unscaledTime;
float unscaledDeltaTime = Time.unscaledDeltaTime;
// 自游戏开始的总帧数
int frameCount = Time.frameCount;
// 固定更新调用次数
int fixedFrameCount = Time.fixedFrameCount;
// 目标帧率
int targetFrameRate = Application.targetFrameRate;
}
// 时间缩放控制
void HandleTimeControls()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// 慢动作效果
Time.timeScale = slowMotionFactor;
}
if (Input.GetKeyUp(KeyCode.Space))
{
// 恢复正常速度
Time.timeScale = 1.0f;
}
if (Input.GetKeyDown(KeyCode.Alpha1))
{
// 暂停游戏
Time.timeScale = 0f;
}
if (Input.GetKeyDown(KeyCode.Alpha2))
{
// 正常速度
Time.timeScale = 1.0f;
}
if (Input.GetKeyDown(KeyCode.Alpha3))
{
// 2倍速
Time.timeScale = 2.0f;
}
}
// 计时器功能
void UpdateTimer()
{
if (timerRunning)
{
timer += Time.unscaledDeltaTime; // 使用未缩放时间确保计时准确性
if (timer >= timerDuration)
{
TimerComplete();
}
}
}
public void StartTimer()
{
timer = 0f;
timerRunning = true;
}
public void StopTimer()
{
timerRunning = false;
}
public void ResetTimer()
{
timer = 0f;
timerRunning = false;
}
void TimerComplete()
{
Debug.Log("计时器完成!");
timerRunning = false;
// 可以触发其他事件
OnTimerComplete();
}
void OnTimerComplete()
{
// 计时器完成时的回调
}
// 帧率控制
void FrameRateControl()
{
// 设置目标帧率
Application.targetFrameRate = 60; // 60 FPS
// 获取当前帧率
float currentFPS = 1.0f / Time.deltaTime;
// VSync控制
QualitySettings.vSyncCount = 1; // 启用垂直同步
}
// 基于时间的平滑移动示例
public void SmoothMoveTo(Vector3 targetPosition, float duration)
{
StartCoroutine(SmoothMoveCoroutine(targetPosition, duration));
}
System.Collections.IEnumerator SmoothMoveCoroutine(Vector3 target, float duration)
{
Vector3 startPosition = transform.position;
float elapsed = 0f;
while (elapsed < duration)
{
transform.position = Vector3.Lerp(startPosition, target,
elapsed / duration);
elapsed += Time.deltaTime;
yield return null;
}
transform.position = target;
}
// 固定时间步长的重要性
void FixedUpdate()
{
// 物理更新应该使用Time.fixedDeltaTime
// 这确保了物理计算的稳定性
rb.velocity = movementDirection * speed * Time.fixedDeltaTime;
}
private Rigidbody rb;
private Vector3 movementDirection = Vector3.forward;
private float speed = 5.0f;
}
时间管理最佳实践
using UnityEngine;
public class TimeManagementBestPractices : MonoBehaviour
{
[Header("游戏状态")]
public GameState gameState = GameState.Playing;
[Header("时间设置")]
public float normalTimeScale = 1.0f;
public float pauseTimeScale = 0.0f;
public float slowMotionScale = 0.25f;
private float normalFixedDeltaTime;
private TimeManager timeManager;
void Start()
{
// 保存正常的固定时间步长
normalFixedDeltaTime = Time.fixedDeltaTime;
timeManager = new TimeManager();
}
void Update()
{
HandleGameStateChanges();
UpdateGameLogic();
}
// 游戏状态时间管理
void HandleGameStateChanges()
{
switch (gameState)
{
case GameState.Paused:
Time.timeScale = pauseTimeScale;
Time.fixedDeltaTime = normalFixedDeltaTime * pauseTimeScale;
break;
case GameState.SlowMotion:
Time.timeScale = slowMotionScale;
Time.fixedDeltaTime = normalFixedDeltaTime * slowMotionScale;
break;
case GameState.Playing:
Time.timeScale = normalTimeScale;
Time.fixedDeltaTime = normalFixedDeltaTime;
break;
}
}
// 游戏逻辑更新(使用时间管理)
void UpdateGameLogic()
{
switch (gameState)
{
case GameState.Playing:
// 正常游戏逻辑
ProcessGameplay();
break;
case GameState.Paused:
// 暂停状态逻辑(如果有)
ProcessPauseLogic();
break;
case GameState.SlowMotion:
// 慢动作逻辑
ProcessSlowMotion();
break;
}
}
void ProcessGameplay()
{
// 正常游戏逻辑
// 使用Time.deltaTime进行时间相关的计算
transform.Translate(Vector3.forward * 5f * Time.deltaTime);
}
void ProcessPauseLogic()
{
// 暂停时的特殊逻辑
// 注意:在暂停状态下,Time.deltaTime为0
}
void ProcessSlowMotion()
{
// 慢动作时的特殊逻辑
// 所有使用Time.deltaTime的计算都会自动适应慢动作
}
// 时间管理器类
public class TimeManager
{
public float GameTime { get; private set; }
public float RealTime { get; private set; }
public float TimeScale { get; private set; } = 1.0f;
public void Update()
{
GameTime += Time.deltaTime;
RealTime += Time.unscaledDeltaTime;
}
public void SetTimeScale(float scale)
{
TimeScale = Mathf.Clamp(scale, 0f, 10f);
Time.timeScale = TimeScale;
}
public void Pause()
{
SetTimeScale(0f);
}
public void Resume()
{
SetTimeScale(1f);
}
public void SlowMotion(float factor)
{
SetTimeScale(factor);
}
}
// 时间相关的工具方法
public static class TimeUtils
{
// 格式化时间显示
public static string FormatTime(float seconds)
{
int minutes = Mathf.FloorToInt(seconds / 60F);
int remainingSeconds = Mathf.FloorToInt(seconds - minutes * 60);
return string.Format("{0:00}:{1:00}", minutes, remainingSeconds);
}
// 平滑时间缩放
public static void SmoothTimeScale(float targetScale, float duration)
{
// 这里可以实现平滑的时间缩放过渡
}
// 检查是否为固定更新帧
public static bool IsFixedUpdateFrame()
{
return Time.frameCount % Mathf.RoundToInt(1f / Time.fixedDeltaTime) == 0;
}
}
// 状态枚举
public enum GameState
{
Playing,
Paused,
SlowMotion,
GameOver
}
}
Unity中的C#实践
Unity特有的C#模式
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class UnityCSharpPatterns : MonoBehaviour
{
// 单例模式
public class Singleton<T> where T : MonoBehaviour
{
private static T _instance;
private static readonly object _lock = new object();
public static T Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
GameObject singletonObject = new GameObject(typeof(T).Name);
_instance = singletonObject.AddComponent<T>();
}
}
}
return _instance;
}
}
protected virtual void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
}
}
// 对象池模式
public class ObjectPool<T> where T : Component
{
private Queue<T> pool;
private T prefab;
private Transform parent;
public ObjectPool(T prefab, int initialSize, Transform parent = null)
{
this.prefab = prefab;
this.parent = parent;
pool = new Queue<T>();
for (int i = 0; i < initialSize; i++)
{
CreateNewObject();
}
}
public T GetObject()
{
T obj;
if (pool.Count > 0)
{
obj = pool.Dequeue();
obj.gameObject.SetActive(true);
}
else
{
obj = CreateNewObject();
}
return obj;
}
public void ReturnObject(T obj)
{
obj.gameObject.SetActive(false);
obj.transform.SetParent(parent);
pool.Enqueue(obj);
}
private T CreateNewObject()
{
T newObj = GameObject.Instantiate(prefab);
newObj.transform.SetParent(parent);
newObj.gameObject.SetActive(false);
return newObj;
}
}
// 事件系统
public static class GameEvents
{
public static System.Action OnGameStart;
public static System.Action OnGameEnd;
public static System.Action<int> OnScoreChanged;
public static System.Action<GameObject> OnPlayerDeath;
public static System.Action<string, object> OnCustomEvent;
public static void TriggerGameStart()
{
OnGameStart?.Invoke();
}
public static void TriggerScoreChanged(int newScore)
{
OnScoreChanged?.Invoke(newScore);
}
public static void TriggerPlayerDeath(GameObject player)
{
OnPlayerDeath?.Invoke(player);
}
public static void TriggerCustomEvent(string eventName, object data)
{
OnCustomEvent?.Invoke(eventName, data);
}
}
// 状态机模式
public abstract class GameState
{
public abstract void Enter();
public abstract void Execute();
public abstract void Exit();
}
public class PlayState : GameState
{
public override void Enter()
{
Debug.Log("进入游戏状态");
}
public override void Execute()
{
// 游戏执行逻辑
}
public override void Exit()
{
Debug.Log("退出游戏状态");
}
}
public class PauseState : GameState
{
public override void Enter()
{
Debug.Log("进入暂停状态");
Time.timeScale = 0f;
}
public override void Execute()
{
// 暂停状态逻辑
}
public override void Exit()
{
Debug.Log("退出暂停状态");
Time.timeScale = 1f;
}
}
// 命令模式
public interface ICommand
{
void Execute();
void Undo();
}
public class MoveCommand : ICommand
{
private Transform transform;
private Vector3 startPosition;
private Vector3 endPosition;
public MoveCommand(Transform transform, Vector3 endPosition)
{
this.transform = transform;
this.startPosition = transform.position;
this.endPosition = endPosition;
}
public void Execute()
{
transform.position = endPosition;
}
public void Undo()
{
transform.position = startPosition;
}
}
// 观察者模式
public interface IObserver
{
void OnNotify(object data);
}
public class Subject
{
private List<IObserver> observers = new List<IObserver>();
public void Attach(IObserver observer)
{
observers.Add(observer);
}
public void Detach(IObserver observer)
{
observers.Remove(observer);
}
public void Notify(object data)
{
foreach (var observer in observers)
{
observer.OnNotify(data);
}
}
}
}
// 使用示例
public class GameSystemExample : MonoBehaviour
{
private UnityCSharpPatterns.ObjectPool<GameObject> objectPool;
private Queue<UnityCSharpPatterns.ICommand> commandHistory = new Queue<UnityCSharpPatterns.ICommand>();
void Start()
{
// 初始化对象池
GameObject prefab = GameObject.CreatePrimitive(PrimitiveType.Cube);
objectPool = new UnityCSharpPatterns.ObjectPool<GameObject>(prefab, 10, transform);
// 订阅游戏事件
UnityCSharpPatterns.GameEvents.OnScoreChanged += OnScoreChanged;
UnityCSharpPatterns.GameEvents.OnPlayerDeath += OnPlayerDeath;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// 从对象池获取对象
GameObject obj = objectPool.GetObject();
obj.transform.position = transform.position + Random.insideUnitSphere * 5f;
// 执行移动命令
UnityCSharpPatterns.MoveCommand moveCmd = new UnityCSharpPatterns.MoveCommand(
obj.transform, obj.transform.position + Vector3.up * 5f);
moveCmd.Execute();
commandHistory.Enqueue(moveCmd);
}
if (Input.GetKeyDown(KeyCode.Z) && Input.GetKey(KeyCode.LeftControl))
{
// 撤销命令
if (commandHistory.Count > 0)
{
UnityCSharpPatterns.ICommand lastCommand = commandHistory.Dequeue();
lastCommand.Undo();
}
}
}
void OnScoreChanged(int newScore)
{
Debug.Log($"分数更新: {newScore}");
}
void OnPlayerDeath(GameObject player)
{
Debug.Log($"玩家死亡: {player.name}");
// 将对象返回池中而不是销毁
StartCoroutine(ReturnToPoolAfterDelay(player));
}
IEnumerator ReturnToPoolAfterDelay(GameObject obj)
{
yield return new WaitForSeconds(1f);
// objectPool.ReturnObject(obj.GetComponent<GameObject>()); // 需要适当调整
}
void OnDestroy()
{
// 取消事件订阅
UnityCSharpPatterns.GameEvents.OnScoreChanged -= OnScoreChanged;
UnityCSharpPatterns.GameEvents.OnPlayerDeath -= OnPlayerDeath;
}
}
Unity协程和异步编程
using UnityEngine;
using System.Collections;
public class CoroutineAsyncDemo : MonoBehaviour
{
[Header("协程设置")]
public float waitDuration = 2.0f;
void Start()
{
// 启动各种协程
StartCoroutine(DelayedAction());
StartCoroutine(RepeatAction());
StartCoroutine(SequenceActions());
}
// 延迟执行协程
IEnumerator DelayedAction()
{
Debug.Log("开始等待...");
yield return new WaitForSeconds(waitDuration);
Debug.Log("等待结束,执行操作");
// 执行具体操作
PerformAction();
}
// 重复执行协程
IEnumerator RepeatAction()
{
while (true) // 注意:需要适当的退出条件
{
Debug.Log($"重复操作执行 - 时间: {Time.time:F2}");
yield return new WaitForSeconds(1.0f); // 每秒执行一次
}
}
// 序列执行多个操作
IEnumerator SequenceActions()
{
Debug.Log("序列开始");
yield return StartCoroutine(Action1());
yield return StartCoroutine(Action2());
yield return StartCoroutine(Action3());
Debug.Log("序列结束");
}
IEnumerator Action1()
{
Debug.Log("执行操作1");
yield return new WaitForSeconds(0.5f);
}
IEnumerator Action2()
{
Debug.Log("执行操作2");
yield return new WaitForSeconds(0.5f);
}
IEnumerator Action3()
{
Debug.Log("执行操作3");
yield return new WaitForSeconds(0.5f);
}
// 条件等待协程
IEnumerator WaitForCondition()
{
Debug.Log("等待条件满足...");
// 等待直到某个条件为真
while (transform.position.y < 5f)
{
yield return null; // 等待下一帧
}
Debug.Log("条件满足!");
}
// 等待动画结束
IEnumerator WaitForAnimation(Animator animator)
{
if (animator != null)
{
// 等待特定动画状态结束
while (animator.GetCurrentAnimatorStateInfo(0).normalizedTime < 1.0f)
{
yield return null;
}
Debug.Log("动画播放完成");
}
}
// 等待WWW/UnityWebRequest完成
/*
IEnumerator DownloadData(string url)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(url))
{
yield return webRequest.SendWebRequest();
if (webRequest.result == UnityWebRequest.Result.Success)
{
string response = webRequest.downloadHandler.text;
Debug.Log($"下载完成: {response}");
}
else
{
Debug.Log($"下载失败: {webRequest.error}");
}
}
}
*/
// 协程管理器
private List<Coroutine> activeCoroutines = new List<Coroutine>();
public Coroutine StartManagedCoroutine(IEnumerator routine)
{
Coroutine coroutine = StartCoroutine(routine);
activeCoroutines.Add(coroutine);
return coroutine;
}
public void StopManagedCoroutine(Coroutine coroutine)
{
if (activeCoroutines.Contains(coroutine))
{
StopCoroutine(coroutine);
activeCoroutines.Remove(coroutine);
}
}
public void StopAllManagedCoroutines()
{
foreach (Coroutine coroutine in activeCoroutines)
{
StopCoroutine(coroutine);
}
activeCoroutines.Clear();
}
// 性能优化的等待
void PerformanceOptimizedWaits()
{
// 不同的等待类型:
yield return null; // 等待下一帧
yield return new WaitForSeconds(1f); // 等待指定时间
yield return new WaitForSecondsRealtime(1f); // 真实时间等待(不受timeScale影响)
yield return new WaitForEndOfFrame(); // 等待渲染结束
yield return new WaitForFixedUpdate(); // 等待固定更新
}
void PerformAction()
{
// 具体的操作实现
transform.Rotate(Vector3.up, 45f);
}
void OnDestroy()
{
// 清理所有协程
StopAllManagedCoroutines();
}
}
// 异步方法示例(需要导入using System.Threading.Tasks;)
/*
public class AsyncMethodsDemo : MonoBehaviour
{
async void Start()
{
await LoadGameDataAsync();
await InitializeGameAsync();
StartGame();
}
async Task LoadGameDataAsync()
{
await Task.Delay(2000); // 模拟加载时间
Debug.Log("游戏数据加载完成");
}
async Task InitializeGameAsync()
{
await Task.Delay(1000); // 模拟初始化时间
Debug.Log("游戏初始化完成");
}
void StartGame()
{
Debug.Log("游戏开始!");
}
}
*/
Unity调试和性能优化
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class UnityDebugging : MonoBehaviour
{
[Header("调试设置")]
public bool enableDebug = true;
public bool showGizmos = true;
public Color gizmoColor = Color.yellow;
[Header("性能监控")]
public bool enablePerformanceMonitor = true;
private float lastUpdateTime = 0f;
private int frameCount = 0;
private float fps = 0f;
void Update()
{
if (enablePerformanceMonitor)
{
UpdatePerformanceStats();
}
}
void UpdatePerformanceStats()
{
frameCount++;
if (Time.time >= lastUpdateTime + 1f)
{
fps = frameCount / (Time.time - lastUpdateTime);
frameCount = 0;
lastUpdateTime = Time.time;
if (enableDebug)
{
Debug.Log($"FPS: {fps:F1}");
}
}
}
// 调试方法
void DebuggingMethods()
{
if (!enableDebug) return;
// 不同类型的调试信息
Debug.Log("普通信息");
Debug.LogWarning("警告信息");
Debug.LogError("错误信息");
// 条件调试
if (transform.position.y < 0)
{
Debug.LogError($"对象掉落到地下: {transform.position}");
}
// 断言
Debug.Assert(transform != null, "Transform不能为空");
// 绘制调试线
Debug.DrawLine(transform.position, transform.position + transform.forward * 5f, Color.blue);
// 绘制射线
Debug.DrawRay(transform.position, transform.forward * 10f, Color.red);
}
// Gizmo绘制
void OnDrawGizmos()
{
if (!showGizmos) return;
Gizmos.color = gizmoColor;
// 绘制对象的范围
Gizmos.DrawWireSphere(transform.position, 1f);
// 绘制朝向
Gizmos.DrawLine(transform.position, transform.position + transform.forward * 3f);
// 绘制边界框
Gizmos.DrawWireCube(transform.position, Vector3.one * 2f);
}
// 性能优化技巧
void PerformanceOptimizations()
{
// 1. 缓存组件引用
// 而不是每次Update都GetComponent
Rigidbody rb = GetComponent<Rigidbody>(); // 在Start或Awake中获取
// 2. 对象池
// 而不是频繁创建销毁对象
// 3. 减少Update中的计算
// 使用计时器减少执行频率
if (Time.time - lastUpdateTime > 0.1f)
{
// 执行不需要每帧执行的操作
lastUpdateTime = Time.time;
}
// 4. 使用对象引用而不是字符串查找
// GameObject.Find很慢,缓存引用更好
// 5. 优化循环
// 缓存数组长度
Transform[] children = new Transform[transform.childCount];
for (int i = 0; i < transform.childCount; i++)
{
children[i] = transform.GetChild(i);
}
}
// 内存管理
void MemoryManagement()
{
// 及时清理引用
// GameObject.Destroy(object); // Unity会自动处理
// 清理事件订阅
// eventHandler -= handlerMethod;
// 清理协程
// StopCoroutine(coroutineName);
}
}
// 编辑器专用调试工具
#if UNITY_EDITOR
[CustomEditor(typeof(UnityDebugging))]
public class UnityDebuggingEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
UnityDebugging debugScript = (UnityDebugging)target;
if (GUILayout.Button("Force GC"))
{
System.GC.Collect();
}
if (GUILayout.Button("Log Scene Info"))
{
Debug.Log($"Active Scene: {UnityEngine.SceneManagement.SceneManager.GetActiveScene().name}");
Debug.Log($"Loaded Scenes: {UnityEngine.SceneManagement.SceneManager.sceneCount}");
}
}
}
#endif
实践练习
练习1: 简单的玩家控制器
using UnityEngine;
public class SimplePlayerController : MonoBehaviour
{
[Header("移动设置")]
public float moveSpeed = 5.0f;
public float jumpForce = 10.0f;
public float gravity = -9.81f;
[Header("地面检测")]
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
private CharacterController controller;
private Vector3 velocity;
private bool isGrounded;
void Start()
{
controller = GetComponent<CharacterController>();
// 设置地面检测点
if (groundCheck == null)
{
groundCheck = new GameObject("GroundCheck").transform;
groundCheck.SetParent(transform);
groundCheck.localPosition = new Vector3(0, -transform.localScale.y / 2 - 0.1f, 0);
}
}
void Update()
{
// 地面检测
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
// 移动输入
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * moveSpeed * Time.deltaTime);
// 跳跃
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpForce * -2f * gravity);
}
// 重力
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
// 地面检测Gizmo
void OnDrawGizmosSelected()
{
if (groundCheck != null)
{
Gizmos.color = isGrounded ? Color.green : Color.red;
Gizmos.DrawSphere(groundCheck.position, groundDistance);
}
}
}
练习2: 敌人AI系统
using UnityEngine;
using System.Collections;
public class EnemyAI : MonoBehaviour
{
[Header("AI设置")]
public float detectionRange = 10f;
public float attackRange = 2f;
public float moveSpeed = 3f;
public float rotationSpeed = 5f;
public int damage = 20;
public float health = 100f;
[Header("巡逻设置")]
public Transform[] patrolPoints;
public float waitTime = 2f;
public bool isPatrolling = true;
private Transform player;
private int currentPatrolIndex = 0;
private bool isChasing = false;
private bool isAttacking = false;
void Start()
{
// 查找玩家
GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
if (playerObj != null)
{
player = playerObj.transform;
}
if (isPatrolling && patrolPoints.Length > 0)
{
StartCoroutine(Patrol());
}
}
void Update()
{
if (health <= 0)
{
Die();
return;
}
if (player == null) return;
float distanceToPlayer = Vector3.Distance(transform.position, player.position);
if (distanceToPlayer <= detectionRange)
{
isChasing = true;
StopCoroutine(Patrol());
if (distanceToPlayer <= attackRange)
{
if (!isAttacking)
{
StartCoroutine(Attack());
}
}
else
{
isAttacking = false;
ChasePlayer();
}
}
else
{
isChasing = false;
if (isPatrolling && patrolPoints.Length > 0 && !isAttacking)
{
StartCoroutine(Patrol());
}
}
}
void ChasePlayer()
{
Vector3 direction = (player.position - transform.position).normalized;
transform.Translate(direction * moveSpeed * Time.deltaTime);
// 朝向玩家
Vector3 lookDirection = new Vector3(player.position.x, transform.position.y, player.position.z);
transform.LookAt(lookDirection);
}
IEnumerator Attack()
{
isAttacking = true;
// 攻击动画/效果
Debug.Log($"{gameObject.name} 攻击玩家!");
// 对玩家造成伤害(这里简化处理)
if (player.GetComponent<SimplePlayerController>() != null)
{
// 实际伤害逻辑
}
yield return new WaitForSeconds(1f); // 攻击间隔
isAttacking = false;
}
IEnumerator Patrol()
{
while (!isChasing && patrolPoints.Length > 0)
{
Transform targetPoint = patrolPoints[currentPatrolIndex];
if (Vector3.Distance(transform.position, targetPoint.position) < 0.5f)
{
yield return new WaitForSeconds(waitTime);
currentPatrolIndex = (currentPatrolIndex + 1) % patrolPoints.Length;
}
else
{
Vector3 direction = (targetPoint.position - transform.position).normalized;
transform.Translate(direction * moveSpeed * Time.deltaTime);
transform.LookAt(new Vector3(targetPoint.position.x, transform.position.y, targetPoint.position.z));
}
yield return null;
}
}
public void TakeDamage(float damage)
{
health -= damage;
Debug.Log($"{gameObject.name} 受到 {damage} 点伤害,剩余生命: {health}");
if (health <= 0)
{
Die();
}
}
void Die()
{
Debug.Log($"{gameObject.name} 被击败!");
Destroy(gameObject);
}
// 可视化AI范围
void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, detectionRange);
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackRange);
// 巡逻路径
if (patrolPoints != null && patrolPoints.Length > 1)
{
Gizmos.color = Color.blue;
for (int i = 0; i < patrolPoints.Length; i++)
{
if (patrolPoints[i] != null)
{
Gizmos.DrawSphere(patrolPoints[i].position, 0.3f);
if (i > 0)
{
Gizmos.DrawLine(patrolPoints[i-1].position, patrolPoints[i].position);
}
}
}
// 闭合路径
if (patrolPoints.Length > 2 && patrolPoints[0] != null)
{
Gizmos.DrawLine(patrolPoints[patrolPoints.Length-1].position, patrolPoints[0].position);
}
}
}
}
常见错误和最佳实践
1. 性能相关错误
public class PerformanceBestPractices : MonoBehaviour
{
// ❌ 错误:在Update中进行昂贵操作
/*
void Update()
{
GameObject player = GameObject.Find("Player"); // 很慢!
Transform target = GameObject.FindGameObjectWithTag("Target").transform; // 很慢!
}
*/
// ✅ 正确:缓存引用
private Transform playerTransform;
private GameObject[] enemies;
void Start()
{
// 缓存引用
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
playerTransform = player.transform;
}
// 一次性获取所有敌人
enemies = GameObject.FindGameObjectsWithTag("Enemy");
}
void Update()
{
// 使用缓存的引用
if (playerTransform != null)
{
// 操作playerTransform
}
}
// ❌ 错误:频繁创建对象
/*
void Update()
{
GameObject effect = new GameObject(); // 每帧创建新对象
Destroy(effect, 1f); // 造成GC压力
}
*/
// ✅ 正确:使用对象池
// 使用前面定义的对象池模式
// ❌ 错误:在循环中进行昂贵操作
/*
void Update()
{
for (int i = 0; i < transform.childCount; i++)
{
// GetComponent在循环中很昂贵
var script = transform.GetChild(i).GetComponent<SomeComponent>();
}
}
*/
// ✅ 正确:预先获取引用
private SomeComponent[] childComponents;
void CacheComponents()
{
childComponents = new SomeComponent[transform.childCount];
for (int i = 0; i < transform.childCount; i++)
{
childComponents[i] = transform.GetChild(i).GetComponent<SomeComponent>();
}
}
}
// SomeComponent的占位符
public class SomeComponent : MonoBehaviour { }
2. 生命周期管理错误
public class LifecycleManagement : MonoBehaviour
{
private Coroutine animationCoroutine;
private System.Action eventCallback;
void Start()
{
// 订阅事件
SomeEventManager.OnEvent += HandleEvent;
}
// ✅ 正确:在OnDestroy中清理
void OnDestroy()
{
// 取消事件订阅
SomeEventManager.OnEvent -= HandleEvent;
// 停止协程
if (animationCoroutine != null)
{
StopCoroutine(animationCoroutine);
}
// 清理回调
eventCallback = null;
}
void HandleEvent()
{
// 事件处理逻辑
}
}
// 事件管理器占位符
public static class SomeEventManager
{
public static event System.Action OnEvent;
}
3. Transform操作最佳实践
public class TransformBestPractices : MonoBehaviour
{
// ✅ 预先缓存常用的向量
private Vector3 tempVector3 = Vector3.zero;
private Quaternion tempQuaternion = Quaternion.identity;
void Update()
{
// ✅ 使用缓存的向量
tempVector3.Set(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
transform.Translate(tempVector3 * Time.deltaTime);
// ✅ 使用静态方法而不是重复创建向量
transform.position = Vector3.Lerp(
transform.position,
GetTargetPosition(),
Time.deltaTime * 5f
);
}
Vector3 GetTargetPosition()
{
return Vector3.zero; // 示例目标位置
}
}
总结
本章我们学习了Unity引擎的基础知识:
✅ Unity环境: 编辑器界面、项目结构、基本操作
✅ MonoBehaviour生命周期: Awake、Start、Update、FixedUpdate、LateUpdate等方法的使用时机
✅ 场景和游戏对象: GameObject操作、场景管理、预制件系统
✅ 组件系统: 组件获取、通信、属性面板自定义
✅ Transform变换: 位置、旋转、缩放操作和空间变换
✅ 输入系统: 键盘、鼠标、手柄、触摸输入处理
✅ Time系统: 时间管理、帧率控制、时间缩放
✅ Unity中的C#实践: 设计模式、协程、调试技巧
Unity为C#开发者提供了强大的游戏开发平台,掌握这些基础知识是进行游戏开发的必要前提。
下一步
继续学习 07. 游戏开发实战 →