Unity入門理論+實踐篇之Luna

創建世界的主角

父子物體

首先創建一個cube物體

可以觀察到其在2D視角下的坐標為(0,0)

此時將cube物體拖拽到ldle_0下,如圖所示,并將其坐標值改為(2,2)

?此時再將ldle_0物體的坐標改為(1,1)并觀察ldle_0和cube的坐標

此時可以觀察到,ldle_0和cube整體位置發生改動,但cube的坐標卻沒有變,那是因為,cube作為ldle_0的子物體,其坐標值是相對于ldle_0來說,即以ldle_0為原點

此時再將cube拖拽出ldle_0?

此時可以看到cube的坐標值變為(3,3),那是因為cube不再作為ldle_0的子物體,其坐標軸以世界坐標軸為準

?

?1. 現在使物體Luna進行初步的移動

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){Vector2 position = transform.position;position.x = position.x + 0.1f;transform.position = position;}
}

Unity中輸入系統與C#浮點型變量(Input)

輸入系統即監聽用戶輸入的指令,如下圖所示為Input所有輸入監聽的對象

?現在獲取水平方向的輸入監聽

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + 0.1f;transform.position = position;}
}

由于Input.GetAxis返回的類型為float(浮點數類型)所以也要創建相應類型的變量去接收它的值,現在開始監聽水平方向的輸入指令,為了監聽輸入指令,于是就用了Debug.Log()函數(用于在控制面板上輸出)

Input.GetAxis(“Horizontal”)輸入范圍為-1~1

現在要先通過輸入來操縱物體Luna的移動,只需下面代碼就可以實現

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + 0.1f * horizontal;position.y = position.y + 0.1f * vertical;transform.position = position;}
}

但是速度過快,所以們需要加入Time.daltaTime使物體Luna以每秒0.1米的速度去移動

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + 0.1f * horizontal * Time.deltaTime;position.y = position.y + 0.1f * vertical * Time.deltaTime;transform.position = position;}
}

其中 horizonal * Time.daltaTime為1m/s

渲染順序與層級設置

?如上圖所示,將物體BattleBackground02拖入場景時,Luna被覆蓋住了,這是因為在2D視角下雖然所有物體的(x,y)為(0,0),但在unity3D渲染中還存在深度這一概念,即在3D視角下,攝像機的z軸值為-10,但BattleBackground02的z軸值為-1,除了這兩個物體以外的其他物體的z軸值為0,在視覺上來看,BattleBackground02在其他物體前面,所以后被渲染,這樣才出現了Luna被覆蓋的情況,為了在2D視角下解決這一問題,方法一是將BattleBackground02的z軸設置為0,但調整深度(z軸)有違于2D游戲的設置理念,所以此時就引出了下面概念,層級

層級(Layer)

可以在2D游戲物體中的SpriteRenderer和地形TilemapRenderer組件中的Order in layer屬性中去設置層級的大小,值越大,越后渲染,值越小,越先渲染,值大的游戲物體會覆蓋值小的游戲物體。

可以在如上圖看到,SpriteRenderer中的Order in layer值都為0,所以渲染順序是隨機,所以在不同電腦下所看到的實際展現效果不同

在實際制作中,層級總會隨著人物的變動而變換,如果一個個去調會很麻煩,因此,就需要進行相關的設置如下圖

其中default代表根據深度(z軸)來進行渲染?先將其改為Custom Axis(自定義)并將y軸的權重設置為1,z軸為0

中心點與軸心點

如上圖所示,Luna已經在馬車的輪子上面,但是Luna并沒有被覆蓋,而是要再往上平移一點才行,因此就要進行細節的設計

如圖所示,從視覺上來看物體Luna此時應該被覆蓋,但是卻沒有,因為此時的渲染層是按物體的中心點來渲染的

如圖所示House的y軸比Luna高,所以未被覆蓋,所以此時就要引出軸心點概念

軸心點(Pivot):

軸心點是可以自定義的特殊點,充當精靈的“錨點”精靈以此為支點進行旋轉,坐標點位置則是指軸
心點的位置。

現在只需將horse的軸心點改為如下所示

?Luna的層級改為如下所示

就可以實現覆蓋效果

注意:

雖然修改了軸心點,但一定要將Order in layer數值改為相同大小,不然雖然改變了軸心位置,但渲染順序不一致還是覆蓋不了(Luna會被后渲染)

Unity中的預制體(Prefab)

預制體(Prefab)是Unity中的一種特殊資源。預制體就是一個或者一系列組件的集合體,可以使用預制體實例化克降體,后續可對克隆體屬性進行統一修改。

創建預制體

預制體作為母體,他做的所有更改都會同步到其克隆體(子體)中

?斷開與預制體(母體)的連接,使其成為單獨個體

?制定世界的因果定律

Unity中的物理系統

剛體組件和碰撞器組件

剛體組件(Rigidbody):

使游戲物體能獲得重力,接受外界的受力和扭力功能的組件,可以通過腳本或是物理引擎為游戲對象添加剛體組件。

現在為物體Luna添加一個剛體組件SS

如上圖所示Rigidbody中的gravity scale屬性值為1,在實際運行中會使Luna沿著y軸一直掉出地圖,為了改變此狀態只需將gravity scale屬性值改為0即可

碰撞器組件

為了使游戲效果更真實,即當Luna碰到NPC時會被擋住過不去,因此就可以采用到碰撞器,現在為Luna和NPC裝上碰撞器組件

但此時發現在Luna碰到NPC時,自身會發生沿著z軸的旋轉,為了解決這一問題就需要用到下列操作固定物體在z軸上的角度

發生碰撞檢測的條件

雙方必須都有碰撞器,且一方有剛體,最好是運動的一方。因為Unity為了性能的優化,剛體長時間不發生碰撞檢測會休眠。

解決震動問題

在實際運行中,可以發現,當兩個碰撞器發生碰撞時,Luna會出現震動效果,這是因為在使用Rigid body2D和Boxcollider 2D這兩個組件時,物體會根據unity中已經設置好的固有邏輯(腳本)去對物體進行移動, 而在實際中物體會先根據用戶輸入的指令進行移動,然后在由系統腳本進行碰撞檢測,若是發生碰撞,系統邏輯會將物體移出,這將造成了用戶輸入指令和體統邏輯判定的一個滯后問題,從而發生震動

為了解決這個問題,我們可以修改代碼,使其直接控制游戲物體的剛體,使物體進行移動

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{private Rigidbody2D rigidbody;// Start is called before the first frame updatevoid Start(){rigidbody = GetComponent<Rigidbody2D>();}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + 3 * horizontal * Time.deltaTime;position.y = position.y + 3 * vertical * Time.deltaTime;//transform.position = position;rigidbody.MovePosition(position);}
}

實體化游戲世界地形并可與游戲人物交互

如下圖可以發現,Luna是可以橫穿整個地圖,但在實際開發中Luna碰到山體是是應該停止的,所以就需要用到碰撞器

現在在Maps下創建一個空物體并添加多面體碰撞器(Polygon Collider 2D),并及逆行相應的編輯

?創世的邏輯思維

訪問級別與訪問修飾符

public :公開的、公共的;

private: 私有的、只能在當前類的內部訪問;

protected:受保護的、只能在當前類的內部以及子類中訪問;

internal: 只能在當前項目中訪問,在項目外無法訪問。當時在本項目中 public 與 internal的訪問權限是一樣的。

protected internal:protected + internal ?只能在本項目的該類及該類的子類中訪問。

能夠修飾類的訪問修飾符只有兩個:public 、internal;

在修飾類時如果沒有修飾符,則默認為 internal。

class Program
{
? ? //默認為 internal,僅能在本項目中進行訪問
}
2、internal vs protected

在同一項目中,internal的訪問權限比protected的訪問權限要大;但是當不在同一個項目中時,protected可以在項目外的子類中被訪問,而internal只能在它的本項目中進行訪問。

3、訪問權限不一致

子類的訪問權限不能高于父類的訪問權限,不然會暴露父類的成員。

該文引用?https://blog.csdn.net/qq_61709335/article/details/129767609

編寫生命值

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{private Rigidbody2D rigidbody;public float movespeed = 0;public int maxHealth = 5;//最大生命值private int currentHealth;//Luna的當前最大生命值// Start is called before the first frame updatevoid Start(){rigidbody = GetComponent<Rigidbody2D>();currentHealth = maxHealth;}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + movespeed * horizontal * Time.deltaTime;position.y = position.y + movespeed * vertical * Time.deltaTime;/*transform.position = position;*/rigidbody.MovePosition(position);}public void ChangeHealth(int amount){currentHealth = Mathf.Clamp(currentHealth + amount,0,maxHealth);Debug.Log(currentHealth + "/" + maxHealth);}
}

函數、函數簽名、返回值與重載

1、構成重載的條件:參數類型不同或者參數個數不同(不嚴謹的),與返回值無關。

2、函數重載:同名的兩個函數的簽名(參數的類型、順序、個數)不能完全一致,否則就會異常。當兩個函數同名但簽名不一樣時,兩個都可以正常使用,即函數重載。函數重載是正確的也是常用的。和參數名無關。函數名拼寫一樣只是大小寫不一樣時,屬于函數不同名,C#大小寫敏感。

3、兩個函數是否可以重名,跟函數的返回值類型無關。只要函數的簽名,即參數的類型、順序、個數不一樣就行。只要參數的類型、順序、個數不一致才能函數重名,函數返回值類型一致與否無關。

變量賦值的順序

根劇代碼的逐行編譯規則,我們可知,以上面所示代碼為例,對于maxhealth這一變量而言,若在start函數里對其進行復制,再在監視面板里對其賦值,其最終的覆蓋順序為1. public int maxHealth = 5 2. start 3. 監視面板,最終呈現出的結果為監視面板里所設置的值

觸發器與發生觸發檢測的條件

  1. 至少一方需要有剛體(Rigidbody)或Kinematic剛體:如果兩個物體都要參與觸發事件,通常至少其中一個物體需要附有Rigidbody或Rigidbody2D(對于2D游戲)組件。如果使用的是靜態物體(如地形),作為觸發的一方則不必附加剛體。但是,如果雙方都是靜止的Kinematic剛體,則無法觸發事件。

  2. 設置Is Trigger屬性:在Inspector面板中,需要將至少一個碰撞器的“Is Trigger”選項勾選上,將其標記為觸發器。這樣,當這個標記為觸發器的碰撞器與其他碰撞器重疊時,會觸發特定的事件,而不是進行物理碰撞。

  3. 編寫事件處理代碼:為了響應觸發器事件,你需要在腳本中編寫相應的事件處理函數。對于3D對象,常用的方法是OnTriggerEnter, OnTriggerStay, 和 OnTriggerExit;對于2D對象,則是OnTriggerEnter2D, OnTriggerStay2D, 和 OnTriggerExit2D。這些方法會在進入觸發區域、停留于觸發區域、以及離開觸發區域時被調用。

  4. 添加 collider 并確保重疊:確保參與觸發的物體都有合適的碰撞器(Collider)或觸發器(Collider)組件,并且它們的形狀和尺寸能夠正確覆蓋你希望觸發事件發生的區域。

制作血瓶并實現回血功能

創建腳本之間的聯系

為了實現Luna實現回血功能的效果,現在創建一個物體Potion并插入相應的C#腳本,在其中裝上觸發檢測

?現在通過腳本觀測觸發器碰到Luna的那個組件

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{private void OnTriggerEnter2D(Collider2D collision){Debug.Log(collision);}
}

現在實現只要碰到血瓶血條+1的效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{private void OnTriggerEnter2D(Collider2D collision){/* Debug.Log(collision);*/LunaController luna = collision.GetComponent<LunaController>();luna.ChangeHealth(1);}
}

現在實現Luna碰撞血瓶后,血瓶消失

屬性的聲明定義與使用

?在前面的腳本編寫過程中,在腳本potion中是可以直接訪問腳本LunaController的各個屬性值(因為是public),但是為了防止人為開掛行為的出現(比如說把maxHealth的值調到無上限),所以就需要把各個屬性進行封裝

封裝(Encapsulation)

封裝是將數據和行為包裝在一起形成類的過程,將數據隱藏在類的內部,只能通過類的公共方法來訪問和修改。C# 中,我們可以使用訪問修飾符來實現封裝,如 private、protected、internal 和 public 等。

私有成員只能在類的內部訪問,受保護的成員可以在派生類中訪問,內部成員可以在同一個程序集中訪問,公共成員可以被所有代碼訪問。
通過封裝,可以避免數據被誤用或修改,增強了程序的安全性和可維護性。

示例如下:

調用封裝的

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{private void OnTriggerEnter2D(Collider2D collision){/* Debug.Log(collision);*/LunaController luna = collision.GetComponent<LunaController>();if(luna.Health < luna.MaxHealth){luna.ChangeHealth(1);}Destroy(gameObject);}
}

賦予任務個人的意識與行為

添加動畫制作組件并創建動畫控制器

?動畫控制器(Animator Controller)

根據當前游戲對象的狀態控制播放對應的游戲動畫。也可以叫做動畫狀態機。

動畫(Animation)

Unity中的動畫資源,可以由動畫控制器,代碼去進行播放,也是一種組件。

現在為Luna賦予個人意識和動作

創建一個Animator組件

為了更好地管理動畫制作組件,要創建一個Animators文件保存Animator Controller,以及為了更好地管理每個人物的動作,創建一個Animations文件,并在Animators文件下創建Animator Controller文件,文件名Luna

注:此時Luna有預制體,應對其預制體進行添加

動畫狀態機與狀態機的過渡條件

現在進行動畫的制作

打開動畫制作窗口

打開動畫狀態機面板

將已有的圖片拖入動畫制作窗口,并設置保存位置,如此便可一創作出動畫,并為每個動作加上關聯

通過如上圖所示,創建完人物動作以后,眾所周知,在實際中每個人物的狀態切換,都會有一個準備動作,如走——跑,但在2D游戲制作中是不需要這樣的,我們需要的是跟控制機器一樣地窗臺切換,所以就需要調整過渡條件

通過上圖可知,Unity3D對我們物體動畫動作之間的切換進行了融合,現在我們需要去掉融合

但是此時發現我們的動畫并沒有播放完,只需要將Exit time值改為1即可

混合書地使用方式和意義

在游戲設計中,一個人物走路地方向有八個方位,這樣創建動作之間地過渡條件會變得很多,形成蛛網,為了建設動畫設計窗口的復雜程度,就引出了混合樹的概念

混合樹(Blend Tree)

用于允許通過按不同程度組合所有動畫的各個部分來平滑混合多個動畫各個運動參與形成最終效果的量使用混合參數進行控制,該參數只是與動畫器控制器關聯的數值動畫參數之一。要使混合運動有意義,混合的運動必須具有相似性質和時間。混合樹是動畫器控制器中的特殊狀態類型,

下圖是已經別寫好的部分以及相關圖像運動

通過使人物運動

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{private Rigidbody2D rigidbody;public float movespeed = 30;private int maxHealth = 5;//最大生命值private int currentHealth;//Luna的當前最大生命值private Vector2 lookDirection;public int Health { get { return currentHealth; } }//返回currentHealthpublic int MaxHealth { get { return maxHealth; } }private Animator animator;// Start is called before the first frame updatevoid Start(){rigidbody = GetComponent<Rigidbody2D>();currentHealth = maxHealth;animator = GetComponentInChildren<Animator>();}// Update is called once per framevoid Update(){//監聽玩家輸入float horizontal = Input.GetAxis("Horizontal");//水平float vertical = Input.GetAxis("Vertical");//垂直Vector2 move = new Vector2(horizontal, vertical);animator.SetFloat("MoveValues", 0);/*Debug.Log(horizontal);*///當前玩家輸入某個軸向不為0if(!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0)){//判斷參數近似相等lookDirection.Set(move.x, move.y);lookDirection.Normalize();//單位化animator.SetFloat("MoveValues", 1);}//動畫控制animator.SetFloat("LookX", lookDirection.x);animator.SetFloat("LookY", lookDirection.y);//物體控制 Vector2 position = transform.position;position.x = position.x + movespeed * horizontal * Time.deltaTime;position.y = position.y + movespeed * vertical * Time.deltaTime;/*transform.position = position;*/rigidbody.MovePosition(position);}public void ChangeHealth(int amount){currentHealth = Mathf.Clamp(currentHealth + amount,0,maxHealth);Debug.Log(currentHealth + "/" + maxHealth);}
}

從實際上來看,雖然實現了跑步效果,但發現人物移動速度有時候快有時候慢,所以就需要把物理相關的函數放在FixedUpdate()里? ?

神之眼

Unity中的攝像機

導入包使攝像機跟隨人物移動

下載好以后創建如下圖所示相機類型

下載完后,Main Camera中會被自動插入相關組件如下圖所示

兩種不同的攝象模式(perspective,orthographic)

perspective(一般在3D游戲中使用)

orthographic(一般在2D游戲中使用)

為了使相機跟隨Luna進行移動,只需如下步驟即可

但是此時發現當Luna快走到邊界時,會出現如下情況

?

為了解決這種情況,需要給攝像機設置一個邊界值

如下圖所示,為MapW設置一個碰撞器,但一定要將其改為觸發器,否則,人物會被彈出地圖之外

現在就可以解決上面問題

使用代碼生成游戲物體并銷毀

實現在Luna吃掉物體后出現星光效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{public GameObject effectGo;private void OnTriggerEnter2D(Collider2D collision){/* Debug.Log(collision);*/LunaController luna = collision.GetComponent<LunaController>();if(luna.Health < luna.MaxHealth){luna.ChangeHealth(1);Destroy(gameObject);Instantiate(effectGo, transform.position, Quaternion.identity);}}
}

但是現在發現,在Luna吃掉血瓶后出現的星光效果沒有消失,現在要使其在播放完一定時間后消失

創建一個effect腳本使特效消失

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class effect : MonoBehaviour
{public float destoryTime;//設置銷毀時間// Start is called before the first frame updatevoid Start(){Destroy(gameObject, destoryTime);}// Update is called once per framevoid Update(){}
}

攀爬系統的實現

在動畫狀態機面板創建Boolean類型的變量Climb,并未動畫Climb賦值,如下圖所示

因為walk與idle,run動畫銜接沒有間隔,所以Climb不需要連接run,idle動畫,只需連接walk即可

在如上圖所示的藤蔓處創建攀爬效果再創建一個碰撞體并將其設置為觸發器即可,如下圖所示

現在就需要編寫代碼使Luna在此位置具有攀爬動作

在LunaContriller腳本下編寫Climb函數對動畫狀態機窗口的Climb進行賦值

 public void Climb(bool start){animator.SetBool("Climb", start);}

現在在Climb_area腳本下編寫代碼進行賦值

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Climb_area : MonoBehaviour
{private void OnTriggerEnter2D(Collider2D collision){/* if(collision.name == "Luna"){}if(collision.tag == "Luna"){}*/if (collision.CompareTag("Luna")){collision.GetComponent<LunaController>().Climb(true);}}
}

但此時發現Luna在進行攀爬動作時具有延遲動作,所以需要修改狀態機窗口

神明與世界交互的接口

現在對用戶界面的ui進行設計,在進行UI設計時,畫布Canvas是必要創建的物體

先創建一個Luna的頭像

制作Luna的血條和藍條,創建Panel_Main文件存放頭像,并且將畫布屬性調成屏幕屬性調成屏幕自適應模式(Scale With Screen Size)

在做外如上圖所示操作以后,需要進行一個血條填充操作,在血條位置上插入image

從上圖可知道血條框是一個不規則圖形,所有為了達到預想的呈現效果,進行如下圖所示操作,在空物體中創建一個遮罩組件(mask)和image組件

現在需要實現血條的填充效果,只需要改變其縮放就行,但其軸心點需要放在最左側

同理創造藍條

給游戲世界帶來戰爭

首先運用單例設計模式將游戲腳本進行管理

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{public GameObject effectGo;private void OnTriggerEnter2D(Collider2D collision){/* Debug.Log(collision);*//*LunaController luna = collision.GetComponent<LunaController>();if(luna.Health < luna.MaxHealth){luna.ChangeHealth(1);Destroy(gameObject);Instantiate(effectGo, transform.position, Quaternion.identity);}*/if (Gamemanager.Instance.Health < Gamemanager.Instance.MaxHealth){Gamemanager.Instance.ChangeHealth(1);Destroy(gameObject);Instantiate(effectGo, transform.position, Quaternion.identity);}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{private Rigidbody2D rigidbody;public float movespeed = 1;private Vector2 lookDirection;private Vector2 move;private Animator animator;private float moveScale; //移動狀態,停,走,跑// Start is called before the first frame updatevoid Start(){rigidbody = GetComponent<Rigidbody2D>();animator = GetComponentInChildren<Animator>();}// Update is called once per framevoid Update(){//監聽玩家輸入float horizontal = Input.GetAxis("Horizontal");//水平float vertical = Input.GetAxis("Vertical");//垂直move = new Vector2(horizontal, vertical);animator.SetFloat("MoveValues", 0);/*Debug.Log(horizontal);*///當前玩家輸入某個軸向不為0if(!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0)){//判斷參數近似相等lookDirection.Set(move.x, move.y);lookDirection.Normalize();//單位化/*animator.SetFloat("MoveValues", 1);*/}//動畫控制moveScale = move.magnitude;if(move.magnitude>0){if(Input.GetKey(KeyCode.LeftShift)){moveScale = 1;}else{moveScale = 2;}}animator.SetFloat("MoveValues", moveScale);animator.SetFloat("LookX", lookDirection.x);animator.SetFloat("LookY", lookDirection.y);}private void FixedUpdate(){//物體控制 Vector2 position = transform.position;/*position.x = position.x + movespeed * horizontal * Time.deltaTime;position.y = position.y + movespeed * vertical * Time.deltaTime;*//*transform.position = position;*/position = position + movespeed * move * Time.fixedDeltaTime;rigidbody.MovePosition(position);}public void Climb(bool start){animator.SetBool("Climb", start);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Gamemanager : MonoBehaviour
{public static Gamemanager Instance;private int maxHealth = 5;//最大生命值private int currentHealth;//Luna的當前最大生命值public int Health { get { return currentHealth; } }//返回currentHealthpublic int MaxHealth { get { return maxHealth; } }public void Awake(){Instance = this;currentHealth = 4;}public void ChangeHealth(int amount){currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);Debug.Log(currentHealth + "/" + maxHealth);}
}

現在將戰斗場景拖拽到攝像機前(注意z軸)

現在將戰斗狀態下的Luna和master拖拽到戰斗場景,并調整Flip使怪物正對著Luna(沿x軸翻轉180度)

現在進行跳躍系統的實現

在動畫狀態機窗口創建Jump,其類型為Boolean,在進行如下圖所示設置

創建Jump_area管理跳躍區域

并在其中文件安裝box collider組件,將其調到相應位置,并設置跳躍點A,B

如圖所示,下面場景的都被碰撞器組件所包圍,Luna物體具有剛體組件,在進行跳躍時會發生碰撞檢測,為了避免這種情況,就可以使Luna的Rigidbody組件暫時休眠(simulated屬性打勾不休眠,不打勾休眠),這樣就不會發生碰撞檢測

如圖所示

創建Jump_area腳本控制跳躍

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;public class Jump_area : MonoBehaviour
{public Transform jumpA;public Transform jumpB;// Start is called before the first frame updateprivate void OnTriggerEnter2D(Collider2D collision){if (collision.CompareTag("Luna")){LunaController luna = collision.transform.GetComponent<LunaController>();luna.Jump(true);//float disA = Vector3.Distance(luna.transform.position, jumpA.position);float disB = Vector3.Distance(luna.transform.position, jumpB.position);Transform targetTrans;/*if (disA > disB){targetTrans = jumpA;}else{targetTrans = jumpB;}*/targetTrans = disA > disB ? jumpA : jumpB;luna.transform.DOMove(targetTrans.position, 0.5f).OnComplete(() => endJump(luna));}}public void endJump(LunaController luna){luna.Jump(false);}
}
現在需要使跳躍效果更加真實

在此場景下,Luna組件由父物體和子物體一起控制,但在進行碰撞檢測時,腳本控制父物體和子物體移動會顯得矛盾,因此只需要做到父物體在移動(子物體會跟隨著一起移動),為了使運動效果更加逼真,只需調整子物體在有、軸上的位移即可,如下面所示

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;public class Jump_area : MonoBehaviour
{public Transform jumpA;public Transform jumpB;// Start is called before the first frame updateprivate void OnTriggerEnter2D(Collider2D collision){if (collision.CompareTag("Luna")){LunaController luna = collision.transform.GetComponent<LunaController>();luna.Jump(true);//float disA = Vector3.Distance(luna.transform.position, jumpA.position);float disB = Vector3.Distance(luna.transform.position, jumpB.position);Transform targetTrans;/*if (disA > disB){targetTrans = jumpA;}else{targetTrans = jumpB;}*/targetTrans = disA > disB ? jumpA : jumpB;//在移動完后執行endJump語句luna.transform.DOMove(targetTrans.position, 0.5f).OnComplete(() => endJump(luna));//獲取子物體Transform lunaLocalTrans = luna.transform.GetChild(0);//表示第0個元素Sequence sequence = DOTween.Sequence();//表示動畫的演示順序sequence.Append(lunaLocalTrans.DOLocalMoveY(1.5f, 0.25f));sequence.Append(lunaLocalTrans.DOLocalMoveY(0.61f, 0.25f));sequence.Play();}}public void endJump(LunaController luna){luna.Jump(false);}
}

為了使跳躍的速度由慢變快再變慢,只需如下圖所示

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;public class Jump_area : MonoBehaviour
{public Transform jumpA;public Transform jumpB;// Start is called before the first frame updateprivate void OnTriggerEnter2D(Collider2D collision){if (collision.CompareTag("Luna")){LunaController luna = collision.transform.GetComponent<LunaController>();luna.Jump(true);//float disA = Vector3.Distance(luna.transform.position, jumpA.position);float disB = Vector3.Distance(luna.transform.position, jumpB.position);Transform targetTrans;/*if (disA > disB){targetTrans = jumpA;}else{targetTrans = jumpB;}*/targetTrans = disA > disB ? jumpA : jumpB;//在移動完后執行endJump語句,表示速度以線性的方式呈現luna.transform.DOMove(targetTrans.position, 0.5f).SetEase(Ease.Linear).OnComplete(() => endJump(luna));//獲取子物體Transform lunaLocalTrans = luna.transform.GetChild(0);//表示第0個元素Sequence sequence = DOTween.Sequence();//表示動畫的演示順序sequence.Append(lunaLocalTrans.DOLocalMoveY(1.5f, 0.25f).SetEase(Ease.InOutSine));//表示速度以正弦函數的方式呈現sequence.Append(lunaLocalTrans.DOLocalMoveY(0.61f, 0.25f).SetEase(Ease.InOutSine));sequence.Play();}}public void endJump(LunaController luna){luna.Jump(false);}
}

敵人腳本的成員變量與組件的獲取

現在將怪物拖拽到場景中,并創建EnemyController腳本控制其運動(巡邏)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EnemyController : MonoBehaviour
{public bool vertical;public float speed = 5;private Rigidbody2D rigidbody2d;//控制方向private int direction = 1;//控制方向改變時間間隔public float changeTime = 5;//計時器public float Timer;//動畫控制器private Animator animator;// Start is called before the first frame updatevoid Start(){rigidbody2d = GetComponent<Rigidbody2D>();Timer = changeTime;animator = GetComponent<Animator>();}// Update is called once per framevoid Update(){Timer -= Time.fixedDeltaTime;if (Timer < 0){Timer = 5;direction = -1 * direction;}}private void FixedUpdate(){Vector3 pos = rigidbody2d.position;if (vertical)//垂直方向移動{animator.SetFloat("LookY", direction * 1);animator.SetFloat("LookX", 0);pos.y = pos.y + speed * direction * Time.fixedDeltaTime;}else//水平軸向移動{animator.SetFloat("LookY", 0);animator.SetFloat("LookX", direction * 1);pos.x = pos.x + speed * direction * Time.fixedDeltaTime;}rigidbody2d.MovePosition(pos);}
}

觸發戰斗與戰斗動畫控制器

創建如下圖所示,戰斗場景

現在創建BattleController腳本來控制戰斗腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BattleController : MonoBehaviour
{public Animator LunaBattleAnimator;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}public void LunaAttack(){StartCoroutine(PerformAttackLogic());}IEnumerator PerformAttackLogic(){UI_Manager.Instance.ShowOrHideBattlePanel(false);LunaBattleAnimator.SetBool("MoveValues", true);LunaBattleAnimator.SetFloat("MoveValues", -1);yield return null;}
}

再對UImanager創建相關函數控制其的顯現(ShowOrHideBattlePanel)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class UI_Manager : MonoBehaviour
{public static UI_Manager Instance;public Image hpmask_image;public Image mpmask_image;public float originalSize;//血條原始寬度public GameObject BattlePanelGo;void Awake(){Instance = this;originalSize = hpmask_image.rectTransform.rect.width;SetHPValue(0.5f);}// Update is called once per framepublic void SetHPValue(float fillPercent){hpmask_image.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, fillPercent * originalSize);}public void SetMPValue(float fillPercent){mpmask_image.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, fillPercent * originalSize);}public void ShowOrHideBattlePanel(bool show){BattlePanelGo.SetActive(show);}
}

對Button_Attack按鈕進行賦值

以下是效果展現

但我們很快發現Luna并沒有返回,現在需要進行如下設置

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;public class BattleController : MonoBehaviour
{public Animator LunaBattleAnimator;public Transform LunaTrans;//獲取Luna_Battle和Master的Transform組件public Transform MonsterTrans;private Vector3 monsterInitPos;//獲取Luna_Battle、怪物初始位置private Vector3 LunaInitPos;public SpriteRenderer MonsterSr;//獲取怪物的SpriteRenderer組件// Start is called before the first frame updatevoid Start(){monsterInitPos = MonsterTrans.localPosition;//賦值為相對于父物體的位置LunaInitPos = LunaTrans.localPosition;}// Update is called once per framevoid Update(){}public void LunaAttack(){StartCoroutine(PerformAttackLogic());}IEnumerator PerformAttackLogic(){UI_Manager.Instance.ShowOrHideBattlePanel(false);LunaBattleAnimator.SetBool("MoveState", true);LunaBattleAnimator.SetFloat("MoveValue", -1);//運用0.5秒時間運動到monster前面一米LunaTrans.DOLocalMove(monsterInitPos + new Vector3(1, 0, 0), 0.5f).OnComplete(() =>{LunaBattleAnimator.SetBool("MoveState", false);LunaBattleAnimator.SetFloat("MoveValue", 0);LunaBattleAnimator.CrossFade("Attack", 0);//直接播放Attack動畫,層級為0MonsterSr.DOFade(0.3f, 0.2f).OnComplete(() =>{JudgeMonsterHP(20);//透明度恢復正常});//是怪物在被攻擊到后出現隱身效果,動畫播放時長為0.2秒,透明度參數(0.3f)范圍為(0-1.0f),表示透明程度,若不用此函數,其范圍為0-255});yield return new WaitForSeconds(1.667f);MonsterTrans.DOLocalMove(monsterInitPos, 0.3f);LunaBattleAnimator.SetBool("MoveState", true);LunaBattleAnimator.SetFloat("MoveValue", 1);LunaTrans.DOLocalMove(LunaInitPos, 0.5f).OnComplete(()=> { LunaBattleAnimator.SetBool("MoveState", false); });//使luna變成Idle狀態yield return new WaitForSeconds(0.8f);StartCoroutine(MonsterAttack());

現在仿照上面做出Monster的攻擊行為

IEnumerator MonsterAttack(){UI_Manager.Instance.ShowOrHideBattlePanel(false);UI_Manager.Instance.ShowOrHideBattlePanel(false);MonsterTrans.DOLocalMove(LunaInitPos - new Vector3(1, 0, 0), 0.5f).OnComplete(() =>{LunaBattleAnimator.CrossFade("Hit", 0);//直接播放Hit動畫,層級為0LunaSr.DOFade(0.3f, 0.2f).OnComplete(() =>{JudgeLunaHP(20);//透明度恢復正常});//是Luna在被攻擊到后出現隱身效果,動畫播放時長為0.2秒,透明度參數(0.3f)范圍為(0-1.0f),表示透明程度,若不用此函數,其范圍為0-255});yield return new WaitForSeconds(1.333f);LunaBattleAnimator.SetBool("MoveState", true);LunaBattleAnimator.SetFloat("MoveValue", 1);MonsterTrans.DOLocalMove(monsterInitPos, 0.5f).OnComplete(() => { LunaBattleAnimator.SetBool("MoveState", false); });//使luna變成Idle狀態yield return new WaitForSeconds(0.5f);UI_Manager.Instance.ShowOrHideBattlePanel(true);}

現在開始制作Luna的防御行為

 public void LunaDefend(){StartCoroutine(PerformDefendLogic());}IEnumerator PerformDefendLogic(){UI_Manager.Instance.ShowOrHideBattlePanel(false);LunaBattleAnimator.SetBool("Defend", true);MonsterTrans.DOLocalMove(LunaInitPos - new Vector3(1.5f, 0, 0), 0.5f);yield return new WaitForSeconds(0.5f);LunaBattleAnimator.SetFloat("MoveValue", -1);//運用0.5秒時間運動到monster前面一米MonsterTrans.DOLocalMove(LunaInitPos, 0.2f).OnComplete(() =>{MonsterTrans.DOLocalMove(LunaInitPos - new Vector3(1.5f, 0, 0), 0.5f);LunaTrans.DOLocalMove(LunaInitPos + new Vector3(1f, 0, 0), 0.2f).OnComplete(() =>{LunaTrans.DOLocalMove(LunaInitPos, 0.2f);});});yield return new WaitForSeconds(0.4f);MonsterTrans.DOLocalMove(monsterInitPos, 0.3f).OnComplete(()=> {UI_Manager.Instance.ShowOrHideBattlePanel(true);LunaBattleAnimator.SetBool("Defend", false);});}

藍耗判定與藍量血量的增加減少方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Gamemanager : MonoBehaviour
{public static Gamemanager Instance;public int LunaHP;//最大生命值public int LunacurrentHP;//Luna的當前最大生命值public int LunaMP;//最大藍量public int LunaCurrentMP;//當前藍量/*public int Health { get { return LunacurrentHP; } }//返回LunacurrentHP*/public int MaxHP { get { return LunaHP; } }public GameObject BattleGo;//戰斗場景,游戲物體public void Awake(){Instance = this;}/*public void ChangeHealth(int amount){LunacurrentHP = Mathf.Clamp(LunacurrentHP + amount, 0, LunaHP);Debug.Log(LunacurrentHP + "/" + LunaHP);}*/public void EnterOrExitBallte(bool enter = true){BattleGo.SetActive(enter);}//回血public void AddOrDecreaseHP(int value){LunacurrentHP += value;if (LunacurrentHP >= LunaHP){LunacurrentHP = LunaHP;}if (LunaCurrentMP <= 0){LunaCurrentMP = 0;}UI_Manager.Instance.SetHPValue((float)LunacurrentHP / LunaHP);}//回藍public void AddOrDecreaseMP(int value){LunaCurrentMP += value;if (LunaCurrentMP >= LunaHP){LunaCurrentMP = LunaHP;}if (LunaCurrentMP <= 0){LunaCurrentMP = 0;}UI_Manager.Instance.SetMPValue((float)LunaCurrentMP / LunaMP);}//是否能使用技能public bool CanUseMP(int value){return LunaCurrentMP >= value;}
}

對話系統的UI與對話信息結構體

創建NPCDialog腳本控制Luna與NPC的對話(DialogInfo)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class NpcDialog : MonoBehaviour
{private List<DialogInfo[]> dialogInfoList;private int contentIndex;//控制每一段的每一條的索引public Animator animator;// Start is called before the first frame updatevoid Start(){dialogInfoList = new List<DialogInfo[]>(){new DialogInfo[]{new DialogInfo() {name="Luna",content="(,,・?・)ノ゛hello,我是LuNa,你可以用上下左右控制我移動,空格鍵與NPC進行對話,戰斗中需要簡單點擊按鈕執行相應行為" }},new DialogInfo[]{new DialogInfo() {name="Nala",content="好久不見了,小貓咪(*ΦωΦ*),Luna~" },new DialogInfo() {name="Luna",content="好久不見,Nala,你還是那么有活力,哈哈" },new DialogInfo() {name="Nala",content="還好吧~" },new DialogInfo() {name="Nala",content="我的狗一直在叫,但是我這會忙不過來,你能幫我安撫一下它嗎?" },new DialogInfo() {name="Luna",content="啊?" },new DialogInfo() {name="Nala",content="(,,′?ω?)ノ(′っω?`。)摸摸他就行,摸摸說呦西呦西,真是個好孩子吶" },new DialogInfo() {name="Nala",content="別看他叫的這么兇,其實他就是想引起別人的注意" },new DialogInfo() {name="Luna",content="可是。。。。" },new DialogInfo() {name="Luna",content="我是貓女郎啊" },new DialogInfo() {name="Nala",content="安心啦,不會咬你噠,去吧去吧~" },},new DialogInfo[]{new DialogInfo() {name="Nala",content="他還在叫呢" }},//3new DialogInfo[]{new DialogInfo() {name="Nala",content="感謝你吶,Luna,你還是那么可靠!" },new DialogInfo() {name="Nala",content="我想請你幫個忙好嗎" },new DialogInfo() {name="Nala",content="說起來這事怪我。。。" },new DialogInfo() {name="Nala",content="今天我睡過頭了,出門比較匆忙" },new DialogInfo() {name="Nala",content="然后裝蠟燭的袋子口子沒封好!o(╥﹏╥)o" },new DialogInfo() {name="Nala",content="結果就。。。蠟燭基本丟完了" },new DialogInfo() {name="Luna",content="你還是老樣子,哈哈。。" },new DialogInfo() {name="Nala",content="所以,所以嘍,你幫幫忙,幫我把蠟燭找回來" },new DialogInfo() {name="Nala",content="如果你能幫我找回全部的5根蠟燭,我就送你一把神器" },new DialogInfo() {name="Luna",content="神器?(ˉ﹃ˉ)" },new DialogInfo() {name="Nala",content="是的,我感覺很適合你,加油吶~" },},new DialogInfo[]{new DialogInfo() {name="Nala",content="你還沒幫我收集到所有的蠟燭,寶~" },},//5new DialogInfo[]{new DialogInfo() {name="Nala",content="可靠啊!竟然一個不差的全收集回來了" },new DialogInfo() {name="Luna",content="你知道多累嗎?" },new DialogInfo() {name="Luna",content="你到處跑,真的很難收集" },new DialogInfo() {name="Nala",content="辛苦啦辛苦啦" },new DialogInfo() {name="Nala",content="這是給你的獎勵" },new DialogInfo() {name="Nala",content="藍紋火錘,傳說中的神器" },new DialogInfo() {name="Nala",content="應該挺適合你的" },new DialogInfo() {name="Luna",content="~~獲得藍紋火錘~~(遇到怪物可觸發戰斗)" },new DialogInfo() {name="Luna",content="哇,謝謝你!Thanks?(・ω・)ノ" },new DialogInfo() {name="Nala",content="嘿嘿(*^▽^*),咱們的關系不用客氣" },new DialogInfo() {name="Nala",content="正好,最近山里出現了一堆怪物,你也算為民除害,幫忙清理5只怪物" },new DialogInfo() {name="Luna",content="啊?" },new DialogInfo() {name="Luna",content="這才是你的真實目的吧?!" },new DialogInfo() {name="Nala",content="拜托拜托啦,否則真的很不方便我賣東西" },new DialogInfo() {name="Luna",content="無語中。。。" },new DialogInfo() {name="Nala",content="求求你了,啵啵~" },new DialogInfo() {name="Luna",content="哎,行吧,誰讓你大呢~" },new DialogInfo() {name="Nala",content="嘻嘻,那辛苦寶子啦" }},new DialogInfo[]{new DialogInfo() {name="Nala",content="寶,你還沒清理干凈呢,這樣我不方便嘛~" },},new DialogInfo[]{new DialogInfo() {name="Nala",content="真棒,luna,周圍的居民都會十分感謝你的,有機會來我家喝一杯吧~" },new DialogInfo() {name="Luna",content="我覺得可行,哈哈~" }},new DialogInfo[]{new DialogInfo() {name="Nala",content="改天再見嘍~" },}};Gamemanager.Instance.dialogInfoIndex = 0;contentIndex = 1;}// Update is called once per framevoid Update(){}public void DisplayDialog(){if (Gamemanager.Instance.dialogInfoIndex > 7){return;}if (contentIndex >= dialogInfoList[Gamemanager.Instance.dialogInfoIndex].Length){//當前對話結束,可以控制LunacontentIndex = 0;UI_Manager.Instance.ShowDialog();Gamemanager.Instance.canControlLuna = true;}else{DialogInfo dialogInfo = dialogInfoList[Gamemanager.Instance.dialogInfoIndex][contentIndex];UI_Manager.Instance.ShowDialog(dialogInfo.content, dialogInfo.name);contentIndex++;animator.SetTrigger("Talk");}}
}
public struct DialogInfo
{public string name;public string content;
}

創建Talk函數控制Luna在與Nala對話時的動作(在LunaController下進行設置)

public void Talk(){//設置觸發器檢測的半徑Collider2D collider = Physics2D.OverlapCircle(rigidbody.position,0.5f, LayerMask.GetMask("NPC"));if (collider != null){if (collider.name == "Nala"){Gamemanager.Instance.canControlLuna = false;collider.GetComponent<NpcDialog>().DisplayDialog();}else if (collider.name == "Dog"&& !Gamemanager.Instance.hasPetTheDog &&Gamemanager.Instance.dialogInfoIndex == 2){PetTheDog();Gamemanager.Instance.canControlLuna = false;collider.GetComponent<Dog>().BeHappy();}}}

其中Physics2D.OverlapCircle控制檢測半徑,LayerMask.GetMask("NPC")表示只有在觸碰到層級為NPC的游戲物體才會進行觸發

層級設置如下

并且在LunaController中的update函數中編寫如下腳本

void Update(){//監聽玩家輸入float horizontal = Input.GetAxis("Horizontal");//水平float vertical = Input.GetAxis("Vertical");//垂直move = new Vector2(horizontal, vertical);animator.SetFloat("MoveValues", 0);/*Debug.Log(horizontal);*///當前玩家輸入某個軸向不為0if(!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0)){//判斷參數近似相等lookDirection.Set(move.x, move.y);lookDirection.Normalize();//單位化/*animator.SetFloat("MoveValues", 1);*/}//動畫控制moveScale = move.magnitude;if(move.magnitude>0){if(Input.GetKey(KeyCode.LeftShift)){moveScale = 1;movespeed = initspeed/2;}else{moveScale = 2;movespeed = initspeed;}}animator.SetFloat("MoveValues", moveScale);animator.SetFloat("LookX", lookDirection.x);animator.SetFloat("LookY", lookDirection.y);//按下空格可以進行交互if (Input.GetKeyDown(KeyCode.Space)){Talk();}}

實現對話之間的跳轉

public void DisplayDialog(){if (Gamemanager.Instance.dialogInfoIndex > 7){return;}if (contentIndex >= dialogInfoList[Gamemanager.Instance.dialogInfoIndex].Length){if (Gamemanager.Instance.dialogInfoIndex == 2 &&!Gamemanager.Instance.hasPetTheDog){}else if (Gamemanager.Instance.dialogInfoIndex == 4 &&Gamemanager.Instance.candleNum < 5){}else if (Gamemanager.Instance.dialogInfoIndex == 6 &&Gamemanager.Instance.killNum < 5){}else{Gamemanager.Instance.dialogInfoIndex++;}//在完成前面的任務以后進行清怪任務if (Gamemanager.Instance.dialogInfoIndex == 6){Gamemanager.Instance.ShowMonsters();}contentIndex = 0;UI_Manager.Instance.ShowDialog();Gamemanager.Instance.canControlLuna = true;}else{DialogInfo dialogInfo = dialogInfoList[Gamemanager.Instance.dialogInfoIndex][contentIndex];UI_Manager.Instance.ShowDialog(dialogInfo.content, dialogInfo.name);contentIndex++;animator.SetTrigger("Talk");//使只有Luna在觸碰到Nala并且有對話時,Nala才會進行Talk動畫}}

任務一:安撫狗狗

在Dog物體上設置Dog腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Dog : MonoBehaviour
{private Animator animator;public GameObject starEffect;public AudioClip petSound;// Start is called before the first frame updatevoid Start(){animator = GetComponent<Animator>();}public void BeHappy(){animator.CrossFade("Comfortable", 0);Gamemanager.Instance.hasPetTheDog = true;Gamemanager.Instance.SetContentIndex();Destroy(starEffect);Gamemanager.Instance.PlaySound(petSound);Invoke("CanControlLuna", 2.5f);}private void CanControlLuna(){Gamemanager.Instance.canControlLuna = true;}
}

任務二:收集蠟燭以及清楚怪物任務

在Candle腳本中進行如下設置

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Candle : MonoBehaviour
{public GameObject effectGo;private void OnTriggerEnter2D(Collider2D collision){Gamemanager.Instance.candleNum++;/* Debug.Log(collision);*/Gamemanager.Instance.AddOrDecreaseHP(1);Destroy(gameObject);Instantiate(effectGo, transform.position, Quaternion.identity);/*LunaController luna = collision.GetComponent<LunaController>();Gamemanager.Instance.ChangeHealth(1);*/if (Gamemanager.Instance.candleNum >= 5){Gamemanager.Instance.SetContentIndex();}Destroy(gameObject);}
}

對函數進行如下改動

Gamemanager:

public void EnterOrExitBallte(bool enter = true,int addkillNum = 0){UI_Manager.Instance.ShowOrHideBattlePanel(enter);BattleGo.SetActive(enter);enterBattle = enter;if (!enter)//非戰斗狀態{killNum += addkillNum;if (addkillNum > 0){DestoryMonster();}MonsterCurrentHP = 50;if (LunaCurrentMP <= 0){LunacurrentHP = 100;LunaCurrentMP = 0;battleMonsterGo.transform.position += new Vector3(0, 2, 0);}}}

BattleController

private void JudgeMonsterHP(int value){if (Gamemanager.Instance.AddOrDecreaseMonsterHP(value) <= 0){Gamemanager.Instance.EnterOrExitBallte(false , 1);}else{MonsterSr.DOFade(1, 0.4f);}}

現在插入背景音樂

Gamemanager

public AudioSource audioSource;//音樂組件
public AudioClip normalClip;
public AudioClip battleClip;
public void PlayMusic(AudioClip audioClip){if (audioSource.clip != audioClip){audioSource.clip = audioClip;audioSource.Play();}}public void PlaySound(AudioClip audioClip){if (audioClip){audioSource.PlayOneShot(audioClip);}}
public void EnterOrExitBallte(bool enter = true,int addkillNum = 0){UI_Manager.Instance.ShowOrHideBattlePanel(enter);BattleGo.SetActive(enter);enterBattle = enter;if (!enter)//非戰斗狀態{killNum += addkillNum;if (addkillNum > 0){DestoryMonster();}MonsterCurrentHP = 50;PlayMusic(normalClip);if (LunaCurrentMP <= 0){LunacurrentHP = 100;LunaCurrentMP = 0;battleMonsterGo.transform.position += new Vector3(0, 2, 0);}}else//戰斗狀態{PlayMusic(battleClip); }}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/16681.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/16681.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/16681.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

95.網絡游戲逆向分析與漏洞攻防-ui界面的設計-ui的設計與架構

免責聲明&#xff1a;內容僅供學習參考&#xff0c;請合法利用知識&#xff0c;禁止進行違法犯罪活動&#xff01; 如果看不懂、不知道現在做的什么&#xff0c;那就跟著做完看效果&#xff0c;代碼看不懂是正常的&#xff0c;只要會抄就行&#xff0c;抄著抄著就能懂了 內容…

基于jeecgboot-vue3的Flowable新建流程定義(二)

因為這個項目license問題無法開源&#xff0c;更多技術支持與服務請加入我的知識星球。 接上一節 4、編輯好后進行保存&#xff0c;保存代碼如下&#xff1a; /*保存流程定義*/const save (data: any) > {//console.log("save data", data); // { process: {..…

python從入門到精通05

一、輸入身份證號碼輸出對應省份 id_number "4313212002" province_code id_number[0] id_number[1] province_code_list {43:"湖南"} print("身份證號對應省份是&#xff1a;",province_code_list[province_code]) 二、統計需要取快遞人員…

博客增長與數據分析:不可不知的 6 大策略

CSDN 的朋友你們好&#xff0c;我是何未來&#xff0c;一個熱愛編程和寫作的計算機本科生&#xff0c;今天給大家帶來專欄【程序員博主教程&#xff08;完全指南&#xff09;】的第 11 篇文章“分析和追蹤博客表現”。本篇文章為你揭示了如何通過數據洞察來優化你的技術博客&am…

-bash: locate: 未找到命令(解決辦法)

-bash: locate: 未找到命令的解決辦法 一、解決辦法二、什么是locate三 、locate命令的具體用法 一、解決辦法 CentOS7默認沒有安裝locate命令&#xff0c;安裝方式如下&#xff1a; 執行以下命令進行安裝&#xff1a; yum install mlocate用 updatedb 指令創建 或更新locate …

數學建模--LaTeX基本介紹和入門

1.引言 &#xff08;1&#xff09;上次我們介紹到了我們這個團隊第一次參加這個數學建模比賽&#xff0c;就是這個電工杯&#xff0c;我是一名論文手&#xff0c;我們在這個下午也是對于這個比賽過程中出現的問題做了相應的分析&#xff0c;每個人也是進行了反思&#xff0c;知…

Tomcat源碼解析(七):底層如何獲取請求url、請求頭、json數據?

Tomcat源碼系列文章 Tomcat源碼解析(一)&#xff1a;Tomcat整體架構 Tomcat源碼解析(二)&#xff1a;Bootstrap和Catalina Tomcat源碼解析(三)&#xff1a;LifeCycle生命周期管理 Tomcat源碼解析(四)&#xff1a;StandardServer和StandardService Tomcat源碼解析(五)&…

golang實現mediasoup的tcp服務及channel通道

tcp模塊 定義相關類 Client&#xff1a;表示客戶端連接&#xff0c;包含網絡連接conn、指向服務器的指針Server和Channel指針c。server&#xff1a;表示TCP服務器&#xff0c;包含服務器地址address、TLS配置config以及三個回調函數&#xff1a; onNewClientCallback&#xf…

最大連續1的個數(滑動窗口)

算法原理&#xff1a; 這道題大眼一看是關于翻轉多少個0的問題&#xff0c;但是&#xff0c;如果你按照這種思維去做題&#xff0c;肯定不容易。所以我們要換一種思維去做&#xff0c;這種思維不是一下就能想到的&#xff0c;所以想不到也情有可原。 題目是&#xff1a;給定一…

Vue3:動態路由+子頁面(新增、詳情頁)動態路由配置(代碼全注釋)

文章目錄 實現思路調用后端接口獲取用戶權限獲取頁面權限動態綁定到路由對象中動態添加子頁面路由 實現思路 emm&#xff0c;項目中使用動態路由實現根據后端返回的用戶詳情信息&#xff0c;動態將該用戶能夠訪問的頁面信息&#xff0c;動態生成并且綁定到路由對象中。但是后…

如何從清空的回收站中恢復已刪除的Excel文件?

“嗨&#xff0c;幾天前我刪除了很多沒有備份的Excel文件。回收站已清空。當我意識到我犯了一個大錯誤時&#xff0c;所有的Excel文件都消失了&#xff0c;回收站里什么都沒有。清空回收站后是否可以恢復已刪除的 Excel 文件&#xff1f; 回收站是一種工具&#xff0c;可讓您在…

LeetCode 343. 整數拆分 (dp動態規劃)

343. 整數拆分 力扣題目鏈接(opens new window) 給定一個正整數 n&#xff0c;將其拆分為至少兩個正整數的和&#xff0c;并使這些整數的乘積最大化。 返回你可以獲得的最大乘積。 示例 1: 輸入: 2輸出: 1解釋: 2 1 1, 1 1 1。 示例 2: 輸入: 10輸出: 36解釋: 10 3 …

【openlayers系統學習】4.2Mapbox 樣式渲染圖層

二、Mapbox 樣式渲染圖層 顯然我們目前的地圖需要一些樣式。 VectorTile? 圖層的樣式與 Vector? 圖層的樣式工作方式完全相同。那里描述的樣式在這里也適用。 對于這樣的地圖&#xff0c;創建數據驅動的樣式&#xff08;對矢量圖層操作&#xff09;非常簡單。但矢量切片也用…

單兵組網設備+指揮中心:集群系統技術詳解

一、單兵設備功能特點 單兵組網設備是現代通信技術的重要成果&#xff0c;旨在為單個作戰或工作單元提供高效的通信和數據傳輸能力。其主要功能特點包括&#xff1a; 1. 便攜性&#xff1a;設備輕巧&#xff0c;便于單兵攜帶和使用&#xff0c;適應各種復雜環境。 2. 通信能…

簡述vue-router 組件復用導致路由參數失效怎么辦

當使用Vue Router時&#xff0c;組件復用可能會導致路由參數失效的問題。為了解決這個問題&#xff0c;我們可以采取以下策略&#xff1a; 1. 監聽路由變化 在Vue組件中&#xff0c;我們可以使用watch屬性來監聽$route對象的變化。當路由發生變化時&#xff0c;如果目標組件是…

第 8 章 機器人實體導航實現_路徑規劃(自學二刷筆記)

重要參考&#xff1a; 課程鏈接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 講義鏈接:Introduction Autolabor-ROS機器人入門課程《ROS理論與實踐》零基礎教程 9.3.5 導航實現05_路徑規劃 路徑規劃仍然使用 navigation 功能包集中的 move_base 功能包。 5.1編寫launch文…

PHP之fastadmin系統配置分組增加配置和使用

目錄 一、實現功能&#xff1a;fasttadmin實現添加系統配置分組和添加參數、使用 二、添加分組 三、配置分組參數 四、最終存儲位置 五、獲取配置參數 一、實現功能&#xff1a;fasttadmin實現添加系統配置分組和添加參數、使用 二、添加分組 在字典配置中找到分組對應鍵值…

linux系統——top資源管理器

在linux系統中&#xff0c;有類似于windows系統中的資源管理器&#xff0c;top用于實時的監控系統的任務執行狀態以及硬件配置信息 在linux中&#xff0c;輸入top命令&#xff0c;可以進入相應界面&#xff0c;在此界面可以使用一些指令進行操作 如&#xff0c;輸入z 可以改變…

終端安全管理系統、天銳DLP(數據泄露防護系統)| 數據透明加密保護,防止外泄!

終端作為企業員工日常辦公、數據處理和信息交流的關鍵工具&#xff0c;承載著企業運營的核心信息資產。一旦終端安全受到威脅&#xff0c;企業的敏感數據將面臨泄露風險&#xff0c;業務流程可能遭受中斷&#xff0c;甚至整個企業的運營穩定性都會受到嚴重影響。 因此&#xff…

【EVI】Hume AI 初探

寫在前面的話 Hume AI宣布已在B輪融資中籌集5000萬美元&#xff0c;由前Google DeepMind研究員Alan Cowen創立并擔任CEO。該AI模型專注于理解人類情感&#xff0c;并發布了「共情語音界面」演示&#xff0c;通過語音對話實現互動。從 Hume AI 官網展示的信息&#xff0c;EVI 能…