Mineable 블록에 대해 전장의 안개 추가

그림자 안나오는 문제도 해결
This commit is contained in:
2026-01-17 20:14:12 +09:00
parent 443942f6ca
commit 8369e4d42f
47 changed files with 1991 additions and 2730 deletions

View File

@@ -0,0 +1,33 @@
using UnityEngine;
using Unity.Netcode;
public class FogOfWarManager : MonoBehaviour
{
public static FogOfWarManager Instance;
[Header("Settings")]
public RenderTexture fogMaskTexture; // 쉐이더에 전달될 텍스처
public Material fogMaterial; // 검은 안개 머티리얼
public float revealRadius = 5f; // 밝혀지는 반경
void Awake() => Instance = this;
void Update()
{
// 멀티플레이어이므로 로컬 플레이어(IsOwner)의 위치만 마스크에 그립니다.
if (NetworkManager.Singleton.LocalClient != null)
{
var localPlayer = NetworkManager.Singleton.LocalClient.PlayerObject;
if (localPlayer != null)
{
UpdateFogMask(localPlayer.transform.position);
}
}
}
private void UpdateFogMask(Vector3 playerPos)
{
// 플레이어의 월드 좌표를 텍스처 좌표로 변환하여
// 해당 지점의 알파값을 0(투명)으로 깎아내는 로직 (Compute Shader나 GPU 가속 권장)
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4867a597119adb047ab17b69beca76be

View File

@@ -22,8 +22,32 @@ public class MineableBlock : NetworkBehaviour
[SerializeField] private float shakeMagnitude = 0.1f; // 흔들리는 강도
private Coroutine _shakeCoroutine;
private Color _originalColor; // 본래의 색상을 저장할 변수
private float _lastVisibleTime;
private const float VisibilityThreshold = 0.25f;
[Header("Fog Settings")]
[Range(0f, 1f)]
[SerializeField] private float darkIntensity = 0.2f; // 안개 속에서 얼마나 어두워질지 (0: 완전 검정, 1: 원본)
private MaterialPropertyBlock _propBlock;
private NetworkVariable<bool> isDiscovered = new NetworkVariable<bool>(false);
private MeshRenderer _renderer;
void Awake()
{
_renderer = GetComponentInChildren<MeshRenderer>();
_propBlock = new MaterialPropertyBlock();
// 시작 시에는 보이지 않게 설정
if (_renderer != null) _renderer.enabled = false;
_originalColor = _renderer.sharedMaterial.HasProperty("_BaseColor")
? _renderer.sharedMaterial.GetColor("_BaseColor")
: _renderer.sharedMaterial.GetColor("_Color");
_renderer.enabled = false;
// 해당 오브젝트 혹은 자식에게서 Outline 컴포넌트를 찾습니다.
_outline = GetComponentInChildren<Outline>();
_originalPos = transform.localPosition; // 로컬 위치 저장
@@ -45,6 +69,60 @@ public class MineableBlock : NetworkBehaviour
{
_currentHp.Value = maxHp;
}
// 데이터가 동기화될 때 비주얼 업데이트
UpdateVisuals(isDiscovered.Value);
isDiscovered.OnValueChanged += (oldVal, newVal) => {
if (newVal) UpdateState();
};
}
void Update()
{
// 1. 서버에서 한 번이라도 발견되었고
// 2. 최근(0.2초 이내)에 플레이어의 시야 범위 안에 있었다면 렌더러를 켭니다.
bool isCurrentlyVisible = (Time.time - _lastVisibleTime) < VisibilityThreshold;
if (_renderer != null)
{
// 실시간 시야: 발견되었더라도 지금 근처에 플레이어가 없으면 다시 숨깁니다.
_renderer.enabled = isDiscovered.Value && isCurrentlyVisible;
}
// 1. 이미 발견된 블록만 실시간 시야 연산을 수행합니다.
if (!isDiscovered.Value) return;
UpdateState();
}
private void UpdateState()
{
if (_renderer == null) return;
// 2. 현재 시야 안에 있는지 판단합니다.
bool isCurrentlyVisible = (Time.time - _lastVisibleTime) < VisibilityThreshold;
// 3. 상태에 따라 색상과 렌더러 상태를 결정합니다.
_renderer.enabled = true; // 발견된 상태이므로 항상 켭니다.
_renderer.GetPropertyBlock(_propBlock);
// 2. 시야 내에 있으면 원본 색상(_originalColor), 멀어지면 어둡게 만든 색상을 적용합니다.
Color targetColor = isCurrentlyVisible ? _originalColor : _originalColor * darkIntensity;
_propBlock.SetColor("_BaseColor", targetColor);
_renderer.SetPropertyBlock(_propBlock);
}
public void RevealBlock() // 서버에서 호출
{
if (IsServer && !isDiscovered.Value) isDiscovered.Value = true;
}
// 플레이어가 주변을 훑을 때 호출해줄 함수
public void UpdateLocalVisibility()
{
_lastVisibleTime = Time.time;
}
// 서버에서만 대미지를 처리하도록 제한
@@ -119,4 +197,11 @@ public class MineableBlock : NetworkBehaviour
droppedItem.Initialize(dropItemData.itemID);
}
}
private void UpdateVisuals(bool discovered)
{
if (_renderer != null) _renderer.enabled = discovered;
// 발견되지 않은 블록은 아웃라인도 표시되지 않아야 함
if (!discovered && _outline != null) _outline.enabled = false;
}
}

View File

@@ -19,6 +19,8 @@ public class PlayerNetworkController : NetworkBehaviour
[SerializeField] private LayerMask interactableLayer;
[SerializeField] private LayerMask constructionLayer;
[SerializeField] private float buildSpeedMultiplier = 2f;
[SerializeField] private float pickupRadius = 1.5f; // 줍기 인식 범위
[SerializeField] private LayerMask itemLayer; // DroppedItem 레이어 설정
[Header("Mining Settings")]
[SerializeField] private float attackRange = 3.5f;
@@ -32,6 +34,11 @@ public class PlayerNetworkController : NetworkBehaviour
[SerializeField] private UnityEngine.UI.Image crosshairUI;
[SerializeField] private Sprite idleCrosshair;
[SerializeField] private Sprite targetCrosshair;
[Header("Fog of War Settings")]
[SerializeField] private float visionRadius = 5f; // 시야 반경
private float _lastRevealTime;
private RectTransform _crosshairRect;
private MineableBlock _currentTargetBlock; // 현재 강조 중인 블록 저장
private MineableBlock _lastHighlightedBlock;
@@ -110,6 +117,13 @@ public class PlayerNetworkController : NetworkBehaviour
UpdateCrosshairPosition(); // 이 안에서 움직여야 합니다.
UpdateTargetFeedback();
// 매 프레임 실행하면 무거우므로 0.2초마다 주변 시야 갱신
if (Time.time - _lastRevealTime > 0.2f)
{
_lastRevealTime = Time.time;
RevealSurroundings();
}
}
// --- 이동 관련 로직 (기존 유지) ---
@@ -355,6 +369,39 @@ public class PlayerNetworkController : NetworkBehaviour
}
}
private void RevealSurroundings()
{
// 시야 반경 내의 블록 감지
Collider[] hitBlocks = Physics.OverlapSphere(transform.position, visionRadius, mineableLayer);
foreach (var col in hitBlocks)
{
if (col.TryGetComponent<MineableBlock>(out var block))
{
// 1. [로컬] 내 화면에서 이 블록을 보이게 함 (실시간 시야)
block.UpdateLocalVisibility();
// 2. [서버] 아직 발견 안 된 블록이라면 서버에 알림 (공유 안개 해제)
if (IsOwner)
{
RequestRevealServerRpc(block.GetComponent<NetworkObject>().NetworkObjectId);
}
}
}
}
[ServerRpc]
private void RequestRevealServerRpc(ulong blockNetId)
{
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(blockNetId, out var netObj))
{
if (netObj.TryGetComponent<MineableBlock>(out var block))
{
block.RevealBlock(); // 서버가 블록 상태를 true로 변경
}
}
}
private void OnDrawGizmos()
{
if (!Application.isPlaying || !IsOwner) return;