Unity強(qiáng)化篇(八) —— 使用Unity運(yùn)行時(shí)網(wǎng)格操作(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2019.11.24 星期日

前言

Unity是由Unity Technologies開(kāi)發(fā)的一個(gè)讓玩家輕松創(chuàng)建諸如三維視頻游戲萨蚕、建筑可視化乍钻、實(shí)時(shí)三維動(dòng)畫(huà)等類(lèi)型互動(dòng)內(nèi)容的多平臺(tái)的綜合型游戲開(kāi)發(fā)工具完丽,是一個(gè)全面整合的專(zhuān)業(yè)游戲引擎姥敛。Unity類(lèi)似于Director,Blender game engine, Virtools 或 Torque Game Builder等利用交互的圖型化開(kāi)發(fā)環(huán)境為首要方式的軟件砂沛。其編輯器運(yùn)行在Windows 和Mac OS X下,可發(fā)布游戲至Windows哀澈、Mac牌借、WiiiPhone割按、WebGL(需要HTML5)膨报、Windows phone 8和Android平臺(tái)。也可以利用Unity web player插件發(fā)布網(wǎng)頁(yè)游戲适荣,支持Mac和Windows的網(wǎng)頁(yè)瀏覽现柠。它的網(wǎng)頁(yè)播放器也被Mac 所支持。網(wǎng)頁(yè)游戲 坦克英雄和手機(jī)游戲王者榮耀都是基于它的開(kāi)發(fā)弛矛。感興趣的看下面幾篇文章够吩。
1. Unity強(qiáng)化篇(一) —— 如何使用Vuforia制作AR游戲(一)
2. Unity強(qiáng)化篇(二) —— 適用于Unity的HTC Vive教程(一)
3. Unity強(qiáng)化篇(三) —— 適用于Unity的HTC Vive教程(二)
4. Unity強(qiáng)化篇(四) —— Unity 和 Ethereum(一)
5. Unity強(qiáng)化篇(五) —— Unity 和 Ethereum(二)
6. Unity強(qiáng)化篇(六) —— 使用Unity和Photon進(jìn)行多人游戲簡(jiǎn)介(一)
7. Unity強(qiáng)化篇(七) —— Unity Sprite Shapes簡(jiǎn)介(一)

開(kāi)始

主要內(nèi)容:將Unity用作游戲開(kāi)發(fā)平臺(tái)的好處之一是其強(qiáng)大的3D引擎。 在本教程中丈氓,您將介紹3D對(duì)象和網(wǎng)格處理的世界周循。

下面看下寫(xiě)作環(huán)境

C# 7.3, Unity 2019.1, Unity

歡迎來(lái)到3D對(duì)象和網(wǎng)格處理的世界! 將Unity用作游戲開(kāi)發(fā)平臺(tái)的好處之一是其強(qiáng)大的3D引擎万俗。 3D引擎以及Unity使用自定義編輯器的能力湾笛,使3D游戲和應(yīng)用程序的開(kāi)發(fā)變得非常容易。

隨著虛擬現(xiàn)實(shí)和增強(qiáng)現(xiàn)實(shí)(VR / AR)技術(shù)的發(fā)展闰歪,大多數(shù)開(kāi)發(fā)人員會(huì)無(wú)意間發(fā)現(xiàn)自己在3D概念的堅(jiān)韌不拔中掙扎嚎研。 因此,以本教程為起點(diǎn)库倘。 不用擔(dān)心嘉赎,這里不會(huì)有復(fù)雜的3D數(shù)學(xué)運(yùn)算-只有很多的心置媳,圖紙,箭頭和無(wú)窮的樂(lè)趣公条!

1. Understanding Meshes

現(xiàn)在開(kāi)始介紹3D渲染的基本詞匯拇囊。 3D對(duì)象的形狀由其網(wǎng)格定義。 網(wǎng)格就像點(diǎn)或頂點(diǎn)的網(wǎng)靶橱。 連接這些頂點(diǎn)的不可見(jiàn)線形成三角形寥袭,三角形定義了對(duì)象的基本形狀。

但是除了形狀之外关霸,引擎還需要知道如何繪制對(duì)象的表面传黄。因此,網(wǎng)格的數(shù)據(jù)還包括其法線队寇,法線是確定特定三角形朝向的方式以及光線如何從其反射的向量膘掰。最后,UV Map將材質(zhì)映射到對(duì)象佳遣,指定紋理如何環(huán)繞形狀识埋。

在Unity中,有兩個(gè)主要的渲染組件:“網(wǎng)格過(guò)濾器”(Mesh Filter)(用于存儲(chǔ)模型的網(wǎng)格數(shù)據(jù))和“網(wǎng)格渲染器”(Mesh Renderer)零渐,其將網(wǎng)格數(shù)據(jù)與材質(zhì)組合以在場(chǎng)景中渲染對(duì)象窒舟。

知道了嗎?以下是備忘單诵盼,以方便參考:

  • Vertices - 頂點(diǎn):頂點(diǎn)是3D空間中的一個(gè)點(diǎn)惠豺。通常縮寫(xiě)為“vert”风宁。
  • Lines/Edges - 線/邊:將頂點(diǎn)相互連接的不可見(jiàn)線洁墙。
  • Triangles - 三角形:當(dāng)邊連接三個(gè)頂點(diǎn)時(shí)形成。
  • UV Map - UV貼圖:將材質(zhì)映射到對(duì)象戒财,指定紋理如何環(huán)繞對(duì)象的形狀扫俺。
  • Normals - 法線:頂點(diǎn)或曲面的方向向量。這典型地指向外部固翰,垂直于網(wǎng)格表面狼纬,并有助于確定光從對(duì)象反彈的方式。
  • Mesh - 網(wǎng)格:包含模型的所有頂點(diǎn)骂际,邊疗琉,三角形,法線和UV數(shù)據(jù)歉铝。

以下是創(chuàng)建3D網(wǎng)格的基本步驟(采用偽代碼):

  • 創(chuàng)建一個(gè)名為“myMesh”的新網(wǎng)格盈简。
  • 將數(shù)據(jù)添加到myMesh的頂點(diǎn)和三角形屬性。
  • 創(chuàng)建一個(gè)名為“myMeshFilter”的新網(wǎng)格過(guò)濾器。
  • myMesh分配給myMeshFiltermesh屬性柠贤。

2. Setting Up the Project

現(xiàn)在您已經(jīng)掌握了基礎(chǔ)知識(shí)香浩,在Unity中打開(kāi)starter項(xiàng)目。 在Project視圖中看下文件夾結(jié)構(gòu):

  • Prefabs - 預(yù)制件:其中包含CustomHeart預(yù)制件臼勉,可用于在運(yùn)行時(shí)保存3D網(wǎng)格邻吭。
  • Scenes - 場(chǎng)景:這包含您將在本教程的不同部分使用的三個(gè)場(chǎng)景。
  • Editor - 編輯器:在開(kāi)發(fā)過(guò)程中宴霸,該文件夾中的腳本為您提供了編輯器中的特殊功能囱晴。
  • Scripts - 腳本:包含運(yùn)行時(shí)腳本或組件。 將這些組件附加到GameObject時(shí)瓢谢,單擊Play即可執(zhí)行畸写。
  • Materials - 材質(zhì):此文件夾包含您要使用的網(wǎng)格物體的材質(zhì)。

在下一部分中氓扛,您將創(chuàng)建一個(gè)自定義編輯器以可視化3D網(wǎng)格的各個(gè)部分枯芬。


Poking and Prodding Meshes With a Custom Editor

RW/Scenes中打開(kāi)01 Mesh Study Demo。 在Scene視圖中采郎,您將看到一個(gè)不起眼的立方體:

您將要建立一個(gè)自定義編輯器千所,以將這個(gè)可憐的立方體拆開(kāi)! (然后尉剩,您將學(xué)習(xí)如何將其保持為一體真慢。)

1. Customizing the Editor Script

Project視圖中選擇Editor文件夾毅臊。 這個(gè)特殊文件夾中的腳本修改了Unity編輯器的工作方式理茎。 它們不會(huì)成為內(nèi)置游戲的一部分。

打開(kāi)MeshInspector.cs并查看源代碼管嬉。 請(qǐng)注意皂林,該類(lèi)繼承自Unity的基本Editor類(lèi)-這就是讓Unity了解這是自定義編輯器而不是游戲腳本的原因。

第一步是告訴Unity這個(gè)特殊的編輯器應(yīng)該繪制什么樣的對(duì)象蚯撩。 在MeshInspector類(lèi)聲明上方的行上添加以下屬性:

[CustomEditor(typeof(MeshStudy))]

現(xiàn)在础倍,當(dāng)任何附加了Mesh Study組件的GameObjectScene視圖中可見(jiàn)時(shí),此類(lèi)將處理其繪制胎挎。 但是現(xiàn)在沟启,您不知道這種情況是否正在發(fā)生。

OnSceneGUIUnity每次在編輯器中渲染Scene視圖時(shí)都會(huì)調(diào)用的事件方法犹菇。 您有機(jī)會(huì)修改Unity在場(chǎng)景中繪制對(duì)象的方式德迹。 在OnSceneGUI的開(kāi)頭添加以下內(nèi)容:

mesh = target as MeshStudy;
Debug.Log("Custom editor is running");

基本的Editor類(lèi)提供了對(duì)您在target變量中具有類(lèi)型Object的自定義對(duì)象的引用。 對(duì)于普通的vanilla對(duì)象揭芍,您無(wú)法做很多有用的事情胳搞,因此此代碼將target強(qiáng)制轉(zhuǎn)換為MeshStudy類(lèi)型。 記錄消息后,您可以在控制臺(tái)中看到自定義編輯器確實(shí)正在運(yùn)行肌毅。

保存文件并返回到Unity筷转。 轉(zhuǎn)到RW / Scripts文件夾,然后將MeshStudy.cs拖到層次結(jié)構(gòu)中的Cube GameObject上悬而,以將組件附加到該對(duì)象呜舒。

在控制臺(tái)中查看并確保您的代碼正在運(yùn)行。 然后繼續(xù)刪除Debug.Log行摊滔,以免淹沒(méi)您的控制臺(tái)阴绢。

2. Cloning a Mesh

Edit模式下使用自定義編輯器處理3D網(wǎng)格時(shí),很容易意外覆蓋Unity的默認(rèn)網(wǎng)格艰躺,即內(nèi)置的Sphere呻袭,Cube,Cylinder等腺兴。 如果發(fā)生這種情況左电,則需要重新啟動(dòng)Unity

為避免這種情況页响,請(qǐng)?jiān)?code>Edit模式下克隆網(wǎng)格篓足,然后再對(duì)其進(jìn)行任何更改。

打開(kāi)MeshStudy.cs闰蚕。 該腳本繼承自MonoBehaviour栈拖,因此其Start不會(huì)在Edit模式下運(yùn)行。 幸運(yùn)的是没陡,這很容易解決涩哟!

MeshStudy的類(lèi)聲明上方,添加以下內(nèi)容:

[ExecuteInEditMode]

當(dāng)一個(gè)類(lèi)具有此屬性時(shí)盼玄,其Start將在Play modeEdit mode下觸發(fā)贴彼。 添加完之后,您可以在更改任何內(nèi)容之前實(shí)例化和克隆網(wǎng)格對(duì)象埃儿。

將以下代碼添加到InitMesh

meshFilter = GetComponent<MeshFilter>();
originalMesh = meshFilter.sharedMesh; //1
clonedMesh = new Mesh(); //2

clonedMesh.name = "clone";
clonedMesh.vertices = originalMesh.vertices;
clonedMesh.triangles = originalMesh.triangles;
clonedMesh.normals = originalMesh.normals;
clonedMesh.uv = originalMesh.uv;
meshFilter.mesh = clonedMesh;  //3

vertices = clonedMesh.vertices; //4
triangles = clonedMesh.triangles;
isCloned = true; //5
Debug.Log("Init & Cloned");

這是正在做的事情:

  • 1) 抓取您最初在MeshFilter中分配的任何網(wǎng)格器仗。
  • 2) 創(chuàng)建一個(gè)名為clonedMesh的新網(wǎng)格實(shí)例,并通過(guò)復(fù)制第一個(gè)網(wǎng)格設(shè)置其屬性童番。
  • 3) 將復(fù)制的網(wǎng)格分配回網(wǎng)格過(guò)濾器精钮。
  • 4) 更新局部變量,稍后將需要剃斧。
  • 5) 將isCloned設(shè)置為true轨香; 您稍后會(huì)參考。

保存文件并返回到Unity悯衬。 控制臺(tái)應(yīng)顯示消息“ Init&Cloned”弹沽。

在層次結(jié)構(gòu)中選擇Cube檀夹,然后查看檢查器。 網(wǎng)格過(guò)濾器將顯示一個(gè)名為clone的網(wǎng)格策橘。 很好炸渡! 這意味著您已經(jīng)成功克隆了網(wǎng)格。

但是請(qǐng)注意丽已,您的項(xiàng)目中沒(méi)有新的Mesh資源-克隆的Mesh現(xiàn)在僅存在于Unity的內(nèi)存中蚌堵,如果關(guān)閉場(chǎng)景,它將消失沛婴。 稍后吼畏,您將學(xué)習(xí)如何保存網(wǎng)格。

3. Resetting a Mesh

目前嘁灯,您想給自己一個(gè)簡(jiǎn)單的方法來(lái)重置網(wǎng)格泻蚊,以便您可以放心地玩。 返回到MeshInspector.cs丑婿。

OnInspectorGUI使您可以使用額外的GUI元素和邏輯為對(duì)象自定義Inspector性雄。 在OnInspectorGUI中,找到注釋//draw reset button并將其替換為以下內(nèi)容:

if (GUILayout.Button("Reset")) //1
{
    mesh.Reset(); //2
}
  • 1) 此代碼在檢查器中繪制一個(gè)“重置”(Reset)按鈕羹奉。 按下時(shí)秒旋,繪制函數(shù)會(huì)返回true
  • 2) 按下后诀拭,該按鈕將調(diào)用MeshStudy.cs中的Reset迁筛。

保存文件并返回到MeshStudy.cs。 添加以下內(nèi)容以到Reset

if (clonedMesh != null && originalMesh != null) //1
{
    clonedMesh.vertices = originalMesh.vertices; //2
    clonedMesh.triangles = originalMesh.triangles;
    clonedMesh.normals = originalMesh.normals;
    clonedMesh.uv = originalMesh.uv;
    meshFilter.mesh = clonedMesh; //3

    vertices = clonedMesh.vertices; //4
    triangles = clonedMesh.triangles;
}

以下是此代碼的逐步操作:

  • 1) 如果對(duì)象的網(wǎng)格過(guò)濾器中沒(méi)有任何數(shù)據(jù)耕挨,請(qǐng)檢查原始網(wǎng)格和克隆網(wǎng)格是否都存在细卧。
  • 2) 將clonedMesh的所有屬性重置為原始網(wǎng)格的屬性。
  • 3) 將clonedMesh分配回Mesh Filter組件俗孝。
  • 4) 更新局部變量酒甸。

保存文件并返回到Unity魄健。

在檢查器中赋铝,單擊Test Edit按鈕使立方體的網(wǎng)格混亂,然后按Reset按鈕將其還原沽瘦。

4. Understanding Vertices and Triangles With Unity

如您先前所見(jiàn)忽妒,網(wǎng)格由邊連接的頂點(diǎn)組成三角形季春。 三角形定義了對(duì)象的基本形狀。

注意:Unity的Mesh類(lèi)使用兩個(gè)數(shù)組跟蹤頂點(diǎn)和三角形:

  • 它將頂點(diǎn)存儲(chǔ)為Vector3的數(shù)組。
  • 它將三角形存儲(chǔ)為整數(shù)數(shù)組拢操。 每個(gè)整數(shù)是verts數(shù)組中一個(gè)頂點(diǎn)的索引,并且每組三個(gè)連續(xù)的整數(shù)代表一個(gè)三角形恳蹲。
    例如嫌变,組triangles[0], triangles[1], triangles[2]代表一個(gè)三角形,組triangles[3], triangles[4], triangles[5]代表下一個(gè)三角形,依此類(lèi)推巍实。

因此滓技,在由四個(gè)頂點(diǎn)和兩個(gè)三角形組成的簡(jiǎn)單四邊形網(wǎng)格中,四邊形的網(wǎng)格數(shù)據(jù)為:

5. Visualizing Vertices

如果您可以在帶有handles的網(wǎng)格上的頂點(diǎn)上繪制和移動(dòng)棚潦,將更容易看到它是如何工作的令漂。 handles是用于在Scene視圖中處理對(duì)象的工具,例如“旋轉(zhuǎn)”工具的可拖動(dòng)球體丸边。 現(xiàn)在叠必,您將要編寫(xiě)自己的handle

MeshInspector.cs中妹窖,查找EditMesh并添加以下內(nèi)容:

handleTransform = mesh.transform; //1
handleRotation = Tools.pivotRotation == PivotRotation.Local ? 
    handleTransform.rotation : Quaternion.identity; //2
for (int i = 0; i < mesh.vertices.Length; i++) //3
{
    ShowPoint(i);
}
  • 1) 獲取網(wǎng)格的transform纬朝,您將需要知道在世界空間中繪制頂點(diǎn)的位置。
  • 2) 獲取當(dāng)前的軸旋轉(zhuǎn)模式骄呼,以與場(chǎng)景中其他所有對(duì)象相同的方式繪制手柄玄组。
  • 3) 遍歷網(wǎng)格的頂點(diǎn)并使用ShowPoint繪制點(diǎn)。

ShowPoint中谒麦,將// draw dot注釋替換為:

Vector3 point = handleTransform.TransformPoint(mesh.vertices[index]); //1
Handles.color = Color.blue;
point = Handles.FreeMoveHandle(point, handleRotation, mesh.handleSize,
    Vector3.zero, Handles.DotHandleCap); //2
  • 1) 這條線將頂點(diǎn)的局部位置轉(zhuǎn)換為世界空間俄讹。
  • 2) 使用Handles實(shí)用程序類(lèi)繪制點(diǎn)。

Handles.FreeMoveHandle制作一個(gè)不受限制的運(yùn)動(dòng)handle绕德,您將在下一部分中使用它來(lái)拖動(dòng)點(diǎn)患膛。

保存文件并返回到Unity

檢查Cube's MeshInspector耻蛇,并確保已選中Move Vertex Point踪蹬。

現(xiàn)在,您應(yīng)該在屏幕上看到標(biāo)有藍(lán)點(diǎn)的網(wǎng)格的頂點(diǎn)臣咖。 嘗試將腳本附加到其他3D對(duì)象跃捣,然后親自查看結(jié)果!

6. Moving a Single Vertex

您將從最基本的網(wǎng)格處理類(lèi)型開(kāi)始:移動(dòng)單個(gè)頂點(diǎn)夺蛇。

打開(kāi)MeshInspector.cs疚漆。 在ShowPoint內(nèi),用以下內(nèi)容替換// drag注釋?zhuān)?/p>

if (GUI.changed) //3
{
    mesh.DoAction(index, handleTransform.InverseTransformPoint(point)); //4
}
  • 3) GUI.changed監(jiān)視對(duì)點(diǎn)所做的任何更改刁赦,這與Handles.FreeMoveHandle配合使用可很好地檢測(cè)拖動(dòng)動(dòng)作娶聘。
  • 4) 拖動(dòng)頂點(diǎn)時(shí),以頂點(diǎn)索引和頂點(diǎn)位置為參數(shù)調(diào)用mesh.DoAction甚脉。 這條線還使用InverseTransformPoint將頂點(diǎn)的位置轉(zhuǎn)換回局部空間丸升。

保存MeshInspector.cs并轉(zhuǎn)到MeshStudy.cs。 在DoAction中添加以下內(nèi)容:

PullOneVertex(index, localPos);

然后將以下內(nèi)容添加到PullOneVertex

vertices[index] = newPos; //1
clonedMesh.vertices = vertices; //2
clonedMesh.RecalculateNormals(); //3
  • 1) 更新目標(biāo)頂點(diǎn)的位置牺氨。
  • 2) 將更新后的頂點(diǎn)數(shù)組分配回克隆的網(wǎng)格狡耻。
  • 3) 告訴Unity重新繪制網(wǎng)格以反映更改墩剖。

保存腳本并返回到Unity。 嘗試拖動(dòng)cube上的點(diǎn)之一夷狰。

似乎有些頂點(diǎn)共享相同的位置涛碑,因此,當(dāng)您僅拉一個(gè)頂點(diǎn)時(shí)孵淘,其他頂點(diǎn)會(huì)留在后面蒲障,并且網(wǎng)格會(huì)斷開(kāi)。 您將很快學(xué)習(xí)如何解決此問(wèn)題瘫证。

7. Looking at the Vertices Array

在外觀上揉阎,一個(gè)立方體網(wǎng)格由八個(gè)頂點(diǎn),六個(gè)邊和12個(gè)三角形組成背捌。 是時(shí)候看看Unity是否一致了毙籽。

轉(zhuǎn)到MeshStudy.cs,然后在Start之前查找名為vertices的變量毡庆。 您將看到它具有[HideInInspector]屬性坑赡。

臨時(shí)注釋掉該屬性,以便快速瀏覽一下數(shù)組:

//[HideInInspector]
public Vector3[] vertices;

注意:更復(fù)雜的3D網(wǎng)格可以具有數(shù)千個(gè)頂點(diǎn)么抗。 如果Unity試圖在Inspector中顯示所有這些值毅否,它將凍結(jié),因此通常蝇刀,您將使用[HideInInspector]隱藏該數(shù)組螟加。 你只是在偷看!

保存文件吞琐,返回Unity并查看您的cube捆探。 現(xiàn)在,您可以在Mesh Study中看到vertices屬性站粟。 單擊其旁邊的箭頭圖標(biāo)以顯示Vector3元素的數(shù)組黍图。

您會(huì)看到數(shù)組大小為24,這意味著肯定有頂點(diǎn)共享相同的位置奴烙! 花點(diǎn)時(shí)間考慮一下為什么同一位置可能有多個(gè)頂點(diǎn)助被。

最簡(jiǎn)單的答案是:
一個(gè)立方體有六個(gè)邊,每個(gè)邊都有四個(gè)形成一個(gè)平面的頂點(diǎn)缸沃。 6×4 = 24個(gè)頂點(diǎn)恰起。

如果很難掌握修械,還有其他方法可以考慮趾牧。 但是現(xiàn)在,只知道某些網(wǎng)格物體的頂點(diǎn)共享相同的位置肯污。

由于已經(jīng)完成了verts數(shù)組的瀏覽翘单,因此請(qǐng)繼續(xù)注釋[HideInInspector]吨枉。

8. Finding All Similar Vertices

您可以看到,操作網(wǎng)格不僅需要移動(dòng)單個(gè)頂點(diǎn)哄芜,還需要更多的操作—您必須移動(dòng)空間中特定點(diǎn)的所有頂點(diǎn)才能將網(wǎng)格保持在一起貌亭。 因此,您現(xiàn)在就可以動(dòng)彈了认臊,呃圃庭,網(wǎng)狀的。

MeshStudy.cs中失晴,將DoAction內(nèi)的所有代碼替換為:

PullSimilarVertices(index, localPos);

轉(zhuǎn)到PullSimilarVertices并添加以下內(nèi)容:

Vector3 targetVertexPos = vertices[index]; //1
List<int> relatedVertices = FindRelatedVertices(targetVertexPos, false); //2
foreach (int i in relatedVertices) //3
{
    vertices[i] = newPos;
}
clonedMesh.vertices = vertices; //4
clonedMesh.RecalculateNormals();
  • 1) 從頂點(diǎn)數(shù)組獲取目標(biāo)vertices位置剧腻。
  • 2) 查找與目標(biāo)頂點(diǎn)共享相同位置的所有頂點(diǎn),并將其索引放入列表中涂屁。
  • 3) 遍歷該列表并更新所有相關(guān)頂點(diǎn)的位置书在。
  • 4) 將更新后的vertices分配回clonedMesh.vertices,然后重新繪制網(wǎng)格拆又。

保存文件并返回到Unity儒旬。 單擊并拖動(dòng)任何一個(gè)頂點(diǎn); 網(wǎng)格現(xiàn)在應(yīng)保持其形狀不變帖族。

保存場(chǎng)景栈源。 您已邁出成為網(wǎng)狀魔術(shù)師的第一步!


Manipulating Meshes

在Unity中編輯網(wǎng)格很有趣竖般,但是如果您可以通過(guò)在運(yùn)行時(shí)變形網(wǎng)格來(lái)向游戲中添加一些“squish”呢凉翻? 接下來(lái),您將以最基本的形式進(jìn)行嘗試-推和拉一些預(yù)定義的頂點(diǎn)捻激。

1. Collecting the Selected Indices

首先制轰,創(chuàng)建一個(gè)自定義編輯器,使您可以選擇要實(shí)時(shí)移動(dòng)的頂點(diǎn)胞谭。 在RW/Scenes中打開(kāi)02 Create Heart Mesh場(chǎng)景垃杖。 您將在Scene視圖中看到一個(gè)紅色的球體。

在“層次結(jié)構(gòu)”中選擇Sphere丈屹,然后查看Heart Mesh組件调俘。 這是將存儲(chǔ)您選擇的頂點(diǎn)的腳本。

但是現(xiàn)在旺垒,場(chǎng)景中沒(méi)有顯示任何變體彩库。 因此,接下來(lái)先蒋,您將解決此問(wèn)題骇钦!

打開(kāi)RW / Editor / HeartMeshInspector.cs。 在ShowHandle中的if語(yǔ)句內(nèi)竞漾,添加以下代碼:

Handles.color = Color.blue;
if (Handles.Button(point, handleRotation, mesh.pickSize, mesh.pickSize, 
    Handles.DotHandleCap)) //1
{
    mesh.selectedIndices.Add(index); //2
}
  • 1) 這使Unity可以將網(wǎng)格的頂點(diǎn)繪制為按鈕眯搭,因此可以單擊它們窥翩。
  • 2) 單擊按鈕時(shí),它將選定的索引添加到mesh.selectedIndices列表鳞仙。

在現(xiàn)有的if語(yǔ)句之后寇蚊,在OnInspectorGUI的末尾添加以下代碼:

if (GUILayout.Button("Clear Selected Vertices"))
{
    mesh.ClearAllData();
}

這會(huì)在檢查器中添加一個(gè)自定義的Reset按鈕。 接下來(lái)棍好,您將編寫(xiě)代碼以清除選擇仗岸。

保存文件并打開(kāi)RW / Scripts / HeartMesh.cs。 在ClearAllData中借笙,添加以下內(nèi)容:

selectedIndices = new List<int>();
targetIndex = 0;
targetVertex = Vector3.zero;

這將清除selectedIndices列表中的值爹梁,并將targetIndex設(shè)置為零。 它還會(huì)重置targetVertex位置提澎。

保存文件并返回到Unity姚垃。 選擇Sphere并查看其HeartMesh組件。 確保已選中Is Edit Mode盼忌,以便可以在Scene視圖中查看網(wǎng)格的頂點(diǎn)积糯。 然后單擊Selected Indices旁邊的箭頭圖標(biāo)以顯示該數(shù)組。

單擊一些藍(lán)點(diǎn)谦纱,然后觀看新條目出現(xiàn)在Selected Indices中看成。 試用您的Clear Selected Vertices按鈕,以確保其正確清除了所有值跨嘉。

注意:您可以選擇使用自定義Inspector中的Show Transform Handle來(lái)顯示/隱藏變換手柄川慌,因?yàn)樗梢苑恋K選擇頂點(diǎn)。 只要記住當(dāng)您發(fā)現(xiàn)其他場(chǎng)景中缺少Transform handle時(shí)不要驚慌祠乃! 退出之前梦重,請(qǐng)務(wù)必將其重新打開(kāi)。

2. Deforming the Sphere Into a Heart Shape

實(shí)時(shí)更新網(wǎng)格頂點(diǎn)需要三個(gè)步驟:

  • 1) 將當(dāng)前的網(wǎng)格頂點(diǎn)(在動(dòng)畫(huà)之前)復(fù)制到ModifyedVertices亮瓷。
  • 2) 計(jì)算并更新modifiedVertices上的值琴拧。
  • 3) 每次更改步驟時(shí),將ModifyedVertices復(fù)制到當(dāng)前網(wǎng)格嘱支,并讓Unity重新繪制網(wǎng)格蚓胸。

轉(zhuǎn)到HeartMesh.cs并在Start之前添加以下變量:

public float radiusOfEffect = 0.3f; //1 
public float pullValue = 0.3f; //2
public float duration = 1.2f; //3
int currentIndex = 0; //4
bool isAnimate = false; 
float startTime = 0f;
float runTime = 0f; 

移動(dòng)頂點(diǎn)應(yīng)該對(duì)其周?chē)捻旤c(diǎn)產(chǎn)生一些影響,以保持平滑的形狀除师。 這些變量控制效果沛膳。

  • 1) 受目標(biāo)頂點(diǎn)影響的區(qū)域半徑。
  • 2) pull的長(zhǎng)度汛聚。
  • 3) 動(dòng)畫(huà)將運(yùn)行多長(zhǎng)時(shí)間锹安。
  • 4) selectedIndices列表的當(dāng)前索引。

Initif語(yǔ)句之前,添加:

currentIndex = 0;

這將在游戲開(kāi)始時(shí)將currentIndexselectedIndices列表的第一個(gè)索引)設(shè)置為0八毯。

仍然在Init中搓侄,在else語(yǔ)句的右括號(hào)之前瞄桨,添加:

StartDisplacement();

StartDisplacement是實(shí)際移動(dòng)頂點(diǎn)的地方话速。 它僅在isEditModefalse時(shí)運(yùn)行。

現(xiàn)在芯侥,此方法不起作用泊交,因此將以下內(nèi)容添加到StartDisplacement中:

targetVertex = originalVertices[selectedIndices[currentIndex]]; //1
startTime = Time.time; //2
isAnimate = true;
  • 1) 從originalVertices數(shù)組中選擇targetVertex以啟動(dòng)動(dòng)畫(huà)。 請(qǐng)記住柱查,每個(gè)數(shù)組項(xiàng)都是一個(gè)整數(shù)值列表廓俭。
  • 2) 將開(kāi)始時(shí)間設(shè)置為當(dāng)前時(shí)間,并將isAnimate更改為true唉工。

StartDisplacement之后研乒,使用以下代碼創(chuàng)建一個(gè)名為FixedUpdate的新方法:

protected void FixedUpdate() //1
{
    if (!isAnimate) //2
    {
        return;
    }

    runTime = Time.time - startTime; //3

    if (runTime < duration)  //4
    {
        Vector3 targetVertexPos = 
            meshFilter.transform.InverseTransformPoint(targetVertex);
        DisplaceVertices(targetVertexPos, pullValue, radiusOfEffect);
    }
    else //5
    {
        currentIndex++;
        if (currentIndex < selectedIndices.Count) //6
        {
            StartDisplacement();
        }
        else //7
        {
            originalMesh = GetComponent<MeshFilter>().mesh;
            isAnimate = false;
            isMeshReady = true;
        }
    }
}

代碼正在執(zhí)行以下操作:

  • 1) FixedUpdate方法以固定的間隔運(yùn)行,這意味著它與幀速率無(wú)關(guān)淋硝。 在此處了解更多信息雹熬。
  • 2) 如果isAnimatefalse,則不會(huì)執(zhí)行任何操作谣膳。
  • 3) 跟蹤動(dòng)畫(huà)運(yùn)行了多長(zhǎng)時(shí)間竿报。
  • 4) 如果動(dòng)畫(huà)沒(méi)有運(yùn)行太長(zhǎng)時(shí)間,它將通過(guò)獲取targetVertex的世界空間坐標(biāo)并調(diào)用DisplaceVertices來(lái)繼續(xù)動(dòng)畫(huà)继谚。
  • 5) 否則烈菌,時(shí)間到了! 向currentIndex添加一個(gè)以開(kāi)始處理下一個(gè)選定頂點(diǎn)的動(dòng)畫(huà)花履。
  • 6) 檢查是否所有選定的頂點(diǎn)都已處理芽世。 如果不是,請(qǐng)使用最新的頂點(diǎn)調(diào)用StartDisplacement诡壁。
  • 7) 否則捂襟,您已到達(dá)所選頂點(diǎn)列表的末尾。 該行將復(fù)制當(dāng)前網(wǎng)格欢峰,并將isAnimate設(shè)置為false以停止動(dòng)畫(huà)葬荷。

3. Making the Vertices Move Smoothly

DisplaceVertices中,添加以下內(nèi)容:

Vector3 currentVertexPos = Vector3.zero;
float sqrRadius = radius * radius; //1

for (int i = 0; i < modifiedVertices.Length; i++) //2
{
    currentVertexPos = modifiedVertices[i];
    float sqrMagnitude = (currentVertexPos - targetVertexPos).sqrMagnitude; //3
    if (sqrMagnitude > sqrRadius)
    {
        continue; //4
    }
    float distance = Mathf.Sqrt(sqrMagnitude); //5
    float falloff = GaussFalloff(distance, radius);
    Vector3 translate = (currentVertexPos * force) * falloff; //6
    translate.z = 0f;
    Quaternion rotation = Quaternion.Euler(translate);
    Matrix4x4 m = Matrix4x4.TRS(translate, rotation, Vector3.one);
    modifiedVertices[i] = m.MultiplyPoint3x4(currentVertexPos);
}
originalMesh.vertices = modifiedVertices; //7
originalMesh.RecalculateNormals();

此代碼循環(huán)遍歷網(wǎng)格中的每個(gè)頂點(diǎn)纽帖,并替換與您在編輯器中選擇的頂點(diǎn)接近的頂點(diǎn)宠漩。它通過(guò)一些數(shù)學(xué)技巧來(lái)創(chuàng)建平滑的效果,例如將拇指推入粘土中懊直。稍后扒吁,您將了解有關(guān)此內(nèi)容的更多信息。

下面是這段代碼的詳細(xì)信息:

  • 1) 獲取半徑的平方室囊。
  • 2) 遍歷網(wǎng)格中的每個(gè)頂點(diǎn)雕崩。
  • 3) 查找當(dāng)前頂點(diǎn)和目標(biāo)頂點(diǎn)之間的距離并將其平方魁索。
  • 4) 如果此頂點(diǎn)不在效果范圍內(nèi),請(qǐng)盡早退出循環(huán)并繼續(xù)到下一個(gè)頂點(diǎn)盼铁。
  • 5) 否則粗蔚,根據(jù)距離計(jì)算衰減falloff值。高斯函數(shù)可創(chuàng)建平滑的鐘形曲線饶火。
  • 6) 根據(jù)距離計(jì)算要移動(dòng)多遠(yuǎn)鹏控,然后根據(jù)結(jié)果設(shè)置旋轉(zhuǎn)(位移方向)。這使頂點(diǎn)“向外”移動(dòng)肤寝,即直接遠(yuǎn)離targetVertex当辐,使其看起來(lái)像從中心噴出。
  • 7) 退出循環(huán)后鲤看,將更新后的ModifyedVertices存儲(chǔ)在原始網(wǎng)格中缘揪,并讓Unity重新計(jì)算法線。

保存文件并返回到Unity义桂。選擇Sphere找筝,轉(zhuǎn)到HeartMesh組件,然后嘗試將一些頂點(diǎn)添加到Selected Indices屬性中澡刹。關(guān)閉Is Edit mode模式呻征,然后按Play以預(yù)覽您的作品。

嘗試使用Radius Of Effect, Pull ValueDuration設(shè)置以查看不同的結(jié)果罢浇。 準(zhǔn)備就緒后陆赋,請(qǐng)按照以下屏幕截圖更新設(shè)置。

點(diǎn)擊Play嚷闭,您的球體氣球變成了心臟形狀嗎攒岛?

恭喜你! 在下一部分中胞锰,您將學(xué)習(xí)如何保存網(wǎng)格以備將來(lái)使用灾锯。

4. Saving Your Mesh in Real Time

現(xiàn)在,每按一次Play按鈕嗅榕,您的心就會(huì)動(dòng)靜顺饮。 如果您想要持久的愛(ài),則需要一種將網(wǎng)格物體寫(xiě)入文件的方法凌那。

一種簡(jiǎn)單的方法是設(shè)置一個(gè)以3D對(duì)象作為其子對(duì)象的占位符預(yù)制件兼雄,然后通過(guò)腳本將其網(wǎng)格物體資源替換為您的心臟(... er,您的Heart mesh)帽蝶。

在項(xiàng)目視圖中赦肋,找到Prefabs / CustomHeart。 雙擊預(yù)制件以在Prefab Editing模式下將其打開(kāi)。

單擊箭頭圖標(biāo)以在層次結(jié)構(gòu)中展開(kāi)其內(nèi)容佃乘,然后選擇子級(jí)囱井。 您將在此處存儲(chǔ)生成的網(wǎng)格。

退出預(yù)制編輯模式趣避,然后打開(kāi)HeartMeshInspector.cs庞呕。 在OnInspectorGUI的結(jié)尾,大括號(hào)之前鹅巍,添加以下內(nèi)容:

if (!mesh.isEditMode && mesh.isMeshReady)
{
    string path = "Assets/RW/Prefabs/CustomHeart.prefab"; //1

    if (GUILayout.Button("Save Mesh"))
    {
        mesh.isMeshReady = false;
        Object prefabToInstantiate = 
            AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)); //2
        Object referencePrefab =
            AssetDatabase.LoadAssetAtPath (path, typeof(GameObject));
        GameObject gameObj =
            (GameObject)PrefabUtility.InstantiatePrefab(prefabToInstantiate);
        Mesh prefabMesh = (Mesh)AssetDatabase.LoadAssetAtPath(path,
            typeof(Mesh)); //3
        if (!prefabMesh)
        {
            prefabMesh = new Mesh();
            AssetDatabase.AddObjectToAsset(prefabMesh, path);
        }
        else
        {
            prefabMesh.Clear();
        }
        prefabMesh = mesh.SaveMesh(prefabMesh); //4

        gameObj.GetComponentInChildren<MeshFilter>().mesh = prefabMesh; //5
        PrefabUtility.SaveAsPrefabAsset(gameObj, path); //6
        Object.DestroyImmediate(gameObj); //7
    }
}

代碼是這樣的:

  • 1) 存儲(chǔ)CustomHeart預(yù)制對(duì)象資產(chǎn)路徑千扶,您需要能夠?qū)⒃撀窂綄?xiě)入文件料祠。
  • 2) 從CustomHeart預(yù)制中創(chuàng)建兩個(gè)對(duì)象骆捧,一個(gè)作為GameObject,另一個(gè)作為參考髓绽。
  • 3) 從CustomHeart創(chuàng)建網(wǎng)格資產(chǎn)prefabMesh的實(shí)例敛苇。 如果找到資產(chǎn),則清除其數(shù)據(jù)顺呕; 否則枫攀,它將創(chuàng)建一個(gè)新的空網(wǎng)格。
  • 4) 用新的網(wǎng)格數(shù)據(jù)更新prefabMesh并將其與CustomHeart資產(chǎn)關(guān)聯(lián)株茶。
  • 5) 使用prefabMesh更新GameObject的網(wǎng)格資源来涨。
  • 6) 在給定gameObj的給定路徑上創(chuàng)建一個(gè)Prefab Asset,包括場(chǎng)景中的所有子代启盛。 這將替代CustomHeart預(yù)制件中的任何東西蹦掐。
  • 7) 立即銷(xiāo)毀gameObj

保存文件僵闯,然后轉(zhuǎn)到HeartMesh.cs卧抗。 將SaveMesh的主體替換為以下內(nèi)容:

meshToSave.name = "HeartMesh";
meshToSave.vertices = originalMesh.vertices;
meshToSave.triangles = originalMesh.triangles;
meshToSave.normals = originalMesh.normals;
return meshToSave;

這將返回基于心形網(wǎng)格的網(wǎng)格資產(chǎn)。

保存文件并返回到Unity鳖粟。 按Play社裆。 動(dòng)畫(huà)結(jié)束時(shí),Save Mesh按鈕將出現(xiàn)在檢查器中向图。 單擊按鈕保存新的網(wǎng)格泳秀,然后停止播放器。

在“項(xiàng)目”視圖中再次找到Prefabs / CustomHeart榄攀,然后Prefab Editing模式下將其打開(kāi)嗜傅。 您會(huì)看到一個(gè)預(yù)制的心形網(wǎng)狀品牌已保存在您的預(yù)制件中!


Putting It All Together

在上一節(jié)中航攒,您學(xué)習(xí)了如何通過(guò)選擇單個(gè)頂點(diǎn)來(lái)修改網(wǎng)格磺陡。 雖然這很酷,但是如果您知道如何按程序選擇頂點(diǎn),則可以做更多有趣的事情币他。

在上一場(chǎng)景中坞靶,DisplaceVertices使用高斯衰減公式來(lái)確定在效果半徑內(nèi)“拉”每個(gè)頂點(diǎn)的量。 但是蝴悉,您還可以使用其他數(shù)學(xué)函數(shù)來(lái)計(jì)算“下降”點(diǎn)彰阴。 也就是說(shuō),拉力pull開(kāi)始衰減拍冠。 每個(gè)函數(shù)產(chǎn)生不同的形狀:

在本部分中尿这,您將學(xué)習(xí)如何使用計(jì)算出的曲線來(lái)操縱頂點(diǎn)。

基于速度等于距離除以時(shí)間(v =(d / t))的原理庆杜,可以通過(guò)將向量的距離除以時(shí)間因子來(lái)確定向量的位置射众。


Using the Curve Method

保存當(dāng)前場(chǎng)景,然后從Scenes文件夾中打開(kāi)03 Customize Heart Mesh晃财。

在層次結(jié)構(gòu)中找到CustomHeart預(yù)制實(shí)例叨橱,然后單擊其旁邊的箭頭圖標(biāo)以擴(kuò)展其內(nèi)容。 選擇Child對(duì)象断盛。

在檢查器中查看其屬性罗洗。 您將看到帶有Heart Mesh資源的Mesh Filter。 將Custom Heart附加到Child上钢猛。 資產(chǎn)現(xiàn)在應(yīng)該從HeartMesh更改為clone伙菜。

打開(kāi)CustomHeart.cs并在Start上方添加以下內(nèi)容:

public enum CurveType
{
    Curve1, Curve2
}

public CurveType curveType;
Curve curve;

這將創(chuàng)建一個(gè)名為CurveType的公共枚舉,并使其在Inspector中可用命迈。

轉(zhuǎn)到CurveType1并添加以下內(nèi)容:

Vector3[] curvepoints = new Vector3[3]; //1
curvepoints[0] = new Vector3(0, 1, 0);
curvepoints[1] = new Vector3(0.5f, 0.5f, 0);
curvepoints[2] = new Vector3(1, 0, 0);
curve = new Curve(curvepoints[0], curvepoints[1], curvepoints[2], false); //2

這里做了什么贩绕?

  • 1) 基本曲線由三個(gè)點(diǎn)組成。 該代碼設(shè)置并繪制第一條曲線的點(diǎn)躺翻。
  • 2) 使用Curve生成第一條曲線并將其值分配給Curve丧叽。 您可以將最后一個(gè)參數(shù)設(shè)置為true,以繪制曲線作為預(yù)覽公你。

現(xiàn)在轉(zhuǎn)到CurveType2并添加以下內(nèi)容:

Vector3[] curvepoints = new Vector3[3]; //1
curvepoints[0] = new Vector3(0, 0, 0);
curvepoints[1] = new Vector3(0.5f, 1, 0);
curvepoints[2] = new Vector3(1, 0, 0);
curve = new Curve(curvepoints[0], curvepoints[1], curvepoints[2], false); //2

這與CurveType1非常相似踊淳。

  • 1) 設(shè)置并繪制第二條曲線的點(diǎn)。
  • 2) 使用Curve方法生成第二條曲線陕靠,并將其值分配給curve迂尝。

StartDisplacement中,在右括號(hào)之前剪芥,添加以下內(nèi)容:

if (curveType == CurveType.Curve1)
{
    CurveType1();
}
else if (curveType == CurveType.Curve2)
{
    CurveType2();
}

根據(jù)在Custom Heart組件中選擇為Curve Type的內(nèi)容垄开,這將生成不同的曲線。

DisplaceVerticesfor循環(huán)內(nèi)税肪,在右花括號(hào)之前溉躲,添加以下內(nèi)容:

float increment = curve.GetPoint(distance).y * force; //1
Vector3 translate = (vert * increment) * Time.deltaTime; //2
Quaternion rotation = Quaternion.Euler(translate); 
Matrix4x4 m = Matrix4x4.TRS(translate, rotation, Vector3.one);
modifiedVertices[i] = m.MultiplyPoint3x4(modifiedVertices[i]);

這看起來(lái)很熟悉-就像您添加到HeartMesh的代碼一樣榜田。

  • 1) 獲取給定距離distance處的曲線位置,并將其y值乘以force即可獲得增量increment锻梳。
  • 2) 創(chuàng)建一個(gè)新的Vector3箭券,稱(chēng)為translate,以存儲(chǔ)當(dāng)前頂點(diǎn)的新位置并相應(yīng)地應(yīng)用其Transform疑枯。

保存文件并返回到Unity辩块。 在子GameObject上檢查Custom Heart中的屬性。

現(xiàn)在荆永,在Edit Type下拉菜單中废亭,可以選擇Add IndicesRemove Indices來(lái)更新頂點(diǎn)列表。 選擇None退出編輯模式具钥,然后單擊Play查看結(jié)果豆村。 試用不同的設(shè)置和頂點(diǎn)選擇。

要查看不同曲線類(lèi)型的示例氓拼,請(qǐng)輸入以下值:

將 Curve Type 設(shè)置為Curve1 你画,檢查Edit Type是否設(shè)置為None抵碟,然后按Play桃漾。

您應(yīng)該看到網(wǎng)格如何呈扇形展開(kāi)。 將模型移至其側(cè)視圖拟逮,以便可以看到該曲線產(chǎn)生的形狀撬统。 Exit Play,然后使用Curve 2再次嘗試比較兩種曲線類(lèi)型的結(jié)果:

就這樣敦迄! 您可以單擊Clear Selected Vertices以重置Selected Indices并嘗試使用自己的樣式恋追。 不要忘記,有幾個(gè)因素會(huì)影響網(wǎng)格的最終形狀:

  • 半徑的大小罚屋。
  • 頂點(diǎn)在區(qū)域內(nèi)的散布苦囱。
  • 選定頂點(diǎn)的圖案位置。
  • 您選擇的位移方法脾猛。

后記

本篇主要講述了使用Unity運(yùn)行時(shí)網(wǎng)格操作撕彤,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市猛拴,隨后出現(xiàn)的幾起案子羹铅,更是在濱河造成了極大的恐慌,老刑警劉巖愉昆,帶你破解...
    沈念sama閱讀 211,423評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件职员,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡跛溉,警方通過(guò)查閱死者的電腦和手機(jī)焊切,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)扮授,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人专肪,你說(shuō)我怎么就攤上這事糙箍。” “怎么了牵祟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,019評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵深夯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我诺苹,道長(zhǎng)咕晋,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,443評(píng)論 1 283
  • 正文 為了忘掉前任收奔,我火速辦了婚禮掌呜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坪哄。我一直安慰自己质蕉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,535評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布翩肌。 她就那樣靜靜地躺著模暗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪念祭。 梳的紋絲不亂的頭發(fā)上兑宇,一...
    開(kāi)封第一講書(shū)人閱讀 49,798評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音粱坤,去河邊找鬼隶糕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛站玄,可吹牛的內(nèi)容都是我干的枚驻。 我是一名探鬼主播,決...
    沈念sama閱讀 38,941評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼株旷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼再登!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起灾常,我...
    開(kāi)封第一講書(shū)人閱讀 37,704評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤霎冯,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后钞瀑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體沈撞,經(jīng)...
    沈念sama閱讀 44,152評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,494評(píng)論 2 327
  • 正文 我和宋清朗相戀三年雕什,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缠俺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片显晶。...
    茶點(diǎn)故事閱讀 38,629評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖壹士,靈堂內(nèi)的尸體忽然破棺而出磷雇,到底是詐尸還是另有隱情,我是刑警寧澤躏救,帶...
    沈念sama閱讀 34,295評(píng)論 4 329
  • 正文 年R本政府宣布唯笙,位于F島的核電站,受9級(jí)特大地震影響盒使,放射性物質(zhì)發(fā)生泄漏崩掘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,901評(píng)論 3 313
  • 文/蒙蒙 一少办、第九天 我趴在偏房一處隱蔽的房頂上張望苞慢。 院中可真熱鬧,春花似錦英妓、人聲如沸挽放。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辑畦。三九已至,卻和暖如春贺纲,著一層夾襖步出監(jiān)牢的瞬間航闺,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,978評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工猴誊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人磕瓷。 一個(gè)月前我還...
    沈念sama閱讀 46,333評(píng)論 2 360
  • 正文 我出身青樓荒叼,卻偏偏與公主長(zhǎng)得像最冰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子澄成,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,499評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容