전장의 안개 로직 개선
미탐험지역은 검게 덮음. 탐험된 지역은 어두운 색, 보고 있는 블록은 밝은색
This commit is contained in:
45
Assets/Materials/FogHeightMask.shader
Normal file
45
Assets/Materials/FogHeightMask.shader
Normal file
@@ -0,0 +1,45 @@
|
||||
// 파일명: FogHeightMask.shader
|
||||
Shader "Custom/FogHeightMask"
|
||||
{
|
||||
Properties { _MainColor ("Fog Color", Color) = (0,0,0,1) }
|
||||
SubShader {
|
||||
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
|
||||
Blend SrcAlpha OneMinusSrcAlpha // 투명도 사용 설정
|
||||
ZWrite Off
|
||||
|
||||
Pass {
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct v2f {
|
||||
float4 vertex : SV_POSITION;
|
||||
float3 worldPos : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _GlobalFogTex;
|
||||
float _FogWorldSize;
|
||||
float _GroundLevelY; // 매니저가 보내줄 높이값
|
||||
float4 _MainColor;
|
||||
|
||||
v2f vert (appdata_base v) {
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag (v2f i) : SV_Target {
|
||||
// [핵심 로직] 현재 픽셀의 높이가 지상 레벨보다 높으면 투명하게!
|
||||
if (i.worldPos.y > _GroundLevelY) return fixed4(0,0,0,0);
|
||||
|
||||
float2 uv = i.worldPos.xz / _FogWorldSize + 0.5;
|
||||
fixed4 fogData = tex2D(_GlobalFogTex, uv);
|
||||
|
||||
return fixed4(_MainColor.rgb, fogData.a); // 안개 텍스처의 알파값(검정 정도) 적용
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Assets/Materials/FogHeightMask.shader.meta
Normal file
9
Assets/Materials/FogHeightMask.shader.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d8b19748651ad54a9350f716d0f7046
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
139
Assets/Materials/FogMaterial.mat
Normal file
139
Assets/Materials/FogMaterial.mat
Normal file
@@ -0,0 +1,139 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &-8250424374119353640
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 11
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
|
||||
version: 10
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: FogMaterial
|
||||
m_Shader: {fileID: 4800000, guid: 7d8b19748651ad54a9350f716d0f7046, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses:
|
||||
- MOTIONVECTORS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AddPrecomputedVelocity: 0
|
||||
- _AlphaClip: 0
|
||||
- _AlphaToMask: 0
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 1
|
||||
- _BlendOp: 0
|
||||
- _BumpScale: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _Cull: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _DstBlendAlpha: 0
|
||||
- _EnvironmentReflections: 1
|
||||
- _GlossMapScale: 0
|
||||
- _Glossiness: 0
|
||||
- _GlossyReflections: 0
|
||||
- _Metallic: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.005
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _SampleGI: 0
|
||||
- _Smoothness: 0.5
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _SrcBlendAlpha: 1
|
||||
- _Surface: 0
|
||||
- _WorkflowMode: 1
|
||||
- _XRMotionVectorsPass: 1
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _Color: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _MainColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
8
Assets/Materials/FogMaterial.mat.meta
Normal file
8
Assets/Materials/FogMaterial.mat.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3deb4a29e039554439a14651844edec2
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -99,8 +99,8 @@ MonoBehaviour:
|
||||
myFilter: {fileID: 8145339523266927793}
|
||||
myRenderer: {fileID: 1476831640209354726}
|
||||
dropScale: 0.3
|
||||
jumpForce: 2
|
||||
spreadForce: 1
|
||||
jumpForce: 0.4
|
||||
spreadForce: 0.1
|
||||
rotationSpeed: 100
|
||||
--- !u!54 &6380490795664845462
|
||||
Rigidbody:
|
||||
|
||||
@@ -305,7 +305,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
|
||||
GlobalObjectIdHash: 4268073897
|
||||
GlobalObjectIdHash: 856270328
|
||||
InScenePlacedSourceGlobalObjectIdHash: 0
|
||||
DeferredDespawnTick: 0
|
||||
Ownership: 1
|
||||
@@ -335,7 +335,7 @@ MonoBehaviour:
|
||||
rotationSpeed: 10
|
||||
jumpHeight: 1.5
|
||||
gravity: -19.62
|
||||
interactRange: 3
|
||||
interactRange: 1
|
||||
interactableLayer:
|
||||
serializedVersion: 2
|
||||
m_Bits: 25600
|
||||
@@ -359,7 +359,7 @@ MonoBehaviour:
|
||||
crosshairUI: {fileID: 0}
|
||||
idleCrosshair: {fileID: 2628378444897590106, guid: 174f7cb20aaa6d4409b788a700a925ad, type: 3}
|
||||
targetCrosshair: {fileID: -5662625722731528258, guid: 7652364ca249c3144813de7eb3d1b129, type: 3}
|
||||
visionRadius: 5
|
||||
visionRadius: 2
|
||||
--- !u!114 &106528027568436521
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -426,7 +426,7 @@ MonoBehaviour:
|
||||
- IsCrossFadeExit: 0
|
||||
Layer: 0
|
||||
OriginatingState: -2089788878
|
||||
DestinationState: -437043462
|
||||
DestinationState: 581277072
|
||||
TransitionDuration: 0.1
|
||||
TriggerNameHash: 1080829965
|
||||
TransitionIndex: 0
|
||||
@@ -448,6 +448,10 @@ MonoBehaviour:
|
||||
NameHash: -662453572
|
||||
Synchronize: 1
|
||||
ParameterType: 9
|
||||
- name: ActionSpeed
|
||||
NameHash: 1673499952
|
||||
Synchronize: 1
|
||||
ParameterType: 1
|
||||
AnimatorParametersExpanded: 0
|
||||
--- !u!114 &5271692149337681293
|
||||
MonoBehaviour:
|
||||
|
||||
@@ -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