3D俯視角射擊——用Unity還原東方彈幕(上)

作者:QXYO
前言
之前我們的專(zhuān)欄中介紹過(guò)2D的俯視角射擊,這次就來(lái)試試在3D場(chǎng)景下的實(shí)現(xiàn)。移動(dòng)射擊的實(shí)現(xiàn)方法差不多,所以本次的主要目標(biāo)是在3D場(chǎng)景下還原東方的符卡(彈幕)效果。
我們先來(lái)看看最終結(jié)果:


本文主要內(nèi)容:
1. 3D場(chǎng)景俯視角射擊
2. 可配置化彈幕
3. 還原東方彈幕
注:為了提升表現(xiàn)效果而使用的模型動(dòng)畫(huà)、屏幕后效(PostProcessing)等方法不在本次教程范圍內(nèi)。同時(shí)本文只寫(xiě)出了部分關(guān)鍵代碼,完整項(xiàng)目下載詳見(jiàn)本文末尾。
一、3D場(chǎng)景俯視角移動(dòng)
實(shí)現(xiàn)目標(biāo):鍵盤(pán)移動(dòng),鼠標(biāo)射擊。不射擊時(shí)面向移動(dòng)方向,射擊時(shí)面向射擊方向,轉(zhuǎn)向有轉(zhuǎn)向速度,射擊方向?yàn)槭髽?biāo)點(diǎn)擊方向。
在之前2D俯視角射擊中已經(jīng)介紹過(guò)移動(dòng)的方法,這里就不再贅述,想要了解的同學(xué)可參考:
【反向元?dú)怛T士】用unity實(shí)現(xiàn)俯視角射擊是一種怎樣的體驗(yàn)
當(dāng)然3D人物轉(zhuǎn)向應(yīng)需要轉(zhuǎn)向速度,否則會(huì)顯得很突兀,可用 Quaternion.LookRotation控制轉(zhuǎn)向, Quaternion.Slerp控制轉(zhuǎn)向速度。
二、彈幕可配置化
實(shí)現(xiàn)目標(biāo):通過(guò)修改Inspector面板中的參數(shù),即可實(shí)現(xiàn)簡(jiǎn)單的彈幕樣式。
本次主要實(shí)現(xiàn)兩種彈幕樣式,散射彈幕和平行彈幕。實(shí)現(xiàn)這兩種樣式需要的參數(shù)(BulletData)如下。
??? public int Count = 1; //一次生成的子彈的數(shù)量
??? public float LifeTime = 4f; //子彈生命周期
??? public float CdTime = 0.1f; //子彈間隔時(shí)間
??? public float Speed = 10; //子彈移動(dòng)速度
??? public float Angle = 0; //相鄰子彈間的旋轉(zhuǎn)角度
??? public float Distance = 0; // 相鄰子彈的間隔
??? public Color BulletColor = Color.white; //子彈的顏色
??? public Vector3 R_Offset = Vector3.zero; //初始旋轉(zhuǎn)的偏移量
??? public Vector3 P_Offset = Vector3.zero;? //位置的偏移量
?
首先創(chuàng)建個(gè)球體作為子彈預(yù)制體,OnEnable時(shí),在FixedUpdate實(shí)現(xiàn)移動(dòng),當(dāng)規(guī)定了子彈的位置和方向,子彈就會(huì)朝著正前方移動(dòng)。
transform.position = transform.position + transform.forward * BulletSpeed * Time.fixedDeltaTime;
?
然后是通過(guò)設(shè)定的參數(shù)實(shí)現(xiàn)彈幕樣式:散射彈幕,如下圖所示,以角色正前方為初始方向,當(dāng)單次彈幕數(shù)量為奇數(shù)或是偶數(shù)時(shí),子彈旋轉(zhuǎn)角度的邏輯是不一樣的。


同理平行彈幕也不相同,下面給出代碼,其實(shí)就是一個(gè)簡(jiǎn)單的數(shù)學(xué)運(yùn)算。
??????? int num = bulletData.Count / 2;
??????? for (int i = 0; i < bulletData.Count; i++)
??????? {
??????????? GameObject go = pool.Get(bulletData.Position, bulletData.LifeTime);
??????????? //從對(duì)象池中獲取,對(duì)對(duì)象池不了解,不需要優(yōu)化的話(huà)可以直接實(shí)例子彈的預(yù)制體
??????????? Bullet bullet = go.GetComponent<Bullet>();
??????????? go.GetComponent<Renderer>().material.SetColor("_EmissionColor",bulletData.color);//修改子彈外發(fā)光顏色
??????????? bullet.BulletSpeed = bulletData.Speed;
??????????? if (bulletData.Count % 2 == 1)
??????????? {
??????????????? go.transform.rotation = bulletData.direction * Quaternion.Euler(0, bulletData.Angle * num, 0);
??????????????? go.transform.position = go.transform.position + go.transform.right * num * bulletData.Distance;
??????????????? num--;
??????????? }
??????????? else
??????????? {
??????????????? go.transform.rotation = bulletData.direction * Quaternion.Euler(0, bulletData.Angle / 2 + bulletData.Angle * (num - 1), 0);
??????????????? go.transform.position = go.transform.position + go.transform.right * ((num - 1) * bulletData.Distance + bulletData.Distance / 2);
??????????????? num--;
??????????? }
??????? }
?
代碼中使用到了對(duì)象池相關(guān)知識(shí),在本專(zhuān)欄之前文章中有詳細(xì)介紹過(guò)對(duì)象池,詳情可見(jiàn):
【Unity】工具類(lèi)系列教程——對(duì)象池!
控制射擊間隔時(shí)間可以用協(xié)程的方式達(dá)成,我這里用了一個(gè)偷懶的方法:因?yàn)樵贔ixedUpdate中每秒是固定運(yùn)行50次(可在Project Setting中修改),所以在FixedUpdate中可以用i++來(lái)控制時(shí)間
??????? if (LimitI > CdTime * 50)
??????? {
??????? ????...
??????????? //執(zhí)行射擊方法
??????????? LimitI = 0;
??????? }
?
三、還原彈幕效果
1.波與粒的境界
用簡(jiǎn)單的函數(shù)繪制成優(yōu)美的彈幕,在文章開(kāi)頭大家應(yīng)該已經(jīng)看到了所實(shí)現(xiàn)的效果,這里再放上一張對(duì)比圖。

新建個(gè)空物體用來(lái)發(fā)射彈幕,除了之前提到的彈幕所需參數(shù)外,還需要
public float SelfRotation = 0; // 每幀自轉(zhuǎn)角度
?public float AddRotation = 0; // 每幀自轉(zhuǎn)角度增量
?
實(shí)現(xiàn)其實(shí)非常簡(jiǎn)單,每次發(fā)射5發(fā)子彈,角度間隔為360/5°,然后發(fā)射子彈的物體每幀進(jìn)行越來(lái)越快的旋轉(zhuǎn),彈幕就能形成上面的效果。
??????? SelfRotation += AddRotation;
??????? SelfRotation = SelfRotation >= 360 ? SelfRotation - 360 : SelfRotation;
??????? //為了防止數(shù)值過(guò)大進(jìn)行了限制。
??????? var q = Quaternion.Euler(0, SelfRotation, 0);
??????? transform.rotation = transform.rotation * q;
??????? if (LimitI > CdTime * 50)
??????? {
??????????? BulletManager.Instance.ShootConfig(bulletData, m_bullet1_pool);
??????????? LimitI = 0;
??????? }
?
附上具體參數(shù)

2.怨靈貓亂步

可以看到子彈是從11個(gè)不同位置生成,每次生成4圈彈幕,每圈彈幕角度隨機(jī),經(jīng)過(guò)一定時(shí)間后才開(kāi)始移動(dòng)。
子彈的延時(shí)還是通過(guò)記錄一個(gè)DelayI來(lái)實(shí)現(xiàn)。
??? void FixedUpdate()
??? {
??????? if (Shoot)
??????? {
??????????? Shooting();
??????? }
??????? else if(DelayI > DelayTime * 50)
??????? {
??????????? Shoot = true;
??????? }
??????? else
??????? {
?? ?????????DelayI++;
??????? }
??? }
?? private void OnEnable()
??? {
??????? DelayI = 0;
??????? Shoot = false;
??? }
?
同時(shí)還需要一個(gè)與發(fā)射中心距離的參數(shù)
public float CenterDis = 0; // 與發(fā)射點(diǎn)的距離
修改子彈位置到中心點(diǎn)前方
go.transform.position = go.transform.position + go.transform.forward * bulletData.CenterDis;
?
來(lái)看看效果

詳細(xì)參數(shù)可以下載本文末尾的項(xiàng)目自行查看。想要完美還原的話(huà),剩下的就是將發(fā)射物體移動(dòng)到指定位置、調(diào)整延遲時(shí)間等,可以使用Dotween等方式實(shí)現(xiàn),本次就不再進(jìn)行展示了。
由于篇幅原因本次只展示了這么多,有興趣的同學(xué)可以自己嘗試去實(shí)現(xiàn)其他符卡。
最后不要忘了這是在3D場(chǎng)景下的彈幕,所以這樣也是能做到的。

工程地址:https://pan.baidu.com/share/init?surl=JEXp9FsNswaihwSBkiHs-Q
提取碼:mpik

對(duì)游戲開(kāi)發(fā)學(xué)習(xí)感興趣的盆友,歡迎訪(fǎng)問(wèn):http://levelpp.com/
同時(shí),也歡迎加入游戲開(kāi)發(fā)群攪基:610475807