国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【用unity實(shí)現(xiàn)100個(gè)游戲之1】制作類(lèi)元?dú)怛T士、挺進(jìn)地牢——俯視角射擊游戲多種射擊效果(一)(附源碼)

這篇具有很好參考價(jià)值的文章主要介紹了【用unity實(shí)現(xiàn)100個(gè)游戲之1】制作類(lèi)元?dú)怛T士、挺進(jìn)地牢——俯視角射擊游戲多種射擊效果(一)(附源碼)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

參考原視頻鏈接
【視頻】:https://space.bilibili.com/641773200
注意:本文為學(xué)習(xí)筆記記錄,推薦支持原作者,去看原視頻自己手敲代碼理解更加深入

本期目標(biāo)

近幾年俯視角射擊游戲隨著《挺進(jìn)地牢》等雙搖桿射擊游戲的火熱再次出現(xiàn)在玩家的視野中,這類(lèi)游戲通常都有種類(lèi)繁多的武器射擊方式,這也鼓勵(lì)著玩家一次次的重開(kāi)游戲來(lái)體驗(yàn)不同的槍械。

本期我們將在unity2d下實(shí)現(xiàn)各種不同的射擊方式,并使用對(duì)像池優(yōu)化內(nèi)存開(kāi)銷(xiāo)。

前言

俯視角射擊游戲Top-down Shooter)是一類(lèi)以俯視視角進(jìn)行游戲展示的射擊游戲。在這種游戲中,玩家控制著一個(gè)角色或載具,從俯視的角度上方觀察游戲世界,并與敵人進(jìn)行戰(zhàn)斗。這種視角使玩家能夠有更好的全局觀察和策略性,同時(shí)也強(qiáng)調(diào)快速反應(yīng)和精確射擊。

俯視角射擊游戲的特點(diǎn)包括:

  • 視角:俯視角度可以是固定的,也可以隨著玩家角色的移動(dòng)而變化,但都允許玩家以全局視角觀察整個(gè)戰(zhàn)場(chǎng),有利于制定戰(zhàn)略和規(guī)劃行動(dòng)。
  • 射擊:玩家通常需要使用各種武器來(lái)與敵人進(jìn)行戰(zhàn)斗,包括槍械、爆炸物等。射擊動(dòng)作依賴于玩家的反應(yīng)速度和準(zhǔn)確性。
    敵人:俯視角射擊游戲通常有大量的敵人,它們可能以固定的路徑或者隨機(jī)移動(dòng),玩家需要躲避敵人的攻擊并選擇最佳時(shí)機(jī)進(jìn)行射擊。
  • 可破壞元素:游戲中的地圖通常會(huì)有各種可以破壞的元素,例如墻壁、箱子等,玩家可以利用這些元素來(lái)尋找掩護(hù)、改變戰(zhàn)術(shù)或者發(fā)起進(jìn)攻。
  • 升級(jí)與解鎖:許多俯視角射擊游戲會(huì)提供升級(jí)系統(tǒng),玩家可以通過(guò)消滅敵人或完成特定任務(wù)來(lái)獲取經(jīng)驗(yàn)值并提升角色的能力或解鎖新的武器和裝備。

一些比較火的俯視角射擊游戲包括:

  1. 元?dú)怛T士》:(Katana ZERO)是一款2D俯視角動(dòng)作射擊游戲。游戲中,你扮演一名忍者刺客,通過(guò)使用刀劍和其他特殊技能,快速且脆弱地消滅敵人。游戲以像素化的藝術(shù)風(fēng)格呈現(xiàn),結(jié)合了劇情、快速反應(yīng)和策略,玩家需要利用時(shí)間的操作和環(huán)境的互動(dòng)來(lái)完成任務(wù)。

  2. 挺進(jìn)地牢》:(Enter the Gungeon)是一款像素風(fēng)格的俯視角射擊游戲。游戲中,你將扮演一位勇敢的冒險(xiǎn)者,進(jìn)入一個(gè)充滿怪物和寶藏的地下城。你需要使用各種武器、道具和特殊技能來(lái)對(duì)抗敵人,并逐步深入地牢的層級(jí)。游戲注重隨機(jī)生成的關(guān)卡和強(qiáng)大的敵人,同時(shí)也提供了多人合作模式。

  3. 失落城堡》:(Dead Cells)是一款像素風(fēng)格的動(dòng)作平臺(tái)游戲,也被歸類(lèi)為俯視角射擊游戲。玩家扮演的角色是一個(gè)不死的戰(zhàn)士,探索一個(gè)被怪物和陷阱充斥的廢墟。玩家需要通過(guò)戰(zhàn)斗、探索和收集資源,不斷改進(jìn)自己的能力,并逐漸深入廢墟。游戲的關(guān)卡是隨機(jī)生成的,每次都有不同的挑戰(zhàn)和發(fā)現(xiàn)。

這些俯視角射擊游戲取得了相當(dāng)?shù)某晒?,并且擁有龐大的玩家群體。它們提供了豐富的內(nèi)容和挑戰(zhàn),讓玩家可以享受到刺激的射擊體驗(yàn)。這只是一些例子,市場(chǎng)上還有許多其他優(yōu)秀的俯視角射擊游戲可供選擇。

欣賞

我在網(wǎng)上找了一些畫(huà)面先給大家欣賞一下unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

開(kāi)始

1. 角色移動(dòng)和場(chǎng)景搭建

因?yàn)楸酒诘闹攸c(diǎn)放在多種射擊效果,角色移動(dòng)環(huán)境如何搭建等一些基礎(chǔ)的知識(shí)這里就不細(xì)說(shuō)了實(shí),節(jié)省大家的時(shí)間
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

當(dāng)然,之前我也寫(xiě)過(guò)很多角色移動(dòng)的方法和環(huán)境搭建的方法,這里我貼出地址,感興趣的同學(xué)也可以先去了解一下:
設(shè)置人物移動(dòng)腳本、動(dòng)畫(huà)的切換和攝像機(jī)的跟隨
繪制地圖Tilemap的使用及一些技巧的使用

角色、環(huán)境和武器素材鏈接:
https://o-lobster.itch.io/simple-dungeon-crawler-16x16-pixel-pack
https://humanisred.itch.io/weapons-and-bullets-pixel-art-asset
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

2. 綁定槍械

2.1 首先將各種槍械的素材添加給人物作為子物體

首先要發(fā)射各式子彈就需要各式槍械作為載體
所以我們先給這個(gè)人物添加多個(gè)槍械并添加切換槍械的功能
首先將各種槍械的素材添加給人物作為子物體
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

2.2 給槍械也分別添加兩個(gè)子物體用作標(biāo)記槍口和彈倉(cāng)位置

unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

3. 槍械動(dòng)畫(huà)

給槍械添加動(dòng)畫(huà)器,包括槍戒的待機(jī)動(dòng)畫(huà)和發(fā)射動(dòng)畫(huà)
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
通過(guò)一個(gè)trigger參數(shù)shoot開(kāi)始播放發(fā)射動(dòng)畫(huà)并在播放完動(dòng)畫(huà)后切換回待機(jī)動(dòng)畫(huà)
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

4. 切換槍械

將所有子物體槍械都取消激活,這樣在激活的時(shí)候才會(huì)使用指定的槍械
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
在控制人物的腳本PlayerMovement中添加切換槍械的功能

新建一個(gè)GameObject的數(shù)組guns用來(lái)儲(chǔ)存所有槍械
一個(gè)int參數(shù)gunNum用作標(biāo)記當(dāng)前使用的槍械下標(biāo)
在Start函數(shù)中激活第一個(gè)槍械用作初始槍械
然后編寫(xiě)一個(gè)函數(shù)SwitchGun:并在Update函數(shù)中調(diào)用
在這個(gè)函數(shù)中將會(huì)檢測(cè)按鍵用來(lái)切換槍械

private Animator animator;
private Rigidbody2D rigidbody;
public GameObject[] guns;
private int gunNum;

void Start()
{
    animator = GetComponent<Animator>();
    rigidbody = GetComponent<Rigidbody2D>();
    guns[0].SetActive(true);
}

void Update()
{
	SwitchGun();
}

void SwitchGun(){}

當(dāng)按下Q鍵時(shí)將當(dāng)前的槍械取消激活
讓槍械下標(biāo)減1并且如果下標(biāo)小于0就將下標(biāo)設(shè)為數(shù)組尾部
然后重新激活當(dāng)前下標(biāo)的槍械
按下E鍵時(shí)基本一致,只是讓槍械下標(biāo)加1
如果下標(biāo)超出數(shù)組邊界就重置為0

void SwitchGun()
{
    if (Input.GetKeyDown(KeyCode.Q))
    {
        guns[gunNum].SetActive(false);
        if (--gunNum < 0)
        {
            gunNum = guns.Length - 1;
        }
        guns[gunNum].SetActive(true);
    }
    if (Input.GetKeyDown(KeyCode.E))
    {
        guns[gunNum].SetActive(false);
        if (++gunNum > guns.Length - 1)
        {
            gunNum = 0;
        }
        guns[gunNum].SetActive(true);
    }
}

將所有槍械綁定給數(shù)組guns
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
然后運(yùn)行游戲,按下Q和E鍵,就可以自由切換槍械了
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

5. 發(fā)射功能

現(xiàn)在依次給各種槍械添加發(fā)射功能

5.1 手槍

(1) 槍械隨著鼠標(biāo)旋轉(zhuǎn)

給初始槍械手槍創(chuàng)建腳本Pistol,代碼已經(jīng)加了詳細(xì)的注釋?zhuān)@里就不過(guò)多解釋了

using UnityEngine;

public class Pistol : MonoBehaviour
{
	//聲明一個(gè)float類(lèi)型的參數(shù)interval作為射擊間隔時(shí)間
	public float interval;
	//兩個(gè)GameObjecta參數(shù)分別傳入子彈和彈殼的預(yù)制體
	public GameObject bulletPrefab;
	public GameObject shellPrefab;
	//兩個(gè)Transform參數(shù)用來(lái)標(biāo)記槍口和彈倉(cāng)位置
	private Transform muzzlePos;
	private Transform shellPos;
	//兩個(gè)Vector2類(lèi)型的參數(shù)用來(lái)記錄鼠標(biāo)位置和發(fā)射的方向
	private Vector2 mousePos;
	private Vector2 direction;
	//一個(gè)float類(lèi)型的參數(shù)timer用作計(jì)時(shí)器
	private float timer;
	//一個(gè)Animator參數(shù)獲取動(dòng)畫(huà)器
	private Animator animator;
	
	//然后在Start函數(shù)中獲取到動(dòng)畫(huà)器和子物體位置方便后續(xù)使用
	void Start()
	{
	    animator = GetComponent<Animator>();
	    muzzlePos = transform.Find("Muzzle");
	    shellPos = transform.Find("BulletShell");
	}
	    
	//接著在Update函數(shù)中持續(xù)的獲取鼠標(biāo)位置
	void Update()
	{
		/*
		 * 我們可以使用Input.mousePosition獲取到鼠標(biāo)的當(dāng)前位置
		 * 但這個(gè)位置是鼠標(biāo)的像素坐標(biāo)位置,這個(gè)位置是以屏幕左下角為原點(diǎn)構(gòu)建的坐標(biāo)系,需要的鼠標(biāo)位置是在世界坐標(biāo)系的實(shí)際坐標(biāo)
		 * 可以使用Camera.main.ScreenToWorldPoint方法來(lái)將像素坐標(biāo)轉(zhuǎn)換為世界坐標(biāo)
		 */
	    mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
	   
	    Shoot();
	}
	
	//這個(gè)函數(shù)中我們會(huì)讓槍口指向鼠標(biāo)方向
	void Shoot()
	{
		/*
		 * 首先要獲取到鼠標(biāo)方向的向量
		 * 用當(dāng)前鼠標(biāo)位置減去槍械位置并進(jìn)行標(biāo)準(zhǔn)化就獲得了槍械需要朝向的方向
		 * 然后更改槍械的局部坐標(biāo),讓槍械的局部右方向始終等于這個(gè)方向
		 * 這樣就實(shí)現(xiàn)了槍械始終指向鼠標(biāo)位置的效果
		 */
	    direction = (mousePos - new Vector2(transform.position.x, transform.position.y)).normalized;
	    transform.right = direction;
	}
}

現(xiàn)在可以嘗試運(yùn)行游戲查看一下效果
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
可以看到槍械在隨著鼠標(biāo)旋轉(zhuǎn),但當(dāng)鼠標(biāo)處于人物的左側(cè)時(shí),槍械就會(huì)倒轉(zhuǎn)過(guò)來(lái)
所以我們需要在鼠標(biāo)在人物左側(cè)時(shí)上下旋轉(zhuǎn)一下槍械
我們可以通過(guò)修改localScale屬性達(dá)到翻轉(zhuǎn)的效果

private float flipY;

void Start()
{
    //...
    flipY = transform.localScale.y;
}

void Update()
{
   	mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);

    if (mousePos.x < transform.position.x)
        transform.localScale = new Vector3(flipY, -flipY, 1);
    else
        transform.localScale = new Vector3(flipY, flipY, 1);

    Shoot();
}
(2) 射擊時(shí)間間隔

然后繼續(xù)編寫(xiě)Shoot函數(shù),當(dāng)按住Fire鍵,也就是鼠標(biāo)左鍵時(shí)

void Shoot()
{
    direction = (mousePos - new Vector2(transform.position.x, transform.position.y)).normalized;
    transform.right = direction;

    if (timer != 0)
    {
        timer -= Time.deltaTime;
        if (timer <= 0)
            timer = 0;
    }

    if (Input.GetButton("Fire1"))
    {
        if (timer == 0)
        {
            timer = interval;
            Fire();
        }
    }
}

void Fire() { //后面完善 }
(3) 創(chuàng)建好子彈、彈殼和爆炸特效

完成發(fā)射子彈的函數(shù)Fire之前我們需要?jiǎng)?chuàng)建好子彈、彈殼和爆炸特效
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
給子彈添加剛體、碰撞器,并設(shè)置參數(shù)
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
然后給彈殼添加剛體,稍微增大重力參數(shù)即可
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
接著給爆炸特效添加動(dòng)畫(huà)器
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

(4) 為子彈添加圖層Bullet并使子彈之間不會(huì)相互碰撞(這個(gè)很重要,子彈間會(huì)互相銷(xiāo)毀)

unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

(5) 編寫(xiě)好子彈、彈殼和爆炸特效腳本

分別給子彈、彈殼和爆炸特效創(chuàng)建腳本Bullet、BulletShellExplosion

首先編寫(xiě)B(tài)ullet腳本

using UnityEngine;

public class Bullet : MonoBehaviour
{
	//聲明一個(gè)float類(lèi)型的參數(shù)speed設(shè)置子彈的速度
    public float speed;
    //一個(gè)GameObject參數(shù)傳入爆炸特效的預(yù)制體
    public GameObject explosionPrefab;
    //一個(gè)Rigidbody2D參數(shù)獲取剛體
    new private Rigidbody2D rigidbody;

	//因?yàn)樯深A(yù)制體后馬上就會(huì)使用這個(gè)參數(shù),Start函數(shù)來(lái)不及獲取到剛體,所以在Awake函數(shù)中獲取剛體
    void Awake()
    {
        rigidbody = GetComponent<Rigidbody2D>();
    }

	//聲明一個(gè)公有的函數(shù)SetSpeed并傳入一個(gè)Vector2的參數(shù)設(shè)置子彈移動(dòng)的方向
    public void SetSpeed(Vector2 direction)
    {
    	//將剛體的速度設(shè)置為方向乘以速度,讓子彈開(kāi)始運(yùn)動(dòng)
        rigidbody.velocity = direction * speed;
    }

    void Update()
    {

    }
	
	//在子彈碰撞到物體時(shí)生成爆炸特效并銷(xiāo)毀子彈
    private void OnTriggerEnter2D(Collider2D other)
    {
        Instantiate(explosionPrefab, transform.position, Quaternion.identity);
        Destroy(gameObject);
    }
}

繼續(xù)編寫(xiě)彈殼腳本BulletShell

using System.Collections;
using UnityEngine;

public class BulletShell : MonoBehaviour
{
	//聲明三個(gè)flot類(lèi)型的參數(shù)用作彈殼被拋出的速度、停下的時(shí)間和彈殼消失的速度
    public float speed;
    public float stopTime = .5f;
    public float fadeSpeed = .01f;
    //一個(gè)Riqidbody2D參數(shù)和一個(gè)SpriteRendera參數(shù)獲取剛體和精靈渲染器
    new private Rigidbody2D rigidbody;
    private SpriteRenderer sprite;

	//同樣在Awake函數(shù)中獲取到剛體和精靈渲染器方便后續(xù)使用
    void Awake()
    {
        rigidbody = GetComponent<Rigidbody2D>();
        sprite = GetComponent<SpriteRenderer>();
        //給彈殼一個(gè)向上的速度實(shí)現(xiàn)拋出的效果
        rigidbody.velocity = Vector3.up * speed;
        //使用協(xié)程實(shí)現(xiàn)這個(gè)效果,新建一個(gè)協(xié)程Stop
        StartCoroutine(Stop());
    }
	
	//彈殼將在一段時(shí)間后停止模擬落地,落地后彈殼會(huì)逐漸淡出,直到完全透明后銷(xiāo)毀彈殼
    IEnumerator Stop()
    {
    	//在這個(gè)協(xié)程中首先等待設(shè)定好的時(shí)間
        yield return new WaitForSeconds(stopTime);
        //然后將重力和速度都設(shè)為0
        rigidbody.velocity = Vector2.zero;
        rigidbody.gravityScale = 0;
		
		//然后開(kāi)始一個(gè)while循環(huán),當(dāng)渲染器的alpha值大于O時(shí)每幀設(shè)置渲染器的顏色
        while (sprite.color.a > 0)
        {
        	//每次循環(huán)都讓alpha的值減小使其變的逐漸透明
            sprite.color = new Color(sprite.color.r, sprite.color.g, sprite.color.g, sprite.color.a - fadeSpeed);
            //然后等待一個(gè)FixedUpdater幀
            yield return new WaitForFixedUpdate();
        }
        //在結(jié)束循環(huán)后銷(xiāo)毀掉彈殼
        Destroy(gameObject);
    }
}

然后編寫(xiě)爆炸特效腳本Explosion

using UnityEngine;

public class Explosion : MonoBehaviour
{	
	//聲明一個(gè)Animator參數(shù)和一個(gè)AnimatorStateInfo參數(shù)獲取動(dòng)畫(huà)器和動(dòng)畫(huà)進(jìn)度
    private Animator animator;
    private AnimatorStateInfo info;

	//在Awake函數(shù)中獲取到動(dòng)畫(huà)器方便后續(xù)使用
    void Awake()
    {
        animator = GetComponent<Animator>();
    }
	
    void Update()
    {
    	//持續(xù)的獲取動(dòng)畫(huà)進(jìn)度
        info = animator.GetCurrentAnimatorStateInfo(0);
        if (info.normalizedTime >= 1)
        {
        	//當(dāng)播放完動(dòng)畫(huà)后,銷(xiāo)毀特效
            Destroy(gameObject);
        }
    }
}
(6)制作子彈、彈殼和爆炸特效預(yù)制體

將子彈、彈殼和爆炸特效都拖動(dòng)到資源窗口中制作成預(yù)制體,然后分別設(shè)置好相應(yīng)的腳本參數(shù)就創(chuàng)建好預(yù)制體了
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

(7) 發(fā)射子彈

現(xiàn)在我們可以繼續(xù)編寫(xiě)手槍腳本中的Fire函數(shù)了

void Fire()
{
	//首先觸發(fā)動(dòng)畫(huà)器的參數(shù)Shoot播放發(fā)射動(dòng)畫(huà)
    animator.SetTrigger("Shoot");
	//然后生成子彈的預(yù)體體,并將生成的子彈位置設(shè)為槍口的位置
    GameObject bullet = Instantiate(bulletPrefab, muzzlePos.position, Quaternion.identity);
	//接著獲取到Bullet腳本然后調(diào)用SetSpeed函數(shù)設(shè)置子彈發(fā)射的方向?yàn)闃尶诔虻姆较?,也就是direction參數(shù)
    bullet.GetComponent<Bullet>().SetSpeed(direction);
	//最后生成彈殼的預(yù)制體并將位置設(shè)為彈倉(cāng)位置,旋轉(zhuǎn)也設(shè)為彈倉(cāng)的旋轉(zhuǎn)角度
    Instantiate(shellPrefab, shellPos.position, shellPos.rotation);
}

回到unity運(yùn)行游戲,按下鼠標(biāo)左鍵,可以看到子彈可以朝著鼠標(biāo)方向發(fā)射了
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

(7) 子彈和彈殼偏移

不過(guò)在很多游戲中子彈都不那么精準(zhǔn),會(huì)在一個(gè)小區(qū)間內(nèi)產(chǎn)生偏移

現(xiàn)在讓我們加上這個(gè)小功能,在發(fā)射子彈前使用Random.Range產(chǎn)生一個(gè)隨機(jī)的偏移角度


void Fire()
{
    animator.SetTrigger("Shoot");

    GameObject bullet = Instantiate(bulletPrefab, muzzlePos.position, Quaternion.identity);
	//我這里就在-5度和5度之間產(chǎn)生了一個(gè)10度內(nèi)的隨機(jī)偏移
    float angel = Random.Range(-5f, 5f);
    /*
     * 然后在設(shè)置速度時(shí)對(duì)方向做一點(diǎn)更改,使用Quaternion.AngleAxis產(chǎn)生一個(gè)相對(duì)偏轉(zhuǎn)
     * 傳入隨機(jī)出的角度并讓其繞著z軸旋轉(zhuǎn)
     * 再乘以正常的方向就產(chǎn)生了以這個(gè)方向?yàn)榛鶞?zhǔn)的偏轉(zhuǎn)方向
     */
    bullet.GetComponent<Bullet>().SetSpeed(Quaternion.AngleAxis(angel, Vector3.forward) * direction);

    Instantiate(shellPrefab, shellPos.position, shellPos.rotation);
}

也可以用這個(gè)方法修改彈殼的代碼,讓彈殼以一個(gè)隨機(jī)的角度拋出提升觀感
進(jìn)入BulletShell腳本,同樣在設(shè)置速度前隨機(jī)一個(gè)角度并讓拋出的方向偏轉(zhuǎn)這個(gè)角度

void Awake()
{
    rigidbody = GetComponent<Rigidbody2D>();
    sprite = GetComponent<SpriteRenderer>();
     
	//修改
    float angel = Random.Range(-30f, 30f);
	rigidbody.velocity = Quaternion.AngleAxis(angel, Vector3.forward) * Vector3.up * speed;
     
    StartCoroutine(Stop());
}

再次運(yùn)行游戲,現(xiàn)在子彈發(fā)射時(shí)會(huì)在一個(gè)范圍內(nèi)隨機(jī)射擊,彈殼也會(huì)以隨機(jī)的角度拋出了
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

(8) 對(duì)象池優(yōu)化

但現(xiàn)在存在一個(gè)問(wèn)題,因?yàn)槲覀兪峭ㄟ^(guò)不斷的實(shí)例化預(yù)制體來(lái)制造子彈或者彈殼的,而這些生成的物體也會(huì)很快被銷(xiāo)毀,現(xiàn)在數(shù)量比較少的情況下還好,一旦需要的物體數(shù)量達(dá)到一定程度,不斷的創(chuàng)建和銷(xiāo)毀物體會(huì)對(duì)游戲性能造成很大的影響,這時(shí)就需要用到對(duì)象池了,我們會(huì)將用完的物體取消激活并放回對(duì)象池中,在需要使用物體時(shí)再?gòu)膶?duì)象池中激活物體使用,只有在對(duì)象池里的待分配物體不足時(shí)才會(huì)進(jìn)行實(shí)例化操作,相對(duì)通常的創(chuàng)建和銷(xiāo)毀只是對(duì)物體進(jìn)行激活和取消激活操作,節(jié)省了很多性能

現(xiàn)在讓我們先來(lái)寫(xiě)一個(gè)對(duì)象池腳本,新建一個(gè)腳本,起名為ObjectPool
這個(gè)腳本使用單例模式進(jìn)行編寫(xiě),因?yàn)槟_本不需要掛載在任何物體上,所以不需要繼承MonoBehavior

using System.Collections.Generic;
using UnityEngine;

public class ObjectPool
{
    private static ObjectPool instance; // 單例模式
	// /**
    //  * 我們希望不同的物體可以被分開(kāi)存儲(chǔ),在這種情況下使用字典是最合適的
    //  * 所以聲明一個(gè)字典objectPool作為對(duì)象池主體,以字符串類(lèi)型的物體的名字作為key
    //  * 使用隊(duì)列存儲(chǔ)物體來(lái)作為value,這里使用隊(duì)列只是因?yàn)槿腙?duì)和出隊(duì)的操作較為方便,也可以換成其他集合方式
    //  * 然后實(shí)例化這個(gè)字典以備后續(xù)使用
    //  * /
    private Dictionary<string, Queue<GameObject>> objectPool = new Dictionary<string, Queue<GameObject>>(); // 對(duì)象池字典
    private GameObject pool; // 為了不讓窗口雜亂,聲明一個(gè)對(duì)象池父物體,作為所有生成物體的父物體
    public static ObjectPool Instance // 單例模式
    {
        get
        {
            if (instance == null)
            {
                instance = new ObjectPool();
            }
            return instance;
        }
    }
    public GameObject GetObject(GameObject prefab) // 從對(duì)象池中獲取對(duì)象
    {
        GameObject _object;
        if (!objectPool.ContainsKey(prefab.name) || objectPool[prefab.name].Count == 0) // 如果對(duì)象池中沒(méi)有該對(duì)象,則實(shí)例化一個(gè)新的對(duì)象
        {
            _object = GameObject.Instantiate(prefab);
            PushObject(_object); // 將新的對(duì)象加入對(duì)象池
            if (pool == null)
                pool = new GameObject("ObjectPool"); // 如果對(duì)象池父物體不存在,則創(chuàng)建一個(gè)新的對(duì)象池父物體
            GameObject childPool = GameObject.Find(prefab.name + "Pool"); // 查找該對(duì)象的子對(duì)象池
            if (!childPool)
            {
                childPool = new GameObject(prefab.name + "Pool"); // 如果該對(duì)象的子對(duì)象池不存在,則創(chuàng)建一個(gè)新的子對(duì)象池
                childPool.transform.SetParent(pool.transform); // 將該子對(duì)象池加入對(duì)象池父物體中
            }
            _object.transform.SetParent(childPool.transform); // 將新的對(duì)象加入該對(duì)象的子對(duì)象池中
        }
        _object = objectPool[prefab.name].Dequeue(); // 從對(duì)象池中取出一個(gè)對(duì)象
        _object.SetActive(true); // 激活該對(duì)象
        return _object; // 返回該對(duì)象
    }
    public void PushObject(GameObject prefab) // 將對(duì)象加入對(duì)象池中
    {
		//獲取對(duì)象的名稱,因?yàn)閷?shí)例化的物體名都會(huì)加上"(Clone)"的后綴,需要先去掉這個(gè)后綴才能使用名稱查找
        string _name = prefab.name.Replace("(Clone)", string.Empty);
        if (!objectPool.ContainsKey(_name))
            objectPool.Add(_name, new Queue<GameObject>()); // 如果對(duì)象池中沒(méi)有該對(duì)象,則創(chuàng)建一個(gè)新的對(duì)象池
        objectPool[_name].Enqueue(prefab); // 將對(duì)象加入對(duì)象池中
        prefab.SetActive(false); // 將對(duì)象禁用
    }
}

現(xiàn)在回到之前的腳本,將所有生成或銷(xiāo)毀的代碼都使用對(duì)象池操作優(yōu)化

# Bullet腳本
private void OnTriggerEnter2D(Collider2D other)
{
    // Instantiate(explosionPrefab, transform.position, Quaternion.identity);
    GameObject exp = ObjectPool.Instance.GetObject(explosionPrefab);
    exp.transform.position = transform.position;

    // Destroy(gameObject);
    ObjectPool.Instance.PushObject(gameObject);
}

# BulletShell腳本
IEnumerator Stop()
{
   	//...
   	
    // Destroy(gameObject);
    ObjectPool.Instance.PushObject(gameObject);
}

# Explosion腳本
void Update()
{
    info = animator.GetCurrentAnimatorStateInfo(0);
    if (info.normalizedTime >= 1)
    {
        // Destroy(gameObject);
        ObjectPool.Instance.PushObject(gameObject);
    }
}

# Gun腳本
protected virtual void Fire()
{
    animator.SetTrigger("Shoot");

    // GameObject bullet = Instantiate(bulletPrefab, muzzlePos.position, Quaternion.identity);
    GameObject bullet = ObjectPool.Instance.GetObject(bulletPrefab);
    bullet.transform.position = muzzlePos.position;

    float angel = Random.Range(-5f, 5f);
    bullet.GetComponent<Bullet>().SetSpeed(Quaternion.AngleAxis(angel, Vector3.forward) * direction);

    // Instantiate(shellPrefab, shellPos.position, shellPos.rotation);
    GameObject shell = ObjectPool.Instance.GetObject(shellPrefab);
    shell.transform.position = shellPos.position;
    shell.transform.rotation = shellPos.rotation;
}

然后修改單殼的腳本BulletShell
因?yàn)橹皰伋龅牟僮魇窃贏wake函數(shù)中進(jìn)行的,而使用對(duì)象池重用彈殼不會(huì)再次調(diào)用Awake函數(shù),所以我們將拋出部分的代碼放在OnEnable函數(shù)中,這個(gè)函數(shù)將在物體被激活時(shí)調(diào)用

private void OnEnable()
{
    float angel = Random.Range(-30f, 30f);
    rigidbody.velocity = Quaternion.AngleAxis(angel, Vector3.forward) * Vector3.up * speed;

	//并且由于彈殼的透明度和重力已經(jīng)被修改過(guò),所以要每次重新設(shè)置彈殼的透明度和重力
    sprite.color = new Color(sprite.color.r, sprite.color.g, sprite.color.b, 1);
    rigidbody.gravityScale = 3;

    StartCoroutine(Stop());
}

運(yùn)行游戲查看一下效果
按下左鍵射擊后窗口中出現(xiàn)了對(duì)象池的父物體
展開(kāi)就可以看到子彈、彈殼和爆炸特效的對(duì)象池和其中的物體了
可以看到現(xiàn)在物體處于失活狀態(tài),再次開(kāi)始射擊后沒(méi)有再生成新物體
而是激活了這些物體使用,當(dāng)物體該被銷(xiāo)毀時(shí)又會(huì)取消激活回到對(duì)象池中
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

5.2 封裝槍械的父類(lèi)

完成優(yōu)化后我們就可以繼續(xù)制作其他種類(lèi)的槍械了
回到手槍腳術(shù)中仔細(xì)觀察其實(shí)可以發(fā)現(xiàn)槍械的行為大同小異,需要更改的只有一些變量或者發(fā)射的行為
這種情況下可以將我們當(dāng)前的腳本作為父類(lèi),讓其他槍械腳本繼承這個(gè)類(lèi)提高代碼復(fù)用性

首先我們新建一個(gè)類(lèi)Gun作為所有槍械的父類(lèi)
將剛才手槍腳本的代碼剪切到這個(gè)類(lèi)中實(shí)現(xiàn)最基礎(chǔ)的槍械功能然后讓手槍類(lèi)繼承承Gm
接著將所有private的變量和函數(shù)更改為protectedi讓子類(lèi)可以繼承這些基礎(chǔ)的變量和函數(shù)
類(lèi)槍械的行為肯定會(huì)與手槍有些許不同
所以我們使用virtual關(guān)鍵子將所有函數(shù)設(shè)置為虛函數(shù)
這樣Z類(lèi)中就可以通過(guò)重寫(xiě)某些函數(shù)達(dá)到修改特定行為的效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Gun : MonoBehaviour
{
    public float interval;
    public GameObject bulletPrefab;
    public GameObject shellPrefab;
    protected Transform muzzlePos;
    protected Transform shellPos;
    protected Vector2 mousePos;
    protected Vector2 direction;
    protected float timer;
    protected float flipY;
    protected Animator animator;

    protected virtual void Start()
    {
        animator = GetComponent<Animator>();
        muzzlePos = transform.Find("Muzzle");
        shellPos = transform.Find("BulletShell");
        flipY = transform.localScale.y;
    }

    protected virtual void Update()
    {
        mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);

        if (mousePos.x < transform.position.x)
            transform.localScale = new Vector3(flipY, -flipY, 1);
        else
            transform.localScale = new Vector3(flipY, flipY, 1);

        Shoot();
    }

    protected virtual void Shoot()
    {
        direction = (mousePos - new Vector2(transform.position.x, transform.position.y)).normalized;
        transform.right = direction;

        if (timer != 0)
        {
            timer -= Time.deltaTime;
            if (timer <= 0)
                timer = 0;
        }

        if (Input.GetButton("Fire1"))
        {
            if (timer == 0)
            {
                timer = interval;
                Fire();
            }
        }
    }

    protected virtual void Fire()
    {
        animator.SetTrigger("Shoot");

        // GameObject bullet = Instantiate(bulletPrefab, muzzlePos.position, Quaternion.identity);
        GameObject bullet = ObjectPool.Instance.GetObject(bulletPrefab);
        bullet.transform.position = muzzlePos.position;

        float angel = Random.Range(-5f, 5f);
        bullet.GetComponent<Bullet>().SetSpeed(Quaternion.AngleAxis(angel, Vector3.forward) * direction);

        // Instantiate(shellPrefab, shellPos.position, shellPos.rotation);
        GameObject shell = ObjectPool.Instance.GetObject(shellPrefab);
        shell.transform.position = shellPos.position;
        shell.transform.rotation = shellPos.rotation;
    }
}

手槍代碼直接基礎(chǔ)父類(lèi)Gun即可,什么也不需要做

public class Pistol : Gun
{

}

5.3 散彈槍

現(xiàn)在來(lái)嘗試編寫(xiě)第一個(gè)子類(lèi)槍械散彈槍

(1) 創(chuàng)建一個(gè)新腳本起名為Shotgun并繼承父類(lèi)Gun
using UnityEngine;

public class Shotgun : Gun
{
	//首先聲明個(gè)公有的int參數(shù)bulletNum表示一次開(kāi)火射出多少發(fā)子彈
    public int bulletNum = 3;
    //一個(gè)公有的float變量bulletAngle表示每個(gè)子彈間的間隔角度
    public float bulletAngle = 15;
	
	//散彈槍與基礎(chǔ)槍械的區(qū)別是開(kāi)槍時(shí)會(huì)均勻的射出多發(fā)子彈
	//這個(gè)不同只涉及開(kāi)火時(shí),所以只需重寫(xiě)Fire函數(shù)即可
	//使用override關(guān)鍵字重寫(xiě)函數(shù),這樣這個(gè)函數(shù)就會(huì)覆蓋掉繼承的函數(shù)
    protected override void Fire()
    {
    	//在重寫(xiě)的函數(shù)中首先依舊是要觸發(fā)動(dòng)畫(huà)器的tigger參數(shù)來(lái)播放射擊動(dòng)畫(huà)
        animator.SetTrigger("Shoot");
		//然后算出子彈數(shù)的中間值用來(lái)計(jì)算每個(gè)子彈的偏轉(zhuǎn)角度
        int median = bulletNum / 2;
        //接著開(kāi)始一個(gè)子彈次數(shù)的循環(huán)生成對(duì)應(yīng)數(shù)量的子彈
        for (int i = 0; i < bulletNum; i++)
        {
        	//從對(duì)象池中取出一個(gè)子彈的預(yù)制體然后將子彈的位置設(shè)置為槍口的位置
            GameObject bullet = ObjectPool.Instance.GetObject(bulletPrefab);
            bullet.transform.position = muzzlePos.position;

			//根據(jù)子彈數(shù)量的奇偶來(lái)計(jì)算子彈應(yīng)該偏轉(zhuǎn)的角度
            if (bulletNum % 2 == 1)
            {
               
            }
            else
            {
                
            }
        }
    }
}
(2) 散彈槍根據(jù)子彈數(shù)量的奇偶來(lái)計(jì)算子彈應(yīng)該偏轉(zhuǎn)的角度

如果是奇數(shù)那么只需要讓當(dāng)前的循環(huán)次數(shù),也就是第幾顆子彈減去法中間值
這個(gè)值就代表這顆子彈是哪邊的第幾顆子彈,負(fù)數(shù)在左側(cè),正數(shù)在右側(cè),零就是中間的子彈
讓這個(gè)值乘以間隔角度就得到了當(dāng)前子彈的偏轉(zhuǎn)角度
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
而偶數(shù)子彈只會(huì)分布在兩側(cè),就需要在奇數(shù)的基礎(chǔ)上加上間隔角度的一半來(lái)得到偏轉(zhuǎn)角度
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

(3) 完善代碼

根據(jù)子彈數(shù)的奇偶計(jì)算出相應(yīng)的偏轉(zhuǎn)角度后,就可以按照之前的方法設(shè)置子彈的偏轉(zhuǎn)向量了
生成完所有子彈后,一同樣生成一個(gè)彈殼并設(shè)置位置和旋轉(zhuǎn)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Shotgun : Gun
{
    public int bulletNum = 3;
    public float bulletAngle = 15;

    protected override void Fire()
    {
        animator.SetTrigger("Shoot");

        int median = bulletNum / 2;
        for (int i = 0; i < bulletNum; i++)
        {
            GameObject bullet = ObjectPool.Instance.GetObject(bulletPrefab);
            bullet.transform.position = muzzlePos.position;

            if (bulletNum % 2 == 1)
            {
                bullet.GetComponent<Bullet>().SetSpeed(Quaternion.AngleAxis(bulletAngle * (i - median), Vector3.forward) * direction);
            }
            else
            {
                bullet.GetComponent<Bullet>().SetSpeed(Quaternion.AngleAxis(bulletAngle * (i - median) + bulletAngle / 2, Vector3.forward) * direction);
            }
        }

        GameObject shell = ObjectPool.Instance.GetObject(shellPrefab);
        shell.transform.position = shellPos.position;
        shell.transform.rotation = shellPos.rotation;
    }
}

(3) 效果

回到Unity,給散彈槍子物體添加腳本并設(shè)置好參數(shù)和預(yù)制體
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
運(yùn)行游戲,現(xiàn)在子彈可以進(jìn)行散射了

unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
也可以通過(guò)調(diào)整子彈總數(shù)和間隔角度來(lái)達(dá)到不同的發(fā)射效果

unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎
unity 俯視角,# 制作100個(gè)游戲,unity,游戲,游戲引擎

預(yù)告

到這里俯視角的直線射擊效果與對(duì)像池優(yōu)化就全部完成了
因?yàn)槲恼缕鶈?wèn)題,在下期內(nèi)容中我們將繼續(xù)實(shí)現(xiàn)曲線射擊與兩種不需要實(shí)體子彈的射擊方式
源碼我也會(huì)一并放在下期內(nèi)容,敬請(qǐng)期待
傳送門(mén):制作類(lèi)元?dú)怛T士、挺進(jìn)地牢——俯視角射擊游戲多種射擊效果(二)

完結(jié)

如果你有其他更好的方法也歡迎評(píng)論分享出來(lái),當(dāng)然如果發(fā)現(xiàn)文章中出現(xiàn)了什么問(wèn)題或者疑問(wèn)的話,也歡迎評(píng)論私信告訴我哦

好了,我是向宇,https://xiangyu.blog.csdn.net/

一位在小公司默默奮斗的開(kāi)發(fā)者,出于興趣愛(ài)好,于是開(kāi)始自習(xí)unity。最近創(chuàng)建了一個(gè)新欄目【你問(wèn)我答】,主要是想收集一下大家的問(wèn)題,有時(shí)候一個(gè)問(wèn)題可能幾句話說(shuō)不清楚,我就會(huì)以發(fā)布文章的形式來(lái)回答。 雖然有些問(wèn)題我可能也不一定會(huì),但是我會(huì)查閱各方資料,爭(zhēng)取給出最好的建議,希望可以幫助更多想學(xué)編程的人,共勉~文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-654573.html

到了這里,關(guān)于【用unity實(shí)現(xiàn)100個(gè)游戲之1】制作類(lèi)元?dú)怛T士、挺進(jìn)地牢——俯視角射擊游戲多種射擊效果(一)(附源碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包