备份CatanBuilding瘦身独立工程

This commit is contained in:
JSD\13999
2026-05-26 16:15:54 +08:00
commit 2d0e6a61b7
12001 changed files with 2431925 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace HexGrid
{
public interface IBuildMediator
{
void Build();
void CheckForBuilding(List<HexSubZone> zoneDataSubZones);
}
public class HexBuildingData
{
public string buildingPrefabName;
public Vector3 position;
}
public class BuildingData
{
//
public string BuildingName;
// 地块状态
public Dictionary<string, int> TileStateMap;
public Vector3 position;
}
public class BuildingManager : MonoBehaviour,IHexGridMediatable
{
//
private IHexGridMediator _mediator;
// public List<string> conditions = new List<string>();
// private List<HexBuilding> _buildings;
//[zoneId,[[build1,[tile1,tile2,tile3,....],],[build1,[tile1,tile2,tile3,....],]]
private readonly Dictionary<int ,List<BuildingData>> _unFinishBuildMap= new ();
//<zoneId, [build1,build2,...]>
private readonly Dictionary<int ,List<BuildingData>> _finishBuildMaps = new ();
private void Awake()
{
// conditions.Clear();
}
public async void Build(Transform root,BuildingData buildingData)
{
// var build = Instantiate(buildingData.buildModel,root);
//var build = await Addressables.InstantiateAsync("BuildModel", root, true).Task;
//build.transform.localPosition = buildingData.position;
//build.name = "highbuilding";
Log($"Build-> {root} {buildingData}");
var buildTrans = root.Find(buildingData.BuildingName);
if (buildTrans)
{
buildTrans.gameObject.SetActive(true);
}
// await RunBuildAnimation(buildingData);
}
public async Task RunBuildAnimation(BuildingData buildingData)
{
}
public void LoadBuildings(List<HexBuilding> buildings)
{
// _buildings = buildings;
_finishBuildMaps.Clear();
_unFinishBuildMap.Clear();
foreach (var hexBuilding in buildings)
{
var tileStateMap = hexBuilding.hasTiles.ToDictionary(tile => tile, tile => 0);
_unFinishBuildMap.TryGetValue(hexBuilding.zoneId, out var buildingDatas);
if (buildingDatas == null)
{
buildingDatas = new List<BuildingData>();
_unFinishBuildMap.Add(hexBuilding.zoneId, buildingDatas);
}
buildingDatas.Add(new BuildingData()
{
BuildingName = hexBuilding.buildingName,
TileStateMap = tileStateMap,
});
}
Log($"LoadBuildings -> {_unFinishBuildMap}");
}
public void CheckForBuilding(List<HexSubZone> zoneDataSubZones)
{
}
public void CheckBuildingAfterBuildImme(HexTile tiler)
{
if (_unFinishBuildMap.TryGetValue(tiler.zoneId, out var buildingDatas))
{
foreach (var buildingData in buildingDatas.Where(buildingData => buildingData.TileStateMap.ContainsKey(tiler.tileId)))
{
buildingData.TileStateMap[tiler.tileId] = 1;
var full = buildingData.TileStateMap.Values.All(value => value == 1);
if (full)
{
buildingDatas.Remove(buildingData);
AddFinishBuilding(tiler.zoneId,buildingData);
_mediator.Build(tiler.zoneId, buildingData,true);
}
break;
}
}
}
public async Task CheckBuildingAfterBuild(HexTile tiler)
{
Log($"CheckBuildingAfterBuild ->{tiler}");
//tiler.zoneId;
if (_unFinishBuildMap.TryGetValue(tiler.zoneId, out var buildingDatas))
{
foreach (var buildingData in buildingDatas.Where(buildingData => buildingData.TileStateMap.ContainsKey(tiler.tileId)))
{
buildingData.TileStateMap[tiler.tileId] = 1;
var full = buildingData.TileStateMap.Values.All(value => value == 1);
if (full)
{
buildingDatas.Remove(buildingData);
AddFinishBuilding(tiler.zoneId,buildingData);
await Awaiters.Seconds(0.5f);
_mediator.Build(tiler.zoneId, buildingData,false);
await Awaiters.Seconds(0.5f);
}
break;
}
}
// _unFinishBuildMap.
// conditions.Add(tiler.tileId);
// if (conditions.Contains("cube012") && conditions.Contains("cube011"))
// {
// _mediator.Build(tiler.zoneId);
// conditions.Clear();
// }
}
private void AddFinishBuilding(int zoneId, BuildingData buildingData)
{
_finishBuildMaps.TryGetValue(zoneId, out var buildingDatas);
if (buildingDatas == null)
{
buildingDatas = new List<BuildingData>();
_finishBuildMaps.Add(zoneId, buildingDatas);
}
buildingDatas.Add(buildingData);
}
private static void Log(object t)
{
Debug.Log($"<color=orange>BuildingManager -> {t} </color>");
}
public void SetMediator(IHexGridMediator mediator)
{
_mediator = mediator;
}
// public HexBuildingData GetCurBuildModelData()
// {
// return BuildingData;
// }
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ad81263d39f54f98b6d374b986975206
timeCreated: 1757386930

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d8cfab14f64f4801a84d7e41065515da
timeCreated: 1757574639

View File

@@ -0,0 +1,897 @@
using com.fpnn.livedata;
using UnityEngine;
namespace HexGrid
{
public class CompleteCameraMove : MonoBehaviour
{
[Header("移动设置")]
[SerializeField] private float moveSpeed = 10f;
[SerializeField] private float dragSpeed = 4f;
[Header("缩放设置")]
[SerializeField] private float zoomSpeed = 20f;
[SerializeField] private float minZoom = 5f;
[SerializeField] private float maxZoom = 20f;
[SerializeField] private bool smoothZoom = true;
[SerializeField] private float zoomSmoothSpeed = 10f;
[SerializeField] private float touchZoomFactor = 0.025f;
[Header("旋转设置")]
public float rotationSpeed = 2f;
public float touchRotationSpeed = 1f;
public float minRotationY = -60f;
public float maxRotationY = 60f;
[Header("调试")] [SerializeField] private bool showDebugInfo = false;
// 拖拽相关变量
private bool isDragging = false;
private Vector3 lastMousePosition;
private Vector3 lastTouchPosition;
// 移动的值,
private Vector3 _moveValue;
// 旋转最终的值,注意是直接改变位置
private Vector3 _rotateCameraPos;
// 缩放偏移值
private Vector3 _rotateDelta;
// 缩放的偏移值
private Vector3 _zoomDelta;
// 缩放相关变量
private float currentZoomDistance;
private float targetZoomDistance;
private Camera cam;
private Vector3 fixedLookDirection; // 固定的观察方向
// 旋转的值
private bool isRotating = false;
private Vector3 targetPosition;
private float distanceCameraToTarget;
private float currentYaw = 0f;
private float currentPitch = 0f;
private bool initialized = false;
// 触摸旋转相关
// private Vector2 lastTouchPosition;
// private bool isTouchRotating = false;
private enum InputState
{
None,
KeyboardMove,
MouseDrag,
MouseRotate,
MouseZoom,
TouchMove,
TouchZoomRotate
}
private InputState currentState = InputState.None;
// 输入状态
// private Vector3 lastMousePosition;
private Vector2 lastTouchCenter;
private float lastTouchDistance;
private bool wasTwoFingerActive = false;
void Start()
{
// 初始化相机和缩放
cam = GetComponent<Camera>();
if (cam == null)
{
cam = Camera.main;
}
if (cam != null)
{
// 记录初始的观察方向(从相机指向目标的方向)
fixedLookDirection = transform.forward;
InitializeRotation();
targetZoomDistance = distanceCameraToTarget;
currentZoomDistance = targetZoomDistance;
Log($"相机控制器初始化完成 - 初始缩放距离: {targetZoomDistance}");
Log($"固定观察方向: {fixedLookDirection}");
}
}
void InitializeRotation()
{
UpdateRotation();
initialized = true;
}
void Update()
{
// HandleKeyboardInput();
// HandleMouseInput();
// HandleTouchInput();
// HandleZoomInput();
// UpdateZoom();
// UpdateRotation();
//
// if (!isDragging)
// {
// // 触摸输入优先
// HandleMouseRotation();
// }
UpdateInputState();
HandleCurrentInput();
UpdateCameraTransform();
if (showDebugInfo && Input.GetKeyDown(KeyCode.F1))
{
Log($"相机位置: {transform.position}, 当前缩放: {currentZoomDistance}");
Log($"观察方向: {transform.forward}");
}
}
void UpdateInputState()
{
InputState newState = InputState.None;
// 触摸输入优先级最高
if (Input.touchCount > 0)
{
newState = Input.touchCount == 1 ? InputState.TouchMove : InputState.TouchZoomRotate;
}
// 鼠标输入次之
else if (Input.GetMouseButton(1))
{
newState = InputState.MouseRotate;
}
else if (Input.GetMouseButton(0))
{
newState = InputState.MouseDrag;
}
else if (Mathf.Abs(Input.GetAxis("Mouse ScrollWheel")) > 0.01f)
{
newState = InputState.MouseZoom;
}
// 键盘输入最后
else if (IsAnyMovementKeyPressed())
{
newState = InputState.KeyboardMove;
}
if (newState != currentState)
{
OnInputStateChanged(currentState, newState);
currentState = newState;
}
}
void HandleCurrentInput()
{
switch (currentState)
{
case InputState.KeyboardMove:
HandleKeyboardInput();
break;
case InputState.MouseDrag:
HandleMouseDrag();
break;
case InputState.MouseRotate:
HandleMouseRotation();
break;
case InputState.MouseZoom:
HandleMouseZoom();
break;
case InputState.TouchMove:
HandleSingleTouchMove();
break;
case InputState.TouchZoomRotate:
HandleTwoFingerGestures();
break;
}
}
private void UpdateCameraTransform()
{
// 移动的值
if (_moveValue != Vector3.zero)
{
transform.position += _moveValue;
_rotateCameraPos = transform.position;
_moveValue = Vector3.zero;
// CalcTargetPosition(); // 更新
}
// else
// {
if (_rotateDelta != Vector3.zero)
{
transform.position += _rotateDelta;
_rotateDelta = Vector3.zero;
transform.LookAt(targetPosition);
}
//
if (_zoomDelta != Vector3.zero)
{
transform.position += _zoomDelta;
_zoomDelta = Vector3.zero;
transform.LookAt(targetPosition);
}
// }
UpdateRotation();
}
void OnInputStateChanged(InputState oldState, InputState newState)
{
if (showDebugInfo)
{
Log($"🎮 输入状态变化: {oldState} → {newState}");
}
// 开始旋转时锁定旋转中心
if (newState == InputState.MouseRotate || newState == InputState.TouchZoomRotate)
{
// 锁定当前的旋转中心
// currentRotationCenter = CalculateViewportCenter();
// currentDistanceToCenter = Vector3.Distance(transform.position, currentRotationCenter);
// targetDistanceToCenter = currentDistanceToCenter;
// if (showDebugInfo)
// {
// Log($"🔒 锁定旋转中心: {currentRotationCenter}, 距离: {currentDistanceToCenter:F2}");
// }
// UpdateRotation();
}
// 状态特定的初始化
switch (newState)
{
case InputState.MouseRotate:
lastMousePosition = Input.mousePosition;
Cursor.lockState = CursorLockMode.Locked;
break;
case InputState.TouchZoomRotate:
if (Input.touchCount >= 2)
{
InitializeTwoFingerGesture();
}
break;
}
// 退出状态的清理
if (oldState == InputState.MouseRotate)
{
Cursor.lockState = CursorLockMode.None;
}
}
void InitializeTwoFingerGesture()
{
if (Input.touchCount >= 2)
{
Touch touch1 = Input.GetTouch(0);
Touch touch2 = Input.GetTouch(1);
lastTouchCenter = (touch1.position + touch2.position) * 0.5f;
lastTouchDistance = Vector2.Distance(touch1.position, touch2.position);
wasTwoFingerActive = true;
}
}
bool IsAnyMovementKeyPressed()
{
return Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) ||
Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D);
}
// 键盘移动(保持原来的逻辑)
void HandleKeyboardInput()
{
Vector3 move = Vector3.zero;
if (Input.GetKey(KeyCode.W)) move.z += 1;
if (Input.GetKey(KeyCode.S)) move.z -= 1;
if (Input.GetKey(KeyCode.A)) move.x -= 1;
if (Input.GetKey(KeyCode.D)) move.x += 1;
if (move != Vector3.zero)
{
// 基于当前相机方向进行移动
Vector3 forward = transform.forward;
Vector3 right = transform.right;
// 计算移动方向(保持在水平面)
Vector3 moveDirection = (forward * move.z + right * move.x).normalized;
moveDirection.y = 0; // 确保只在水平面移动
//transform.position += moveDirection * moveSpeed * Time.deltaTime;
_moveValue = moveDirection * moveSpeed * Time.deltaTime;
}
}
void HandleMouseDrag()
{
if (Input.GetMouseButtonDown(0))
{
lastMousePosition = Input.mousePosition;
}
else if (Input.GetMouseButton(0))
{
Vector3 currentMousePos = Input.mousePosition;
Vector3 mouseDelta = lastMousePosition - currentMousePos;
Vector3 worldDelta = ScreenToWorldDelta(mouseDelta);
// transform.position += worldDelta * dragSpeed * Time.deltaTime;
_moveValue = worldDelta * dragSpeed * Time.deltaTime;
// 移动后重新计算距离
// currentDistanceToCenter = Vector3.Distance(transform.position, currentRotationCenter);
// targetDistanceToCenter = currentDistanceToCenter;
lastMousePosition = currentMousePos;
}
}
// 鼠标拖拽移动
// void HandleMouseInput()
// {
// // 忽略触摸输入时的鼠标输入
// if (Input.touchCount > 0) return;
// if (Input.GetMouseButtonDown(0))
// {
// isDragging = true;
// lastMousePosition = Input.mousePosition;
//
// if (showDebugInfo)
// {
// Log("开始鼠标拖拽");
// }
// }
// else if (Input.GetMouseButtonUp(0))
// {
// isDragging = false;
//
// if (showDebugInfo)
// {
// Log("结束鼠标拖拽");
// }
// }
// if (isDragging)
// {
// Vector3 currentMousePosition = Input.mousePosition;
// Vector3 deltaScreenPosition = lastMousePosition - currentMousePosition;
//
// // 将屏幕空间的移动转换为世界空间
// Vector3 worldDelta = ScreenToWorldDelta(deltaScreenPosition);
//
// // 移动相机(保持观察角度)
// transform.position += worldDelta * dragSpeed * Time.deltaTime;
//
// // 更新记录位置
// lastMousePosition = currentMousePosition;
//
// if (showDebugInfo)
// {
// Log($"鼠标拖拽移动: {worldDelta}");
// }
// }
// }
// 触摸拖拽移动
// void HandleTouchInput()
// {
// if (Input.touchCount == 1)
// {
// var touch = Input.GetTouch(0);
// if (touch.phase == TouchPhase.Began)
// {
// isDragging = true;
// lastTouchPosition = touch.position;
//
// if (showDebugInfo)
// {
// Log("开始触摸拖拽");
// }
// }
// else if (touch.phase == TouchPhase.Ended)
// {
// isDragging = false;
//
// if (showDebugInfo)
// {
// Log("结束触摸拖拽");
// }
// }
// else if (touch.phase == TouchPhase.Moved && isDragging)
// {
// Vector3 currentTouchPosition = touch.position;
// Vector3 deltaScreenPosition = lastTouchPosition - currentTouchPosition;
//
// // Vector2 deltaPosition = touch.deltaPosition;
// // 将屏幕空间的移动转换为世界空间
// Vector3 worldDelta = ScreenToWorldDelta(deltaScreenPosition);
// // 移动相机(保持观察角度)
// transform.position += worldDelta * dragSpeed * Time.deltaTime;
//
// // 更新记录位置
// lastTouchPosition = currentTouchPosition;
//
// if (showDebugInfo)
// {
// Log($"触摸拖拽移动: {worldDelta}");
// }
// }
// }
// else if (Input.touchCount == 2)
// {
// // 双指捏合缩放
// HandlePinchZoom();
// HandleTouchRotation();
// isDragging = false; // 停止拖拽
// }
// else if (Input.touchCount > 2)
// {
// // 多点触控时停止所有操作
// isDragging = false;
// }
// }
// 处理缩放输入(鼠标滚轮 + 双指捏合)
// void HandleZoomInput()
// {
// // 鼠标滚轮缩放
// float scrollInput = Input.GetAxis("Mouse ScrollWheel");
// if (scrollInput != 0)
// {
// var zoomChange = -scrollInput * zoomSpeed; // 负号使滚轮向上为放大
// targetZoomDistance = Mathf.Clamp(targetZoomDistance + zoomChange, minZoom, maxZoom);
//
// if (showDebugInfo)
// {
// Log($"滚轮缩放: {scrollInput} -> 目标缩放距离: {targetZoomDistance}");
// }
// }
//
// // 键盘缩放(备选)
// if (Input.GetKey(KeyCode.Q))
// {
// targetZoomDistance = Mathf.Clamp(targetZoomDistance - zoomSpeed * Time.deltaTime, minZoom, maxZoom);
// }
//
// if (Input.GetKey(KeyCode.E))
// {
// targetZoomDistance = Mathf.Clamp(targetZoomDistance + zoomSpeed * Time.deltaTime, minZoom, maxZoom);
// }
// }
void HandleTwoFingerRotation(Vector2 currentCenter)
{
Vector2 centerDelta = currentCenter - lastTouchCenter;
if (centerDelta.magnitude > 3f)
{
float deltaX = centerDelta.x * 0.2f;
float deltaY = centerDelta.y * 0.2f;
ApplyRotationDelta(deltaX, deltaY,touchRotationSpeed);
}
}
void HandlePinchZoom(float currentTouchDistance)
{
float distanceDelta = currentTouchDistance - lastTouchDistance;
if (Mathf.Abs(distanceDelta) > 5f)
{
float zoomDelta = -distanceDelta * zoomSpeed * touchZoomFactor;
targetZoomDistance = Mathf.Clamp(targetZoomDistance + zoomDelta, minZoom, maxZoom);
}
ApplyZoom();
}
// 双指捏合缩放
// void HandlePinchZoom()
// {
// Log("HandePinchZoom");
//
// Touch touch1 = Input.GetTouch(0);
// Touch touch2 = Input.GetTouch(1);
//
// // 计算当前两指距离
// float currentDistance = Vector2.Distance(touch1.position, touch2.position);
//
// // 计算上一帧两指距离
// Vector2 touch1PrevPos = touch1.position - touch1.deltaPosition;
// Vector2 touch2PrevPos = touch2.position - touch2.deltaPosition;
// float prevDistance = Vector2.Distance(touch1PrevPos, touch2PrevPos);
//
// // 计算缩放变化
// if (prevDistance > 0)
// {
// float deltaDistance = currentDistance - prevDistance;
// float zoomChange = deltaDistance * zoomSpeed * 0.01f; // 调整灵敏度
// targetZoomDistance = Mathf.Clamp(targetZoomDistance - zoomChange, minZoom, maxZoom); // 注意符号
// if (showDebugInfo)
// {
// Log($"捏合缩放: 距离变化={deltaDistance} -> 目标缩放距离: {targetZoomDistance}");
// }
// }
// }
//
private void Log(string t)
{
Debug.Log($"<color=yellow> CompleteCameraMove: {t} </color>");
}
// 更新旋转
void UpdateRotation()
{
CalcTargetPosition();
// 计算初始参数
Vector3 direction = transform.position - targetPosition;
distanceCameraToTarget = direction.magnitude;
currentYaw = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
currentPitch = Mathf.Asin(direction.y / distanceCameraToTarget) * Mathf.Rad2Deg;
_rotateCameraPos = transform.position;
}
private void CalcTargetPosition()
{
Ray ray = new Ray(transform.position, transform.forward);
float groundHeight = 0f;
Plane groundPlane = new Plane(Vector3.up, new Vector3(0, groundHeight, 0));
if (groundPlane.Raycast(ray, out float enter) )
{
Vector3 hitPoint = ray.GetPoint(enter);
// Log($"数学检测到地面点: {hitPoint} {enter}");
targetPosition = hitPoint;
}
}
// 更新缩放效果(关键修改:保持视角方向不变)
void ApplyZoom()
{
if (cam == null) return;
if (Mathf.Abs(currentZoomDistance - targetZoomDistance) > 0.01f)
{
if (smoothZoom)
{
// 平滑缩放
currentZoomDistance = Mathf.Lerp(currentZoomDistance, targetZoomDistance,
zoomSmoothSpeed * Time.deltaTime);
}
else
{
// 直接缩放
currentZoomDistance = targetZoomDistance;
}
ApplyPerspectiveZoom(currentZoomDistance);
}
}
// 透视相机缩放的具体实现
void ApplyPerspectiveZoom(float zoomDistance)
{
var targetPoint = targetPosition;
// 计算从目标点到相机的方向(当前观察方向的反向)
Vector3 directionToCamera = -transform.forward;
// 根据缩放距离重新定位相机
Vector3 newPosition = targetPoint + directionToCamera * zoomDistance;
// 只更新位置,保持旋转不变
// transform.position = newPosition;
_rotateCameraPos = newPosition;
// _zoomDelta = directionToCamera * zoomDistance;
_zoomDelta = newPosition - transform.position;
// if (showDebugInfo)
// {
// Log($"透视缩放 - 目标点: {targetPoint}, 新位置: {newPosition}, 距离: {zoomDistance}");
// }
}
Vector3 ScreenTargetPoint()
{
Vector3 screenCenter = new Vector3(Screen.width * 0.5f, Screen.height * 0.5f, 0);
Ray ray = cam.ScreenPointToRay(screenCenter);
// 假设地面在Y=0计算射线与地面的交点
Vector3 targetPoint = Vector3.zero;
if (ray.direction.y != 0)
{
float t = -ray.origin.y / ray.direction.y;
if (t > 0)
{
targetPoint = ray.origin + ray.direction * t;
}
}
return targetPoint;
}
// 将屏幕空间的移动差值转换为世界空间
Vector3 ScreenToWorldDelta(Vector3 screenDelta)
{
// 根据当前缩放级别调整移动灵敏度
float scaleFactor = currentZoomDistance * 0.01f;
// 基于相机的当前方向向量进行转换
Vector3 right = transform.right;
Vector3 forward = transform.forward;
// 计算世界空间移动(保持在水平面)
Vector3 worldDelta = (right * screenDelta.x + forward * screenDelta.y) * scaleFactor;
worldDelta.y = 0; // 确保只在水平面移动
return worldDelta;
}
// 公共接口
public void SetZoom(float zoom)
{
targetZoomDistance = Mathf.Clamp(zoom, minZoom, maxZoom);
}
public float GetZoom()
{
return currentZoomDistance;
}
public void ZoomIn(float amount = 1f)
{
targetZoomDistance = Mathf.Clamp(targetZoomDistance - amount, minZoom, maxZoom);
}
public void ZoomOut(float amount = 1f)
{
targetZoomDistance = Mathf.Clamp(targetZoomDistance + amount, minZoom, maxZoom);
}
// 重置观察方向(如果需要的话)
public void SetLookDirection(Vector3 direction)
{
fixedLookDirection = direction.normalized;
transform.forward = fixedLookDirection;
}
void HandleMouseRotation()
{
float deltaX = Input.GetAxis("Mouse X");
float deltaY = Input.GetAxis("Mouse Y");
ApplyRotationDelta(deltaX,deltaY,rotationSpeed);
}
void HandleMouseZoom()
{
float scrollInput = Input.GetAxis("Mouse ScrollWheel");
if (scrollInput != 0)
{
var zoomChange = -scrollInput * zoomSpeed; // 负号使滚轮向上为放大
targetZoomDistance = Mathf.Clamp(targetZoomDistance + zoomChange, minZoom, maxZoom);
if (showDebugInfo)
{
Log($"滚轮缩放: {scrollInput} -> 目标缩放距离: {targetZoomDistance}");
}
}
ApplyZoom();
}
void HandleSingleTouchMove()
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
lastTouchCenter = touch.position;
wasTwoFingerActive = false;
}
else if (touch.phase == TouchPhase.Moved)
{
// Vector2 touchDelta = lastTouchCenter - touch.position;
// Vector3 worldDelta = CalculateWorldDragDirection(touchDelta);
// transform.position += worldDelta * dragSpeed * Time.deltaTime;
// Vector3 currentMousePos = lastTouchCenter;
// Vector3 mouseDelta = lastMousePosition - currentMousePos;
if (!wasTwoFingerActive)
{
Vector2 touchDelta = lastTouchCenter - touch.position;
Vector3 worldDelta = ScreenToWorldDelta(touchDelta);
// transform.position += worldDelta * dragSpeed * Time.deltaTime;
_moveValue = worldDelta * dragSpeed * Time.deltaTime;
}
// 移动后重新计算距离
// currentDistanceToCenter = Vector3.Distance(transform.position, currentRotationCenter);
// targetDistanceToCenter = currentDistanceToCenter;
// lastMousePosition = currentMousePos;
// 移动后重新计算距离
// currentDistanceToCenter = Vector3.Distance(transform.position, currentRotationCenter);
// targetDistanceToCenter = currentDistanceToCenter;
lastTouchCenter = touch.position;
}
}
void HandleTwoFingerGestures()
{
if (Input.touchCount < 2) return;
Touch touch1 = Input.GetTouch(0);
Touch touch2 = Input.GetTouch(1);
Vector2 currentCenter = (touch1.position + touch2.position) * 0.5f;
float currentDistance = Vector2.Distance(touch1.position, touch2.position);
if (wasTwoFingerActive)
{
// HandleTwoFingerRotation(currentCenter);
HandlePinchZoom(currentDistance);
}
lastTouchCenter = currentCenter;
lastTouchDistance = currentDistance;
wasTwoFingerActive = true;
}
// void HandleTouchRotation()
// {
// if (Input.touchCount == 2)
// {
// Touch touch1 = Input.GetTouch(0);
// Touch touch2 = Input.GetTouch(1);
// // 只在两个手指都在移动时旋转
// if (touch1.phase == TouchPhase.Moved && touch2.phase == TouchPhase.Moved)
// {
// // 取两个手指移动的平均值
// Vector2 avgDelta = (touch1.deltaPosition + touch2.deltaPosition) * 0.5f;
// float deltaX = avgDelta.x * 0.01f * touchRotationSpeed;
// float deltaY = -avgDelta.y * 0.01f * touchRotationSpeed;
// RotateCamera(deltaX, deltaY,touchRotationSpeed);
// }
// }
// else
// {
// isTouchRotating = false;
// }
// }
void ApplyRotationDelta(float deltaX, float deltaY, float speed)
{
Log($"ApplyRotationDelta -> {deltaX} {deltaY} {speed}");
// 更新角度
currentYaw += deltaX * speed;
currentPitch -= deltaY * speed;
// 限制垂直角度
currentPitch = Mathf.Clamp(currentPitch, minRotationY, maxRotationY);
// Log($"UpdateRotationDelta -> {currentPitch}");
// 计算新位置
float yawRad = currentYaw * Mathf.Deg2Rad;
float pitchRad = currentPitch * Mathf.Deg2Rad;
float x = distanceCameraToTarget * Mathf.Cos(pitchRad) * Mathf.Sin(yawRad);
float y = distanceCameraToTarget * Mathf.Sin(pitchRad);
float z = distanceCameraToTarget * Mathf.Cos(pitchRad) * Mathf.Cos(yawRad);
var newPosition = targetPosition + new Vector3(x, y, z);
_rotateDelta = newPosition - transform.position;
Log($"UpdateRotationDelta ->{distanceCameraToTarget} {currentPitch} {_rotateDelta}");
}
// void RotateCamera(float deltaX, float deltaY, float speed)
// {
// Log($"RotateCamera ->{deltaX} {deltaY} {speed}");
//
// if (!initialized) return;
//
// // 更新角度
// currentYaw += deltaX * speed;
// currentPitch -= deltaY * speed;
//
// // 限制垂直角度
// currentPitch = Mathf.Clamp(currentPitch, -80f, 80f);
//
// // 计算新位置
// float yawRad = currentYaw * Mathf.Deg2Rad;
// float pitchRad = currentPitch * Mathf.Deg2Rad;
//
// float x = distanceCameraToTarget * Mathf.Cos(pitchRad) * Mathf.Sin(yawRad);
// float y = distanceCameraToTarget * Mathf.Sin(pitchRad);
// float z = distanceCameraToTarget * Mathf.Cos(pitchRad) * Mathf.Cos(yawRad);
//
// // 更新相机位置和朝向
// transform.position = targetPosition + new Vector3(x, y, z);
// transform.LookAt(targetPosition);
// }
Color GetStateColor(InputState state)
{
switch (state)
{
case InputState.None: return Color.gray;
case InputState.KeyboardMove: return Color.green;
case InputState.MouseDrag: return Color.blue;
case InputState.MouseRotate: return Color.red;
case InputState.TouchMove: return Color.cyan;
case InputState.TouchZoomRotate: return Color.magenta;
default: return Color.white;
}
}
void OnGUI()
{
if (!showDebugInfo || !Application.isEditor) return;
GUI.backgroundColor = new Color(0, 0, 0, 0.8f);
GUI.Box(new Rect(10, 10, 450, 220), "");
GUI.backgroundColor = Color.white;
GUI.color = Color.yellow;
GUI.Label(new Rect(20, 20, 430, 25), "🎯 视口中心旋转相机控制器",
new GUIStyle(GUI.skin.label) { fontSize = 14, fontStyle = FontStyle.Bold });
GUI.color = Color.white;
GUI.color = Color.white;
var labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 12 };
int yPos = 50;
GUI.color = GetStateColor(currentState);
GUI.Label(new Rect(20, yPos, 430, 20), $"🎮 输入状态: {currentState}", labelStyle);
yPos += 20;
GUI.color = Color.cyan;
GUI.Label(new Rect(20, yPos, 430, 20),
$"📏 距离旋转中心: {targetPosition:F2} → {distanceCameraToTarget:F2} {currentZoomDistance}", labelStyle);
yPos += 20;
GUI.color = Color.cyan;
GUI.Label(new Rect(20, yPos, 430, 20),
$"📏 移动旋转缩放: {_moveValue:F2} → {_rotateDelta:F2} {_zoomDelta}", labelStyle);
// yPos += 20;
// var labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 11 };
}
// 调试可视化
void OnDrawGizmos()
{
if (Application.isPlaying && cam != null)
{
// 绘制当前观察方向
Gizmos.color = Color.red;
Gizmos.DrawRay(transform.position, transform.forward * 5f);
// 绘制缩放中心点
// Vector3 screenCenter = new Vector3(Screen.width * 0.5f, Screen.height * 0.5f, 0);
// Ray ray = cam.ScreenPointToRay(screenCenter);
// if (ray.direction.y != 0)
{
// float t = -ray.origin.y / ray.direction.y;
// if (t > 0)
{
// Vector3 targetPoint = ray.origin + ray.direction * t;
var targetPoint = targetPosition;
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(targetPoint, 1f);
// 连线显示缩放关系
Gizmos.color = Color.yellow;
Gizmos.DrawLine(transform.position, targetPoint);
}
}
// if (initialized)
// {
// Gizmos.color = Color.red;
// Gizmos.DrawWireSphere(targetPosition, 1f);
// }
// 缩放信息
#if UNITY_EDITOR
UnityEditor.Handles.Label(transform.position + Vector3.up * 2,
$"缩放: {currentZoomDistance:F1} (目标: {targetZoomDistance:F1})");
#endif
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f3be3f122a2040dc9ec1cc47700c0dd9
timeCreated: 1757576580

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7c3b48a6c8cf493290afceba8a55464a
timeCreated: 1757337898

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using UnityEngine;
namespace HexGrid
{
public class Condtion{
};
[System.Serializable]
public class HexBuilding
{
public int zoneId;
//public int tileId;
public int buildingId;
public int level;
public string buildingName;
// 直接使用的坐标
public Vector3 buildingPosition; //
public string buildCenterTileId;
//TODO:LF 是否使用坐标
public Vector2Int buildPosition;
public List<Condtion> condtions;
public List<string> hasTiles;
// public GameObject buildModel;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ae04bca5282b4ac9a1f57f0c3636146c
timeCreated: 1757504293

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace HexGrid
{
[System.Serializable]
public class HexSubZone
{
//
public int zoneId; // 属于哪个zone
public int subZoneId;
//public List<HexTile> tiles= new ();
// 当前包括的地块Id
public List<string> tileNames = new();
//todo:LF 使用ID 比较好,效率更好一点
// public List<int> tileIds = new();
//TODO:LF 建筑列表
// public List<HexBuilding> buildings = new();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a2c69a061a164687896f45bc691fee8a
timeCreated: 1757322216

View File

@@ -0,0 +1,39 @@
using UnityEngine;
using UnityEngine.Serialization;
// 应该移动到Data 中去
namespace HexGrid
{
public enum TileType
{
None,
Grass,
Road,
Water
}
[System.Serializable]
public class HexTile
{
//可变的值
//0 未激活 1 可建造 2 已经建造完成
public int tileStatus;
// 固定的值
public int id; // 记录地块的Id,取代字符串,引入HexMath 的时候使用
public int zoneId; // 保留 一下zoneId,可以快速找到zone
public int subZoneIndex; // 子区域的索引
public string zoneName;
public string tileOriName; // 块的原始名,
public string tileId; // 考虑是坐标合并x_y tile_0_0
public SerializableVector2Int tileCoords; // 在 Chunk 内的坐标
public TileType type;
public GameObject visual;
public override string ToString()
{
return $"{tileId} -> {tileCoords} {type} -> tileStatus:{tileStatus}";
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b56b1822990b4b81802eb5e71412a047
timeCreated: 1757322316

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace HexGrid
{
[System.Serializable]
public class HexZone
{
//
public int zoneId;
public string zoneName;
public bool isUnlocked;
// 只渲染边缘即可,暂时保留
public GameObject zoneObject; // Prefab / 解锁未建的时候构建一下
public List<HexSubZone> subZones = new();
public List<HexTile> tiles= new ();
public HexTile AddTile(HexTile tile)
{
tiles.Add(tile);
return tile;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4732739fb4c44dcc89ec4712de280e7e
timeCreated: 1757322148

View File

@@ -0,0 +1,33 @@
using UnityEngine;
namespace HexGrid
{
[System.Serializable]
public class SerializableVector2Int
{
public int x;
public int y;
public SerializableVector2Int(int x, int y)
{
this.x = x;
this.y = y;
}
// 隐式转换,方便使用
public static implicit operator Vector2Int(SerializableVector2Int v)
{
return new Vector2Int(v.x, v.y);
}
public static implicit operator SerializableVector2Int(Vector2Int v)
{
return new SerializableVector2Int(v.x, v.y);
}
public override string ToString()
{
return $"({x},{y})";
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4b763fe890be432b87f12145969ddd1a
timeCreated: 1757939498

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 26e7d1599e26438dbf61884703bfaaf0
timeCreated: 1757931277

View File

@@ -0,0 +1,486 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using HexGrid.Math;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace HexGrid.Editor
{
public class HexMapEditorWindow: EditorWindow
{
private const string WindowName = "HexMapEditor";
//
private GameObject _tileRoot;
//
private GameObject _prefabTileRoot;
private Transform _rootForDelTrans;
// 当前的数量
private int buttonCount = 0;
private int selectedButton = 0;
private string[] buttonLabels = {"选项1", "选项2", "选项3", "选项4"};
private int selectedIndex = 0;
private List<string> buttonList = new List<string>();
private string newButtonName = "新按钮";
[MenuItem("Tools/HexMap/Editor")]
static void OpenWindow()
{
// 创建窗口
var window = GetWindow<HexMapEditorWindow>(WindowName);
window.Show();
}
private void OnGUI()
{
HexMath.SetHexMapOrigin(Vector3.zero);
HexMath.SetGridSize(1.52f);
//EditorGUILayout.Label("编辑器设置", EditorStyles.boldLabel);
// GUILayout.Label("编辑器设置", EditorStyles.boldLabel);
// PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
GameObject mapRootGo;
if (!prefabStage)
{
var sceneName = SceneManager.GetActiveScene().name;
GUILayout.Label($"场景名: {sceneName}",EditorStyles.boldLabel);
_tileRoot = EditorGUILayout.ObjectField("地图节点",_tileRoot,typeof(GameObject),true) as GameObject;
mapRootGo = _tileRoot;
_rootForDelTrans = null;
}
else
{
var prefabName = PrefabStageUtility.GetCurrentPrefabStage()?.prefabContentsRoot?.name?? "";
GUILayout.Label("Prefab 编辑模式");
GUILayout.Label($"prefabName: {prefabName}",EditorStyles.boldLabel);
_prefabTileRoot = EditorGUILayout.ObjectField("Prefab地图节点",_prefabTileRoot,typeof(GameObject),true) as GameObject;
mapRootGo = _prefabTileRoot;
_rootForDelTrans = PrefabStageUtility.GetCurrentPrefabStage().prefabContentsRoot.transform;
}
EditorGUILayout.Space(5f);
EditorGUILayout.Space(10);
if (GUILayout.Button("检查",GUILayout.Height(30)))
{
if (!mapRootGo)
{
EditorUtility.DisplayDialog("提示", "请设置地图节点", "确定");
return;
}
CheckFatherTile(mapRootGo.transform);
}
EditorGUILayout.Space();
if (GUILayout.Button("添加到Top",GUILayout.Height(30)))
{
if (!mapRootGo)
{
EditorUtility.DisplayDialog("提示", "请设置地图节点", "确定");
return;
}
// ApplySettings(mapRootGo);
MoveTerrainToMap(mapRootGo.transform);
}
EditorGUILayout.Space();
if (GUILayout.Button("从Top移除父类",GUILayout.Height(30)))
{
if (!_tileRoot)
{
EditorUtility.DisplayDialog("提示", "请设置地图节点", "确定");
return;
}
RemoveFromTop();
}
// EditorGUILayout.Space(10);
// GUILayout.Label("动态按钮创建示例", EditorStyles.boldLabel);
// EditorGUILayout.Space(5);
//
//
// GUILayout.Label("动态 Selection Grid 示例", EditorStyles.boldLabel);
//
// // 添加按钮的控制区域
// GUILayout.BeginHorizontal();
// newButtonName = GUILayout.TextField(newButtonName, GUILayout.Width(150));
// if (GUILayout.Button("添加按钮", GUILayout.Width(80)))
// {
// AddButton(newButtonName);
// }
// GUILayout.EndHorizontal();
//
// GUILayout.Space(10);
//
// // 只有当列表中有按钮时才显示 SelectionGrid
// if (buttonList.Count > 0)
// {
// // 将 List 转换为数组传给 SelectionGrid[1][2]
// selectedIndex = GUILayout.SelectionGrid(
// selectedIndex,
// buttonList.ToArray(), // 转换为数组
// 3, // 每行3个按钮
// GUILayout.Height(100)
// );
//
// // 确保选中索引在有效范围内
// if (selectedIndex >= buttonList.Count)
// {
// selectedIndex = buttonList.Count - 1;
// }
//
// GUILayout.Space(10);
// GUILayout.Label($"当前选中: {buttonList[selectedIndex]} (索引: {selectedIndex})");
//
// // 删除选中的按钮
// if (GUILayout.Button("删除选中的按钮"))
// {
// RemoveSelectedButton();
// }
// }
// else
// {
// GUILayout.Label("暂无按钮,请添加新按钮");
// }
//
// // 清空所有按钮
// if (buttonList.Count > 0 && GUILayout.Button("清空所有"))
// {
// ClearAll();
// }
// // 主按钮 - 用于创建新按钮
// if (GUILayout.Button("点击创建新按钮", GUILayout.Height(30)))
// {
// CreateNewButton();
// }
//
// // 使用ScrollView以防按钮过多
// if (buttonCount > 0)
// {
// GUILayout.Label($"已创建 {buttonCount} 个按钮:");
//
// // 显示所有动态创建的按钮
// for (int i = 0; i < buttonCount; i++)
// {
// int index = i; // 捕获当前索引
// if (GUILayout.Button($"动态按钮 #{index + 1}", GUILayout.Height(25)))
// {
// OnDynamicButtonClick(index);
// }
// }
// }
//
// EditorGUILayout.Space(10);
// // 清除所有按钮
// if (buttonCount > 0 && GUILayout.Button("清除所有按钮"))
// {
// buttonCount = 0;
// Repaint();
// }
//
// // 使用水平布局
// GUILayout.BeginHorizontal();
// if (GUILayout.Button("创建普通按钮", GUILayout.Width(120),GUILayout.Height(120)))
// {
// // CreateButton("普通");
// }
// if (GUILayout.Button("创建特殊按钮", GUILayout.Width(120)))
// {
// // CreateButton("特殊");
// }
// GUILayout.EndHorizontal();
// // 使用SelectionGrid创建按钮组
// int selected = GUILayout.SelectionGrid(-1, new string[] {"按钮1", "按钮2", "按钮3","按钮4"}, 3,
// GUILayout.Width(300), // 设置总宽度为200
// GUILayout.Height(100) // 设置总高度为100
// );
//
// if (selected >= 0)
// {
// Debug.Log($"选择了按钮 {selected + 1}");
// }
//
// GUILayout.Label("带样式的 Selection Grid");
//
// // 创建自定义样式
// GUIStyle gridStyle = new GUIStyle(GUI.skin.button);
//
// // 未选中按钮样式
// gridStyle.normal.textColor = Color.gray;
//
// // 选中按钮样式on前缀表示选中状态
// gridStyle.onNormal.textColor = Color.cyan;
// gridStyle.fontStyle = FontStyle.Bold;
//
// // 创建带自定义样式的选择网格
// selectedButton = GUILayout.SelectionGrid(
// selectedButton,
// buttonLabels,
// 3, // 每行2个按钮
// gridStyle, // 使用自定义样式[1][2]
// GUILayout.Width(300),
// GUILayout.Height(100)
// );
//
// GUILayout.Space(10);
// GUILayout.Label($"当前选中: {buttonLabels[selectedButton]}");
}
void AddButton(string name)
{
buttonList.Add($"{name} {buttonList.Count + 1}");
Repaint();
}
void RemoveSelectedButton()
{
if (selectedIndex >= 0 && selectedIndex < buttonList.Count)
{
buttonList.RemoveAt(selectedIndex);
// 调整选中索引
if (selectedIndex >= buttonList.Count && buttonList.Count > 0)
{
selectedIndex = buttonList.Count - 1;
}
Repaint();
}
}
void ClearAll()
{
buttonList.Clear();
selectedIndex = 0;
Repaint();
}
private void CreateNewButton()
{
buttonCount++;
Debug.Log($"创建了新按钮,当前按钮总数:{buttonCount}");
Repaint(); // 重绘窗口以显示新按钮
}
private void OnDynamicButtonClick(int index)
{
Log($"点击了动态按钮 #{index + 1}");
}
private static void Log(string s)
{
Debug.Log($"<color=yellow>FishingChallengeManager -> {s} </color>");
}
private void RemoveFromTop()
{
if (Selection.gameObjects.Length == 0)
{
Debug.LogWarning("请先选择一个或多个对象");
return;
}
var list = new List<Object>();
foreach (var t in Selection.gameObjects)
{
var paGameObj = t.transform.parent?.gameObject;
if (paGameObj && paGameObj.name.StartsWith("top"))
{
Undo.SetTransformParent(t.transform, null, "Remove Parent of " + t.gameObject.name ?? "");
t.transform.SetParent(_rootForDelTrans, true);
list.Add(t);
}
}
Selection.objects = list.ToArray();
FocusSelectinHierarchy();
}
private void Test()
{
// var enumerable = Selection.gameObjects.Select(elem => elem.transform).ToList();
// var mapRoot = _tileRoot.transform;
// foreach (var child in enumerable)
// {
// // child.SetParent(mapRoot,true);
// Undo.SetTransformParent(child,mapRoot,$"Reparent {child.gameObject.name}");
// child.SetParent(mapRoot,true);
// }
// var obj = Selection.activeGameObject;
// EditorApplication.delayCall += () =>
// {
// EditorGUIUtility.PingObject(obj);
// Selection.activeGameObject = obj;
// };
}
private void CheckFatherTile(Transform mapRoot)
{
var mapTiles = mapRoot.GetComponentsInChildren<Transform>(true);
var list = new List<Object>();
var enumerable = Selection.gameObjects.Where(elem => elem.transform != mapRoot).Select(elem => elem.transform).ToList();
foreach (var pos in enumerable.Select(child => HexMath.PixelToAxial(child.position)))
{
foreach (Transform tile in mapTiles)
{
// if(tile.Equals(mapRoot)) continue;
// if()
var hexGridController = tile.GetComponent<HexGridTiler>();
if(!hexGridController) continue;
var tilePos = HexMath.PixelToAxial(tile.position);
if (tilePos.Equals(pos))
{
var onTop = tile.Find("on/top");
ExpandParentHierarchy(onTop);
list.Add(tile.gameObject);
break;
}
}
}
list.AddRange(Selection.objects);
Selection.objects = list.ToArray();
FocusSelectinHierarchy();
}
//
private void MoveTerrainToMap(Transform mapRoot)
{
Undo.IncrementCurrentGroup();
Undo.SetCurrentGroupName("Reparent");
var mapTiles = mapRoot.GetComponentsInChildren<Transform>(true);
var list = new List<GameObject>();
var enumerable = Selection.gameObjects.Select(elem => elem.transform).ToList();
foreach (var child in enumerable)
{
var pos = HexMath.PixelToAxial(child.position);
foreach (Transform tile in mapTiles)
{
var tilePos = HexMath.PixelToAxial(tile.position);
if (tilePos.Equals(pos))
{
var onTop = tile.Find("on/top");
Undo.SetTransformParent(child,onTop,$"$Reparent {child.gameObject.name}");
child.SetParent(onTop,true);
list.Add(child.gameObject);
break;
}
}
// var tile = HexMath.PixelToAxial(terr.position)
}
Selection.objects = list.ToArray();
FocusSelectinHierarchy();
}
private static void ExpandParentHierarchy(Transform target)
{
if (!target) return;
List<GameObject> path = new List<GameObject>();
Transform current = target;
// 收集从目标到根的路径
path.Add(current.gameObject);
// 从根到目标依次展开
for (int i = path.Count - 1; i >= 0; i--)
{
// 使用内部方法展开 Hierarchy 中的对象
var type = typeof(EditorWindow).Assembly.GetType("UnityEditor.SceneHierarchyWindow");
var window = EditorWindow.GetWindow(type);
var expandMethod = type.GetMethod("SetExpandedRecursive");
if (expandMethod != null)
{
expandMethod.Invoke(window, new object[] { path[i].GetInstanceID(), true });
}
}
}
private void FocusSelectinHierarchy()
{
SceneView sceneView = SceneView.lastActiveSceneView;
sceneView.Focus();
sceneView.FrameSelected();
var selected = Selection.activeGameObject;
if (selected)
{
// EditorGUIUtility.PingObject(selected);
EditorApplication.delayCall += () =>
{
// ExpandParentHierarchy(selected.transform);
EditorGUIUtility.PingObject(selected);
};
}
}
private void ApplySettings(Transform mapRoot)
{
MoveTerrainToMap(mapRoot);
// PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
//
// GameObject prefabRoot = prefabStage.prefabContentsRoot;
//
// Debug.Log($"当前Prefab根节点: {prefabRoot.name}");
// // 选中根节点
// Selection.activeGameObject = prefabRoot;
// 检查是否在Prefab模式
// PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
// if (prefabStage != null)
// {
// // 在Prefab模式中
// GameObject prefabRoot = prefabStage.prefabContentsRoot;
// Debug.Log($"Prefab根节点: {prefabRoot.name}");
// Selection.activeGameObject = prefabRoot;
// }
// else
// {
// // 在场景模式中
// Scene activeScene = SceneManager.GetActiveScene();
// GameObject[] sceneRoots = activeScene.GetRootGameObjects();
//
// Debug.Log($"场景 '{activeScene.name}' 的根节点:");
// foreach (var root in sceneRoots)
// {
// Debug.Log($"- {root.name}");
// }
// Selection.objects = sceneRoots;
// }
// GameObject[] rootGameObjects = SceneManager.GetActiveScene().GetRootGameObjects();
//
// foreach (GameObject root in rootGameObjects)
// {
// Debug.Log("根节点: " + root.name);
// }
// if (Selection.activeGameObject)
// {
// Undo.RecordObject(Selection.activeGameObject, "Apply Settings");
// // 应用设置到选中的对象
// // Selection.activeGameObject.name = objectName;
// }
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1791b26af7cd42d3a08f13f1f73b8038
timeCreated: 1758249532

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d0e13fbedff0461faabb401366c92767
timeCreated: 1757930649

View File

@@ -0,0 +1,94 @@
// using NUnit.Framework;
// using UnityEngine;
//
namespace HexGrid.Tests
{
// public class AxialToPixelTests
// {
// // 假设的网格大小和原点位置,应与被测试代码中的值一致
// private const float _gridSize = 1.0f; // 根据实际情况调整
// private static readonly Vector3 _originPos = Vector3.zero; // 根据实际情况调整
//
// [Test]
// public void TestAxialToPixel_Origin()
// {
// // 测试原点坐标 (0, 0)
// Vector3 result = AxialToPixel(0, 0);
// Vector3 expected = _originPos;
// Assert.AreEqual(expected, result, "原点坐标转换不正确");
// }
//
// [Test]
// public void TestAxialToPixel_QDirection()
// {
// // 测试沿Q轴方向的移动
// Vector3 result = AxialToPixel(2, 0);
// Vector3 expected = new Vector3(_gridSize * Mathf.Sqrt(3f) * 2, 0, 0) + _originPos;
// Assert.AreEqual(expected, result, "Q轴方向坐标转换不正确");
// }
//
// [Test]
// public void TestAxialToPixel_RDirection()
// {
// // 测试沿R轴方向的移动
// Vector3 result = AxialToPixel(0, 2);
// Vector3 expected = new Vector3(0, 0, _gridSize * 1.5f * 2) + _originPos;
// Assert.AreEqual(expected, result, "R轴方向坐标转换不正确");
// }
//
// [Test]
// public void TestAxialToPixel_BothDirections()
// {
// // 测试同时沿Q轴和R轴移动
// Vector3 result = AxialToPixel(2, 3);
//
// float expectedX = _gridSize * Mathf.Sqrt(3f) * (2 + 3 / 2f);
// float expectedZ = _gridSize * 1.5f * 3;
// Vector3 expected = new Vector3(expectedX, 0, expectedZ) + _originPos;
//
// Assert.AreEqual(expected, result, "Q+R方向坐标转换不正确");
// }
//
// [Test]
// public void TestAxialToPixel_NegativeValues()
// {
// // 测试负值坐标
// Vector3 result = AxialToPixel(-2, -3);
//
// float expectedX = _gridSize * Mathf.Sqrt(3f) * (-2 + -3 / 2f);
// float expectedZ = _gridSize * 1.5f * -3;
// Vector3 expected = new Vector3(expectedX, 0, expectedZ) + _originPos;
//
// Assert.AreEqual(expected, result, "负值坐标转换不正确");
// }
//
// [Test]
// public void TestAxialToPixel_WithDifferentGridSize()
// {
// // 如果_gridSize可配置测试不同的网格大小
// // 注意这需要你能修改_gridSize的值
// // float originalGridSize = _gridSize;
// // _gridSize = 2.0f;
//
// // Vector3 result = AxialToPixel(1, 1);
// // float expectedX = 2.0f * Mathf.Sqrt(3f) * (1 + 1 / 2f);
// // float expectedZ = 2.0f * 1.5f * 1;
// // Vector3 expected = new Vector3(expectedX, 0, expectedZ) + _originPos;
//
// // Assert.AreEqual(expected, result, "不同网格大小下的坐标转换不正确");
//
// // _gridSize = originalGridSize; // 恢复原始值
//
// // 如果_gridSize不可修改可以跳过这个测试或添加注释说明
// Assert.Pass("网格大小不可配置,跳过此测试");
// }
//
// // 这是被测试的函数副本,用于测试
// public static Vector3 AxialToPixel(int q, int r)
// {
// var x = _gridSize * Mathf.Sqrt(3f) * (q + r / 2f);
// var z = _gridSize * 1.5f * r;
// return new Vector3(x, 0, z) + _originPos;
// }
// }
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: efbe296c664a45f79d6e940de63e03fe
timeCreated: 1757930665

View File

@@ -0,0 +1,23 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
// 状态的存储用的是PlayFab 吗?
namespace HexGrid
{
[CreateAssetMenu(fileName = "HexGridMap", menuName = "ScriptableObjects/HexMap")]
public class HexGridMap:ScriptableObject
{
public int width = 10;
public int height = 10;
//
public List<HexBuilding> buildings = new();
public List<HexZone> zones = new();
//
public void Clear()
{
buildings.Clear();
zones.Clear();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e0c331f9403c40c8b10fc9591c52e558
timeCreated: 1757319406

View File

@@ -0,0 +1,126 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DG.Tweening;
using UnityEngine;
namespace HexGrid
{
// 调停者
// 进度管理器
public class HexGridMediator : MonoBehaviour,IHexGridMediator
{
private HexZoneManager _zoneManager;
//
private BuildingManager _buildingManager;
public void RegisterZoneManager(HexZoneManager zoneManager)
{
_zoneManager = zoneManager;
}
public void RegisterBuildingManager(BuildingManager buildMamanger)
{
_buildingManager = buildMamanger;
}
public async void OnHexCellClicked(HexTile tiler)
{
if (_zoneManager.OnHexTileData(tiler))
{
await Awaiters.Seconds(0.2f);
await CheckBuildingAfterBuild(tiler);
await OnActiveNextTile(tiler);
}
}
public async Task OnActiveNextTile(HexTile tiler)
{
await _zoneManager.OnActiveNextTile(tiler);
}
public async Task CheckBuildingAfterBuild(HexTile tiler)
{
await _buildingManager.CheckBuildingAfterBuild(tiler);
}
public void CheckBuildingAfterBuildImme(HexTile tile)
{
_buildingManager.CheckBuildingAfterBuildImme(tile);
}
public void LoadZones(List<HexZone> zones)
{
}
//-
public void LoadBuildings(List<HexBuilding> buildings)
{
_buildingManager.LoadBuildings(buildings);
}
public async void Build(int zoneId,BuildingData buildingData,bool isImme)
{
Log($"Build-> {zoneId} {buildingData.BuildingName}");
var root = _zoneManager.GetBuildingRoot(zoneId);
if (!root) return;
//_buildingManager.Build(root.transform, buildingData);
var buildTrans = root.transform.Find(buildingData.BuildingName);
var endPos = buildTrans.position;
var tileNames = buildingData.TileStateMap.Keys;
var duration = 0.5f;
foreach (var tileName in tileNames)
{
var tile = _zoneManager.GetTileByName(zoneId, tileName);
if (tile.visual)
{
var top = tile.visual.transform.Find("on/top");
if (top)
{
if (isImme)
{
top.gameObject.SetActive(false);
}
else
{
top.DOMove(endPos, duration).SetEase(Ease.InSine).OnComplete(() =>
{
top.gameObject.SetActive(false);
});
}
}
}
}
if (!isImme)
{
await Awaiters.Seconds(duration);
}
if (buildTrans)
{
buildTrans.gameObject.SetActive(true);
}
if (isImme)
{
buildTrans.localScale = Vector3.one;
}
else
{
var ani = buildTrans.GetComponent<Animation>();
if (ani)
{
ani.Play("grow");
}
}
}
private static void Log(string t)
{
Debug.Log($"<color=orange>HexGridMediator -> {t} </color>");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d14371ef6ed24213bad2610af429598e
timeCreated: 1757391856

View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using com.fpnn.livedata;
using HexGrid.MapHelper;
using HexGrid.Math;
using TMPro;
using UnityEngine;
namespace HexGrid
{
// 看看用来触发事件可行吗
public class HexGridTiler : MonoBehaviour
{
//
private HexTile _tileData;
//组件
private Transform _on;
private Transform _off;
// 坐标显示
private TMP_Text _textCoords;
// 调停者
private IHexGridMediator _mediator;
// 动画
private Animation _animation;
private void Awake()
{
Log($"Awake ->{gameObject.name}");
//
_on = transform.Find("on");
_off = transform.Find("off");
_textCoords = transform.Find("text_coords").GetComponent<TMP_Text>();
//
_animation = GetComponent<Animation>();
}
private void Start()
{
Log($"Start()-> {gameObject.name}: {_tileData}");
}
public void SetMediator(IHexGridMediator mediator)
{
_mediator = mediator;
}
public void LoadTileData(HexTile tile)
{
Log($"LoadTileData() ->{tile}");
_tileData = tile;
UpdateName();
UpdateCoordsText();
}
private void UpdateCoordsText()
{
if (_textCoords)
{
_textCoords.text = _tileData.tileCoords.ToString();
}
}
private void UpdateName()
{
// var tileName = $"tile{_tileData.tileCoords}";
var tileName = HexUtils.MakeTileId(_tileData.tileCoords);
Log($"UpdateName()-> {tileName}");
gameObject.name = tileName;
}
//
public async void OnHexCellClicked()
{
if (_tileData == null)
{
return;
}
// 放到调停者当中去
if (_tileData.tileStatus == 1)
{
_animation.Play("rotation");
}
_mediator.OnHexCellClicked(_tileData);
}
public void UpdateVisual()
{
Log("UpdateVisual -> ");
var status = _tileData.tileStatus;
if (status == 0)
{
gameObject.SetActive(false);
}
else
{
gameObject.SetActive(true);
if (status == 1)
{
_on.gameObject.SetActive(true);
_off.gameObject.SetActive(true);
_off.localScale = Vector3.one;
_on.localScale = new Vector3(0, 0, 0);
_on.rotation = Quaternion.Euler(0,60,270);
}
else if (status == 2)
{
_on.gameObject.SetActive(true);
_off.gameObject.SetActive(false);
_off.localScale = Vector3.zero;
}
}
}
private static void Log(object t)
{
Debug.Log($"<color=yellow>HexGridTiler -> {t} </color>");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 40b5bb603e91485d88cc2cee57ccab0c
timeCreated: 1757335157

View File

@@ -0,0 +1,47 @@
using System;
using asap.core;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UniRx;
using UnityEngine;
using UnityEngine.AddressableAssets;
using System.Threading.Tasks;
using GameCore;
namespace HexGrid
{
public class HexMapAct :AGameAct
{
//
public string mapName = "hex_build_01";
// 地图 Prefab
private GameObject _obj;
public override async Task<bool> StartAsync()
{
_obj = await Addressables.InstantiateAsync(mapName).Task;
Input.multiTouchEnabled = true;
GetUI();
return await base.StartAsync();
}
private async void GetUI()
{
await Task.Delay(400);
await UIManager.Instance.ShowUI(new UIType("HexMapPanel"));
}
public override async Task StopAsync()
{
if (_obj)
{
Addressables.Release(_obj);
DestroyImmediate(_obj);
}
Input.multiTouchEnabled = false;
UIManager.Instance.DestroyUI("HexMapPanel");
await Awaiters.NextFrame;
}
protected override void OnDestroy()
{
Debug.Log("HexGridAct Destroyed!");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ac9b12025b39414f8d1ff8d076c7091d
timeCreated: 1757329066

View File

@@ -0,0 +1,196 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using asap.core;
using Cinemachine;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Serialization;
// 格子管理
// 作为 调停者 使用
namespace HexGrid{
public class HexGridMapUserData
{
//
private int zoneNum = 2;
//
public int zoneCurrentIndex = 0; // [0,1];
//
public int subZoneCurrentIndex = 0; // []
// 已经完成的地块
public List<string> completeTiles = new();
// 属性
public Dictionary<string, int> Properties1 = new();
//
public Dictionary<string, int> Properties2 = new();
// 当前tile块的数值
// <Id,status>
// private Dictionary<int, int> tiles = new Dictionary<int, int>();
// private Dictionary<int, int> buildings = new Dictionary<int, int>();
}
public class HexMapDataManager
{
public const string SaveKey = "HexMapData";
public HexGridMapUserData UserMapData { get; private set; }
public void Init()
{
UserMapData ??= new HexGridMapUserData
{
zoneCurrentIndex = 0,
subZoneCurrentIndex = 0
};
SaveUserData();
}
public void ClearMapData()
{
UserMapData = null;
SaveUserData();
}
//
public void SaveUserData()
{
PlayFabMgr.Instance.UpdateUserDataValue(SaveKey, JsonConvert.SerializeObject(UserMapData));
}
public void LoadUserData(string dataValue)
{
UserMapData = JsonConvert.DeserializeObject<HexGridMapUserData>(dataValue);
}
}
public class HexMapManager : MonoBehaviour
{
//
public HexZoneManager zoneManager;
public TerrainManager terrainManager;
public BuildingManager buildingManager;
public HexGridMediator hexGridMediator;
public Transform hexMapRoot;
public Camera mainCamera;
public string mapName = "hex_build_01";
// 设置的
private CinemachineVirtualCamera _vCam;
// 数据
private HexGridMap _mapInfo;
private HexMapDataManager _dataManager;
//
private void Awake()
{
mainCamera = Camera.main;
_dataManager = GContext.container.Resolve<HexMapDataManager>();
}
private void Start()
{
zoneManager.SetRoot(hexMapRoot);
zoneManager.SetMediator(hexGridMediator);
zoneManager.SetHexMapManager(this);
//
buildingManager.SetMediator(hexGridMediator);
//
hexGridMediator.RegisterZoneManager(zoneManager);
hexGridMediator.RegisterBuildingManager(buildingManager);
_ = InitMapInfo();
}
private void Update()
{
if (Input.touchCount > 0 )
{
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
CheckHit(Input.GetTouch(0).position);
}
}
else
{
// 鼠标点击PC
if (Input.GetMouseButtonDown(0))
{
CheckHit(Input.mousePosition);
}
}
}
private void OnDisable()
{
if (_vCam)
{
_vCam.m_Lens.Orthographic = false;
if (!Camera.main) return;
var brain = Camera.main.GetComponent<CinemachineBrain>();
brain?.ManualUpdate();
}
}
/// <summary>
/// 检测点击或触摸位置击中了哪个物体
/// </summary>
private void CheckHit(Vector3 screenPosition)
{
if (mainCamera == null)
mainCamera = Camera.main;
var ray = mainCamera.ScreenPointToRay(screenPosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
Debug.Log("点击到了: " + hit.collider.name);
var hexGridTiler = hit.collider.GetComponent<HexGridTiler> ();
if (hexGridTiler)
{
hexGridTiler.OnHexCellClicked();
}
}
}
public void UnlockZone(int zoneId)
{
var zone = zoneManager.GetZoneById(zoneId);
if (zone != null && !zone.isUnlocked)
{
// if (GameManager.Instance.SpendMoney(zone.unlockCost))
{
zone.isUnlocked = true;
zone.zoneObject.SetActive(true);
terrainManager.UpdateTerrainForZone(zoneId);
// 假设用 zoneId 推导 Chunk 坐标
Vector2Int chunkCoord = new Vector2Int(zoneId % 3, zoneId / 3);
// ChunkManager.Instance.UnlockChunk(chunkCoord);
// cameraController.FocusOn(zone.zoneObject.transform.position);
// FXManager.Instance.PlayUnlockEffect(zone.zoneObject.transform.position);
//SaveMapState();
SaveUserData();
}
}
}
private async Task InitMapInfo()
{
var mapInfo = mapName + "_info";
_mapInfo = (HexGridMap)await Addressables.LoadAssetAsync<object>(mapInfo).Task;
LoadMapInfo(_mapInfo);
}
public void LoadMapInfo(HexGridMap mapInfo)
{
zoneManager.LoadZonesFromSave(mapInfo,_dataManager.UserMapData);
}
//
public void SaveUserData()
{
_dataManager.SaveUserData();
}
//
public void LoadUserData(string dataValue)
{
_dataManager.LoadUserData(dataValue);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ac6e691661dacb4fb78b36f4155cea0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
using System;
using asap.core;
using game;
using GameCore;
using PlayFab.Internal;
using UnityEngine;
using UnityEngine.UI;
namespace HexGrid
{
public class HexMapPanel : MonoBehaviour
{
private Button btn_close;
private Button btnClear;
private void Start()
{
btn_close = transform.Find("root/btn_close").GetComponent<Button>();
btn_close.onClick.AddListener(OnClose);
btnClear = transform.Find("root/btn_clear").GetComponent<Button>();
btnClear.onClick.AddListener(OnClear);
}
private void OnClear()
{
Log("OnClear-> ");
var hmdm = GContext.container.Resolve<HexMapDataManager>();
hmdm.ClearMapData();
hmdm.Init();
OnClose();
}
private void OnClose()
{
GContext.Publish(new UnloadActToNextAct());
btn_close.onClick.RemoveAllListeners();
}
private static void Log(object message)
{
Debug.Log($"<color=orange>HexMapPanel => {message} </color>");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c1975c3f4ad44ba781f5a9214171b29a
timeCreated: 1757384557

View File

@@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Contexts;
using HexGrid.MapHelper;
using UnityEngine;
using HexGrid.Math;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace HexGrid
{
// 数据保存不放在Manager 中处理
// [ExecuteInEditMode]
public class HexMapSaver : MonoBehaviour
{
public Transform root;
// 标准格子
[SerializeField] private GameObject standGridPrefab;
private GameObject standGridGo;
//
private Transform rootBuilding;
//
public HexGridMap data;
//
private void Awake()
{
}
private void SetupStandGrid()
{
if (standGridPrefab)
{
standGridGo = Instantiate(standGridPrefab, Vector3.zero, Quaternion.identity, transform);
standGridGo.name = "StandGrid";
standGridGo.SetActive(false);
var size = HexUtils.DetermineTileSize(standGridGo, out var bounds);
HexMath.SetGridSize(size.z * 0.5f);
DestroyImmediate(standGridGo); // 移除
}
HexMath.SetHexMapOrigin(Vector3.zero);
}
// 保存建筑数据
private void SaveBuildings()
{
rootBuilding = root.Find("merge");
var buildingNum = rootBuilding.childCount;
foreach (Transform child in rootBuilding)
{
// data.buildings.Add();
var building = new HexBuilding
{
buildingName = child.gameObject.name,
buildingPosition = child.position,
hasTiles = new List<string>()
};
if (building.buildingName == "Hotel")
{
building.hasTiles.Add(HexUtils.MakeTileId(new Vector2Int(-3, 4)));
building.hasTiles.Add(HexUtils.MakeTileId(new Vector2Int(-2, 4)));
}
if (building.buildingName == "hill")
{
building.hasTiles.Add("tile:(-2,6)");
building.hasTiles.Add("tile:(-3,6)");
building.hasTiles.Add("tile:(-1,5)");
building.hasTiles.Add("tile:(-2,5)");
}
data.buildings.Add(building);
}
}
private void SaveZone()
{
}
private static HexTile BuildTile(int id,Transform tileTransform,HexZone zone,HexSubZone subZone,TileType tileType)
{
var worldPos = tileTransform.position;
var tileCoords = HexMath.PixelToAxial(worldPos);
var tile = new HexTile
{
id = id,
tileOriName = tileTransform.gameObject.name,
tileId = HexUtils.MakeTileId(tileCoords),
zoneId = zone.zoneId,
zoneName = zone.zoneName,
subZoneIndex = subZone?.subZoneId ?? 0,
tileCoords = tileCoords,
type = tileType,
};
return tile;
}
#if UNITY_EDITOR
[ContextMenu("Save Data")]
void SaveData()
{
EditorUtility.SetDirty(data);
AssetDatabase.SaveAssets();
Debug.Log("Data saved to asset file.");
}
[ContextMenu("Save Data2")]
void SaveData2()
{
SetupStandGrid();
data.Clear();
SaveBuildings();
SaveZone();
var zone = new HexZone
{
zoneName = "zone_01",
zoneId = 0,
isUnlocked = true
};
var zone1 = root.Find(zone.zoneName);
var count = 0;
var subZoneId = 0;
HexSubZone subZone = null;
foreach (Transform child in zone1)
{
if (!child.GetComponent<HexGridTiler>())
{
continue;
}
if (count % 3 == 0)
{
subZone = new HexSubZone();
zone.subZones.Add(subZone);
subZone.subZoneId = subZoneId++;
}
// var worldPos = child.position;
// var tileCoords = HexMath.PixelToAxial(worldPos);
// Debug.Log($"tileCoords -> {tileCoords.x}, {tileCoords.y}");
// var tile = new HexTile
// {
// id = count,
// tileOriName = child.gameObject.name,
// tileId = HexUtils.MakeTileId(tileCoords),
// zoneId = zone.zoneId,
// zoneName = zone.zoneName,
// subZoneIndex = subZone?.subZoneId ?? 0,
// tileCoords = tileCoords,
// type = TileType.Grass,
// };
// zone.tiles.Add(BuildTile(count,child,zone,subZone,TileType.Grass));
var tile = zone.AddTile(BuildTile(count, child, zone, subZone, TileType.Grass));
subZone?.tileNames.Add(tile.tileId);
count++;
}
data.zones.Add(zone);
zone = new HexZone
{
zoneName = "zone_02",
zoneId = 1,
isUnlocked = false
};
data.zones.Add(zone);
var zoneTransform = root.Find(zone.zoneName);
count = 0;
subZone = null;
subZoneId = 0;
foreach (Transform child in zoneTransform)
{
if (!child.GetComponent<HexGridTiler>())
{
continue;
}
if (count % 3 == 0)
{
subZone = new HexSubZone();
zone.subZones.Add(subZone);
subZone.subZoneId = subZoneId++;
}
var worldPos = child.position;
var tileCoords = HexMath.PixelToAxial(worldPos);
Debug.Log($"tileCoords -> {tileCoords.x}, {tileCoords.y}");
// var tile = new HexTile
// {
// id = count,
// tileOriName = child.gameObject.name,
// tileId = HexUtils.MakeTileId(tileCoords),
// zoneId = zone.zoneId,
// zoneName = zone.zoneName,
// subZoneIndex = subZone?.subZoneId ?? 0,
// tileCoords = tileCoords,
// type = TileType.Grass,
// };
// zone.tiles.Add(tile);
var tile = zone.AddTile(BuildTile(count, child, zone, subZone, TileType.Grass));
subZone?.tileNames.Add(tile.tileId);
count++;
}
SaveData();
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e902553b41074d6bb40120827a5a8d05
timeCreated: 1757324607

View File

@@ -0,0 +1,18 @@
using UnityEngine;
namespace HexGrid
{
using UnityEngine;
using UnityEngine.AI;
public class NPCManager : MonoBehaviour
{
public Transform targetPoint;
private NavMeshAgent agent;
void Start()
{
agent = GetComponent<NavMeshAgent>();
if (targetPoint != null)
agent.SetDestination(targetPoint.position);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c7d02a00f9f043c89b7eebd926a64925
timeCreated: 1757325680

View File

@@ -0,0 +1,44 @@
using System.Linq;
using UnityEngine;
namespace HexGrid
{
public class HexZoneController : MonoBehaviour,IZoneController
{
//
private HexZone _zoneData;
private IBuildMediator _buildMediator;
private void Awake()
{
Log("Awake()-> ");
}
private static void Log(object t)
{
Debug.Log($"<color=yellow>HexZoneController -> {t} </color>");
}
public void LoadData(HexZone zoneData)
{
_zoneData = zoneData;
}
public void UpdateZoneStatus()
{
Log("UpdateZoneStatus() -> ");
}
public void OnHexCellClicked(HexTile tiler)
{
Log($"OnHexCellClicked -> {tiler}");
var tiles = _zoneData.tiles;
foreach (var tileData in tiles.Where(tileData => tileData.tileId == tiler.tileId))
{
tileData.tileStatus = tiler.tileStatus;
break;
}
// _buildMediator?.CheckForBuilding(_zoneData.subZones);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2448d233ec134e92896704aa5d79b779
timeCreated: 1757339298

View File

@@ -0,0 +1,579 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using HexGrid.MapHelper;
using HexGrid.Math;
using TMPro;
using UnityEngine;
namespace HexGrid
{
// 区域管理
public class HexZoneManager : MonoBehaviour,IHexGridMediatable
{
//
private List<HexZone> _zones;
private List<HexBuilding> _buildings;
//
private HexGridMapUserData _userMapData;
//
private Transform transformRoot;
//
private List<HexZoneController> zoneControllers;
//
private IHexGridMediator _mediator;
//
private HexMapManager _hexMapManager;
public void SetMediator(IHexGridMediator mediator)
{
_mediator = mediator;
}
private void Awake()
{
zoneControllers = new List<HexZoneController>();
}
private void Start()
{
}
//
public void SetHexMapManager(HexMapManager manager)
{
_hexMapManager = manager;
}
//
public void SetRoot(Transform root)
{
transformRoot = root;
}
public HexZone GetZoneById(int zoneId)
{
return _zones.Find(z => z.zoneId == zoneId);
}
//TODO:LF 侵入了数据块, 正式版需要分开,生成User版本,然后和固定的刷在一起提供出更好
public void BuildMapData(HexGridMapUserData userMapData)
{
var currZoneIndex = userMapData.zoneCurrentIndex;
for (var i = 0; i < currZoneIndex; ++i)
{
_zones[i].isUnlocked = true;
foreach (var tile in _zones[i].tiles)
{
tile.tileStatus = 2;
// _mediator.CheckBuildingAfterBuildImme(tile);
}
//BuildBuildingData(_zones[i].subZones);
}
// 全部隐藏
for (var i = currZoneIndex + 1; i < _zones.Count; ++i)
{
_zones[i].isUnlocked = false;
foreach (var tile in _zones[i].tiles)
{
tile.tileStatus = 0;
}
}
//
var subZoneCurrentIndex = userMapData.subZoneCurrentIndex;
if (currZoneIndex > _zones.Count -1)
{
return;
}
//
var zoneCur = _zones[currZoneIndex];
zoneCur.isUnlocked = true;
for (var i = 0; i < subZoneCurrentIndex; ++i)
{
var tileNames = zoneCur.subZones[i].tileNames;
foreach (var tile in tileNames.SelectMany(tileName => zoneCur.tiles.Where(tile => string.Equals(tile.tileId, tileName))))
{
tile.tileStatus = 2;
// _mediator.CheckBuildingAfterBuildImme(tile);
}
}
for (var i = subZoneCurrentIndex + 1; i < zoneCur.subZones.Count; ++i)
{
var tileNames = zoneCur.subZones[i].tileNames;
foreach (var tile in tileNames.SelectMany(tileName => zoneCur.tiles.Where(tile => string.Equals(tile.tileId, tileName))))
{
tile.tileStatus = 0;
}
}
var subZone = zoneCur.subZones[subZoneCurrentIndex];
var curTiles = subZone.tileNames;
var completeTiles = userMapData.completeTiles;
int count = 0;
foreach (var tile in curTiles.SelectMany(tileName => zoneCur.tiles.Where(tile => string.Equals(tile.tileId, tileName))))
{
Log($"curTile -> {tile.tileId}");
if (count != 0)
{
tile.tileStatus = 0;
continue;
}
tile.tileStatus = completeTiles.Contains(tile.tileId) ? 2 : 1;
if (tile.tileStatus == 1)
{
count++;
}
if (tile.tileStatus == 2)
{
// _mediator.CheckBuildingAfterBuildImme(tile);
}
}
}
//TODO: 构建完成版家住的数据
public void LoadZonesFromSave(HexGridMap mapInfo,HexGridMapUserData userMapData)
{
_zones = mapInfo.zones;
_buildings = mapInfo.buildings;
_userMapData = userMapData;
_mediator.LoadBuildings(mapInfo.buildings);
BuildMapData(userMapData);
LoadZones(_zones);
LoadBuildings();
}
private void LoadBuildings()
{
foreach (var tile in _zones.SelectMany(zone => zone.tiles))
{
if (tile.tileStatus == 2)
{
_mediator.CheckBuildingAfterBuildImme(tile);
}
}
}
public void LoadZones(List<HexZone> zones)
{
foreach (var zone in _zones)
{
var zoneTrans = transformRoot.Find(zone?.zoneName);
if (zone != null)
{
var zoneObject = zoneTrans.gameObject;
zone.zoneObject = zoneObject;
var hexZoneController = zoneObject.GetComponent<HexZoneController>();
if(hexZoneController == null)
hexZoneController = zoneObject.AddComponent<HexZoneController>();
zoneControllers.Add(hexZoneController);
hexZoneController.LoadData(zone);
var statusText = zoneTrans.Find("LockStatus").GetComponent<TMP_Text>();
statusText.text = zone.isUnlocked ? "" : "LOCK";
foreach (var tileData in zone.tiles)
{
var tileTransform = zoneTrans.Find(tileData.tileOriName);
if (!tileTransform) continue;
tileData.visual = tileTransform.gameObject;
var tiler = tileTransform.GetComponent<HexGridTiler>();
if (tiler)
{
tiler.SetMediator(_mediator);
tiler.LoadTileData(tileData);
tiler.UpdateVisual();
}
}
}
}
}
public void SaveZonesToFile()
{
foreach (var zone in _zones)
{
// PlayerPrefs.SetInt("Zone_" + zone.id, zone.isUnlocked ? 1 : 0);
}
// PlayerPrefs.Save();
}
public bool UpdateZoneStatus()
{
return false;
}
// TODO: 没有检测邻居的数值,先当前子区域里的全部显示,都完成进入下一个
public bool UpdateSubZoneStatus(HexZone zone, int subZoneId)
{
Log($"UpdateSubZoneStatus -> {subZoneId}");
//return false;
//TODO:LF 先这样用吧.可以绑定一下或者存 MONObehavier 的 List
var subZoneCurrentIndex = _userMapData.subZoneCurrentIndex;
var subzone = zone?.subZones[subZoneCurrentIndex];
var zoneTrans = zone?.zoneObject.transform;
if (subzone?.tileNames != null)
foreach (var tiler in (subzone?.tileNames).Select(tileName => zoneTrans.Find(tileName)).Select(tileTransform => tileTransform.GetComponent<HexGridTiler>()).Where(tiler => tiler))
{
//tiler.LoadTileData();
//this.UpdateTileData(tiler);
tiler.UpdateVisual();
}
return false;
}
public bool UpdateMapStatus()
{
return false;
}
public bool OnHexTileData(HexTile tiler)
{
var zone = _zones.FirstOrDefault(zone => zone.zoneId == tiler.zoneId);
var result = zone?.tiles.FirstOrDefault(myTile => myTile.tileId == tiler.tileId);
Log($"tiler -> {tiler} result: {result}");
// 更新数据
if (result != null && result.tileStatus == 1)
{
result.tileStatus = 2;
_userMapData.completeTiles.Add(result.tileId);
return true;
}
return false;
// 更新View
// var rt = UpdateSubZoneStatus(zone,tiler.subZoneIndex);
// if (!rt) return;
// var rt2 = UpdateZoneStatus();
// if (!rt2) return;
// var rt3 = UpdateMapStatus();
// if (!rt3) return;
// _userMapData.
// UpdateUserData();
}
//
private List<HexTile> GetTilesFromName(HexZone zone,List<string> tileNames)
{
return zone.tiles.Where(tile => tileNames.Contains(tile.tileId)).ToList();
}
public async Task OnActiveNextTile(HexTile result)
{
//TODO:LF 注意这里有报错Camera 完成之后需要修复
var subZoneCurrentIndex = _userMapData.subZoneCurrentIndex;
var zone = GetZoneById(result.zoneId);
var zoneSubTileNames = zone.subZones[subZoneCurrentIndex].tileNames;
var rt = GetNeighborsInZone(result,zone,zone.subZones[subZoneCurrentIndex]);
foreach (var name in rt)
{
Log($"{result.tileCoords} neighbor: {name}" );
}
var rtList = GetTilesFromName(zone, rt);
var nexTile = rtList.FirstOrDefault(c => c.tileStatus == 0);
if (nexTile == null)
{
var subzoneTiles = GetTilesFromName(zone, zoneSubTileNames);
nexTile = subzoneTiles.FirstOrDefault(c => c.tileStatus == 0);
}
if (nexTile != null)
{
await ActiveHexTile(nexTile);
}
else
{
// 进入下一个阶段
if (CheckSubZoneFinished(zone.subZones[subZoneCurrentIndex]))
{
await OnActiveNextPart();
}
}
DoSave();
}
private async Task ActiveHexTile(HexTile nexTile)
{
Log($"ActiveHexTile ->{nexTile}");
await Awaiters.Seconds(0.7f);
ChangeTileStatus(nexTile,1);
}
private static void ChangeTileStatus(HexTile nexTile,int tileStatus)
{
nexTile.tileStatus = tileStatus;
nexTile.visual.GetComponent<HexGridTiler>().UpdateVisual();
}
// private async Task CheckBuilding(HexZone zone,HexSubZone subZone)
// {
// Log("CheckBuilding-> ");
// var buildList = subZone.buildings;
// var parents = zone.zoneObject;
//
// if (buildList is { Count: > 0 })
// {
// foreach (var building in buildList)
// {
//
// if (true)
// {
// var build = Instantiate(building.buildModel, parents.transform);
// build.transform.position = new Vector3(-1.38f, 2.69f, 8.41f);
// }
// }
// }
//
// }
private bool CheckSubZoneFinished(HexSubZone zoneSubZone)
{
var zoneCur = _zones[_userMapData.zoneCurrentIndex];
bool ret = true;
foreach (var tileName in zoneSubZone.tileNames)
{
foreach (var tile in zoneCur.tiles.Where(tile => string.Equals(tile.tileId, tileName)))
{
if (tile.tileStatus != 2)
{
ret = false;
break;
}
}
}
Log($"CheckSubZoneFinished ->{_userMapData.subZoneCurrentIndex}, {zoneSubZone.subZoneId } {ret}" );
return ret;
}
private async Task OnActiveNextPart()
{
Log("OnActiveNextSubZone -> ");
await Awaiters.Seconds(0.7f);
// _userMapData.subZoneCurrentIndex++;
var nextSubzoneIndex = _userMapData.subZoneCurrentIndex + 1;
var zoneCur = _zones[_userMapData.zoneCurrentIndex];
if (nextSubzoneIndex >= zoneCur.subZones.Count)
{
StartNextZone();
}
else
{
StartSubZone();
}
}
public GameObject GetCurZoneRoot()
{
var currZoneIndex = _userMapData.zoneCurrentIndex;
var zoneCur = _zones[currZoneIndex];
if (zoneCur == null)
return null;
if (zoneCur.zoneObject)
{
return zoneCur.zoneObject;
}
var zoneTrans = transformRoot.Find(zoneCur?.zoneName);
return zoneTrans.gameObject;
}
public GameObject GetBuildingRoot(int id)
{
var mergeTrans = transformRoot.Find("merge");
return mergeTrans.gameObject;
}
public GameObject GetZoneObject(int id)
{
var zoneCur = GetZoneById(id);
if (zoneCur == null)
return null;
if (zoneCur.zoneObject)
{
return zoneCur.zoneObject;
}
var zoneTrans = transformRoot.Find(zoneCur?.zoneName);
return zoneTrans.gameObject;
}
private void UpdateCurrZoneStatus()
{
var currZoneIndex = _userMapData.zoneCurrentIndex;
var subZoneCurrentIndex = _userMapData.subZoneCurrentIndex;
var zoneCur = _zones[currZoneIndex];
var zoneObject = zoneCur.zoneObject;
if (zoneObject)
{
var statusText = zoneObject.transform.Find("LockStatus").GetComponent<TMP_Text>();
statusText.text = zoneCur.isUnlocked ? "" : "LOCK";
}
for (var i = 0; i < subZoneCurrentIndex; ++i)
{
var tileNames = zoneCur.subZones[i].tileNames;
foreach (var tile in tileNames.SelectMany(tileName => zoneCur.tiles.Where(tile => string.Equals(tile.tileId, tileName))))
{
tile.tileStatus = 2;
}
}
for (var i = subZoneCurrentIndex + 1; i < zoneCur.subZones.Count; ++i)
{
var tileNames = zoneCur.subZones[i].tileNames;
foreach (var tile in tileNames.SelectMany(tileName => zoneCur.tiles.Where(tile => string.Equals(tile.tileId, tileName))))
{
tile.tileStatus = 0;
}
}
var subZone = zoneCur.subZones[subZoneCurrentIndex];
var curTiles = subZone.tileNames;
var completeTiles = _userMapData.completeTiles;
int count = 0;
foreach (var tile in curTiles.SelectMany(tileName => zoneCur.tiles.Where(tile => string.Equals(tile.tileId, tileName))))
{
if (count != 0)
{
tile.tileStatus = 0;
continue;
}
tile.tileStatus = completeTiles.Contains(tile.tileId) ? 2 : 1;
if (tile.tileStatus == 1)
{
tile.visual.GetComponent<HexGridTiler>().UpdateVisual();
count++;
}
Log($"curTile -> {tile.tileId} : {tile.tileStatus}");
}
}
private void StartNextZone()
{
Log("StartNextZone() -> ");
_userMapData.zoneCurrentIndex++;
_userMapData.subZoneCurrentIndex = 0;
_userMapData.completeTiles.Clear();
//
if (_userMapData.zoneCurrentIndex > _zones.Count -1)
{
DoSave();
return;
}
var zoneCur = _zones[_userMapData.zoneCurrentIndex];
Log($"解锁 Zone -> {zoneCur.zoneId}");
zoneCur.isUnlocked = true;
UpdateCurrZoneStatus();
}
private void StartSubZone()
{
var subZoneCurrentIndex = ++_userMapData.subZoneCurrentIndex;
var zoneCur = _zones[_userMapData.zoneCurrentIndex];
var subZone = zoneCur.subZones[subZoneCurrentIndex];
var curTiles = subZone.tileNames;
var completeTiles = _userMapData.completeTiles;
completeTiles.Clear();
Log($"StartSubZone -> {subZoneCurrentIndex} {subZone.subZoneId} ");
// 查找第一个地块,激活
int count = 0;
foreach (var tile in curTiles.SelectMany(tileName => zoneCur.tiles.Where(tile => string.Equals(tile.tileId, tileName))))
{
if (count == 0)
{
tile.tileStatus = 1;
}
count++;
var hexGridTiler = tile.visual.GetComponent<HexGridTiler>();
if (hexGridTiler)
{
hexGridTiler.UpdateVisual();
}
// tile.visual.GetComponent<HexGridTiler>().UpdateVisual();
Log($"StartSubZone -> curTile -> {tile.tileId} {tile.tileStatus}");
}
}
private static List<string> GetNeighborsInZone(HexTile hexTile,HexZone zone, HexSubZone subZone)
{
// foreach (var neighbor in neighbors)
// {
// // var x = tile.tileCoords.x + neighbor.x;
// // var y = tile.tileCoords.y + neighbor.y;
// Vector2Int user = HexUtils.GetNeighborList(tile,i);
// var tileCoord = new Vector2Int(x,y);
// var tileName = HexUtils.MakeTileId(tileCoord);
// list.AddRange(from elem in zone.tiles where elem.tileCoords == tileCoord select tileName);
// }
// var listNeighbor = ;
var listNeighbor = HexUtils.GetNeighborList(hexTile).Select(HexUtils.MakeTileId).ToList();
return listNeighbor.Intersect(subZone.tileNames).ToList();
// return listNeighbor.Select(HexUtils.MakeTileId).Where(tileId => zone.tiles.Any(tile => string.Equals(tile.tileId, tileId))).ToList();
}
// 返回邻居,需要使用HexMatrix 整体流程
// private List<string> GetNeighbors(HexTile tile)
// {
// Dictionary<string, List<string>> colls = new()
// {
// { "cube001", new List<string> { "cube002" } },
// { "cube002", new List<string> { "cube001","cube003","cube004","cube009","cube008" } },
// { "cube003", new List<string> {"cube002","cube009","cube010"}},
// { "cube004", new List<string> {"cube001","cube002","cube005","cube007","cube008"}},
// { "cube005", new List<string> {"cube004","cube006","cube007"}},
// { "cube006", new List<string> {"cube014","cube013","cube007","cube005"}},
// { "cube007", new List<string> {"cube005","cube006","cube004","cube008","cube012","cube013"}},
// { "cube008", new List<string> {"cube002","cube007","cube004","cube009","cube012","cube011"}},
// { "cube009", new List<string> {"cube003","cube010","cube016","cube008","cube002","cube011"}},
// { "cube010", new List<string> {"cube003","cube009","cube016"}},
// { "cube011", new List<string> {"cube008","cube009","cube016","cube018","cube015","cube012"}},
// { "cube012", new List<string> {"cube007","cube008","cube011","cube013","cube015"}},
// { "cube013", new List<string> {"cube014","cube006","cube007","cube013","cube012"}},
// { "cube014", new List<string> {"cube013","cube006"}},
// { "cube015", new List<string> {"cube012","cube019","cube017","cube011","cube018"}},
// { "cube016", new List<string> {"cube009","cube010","cube011","cube018","cube020"}},
// { "cube017", new List<string> {"cube015","cube019"}},
// { "cube018", new List<string> {"cube015","cube016","cube019","cube021","cube011","cube020"}},
// { "cube019", new List<string> {"cube015","cube018","cube021","cube017"}},
// { "cube020", new List<string> {"cube016","cube018","cube021"}},
// { "cube021", new List<string> {"cube018","cube019","cube020"}},
//
// };
// return colls[tile.tileId];
// }
// 解锁子区域,激活第一块
private void UnlockSubZone(int zoneId, int subZoneId)
{
}
// private void UpdateUserData()
// {
//
// }
private void DoSave()
{
//const string saveKey = HexMapManager.SaveKey;
//PlayFabMgr.Instance.UpdateUserDataValue(saveKey, JsonConvert.SerializeObject(_userMapData));
_hexMapManager.SaveUserData();
}
private static void Log(object t)
{
Debug.Log($"<color=yellow>HexZoneManager -> {t} </color>");
}
public HexTile GetTileByName(int zoneId, string tileName)
{
var zone = GetZoneById(zoneId);
return zone.tiles.FirstOrDefault(hexTile => hexTile.tileId.Equals(tileName));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7ae8f8d112414dc3a8dedb74b921d849
timeCreated: 1757320050

View File

@@ -0,0 +1,40 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace HexGrid
{
public interface IZoneManager
{
void UpdateZoneStatus();
}
public interface IZoneController
{
void UpdateZoneStatus();
void OnHexCellClicked(HexTile tiler);
}
public interface IHexGridMediatable
{
void SetMediator(IHexGridMediator mediator);
}
public interface IHexGridMediator
{
void RegisterZoneManager(HexZoneManager controller);
void RegisterBuildingManager(BuildingManager buildManager);
void OnHexCellClicked(HexTile tiler);
Task OnActiveNextTile(HexTile tiler);
// 读取数据 zone
void LoadZones(List<HexZone> zones);
//读取数据Buildings
void LoadBuildings(List<HexBuilding> buildings);
Task CheckBuildingAfterBuild(HexTile tile);
void CheckBuildingAfterBuildImme(HexTile tile);
void Build(int zoneId,BuildingData buildingData,bool isImme);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8365258c02014c51b524ed016823a444
timeCreated: 1757391762

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d7e2493464c54eb6aa493866835f7201
timeCreated: 1757907441

View File

@@ -0,0 +1,72 @@
using System.Collections.Generic;
using UnityEngine;
namespace HexGrid.MapHelper
{
public static class HexUtils
{
public static readonly Vector2Int[] neighbors =
{
new Vector2Int(1, 0),
new Vector2Int(+1, 0),
new Vector2Int(+1, -1),
new Vector2Int(0, -1),
new Vector2Int(-1, 0),
new Vector2Int(-1, +1),
new Vector2Int(0, +1)
};
public static Vector3 DetermineTileSize(GameObject tileObject,out Bounds bounds)
{
var meshFilter = tileObject.GetComponentInChildren<MeshFilter>(true);
bounds = meshFilter.sharedMesh.bounds;
return new Vector3(bounds.size.x, 0,bounds.size.y);
}
// 构建地块Id
public static string MakeTileId(Vector2Int tileCoords)
{
var x = tileCoords.x;
var y = tileCoords.y;
return $"tile:({x},{y})";
}
// 通过id解析地块
public static Vector2Int ParseTileId(string tileString)
{
const string prefix = "tile:";
if (string.IsNullOrEmpty(tileString))
return Vector2Int.zero;
// 检查是否以 "tile:" 开头
if (!tileString.StartsWith(prefix))
return Vector2Int.zero;
int startIndex = prefix.Length;
int endIndex = tileString.Length - 1;
if (endIndex <= startIndex)
return Vector2Int.zero;
var coordsPart = tileString.Substring(startIndex,endIndex);
var coords = coordsPart.Split(',');
if (coords.Length != 2)
return Vector2Int.zero;
// 尝试解析 x 和 y 坐标
if (int.TryParse(coords[0].Trim(), out var x) &&
int.TryParse(coords[1].Trim(), out var y))
{
return new Vector2Int(x, y);
}
return Vector2Int.zero;
}
// 获得当前模块的邻接Tile
public static List<Vector2Int> GetNeighborList(HexTile tile)
{
var list = new List<Vector2Int>();
foreach (var neighbor in neighbors)
{
var x = tile.tileCoords.x + neighbor.x;
var y = tile.tileCoords.y + neighbor.y;
list.Add(new Vector2Int(x,y));
}
return list;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dd82ff490dfb40e08b4cb74012fcfd72
timeCreated: 1757937441

View File

@@ -0,0 +1,202 @@
using System;
using HexGrid.Math;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;
namespace HexGrid.MapHelper
{
public enum GridType
{
PointyTop,
// 当前仅支持尖顶
// FlatTop,
}
public class MapRoot : MonoBehaviour
{
//
[SerializeField] private GridType gridType;
// 格子的大小
[SerializeField] private float gridSize = 1.52f;
//
[SerializeField] private GameObject standGridPrefab;
private GameObject go;
private void Awake()
{
if (standGridPrefab)
{
go = Instantiate(standGridPrefab, Vector3.zero, Quaternion.identity, this.transform);
go.name = "grid_00";
go.SetActive(false);
var size = DetermineTileSize(go, out var bounds);
gridSize = size.z * 0.5f;
}
// HexMath.SetHexMapOrigin( new Vector3(1.32f, 0.00f, -2.28f));
HexMath.SetHexMapOrigin(Vector3.zero);
HexMath.SetGridSize(gridSize);
}
private void Start()
{
// var worldPos0 = HexMath.AxialToPixel(0, 0, gridSize);
// var worldPos1 = HexMath.AxialToPixel(0, 1, gridSize);
// Log($"worldPos0 -> {worldPos0} ,worldPos1 ->{worldPos1}");
//
// //
// DrawHex(worldPos0,gridSize);
// DrawHex(worldPos1,gridSize);
// for (int i = -5; i < 5; i++)
// {
// for (int j = -5; j < 5; j++)
// {
// Log($"Grid-({i},{j}): {HexMath.AxialToPixel(i,j)}");
// }
// }
// Log($"World -> -1.29904,0,2.25 -> {HexMath.PixelToAxial(new Vector3(-1.29904f,0,2.25f))}");
// Log($"World -> 0,0,0 -> {HexMath.PixelToAxial(Vector3.zero)}");
// Log($"(1,1) -> World -> 3.95, 0.00, 2.28 -> {HexMath.PixelToAxial(new Vector3(3.95f, 0.00f, 2.28f))}");
// Log($"(1,-1)-> World -> 1.32, 0.00, -2.28 -> {HexMath.PixelToAxial(new Vector3(1.32f, 0.00f, -2.28f))}");
// Log($"(-1,1)-> World -> -1.32, 0.00, 2.28 -> {HexMath.PixelToAxial(new Vector3(-1.32f, 0.00f, 2.28f))}");
// Log($"(-1,-1)-> World ->-3.95, 0.00, -2.28 -> {HexMath.PixelToAxial(new Vector3(-3.95f, 0.00f, -2.28f))}");
}
private void Update()
{
// var worldPos0 = HexMath.AxialToPixel(0, 0 );
// var worldPos1 = HexMath.AxialToPixel(0, 1);
// var worldPos2 = HexMath.AxialToPixel(0, 2);
//
// var worldPos10 = HexMath.AxialToPixel(1, 0, gridSize);
// var worldPos20 = HexMath.AxialToPixel(2, 0, gridSize);
// var worldPos30 = HexMath.AxialToPixel(3, 0, gridSize);
//
// var worldPos11 = HexMath.AxialToPixel(1, 1, gridSize);
// var worldPos21 = HexMath.AxialToPixel(2, 1, gridSize);
// var worldPos31 = HexMath.AxialToPixel(3, 1, gridSize);
//
// Log(HexMath.PixelToAxial(worldPos0,gridSize));
// Log(HexMath.PixelToAxial(worldPos1,gridSize));
// Log(HexMath.PixelToAxial(worldPos2,gridSize));
// Log(HexMath.PixelToAxial(worldPos10,gridSize));
// Log(HexMath.PixelToAxial(worldPos20,gridSize));
// Log(HexMath.PixelToAxial(worldPos30,gridSize));
// Log(HexMath.PixelToAxial(worldPos11,gridSize));
// Log(HexMath.PixelToAxial(worldPos21,gridSize));
// Log(HexMath.PixelToAxial(worldPos31,gridSize));
//
//
//
// DrawHex(worldPos0,gridSize);
// DrawHex(worldPos1,gridSize);
// DrawHex(worldPos2,gridSize);
//
// DrawHex(worldPos10,gridSize,Color.green);
// DrawHex(worldPos20,gridSize,Color.green);
// DrawHex(worldPos30,gridSize,Color.green);
//
// DrawHex(worldPos11,gridSize,Color.yellow);
// DrawHex(worldPos21,gridSize,Color.yellow);
// DrawHex(worldPos31,gridSize,Color.yellow);
//
// DrawHex(HexMath.AxialToPixel(-1,1,gridSize),gridSize,Color.blue);
// DrawHex(HexMath.AxialToPixel(-2,1,gridSize),gridSize,Color.blue);
// DrawHex(HexMath.AxialToPixel(-3,1,gridSize),gridSize,Color.blue);
// DrawHex(HexMath.AxialToPixel(-4,1,gridSize),gridSize,Color.blue);
// DrawHex(HexMath.AxialToPixel(-5,1,gridSize),gridSize,Color.blue);
//
}
private Vector3 DetermineTileSize(GameObject tileObject,out Bounds bounds)
{
var meshFilter = tileObject.GetComponentInChildren<MeshFilter>(true);
bounds = meshFilter.sharedMesh.bounds;
Log($"DetermineTileSize -> {bounds} {bounds.size}");
// return new Vector3((bounds.extents.x * 2) * 0.75f,0, bounds.extents.y * 2);
return new Vector3(bounds.size.x, 0,bounds.size.y);
// return bounds;
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
// GameObject[] selectedObjects = Selection.gameObjects;
// var tile = GameObject.Find("grid_00");
// var size = DetermineTileSize(tile,out var bounds);
// Gizmos.color = Color.yellow;
// Gizmos.DrawWireCube(Vector3.zero, bounds.size * 2);
// Gizmos.DrawWireCube(Vector3.zero, size);
}
private void OnDrawGizmos()
{
// if (Application.isPlaying && Application.isEditor)
// {
// Vector3 origin = Vector3.zero;
//
// if (go)
// {
// var size = DetermineTileSize(go, out var bounds);
// Gizmos.color = Color.yellow;
// Gizmos.DrawWireCube(origin, size);
// gridSize = size.z * 0.5f;
// Gizmos.color = Color.magenta;
// DrawHex(origin, gridSize);
// }
// }
// Gizmos.color = Color.green;
// Gizmos.DrawWireCube(Vector3.zero, size);
}
#endif
// HexGrid
private void DrawGrid(int q,int r)
{
// 00,01
// Vector3 gridPos = HexMath.AxialToPixel(q, r, gridSize);
}
private void DrawHex(Vector3 center, float size,Color color = default)
{
color = color == default ? Color.magenta : color;
var angleDelta = gridType == GridType.PointyTop ? 30 : 0;
var corners = new Vector3[7];
for (var i = 0; i < 6; i++)
{
float angleDeg = 60 * i + angleDelta;
var angleRad = Mathf.Deg2Rad * angleDeg;
corners[i] = new Vector3(
center.x + size * Mathf.Cos(angleRad),
0,
center.z + size * Mathf.Sin(angleRad)
);
}
corners[6] = corners[0];
for (var i = 0; i < 6; i++)
{
// Gizmos.DrawLine(corners[i], corners[i + 1]);
Debug.DrawLine(corners[i],corners[i + 1],color);
}
}
// 画格子
private void DrawHexGrid(Vector3 origin)
{
Log("DrawHexGrid-> {}");
}
// MapRoot
private static void Log(object t)
{
Debug.Log($"<color=yellow> MapRoot -> {t} </color>");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3c66c391ffdb4fdb8340fcdcffcb0b47
timeCreated: 1757907477

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 29adabee47ea49d0b993cd48eeffa31c
timeCreated: 1757904468

View File

@@ -0,0 +1,29 @@
namespace HexGrid.Math
{
public struct HexCoord {
public int q; // axial q(col)
public int r; // axial r(row)
public HexCoord(int q, int r) { this.q = q; this.r = r; }
public static readonly HexCoord[] neighbors = {
new HexCoord(+1, 0),
new HexCoord(+1, -1),
new HexCoord(0, -1),
new HexCoord(-1, 0),
new HexCoord(-1, +1),
new HexCoord(0, +1)
};
public HexCoord Neighbor(int i) => new HexCoord(q + neighbors[i].q, r + neighbors[i].r);
public override bool Equals(object obj) {
if (!(obj is HexCoord)) return false;
HexCoord o = (HexCoord)obj;
return q == o.q && r == o.r;
}
//??
public override int GetHashCode() => q * 73856093 ^ r * 19349663;
public override string ToString() => $"({q},{r})";
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9d569dbffffb41d382ed771b3aee08cf
timeCreated: 1757055757

View File

@@ -0,0 +1,121 @@
using System.Numerics;
using PlayFab.Internal;
using UnityEngine;
using Vector3 = UnityEngine.Vector3;
// 项目中主要采用的是尖顶的 pointy-top ,平顶的 Flat-top 后续在考虑
namespace HexGrid.Math
{
public static class HexMath {
//
private static float _gridSize;
private static Vector3 _originPos = Vector3.zero;
// size = hex radius (distance center->corner)
// pointy-top axial coordinates on XZ plane
// Arial坐标 ->World
public static Vector3 AxialToPixel(int q, int r )
{
var x = _gridSize * Mathf.Sqrt(3f) * (q + r / 2f);
var z = _gridSize * 1.5f * r;
return new Vector3(x, 0, z) + _originPos;
}
//Arial -> World
public static Vector2Int PixelToAxial(Vector3 pos)
{
var posRelative = pos - _originPos;
Debug.Log($"posRelative->{pos}-> {posRelative}");
var q = (Mathf.Sqrt(3f) / 3f * posRelative.x - 1f / 3f * posRelative.z) / _gridSize;
var r = (2f / 3f * posRelative.z) / _gridSize;
return HexRound(q, r);
}
private static Vector2Int HexRound(float q, float r)
{
float x = q;
float z = r;
float y = -x - z;
int rx = Mathf.RoundToInt(x);
int ry = Mathf.RoundToInt(y);
int rz = Mathf.RoundToInt(z);
float x_diff = Mathf.Abs(rx - x);
float y_diff = Mathf.Abs(ry - y);
float z_diff = Mathf.Abs(rz - z);
if (x_diff > y_diff && x_diff > z_diff)
{
rx = -ry - rz;
}
else if (y_diff > z_diff)
{
ry = -rx - rz;
}
else
{
rz = -rx - ry;
}
return new Vector2Int(rx, rz);
}
public static Vector3 HexToWorld(HexCoord h, float size, float height=0f) {
float x = size * (Mathf.Sqrt(3f) * h.q + Mathf.Sqrt(3f) / 2f * h.r);
float z = size * (3f/2f * h.r);
return new Vector3(x, height, z);
}
// worldPos: arbitrary world point (x,y,z). We project onto XZ plane and compute axial coords.
public static HexCoord WorldToHexXZ(Vector3 worldPos, float size) {
float x = worldPos.x;
float z = worldPos.z;
float qf = (Mathf.Sqrt(3f)/3f * x - 1f/3f * z) / size;
float rf = (2f/3f * z) / size;
return CubeRound(AxialToCube(qf, rf));
}
private struct Cube { public float x,y,z; public Cube(float x,float y,float z){this.x=x;this.y=y;this.z=z;} }
private static Cube AxialToCube(float q, float r) {
float x = q;
float z = r;
float y = -x - z;
return new Cube(x,y,z);
}
private static HexCoord CubeRound(Cube c) {
int rx = Mathf.RoundToInt(c.x);
int ry = Mathf.RoundToInt(c.y);
int rz = Mathf.RoundToInt(c.z);
float x_diff = Mathf.Abs(rx - c.x);
float y_diff = Mathf.Abs(ry - c.y);
float z_diff = Mathf.Abs(rz - c.z);
if (x_diff > y_diff && x_diff > z_diff) rx = -ry - rz;
else if (y_diff > z_diff) ry = -rx - rz;
else rz = -rx - ry;
return new HexCoord(rx, rz); // axial q = x, r = z
}
public static int Distance(HexCoord a, HexCoord b) {
int ax = a.q, az = a.r, ay = -ax - az;
int bx = b.q, bz = b.r, by = -bx - bz;
return Mathf.Max(Mathf.Abs(ax - bx), Mathf.Abs(ay - by), Mathf.Abs(az - bz));
}
public static void SetGridSize(float gridSize)
{
_gridSize = gridSize;
}
public static float GetGridSize() => _gridSize;
public static void SetHexMapOrigin(Vector3 zero)
{
_originPos = new Vector3(zero.x,0,zero.z);
}
public static void Log(object t)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 97acc7f3343b470da16958a6ebcfa077
timeCreated: 1757055818

View File

@@ -0,0 +1,27 @@
using UnityEngine;
namespace HexGrid
{
// 地形管理
// 未解锁的时候,放个黑块
public class TerrainManager : MonoBehaviour,IHexGridMediatable
{
[SerializeField] private Material unlockedMaterial;
[SerializeField] private Material lockedMaterial;
public void UpdateTerrainForZone(int zoneId)
{
var obj = GameObject.Find("ZoneTerrain_" + zoneId);
if (obj != null)
{
var rend = obj.GetComponent<Renderer>();
if (rend != null) rend.material = unlockedMaterial;
}
}
public void SetMediator(IHexGridMediator mediator)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fef103e5ae714810953181d24396bd77
timeCreated: 1757322413