这个是unity官方演示ECS
https://connect.unity.com/p/zhi-bo-hui-gu-shi-yong-unity-ecskai-fa-wo-de-shi-jie
环境搭设
打开PlayerSetting确保.NET库在4.X以上
进入工程文件夹
把下面脚本加入进入
{
{
"dependencies": {
"com.unity.entities": "0.0.12-preview.16"
},
"registry": "https://packages.unity.com",
"testables": [
"com.unity.collections",
"com.unity.entities",
"com.unity.jobs"
]
}
然后就自动下载
我试了很多版本都报迷之错误,这个不会报错
这里可以查看安装,有的版本可以直接在这个界面安装,就不用那样Json输入然后又匹配不对了
然后环境算是安装好了
我们可以看到ECS方式创建预制体,一定有一个GameObjectEntity还有一堆Component
打开EntitiesDebug界面
创建对比
然后是三种创建方式对比下
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Rendering;
using Unity.Collections;
public class EntitiesTest : MonoBehaviour
{
//宣告内存字段放在一起 而不是杂乱无序的 提高速率
public static EntityArchetype blockArchetype;
public EntityManager manager;
public Mesh blockMesh;
public Material blockMaterial;
public GameObject go;
//宣告在加载场景之前运行 可以理解为预存
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void Initialize()
{
//宣告管理器
EntityManager manager = World.Active.GetOrCreateManager();
//宣告内存区块(区域)
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
//[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
void Start()
{
//Unity自己产生预制体 但是要添加GameObjectEntity Entities才能标记
//或者 Instantiate
GameObject.CreatePrimitive(PrimitiveType.Cube)
.AddComponent()
.transform.position = new Vector3(-2, 0, 0);
//PureECS 下面是不用Unity自带组件创建预制体 就是说脱离Unity也可以用的代码
manager = World.Active.GetOrCreateManager();
//在内存块中设置Pos和创建一个tag(并没有用)
Entity entities = manager.CreateEntity(blockArchetype);
manager.SetComponentData(entities, new Position { Value = new int3(2, 0, 0) });
//这个是自定义的我们没有..
//manager.AddComponentData(entities, new BlockTag());
//添加材质
manager.AddSharedComponentData(entities, new MeshInstanceRenderer
{
mesh = blockMesh,
material = blockMaterial,
});
//Hybrid ECS 引用unity自带GameObject创建预制体
if (go)
{
//产生一个新的阵列
using (NativeArray entityArray = new NativeArray(1, Allocator.Temp))
{
manager.Instantiate(go, entityArray);
manager.SetComponentData(entities, new Position { Value = new float3(4, 0f, 0f) });
}
}
}
}
这个是预制体创建要挂的脚本,框架自带
前两个是不依赖Unity所需要的网格和材质
最后一个就是以产生预制体方式产生
通过Entities创建的预制体不会在这里显示
在自带的EntitiesDebug窗口中可看见
选择一个,右边有相应的信息,可是只能代码动态调整,以后会改成可调整,也就是说和GameObject对比起来没有什么区别的样子
世界生成
然后是我的世界生成世界是随机的,这里要用到柏林噪声,有规则的乱序生成,保证了之后地图再生成的衔接问题,不会出现地图生成断开了
这个是生成的图
这个是3D效果
上去官方开源gitHub扒素材学习 建议源码看视屏写不出来 有bug再看 因为这个教程都是干货 2小时含金量特别大
https://github.com/UnityTechnologies/MinecraftECS
找到这四个文件,放入unity
生成地图
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//生成一个方块
public class PerlinNoiseGenerator
{
public static int BlockFaces = 0;
public static Texture2D noiseHeightMap;
//长宽的地图
int texWidth = 200, texHeight = 200;
//噪声缩放值 值越大越密集
float scale1 = 1f;
float scale2 = 10f;
float scale3 = 20f;
//随机采样偏移
float offectX;
float offectY;
public PerlinNoiseGenerator()
{
offectX = Random.Range(0, 99999);
offectY = Random.Range(0, 99999);
}
///
/// 根据长短创建200X200的每一个点
///
///
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;
}
///
/// 用unity自带的2维柏林噪声计算每个点的偏移值
///
///
///
///
Color CakculateColor(int x, int y)
{
//根据我们的偏移值 计算出类似于波形的图 就是黑白黑白间隔的图
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之间的小数 可能会略大于一
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;
///
/// 加载一堆方块
///
public class SpawnNumBlocks : MonoBehaviour
{
public Texture2D heightmap;
public static EntityArchetype blockArchetype;
//10X10的地方留着 之外的不显示或删除 类似于遮挡剔除
[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();
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
void Start()
{
manager = World.Active.GetOrCreateManager();
PerlinNoiseGenerator perlin = new PerlinNoiseGenerator();
heightmap = perlin.GenerateHeightMap();
ChunkGenerator(chunckBase);
}
void ChunkGenerator(int amount)
{
//一个chunckBase相当于1500个方块
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++)
{
//返回像素颜色 数很小乘上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
{
//超过的视为空气
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,
});
}
}
}
}
}
}
然后是找到数字挂在
运行
其实本来应该运行Batches数到达2w的但为什么这么低
因为勾了用GPU Instance创建预制体,在大量物体创建时会进行优化,如果是少量就和直接Instance没有区别
然后我们就可以根据层生成不同的方块
树就是从根部开始 柱子0-7 然后再周围加树叶
把之前代码进一步扩充
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()
{
//检查场景是否有 有得到没有创建
EntityManager manager = World.Active.GetOrCreateManager();
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
void Start()
{
manager = World.Active.GetOrCreateManager();
PerlinNoiseGenerator perlin = new PerlinNoiseGenerator();
heightmap = perlin.GenerateHeightMap();
//创建一个碰撞池
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++)
{
//返回像素颜色 数很小乘上100
highlevel = (int)(heightmap.GetPixel(x, z).r * 100) - y;
airChecker = false;
switch (highlevel)
{
//表层 根据一个单位 和 多个单位 分开方法创建
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)
{
//树
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)
{
//树顶
maTemp = leavesMat;
}
else
{
maTemp = woodMat;
}
CreatePrefab(x, i, z, blockMesh, maTemp);
//树叶 就是个正方形
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++)
{
//不能随机到躯干
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的高度 产生一个方形的云
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,
});
}
//在相同位置生成一个1x1x1的正方形碰撞
void AddCollider(Vector3 vec3)
{
if (createCollider)
{
colPool.AddCollider(vec3);
}
}
}
还有个碰撞脚本
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;
}
//在同样位置创建一个单位碰撞
public void AddCollider(Vector3 vec3)
{
GameObject obj = GameObject.Instantiate(boxCollider);
obj.transform.position = vec3;
obj.transform.parent = parent;
obj.layer = 9;
}
}
这么挂脚本
运行
把Chunckbase改为100卡了N久700w个物体
Collider就是(1,1,1)的方块的碰撞
我写到现在出过几次错误,就是mesh被拉伸,加加减减比较多
人物控制
从这里下载官方标准资源包,导入
把第一人称控制器拖出来就是第一个,因为他自带摄像机,把一开始世界的摄像机删掉
因为我们草也是Cube碰撞,所以没办法从草上传过去
第一人称自动把鼠标屏蔽了 ESC就可以移动出Game视图了
实现挖掘
然后先把压缩包的音效文件导入
然后创建一个新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;
//挖掘喷发的特效
public ParticleSystem digEffect;
EntityManager manager;
GameSetting gs;
// Start is called before the first frame update
void Start()
{
AS = transform.GetComponent();
gs = FindObjectOfType();
player = gameObject;
Cursor.lockState = CursorLockMode.Locked;
manager = World.Active.GetOrCreateManager();
}
// Update is called once per frame
void Update()
{
//滑动过界就改变为另一边
if (blockID > 7)
{
blockID = 1;
}
if (blockID < 1)
{
blockID = 7;
}
//滑动选择方块
if (Input.GetAxis("Mouse ScrollWheel") < 0)
{
blockID++;
}
if (Input.GetAxis("Mouse ScrollWheel") > 0)
{
blockID--;
}
//看是选择哪个砖块
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;
//向角色正前方发射射线
Physics.Raycast(transform.position, transform.forward, out hit, blockLayer);
if (hit.transform != null)
{
//根据不同方块播放音效
if (blockID == 1 || blockID == 3 || blockID == 5 || blockID == 7)
{
AS.PlayOneShot(stone_audio);
}
else if (blockID == 2 || blockID == 4)
{
AS.PlayOneShot(wood_audio);
}
//创建一个方块在射线前方法线的位置 加上碰撞
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;
//向角色正前方发射射线
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);
}
}
}
然后在第一人称控制器的子节点挂载脚本
然后还是不能删除,因为删除的只有碰撞
删除系统
到现在ECS只用到EC没说S
之前我们的面向对象是一个Class贯穿这么多处理
转化为System的话 是有个System处理专门每一个模块(用JobSystem)
所以做挖掘(删除)需要处理,这个就是之前我们打不出来的Tag,根据Tag区分
这个是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;
//都只是作为一个标签
//比如自带的Position 如果需要的属性没有可以自己在这边定义
[Serializable]
public struct BlockTag : IComponentData { }
public class BlockTagComponment : ComponentDataWrapper { };
[Serializable]
public struct DestoryTag : IComponentData { }
public class DestoryTagComponment : ComponentDataWrapper { };
[Serializable]
public struct SurfacePlantTag : IComponentData { }
public class SurfacePlantTagComponment : ComponentDataWrapper { };
删除系统 这个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
{
//类似于Select语句
struct BlockGroup
{
[ReadOnly] public readonly int Length;
[ReadOnly] public EntityArray entity;
//就是检索语句 符合的条件都在这里面
[ReadOnly] public ComponentDataArray postions;
[ReadOnly] public ComponentDataArray tags;
}
struct DestoryBlockGroup
{
[ReadOnly] public readonly int Length;
[ReadOnly] public EntityArray entity;
[ReadOnly] public ComponentDataArray postions;
[ReadOnly] public ComponentDataArray tags;
}
//某些方块上的花
struct SurfacePlantGroup
{
[ReadOnly] public readonly int Length;
[ReadOnly] public EntityArray entity;
[ReadOnly] public ComponentDataArray postions;
[ReadOnly] public ComponentDataArray tags;
}
//群组 Inject是注入数据
[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)
{
//同时寻找砖块上是否有草 有了也删除 就是草的位置y-1如果等于现在位置就删除
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]);
}
}
}
}
}
然后把刚才写的挖掘Destory的Tag解开
不要忘了解除生成方块的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()
{
//检查场景是否有 有得到没有创建
EntityManager manager = World.Active.GetOrCreateManager();
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
void Start()
{
manager = World.Active.GetOrCreateManager();
PerlinNoiseGenerator perlin = new PerlinNoiseGenerator();
heightmap = perlin.GenerateHeightMap();
//创建一个碰撞池
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++)
{
//返回像素颜色 数很小乘上100
highlevel = (int)(heightmap.GetPixel(x, z).r * 100) - y;
airChecker = false;
switch (highlevel)
{
//表层 根据一个单位 和 多个单位 分开方法创建
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)
{
//树
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)
{
//树顶
maTemp = leavesMat;
}
else
{
maTemp = woodMat;
}
CreatePrefab(x, i, z, blockMesh, maTemp, new BlockTag { });
//树叶 就是个正方形
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++)
{
//不能随机到躯干
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的高度 产生一个方形的云
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(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,
});
}
//在相同位置生成一个1x1x1的正方形碰撞
void AddCollider(Vector3 vec3)
{
if (createCollider)
{
colPool.AddCollider(vec3);
}
}
}
然后是左键可以销毁Cube 右键可以放置Cube,不过会出问题,就是销毁后他本体不会消失,添加还会重叠233,最后再找这些BUG,先上UI
UI
他做好的直接拿出来用
是这个样子
下面的脚本都丢失了,我们写一个
/**
*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().enabled = true;
}
else
{
select.GetComponent().enabled = false;
}
}
}
然后像这样每个都处理下
来找找BUG问题
运行后发现DestorySystem有点不正常
第一个是草走不过去,因为给草加了碰撞 修改如下
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()
{
//检查场景是否有 有得到没有创建
EntityManager manager = World.Active.GetOrCreateManager();
blockArchetype = manager.CreateArchetype(
typeof(Position)
);
}
void Start()
{
manager = World.Active.GetOrCreateManager();
PerlinNoiseGenerator perlin = new PerlinNoiseGenerator();
heightmap = perlin.GenerateHeightMap();
//创建一个碰撞池
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++)
{
//返回像素颜色 数很小乘上100
highlevel = (int)(heightmap.GetPixel(x, z).r * 100) - y;
airChecker = false;
switch (highlevel)
{
//表层 根据一个单位 和 多个单位 分开方法创建
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)
{
//树
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)
{
//树顶
maTemp = leavesMat;
}
else
{
maTemp = woodMat;
}
CreatePrefab(x, i, z, blockMesh, maTemp, new BlockTag { });
//树叶 就是个正方形
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++)
{
//不能随机到躯干
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的高度 产生一个方形的云
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(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,
});
}
//在相同位置生成一个1x1x1的正方形碰撞
void AddCollider(Vector3 vec3)
{
if (createCollider)
{
colPool.AddCollider(vec3);
}
}
}
还有发现之前手动改layer没必要,都自动加了,但是要确认下是不是第九层就Blocks层
对照源码,需要挂到子节点自己再加个AudioSource
当然特效预制体要拖出来用这个
很明显删除组21个没有删除
然后仔细看了看因为都不是整数
连很多碰撞坐标都不是整数
找了半天是因为脚本挂灯光上了,灯光有旋转,自带45度所以都坐标乱了,以后要找个空物体挂上。然后删除没人问题了,创建距离近的话会跑到头上
然后打断点发现如果距离够近,碰撞物体就是玩家自己
因为重载,这个不是layer而是距离长度,最远距离
所以改成
问题解决
优化处理及问题
我们Collider比较多,性能消耗海星,因为没有Rigibody不会一直去检测
如果还觉得性能消耗比较大
把Blocks Blocks之间的影响取消掉,只剩下Default也就是我们第一人称控制器才有影响
Q&A
1.Entity并非GameObject,可以在编辑器内调整嘛?
现在还不行,预期以后会整和与GameObject一样
2.ECS除了移动物件坐标之外,能处理读数据库这样很花时间的工作吗?
可以,ECS就是Data和Data的处理,把数据拉出来存到ComponentData去处理
3.我可以产生出来的Entity在新增修改移除Component嘛?
可以,需要先把指令放在ComponentBuffer里,确保(没听清什么Type)密合的,会耗用一点时间,太频繁问题会比较大
4.ECS对于有经验的Unity是否对没经验的学的难
一时之间会比较难接受,还是上手比较快,其实都差不多,都是新东西。
5.有性能瓶颈旧方案,有什么快的方法转换ECS
首先要升级到Unity2018,第二是你需要写物理系统,还有特效系统,万一你写好了,官方也写好了就尴尬了,现在ECS主要是做大量物件出现和消失。旧的整个改其实不是太合适,最好2019之后。
6.全新的游戏,是否策划也要考虑ECS
策划(比较高级的)需要考虑System和ComponentData之间的关系处理,主要是偏向程序员考虑。
7.PureECS和HybirdECS可以混合用吗
可以,这个游戏就是,PureECS性能高于HybirdECS,个人感觉比较难用
8.System可以分不同场景运作嘛
可以,但是System是没有场景概念的,所有场景的东西都会处理,只要有Entity就会运行
但是可以用代码在不同场景不需要System关闭掉,现在好像没有,之后会出Api
这个是System也分先后初始化,这个就是死亡系统在渲染系统之前进行,不加的话可能造成渲染之后才删除,会短暂闪一下
这个是GitHub
https://github.com/1004019267/Minecraft-with-ECS