- 准备工作
- 鼠标触碰六边形单元
- 鼠标点击位置
- 更新计划
- 作者的话
- ECS系列目录
- ECS官方示例1:ForEach
- ECS官方案例2:IJobForEach
- ECS官方案例3:IJobChunk
- ECS官方案例4:SubScene
- ECS官方案例5:SpawnFromMonoBehaviour
- ECS官方案例6:SpawnFromEntity
- ECS官方案例7:SpawnAndRemove
- ECS进阶:FixedTimestepWorkaround
- ECS进阶:Boids
- ECS进阶:场景切换器
- ECS进阶:MegaCity0
- ECS进阶:MegaCity1
- UnityMMO资源整合&服务器部署
- UnityMMO选人流程
- UnityMMO主世界
- UnityMMO网络同步
- 用ECS做HexMap:自动生成地图系统
- 用ECS做HexMap:利用RenderMesh绘制六边形
- 用ECS做HexMap:利用RenderMesh为六边形涂色
- 用ECS做HexMap:六边形单元的颜色混合
- 用ECS做HexMap:重构地图系统
- 用ECS做HexMap:鼠标点击六边形单元涂色
一不留神已经写了二十余篇笔记了,真是一入ECS深似海啊,掉进坑里面了,没办法啊,自己挖坑埋自己,于是只能自己填坑累了。MMO的项目也还没有填完,这个地图坑,恐怕一时半会儿也搞不定,慢慢来吧。 Anyway,如果你对ECS版的海克斯无限地图感兴趣,不妨参与进来,欢迎Star/Folk源码,如果有大佬可以贡献点代码,那就太好了,具体操作如下: 0下载Unity编辑器(2019.1.12f1 or 更新的版本),if(已经下载了)continue; 1克隆源码:git clone https://github.com/cloudhu/HexMapMadeInUnity2019ECS.git --recurse
or 点击ECSHexMap下载Zip压缩包 if(已经下载了)continue; 2如果下载的是压缩包,需要先将压缩包解压。然后将HexMapMadeInUnity2019ECS添加到Unity Hub项目中; 3用Unity Hub打开的开源项目:HexMapMadeInUnity2019ECS,等待Unity进行编译工作; 4打开项目后,启动场景在Scenes目录下,打开AdvancedHexMap场景。
AdvancedHexMap场景是优化重构的场景,点击运行后,如下图所示:
我们得到了计算出来的六边形地图,下面我们需要使用鼠标来点击六边形单元,使其改变颜色。
我们在Common文件夹中新建一个C#脚本,命名为:HexMapEditor,在这个脚本中实现鼠标点击地图,把屏幕坐标转化成射线,再把射线触碰到的点转化成世界坐标:
using UnityEngine;
using UnityEngine.EventSystems;
public class HexMapEditor : MonoBehaviour {
public Color[] colors;
private Color activeColor;
void Awake()
{
SelectColor(0);
}
void Update()
{
if (
Input.GetMouseButton(0) &&
!EventSystem.current.IsPointerOverGameObject()
)
{
HandleInput();
}
}
void HandleInput()
{
Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(inputRay, out hit))
{
MainWorld.Instance.ColorCell(hit.point, activeColor);
}
}
public void SelectColor(int index)
{
activeColor = colors[index];
}
}
这段代码算是Unity入门的基本操作了,不需要多余的注释,但是要解释一下,本来应该一开始就实现的简单功能,一直拖到现在才做。那是因为ECS没法使用MeshCollider,所以不能响应射线,进而无法确定鼠标点击的单元所在。 现在把六边形放到OOP中来渲染,就可以获取到鼠标点击的六边形单元的所在位置了,应该是通过鼠标点击的位置来判断点击了哪一个六边形单元。我们需要一个坐标系来进行转换,新建一个C#脚本,命名为HexCoordinates:
using UnityEngine;
[System.Serializable]
public struct HexCoordinates {
[SerializeField]
private int x, z;
public int X {
get {
return x;
}
}
public int Z {
get {
return z;
}
}
public int Y {
get {
return -X - Z;
}
}
public HexCoordinates(int x, int z)
{
this.x = x;
this.z = z;
}
public static HexCoordinates FromOffsetCoordinates(int x, int z)
{
return new HexCoordinates(x - z / 2, z);
}
public static HexCoordinates FromPosition(Vector3 position)
{
float x = position.x / (HexMetrics.InnerRadius * 2f);
float y = -x;
float offset = position.z / (HexMetrics.OuterRadius * 3f);
x -= offset;
y -= offset;
int iX = Mathf.RoundToInt(x);
int iY = Mathf.RoundToInt(y);
int iZ = Mathf.RoundToInt(-x - y);
if (iX + iY + iZ != 0)
{
float dX = Mathf.Abs(x - iX);
float dY = Mathf.Abs(y - iY);
float dZ = Mathf.Abs(-x - y - iZ);
if (dX > dY && dX > dZ)
{
iX = -iY - iZ;
}
else if (dZ > dY)
{
iZ = -iX - iY;
}
}
return new HexCoordinates(iX, iZ);
}
public override string ToString()
{
return "(" +
X.ToString() + ", " + Y.ToString() + ", " + Z.ToString() + ")";
}
public string ToStringOnSeparateLines()
{
return X.ToString() + "\n" + Y.ToString() + "\n" + Z.ToString();
}
}
没有什么特别的,就是一些坐标转换的计算,下面需要新建一个系统来更新单元的颜色UpdateCellSystem:
///
/// 更新六边形单元系统
///
//[DisableAutoCreation]
[UpdateInGroup(typeof(SimulationSystemGroup))]
public class UpdateCellSystem : JobComponentSystem {
BeginSimulationEntityCommandBufferSystem m_EntityCommandBufferSystem;
protected override void OnCreate()
{
m_EntityCommandBufferSystem = World.GetOrCreateSystem();
}
///
/// 循环创建六边形单元,使其生成对应长宽的阵列
///
struct CalculateJob : IJobForEachWithEntity {
public EntityCommandBuffer.Concurrent CommandBuffer;
[BurstCompile]
public void Execute(Entity entity, int index, ref Cell cellData, [ReadOnly]ref UpdateData updata)
{
//0.获取单元索引,Execute的index顺序混乱
int cellIndex = cellData.Index;
int updateIndex = updata.CellIndex;
int width = updata.Width;
//1.判断并更新自身单元颜色以及相邻单元颜色
if (cellIndex updateIndex - width-2)
{
Color color = updata.NewColor;
//更新相邻单元的颜色
if (cellData.NEIndex == updateIndex) cellData.NE = color;
if (cellData.EIndex == updateIndex) cellData.E = color;
if (cellData.SEIndex == updateIndex) cellData.SE = color;
if (cellData.SWIndex == updateIndex) cellData.SW = color;
if (cellData.WIndex == updateIndex) cellData.W = color;
if (cellData.NWIndex == updateIndex) cellData.NW = color;
if (cellIndex == updateIndex)//更新自身单元的颜色
{
cellData.Color = color;
}
}
//2.remove UpdateTag after Update,therefor NewDataTag need to be added
CommandBuffer.RemoveComponent(index, entity);
CommandBuffer.AddComponent(index, entity);
}
}
///
/// 如果有新地图,则启动任务
///
/// 依赖
/// 任务句柄
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
var job = new CalculateJob
{
CommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer().ToConcurrent(),
}.Schedule(this, inputDeps);
m_EntityCommandBufferSystem.AddJobHandleForProducer(job);
return job;
}
}
这个系统的功能就是更新六边形单元的颜色数据,为此我新增了一个组件来保存更新的数据:
///
/// 单元的更新数据
///
public struct UpdateData : IComponentData
{
///
/// 单元的索引
///
public int CellIndex;
///
/// 新的颜色
///
public Color NewColor;
///
/// 宽度
///
public int Width;
}
宽度实际上是为了缩小更新的范围,毕竟和单元相邻的只有六个,那么我们就可以根据宽度来缩小范围了,超出范围不更新。
接下来只需把数据传递给实体即可,当我们点击单元的时候,调用了MainWorld的公共方法:
///
/// 染色指定位置的六边形单元
///
/// 位置
/// 颜色
public void ColorCell(Vector3 position, Color color)
{
position = transform.InverseTransformPoint(position);
HexCoordinates coordinates = HexCoordinates.FromPosition(position);
int index = coordinates.X + coordinates.Z * MapWidth + coordinates.Z / 2;
if (index==m_PrevClickCell && color==m_PrevSelect)
{//避免玩家重复操作
return;
}
m_PrevClickCell = index;
m_PrevSelect = color;
StartCoroutine(UpdateCellColor(index,color));
}
///
/// 更新单元的颜色
///
/// 单元索引
/// 颜色
///
IEnumerator UpdateCellColor(int cellIndex,Color color)
{
yield return null;
NativeArray entities = m_EntityManager.GetAllEntities();
if (entities.Length
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?