- Survival Shooter Tutorial 是Unity官方提供的一個(gè)3D生存類游戲的一個(gè)資源包,里面包含了做一個(gè)簡(jiǎn)單3D射擊游戲的通常所需要使用到的功能代碼以及資源,也可以了解到資源的一些調(diào)整和設(shè)置,總體來(lái)說(shuō)是一個(gè)對(duì)于沒(méi)有做過(guò)3D游戲的初學(xué)者來(lái)說(shuō)是一個(gè)很不錯(cuò)的資源,并且代碼注釋也比較完整,理解難度也不高。
- 適用Unity版本:4.7.1
- 資源下載界面
花了點(diǎn)時(shí)間看了下這個(gè)資源,理解起來(lái)并不難,也是剛開(kāi)始學(xué)習(xí)3D游戲的制作,以前都是2D,記錄下在這個(gè)資源包中所了解到的一些關(guān)于3D或者2D的知識(shí)點(diǎn)。其實(shí)現(xiàn)在隨著Unity的發(fā)展,很多的要實(shí)現(xiàn)的功能已經(jīng)被封裝成了各式各樣的接口,調(diào)用起來(lái)也很方便,但是,這些接口背后所做的工作或是其他的一些數(shù)學(xué)物理基礎(chǔ)和概念,如果了解的話更容易理解這個(gè)接口的實(shí)現(xiàn)原理和為什么這樣去實(shí)現(xiàn)。
主要記錄一些腳本方面的筆記。
腳本部分主要接觸資源包腳本代碼的片段進(jìn)行各個(gè)腳本想要記錄的知識(shí)點(diǎn),需要注意的是資源包導(dǎo)入到工程中是有如果有兩份同名代碼的時(shí)候,需要記得區(qū)別游戲運(yùn)行的是哪個(gè)地址的腳本,否則打印調(diào)試的時(shí)候可能出現(xiàn)沒(méi)有反應(yīng)的情況。
CameraFollow.cs
這個(gè)腳本主要實(shí)現(xiàn)了攝像頭跟隨游戲主角移動(dòng)的功能。
void FixedUpdate ()
{
// Create a postion the camera is aiming for based on the offset from the target.
// 刷新相機(jī)當(dāng)前應(yīng)該所在位置
Vector3 targetCamPos = target.position + offset;
// Smoothly interpolate between the camera's current position and it's target position.
// 平滑移動(dòng)相機(jī)的位置(當(dāng)前位置,目標(biāo)位置,移動(dòng)速度)
transform.position = Vector3.Lerp (transform.position, targetCamPos, smoothing * Time.deltaTime);
}
Note
- FixedUpdate:這個(gè)方法將按照固定的幀率被調(diào)用,通常用來(lái)處理跟RigidBody,或者物理引擎相關(guān)的物體運(yùn)動(dòng)等方面的操作。
- Vector3.Lerp:用作兩個(gè)坐標(biāo)點(diǎn)之間的線性插值,通過(guò)不斷的修改坐標(biāo)值,獲得一個(gè)相對(duì)于平滑的物體移動(dòng)軌跡,第三個(gè)參數(shù)值越大,移動(dòng)速度越快。2D環(huán)境下可以使用Vector2.Lerp。
EnemyManager.cs
這個(gè)腳本負(fù)責(zé)了怪物的創(chuàng)建,主要是調(diào)用循環(huán)執(zhí)行在規(guī)定的時(shí)間周期內(nèi)重復(fù)調(diào)用創(chuàng)建敵人。所有怪物的創(chuàng)建都通過(guò)該腳本,只是創(chuàng)建的怪物不一樣而已。
// Call the Spawn function after a delay of the spawnTime and then continue to call after the same amount of time.
// 間隔時(shí)間執(zhí)行
InvokeRepeating ("Spawn", spawnTime, spawnTime);
// Find a random index between zero and one less than the number of spawn points.
// 隨機(jī)選擇出生地點(diǎn)
int spawnPointIndex = Random.Range (0, spawnPoints.Length);
Note
- InvokeRepeating("執(zhí)行方法名", 延后執(zhí)行時(shí)間, 周期時(shí)間)
- Random.Range(下限, 上限),結(jié)果包含下限不包含上限
GameOverManager.cs
這個(gè)腳本主要負(fù)責(zé)的是玩家控制游戲的角色死亡之后的代碼邏輯。
// ... tell the animator the game is over.
// 播放游戲結(jié)束動(dòng)畫(huà)
anim.SetTrigger ("GameOver");
Note
- Animator.SetTrigger("狀態(tài)名") 方法是用作從當(dāng)前動(dòng)畫(huà)狀態(tài)切換到目標(biāo)動(dòng)畫(huà)狀態(tài),跟bool的區(qū)別是bool需要手動(dòng)去設(shè)置bool的值是True還是False,而SetTrigger則自動(dòng)回復(fù)狀態(tài),如果說(shuō)setBool是兩個(gè)狀態(tài)的來(lái)回切換的話,SetTrigger就是點(diǎn)一次觸發(fā)一次到目標(biāo)狀態(tài)。SetTrigger也可以從自身狀態(tài)轉(zhuǎn)到自身狀態(tài),在Transition的Sitting中設(shè)置。
PauseManager.cs
這個(gè)腳本主要是停止游戲時(shí)間然后切換游戲音樂(lè)狀態(tài)
Time.timeScale = Time.timeScale == 0 ? 1 : 0;
void Lowpass()
{
if (Time.timeScale == 0)
{
paused.TransitionTo(.01f);
}
else
{
unpaused.TransitionTo(.01f);
}
}
Note
- Time.scale,用來(lái)調(diào)整依賴于時(shí)間進(jìn)行的一系列物理運(yùn)動(dòng),這是所指的時(shí)間并不是現(xiàn)實(shí)時(shí)間而是Unity中的時(shí)間概念,通常會(huì)影響FixedUpdate的調(diào)用頻率而不影響Update和LateUpdate。
- AudioMixerSnapshot.TransitionTo(),在指定的時(shí)間間隔內(nèi)對(duì)此快照?qǐng)?zhí)行插值轉(zhuǎn)換。暫時(shí)還不是很理解。(待解決)有興趣再挖一下。
EnemyAttack.cs
這個(gè)腳本主要判斷是否接觸了玩家,如果是的話則開(kāi)始攻擊也就是扣血。
Note
- void OnTriggerEnter (Collider other)
- void OnTriggerExit(Collider other)
- void OnTriggerStay(Collider other)
- 這三個(gè)觸發(fā)器方法僅當(dāng)碰撞體使用Trigger作為輸入的時(shí)候才有效也就是碰撞體上面的isTrigger
EnemyHealth.cs
這個(gè)腳本主要控制怪物的存亡和受到攻擊播放例子特效。
// Turn the collider into a trigger so shots can pass through it.
// 修改碰撞體isTrigger,使其被物理引擎忽略,從而讓子彈穿過(guò),也使得可以穿過(guò)碰撞體
capsuleCollider.isTrigger = true;
Note
- 當(dāng)碰撞體上的IsTrigger被設(shè)置為T(mén)rue,那么該物體就會(huì)不受到物理引擎的影響,從可以可以穿過(guò)其他碰撞體。這里是為了可以讓怪物死后下沉以及讓射擊穿過(guò)已經(jīng)死亡的怪物。
EnemyMovement.cs
這個(gè)腳本主要控制怪物的朝向以及向玩家尋路靠近。
Update()
{
// ... set the destination of the nav mesh agent to the player.
// 使用Unity AI自動(dòng)尋路
nav.SetDestination (player.position);
}
Note
- UnityEngine.AI.NavMeshAgent.setSetDestination(),提供了將綁定的物體尋路到固定坐標(biāo)的方法,通過(guò)Update逐幀調(diào)用,從而不斷刷新尋路坐標(biāo)而逐漸靠近目標(biāo)坐標(biāo)或者物體。
PlayerMovement.cs
這個(gè)腳本主要負(fù)責(zé)響應(yīng)玩家的輸入,對(duì)控制的角色進(jìn)行移動(dòng)以及瞄準(zhǔn)方向的改變。
// Create a layer mask for the floor layer.
// 得到的值是2的需要獲取的Layer的次方,用于模擬射線忽略其他的碰撞體
floorMask = LayerMask.GetMask ("Floor");
void Move (float h, float v)
{
// Set the movement vector based on the axis input.
// 設(shè)置方向
movement.Set (h, 0f, v);
// 歸一化方向向量,乘以速度和時(shí)間獲得移動(dòng)距離
// Normalise the movement vector and make it proportional to the speed per second.
movement = movement.normalized * speed * Time.deltaTime;
// Move the player to it's current position plus the movement.
// 移動(dòng)物體到新的坐標(biāo),使用剛體的movePostion更自然流暢
playerRigidbody.MovePosition (transform.position + movement);
}
if(Physics.Raycast (camRay, out floorHit, camRayLength, floorMask))
Note
- 通過(guò)LayerMask.GetMask()獲得的Layer ID是Int 32位的二進(jìn)制形式保存的,這也是為什么一共只有32個(gè)Layer可以設(shè)置,獲得的值也是2的冪次方。
- 使用RigidBody.MovePosition會(huì)比直接設(shè)置Transform的坐標(biāo)更流暢自然。
- 模擬射線時(shí)傳入Layer的ID就可以模擬射線的時(shí)候忽略其他層的影響。
PlayerHealth.cs
這個(gè)腳本控制玩家的血量,判斷玩家是否游戲結(jié)束。
// ... transition the colour back to clear.
// 從紅色到透明
damageImage.color = Color.Lerp (damageImage.color, Color.clear, flashSpeed * Time.deltaTime);
Note
- Color.Lerp用來(lái)做顏色的插值過(guò)渡。
PlayerShooting.cs
這個(gè)腳本主要負(fù)責(zé)玩家射擊功能,射線模擬,確認(rèn)擊中的坐標(biāo),通知怪物播放受擊例子特效和扣血。
Input.GetButton ("Fire1")
gunLine.SetPosition (0, transform.position);
Note
- Input.GetButton用來(lái)獲取定義好的輸入信號(hào)。
- LineRenderer.SetPosition用來(lái)設(shè)定描線的節(jié)點(diǎn)。