古詩詞大全網 - 古詩大全 - unity 怎麽銷毀shuriken

unity 怎麽銷毀shuriken

在本篇教程中,我們將使用簡單的物理機制模擬壹個動態的2D水體。我們將使用壹個線性渲染器、網格渲染器,觸發器以及粒子的混合體來創造這壹水體效果,最終得到可運用於妳下款遊戲的水紋和水花。這裏包含了Unity樣本源,但妳應該能夠使用任何遊戲引擎以相同的原理執行類似的操作。

設置水體管理器

我們將使用Unity的壹個線性渲染器來渲染我們的水體表面,並使用這些節點來展現持續的波紋。

unity-water-linerenderer(from gamedevelopment)

我們將追蹤每個節點的位置、速度和加速情況。為此,我們將會使用到陣列。所以在我們的類頂端將添加如下變量:

float[] xpositions;

float[] ypositions;

float[] velocities;

float[] accelerations;

LineRenderer Body;

LineRenderer將存儲我們所有的節點,並概述我們的水體。我們仍需要水體本身,將使用Meshes來創造。我們將需要對象來托管這些網格。

GameObject[] meshobjects;

Mesh[] meshes;

我們還需要碰撞器以便事物可同水體互動:

GameObject[] colliders;

我們也存儲了所有的常量:

const float springconstant = 0.02f;

const float damping = 0.04f;

const float spread = 0.05f;

const float z = -1f;

這些常量中的z是我們為水體設置的Z位移。我們將使用-1標註它,這樣它就會呈現於我們的對象之前(遊戲邦註:妳可能想根據自己的需求將其調整為在對象之前或之後,那妳就必須使用Z坐標來確定與之相關的精靈所在的位置)。

下壹步,我們將保持壹些值:

float baseheight;

float left;

float bottom;

這些就是水的維度。

我們將需要壹些可以在編輯器中設置的公開變量。首先,我們將為水花使用粒子系統:

public GameObject splash:

接下來就是我們將用於線性渲染器的材料:

public Material mat:

此外,我們將為主要水體使用的網格類型如下:

public GameObject watermesh:

我們想要能夠托管所有這些數據的遊戲對象,令其作為管理器,產出我們遊戲中的水體。為此,我們將編寫SpawnWater()函數。

這個函數將采用水體左邊、跑馬度、頂點以及底部的輸入:

public void SpawnWater(float Left, float Width, float Top, float Bottom)

{

(雖然這看似有所矛盾,但卻有利於從左往右快速進行關卡設計)

創造節點

現在我們將找出自己需要多少節點:

int edgecount = Mathf.RoundToInt(Width) * 5;

int nodecount = edgecount + 1;

我們將針對每個單位寬度使用5個節點,以便呈現流暢的移動(妳可以改變這壹點以便平衡效率與流暢性)。我們由此可得到所有線段,然後需要在末端的節點 + 1。

我們要做的首件事就是以LineRenderer組件渲染水體:

Body = gameObject.AddComponentlt;LineRenderer;();

Body.material = mat;

Body.material.renderQueue = 1000;

Body.SetVertexCount(nodecount);

Body.SetWidth(0.1f, 0.1f);

我們在此還要做的是選擇材料,並通過選擇渲染隊列中的位置而令其在水面之上渲染。我們設置正確的節點數據,將線段寬度設為0.1。

妳可以根據自己所需的線段粗細來改變這壹寬度。妳可能註意到了SetWidth()需要兩個參數,這是線段開始及末尾的寬度。我們希望該寬度恒定不變。

現在我們制作了節點,將初始化我們所有的頂級變量:

xpositions = new float[nodecount];

ypositions = new float[nodecount];

velocities = new float[nodecount];

accelerations = new float[nodecount];

meshobjects = new GameObject[edgecount];

meshes = new Mesh[edgecount];

colliders = new GameObject[edgecount];

baseheight = Top;

bottom = Bottom;

left = Left;

我們已經有了所有陣列,將控制我們的數據。

現在要設置我們陣列的值。我們將從節點開始:

for (int i = 0; i lt; nodecount; i++)

{

ypositions[i] = Top;

xpositions[i] = Left + Width * i / edgecount;

accelerations[i] = 0;

velocities[i] = 0;

Body.SetPosition(i, new Vector3(xpositions[i], ypositions[i], z));

}

在此,我們將所有Y位置設於水體之上,之後壹起漸進增加所有節點。因為水面平靜,我們的速度和加速值最初為0。

我們將把LineRenderer (Body)中的每個節點設為其正確的位置,以此完成這個循環。

創造網格

這正是它棘手的地方。

我們有自己的線段,但我們並沒有水體本身。我們要使用網格來制作,如下所示:

for (int i = 0; i lt; edgecount; i++)

{

meshes[i] = new Mesh();

現在,網格存儲了壹系列變量。首個變量相當簡單:它包含了所有頂點(或轉角)。

unity-water-Firstmesh(from gamedevelopment)

該圖表顯示了我們所需的網格片段的樣子。第壹個片段中的頂點被標註出來了。我們總***需要4個頂點。

Vector3[] Vertices = new Vector3[4];

Vertices[0] = new Vector3(xpositions[i], ypositions[i], z);

Vertices[1] = new Vector3(xpositions[i + 1], ypositions[i + 1], z);

Vertices[2] = new Vector3(xpositions[i], bottom, z);

Vertices[3] = new Vector3(xpositions[i+1], bottom, z);

現在如妳所見,頂點0處於左上角,1處於右上角,2是左下角,3是右下角。我們之後要記住。

網格所需的第二個性能就是UV。網格擁有紋理,UV會選擇我們想擷取的那部分紋理。在這種情況下,我們只想要左上角,右上角,右下角和右下角的紋理。

Vector2[] UVs = new Vector2[4];

UVs[0] = new Vector2(0, 1);

UVs[1] = new Vector2(1, 1);

UVs[2] = new Vector2(0, 0);

UVs[3] = new Vector2(1, 0);

現在我們又需要這些數據了。網格是由三角形組成的,我們知道任何四邊形都是由兩個三角形組成的,所以現在我們需要告訴網格它如何繪制這些三角形。

unity-water-Tris(from gamedevelopment)

看看含有節點順序標註的轉角。三角形A連接節點0,1,以及3,三角形B連接節點3,2,1。因此我們想制作壹個包含6個整數的陣列:

int[] tris = new int[6] { 0, 1, 3, 3, 2, 0 };

這就創造了我們的四邊形。現在我們要設置網格的值。

meshes[i].vertices = Vertices;

meshes[i].uv = UVs;

meshes[i].triangles = tris;

現在我們已經有了自己的網格,但我們沒有在場景是渲染它們的遊戲對象。所以我們將從包括壹個網格渲染器和篩網過濾器的watermesh預制件來創造它們。

meshobjects[i] = Instantiate(watermesh,Vector3.zero,Quaternion.identity) as GameObject;

meshobjects[i].GetComponentlt;MeshFilter;().mesh = meshes[i];

meshobjects[i].transform.parent = transform;

我們設置了網格,令其成為水體管理器的子項。

創造碰撞效果

現在我們還需要自己的碰撞器:

colliders[i] = new GameObject();

colliders[i].name = “Trigger”;

colliders[i].AddComponentlt;BoxCollider2D;();

colliders[i].transform.parent = transform;

colliders[i].transform.position = new Vector3(Left + Width * (i + 0.5f) / edgecount, Top – 0.5f, 0);

colliders[i].transform.localScale = new Vector3(Width / edgecount, 1, 1);

colliders[i].GetComponentlt;BoxCollider2D;().isTrigger = true;

colliders[i].AddComponentlt;WaterDetector;();

至此,我們制作了方形碰撞器,給它們壹個名稱,以便它們會在場景中顯得更整潔壹點,並且再次制作水體管理器的每個子項。我們將它們的位置設置於兩個節點之點,設置好大小,並為其添加了WaterDetector類。

現在我們擁有自己的網格,我們需要壹個函數隨著水體移動進行更新:

void UpdateMeshes()

{

for (int i = 0; i lt; meshes.Lenh; i++)

{

Vector3[] Vertices = new Vector3[4];

Vertices[0] = new Vector3(xpositions[i], ypositions[i], z);

Vertices[1] = new Vector3(xpositions[i+1], ypositions[i+1], z);

Vertices[2] = new Vector3(xpositions[i], bottom, z);

Vertices[3] = new Vector3(xpositions[i+1], bottom, z);

meshes[i].vertices = Vertices;

}

}

妳可能註意到了這個函數只使用了我們之前編寫的代碼。唯壹的區別在於這次我們並不需要設置三角形的UV,因為這些仍然保持不變。

我們的下壹步任務是讓水體本身運行。我們將使用FixedUpdate()遞增地來調整它們。

void FixedUpdate()

{

執行物理機制

首先,我們將把Hooke定律寫Euler方法結合在壹起找到新坐標、加速和速度。

Hooke定律是F=kx,這裏的F是指由水流產生的力(記住,我們將把水體表面模擬為水流),k是指水流的常量,x則是位移。我們的位移將成為每個節點的y坐標減去節點的基本高度。

下壹步,我們將添加壹個與力的速度成比例的阻尼因素來削弱力。

for (int i = 0; i lt; xpositions.Lenh ; i++)

{

float force = springconstant * (ypositions[i] – baseheight) + velocities[i]*damping ;

accelerations[i] = -force;

ypositions[i] += velocities[i];

velocities[i] += accelerations[i];

Body.SetPosition(i, new Vector3(xpositions[i], ypositions[i], z));

}

Euler方法很簡單,我們只要向速度添加加速,向每幀坐標增加速度。

註:我只是假設每個節點的質量為1,但妳可能會想用:

accelerations[i] = -force/mass;

現在我們將創造波傳播。以下節點是根據Michael Hoffman的教程調整而來的:

float[] leftDeltas = new float[xpositions.Lenh];

float[] rightDeltas = new float[xpositions.Lenh];

在此,我們要創造兩個陣列。針對每個節點,我們將檢查之前節點的高度,以及當前節點的高度,並將二者差別放入leftDeltas。

之後,我們將檢查後續節點的高度與當前檢查節點的高度,並將二者的差別放入rightDeltas(我們將乘以壹個傳播常量來增加所有值)。

for (int j = 0; j lt; 8; j++)

{

for (int i = 0; i lt; xpositions.Lenh; i++)

{

if (i ; 0)

{

leftDeltas[i] = spread * (ypositions[i] – ypositions[i-1]);

velocities[i - 1] += leftDeltas[i];

}

if (i lt; xpositions.Lenh – 1)

{

rightDeltas[i] = spread * (ypositions[i] – ypositions[i + 1]);

velocities[i + 1] += rightDeltas[i];

}

}

}

當我們集齊所有的高度數據時,我們最後就可以派上用場了。我們無法查看到最右端的節點右側,或者最大左端的節點左側,因此基條件就是i ; 0以及i lt; xpositions.Lenh – 1。

因此,要註意我們在壹個循環中包含整片代碼,並運行它8次。這是因為我們想以少量而多次的時間運行這壹過程,而不是進行壹次大型運算,因為這會削弱流動性。

添加水花

現在我們已經有了流動的水體,下壹步就需要讓它濺起水花!

為此,我們要增加壹個稱為Splash()的函數,它會檢查水花的X坐標,以及它所擊中的任何物體的速度。將其設置為公開狀態,這樣我們可以在之後的碰撞器中調用它。

public void Splash(float xpos, float velocity)

{

首先,我們應該確保特定的坐標位於我們水體的範圍之內:

if (xpos ;= xpositions[0] ?xpos lt;= xpositions[xpositions.Lenh-1])

{

然後我們將調整xpos,讓它出現在相對於水體起點的位置上:

xpos -= xpositions[0];

下壹步,我們將找到它所接觸的節點。我們可以這樣計算:

int index = Mathf.RoundToInt((xpositions.Lenh-1)*(xpos / (xpositions[xpositions.Lenh-1] – xpositions[0])));

這就是它的運行方式:

我們選取相對於水體左側邊緣位置的水花位置(xpos)。

2.我們將相對於水體左側邊緣的的右側位置進行劃分。

3.這讓我們知道了水花所在的位置。例如,位於水體四分之三處的水花的值就是0.75。

4.我們將把這壹數字乘以邊緣的數量,這就可以得到我們水花最接近的節點。

velocities[index] = velocity;

現在我們要設置擊中水面的物體的速度,令其與節點速度壹致,以樣節點就會被該物體拖入深處。

Particle-System(from gamedevelopment)

註:妳可以根據自己的需求改變這條線段。例如,妳可以將其速度添加到當前速度,或者使用動量而非速度,並除以妳節點的質量。

現在,我們想制作壹個將產生水花的粒子系統。我們早點定義,將其稱為“splash”。要確保不要讓它與Splash()相混淆。

首先,我們要設置水花的參,以便調整物體的速度:

float lifetime = 0.93f + Mathf.Abs(velocity)*0.07f;

splash.GetComponentlt;ParticleSystem;().startSpeed = 8+2*Mathf.Pow(Mathf.Abs(velocity),0.5f);

splash.GetComponentlt;ParticleSystem;().startSpeed = 9 + 2 * Mathf.Pow(Mathf.Abs(velocity), 0.5f);

splash.GetComponentlt;ParticleSystem;().startLifetime = lifetime;

在此,我們要選取粒子,設置它們的生命周期,以免他們擊中水面就快速消失,並且根據它們速度的直角設置速度(為小小的水花增加壹個常量)。

妳可能會看著代碼心想,“為什麽要兩次設置startSpeed?”妳這樣想沒有錯,問題在於,我們使用壹個起始速度設置為“兩個常量間的隨機數”這種粒子系統(Shuriken)。不幸的是,我們並沒有太多以腳本訪問Shuriken的途徑 ,所以為了獲得這壹行為,我們必須兩次設置這個值。