Working materials with two modes - binary and continuous
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: MazeObjectProgressiveTemplate
|
||||
m_Shader: {fileID: 4800000, guid: 732fa975ac924d89bb0078279d2cdb0b, 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: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _AlphaTex:
|
||||
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}
|
||||
- _MaskTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _NormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OutlineTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _RevealMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _EnableExternalAlpha: 0
|
||||
- _IsInVision: 0
|
||||
- _ZWrite: 0
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _RendererColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e053220514a0c64883d9484863533fe
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -504,7 +504,7 @@ PrefabInstance:
|
||||
m_Modifications:
|
||||
- target: {fileID: 6259373434446242904, guid: 07f826f001311e04984c3efc9ee2b897, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: RevealableObject (2)
|
||||
value: RevealableObject_Full_2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6487644332527623320, guid: 07f826f001311e04984c3efc9ee2b897, type: 3}
|
||||
propertyPath: m_SortingOrder
|
||||
@@ -565,7 +565,7 @@ PrefabInstance:
|
||||
m_Modifications:
|
||||
- target: {fileID: 6259373434446242904, guid: 07f826f001311e04984c3efc9ee2b897, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: RevealableObject (1)
|
||||
value: RevealableObject_Full
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6487644332527623320, guid: 07f826f001311e04984c3efc9ee2b897, type: 3}
|
||||
propertyPath: m_SortingOrder
|
||||
@@ -1035,14 +1035,22 @@ PrefabInstance:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 397845239581813408, guid: 07f826f001311e04984c3efc9ee2b897, type: 3}
|
||||
propertyPath: revealMode
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6259373434446242904, guid: 07f826f001311e04984c3efc9ee2b897, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: RevealableObject
|
||||
value: RevealableObject_Partial
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6487644332527623320, guid: 07f826f001311e04984c3efc9ee2b897, type: 3}
|
||||
propertyPath: m_SortingOrder
|
||||
value: 5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6487644332527623320, guid: 07f826f001311e04984c3efc9ee2b897, type: 3}
|
||||
propertyPath: 'm_Materials.Array.data[0]'
|
||||
value:
|
||||
objectReference: {fileID: 2100000, guid: 6e053220514a0c64883d9484863533fe, type: 2}
|
||||
- target: {fileID: 7983424933738472089, guid: 07f826f001311e04984c3efc9ee2b897, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
|
||||
@@ -452,7 +452,6 @@ namespace UI.CardSystem
|
||||
if (cardData == null) return null;
|
||||
|
||||
var allSlots = FindObjectsByType<AlbumCardSlot>(FindObjectsSortMode.None);
|
||||
|
||||
foreach (var slot in allSlots)
|
||||
{
|
||||
if (slot.TargetCardDefinition != null &&
|
||||
|
||||
@@ -72,6 +72,9 @@ namespace AppleHills.Core.Settings
|
||||
[SerializeField] private float followUpdateInterval = 0.1f;
|
||||
[SerializeField] private float followerSpeedMultiplier = 1.2f;
|
||||
[SerializeField] private float heldIconDisplayHeight = 2.0f;
|
||||
|
||||
[Header("Trash Maze Vision")]
|
||||
[SerializeField] private float trashMazeVisionRadius = 8f;
|
||||
|
||||
public float FollowDistance => followDistance;
|
||||
public float ManualMoveSmooth => manualMoveSmooth;
|
||||
@@ -81,6 +84,7 @@ namespace AppleHills.Core.Settings
|
||||
public float FollowUpdateInterval => followUpdateInterval;
|
||||
public float FollowerSpeedMultiplier => followerSpeedMultiplier;
|
||||
public float HeldIconDisplayHeight => heldIconDisplayHeight;
|
||||
public float TrashMazeVisionRadius => trashMazeVisionRadius;
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
@@ -92,6 +96,7 @@ namespace AppleHills.Core.Settings
|
||||
followUpdateInterval = Mathf.Max(0.01f, followUpdateInterval);
|
||||
followerSpeedMultiplier = Mathf.Max(0.1f, followerSpeedMultiplier);
|
||||
heldIconDisplayHeight = Mathf.Max(0f, heldIconDisplayHeight);
|
||||
trashMazeVisionRadius = Mathf.Max(1f, trashMazeVisionRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace AppleHills.Core.Settings
|
||||
float FollowUpdateInterval { get; }
|
||||
float FollowerSpeedMultiplier { get; }
|
||||
float HeldIconDisplayHeight { get; }
|
||||
float TrashMazeVisionRadius { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -14,16 +14,16 @@ namespace Minigames.TrashMaze.Core
|
||||
{
|
||||
public static PulverController Instance { get; private set; }
|
||||
|
||||
[Header("Vision")]
|
||||
[SerializeField] private float visionRadius = 3f;
|
||||
|
||||
// Cached shader property IDs for performance
|
||||
private static readonly int PlayerWorldPosID = Shader.PropertyToID("_PlayerWorldPos");
|
||||
private static readonly int VisionRadiusID = Shader.PropertyToID("_VisionRadius");
|
||||
|
||||
// Vision radius loaded from settings
|
||||
private float _visionRadius;
|
||||
|
||||
// Public accessors for other systems
|
||||
public static Vector2 PlayerPosition => Instance != null ? Instance.transform.position : Vector2.zero;
|
||||
public static float VisionRadius => Instance != null ? Instance.visionRadius : 3f;
|
||||
public static float VisionRadius => Instance != null ? Instance._visionRadius : 8f;
|
||||
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
@@ -46,6 +46,10 @@ namespace Minigames.TrashMaze.Core
|
||||
{
|
||||
var configs = GameManager.GetSettingsObject<IPlayerMovementConfigs>();
|
||||
_movementSettings = configs.TrashMazeMovement;
|
||||
|
||||
// Load vision radius from follower settings
|
||||
_visionRadius = configs.FollowerMovement.TrashMazeVisionRadius;
|
||||
Logging.Debug($"[PulverController] Loaded vision radius from settings: {_visionRadius}");
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@@ -62,7 +66,7 @@ namespace Minigames.TrashMaze.Core
|
||||
private void UpdateShaderGlobals()
|
||||
{
|
||||
Shader.SetGlobalVector(PlayerWorldPosID, transform.position);
|
||||
Shader.SetGlobalFloat(VisionRadiusID, visionRadius);
|
||||
Shader.SetGlobalFloat(VisionRadiusID, _visionRadius);
|
||||
}
|
||||
|
||||
internal override void OnManagedDestroy()
|
||||
@@ -80,7 +84,7 @@ namespace Minigames.TrashMaze.Core
|
||||
/// </summary>
|
||||
public void SetVisionRadius(float radius)
|
||||
{
|
||||
visionRadius = Mathf.Max(0.1f, radius);
|
||||
_visionRadius = Mathf.Max(0.1f, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Scripts/Minigames/TrashMaze/Objects/Editor.meta
Normal file
3
Assets/Scripts/Minigames/TrashMaze/Objects/Editor.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a935f5e791c46df8920c2c33f1c24c0
|
||||
timeCreated: 1765361215
|
||||
@@ -0,0 +1,99 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Minigames.TrashMaze.Objects;
|
||||
|
||||
namespace Minigames.TrashMaze.Editor
|
||||
{
|
||||
[CustomEditor(typeof(RevealableObject))]
|
||||
public class RevealableObjectEditor : UnityEditor.Editor
|
||||
{
|
||||
private RenderTexture _cachedStampTexture;
|
||||
private Texture2D _previewTexture;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
|
||||
// Only show debug info in play mode
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField("Progressive Reveal Debug (Play Mode Only)", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox("Shows the current stamp texture for Progressive reveal mode.", MessageType.Info);
|
||||
|
||||
RevealableObject revealableObject = (RevealableObject)target;
|
||||
|
||||
// Use reflection to get private _revealStampTexture field
|
||||
var field = typeof(RevealableObject).GetField("_revealStampTexture",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
RenderTexture stampTexture = field.GetValue(revealableObject) as RenderTexture;
|
||||
|
||||
if (stampTexture != null)
|
||||
{
|
||||
// Display stamp texture info
|
||||
EditorGUILayout.LabelField("Stamp Texture:", $"{stampTexture.width}x{stampTexture.height}");
|
||||
|
||||
// Show preview of stamp texture
|
||||
GUILayout.Label("Reveal Mask Preview (White = Revealed):");
|
||||
|
||||
// Create preview texture if needed
|
||||
if (_cachedStampTexture != stampTexture || _previewTexture == null)
|
||||
{
|
||||
_cachedStampTexture = stampTexture;
|
||||
|
||||
if (_previewTexture != null)
|
||||
{
|
||||
DestroyImmediate(_previewTexture);
|
||||
}
|
||||
|
||||
_previewTexture = new Texture2D(stampTexture.width, stampTexture.height, TextureFormat.R8, false);
|
||||
_previewTexture.filterMode = FilterMode.Point;
|
||||
}
|
||||
|
||||
// Copy RenderTexture to Texture2D for preview
|
||||
RenderTexture.active = stampTexture;
|
||||
_previewTexture.ReadPixels(new Rect(0, 0, stampTexture.width, stampTexture.height), 0, 0);
|
||||
_previewTexture.Apply();
|
||||
RenderTexture.active = null;
|
||||
|
||||
// Display preview with fixed size
|
||||
float previewSize = 256f;
|
||||
float aspectRatio = (float)stampTexture.height / stampTexture.width;
|
||||
Rect previewRect = GUILayoutUtility.GetRect(previewSize, previewSize * aspectRatio);
|
||||
EditorGUI.DrawPreviewTexture(previewRect, _previewTexture, null, ScaleMode.ScaleToFit);
|
||||
|
||||
// Auto-refresh in play mode
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("No stamp texture found. Make sure object is in Progressive reveal mode.", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Could not access stamp texture via reflection.", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// Clean up preview texture
|
||||
if (_previewTexture != null)
|
||||
{
|
||||
DestroyImmediate(_previewTexture);
|
||||
_previewTexture = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d081993ee424269bf8eae99db36a54c
|
||||
timeCreated: 1765361215
|
||||
@@ -1,9 +1,20 @@
|
||||
using Core;
|
||||
using Minigames.TrashMaze.Core;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using AppleHills.Core.Settings;
|
||||
|
||||
namespace Minigames.TrashMaze.Objects
|
||||
{
|
||||
/// <summary>
|
||||
/// Reveal mode for object visibility
|
||||
/// </summary>
|
||||
public enum RevealMode
|
||||
{
|
||||
Binary, // Simple on/off reveal (current system)
|
||||
Progressive // Pixel-by-pixel progressive reveal with stamp texture
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Component for objects that need reveal memory (obstacles, booster packs, treasures).
|
||||
/// Tracks if object has been revealed and updates material properties accordingly.
|
||||
@@ -11,10 +22,17 @@ namespace Minigames.TrashMaze.Objects
|
||||
[RequireComponent(typeof(SpriteRenderer))]
|
||||
public class RevealableObject : MonoBehaviour
|
||||
{
|
||||
[Header("Reveal Settings")]
|
||||
[SerializeField] private RevealMode revealMode = RevealMode.Binary;
|
||||
|
||||
[Header("Textures")]
|
||||
[SerializeField] private Sprite normalSprite;
|
||||
[SerializeField] private Sprite outlineSprite;
|
||||
|
||||
[Header("Progressive Reveal Settings")]
|
||||
[SerializeField, Range(0.5f, 3f)] private float stampWorldRadius = 1.5f;
|
||||
[Tooltip("Size of each stamp in world units. Smaller = more gradual reveal. Should be smaller than vision radius.")]
|
||||
|
||||
[Header("Object Type")]
|
||||
[SerializeField] private bool isBoosterPack = false;
|
||||
[SerializeField] private bool isExit = false;
|
||||
@@ -27,64 +45,403 @@ namespace Minigames.TrashMaze.Objects
|
||||
// Material property IDs (cached for performance)
|
||||
private static readonly int IsRevealedID = Shader.PropertyToID("_IsRevealed");
|
||||
private static readonly int IsInVisionID = Shader.PropertyToID("_IsInVision");
|
||||
private static readonly int RevealMaskID = Shader.PropertyToID("_RevealMask");
|
||||
|
||||
// Progressive reveal system
|
||||
private RenderTexture _revealStampTexture;
|
||||
private Coroutine _stampCoroutine;
|
||||
private Bounds _objectBounds;
|
||||
private float _activationDistance;
|
||||
|
||||
// Binary reveal system
|
||||
private Coroutine _binaryRevealCoroutine;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
// Create instance material so each object has its own properties
|
||||
if (_spriteRenderer.material != null)
|
||||
if (_spriteRenderer.material == null)
|
||||
{
|
||||
_instanceMaterial = new Material(_spriteRenderer.material);
|
||||
_spriteRenderer.material = _instanceMaterial;
|
||||
Logging.Error($"[RevealableObject] No material assigned to {gameObject.name}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create instance material
|
||||
_instanceMaterial = new Material(_spriteRenderer.material);
|
||||
_spriteRenderer.material = _instanceMaterial;
|
||||
|
||||
// Call mode-specific initialization - COMPLETELY SEPARATE
|
||||
if (revealMode == RevealMode.Binary)
|
||||
{
|
||||
InitializeBinaryMode();
|
||||
}
|
||||
else if (revealMode == RevealMode.Progressive)
|
||||
{
|
||||
InitializeProgressiveMode();
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// BINARY MODE INITIALIZATION
|
||||
// ========================================
|
||||
|
||||
private void InitializeBinaryMode()
|
||||
{
|
||||
// Validate Binary shader
|
||||
string shaderName = _instanceMaterial.shader.name;
|
||||
if (!shaderName.Contains("ObjectVisibility") || shaderName.Contains("Progressive"))
|
||||
{
|
||||
Logging.Error($"[RevealableObject] {gameObject.name} Binary mode needs shader 'TrashMaze/ObjectVisibility', currently: {shaderName}");
|
||||
}
|
||||
|
||||
// Set initial Binary mode properties
|
||||
_instanceMaterial.SetFloat(IsRevealedID, 0f);
|
||||
_instanceMaterial.SetFloat(IsInVisionID, 0f);
|
||||
|
||||
// Set textures
|
||||
if (normalSprite != null)
|
||||
{
|
||||
_instanceMaterial.SetTexture("_MainTex", normalSprite.texture);
|
||||
}
|
||||
if (outlineSprite != null)
|
||||
{
|
||||
_instanceMaterial.SetTexture("_OutlineTex", outlineSprite.texture);
|
||||
}
|
||||
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} Binary mode initialized");
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// PROGRESSIVE MODE INITIALIZATION
|
||||
// ========================================
|
||||
|
||||
private void InitializeProgressiveMode()
|
||||
{
|
||||
// Validate Progressive shader
|
||||
string shaderName = _instanceMaterial.shader.name;
|
||||
if (!shaderName.Contains("ObjectVisibilityProgressive"))
|
||||
{
|
||||
Logging.Error($"[RevealableObject] {gameObject.name} Progressive mode needs shader 'TrashMaze/ObjectVisibilityProgressive', currently: {shaderName}");
|
||||
}
|
||||
|
||||
// Initialize progressive reveal system
|
||||
InitializeProgressiveReveal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize progressive reveal system with dynamic texture sizing
|
||||
/// </summary>
|
||||
private void InitializeProgressiveReveal()
|
||||
{
|
||||
// Get object bounds for UV calculations
|
||||
_objectBounds = _spriteRenderer.bounds;
|
||||
|
||||
// Load activation distance from settings (use vision radius from follower settings)
|
||||
var configs = GameManager.GetSettingsObject<IPlayerMovementConfigs>();
|
||||
_activationDistance = configs.FollowerMovement.TrashMazeVisionRadius;
|
||||
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} loaded activation distance from settings: {_activationDistance}");
|
||||
|
||||
// Dynamically determine texture size from sprite
|
||||
int textureWidth = 128;
|
||||
int textureHeight = 128;
|
||||
|
||||
if (normalSprite != null && normalSprite.texture != null)
|
||||
{
|
||||
// Use sprite's texture resolution (match 1:1 for pixel-perfect)
|
||||
textureWidth = normalSprite.texture.width;
|
||||
textureHeight = normalSprite.texture.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Error($"[RevealableObject] No material assigned to {gameObject.name}");
|
||||
Logging.Warning($"[RevealableObject] {gameObject.name} in Progressive mode but normalSprite not assigned! Using default 128x128 texture. Assign Normal Sprite in inspector!");
|
||||
}
|
||||
|
||||
// Create reveal stamp texture (R8 format = 8-bit grayscale, minimal memory)
|
||||
_revealStampTexture = new RenderTexture(textureWidth, textureHeight, 0, RenderTextureFormat.R8);
|
||||
_revealStampTexture.filterMode = FilterMode.Point; // Sharp edges for binary reveals
|
||||
_revealStampTexture.wrapMode = TextureWrapMode.Clamp;
|
||||
_revealStampTexture.Create(); // Explicitly create the texture
|
||||
|
||||
// Clear to black (nothing revealed initially)
|
||||
RenderTexture previousActive = RenderTexture.active;
|
||||
RenderTexture.active = _revealStampTexture;
|
||||
GL.Clear(false, true, Color.black);
|
||||
RenderTexture.active = previousActive;
|
||||
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} cleared reveal texture to black - should be invisible initially");
|
||||
|
||||
// Set Progressive shader properties
|
||||
_instanceMaterial.SetTexture(RevealMaskID, _revealStampTexture);
|
||||
|
||||
// Progressive shader uses global _PlayerWorldPos and _VisionRadius (set by PulverController)
|
||||
// No need to set _IsInVision - shader does per-pixel distance checks
|
||||
|
||||
// Set textures
|
||||
if (normalSprite != null)
|
||||
{
|
||||
_instanceMaterial.SetTexture("_MainTex", normalSprite.texture);
|
||||
}
|
||||
if (outlineSprite != null)
|
||||
{
|
||||
_instanceMaterial.SetTexture("_OutlineTex", outlineSprite.texture);
|
||||
}
|
||||
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} Progressive mode initialized: {textureWidth}x{textureHeight} reveal texture");
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Initialize material properties
|
||||
if (_instanceMaterial != null)
|
||||
if (PulverController.Instance == null)
|
||||
{
|
||||
_instanceMaterial.SetFloat(IsRevealedID, 0f);
|
||||
_instanceMaterial.SetFloat(IsInVisionID, 0f);
|
||||
|
||||
// Set textures if provided
|
||||
if (normalSprite != null)
|
||||
{
|
||||
_instanceMaterial.SetTexture("_MainTex", normalSprite.texture);
|
||||
}
|
||||
if (outlineSprite != null)
|
||||
{
|
||||
_instanceMaterial.SetTexture("_OutlineTex", outlineSprite.texture);
|
||||
}
|
||||
Logging.Error($"[RevealableObject] {gameObject.name} cannot start - PulverController not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start mode-specific runtime logic - COMPLETELY SEPARATE
|
||||
if (revealMode == RevealMode.Binary)
|
||||
{
|
||||
StartBinaryModeTracking();
|
||||
}
|
||||
else if (revealMode == RevealMode.Progressive)
|
||||
{
|
||||
StartProgressiveModeTracking();
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
// ========================================
|
||||
// BINARY MODE: Start tracking
|
||||
// ========================================
|
||||
|
||||
private void StartBinaryModeTracking()
|
||||
{
|
||||
if (_isCollected || _instanceMaterial == null) return;
|
||||
_binaryRevealCoroutine = StartCoroutine(BinaryRevealTrackingCoroutine());
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} Binary mode tracking started");
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// PROGRESSIVE MODE: Start tracking
|
||||
// ========================================
|
||||
|
||||
private void StartProgressiveModeTracking()
|
||||
{
|
||||
// Subscribe to movement events for stamping
|
||||
PulverController.Instance.OnMovementStarted += OnPlayerMovementStarted;
|
||||
PulverController.Instance.OnMovementStopped += OnPlayerMovementStopped;
|
||||
|
||||
// Calculate distance to player
|
||||
float distance = Vector2.Distance(transform.position, PulverController.PlayerPosition);
|
||||
bool isInRadius = distance < PulverController.VisionRadius;
|
||||
// NO vision tracking coroutine - Progressive shader does per-pixel distance checks using global _PlayerWorldPos
|
||||
|
||||
// Update real-time vision flag
|
||||
_instanceMaterial.SetFloat(IsInVisionID, isInRadius ? 1f : 0f);
|
||||
|
||||
// Set revealed flag (once true, stays true)
|
||||
if (isInRadius && !_hasBeenRevealed)
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} Progressive mode tracking started");
|
||||
}
|
||||
|
||||
|
||||
// ========================================
|
||||
// BINARY MODE: Vision-based reveal coroutine
|
||||
// ========================================
|
||||
|
||||
/// <summary>
|
||||
/// Binary mode coroutine - tracks player distance and updates vision/reveal flags
|
||||
/// Runs continuously, checks every 0.1s for performance
|
||||
/// </summary>
|
||||
private IEnumerator BinaryRevealTrackingCoroutine()
|
||||
{
|
||||
while (!_isCollected && _instanceMaterial != null)
|
||||
{
|
||||
_hasBeenRevealed = true;
|
||||
_instanceMaterial.SetFloat(IsRevealedID, 1f);
|
||||
// Calculate distance to player
|
||||
float distance = Vector2.Distance(transform.position, PulverController.PlayerPosition);
|
||||
bool isInRadius = distance < PulverController.VisionRadius;
|
||||
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} revealed!");
|
||||
// Set real-time vision flag (controls shader color vs outline)
|
||||
_instanceMaterial.SetFloat(IsInVisionID, isInRadius ? 1f : 0f);
|
||||
|
||||
// Set reveal flag (once revealed, stays revealed)
|
||||
if (isInRadius && !_hasBeenRevealed)
|
||||
{
|
||||
_hasBeenRevealed = true;
|
||||
_instanceMaterial.SetFloat(IsRevealedID, 1f);
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} revealed!");
|
||||
}
|
||||
|
||||
// Wait before next check (reduces CPU load)
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// PROGRESSIVE MODE: Event-based stamp reveal
|
||||
// ========================================
|
||||
|
||||
/// <summary>
|
||||
/// Called when player starts moving - begin stamping if near object
|
||||
/// </summary>
|
||||
private void OnPlayerMovementStarted()
|
||||
{
|
||||
if (_isCollected || revealMode != RevealMode.Progressive) return;
|
||||
|
||||
// Check if player's vision circle could overlap with object bounds
|
||||
// Use closest point on bounds to check distance
|
||||
Vector2 playerPos = PulverController.PlayerPosition;
|
||||
Vector2 closestPoint = _objectBounds.ClosestPoint(playerPos);
|
||||
float distanceToBounds = Vector2.Distance(playerPos, closestPoint);
|
||||
|
||||
// Start stamping if vision radius could reach the object
|
||||
// Add padding to account for vision radius overlap
|
||||
float activationThreshold = _activationDistance + _objectBounds.extents.magnitude;
|
||||
|
||||
if (distanceToBounds < activationThreshold && _stampCoroutine == null)
|
||||
{
|
||||
_stampCoroutine = StartCoroutine(StampRevealCoroutine());
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} started stamping coroutine (distanceToBounds: {distanceToBounds:F2}, threshold: {activationThreshold:F2})");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when player stops moving - stop stamping
|
||||
/// </summary>
|
||||
private void OnPlayerMovementStopped()
|
||||
{
|
||||
if (_stampCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_stampCoroutine);
|
||||
_stampCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that stamps reveal texture while player is moving and near object
|
||||
/// </summary>
|
||||
private IEnumerator StampRevealCoroutine()
|
||||
{
|
||||
while (!_isCollected)
|
||||
{
|
||||
// Check if player's vision circle overlaps with object bounds
|
||||
Vector2 playerPos = PulverController.PlayerPosition;
|
||||
Vector2 closestPoint = _objectBounds.ClosestPoint(playerPos);
|
||||
float distanceToBounds = Vector2.Distance(playerPos, closestPoint);
|
||||
|
||||
// Calculate activation threshold with padding
|
||||
float activationThreshold = _activationDistance + _objectBounds.extents.magnitude;
|
||||
|
||||
// If player moved too far away, stop stamping
|
||||
if (distanceToBounds >= activationThreshold)
|
||||
{
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} stopping stamping coroutine (too far)");
|
||||
_stampCoroutine = null;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Stamp if player's vision radius reaches any part of the object
|
||||
if (distanceToBounds < PulverController.VisionRadius)
|
||||
{
|
||||
StampPlayerPosition();
|
||||
}
|
||||
|
||||
// Wait before next stamp (reduces GPU writes)
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stamp the player's current position onto the reveal texture
|
||||
/// Direct CPU-based stamping - calculates circle-rectangle intersection in world space
|
||||
/// </summary>
|
||||
private void StampPlayerPosition()
|
||||
{
|
||||
if (_revealStampTexture == null)
|
||||
{
|
||||
Logging.Warning($"[RevealableObject] {gameObject.name} cannot stamp: texture is null");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get player position and vision radius in world space
|
||||
Vector2 playerWorldPos = PulverController.PlayerPosition;
|
||||
float visionRadius = PulverController.VisionRadius;
|
||||
|
||||
// Get object bounds in world space
|
||||
Vector2 boundsMin = _objectBounds.min;
|
||||
Vector2 boundsSize = _objectBounds.size;
|
||||
|
||||
// Get texture dimensions
|
||||
int texWidth = _revealStampTexture.width;
|
||||
int texHeight = _revealStampTexture.height;
|
||||
|
||||
// Calculate the bounding box of the circle in world space
|
||||
Vector2 circleMin = playerWorldPos - Vector2.one * visionRadius;
|
||||
Vector2 circleMax = playerWorldPos + Vector2.one * visionRadius;
|
||||
|
||||
// Calculate intersection of circle bounding box with object bounds
|
||||
Vector2 intersectMin = new Vector2(
|
||||
Mathf.Max(circleMin.x, boundsMin.x),
|
||||
Mathf.Max(circleMin.y, boundsMin.y)
|
||||
);
|
||||
Vector2 intersectMax = new Vector2(
|
||||
Mathf.Min(circleMax.x, boundsMin.x + boundsSize.x),
|
||||
Mathf.Min(circleMax.y, boundsMin.y + boundsSize.y)
|
||||
);
|
||||
|
||||
// Check if there's any intersection
|
||||
if (intersectMin.x >= intersectMax.x || intersectMin.y >= intersectMax.y)
|
||||
{
|
||||
return; // No intersection, nothing to stamp
|
||||
}
|
||||
|
||||
// Convert world space intersection to texture pixel coordinates
|
||||
int pixelMinX = Mathf.FloorToInt((intersectMin.x - boundsMin.x) / boundsSize.x * texWidth);
|
||||
int pixelMaxX = Mathf.CeilToInt((intersectMax.x - boundsMin.x) / boundsSize.x * texWidth);
|
||||
int pixelMinY = Mathf.FloorToInt((intersectMin.y - boundsMin.y) / boundsSize.y * texHeight);
|
||||
int pixelMaxY = Mathf.CeilToInt((intersectMax.y - boundsMin.y) / boundsSize.y * texHeight);
|
||||
|
||||
// Clamp to texture bounds
|
||||
pixelMinX = Mathf.Max(0, pixelMinX);
|
||||
pixelMaxX = Mathf.Min(texWidth, pixelMaxX);
|
||||
pixelMinY = Mathf.Max(0, pixelMinY);
|
||||
pixelMaxY = Mathf.Min(texHeight, pixelMaxY);
|
||||
|
||||
// Read current texture data
|
||||
RenderTexture.active = _revealStampTexture;
|
||||
Texture2D tempTex = new Texture2D(texWidth, texHeight, TextureFormat.R8, false);
|
||||
tempTex.ReadPixels(new Rect(0, 0, texWidth, texHeight), 0, 0);
|
||||
tempTex.Apply();
|
||||
|
||||
// Stamp pixels within the circle
|
||||
bool anyPixelStamped = false;
|
||||
float radiusSquared = visionRadius * visionRadius;
|
||||
|
||||
for (int py = pixelMinY; py < pixelMaxY; py++)
|
||||
{
|
||||
for (int px = pixelMinX; px < pixelMaxX; px++)
|
||||
{
|
||||
// Convert pixel coordinates back to world space
|
||||
float worldX = boundsMin.x + (px / (float)texWidth) * boundsSize.x;
|
||||
float worldY = boundsMin.y + (py / (float)texHeight) * boundsSize.y;
|
||||
|
||||
// Check if this pixel is within the circle
|
||||
float dx = worldX - playerWorldPos.x;
|
||||
float dy = worldY - playerWorldPos.y;
|
||||
float distSquared = dx * dx + dy * dy;
|
||||
|
||||
if (distSquared <= radiusSquared)
|
||||
{
|
||||
// Stamp this pixel (set to white)
|
||||
tempTex.SetPixel(px, py, Color.white);
|
||||
anyPixelStamped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyPixelStamped)
|
||||
{
|
||||
// Upload modified texture back to GPU
|
||||
tempTex.Apply();
|
||||
Graphics.CopyTexture(tempTex, _revealStampTexture);
|
||||
|
||||
Logging.Debug($"[RevealableObject] {gameObject.name} stamped pixels at world pos ({playerWorldPos.x:F2}, {playerWorldPos.y:F2}), radius {visionRadius:F2}");
|
||||
}
|
||||
|
||||
RenderTexture.active = null;
|
||||
Destroy(tempTex);
|
||||
}
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
// Check if player is interacting
|
||||
@@ -135,6 +492,33 @@ namespace Minigames.TrashMaze.Objects
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Stop Binary mode coroutine
|
||||
if (_binaryRevealCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_binaryRevealCoroutine);
|
||||
}
|
||||
|
||||
// Unsubscribe from Progressive mode movement events
|
||||
if (revealMode == RevealMode.Progressive && PulverController.Instance != null)
|
||||
{
|
||||
PulverController.Instance.OnMovementStarted -= OnPlayerMovementStarted;
|
||||
PulverController.Instance.OnMovementStopped -= OnPlayerMovementStopped;
|
||||
}
|
||||
|
||||
// Stop Progressive mode stamping coroutine
|
||||
if (_stampCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_stampCoroutine);
|
||||
}
|
||||
|
||||
// Clean up progressive reveal resources
|
||||
if (_revealStampTexture != null)
|
||||
{
|
||||
_revealStampTexture.Release();
|
||||
Destroy(_revealStampTexture);
|
||||
}
|
||||
|
||||
|
||||
// Clean up instance material
|
||||
if (_instanceMaterial != null)
|
||||
{
|
||||
|
||||
106
Assets/Shaders/TrashMaze/ObjectVisibilityProgressive.shader
Normal file
106
Assets/Shaders/TrashMaze/ObjectVisibilityProgressive.shader
Normal file
@@ -0,0 +1,106 @@
|
||||
Shader "TrashMaze/ObjectVisibilityProgressive"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_MainTex ("Normal Texture (Color)", 2D) = "white" {}
|
||||
_OutlineTex ("Outline Texture (White)", 2D) = "white" {}
|
||||
_RevealMask ("Reveal Mask", 2D) = "black" {}
|
||||
[PerRendererData] _IsInVision ("Is In Vision", Float) = 0
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags
|
||||
{
|
||||
"Queue"="Transparent"
|
||||
"RenderType"="Transparent"
|
||||
"IgnoreProjector"="True"
|
||||
}
|
||||
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
ZWrite Off
|
||||
Cull Off
|
||||
|
||||
Pass
|
||||
{
|
||||
HLSLPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
|
||||
struct Attributes
|
||||
{
|
||||
float4 positionOS : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct Varyings
|
||||
{
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 positionCS : SV_POSITION;
|
||||
float3 positionWS : TEXCOORD1;
|
||||
};
|
||||
|
||||
TEXTURE2D(_MainTex);
|
||||
SAMPLER(sampler_MainTex);
|
||||
TEXTURE2D(_OutlineTex);
|
||||
SAMPLER(sampler_OutlineTex);
|
||||
TEXTURE2D(_RevealMask);
|
||||
SAMPLER(sampler_RevealMask);
|
||||
float4 _MainTex_ST;
|
||||
|
||||
// Global shader properties (set by PulverController)
|
||||
float2 _PlayerWorldPos;
|
||||
float _VisionRadius;
|
||||
|
||||
Varyings vert(Attributes input)
|
||||
{
|
||||
Varyings output;
|
||||
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
|
||||
output.positionCS = vertexInput.positionCS;
|
||||
output.positionWS = vertexInput.positionWS;
|
||||
output.uv = TRANSFORM_TEX(input.uv, _MainTex);
|
||||
return output;
|
||||
}
|
||||
|
||||
half4 frag(Varyings input) : SV_Target
|
||||
{
|
||||
// Sample reveal mask (0 = not revealed, 1 = revealed)
|
||||
float revealAmount = SAMPLE_TEXTURE2D(_RevealMask, sampler_RevealMask, input.uv).r;
|
||||
|
||||
// Binary decision: is this pixel revealed?
|
||||
bool isRevealed = revealAmount > 0.5;
|
||||
|
||||
// If pixel was never revealed, hide it completely
|
||||
if (!isRevealed)
|
||||
{
|
||||
return half4(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Calculate per-pixel distance to player in world space
|
||||
float2 pixelWorldPos = input.positionWS.xy;
|
||||
float distanceToPlayer = distance(pixelWorldPos, _PlayerWorldPos);
|
||||
|
||||
// Three-state logic per pixel:
|
||||
// 1. Never revealed -> hide (handled above)
|
||||
// 2. Revealed + currently in player vision radius -> show color
|
||||
// 3. Revealed + outside player vision radius -> show outline
|
||||
|
||||
if (distanceToPlayer < _VisionRadius)
|
||||
{
|
||||
// Pixel is revealed AND currently in vision - show color
|
||||
return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pixel is revealed but NOT currently in vision - show outline
|
||||
return SAMPLE_TEXTURE2D(_OutlineTex, sampler_OutlineTex, input.uv);
|
||||
}
|
||||
}
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
|
||||
FallBack "Transparent/Diffuse"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 732fa975ac924d89bb0078279d2cdb0b
|
||||
timeCreated: 1765358086
|
||||
64
Assets/Shaders/TrashMaze/RevealStamp.shader
Normal file
64
Assets/Shaders/TrashMaze/RevealStamp.shader
Normal file
@@ -0,0 +1,64 @@
|
||||
Shader "Hidden/TrashMaze/RevealStamp"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_StampPos ("Stamp Position", Vector) = (0.5, 0.5, 0, 0)
|
||||
_StampRadius ("Stamp Radius", Float) = 0.2
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags { "Queue"="Overlay" "RenderType"="Opaque" }
|
||||
|
||||
// Additive blend to accumulate stamps
|
||||
Blend One One
|
||||
ZTest Always
|
||||
ZWrite Off
|
||||
Cull Off
|
||||
|
||||
Pass
|
||||
{
|
||||
HLSLPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
|
||||
struct Attributes
|
||||
{
|
||||
float4 positionOS : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct Varyings
|
||||
{
|
||||
float4 positionCS : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
float4 _StampPos;
|
||||
float _StampRadius;
|
||||
|
||||
Varyings vert(Attributes input)
|
||||
{
|
||||
Varyings output;
|
||||
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
|
||||
output.uv = input.uv;
|
||||
return output;
|
||||
}
|
||||
|
||||
half4 frag(Varyings input) : SV_Target
|
||||
{
|
||||
// Calculate distance from stamp center
|
||||
float dist = distance(input.uv, _StampPos.xy);
|
||||
|
||||
// Binary circle: 1.0 inside radius, 0.0 outside
|
||||
float alpha = dist < _StampRadius ? 1.0 : 0.0;
|
||||
|
||||
// Return white with calculated alpha (additive blend accumulates)
|
||||
return half4(alpha, alpha, alpha, alpha);
|
||||
}
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Shaders/TrashMaze/RevealStamp.shader.meta
Normal file
3
Assets/Shaders/TrashMaze/RevealStamp.shader.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33afb80a55e64e53b8552498ad61acfa
|
||||
timeCreated: 1765358067
|
||||
Reference in New Issue
Block a user