這個(gè)是unity官方演示ECS
https://connect.unity.com/p/zhi-bo-hui-gu-shi-yong-unity-ecskai-fa-wo-de-shi-jie
環(huán)境搭設(shè)
打開(kāi)PlayerSetting確保.NET庫(kù)在4.X以上
進(jìn)入工程文件夾
把下面腳本加入進(jìn)入
{
{
"dependencies": {
"com.unity.entities": "0.0.12-preview.16"
},
"registry": "https://packages.unity.com",
"testables": [
"com.unity.collections",
"com.unity.entities",
"com.unity.jobs"
]
}
然后就自動(dòng)下載
我試了很多版本都報(bào)迷之錯(cuò)誤,這個(gè)不會(huì)報(bào)錯(cuò)
這里可以查看安裝胯杭,有的版本可以直接在這個(gè)界面安裝,就不用那樣Json輸入然后又匹配不對(duì)了
然后環(huán)境算是安裝好了
我們可以看到ECS方式創(chuàng)建預(yù)制體磺送,一定有一個(gè)GameObjectEntity還有一堆Component
打開(kāi)EntitiesDebug界面
創(chuàng)建對(duì)比
然后是三種創(chuàng)建方式對(duì)比下
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Rendering;
using Unity.Collections;
public class EntitiesTest : MonoBehaviour
{
//宣告內(nèi)存字段放在一起 而不是雜亂無(wú)序的 提高速率
public static EntityArchetype blockArchetype;
public EntityManager manager;
public Mesh blockMesh;
public Material blockMaterial;
public GameObject go;
//宣告在加載場(chǎng)景之前運(yùn)行 可以理解為預(yù)存
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void Initialize()
{
//宣告管理器
EntityManager manager = World.Active.GetOrCreateManager<EntityManager>();
//宣告內(nèi)存區(qū)塊(區(qū)域)
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
//[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
void Start()
{
//Unity自己產(chǎn)生預(yù)制體 但是要添加GameObjectEntity Entities才能標(biāo)記
//或者 Instantiate
GameObject.CreatePrimitive(PrimitiveType.Cube)
.AddComponent<GameObjectEntity>()
.transform.position = new Vector3(-2, 0, 0);
//PureECS 下面是不用Unity自帶組件創(chuàng)建預(yù)制體 就是說(shuō)脫離Unity也可以用的代碼
manager = World.Active.GetOrCreateManager<EntityManager>();
//在內(nèi)存塊中設(shè)置Pos和創(chuàng)建一個(gè)tag(并沒(méi)有用)
Entity entities = manager.CreateEntity(blockArchetype);
manager.SetComponentData(entities, new Position { Value = new int3(2, 0, 0) });
//這個(gè)是自定義的我們沒(méi)有..
//manager.AddComponentData(entities, new BlockTag());
//添加材質(zhì)
manager.AddSharedComponentData(entities, new MeshInstanceRenderer
{
mesh = blockMesh,
material = blockMaterial,
});
//Hybrid ECS 引用unity自帶GameObject創(chuàng)建預(yù)制體
if (go)
{
//產(chǎn)生一個(gè)新的陣列
using (NativeArray<Entity> entityArray = new NativeArray<Entity>(1, Allocator.Temp))
{
manager.Instantiate(go, entityArray);
manager.SetComponentData(entities, new Position { Value = new float3(4, 0f, 0f) });
}
}
}
}
這個(gè)是預(yù)制體創(chuàng)建要掛的腳本,框架自帶
前兩個(gè)是不依賴(lài)Unity所需要的網(wǎng)格和材質(zhì)
最后一個(gè)就是以產(chǎn)生預(yù)制體方式產(chǎn)生
通過(guò)Entities創(chuàng)建的預(yù)制體不會(huì)在這里顯示
在自帶的EntitiesDebug窗口中可看見(jiàn)
選擇一個(gè)灿意,右邊有相應(yīng)的信息,可是只能代碼動(dòng)態(tài)調(diào)整崇呵,以后會(huì)改成可調(diào)整缤剧,也就是說(shuō)和GameObject對(duì)比起來(lái)沒(méi)有什么區(qū)別的樣子
世界生成
然后是我的世界生成世界是隨機(jī)的,這里要用到柏林噪聲域慷,有規(guī)則的亂序生成荒辕,保證了之后地圖再生成的銜接問(wèn)題,不會(huì)出現(xiàn)地圖生成斷開(kāi)了
這個(gè)是生成的圖
這個(gè)是3D效果
上去官方開(kāi)源gitHub扒素材學(xué)習(xí) 建議源碼看視屏寫(xiě)不出來(lái) 有bug再看 因?yàn)檫@個(gè)教程都是干貨 2小時(shí)含金量特別大
https://github.com/UnityTechnologies/MinecraftECS
找到這四個(gè)文件犹褒,放入unity
生成地圖
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//生成一個(gè)方塊
public class PerlinNoiseGenerator
{
public static int BlockFaces = 0;
public static Texture2D noiseHeightMap;
//長(zhǎng)寬的地圖
int texWidth = 200, texHeight = 200;
//噪聲縮放值 值越大越密集
float scale1 = 1f;
float scale2 = 10f;
float scale3 = 20f;
//隨機(jī)采樣偏移
float offectX;
float offectY;
public PerlinNoiseGenerator()
{
offectX = Random.Range(0, 99999);
offectY = Random.Range(0, 99999);
}
/// <summary>
/// 根據(jù)長(zhǎng)短創(chuàng)建200X200的每一個(gè)點(diǎn)
/// </summary>
/// <returns></returns>
public Texture2D GenerateHeightMap()
{
Texture2D heightMap = new Texture2D(texWidth, texHeight);
for (int i = 0; i < texWidth; i++)
{
for (int j = 0; j < texHeight; j++)
{
Color color = CakculateColor(i, j);
heightMap.SetPixel(i, j, color);
}
}
heightMap.Apply();
return heightMap;
}
/// <summary>
/// 用unity自帶的2維柏林噪聲計(jì)算每個(gè)點(diǎn)的偏移值
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
Color CakculateColor(int x, int y)
{
//根據(jù)我們的偏移值 計(jì)算出類(lèi)似于波形的圖 就是黑白黑白間隔的圖
float xCoord1 = (float)x / texWidth * scale1 + offectX;
float yCoord1 = (float)y / texHeight * scale1 + offectY;
float xCoord2 = (float)x / texWidth * scale2 + offectX;
float yCoord2 = (float)y / texHeight * scale2 + offectY;
float xCoord3 = (float)x / texWidth * scale3 + offectX;
float yCoord3 = (float)y / texHeight * scale3 + offectY;
//返回值為0.0 ~ 1.0之間的小數(shù) 可能會(huì)略大于一
float sample1 = Mathf.PerlinNoise(xCoord1, yCoord1) / 15;
float sample2 = Mathf.PerlinNoise(xCoord2, yCoord2) / 15;
float sample3 = Mathf.PerlinNoise(xCoord3, yCoord3) / 15;
return new Color(sample1 + sample2 + sample3, sample1 + sample2 + sample3, sample1 + sample2 + sample3);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Rendering;
/// <summary>
/// 加載一堆方塊
/// </summary>
public class SpawnNumBlocks : MonoBehaviour
{
public Texture2D heightmap;
public static EntityArchetype blockArchetype;
//10X10的地方留著 之外的不顯示或刪除 類(lèi)似于遮擋剔除
[Header("Wrold = ChunkBase x ChunkBase")]
public int chunckBase = 1;
[Header("Mesh Info")]
public Mesh blockMesh;
[Header("For Log")]
public Material[] mats;
Material maTemp;
public EntityManager manager;
public Entity entities;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void Initialize()
{
EntityManager manager = World.Active.GetOrCreateManager<EntityManager>();
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
void Start()
{
manager = World.Active.GetOrCreateManager<EntityManager>();
PerlinNoiseGenerator perlin = new PerlinNoiseGenerator();
heightmap = perlin.GenerateHeightMap();
ChunkGenerator(chunckBase);
}
void ChunkGenerator(int amount)
{
//一個(gè)chunckBase相當(dāng)于1500個(gè)方塊
int totalamount = (amount * amount) * 1500;
int highlevel;
bool airChecker;
for (int y = 0; y < 15; y++)
{
for (int x = 0; x < 10 * amount; x++)
{
for (int z = 0; z < 10 * amount; z++)
{
//返回像素顏色 數(shù)很小乘上100
highlevel = (int)(heightmap.GetPixel(x, z).r * 100) - y;
airChecker = false;
Vector3 posTemp = new Vector3(x, y, z);
if (highlevel>=0& highlevel< mats.Length-1)
{
maTemp = mats[highlevel];
}
else
{
//超過(guò)的視為空氣
maTemp = mats[mats.Length - 1];
airChecker = true;
}
if (!airChecker)
{
Entity entities = manager.CreateEntity(blockArchetype);
manager.SetComponentData(entities, new Position { Value = new int3(x, y, z) });
//manager.AddComponentData(entities, new BlockTag { });
manager.AddSharedComponentData(entities, new MeshInstanceRenderer
{
mesh = blockMesh,
material = maTemp,
});
}
}
}
}
}
}
然后是找到數(shù)字掛在
運(yùn)行
其實(shí)本來(lái)應(yīng)該運(yùn)行Batches數(shù)到達(dá)2w的但為什么這么低
因?yàn)楣戳擞肎PU Instance創(chuàng)建預(yù)制體抵窒,在大量物體創(chuàng)建時(shí)會(huì)進(jìn)行優(yōu)化,如果是少量就和直接Instance沒(méi)有區(qū)別
然后我們就可以根據(jù)層生成不同的方塊
樹(shù)就是從根部開(kāi)始 柱子0-7 然后再周?chē)訕?shù)葉
把之前代碼進(jìn)一步擴(kuò)充
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Rendering;
using System;
public class GameSetting : MonoBehaviour
{
Texture2D heightmap;
public static EntityArchetype blockArchetype;
[Header("Wrold = ChunkBase x ChunkBase")]
public int chunckBase = 1;
[Header("Mesh Info")]
public Mesh blockMesh;
public Mesh surfaceMesh;
public Mesh tallGrassMesh;
[Header("Nature Block Type")]
public Material stoneMat;
public Material woodMat;
public Material leavesMat;
public Material surfaceMat;
public Material cobbleMat;
public Material dirtMaterial;
public Material tallGrassMat;
public Material roseMat;
public Material CloudMat;
[Header("Other Block Type")]
public Material glassMat;
public Material brickMat;
public Material plankMat;
public Material tntMat;
//找不到用粉色
[Header("")]
public Material pinkMat;
public bool createCollider = true;
public GameObject boxCollider;
Mesh meshTemp;
Material maTemp;
EntityManager manager;
Entity entities;
int random;
ColliderPool colPool;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void Initialize()
{
//檢查場(chǎng)景是否有 有得到?jīng)]有創(chuàng)建
EntityManager manager = World.Active.GetOrCreateManager<EntityManager>();
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
void Start()
{
manager = World.Active.GetOrCreateManager<EntityManager>();
PerlinNoiseGenerator perlin = new PerlinNoiseGenerator();
heightmap = perlin.GenerateHeightMap();
//創(chuàng)建一個(gè)碰撞池
colPool = new ColliderPool(boxCollider, transform);
ChunckGenerator(chunckBase);
}
void ChunckGenerator(int amount)
{
int totalamount = (amount * amount) * 1500;
int highlevel;
bool airChecker;
for (int y = 0; y < 15; y++)
{
for (int x = 0; x < 10 * amount; x++)
{
for (int z = 0; z < 10 * amount; z++)
{
//返回像素顏色 數(shù)很小乘上100
highlevel = (int)(heightmap.GetPixel(x, z).r * 100) - y;
airChecker = false;
switch (highlevel)
{
//表層 根據(jù)一個(gè)單位 和 多個(gè)單位 分開(kāi)方法創(chuàng)建
case 0:
random = UnityEngine.Random.Range(1, 201);
if (random <= 20)
{
//草
PlantGenerator(x, y, z, 1);
}
else if (random == 198)
{
//云
CloudGenerator(x, y, z);
}
else if (random == 199)
{
//樹(shù)
TreeGenerator(x, y, z);
}
else if (random == 200)
{
//花
PlantGenerator(x, y, z, 2);
}
airChecker = true;
break;
case 1:
//綠色帶土的方塊
meshTemp = surfaceMesh;
maTemp = surfaceMat;
break;
case 2:
case 3:
case 4:
//土
meshTemp = blockMesh;
maTemp = dirtMaterial;
break;
case 5:
case 6:
//石頭
meshTemp = blockMesh;
maTemp = stoneMat;
break;
case 7:
case 8:
//鵝卵石
meshTemp = blockMesh;
maTemp = cobbleMat;
break;
default:
airChecker = true;
break;
}
if (!airChecker)
{
CreatePrefab(x, y, z, meshTemp, maTemp);
}
}
}
}
}
void TreeGenerator(int x, int y, int z)
{
for (int i = y; i < y + 7; i++)
{
//軀干部分
if (i == y + 6)
{
//樹(shù)頂
maTemp = leavesMat;
}
else
{
maTemp = woodMat;
}
CreatePrefab(x, i, z, blockMesh, maTemp);
//樹(shù)葉 就是個(gè)正方形
if (i >= y + 3 && i <= y + 6)
{
for (int j = x - 1; j <= x + 1; j++)
{
for (int k = z - 1; k <= z + 1; k++)
{
//不能隨機(jī)到軀干
if ( j != x||k != z)
{
CreatePrefab(j, i, k, blockMesh, leavesMat);
}
}
}
}
}
}
void PlantGenerator(int x, int y, int z, int plantType)
{
switch (plantType)
{
case 1:
maTemp = tallGrassMat;
break;
default:
maTemp = roseMat;
break;
}
CreatePrefab(x, y, z, tallGrassMesh, maTemp, (entities) => { manager.AddComponentData(entities, new Rotation { Value = Quaternion.Euler(0, 45, 0) }); });
}
void CloudGenerator(int x, int y, int z)
{
random = UnityEngine.Random.Range(4, 7);
//提升y的高度 產(chǎn)生一個(gè)方形的云
for (int i = 0; i < random; i++)
{
for (int j = 0; j < random; j++)
{
CreatePrefab(x + i, y + 15, z + j, blockMesh, CloudMat);
}
}
}
delegate void CreateFunc(Entity entities);
void CreatePrefab(int x, int y, int z, Mesh mesh, Material ma, CreateFunc func = null)
{
AddCollider(new Vector3(x, y, z));
Entity entities = manager.CreateEntity(blockArchetype);
manager.SetComponentData(entities, new Position { Value = new int3(x, y, z) });
//manager.AddComponentData(entities, new BlockTag { });
//找不到是粉色方塊
if (!maTemp)
maTemp = pinkMat;
func?.Invoke(entities);
manager.AddSharedComponentData(entities, new MeshInstanceRenderer
{
mesh = mesh,
material = ma,
});
}
//在相同位置生成一個(gè)1x1x1的正方形碰撞
void AddCollider(Vector3 vec3)
{
if (createCollider)
{
colPool.AddCollider(vec3);
}
}
}
還有個(gè)碰撞腳本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ColliderPool
{
GameObject boxCollider;
Transform parent;
public ColliderPool(GameObject boxCollider, Transform parent)
{
this.boxCollider = boxCollider;
this.parent = parent;
}
//在同樣位置創(chuàng)建一個(gè)單位碰撞
public void AddCollider(Vector3 vec3)
{
GameObject obj = GameObject.Instantiate(boxCollider);
obj.transform.position = vec3;
obj.transform.parent = parent;
obj.layer = 9;
}
}
這么掛腳本
運(yùn)行
把Chunckbase改為100卡了N久700w個(gè)物體
Collider就是(1,1,1)的方塊的碰撞
我寫(xiě)到現(xiàn)在出過(guò)幾次錯(cuò)誤叠骑,就是mesh被拉伸,加加減減比較多
人物控制
從這里下載官方標(biāo)準(zhǔn)資源包李皇,導(dǎo)入
把第一人稱(chēng)控制器拖出來(lái)就是第一個(gè),因?yàn)樗詭z像機(jī)宙枷,把一開(kāi)始世界的攝像機(jī)刪掉
因?yàn)槲覀儾菀彩荂ube碰撞掉房,所以沒(méi)辦法從草上傳過(guò)去
第一人稱(chēng)自動(dòng)把鼠標(biāo)屏蔽了 ESC就可以移動(dòng)出Game視圖了
實(shí)現(xiàn)挖掘
然后先把壓縮包的音效文件導(dǎo)入
然后創(chuàng)建一個(gè)新Tag Blocks
找到碰撞器 改變layer
/**
*Copyright(C) 2019 by #COMPANY#
*All rights reserved.
*FileName: #SCRIPTFULLNAME#
*Author: #AUTHOR#
*Version: #VERSION#
*UnityVersion:#UNITYVERSION#
*Date: #DATE#
*Description:
*History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using Unity.Entities;
using System;
using Unity.Transforms;
using Unity.Rendering;
public class PickaxeController : MonoBehaviour
{
public LayerMask blockLayer;
GameObject player;
public static Transform blockToDestroy;
Material blockToPlace;
public static int blockID = 1;
//音效
public AudioClip grass_audio;
public AudioClip stone_audio;
public AudioClip dirt_audio;
public AudioClip wood_audio;
AudioSource AS;
//挖掘噴發(fā)的特效
public ParticleSystem digEffect;
EntityManager manager;
GameSetting gs;
// Start is called before the first frame update
void Start()
{
AS = transform.GetComponent<AudioSource>();
gs = FindObjectOfType<GameSetting>();
player = gameObject;
Cursor.lockState = CursorLockMode.Locked;
manager = World.Active.GetOrCreateManager<EntityManager>();
}
// Update is called once per frame
void Update()
{
//滑動(dòng)過(guò)界就改變?yōu)榱硪贿? if (blockID > 7)
{
blockID = 1;
}
if (blockID < 1)
{
blockID = 7;
}
//滑動(dòng)選擇方塊
if (Input.GetAxis("Mouse ScrollWheel") < 0)
{
blockID++;
}
if (Input.GetAxis("Mouse ScrollWheel") > 0)
{
blockID--;
}
//看是選擇哪個(gè)磚塊
switch (blockID)
{
case 1:
blockToPlace = gs.stoneMat;
break;
case 2:
blockToPlace = gs.plankMat;
break;
case 3:
blockToPlace = gs.glassMat;
break;
case 4:
blockToPlace = gs.woodMat;
break;
case 5:
blockToPlace = gs.cobbleMat;
break;
case 6:
blockToPlace = gs.tntMat;
break;
case 7:
blockToPlace = gs.brickMat;
break;
}
//左鍵放方塊 右鍵刪除方塊
if (Input.GetMouseButtonDown(1))
{
PlaceBlock(blockToPlace);
}
else if (Input.GetMouseButtonDown(0))
{
DestroyBlock();
}
}
private void PlaceBlock(Material blockToPlace)
{
RaycastHit hit;
//向角色正前方發(fā)射射線(xiàn)
Physics.Raycast(transform.position, transform.forward, out hit, blockLayer);
if (hit.transform != null)
{
//根據(jù)不同方塊播放音效
if (blockID == 1 || blockID == 3 || blockID == 5 || blockID == 7)
{
AS.PlayOneShot(stone_audio);
}
else if (blockID == 2 || blockID == 4)
{
AS.PlayOneShot(wood_audio);
}
//創(chuàng)建一個(gè)方塊在射線(xiàn)前方法線(xiàn)的位置 加上碰撞
Position pos = new Position { Value = hit.transform.position + hit.normal };
Entity entities = manager.CreateEntity(GameSetting.blockArchetype);
manager.SetComponentData(entities, pos);
manager.AddComponentData(entities, new BlockTag { });
manager.AddSharedComponentData(entities, new MeshInstanceRenderer
{
mesh = gs.blockMesh,
material = blockToPlace
});
gs.colPool.AddCollider(pos.Value);
}
}
private void DestroyBlock()
{
RaycastHit hit;
//向角色正前方發(fā)射射線(xiàn)
Physics.Raycast(transform.position, transform.forward, out hit,7, blockLayer);
if (hit.transform!=null)
{
//在同樣位置
Entity entities = manager.CreateEntity(GameSetting.blockArchetype);
manager.SetComponentData(entities, new Position { Value=hit.transform.position});
manager.AddComponentData(entities, new DestoryTag { });
if (digEffect&&!digEffect.isPlaying)
{
digEffect.transform.position = hit.transform.position;
digEffect.Play();
}
//放音效刪除
AS.PlayOneShot(dirt_audio);
Destroy(hit.transform.gameObject);
}
}
}
然后在第一人稱(chēng)控制器的子節(jié)點(diǎn)掛載腳本
然后還是不能刪除茧跋,因?yàn)閯h除的只有碰撞
刪除系統(tǒng)
到現(xiàn)在ECS只用到EC沒(méi)說(shuō)S
之前我們的面向?qū)ο笫且粋€(gè)Class貫穿這么多處理
轉(zhuǎn)化為System的話(huà) 是有個(gè)System處理專(zhuān)門(mén)每一個(gè)模塊(用JobSystem)
所以做挖掘(刪除)需要處理,這個(gè)就是之前我們打不出來(lái)的Tag卓囚,根據(jù)Tag區(qū)分
這個(gè)是Tag腳本
/**
*Copyright(C) 2019 by #COMPANY#
*All rights reserved.
*FileName: #SCRIPTFULLNAME#
*Author: #AUTHOR#
*Version: #VERSION#
*UnityVersion:#UNITYVERSION#
*Date: #DATE#
*Description:
*History:
*/
using System;
using Unity.Entities;
//都只是作為一個(gè)標(biāo)簽
//比如自帶的Position 如果需要的屬性沒(méi)有可以自己在這邊定義
[Serializable]
public struct BlockTag : IComponentData { }
public class BlockTagComponment : ComponentDataWrapper<BlockTag> { };
[Serializable]
public struct DestoryTag : IComponentData { }
public class DestoryTagComponment : ComponentDataWrapper<DestoryTag> { };
[Serializable]
public struct SurfacePlantTag : IComponentData { }
public class SurfacePlantTagComponment : ComponentDataWrapper<SurfacePlantTag> { };
刪除系統(tǒng) 這個(gè)OnUpdate就跟unity的update一樣
/**
*Copyright(C) 2019 by #COMPANY#
*All rights reserved.
*FileName: #SCRIPTFULLNAME#
*Author: #AUTHOR#
*Version: #VERSION#
*UnityVersion:#UNITYVERSION#
*Date: #DATE#
*Description:
*History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Collections;
using Unity.Mathematics;
//IJobProcessComponentData可以放到JobSystem處理
public class DestroySystem : ComponentSystem
{
//類(lèi)似于Select語(yǔ)句
struct BlockGroup
{
[ReadOnly] public readonly int Length;
[ReadOnly] public EntityArray entity;
//就是檢索語(yǔ)句 符合的條件都在這里面
[ReadOnly] public ComponentDataArray<Position> postions;
[ReadOnly] public ComponentDataArray<BlockTag> tags;
}
struct DestoryBlockGroup
{
[ReadOnly] public readonly int Length;
[ReadOnly] public EntityArray entity;
[ReadOnly] public ComponentDataArray<Position> postions;
[ReadOnly] public ComponentDataArray<DestoryTag> tags;
}
//某些方塊上的花
struct SurfacePlantGroup
{
[ReadOnly] public readonly int Length;
[ReadOnly] public EntityArray entity;
[ReadOnly] public ComponentDataArray<Position> postions;
[ReadOnly] public ComponentDataArray<SurfacePlantTag> tags;
}
//群組 Inject是注入數(shù)據(jù)
[Inject] BlockGroup targetBlocks;
[Inject] DestoryBlockGroup sourceBlocks;
[Inject] SurfacePlantGroup surfacePlants;
protected override void OnUpdate()
{
for (int i = 0; i < sourceBlocks.Length; i++)
{
for (int j = 0; j < targetBlocks.Length; j++)
{
Vector3 offect = targetBlocks.postions[j].Value - sourceBlocks.postions[i].Value;
//平方
float sqrLen = offect.sqrMagnitude;
//就是刪除方塊組中有和總方塊組一樣的 就刪除
if (sqrLen == 0)
{
//同時(shí)尋找磚塊上是否有草 有了也刪除 就是草的位置y-1如果等于現(xiàn)在位置就刪除
for (int k = 0; k < surfacePlants.Length; k++)
{
float3 pos = new float3(surfacePlants.postions[k].Value.x, surfacePlants.postions[k].Value.y + Vector3.down.y, surfacePlants.postions[k].Value.z);
offect = targetBlocks.postions[j].Value - pos;
sqrLen = offect.sqrMagnitude;
if (sqrLen == 0)
{
PostUpdateCommands.DestroyEntity(surfacePlants.entity[k]);
}
}
//刪除 刪除方塊組和總方塊組的該entity
PostUpdateCommands.DestroyEntity(sourceBlocks.entity[i]);
PostUpdateCommands.DestroyEntity(targetBlocks.entity[j]);
}
}
}
}
}
然后把剛才寫(xiě)的挖掘Destory的Tag解開(kāi)
不要忘了解除生成方塊的Tag
然后把GameSetting改一下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Rendering;
using System;
public class GameSetting : MonoBehaviour
{
Texture2D heightmap;
public static EntityArchetype blockArchetype;
[Header("Wrold = ChunkBase x ChunkBase")]
public int chunckBase = 1;
[Header("Mesh Info")]
public Mesh blockMesh;
public Mesh surfaceMesh;
public Mesh tallGrassMesh;
[Header("Nature Block Type")]
public Material stoneMat;
public Material woodMat;
public Material leavesMat;
public Material surfaceMat;
public Material cobbleMat;
public Material dirtMaterial;
public Material tallGrassMat;
public Material roseMat;
public Material CloudMat;
[Header("Other Block Type")]
public Material glassMat;
public Material brickMat;
public Material plankMat;
public Material tntMat;
//找不到用粉色
[Header("")]
public Material pinkMat;
public bool createCollider = true;
public GameObject boxCollider;
Mesh meshTemp;
Material maTemp;
EntityManager manager;
Entity entities;
int random;
public ColliderPool colPool;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void Initialize()
{
//檢查場(chǎng)景是否有 有得到?jīng)]有創(chuàng)建
EntityManager manager = World.Active.GetOrCreateManager<EntityManager>();
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
void Start()
{
manager = World.Active.GetOrCreateManager<EntityManager>();
PerlinNoiseGenerator perlin = new PerlinNoiseGenerator();
heightmap = perlin.GenerateHeightMap();
//創(chuàng)建一個(gè)碰撞池
colPool = new ColliderPool(boxCollider, transform);
ChunckGenerator(chunckBase);
}
void ChunckGenerator(int amount)
{
int totalamount = (amount * amount) * 1500;
int highlevel;
bool airChecker;
for (int y = 0; y < 15; y++)
{
for (int x = 0; x < 10 * amount; x++)
{
for (int z = 0; z < 10 * amount; z++)
{
//返回像素顏色 數(shù)很小乘上100
highlevel = (int)(heightmap.GetPixel(x, z).r * 100) - y;
airChecker = false;
switch (highlevel)
{
//表層 根據(jù)一個(gè)單位 和 多個(gè)單位 分開(kāi)方法創(chuàng)建
case 0:
random = UnityEngine.Random.Range(1, 201);
if (random <= 20)
{
//草
PlantGenerator(x, y, z, 1);
}
else if (random == 198)
{
//云
CloudGenerator(x, y, z);
}
else if (random == 199)
{
//樹(shù)
TreeGenerator(x, y, z);
}
else if (random == 200)
{
//花
PlantGenerator(x, y, z, 2);
}
airChecker = true;
break;
case 1:
//綠色帶土的方塊
meshTemp = surfaceMesh;
maTemp = surfaceMat;
break;
case 2:
case 3:
case 4:
//土
meshTemp = blockMesh;
maTemp = dirtMaterial;
break;
case 5:
case 6:
//石頭
meshTemp = blockMesh;
maTemp = stoneMat;
break;
case 7:
case 8:
//鵝卵石
meshTemp = blockMesh;
maTemp = cobbleMat;
break;
default:
airChecker = true;
break;
}
if (!airChecker)
{
CreatePrefab(x, y, z ,meshTemp, maTemp, new BlockTag { });
}
}
}
}
}
void TreeGenerator(int x, int y, int z)
{
for (int i = y; i < y + 7; i++)
{
//軀干部分
if (i == y + 6)
{
//樹(shù)頂
maTemp = leavesMat;
}
else
{
maTemp = woodMat;
}
CreatePrefab(x, i, z, blockMesh, maTemp, new BlockTag { });
//樹(shù)葉 就是個(gè)正方形
if (i >= y + 3 && i <= y + 6)
{
for (int j = x - 1; j <= x + 1; j++)
{
for (int k = z - 1; k <= z + 1; k++)
{
//不能隨機(jī)到軀干
if (j != x || k != z)
{
CreatePrefab(j, i, k, blockMesh, leavesMat, new BlockTag { });
}
}
}
}
}
}
void PlantGenerator(int x, int y, int z, int plantType)
{
switch (plantType)
{
case 1:
maTemp = tallGrassMat;
break;
default:
maTemp = roseMat;
break;
}
CreatePrefab(x, y, z, tallGrassMesh, maTemp, new SurfacePlantTag { },(entities) => { manager.AddComponentData(entities, new Rotation { Value = Quaternion.Euler(0, 45, 0) }); });
}
void CloudGenerator(int x, int y, int z)
{
random = UnityEngine.Random.Range(4, 7);
//提升y的高度 產(chǎn)生一個(gè)方形的云
for (int i = 0; i < random; i++)
{
for (int j = 0; j < random; j++)
{
CreatePrefab(x + i, y + 15, z + j, blockMesh, CloudMat,new BlockTag { });
}
}
}
delegate void CreateFunc(Entity entities);
void CreatePrefab<T>(int x, int y, int z, Mesh mesh, Material ma,T componentData ,CreateFunc func = null)where T :struct,IComponentData
{
AddCollider(new Vector3(x, y, z));
Entity entities = manager.CreateEntity(blockArchetype);
manager.SetComponentData(entities, new Position { Value = new int3(x, y, z) });
manager.AddComponentData(entities, componentData);
//找不到是粉色方塊
if (!maTemp)
maTemp = pinkMat;
func?.Invoke(entities);
manager.AddSharedComponentData(entities, new MeshInstanceRenderer
{
mesh = mesh,
material = ma,
});
}
//在相同位置生成一個(gè)1x1x1的正方形碰撞
void AddCollider(Vector3 vec3)
{
if (createCollider)
{
colPool.AddCollider(vec3);
}
}
}
然后是左鍵可以銷(xiāo)毀Cube 右鍵可以放置Cube瘾杭,不過(guò)會(huì)出問(wèn)題,就是銷(xiāo)毀后他本體不會(huì)消失哪亿,添加還會(huì)重疊233粥烁,最后再找這些BUG,先上UI
UI
他做好的直接拿出來(lái)用
是這個(gè)樣子
下面的腳本都丟失了蝇棉,我們寫(xiě)一個(gè)
/**
*Copyright(C) 2019 by #COMPANY#
*All rights reserved.
*FileName: #SCRIPTFULLNAME#
*Author: #AUTHOR#
*Version: #VERSION#
*UnityVersion:#UNITYVERSION#
*Date: #DATE#
*Description:
*History:
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ToolbarScript : MonoBehaviour
{
public int blockNum;
public Transform select;
// Start is called before the first frame update
void Start()
{
select = transform.GetChild(0);
}
// Update is called once per frame
void Update()
{
if (PickaxeController.blockID==blockNum)
{
select.GetComponent<RawImage>().enabled = true;
}
else
{
select.GetComponent<RawImage>().enabled = false;
}
}
}
然后像這樣每個(gè)都處理下
來(lái)找找BUG問(wèn)題
運(yùn)行后發(fā)現(xiàn)DestorySystem有點(diǎn)不正常
第一個(gè)是草走不過(guò)去页徐,因?yàn)榻o草加了碰撞 修改如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Rendering;
using System;
public class GameSetting : MonoBehaviour
{
Texture2D heightmap;
public static EntityArchetype blockArchetype;
[Header("Wrold = ChunkBase x ChunkBase")]
public int chunckBase = 1;
[Header("Mesh Info")]
public Mesh blockMesh;
public Mesh surfaceMesh;
public Mesh tallGrassMesh;
[Header("Nature Block Type")]
public Material stoneMat;
public Material woodMat;
public Material leavesMat;
public Material surfaceMat;
public Material cobbleMat;
public Material dirtMaterial;
public Material tallGrassMat;
public Material roseMat;
public Material CloudMat;
[Header("Other Block Type")]
public Material glassMat;
public Material brickMat;
public Material plankMat;
public Material tntMat;
//找不到用粉色
[Header("")]
public Material pinkMat;
public bool createCollider = true;
public GameObject boxCollider;
Mesh meshTemp;
Material maTemp;
EntityManager manager;
Entity entities;
int random;
public ColliderPool colPool;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void Initialize()
{
//檢查場(chǎng)景是否有 有得到?jīng)]有創(chuàng)建
EntityManager manager = World.Active.GetOrCreateManager<EntityManager>();
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
void Start()
{
manager = World.Active.GetOrCreateManager<EntityManager>();
PerlinNoiseGenerator perlin = new PerlinNoiseGenerator();
heightmap = perlin.GenerateHeightMap();
//創(chuàng)建一個(gè)碰撞池
colPool = new ColliderPool(boxCollider, transform);
ChunckGenerator(chunckBase);
}
void ChunckGenerator(int amount)
{
int totalamount = (amount * amount) * 1500;
int highlevel;
bool airChecker;
for (int y = 0; y < 15; y++)
{
for (int x = 0; x < 10 * amount; x++)
{
for (int z = 0; z < 10 * amount; z++)
{
//返回像素顏色 數(shù)很小乘上100
highlevel = (int)(heightmap.GetPixel(x, z).r * 100) - y;
airChecker = false;
switch (highlevel)
{
//表層 根據(jù)一個(gè)單位 和 多個(gè)單位 分開(kāi)方法創(chuàng)建
case 0:
random = UnityEngine.Random.Range(1, 201);
if (random <= 20)
{
//草
PlantGenerator(x, y, z, 1);
}
else if (random == 198)
{
//云
CloudGenerator(x, y, z);
}
else if (random == 199)
{
//樹(shù)
TreeGenerator(x, y, z);
}
else if (random == 200)
{
//花
PlantGenerator(x, y, z, 2);
}
airChecker = true;
break;
case 1:
//綠色帶土的方塊
meshTemp = surfaceMesh;
maTemp = surfaceMat;
break;
case 2:
case 3:
case 4:
//土
meshTemp = blockMesh;
maTemp = dirtMaterial;
break;
case 5:
case 6:
//石頭
meshTemp = blockMesh;
maTemp = stoneMat;
break;
case 7:
case 8:
//鵝卵石
meshTemp = blockMesh;
maTemp = cobbleMat;
break;
default:
airChecker = true;
break;
}
if (!airChecker)
{
CreatePrefab(x, y, z ,meshTemp, maTemp, new BlockTag { });
}
}
}
}
}
void TreeGenerator(int x, int y, int z)
{
for (int i = y; i < y + 7; i++)
{
//軀干部分
if (i == y + 6)
{
//樹(shù)頂
maTemp = leavesMat;
}
else
{
maTemp = woodMat;
}
CreatePrefab(x, i, z, blockMesh, maTemp, new BlockTag { });
//樹(shù)葉 就是個(gè)正方形
if (i >= y + 3 && i <= y + 6)
{
for (int j = x - 1; j <= x + 1; j++)
{
for (int k = z - 1; k <= z + 1; k++)
{
//不能隨機(jī)到軀干
if (j != x || k != z)
{
CreatePrefab(j, i, k, blockMesh, leavesMat, new BlockTag { });
}
}
}
}
}
}
void PlantGenerator(int x, int y, int z, int plantType)
{
switch (plantType)
{
case 1:
maTemp = tallGrassMat;
break;
default:
maTemp = roseMat;
break;
}
CreatePrefab(x, y, z, tallGrassMesh, maTemp, new SurfacePlantTag { },false,(entities) => { manager.AddComponentData(entities, new Rotation { Value = Quaternion.Euler(0, 45, 0) }); });
}
void CloudGenerator(int x, int y, int z)
{
random = UnityEngine.Random.Range(4, 7);
//提升y的高度 產(chǎn)生一個(gè)方形的云
for (int i = 0; i < random; i++)
{
for (int j = 0; j < random; j++)
{
CreatePrefab(x + i, y + 15, z + j, blockMesh, CloudMat,new BlockTag { },false);
}
}
}
delegate void CreateFunc(Entity entities);
void CreatePrefab<T>(int x, int y, int z, Mesh mesh, Material ma,T componentData ,bool isCollider=true,CreateFunc func = null)where T :struct,IComponentData
{
if(isCollider)
AddCollider(new Vector3(x, y, z));
Entity entities = manager.CreateEntity(blockArchetype);
manager.SetComponentData(entities, new Position { Value = new int3(x, y, z) });
manager.AddComponentData(entities, componentData);
//找不到是粉色方塊
if (!maTemp)
maTemp = pinkMat;
func?.Invoke(entities);
manager.AddSharedComponentData(entities, new MeshInstanceRenderer
{
mesh = mesh,
material = ma,
});
}
//在相同位置生成一個(gè)1x1x1的正方形碰撞
void AddCollider(Vector3 vec3)
{
if (createCollider)
{
colPool.AddCollider(vec3);
}
}
}
還有發(fā)現(xiàn)之前手動(dòng)改layer沒(méi)必要,都自動(dòng)加了银萍,但是要確認(rèn)下是不是第九層就Blocks層
對(duì)照源碼变勇,需要掛到子節(jié)點(diǎn)自己再加個(gè)AudioSource
當(dāng)然特效預(yù)制體要拖出來(lái)用這個(gè)
很明顯刪除組21個(gè)沒(méi)有刪除
然后仔細(xì)看了看因?yàn)槎疾皇钦麛?shù)
連很多碰撞坐標(biāo)都不是整數(shù)
找了半天是因?yàn)槟_本掛燈光上了,燈光有旋轉(zhuǎn)贴唇,自帶45度所以都坐標(biāo)亂了搀绣,以后要找個(gè)空物體掛上。然后刪除沒(méi)人問(wèn)題了戳气,創(chuàng)建距離近的話(huà)會(huì)跑到頭上
然后打斷點(diǎn)發(fā)現(xiàn)如果距離夠近链患,碰撞物體就是玩家自己
因?yàn)橹剌d,這個(gè)不是layer而是距離長(zhǎng)度瓶您,最遠(yuǎn)距離
所以改成
問(wèn)題解決
優(yōu)化處理及問(wèn)題
我們Collider比較多麻捻,性能消耗海星,因?yàn)闆](méi)有Rigibody不會(huì)一直去檢測(cè)
如果還覺(jué)得性能消耗比較大
把Blocks Blocks之間的影響取消掉呀袱,只剩下Default也就是我們第一人稱(chēng)控制器才有影響
Q&A
1.Entity并非GameObject贸毕,可以在編輯器內(nèi)調(diào)整嘛?
現(xiàn)在還不行夜赵,預(yù)期以后會(huì)整和與GameObject一樣
2.ECS除了移動(dòng)物件坐標(biāo)之外明棍,能處理讀數(shù)據(jù)庫(kù)這樣很花時(shí)間的工作嗎?
可以寇僧,ECS就是Data和Data的處理摊腋,把數(shù)據(jù)拉出來(lái)存到ComponentData去處理
3.我可以產(chǎn)生出來(lái)的Entity在新增修改移除Component嘛?
可以嘁傀,需要先把指令放在ComponentBuffer里兴蒸,確保(沒(méi)聽(tīng)清什么Type)密合的,會(huì)耗用一點(diǎn)時(shí)間细办,太頻繁問(wèn)題會(huì)比較大
4.ECS對(duì)于有經(jīng)驗(yàn)的Unity是否對(duì)沒(méi)經(jīng)驗(yàn)的學(xué)的難
一時(shí)之間會(huì)比較難接受橙凳,還是上手比較快,其實(shí)都差不多,都是新東西痕惋。
5.有性能瓶頸舊方案区宇,有什么快的方法轉(zhuǎn)換ECS
首先要升級(jí)到Unity2018,第二是你需要寫(xiě)物理系統(tǒng)值戳,還有特效系統(tǒng)议谷,萬(wàn)一你寫(xiě)好了,官方也寫(xiě)好了就尷尬了堕虹,現(xiàn)在ECS主要是做大量物件出現(xiàn)和消失卧晓。舊的整個(gè)改其實(shí)不是太合適,最好2019之后赴捞。
6.全新的游戲逼裆,是否策劃也要考慮ECS
策劃(比較高級(jí)的)需要考慮System和ComponentData之間的關(guān)系處理,主要是偏向程序員考慮赦政。
7.PureECS和HybirdECS可以混合用嗎
可以胜宇,這個(gè)游戲就是,PureECS性能高于HybirdECS恢着,個(gè)人感覺(jué)比較難用
8.System可以分不同場(chǎng)景運(yùn)作嘛
可以桐愉,但是System是沒(méi)有場(chǎng)景概念的,所有場(chǎng)景的東西都會(huì)處理掰派,只要有Entity就會(huì)運(yùn)行
但是可以用代碼在不同場(chǎng)景不需要System關(guān)閉掉从诲,現(xiàn)在好像沒(méi)有,之后會(huì)出Api
這個(gè)是System也分先后初始化靡羡,這個(gè)就是死亡系統(tǒng)在渲染系統(tǒng)之前進(jìn)行系洛,不加的話(huà)可能造成渲染之后才刪除,會(huì)短暫閃一下
這個(gè)是GitHub
https://github.com/1004019267/Minecraft-with-ECS