133 lines
4.1 KiB
C#
133 lines
4.1 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using UnityEngine;
|
|
|
|
namespace Cryville.EEW.Unity.Map {
|
|
[RequireComponent(typeof(Camera))]
|
|
sealed class CameraController : MonoBehaviour {
|
|
Camera _camera;
|
|
|
|
[SerializeField]
|
|
Transform m_layerTile;
|
|
[SerializeField]
|
|
MapElementManager m_layerElement;
|
|
[SerializeField]
|
|
MapElementManager m_layerElementSub;
|
|
[SerializeField]
|
|
GameObject m_prefabTile;
|
|
[SerializeField]
|
|
GameObject m_prefabBitmapHolder;
|
|
[SerializeField]
|
|
int m_maxMapTileZoom = 10;
|
|
[SerializeField]
|
|
bool m_isEditor;
|
|
|
|
MapTileCacheManager _tiles;
|
|
float _elementLayerZ;
|
|
|
|
void Start() {
|
|
_camera = GetComponent<Camera>();
|
|
_tiles = m_isEditor ? new EditorMapTileCacheManager() : new MapTileCacheManager();
|
|
_tiles.ExtraCachedZoomLevel = 20;
|
|
_tiles.Parent = m_layerTile;
|
|
_tiles.PrefabTile = m_prefabTile;
|
|
_tiles.PrefabBitmapHolder = m_prefabBitmapHolder;
|
|
_tiles.CacheDir = Application.temporaryCachePath;
|
|
_camera.orthographicSize = 0.5f / MathF.Max(1, (float)_camera.pixelWidth / _camera.pixelHeight);
|
|
if (m_layerElement != null) _elementLayerZ = m_layerElement.transform.position.z;
|
|
_mapElementUpdated = true;
|
|
}
|
|
void OnDestroy() {
|
|
_tiles.Dispose();
|
|
}
|
|
|
|
float Scale {
|
|
get => _camera.orthographicSize;
|
|
set => _camera.orthographicSize = Mathf.Clamp(value, 1e-3f, 0.5f / MathF.Max(1, (float)_camera.pixelWidth / _camera.pixelHeight));
|
|
}
|
|
|
|
static readonly Rect _viewportRect = new(0, 0, 1, 1);
|
|
Vector3? ppos;
|
|
bool _mapElementUpdated;
|
|
void Update() {
|
|
bool flag = false;
|
|
var cpos = Input.mousePosition;
|
|
bool isMouseInViewport = _viewportRect.Contains(_camera.ScreenToViewportPoint(Input.mousePosition));
|
|
if (Input.GetMouseButtonDown(0) && isMouseInViewport) {
|
|
ppos = cpos;
|
|
}
|
|
if (Input.GetMouseButton(0) && ppos is Vector3 pos0) {
|
|
var delta = _camera.ScreenToWorldPoint(pos0) - _camera.ScreenToWorldPoint(cpos);
|
|
transform.position += delta;
|
|
ppos = cpos;
|
|
flag = true;
|
|
}
|
|
if (Input.GetMouseButtonUp(0)) {
|
|
ppos = null;
|
|
}
|
|
if (Input.mouseScrollDelta.y != 0 && isMouseInViewport) {
|
|
Scale *= Mathf.Pow(2, -Input.mouseScrollDelta.y / 8);
|
|
flag = true;
|
|
}
|
|
if (_mapElementUpdated) {
|
|
_mapElementUpdated = false;
|
|
ZoomToMapElement();
|
|
flag = true;
|
|
}
|
|
if (flag) {
|
|
UpdateTransform(); // Ensure UpdateTransform is called at most once per frame for tiles to unload correctly
|
|
}
|
|
}
|
|
void ZoomToMapElement() {
|
|
var aabb = m_layerElement != null ? m_layerElement.AABB : null;
|
|
if (aabb is not RectangleF b) return;
|
|
if (b.Width * _camera.pixelHeight < _camera.pixelWidth * b.Height)
|
|
Scale = b.Height;
|
|
else
|
|
Scale = b.Width * _camera.pixelHeight / _camera.pixelWidth;
|
|
Scale *= 0.6f;
|
|
if (Scale < 0.01f) Scale = 0.01f;
|
|
transform.localPosition = new PointF(b.X + b.Width / 2, b.Y + b.Height / 2).ToVector2();
|
|
}
|
|
public void OnMapElementUpdated() {
|
|
_mapElementUpdated = true;
|
|
}
|
|
|
|
void UpdateTransform() {
|
|
float h = _camera.orthographicSize * 2;
|
|
float w = h * _camera.pixelWidth / _camera.pixelHeight;
|
|
float vz = 1 / h * _camera.pixelHeight;
|
|
float nx = transform.position.x % 1;
|
|
if (nx < 0) nx += 1;
|
|
transform.localPosition = new(nx, Math.Clamp(transform.position.y, h / 2 - 1, -h / 2), -20);
|
|
|
|
var bounds = new Bounds((Vector2)transform.position, new Vector2(w, h));
|
|
int zoom = Math.Clamp((int)Math.Log(vz / 256, 2) + 1, 0, m_maxMapTileZoom);
|
|
int zoomScale = 1 << zoom;
|
|
_tiles.MoveTo(
|
|
new(Mathf.FloorToInt(bounds.min.x * zoomScale), Mathf.FloorToInt(-bounds.max.y * zoomScale), zoom),
|
|
new(Mathf.CeilToInt(bounds.max.x * zoomScale), Mathf.CeilToInt(-bounds.min.y * zoomScale), zoom)
|
|
);
|
|
|
|
if (m_layerElement != null) {
|
|
m_layerElement.Scale = h;
|
|
}
|
|
if (m_layerElementSub != null) {
|
|
m_layerElementSub.Scale = h;
|
|
|
|
if (nx - w / 2 < 0) {
|
|
m_layerElementSub.gameObject.SetActive(true);
|
|
m_layerElementSub.transform.localPosition = new(-1, 0, _elementLayerZ);
|
|
}
|
|
else if (nx + w / 2 > 1) {
|
|
m_layerElementSub.gameObject.SetActive(true);
|
|
m_layerElementSub.transform.localPosition = new(1, 0, _elementLayerZ);
|
|
}
|
|
else {
|
|
m_layerElementSub.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|