目录
简要概括
正在运行的Unity游戏就可以看做一个进程的实例,线程是进程内的执行单元(一个进程可以拥有多个线程),Unity游戏中的详细操作是在同一个进程里的中执行的。
名词解释
实例
“实例”指具体的实际存在的单个对象或者事物。在计算机中,进程就是程序的实例,也就是指程序在某个时间段内的运行过程。
进程
进程(Process)是计算机中正在运行的一个程序或任务的实例。在操作系统中,进程是资源分配的基本单位,每个进程都拥有自己独立的内存空间和系统资源,包括CPU、内存、文件、网络连接等。进程的出现是为了解决多道程序同时运行的冲突问题,通过将系统资源分配给多个进程,提高了计算机系统的效率和可用性。
每个进程都有自己的地址空间,包括代码段、数据段、堆和栈。堆和栈是两种不同的内存分配方式,堆用于动态分配内存,而栈则用于管理函数调用和变量分配。进程之间是相互独立的,可以并行运行,并且通过IPC和共享内存等方式进行通信和数据交换。
操作系统通过进程调度算法决定何时和如何切换进程,以保证每个进程都能得到足够的运行时间和系统资源,并且根据进程的优先级和状态等进行调度。进程的状态通常包括运行态、就绪态、等待态和终止态等。操作系统通过进程管理来保证系统的稳定性和安全性,防止进程之间的冲突和竞争,提高了计算机系统的效率和可靠性。
线程
线程是进程内的执行单元,一个进程可以拥有多个线程,每个线程都是独立的执行流。在Unity的代码中,update是在主线程中被执行的,不是在单独的线程中,而销毁物品等操作也是在主线程中执行的。
对象池
为了减少线程压力而创造的方法。
实际使用
对象池实际操作(含代码)
简单来说,对象池就是个含有list的类,而list也就是所谓的对象池的“池子”。
将需要操作的物品放在list中,然后隐藏。
需要使用对象的时候从对象池拿出对象,也就是显示物品。
不需要的时候放回,也就是隐藏物品。
彻底不需要时销毁(destroy),释放内存,并将对象从池子中移除。
对象池:
public class EnemyPool
{
private List<GameObject> enemyPool;
private GameObject enemyPrefab;
private int poolSize;
//创建对象池;更新对象池对象
public EnemyPool(GameObject prefab, int size)
{
enemyPrefab = prefab;
poolSize = size;
enemyPool = new List<GameObject> ();
for (int i = 0; i < poolSize; i++) {
GameObject enemy = GameObject.Instantiate(enemyPrefab) as GameObject;
enemy.SetActive(false);
enemyPool.Add(enemy);
}
}
//从对象池中拿出对象
public GameObject GetEnemy()
{
foreach (GameObject enemy in enemyPool) {
if (!enemy.activeInHierarchy) {
enemy.SetActive(true);
return enemy;
}
}
GameObject newEnemy = GameObject.Instantiate(enemyPrefab) as GameObject;
newEnemy.SetActive(true);
enemyPool.Add(newEnemy);
return newEnemy;
}
//放回对象
public void ReleaseEnemy(GameObject enemy)
{
enemy.SetActive(false);
}
}
实际使用:
public class GameManager : MonoBehaviour
{
public GameObject enemyPrefab;
public int enemyPoolSize;
private EnemyPool enemyPool;
private void Start ()
{
enemyPool = new EnemyPool(enemyPrefab, enemyPoolSize);
}
private void Update ()
{
//从对象池中获取敌人对象
GameObject enemy = enemyPool.GetEnemy();
//对敌人对象进行操作
enemy.transform.position += Vector3.forward * Time.deltaTime;
//将敌人对象放回对象池中
enemyPool.ReleaseEnemy(enemy);
}
}
为什么要用对象池?
对象池可以使应用程序更高效地利用有限的线程资源,相比起“在编辑器里就把所有物品放到场景中”和“在游戏开始时实例化所有物品(这个方法会让游戏卡顿)”,对象池能动态操控物品的出现和隐藏。
线程效率受内存限制,而物体的实例化、销毁、隐藏显示都在线程中进行,为了节省资源,我们要尽量减少一次性的操作数量。
使用对象池,需要考虑的东西:物品创建和销毁的时机
物品创建时机:可以在游戏加载时一次性创建所有需要的物品对象,或者等到玩家进入游戏世界后,根据需要动态创建。
物品销毁时机:当物品离开玩家的视野范围时,可以将其销毁,或者等到玩家不再需要该物品时再销毁。
对象池的大小:需要考虑在游戏中最大需要创建的物品数量,确定对象池的大小。对象池的大小应该不要过小,否则可能会频繁地创建和销毁物品对象,造成性能损失。(不过用list就不用考虑这一点了,使用数组才需要考虑,用list只需要在不需要物品时及时把物品移出对象池)
对象池的回收和重复利用:一旦物品被销毁,其所占用的资源应该被释放,并将该物品对象重新放回对象池中,以便下次重复利用。
建立主线程以外的线程方法
1.Thread类
示例:
using System;
using System.Threading;
public class Program
{
static void Main(string[] args)
{
// 创建一个新线程
Thread newThread = new Thread(Compute);
// 启动线程
newThread.Start();
// 在主线程中执行一些其他操作
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Main thread is running.");
Thread.Sleep(100);
}
}
static void Compute()
{
// 在新线程中执行一些计算密集型的操作
for (int i = 0; i < 10; i++)
{
Console.WriteLine("New thread is running.");
Thread.Sleep(100);
}
}
}
2.异步
3.协程(Coroutine类)
4.Job System
Job System是Unity自带的,可以使游戏逻辑转移到多线程中执行。使用Job、JobHandle和JobQueue等组件来管理和调度任务。
5.Entity Component System (ECS)
ECS是Unity中的一种数据驱动的编程模式,可以将游戏逻辑从主线程转移到其他线程中执行,并行处理大量数据。
6.Native Plugin
使用Native Plugin可以在C++或其他编程语言中编写独立于Unity的多线程代码,然后将其集成到Unity中。使用该方法可以获得更高的性能和更灵活的控制。
在主线程中运行的生命周期
- Awake()
- Start()
- Update()
- FixedUpdate()
- LateUpdate()
- OnGUI()
- OnDisable()
- OnEnable()
- OnDestroy()
在副线程中运行的生命周期
- OnApplicationFocus()
- OnApplicationPause()
- OnApplicationQuit()