전장의 안개 로직 개선
미탐험지역은 검게 덮음. 탐험된 지역은 어두운 색, 보고 있는 블록은 밝은색
This commit is contained in:
@@ -6,28 +6,83 @@ public class FogOfWarManager : MonoBehaviour
|
||||
public static FogOfWarManager Instance;
|
||||
|
||||
[Header("Settings")]
|
||||
public RenderTexture fogMaskTexture; // 쉐이더에 전달될 텍스처
|
||||
public Material fogMaterial; // 검은 안개 머티리얼
|
||||
public float revealRadius = 5f; // 밝혀지는 반경
|
||||
public float worldSize = 100f; // 1단계에서 만든 Plane의 크기와 맞춤
|
||||
public int textureSize = 512; // 안개 해상도 (높을수록 부드러움)
|
||||
public float revealRadius = 5f; // 밝혀지는 반경
|
||||
|
||||
void Awake() => Instance = this;
|
||||
[Header("Underground Settings")]
|
||||
public float groundLevelY = 0f; // 이 높이보다 낮으면 지하로 간주
|
||||
|
||||
private Texture2D _fogTexture;
|
||||
private Color32[] _pixels;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
InitTexture();
|
||||
}
|
||||
|
||||
private void InitTexture()
|
||||
{
|
||||
// 1. 텍스처 생성 (모두 검은색으로 시작)
|
||||
_fogTexture = new Texture2D(textureSize, textureSize, TextureFormat.RGBA32, false);
|
||||
_pixels = new Color32[textureSize * textureSize];
|
||||
for (int i = 0; i < _pixels.Length; i++) _pixels[i] = new Color32(0, 0, 0, 255);
|
||||
|
||||
_fogTexture.SetPixels32(_pixels);
|
||||
_fogTexture.Apply();
|
||||
|
||||
// 2. 셰이더 전역 변수로 전달 (이름이 중요!)
|
||||
Shader.SetGlobalTexture("_GlobalFogTex", _fogTexture);
|
||||
Shader.SetGlobalFloat("_FogWorldSize", worldSize);
|
||||
|
||||
// 셰이더에 기준 높이 전달
|
||||
Shader.SetGlobalFloat("_GroundLevelY", groundLevelY);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// 멀티플레이어이므로 로컬 플레이어(IsOwner)의 위치만 마스크에 그립니다.
|
||||
if (NetworkManager.Singleton.LocalClient != null)
|
||||
if (NetworkManager.Singleton != null && NetworkManager.Singleton.LocalClient != null)
|
||||
{
|
||||
var localPlayer = NetworkManager.Singleton.LocalClient.PlayerObject;
|
||||
if (localPlayer != null)
|
||||
{
|
||||
UpdateFogMask(localPlayer.transform.position);
|
||||
|
||||
// 추가: 플레이어가 지상에 있을 때는 안개 판을 아예 꺼버릴 수도 있습니다 (선택 사항)
|
||||
// bool isUnderground = localPlayer.transform.position.y < groundLevelY + 2f;
|
||||
// fogOverlayObject.SetActive(isUnderground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFogMask(Vector3 playerPos)
|
||||
{
|
||||
// 플레이어의 월드 좌표를 텍스처 좌표로 변환하여
|
||||
// 해당 지점의 알파값을 0(투명)으로 깎아내는 로직 (Compute Shader나 GPU 가속 권장)
|
||||
// 월드 좌표를 텍스처 좌표로 변환
|
||||
int centerX = Mathf.RoundToInt((playerPos.x / worldSize + 0.5f) * textureSize);
|
||||
int centerZ = Mathf.RoundToInt((playerPos.z / worldSize + 0.5f) * textureSize);
|
||||
int radius = Mathf.RoundToInt((revealRadius / worldSize) * textureSize);
|
||||
|
||||
bool changed = false;
|
||||
for (int y = centerZ - radius; y <= centerZ + radius; y++)
|
||||
{
|
||||
for (int x = centerX - radius; x <= centerX + radius; x++)
|
||||
{
|
||||
if (x >= 0 && x < textureSize && y >= 0 && y < textureSize)
|
||||
{
|
||||
float dist = Vector2.Distance(new Vector2(x, y), new Vector2(centerX, centerZ));
|
||||
if (dist < radius)
|
||||
{
|
||||
int idx = y * textureSize + x;
|
||||
if (_pixels[idx].a != 0)
|
||||
{ // 아직 안 밝혀진 곳만
|
||||
_pixels[idx] = new Color32(0, 0, 0, 0); // 투명하게!
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed) { _fogTexture.SetPixels32(_pixels); _fogTexture.Apply(); }
|
||||
}
|
||||
}
|
||||
@@ -80,22 +80,33 @@ public class MineableBlock : NetworkBehaviour
|
||||
|
||||
void Update()
|
||||
{
|
||||
// 1. 서버에서 한 번이라도 발견되었고
|
||||
// 2. 최근(0.2초 이내)에 플레이어의 시야 범위 안에 있었다면 렌더러를 켭니다.
|
||||
bool isCurrentlyVisible = (Time.time - _lastVisibleTime) < VisibilityThreshold;
|
||||
|
||||
if (_renderer != null)
|
||||
// 1. 이미 발견된 블록인지는 서버 변수(isDiscovered)로 확인
|
||||
// 2. 현재 내 위치가 안개에서 벗어났는지 확인 (매우 단순화된 로직)
|
||||
if (!isDiscovered.Value)
|
||||
{
|
||||
// 실시간 시야: 발견되었더라도 지금 근처에 플레이어가 없으면 다시 숨깁니다.
|
||||
_renderer.enabled = isDiscovered.Value && isCurrentlyVisible;
|
||||
float dist = Vector3.Distance(transform.position, NetworkManager.Singleton.LocalClient.PlayerObject.transform.position);
|
||||
if (dist < FogOfWarManager.Instance.revealRadius)
|
||||
{
|
||||
// 서버에 "나 발견됐어!"라고 보고
|
||||
RequestRevealServerRpc();
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 이미 발견된 블록만 실시간 시야 연산을 수행합니다.
|
||||
if (!isDiscovered.Value) return;
|
||||
// 3. 비주얼 업데이트: 발견된 적이 있을 때만 렌더러를 켬
|
||||
if (_renderer != null)
|
||||
{
|
||||
_renderer.enabled = isDiscovered.Value;
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RequestRevealServerRpc()
|
||||
{
|
||||
isDiscovered.Value = true;
|
||||
}
|
||||
|
||||
private void UpdateState()
|
||||
{
|
||||
if (_renderer == null) return;
|
||||
@@ -104,7 +115,7 @@ public class MineableBlock : NetworkBehaviour
|
||||
bool isCurrentlyVisible = (Time.time - _lastVisibleTime) < VisibilityThreshold;
|
||||
|
||||
// 3. 상태에 따라 색상과 렌더러 상태를 결정합니다.
|
||||
_renderer.enabled = true; // 발견된 상태이므로 항상 켭니다.
|
||||
if (_renderer.enabled == false) return;
|
||||
|
||||
_renderer.GetPropertyBlock(_propBlock);
|
||||
// 2. 시야 내에 있으면 원본 색상(_originalColor), 멀어지면 어둡게 만든 색상을 적용합니다.
|
||||
|
||||
Reference in New Issue
Block a user