最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

Unity快速上手系列之4:《塔防》續(xù)

2019-05-17 17:42 作者:皮皮關(guān)做游戲  | 我要投稿

作者:四五二十


大家好。

最近狀態(tài)比較佳,趁這個(gè)機(jī)會(huì)繼續(xù)豐富咱們的塔防游戲。

對(duì)了,如大家覺(jué)得哪里沒(méi)能理解,歡迎在評(píng)論區(qū)給我留言。


在上一篇中,我們已經(jīng)能夠通過(guò)生成器產(chǎn)生敵人,這些敵人能自動(dòng)尋路到達(dá)主城所在位置進(jìn)行攻擊。主城被攻破后游戲結(jié)束。攻擊方已經(jīng)具備。

接下來(lái)是防御方了。這里,咱們建立防御塔阻止敵人的進(jìn)攻。首先說(shuō)說(shuō)本例中三種防御塔的攻擊方式:

弓箭手:遠(yuǎn)程攻擊,對(duì)敵人射出弓箭造成傷害,弓箭可以插在敵人身上;

錘兵:群體攻擊,錘擊地面,原地?fù)麸w敵人,減慢敵人移動(dòng)速度;

劍士:近程攻擊。

那么從弓箭手開(kāi)始,來(lái)做他的攻擊功能:

弓箭手攻擊時(shí)會(huì)從“槍口”處發(fā)出弓箭,所以先在弓箭手模型上創(chuàng)建一個(gè)槍口Muzzle:

槍口一Z軸指向發(fā)射方向

槍口要隨弓箭移動(dòng),在Hierarchy面板中大概在這里:

為了獲取這個(gè)未知層級(jí)的子物體,也為了讓其它類(lèi)也方便調(diào)用,我們先寫(xiě)一個(gè)工具類(lèi),里面創(chuàng)建一個(gè)查找未知層級(jí)子物體的方法:

public class ToolsMethod

{

??? private static ToolsMethod _Instance;

??? public static ToolsMethod Instance //單例

??? {

??????? get

??????? {

??????????? if (_Instance == null)

??????????????? _Instance = new ToolsMethod();

??????????? return _Instance;

??????? }

??? }

??? //根據(jù)名稱獲取未知層級(jí)子物體

??? public Transform FindChildByName(Transform currentTF, string childName)

??? {

??????? Transform childTF = currentTF.Find(childName);

??????? if (childTF != null) return childTF;

??????? for (int i = 0; i < currentTF.childCount; i++)

??????? {

??????????? childTF = FindChildByName(currentTF.GetChild(i), childName);

??????????? if (childTF != null) return childTF;

??????? }

??????? return null;

??? }

}

?


弓箭手的腳本中還需要拿到箭矢的預(yù)制體,先把箭矢預(yù)制體放入路徑中:

為箭矢預(yù)制體創(chuàng)建一個(gè)腳本Bullet掛上去:


public class Bullet : MonoBehaviour

{

}

?

為了讓箭矢和其它物體可以使用對(duì)象池,我們先創(chuàng)建一個(gè)空物體,作為所有對(duì)象池的管理器。取名“PoolManager”,創(chuàng)建對(duì)象池類(lèi):


public class GameObjectPool

{

??? private static GameObjectPool _Instance;

? ??public static GameObjectPool Instance //單例模式

??? {

??????? get

??????? {

??????????? if (_Instance == null)

??????????????? _Instance = new GameObjectPool();

??????????? return _Instance;

??????? }

??? }

??? //用于保存所有對(duì)象池

??? public Dictionary<string, Transform> poolDict = new Dictionary<string, Transform>();

??? //獲取對(duì)象池

??? public Transform GetPool(string poolName)

??? {

??????? if (poolDict.ContainsKey(poolName))

??????????? return poolDict[poolName];

??????? //字典中沒(méi)有重新創(chuàng)建

??????? Transform poolObj = new GameObject(poolName + "_Pool").transform;

??????? //創(chuàng)建的對(duì)象池放入對(duì)象池管理器的子物體中

??????? poolObj.SetParent(GameObject.Find("PoolManager").transform);

??????? poolObj.gameObject.SetActive(false);

??????? poolDict.Add(poolName, poolObj);

??????? return poolObj;

??? }

}

?


其它物體需要對(duì)象池也可以調(diào)用該類(lèi)的方法。

弓箭手需要有一個(gè)攻擊范圍。敵人進(jìn)入該范圍后才會(huì)被認(rèn)定為目標(biāo)并展開(kāi)攻擊。

首先將敵人都放在Enemy層,創(chuàng)建弓箭手的腳本:

public class Pagoda : MonoBehaviour

{

??? protected Animator anim;

??? protected Transform muzzle;

??? Bullet Arrow;

??? Transform arrowPool; //箭矢對(duì)象池

??? //初始化

??? public void initPagoda()

???? {

??????? enabled = true; //啟用腳本

??????? anim = GetComponentInChildren<Animator>();

??????? muzzle = ToolsMethod.Instance.FindChildByName(transform, "Muzzle");

??????? Arrow = Resources.Load<Bullet>("Prefab/Bullet/Arrow");

??????? //為箭矢創(chuàng)建一個(gè)以它命名的對(duì)象池

??????? arrowPool = GameObjectPool.Instance.GetPool(Arrow.name);

???? }

??? private void Update()

???? {

??????? //游戲結(jié)束,停止攻擊

??????? if (GameMain.instance.gameOver)

??????? {

??????????? anim.SetBool("Attack", false);

??????????? return;

??????? }

??????? GetTarget();

??? }

??? public float attactRange; //攻擊范圍

??? public float damage; //傷害值

??? protected Enemy target; //攻擊目標(biāo)

??? //獲取攻擊目標(biāo)

??? void GetTarget()

??? {

??????? if (target == null) //攻擊目標(biāo)為空時(shí),用球形射線檢測(cè)Enemy層找尋攻擊目標(biāo)

??????? {

??????????? Collider[] enemys = Physics.OverlapSphere(transform.position, attactRange, LayerMask.GetMask("Enemy"));

??????????? if (enemys.Length == 0)

??????????????? anim.SetBool("Attack", false);

??????????? //發(fā)現(xiàn)敵人,設(shè)為目標(biāo),進(jìn)行攻擊(播放攻擊動(dòng)畫(huà))

??????????? for (int i = 0; i < enemys.Length;)

??????????? {

??????????????? target = enemys[i].GetComponent<Enemy>();

??????????????? anim.SetBool("Attack", true);

??????????????? break;

??????????? }

??????? }

??????? else

??????? {

??????????? //面向攻擊目標(biāo)

??????????? Vector3 pos = target.transform.position;

??????????? Quaternion dir = Quaternion.LookRotation(new Vector3(pos.x, transform.position.y, pos.z) - transform.position);

??????????? transform.rotation = Quaternion.Lerp(transform.rotation, dir, 0.1f);

??????????? //攻擊目標(biāo)離開(kāi)攻擊范圍或死亡,重新獲取攻擊目標(biāo)

??????????? if (Vector3.Distance(target.transform.position, transform.position) >= attactRange || target.state == EnemyState.death)

??????????????? target = null;

??????? }

??? }

??? //攻擊方法(放在攻擊動(dòng)畫(huà)事件中)

??? public virtual void PagodaAttack()

??? {

??????? //在槍口位置創(chuàng)建箭矢

??? }

}

?

弓箭手的初始化在弓箭手創(chuàng)建時(shí)調(diào)用。

如果弓箭手已經(jīng)能檢測(cè)敵人并發(fā)射箭矢,接下來(lái)就是箭矢的功能了。主要如下:

1. 飛向目標(biāo)(始終面向目標(biāo),并向前飛);

2. 打到目標(biāo),調(diào)用目標(biāo)受傷方法(用距離判斷是否打到);

3. 插在目標(biāo)身上(認(rèn)目標(biāo)做父物體,停止移動(dòng))

要讓箭矢能插在敵人身上,需要在敵人模型上創(chuàng)建一個(gè)空物體做打擊點(diǎn),取名HitPos,為了效果逼真,HitPos最好放在模型骨骼上,并可以在敵人腳本中聲明一個(gè)hitPos,使用查找未知層級(jí)子物體的方法來(lái)獲?。?/p>

Transform hitPos = ToolsMethod.Instance.FindChildByName(transform, "HitPos");

?

當(dāng)弓箭手檢測(cè)到敵人創(chuàng)建箭矢時(shí),同時(shí)將敵人信息、傷害值、箭矢對(duì)象池賦給箭矢,由箭矢去做接下來(lái)的工作,如傷害敵人,它的腳本可以這樣寫(xiě):

public class Bullet : MonoBehaviour

{

??? public float speed;

?

??? Enemy target; //攻擊目標(biāo)

??? float damage; //傷害值

??? Transform pool; //對(duì)象池

??? Vector3 initPos; //初始位置

??? //初始化

??? public void InitBullet(Vector3 position, Quaternion rotation, Enemy _target, float _damage, Transform _pool)

??? {

??????? transform.SetParent(null);

??????? transform.position = position;

??????? transform.rotation = rotation;

??????? target = _target;

??????? damage = _damage;

??????? pool = _pool;

??????? initPos = transform.position;

??? }

??? private void Update()

??? {

??????? if (transform.parent == null) //沒(méi)有射中目標(biāo),繼續(xù)飛

??????? {

??????????? transform.Translate(0, 0, speed * Time.deltaTime);

??????????? if (Vector3.Distance(initPos, transform.position) > 500) //飛出一定范圍自動(dòng)銷(xiāo)毀

??????????????? DestroySelf();

?

??????????? if (target != null && target.state != EnemyState.death) //如果目標(biāo)活著朝向目標(biāo)

??????????? {

??????????????? transform.LookAt(target.hitPos);

??????????????? //到達(dá)有效范圍,調(diào)用目標(biāo)受傷方法,成為目標(biāo)子物體(插在目標(biāo)身上)

??????????????? if (Vector3.Distance(target.hitPos.position, transform.position) <= 1)

??????????????? {

??????????????????? target.Damage(damage);

??????????????????? transform.SetParent(target.hitPos);

??????????????? }

??????????? }

??????? }

??????? else if (target.state == EnemyState.death) //射中后,只要目標(biāo)一死就銷(xiāo)毀

??????????? DestroySelf();

??? }

??? //銷(xiāo)毀自身(進(jìn)入對(duì)象池)

??? private void DestroySelf()

??? {

??????? transform.SetParent(pool);

??? }

}

?

箭矢的移動(dòng)速度在編輯器界面自行設(shè)定,在弓箭手的攻擊方法中,就可以在創(chuàng)建箭矢的同時(shí)把相關(guān)信息賦給它:

??? //攻擊方法(放在攻擊動(dòng)畫(huà)事件中)

??? public virtual void PagodaAttack()

??? {

??????? //如果對(duì)象池有,則從對(duì)象池取子彈,否則重新實(shí)例化

??????? //設(shè)定位置,方向,攻擊目標(biāo),傷害值,所在對(duì)象池

??????? if (arrowPool.childCount > 0)

??????????? arrowPool.GetChild(0).GetComponent<Bullet>().InitBullet(muzzle.position, muzzle.rotation, target, damage, arrowPool);

??????? else

??????????? Instantiate(Arrow).InitBullet(muzzle.position, muzzle.rotation, target, damage, arrowPool);

??? }

?

弓箭手做完,接下來(lái)是錘子兵的功能:

錘子兵的功能和弓箭手非常相似。除了攻擊方式不同,其它都一樣。所以我們創(chuàng)建錘子兵的腳本可以繼承自弓箭手的腳本:

public class Pagoda2 : Pagoda

{

??? public float force; //擊飛力度

??? public ParticleSystem effect; //擊飛特效

??? //重寫(xiě)攻擊方法(在攻擊動(dòng)畫(huà)事件中調(diào)用)

??? public override void PagodaAttack()

??? {

??????? //群體攻擊,作用范圍始攻擊范圍的一半

??????? Collider[] enemys = Physics.OverlapSphere(muzzle.position, attactRange / 2, LayerMask.GetMask("Enemy"));

??????? for (int i = 0; i < enemys.Length; i++)

??????? {

??????????? //傷害作用范圍內(nèi)的每個(gè)敵人

??????????? Enemy enemy = enemys[i].GetComponent<Enemy>();

??????????? enemy.Damage(damage);???????

??????????? //播放特效

??????????? effect.transform.position = muzzle.position;

??????????? effect.Play();

??????????? //擊飛方法

??????? }

??? }

}

?

除了對(duì)敵人造成傷害之外,需要專(zhuān)門(mén)寫(xiě)一個(gè)擊飛的方法,擊飛方法可以寫(xiě)在錘子兵的腳本里,也可以在敵人腳本(Enemy)中寫(xiě)一個(gè)被擊飛方法:

??? //被擊飛方法

??? bool isFly; //是否被擊飛(處于擊飛狀態(tài)時(shí)不能再被擊飛)

??? public void StrikeFly(float force)

??? {

??????? if (isFly == false) //未被擊飛狀態(tài)下才可以被擊飛

??????? {

??????????? isFly = true;

??????????? rigid.AddForce(Vector3.up * force, ForceMode.Impulse);

??????????? float initSpeed = speed; //初始速度

??????????? speed = 0;

??????????? //0.5秒后恢復(fù)

??????????? Util.Instance.AddTimeTask(() =>

??????????? {

??????????????? speed = initSpeed;

??????????????? isFly = false;

??????????? }, 0.5f);

??????? }

??? }

?

該方法是公開(kāi)屬性,在錘子兵那邊調(diào)用。

然后是劍士的功能:

劍士功能最簡(jiǎn)單,但增加了一個(gè)暴擊的屬性,將暴擊率代入攻擊力的計(jì)算就好,腳本也繼承自弓箭手:

public class Pagoda3 : Pagoda

{

??? public float critChance = 0.2f; //暴擊率

??? //重新攻擊方法

??? public override void PagodaAttack()

??? {

??????? if (target != null)

??????? {

??????????? //代入暴擊率,計(jì)算最終傷害(暴擊是雙倍傷害)

??????????? int crit = (int)(critChance * 100);

??????????? target.Damage(damage * (Random.Range(0, 100) < crit ? 2 : 1), this);

??????? }

??? }

}

?

是不是很簡(jiǎn)單?

好了,三個(gè)人形防御塔的功能都做完了?,F(xiàn)在正式做安放防御塔的功能,用Image搭建一個(gè)防御塔菜單UI界面,放入精靈圖片,取名“PagodaMenu”:

在PagodaMenu下創(chuàng)建三個(gè)Image做頭像:

我是直接把模型放在紅色背景板前截圖

在道路旁擺上若干的防御塔地形,將層設(shè)為Pagoda:

我們先來(lái)看下放置的過(guò)程:

通過(guò)演示,我們大概可以理清創(chuàng)建的邏輯:

1. 點(diǎn)擊頭像實(shí)例化一個(gè)防御塔,并顯示攻擊范圍;

2. 按住鼠標(biāo)不放防御塔會(huì)跟隨鼠標(biāo)移動(dòng);

3. 攻擊范圍的顏色在可放置位置顯示為綠色,其余地方為紅色;

4. 在可放置位置彈起鼠標(biāo)時(shí),會(huì)將防御塔放在地形上,且同時(shí)為防御塔初始化。

根據(jù)以上的邏輯順序,我們首先要讓圖片具有可點(diǎn)擊事件與彈起事件。為頭像Image創(chuàng)建一個(gè)腳本,引入相應(yīng)接口:

public class IconElement : MonoBehaviour, IPointerDownHandler, IPointerUpHandler

{

??? //點(diǎn)擊事件

??? public void OnPointerDown(PointerEventData eventData)

??? {

??? }

??? //彈起事件

??? public void OnPointerUp(PointerEventData eventData)

??? {

??? }

}

?


然后可以從主攝像打出射線,將射線檢測(cè)點(diǎn)的坐標(biāo)賦給防御塔,可以放在在Update中調(diào)用。攻擊范圍的顯示可以通過(guò)創(chuàng)建一個(gè)普通球形物體來(lái)實(shí)現(xiàn),后面在代碼中調(diào)整顏色就行了。位置也跟隨射線檢測(cè)點(diǎn)移動(dòng),平時(shí)處于禁用狀態(tài),只有在擺放防御塔過(guò)程中調(diào)用:

現(xiàn)在將所有防御塔預(yù)制體放入路徑中:

預(yù)制體名字和頭像圖片的名字相同,且一一對(duì)應(yīng):

為Icon的腳本創(chuàng)建初始化方法:

??? string pagodaName; //防御塔名,用來(lái)加載防御塔

??? Camera mainCamera; //主攝像

??? Transform attRange; //攻擊范圍顯示器

??? Material ria; //范圍顯示器的材質(zhì)球

??? LayerMask layer; //射線可照射層

??? //初始化

??? public void Init(Camera _mainCamera, Transform _attRange)

??? {

??????? pagodaName = GetComponent<Image>().sprite.name; //自身圖片的名字就是對(duì)應(yīng)防御塔名字

??????? mainCamera = _mainCamera;

??????? attRange = _attRange;

??????? ria = attRange.GetComponent<MeshRenderer>().material;

??????? layer = LayerMask.GetMask("Ground") | LayerMask.GetMask("Way") | LayerMask.GetMask("Pagoda");

??? }

?

然后在點(diǎn)擊事件中寫(xiě)入點(diǎn)擊時(shí)要執(zhí)行的功能:


??? Pagoda pagodaObj; //防御塔實(shí)例???

//點(diǎn)擊頭像實(shí)例化防御塔

??? public void OnPointerDown(PointerEventData eventData)

??? {

??????? //加載防御塔模型

??????? pagodaObj = Instantiate(Resources.Load<Pagoda>("Prefab/Chara/PagodaChara/" + pagodaName));

??????? //啟用攻擊范圍顯示器并將防御塔攻擊方位反映在尺寸上

??????? attRange.gameObject.SetActive(true);

??????? attRange.localScale = new Vector3(pagodaObj.attactRange * 2, 10, pagodaObj.attactRange * 2);

??????? GetComponent<Image>().color = new Color(0, 1, 0); //頭像變色

??? }

?

彈起事件中根據(jù)條件判斷當(dāng)前是否可放置防御塔,判斷邏輯放在Update中:


??? bool isPlace; //是否可放置

??? Transform terrain; //可放置地形

??? //抬起鼠標(biāo)放置或刪除防御塔

??? public void OnPointerUp(PointerEventData eventData)

??? {

??????? if (isPlace) //可放置時(shí)

??????? {

??????????? //放置在該地形并成為地形子物體,然后初始化

??????????? pagodaObj.transform.position = terrain.position;

??????????? pagodaObj.transform.SetParent(terrain);

??????????? pagodaObj.initPagoda();

??????? }

??????? else //不可放置則銷(xiāo)毀

??????????? Destroy(pagodaObj.gameObject);

?

??????? attRange.gameObject.SetActive(false); //禁用范圍顯示器

??????? pagodaObj = null;

??????? GetComponent<Image>().color = new Color(1, 1, 1); //頭像變色

??? }

??? void Update()

??? {

??????? //如果防御塔實(shí)例化,則找尋可以放置的位置

??????? if (pagodaObj != null)

??????? {

??????????? //攝像機(jī)向鼠標(biāo)位置發(fā)射線

??????????? Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);

??????????? RaycastHit hit;

??????????? if (Physics.Raycast(ray, out hit, 500, layer))

??????????? {

??????????????? pagodaObj.transform.position = hit.point; //防御塔模型根據(jù)鼠標(biāo)移動(dòng)

??????????????? attRange.position = hit.point; //范圍顯示器根據(jù)鼠標(biāo)移動(dòng)

??????????????? int index = hit.collider.gameObject.layer; //獲取照射到物體的層

??????????????? //如果是可以放置的地形,并且該地形上沒(méi)有其它防御塔,就可以放置

??????????????? if (LayerMask.LayerToName(index) == "Pagoda" && hit.collider.transform.childCount == 0)

??????????????? {

??????????????????? isPlace = true;

??????????????????? terrain = hit.collider.transform;

??????????????????? ria.color = new Color(0, 1, 0, 0.3f);

?????????????? ?}

??????????????? else

??????????????? {

??????????????????? isPlace = false;

??????????????????? ria.color = new Color(1, 0, 0, 0.3f);

??????????????? }

??????????? }

??????? }

??? }

?

好的,頭像功能的腳本就做完了。頭像的數(shù)量可以根據(jù)防御塔具體數(shù)量增減。我們注意到每個(gè)頭像的初始化方法沒(méi)地方調(diào)用,可以創(chuàng)建一個(gè)管理類(lèi)來(lái)對(duì)它們統(tǒng)一初始化,將它掛在PagodaMenu上:

public class PagodaMenu : MonoBehaviour

{

??? public Camera mainCamera; //主攝像機(jī)

??? public Transform attRange; //攻擊范圍顯示器

??? public void Init()

??? {

??????? IconElement[] icons = GetComponentsInChildren<IconElement>();

??????? for (int i = 0; i < icons.Length; i++)

??????? {

??????????? icons[i].Init(mainCamera, attRange);

? ??????}

??? }

}

?

主攝像機(jī)和范圍顯示器在編輯器界面直接拖入。

到這里,我們之前在第一篇文章演示視頻里的功能就做完了。之后可能會(huì)做一些經(jīng)濟(jì)系統(tǒng)方面的功能,如消滅敵人可以獲得金錢(qián)、使用金錢(qián)購(gòu)買(mǎi)和升級(jí)防御塔等等。

工程鏈接:https://pan.baidu.com/share/init?surl=e8UOSkOtG7hr93t2kl3xnA

提取碼:oshk


有意向參與線下游戲開(kāi)發(fā)學(xué)習(xí)的童鞋,歡迎訪問(wèn):http://levelpp.com/

皮皮關(guān)的游戲開(kāi)發(fā)QQ群也歡迎各位強(qiáng)勢(shì)插入:869551769

Unity快速上手系列之4:《塔防》續(xù)的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
犍为县| 横山县| 东方市| 通城县| 玉溪市| 宁安市| 巴里| 江川县| 澎湖县| 乐昌市| 太仆寺旗| 桂阳县| 昆山市| 汕头市| 南宫市| 苍梧县| 石城县| 泰兴市| 霞浦县| 顺昌县| 剑河县| 沙湾县| 富裕县| 扎赉特旗| 哈密市| 裕民县| 建宁县| 玉山县| 太仆寺旗| 资溪县| 稷山县| 灵石县| 二连浩特市| 平和县| 德阳市| 乐陵市| 公主岭市| 南溪县| 浦江县| 龙井市| 神农架林区|