8.2.3 ?頻管理器
????????在實(shí)際游戲開(kāi)發(fā)中,?效既是?個(gè)相對(duì)獨(dú)?的部分,?與其他游戲邏輯密切關(guān)聯(lián)。也就是說(shuō),與?效相關(guān)的代碼會(huì)插?很多細(xì)節(jié)代碼中。
?????????且在?效?常豐富的情況下,如果每?個(gè)游戲模塊都單獨(dú)播放?效,那么可能會(huì)帶來(lái)?些問(wèn)題。例如,Audio Source組件很多,但?部分閑置,同時(shí)播放的?效太多會(huì)顯得混亂。
????????成熟的技術(shù)開(kāi)發(fā)者的思路是:如果?效不多、沒(méi)有造成問(wèn)題,則完全可以簡(jiǎn)單處理;?如果?效已經(jīng)引起了代碼的混亂和性能問(wèn)題,就有必要統(tǒng)?管理所有的?源和?效,也就是設(shè)計(jì)?個(gè)易?的?頻管理器。有了?頻管理器,所有的Audio Source組件都會(huì)統(tǒng)?創(chuàng)建,?所有?效播放的需求都要通過(guò)調(diào)??頻管理器的?法間接實(shí)現(xiàn)。
?????????頻管理器有很多設(shè)計(jì)思路,其中?種?較簡(jiǎn)潔的思路是,事先指定游戲中最多同時(shí)播放多少個(gè)?頻,然后創(chuàng)建若?個(gè)?源。例如,最多播放8個(gè)?頻,那么就創(chuàng)建8個(gè)Audio Source組件,這8個(gè)AudioSource組件可以看作8個(gè)頻道。需要播放?頻時(shí),只要找到任意?個(gè)空閑的頻道播放即可;?如果8個(gè)頻道都正在播放,那么就可以?某種策略替換?頻(如將播放時(shí)間最早的?頻替換成新的?頻)。?這種簡(jiǎn)單的思路創(chuàng)建?頻管理器的代碼如下。
using UnityEngine;
// ?頻管理器
public class AudioManager : MonoBehaviour
{
// 整個(gè)游戲中,總的?源(頻道)數(shù)量
private const int AUDIO _ CHANNEL _ NUM = 8;
private struct CHANNEL
{
public AudioSource channel;
public float keyOnTime; // 記錄最近?次播放?樂(lè)的時(shí)刻
};
private CHANNEL[] m _ channels;
void Awake()
{
m _ channels = new CHANNEL[AUDIO _ CHANNEL _ NUM];
for (int i = 0; i < AUDIO _ CHANNEL _ NUM; i++)
{
// 每個(gè)頻道對(duì)應(yīng)?個(gè)?源
m _ channels[i].channel =
gameObject.AddComponent<AudioSource>();
m _ channels[i].keyOnTime = 0;
}
}
// 公開(kāi)?法:播放?次。參數(shù)為?頻?段、?量、左右聲道、速度
// 這個(gè)?法主要?于?效,因此考慮了?效頂替的邏輯
public int PlayOneShot(AudioClip clip, float volume,
float pan, float pitch =
1.0f)
{
for (int i = 0; i < m _ channels.Length; i++)
{
// 如果正在播放同?個(gè)?段,?且剛剛才開(kāi)始,則直接退出函數(shù)
if (m _ channels[i].channel.isPlaying &&
m _ channels[i].channel.clip == clip &&
m _ channels[i].keyOnTime >= Time.time -
0.03f)
return -1;
}
// 遍歷所有頻道,如果有頻道空閑直接播放新?頻,并退出
// 如果沒(méi)有空閑頻道,先找到最早開(kāi)始播放的頻道(oldest),稍后
使?
int oldest = -1;
float time = 1000000000.0f;
for (int i = 0; i < m _ channels.Length; i++)
{
if (m _ channels[i].channel.loop == false &&
m _ channels[i].channel.isPlaying &&
m _ channels[i].keyOnTime < time)
{
oldest = i;
time = m _ channels[i].keyOnTime;
}
if (!m _ channels[i].channel.isPlaying)
{
m _ channels[i].channel.clip = clip;
m _ channels[i].channel.volume = volume;
m _ channels[i].channel.panStereo = pan;
m _ channels[i].channel.loop = false;
m _ channels[i].channel.pitch = pitch;
m _ channels[i].channel.Play();
m _ channels[i].keyOnTime = Time.time;
return i;
}
}
// 運(yùn)?到這?說(shuō)明沒(méi)有空閑頻道。讓新的?頻頂替最早播出的?頻
if (oldest >= 0)
{
m _ channels[oldest].channel.clip = clip;m _ channels[oldest].channel.volume = volume;
m _ channels[oldest].channel.panStereo = pan;
m _ channels[oldest].channel.loop = false;
m _ channels[oldest].channel.pitch = pitch;
m _ channels[oldest].channel.Play();
m _ channels[oldest].keyOnTime = Time.time;
return oldest;
}
return -1;
}
// 公開(kāi)?法:循環(huán)播放。?于播放?時(shí)間的背景?樂(lè),處理?式相對(duì)簡(jiǎn)單?
些
public int PlayLoop(AudioClip clip, float volume, float
pan, float pitch = 1.0f)
{
for (int i = 0; i < m _ channels.Length; i++)
{
if (!m _ channels[i].channel.isPlaying)
{
m _ channels[i].channel.clip = clip;
m _ channels[i].channel.volume = volume;
m _ channels[i].channel.panStereo = pan;
m _ channels[i].channel.loop = true;
m _ channels[i].channel.pitch = pitch;
m _ channels[i].channel.Play();
m _ channels[i].keyOnTime = Time.time;
return i;
}
}
return -1;
}
// 公開(kāi)?法:停?所有?頻
public void StopAll()
{
foreach (CHANNEL channel in m _ channels)
channel.channel.Stop();
}
// 公開(kāi)?法:根據(jù)頻道ID停??頻
public void Stop(int id)
{
if (id >= 0 && id < m _ channels.Length)
{
m _ channels[id].channel.Stop();
}}
}
????????以上代碼可以作為創(chuàng)建?頻管理器的?種思路參考。實(shí)際上,根據(jù)游戲類型的不同,?頻管理器的創(chuàng)建思路也有區(qū)別。例如,在很多3D游戲中,需要考慮?效播放的空間位置(?的是營(yíng)造真實(shí)感),這時(shí)統(tǒng)?創(chuàng)建?源就不是很合適了。
第9章 腳本與資源管理
????????由于游戲中包含?量美術(shù)、?樂(lè)、數(shù)據(jù)等類型的資源,因此資源管理?直是游戲開(kāi)發(fā)的重點(diǎn)技術(shù)。游戲中的資源管理包含了多重含義,如靜態(tài)資源的組織?式,資源的打包和壓縮,資源何時(shí)加載到內(nèi)存、何時(shí)釋放都屬于資源管理的范疇。本章主要講解?些與資源管理相關(guān)的基本概念。
9.1 ?程與資源
????????之前的章節(jié)已經(jīng)介紹了多個(gè)Unity實(shí)踐?程,但我們還沒(méi)有仔細(xì)了解過(guò)這些?程的?件夾結(jié)構(gòu)。本節(jié)將詳細(xì)講解Unity?程的?件夾結(jié)構(gòu),以及動(dòng)態(tài)加載資源的技術(shù)要點(diǎn)。
9.1.1 Unity項(xiàng)?的?件夾結(jié)構(gòu)
1. ?程?件夾
在新建?程時(shí),Unity會(huì)創(chuàng)建所有必要的?件夾。第?級(jí)?件夾如圖9-1所?。
????????Assets(資產(chǎn))是最主要的?件夾,保存著所有游戲?到的資產(chǎn)。Library(庫(kù))?件夾?于存放引擎必需的程序集和緩存資源。Library不存在時(shí)會(huì)?動(dòng)?成,不需要也不建議上傳到版本倉(cāng)庫(kù)(如SVN或Git倉(cāng)庫(kù))中去。
????????Logs(?志)?件夾?于存放使?時(shí)產(chǎn)?的?志。?前?部分Unity的官?功能擴(kuò)展都通過(guò)擴(kuò)展包提供,如PostProcessing(后期處理)、Text Mesh Pro(?級(jí)?本渲染)等常?的擴(kuò)展包。Packages(包)?件夾雖與擴(kuò)展包有關(guān),但??只保存配置?件。
????????所有的?程設(shè)置,包括?程對(duì)應(yīng)的Unity版本都在ProjectSettings(?程設(shè)置)?件夾中。不能直接改動(dòng)該?件夾中的內(nèi)容,不然會(huì)造成版本兼容性的問(wèn)題。
????????如果該?程正在被編輯,則會(huì)多出?個(gè)Temp(臨時(shí))?件夾,?旦關(guān)閉?程,該?件夾會(huì)?動(dòng)消失。不要上傳Temp?件夾到版本倉(cāng)庫(kù)。
2. 資產(chǎn)?件夾
????????由于Asset和Resource的含義相近,?且它們?cè)赨nity中都有特定的含義,因此翻譯時(shí)將Asset稱為資產(chǎn),Resource稱為資源,以?區(qū)分。Assets?件夾下所有的?件都是資產(chǎn)的?部分,但某?些資產(chǎn)不會(huì)被“打包”到最終發(fā)布的程序中,?其他資產(chǎn)則會(huì)被“打包”。
?提?
“打包”是指包含在發(fā)布版本中“打包”?詞也有多種含義,如有時(shí)是指將多個(gè)?件打成壓縮包。這?的“打包”特指將資產(chǎn)放?最終發(fā)布版程序中。?個(gè)?件會(huì)被打包,是指它會(huì)成為發(fā)布版的?部分,?不打包則是指它不會(huì)進(jìn)?最終發(fā)布的程序,僅在開(kāi)發(fā)階段使?。被打包的資源可能會(huì)被壓縮,也可能不會(huì)被壓縮。要理解Assets?件夾的結(jié)構(gòu),?先要了解Assets?件夾下的?個(gè)特殊?件夾,如表9-1所?。表9-1?Assets?件夾下的特殊?件夾說(shuō)明
????????除以上特殊?件夾,在其他?特殊?件夾中的資產(chǎn),Unity會(huì)根據(jù)是否引?了該資源?決定是否打包。所有?編輯器專?的腳本資產(chǎn)?件都會(huì)被打包。這是由于?組件腳本也可能會(huì)被引?,不能依據(jù)是否掛載到物體上來(lái)確定?個(gè)腳本是否被?到。
????????被打包的資產(chǎn)都可以看作是發(fā)布的程序的?部分,但它們都是只讀的,不能在運(yùn)?時(shí)改寫(xiě)它們。換句話說(shuō),以上?件夾都不能?于做熱更新,熱更新的概念在后?會(huì)提到。
?提?
????????為什么資源?定要有組織如果直接使?原始資源?件,那么就只能通過(guò)路徑和?件名定位資源,這會(huì)帶來(lái)很多?煩。?先,資源不能隨意更改路徑,?旦移動(dòng)到其他?件夾,就會(huì)找不到資源。其次,不容易確定資源之間的引?關(guān)系。例如,預(yù)置體資源A?到了預(yù)制體B和模型C,引擎就必須建?它們之間的聯(lián)系,?且它們之間的引?信息在打包和游戲運(yùn)?時(shí)都需要?到。
9.1.2 META?件
????????在游戲的開(kāi)發(fā)階段會(huì)存在?量原始的資源和素材,如何管理它們是引擎需要考慮的。市?上的游戲引擎對(duì)原始資源的管理有以下兩種主流?案。
????????第1種,引擎統(tǒng)?打包和管理所有資產(chǎn)。添加新資源時(shí),通過(guò)統(tǒng)?的導(dǎo)?流程打包到專門的?件中,原始?件不再使?。第2種,雖然引擎管理所有資產(chǎn),但依然會(huì)使?原始資源?件。?些必要的信息(如模型的導(dǎo)?設(shè)置)會(huì)寫(xiě)在另外的配置?件中。
?????????論哪種?案,都必須對(duì)所有資產(chǎn)統(tǒng)?管理,?不能使?未處理、?記錄的原始資源。
Unity所采?的?案屬于上?第2種,它會(huì)對(duì)Assets?件夾下的所有?件?成?個(gè)名稱相同,擴(kuò)展名為meta的?件,包括?件夾也會(huì)?成對(duì)應(yīng)的META?件。META?件是?個(gè)?本?件,??記錄了很多必要的信息,包括資產(chǎn)唯?標(biāo)識(shí)符GUID、引?關(guān)系和資源導(dǎo)?設(shè)置的信息等。
????????其中資產(chǎn)唯?標(biāo)識(shí)符GUID?常重要,它會(huì)在資源初次導(dǎo)?時(shí)?成,有了它就能準(zhǔn)確定位資源?件,?件的改名、移動(dòng)和內(nèi)容修改都不會(huì)使GUID變化。
?提?
在Unity之外修改?件要注意Unity的Project窗?的功能?全,?持?件的移動(dòng)、重命名和復(fù)制等操作。關(guān)鍵是,在Project窗?中進(jìn)??件操作,Unity會(huì)妥善處理這些改動(dòng)。?如果在Unity之外操作,很可能會(huì)因找不到對(duì)應(yīng)的META?件?失去對(duì)應(yīng)關(guān)系。很多時(shí)候材質(zhì)丟失、導(dǎo)?信息被重置和引?丟失等問(wèn)題,都是不恰當(dāng)?shù)?件操作引起的。腳本的META?件內(nèi)容通常?較簡(jiǎn)單,只有???,?某些資源(如3D模型動(dòng)畫(huà))往往有上千?,??記錄了必要的設(shè)置信息。以下是?個(gè)腳本?件對(duì)應(yīng)的META?件的內(nèi)容。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-856944.html
fileFormatVersion: 2 ?件格式版本
guid: 42850487fa989b342bcb1cb935a1f43d 資源唯?標(biāo)識(shí)符GUID
MonoImporter: 腳本導(dǎo)?信息
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0} 圖標(biāo)
userData: ?定義數(shù)據(jù)
assetBundleName: Asset Bundle(資源包)名稱
assetBundleVariant: 資源包參數(shù)
????????理解了META?件的重要性,在實(shí)際?作中還要注意以下?點(diǎn)。?是META?件與原始資源?件要?起管理。例如,新增Assets?件或?件夾時(shí),?定要連同?成的META?件?同提交到版本倉(cāng)庫(kù)。?是重命名和移動(dòng)?件要在Unity內(nèi)進(jìn)?,這樣可以保證相應(yīng)的META?件?動(dòng)完成相應(yīng)操作。
????????三是不能直接復(fù)制META?件,否則會(huì)導(dǎo)致GUID重復(fù)。復(fù)制資產(chǎn)時(shí)應(yīng)盡量在Unity內(nèi)?復(fù)制命令(快捷鍵Ctrl+D)進(jìn)?,這樣會(huì)?動(dòng)?成GUID不同的META?件。
????????四是?腳本操作資產(chǎn)時(shí)要注意META?件的同步,盡量使?Unity提供的API,?不要使?原始的?件進(jìn)?讀寫(xiě)操作。這?點(diǎn)主要針對(duì)編輯器腳本,因?yàn)榫庉嬈髂_本有時(shí)會(huì)修改資源?件的內(nèi)容。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-856944.html
到了這里,關(guān)于Unity 3D腳本編程與游戲開(kāi)發(fā)【4.2】的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!