Music and transparent fixes

This commit is contained in:
Savya Bikram Shah
2026-05-29 18:42:12 +05:45
parent 47fb204446
commit 0b22ed6d09
20 changed files with 492 additions and 75 deletions

View File

@@ -1,89 +1,115 @@
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using Darkmatter.Core.Contracts.Services.Camera;
using Darkmatter.Core.Contracts.Services.Capture;
using UnityEngine;
using CameraType = Darkmatter.Core.Enums.Services.Camera.CameraType;
namespace Darkmatter.Services.Capture
{
public class CaptureService : ICaptureService
{
private readonly ICameraService _cameraService;
public CaptureService(ICameraService cameraService) => _cameraService = cameraService;
public async UniTask<byte[]> CapturePngAsync(GameObject captureObject, float scale,
CancellationToken cancellationToken = default)
{
if (captureObject == null) return null;
var paperRT = captureObject.transform as RectTransform;
var paperCanvas = GetRootCanvas(captureObject);
var disabledCanvases = DisableOtherRootCanvases(paperCanvas);
var disabledGraphics = HideNonPaperGraphics(captureObject != null ? captureObject.transform : null);
var cam = Camera.main;
CameraClearFlags prevFlags = default;
Color prevBg = default;
if (cam != null)
{
prevFlags = cam.clearFlags;
prevBg = cam.backgroundColor;
cam.clearFlags = CameraClearFlags.SolidColor;
cam.backgroundColor = new Color(0f, 0f, 0f, 0f);
}
await UniTask.WaitForEndOfFrame(cancellationToken);
var cam = _cameraService.GetCamera(CameraType.CaptureCamera);
if (paperCanvas == null || paperRT == null || cam == null) return null;
int sw = Screen.width;
int sh = Screen.height;
var captureRT = RenderTexture.GetTemporary(sw, sh, 0, RenderTextureFormat.ARGB32);
ScreenCapture.CaptureScreenshotIntoRenderTexture(captureRT);
var prevActive = RenderTexture.active;
RenderTexture.active = captureRT;
var fullScreen = new Texture2D(sw, sh, TextureFormat.RGBA32, mipChain: false);
fullScreen.ReadPixels(new Rect(0, 0, sw, sh), 0, 0);
fullScreen.Apply();
RenderTexture.active = prevActive;
RenderTexture.ReleaseTemporary(captureRT);
var disabledCanvases = DisableOtherRootCanvases(paperCanvas);
var disabledGraphics = HideNonPaperGraphics(paperRT);
FlipVertical(fullScreen);
Rect crop = ComputeCropRect(captureObject, sw, sh);
int cropW = Mathf.Max(1, (int)crop.width);
int cropH = Mathf.Max(1, (int)crop.height);
var prevFlags = cam.clearFlags;
var prevBg = cam.backgroundColor;
var prevTarget = cam.targetTexture;
cam.clearFlags = CameraClearFlags.SolidColor;
cam.backgroundColor = new Color(0f, 0f, 0f, 0f);
var rt = RenderTexture.GetTemporary(sw, sh, 24, RenderTextureFormat.ARGB32);
cam.targetTexture = rt;
var prevMode = paperCanvas.renderMode;
var prevWorldCam = paperCanvas.worldCamera;
var prevPlaneDist = paperCanvas.planeDistance;
paperCanvas.renderMode = RenderMode.ScreenSpaceCamera;
paperCanvas.worldCamera = cam;
paperCanvas.planeDistance = Mathf.Max(0.5f, (cam.nearClipPlane + cam.farClipPlane) * 0.5f);
try
{
Rect crop = ComputeCropRect(captureObject, fullScreen.width, fullScreen.height);
int cropW = Mathf.Max(1, (int)crop.width);
int cropH = Mathf.Max(1, (int)crop.height);
await UniTask.WaitForEndOfFrame(cancellationToken);
Canvas.ForceUpdateCanvases();
cam.Render();
var prevActive = RenderTexture.active;
RenderTexture.active = rt;
var fullScreen = new Texture2D(sw, sh, TextureFormat.RGBA32, mipChain: false);
fullScreen.ReadPixels(new Rect(0, 0, sw, sh), 0, 0);
fullScreen.Apply();
RenderTexture.active = prevActive;
var cropped = new Texture2D(cropW, cropH, TextureFormat.RGBA32, mipChain: false);
try
{
cropped.SetPixels(fullScreen.GetPixels((int)crop.x, (int)crop.y, cropW, cropH));
cropped.Apply();
int targetW = Mathf.Max(1, Mathf.RoundToInt(cropW * scale));
int targetH = Mathf.Max(1, Mathf.RoundToInt(cropH * scale));
Texture2D output = cropped;
if (output.width != targetW || output.height != targetH)
output = Resize(cropped, targetW, targetH);
var cropped = new Texture2D(cropW, cropH, TextureFormat.RGBA32, mipChain: false);
try
{
return output.EncodeToPNG();
cropped.SetPixels(fullScreen.GetPixels((int)crop.x, (int)crop.y, cropW, cropH));
cropped.Apply();
int targetW = Mathf.Max(1, Mathf.RoundToInt(cropW * scale));
int targetH = Mathf.Max(1, Mathf.RoundToInt(cropH * scale));
Texture2D output = cropped;
if (output.width != targetW || output.height != targetH)
output = Resize(cropped, targetW, targetH);
try
{
return output.EncodeToPNG();
}
finally
{
if (output != cropped) Object.Destroy(output);
}
}
finally
{
if (output != cropped) Object.Destroy(output);
Object.Destroy(cropped);
}
}
finally
{
Object.Destroy(cropped);
Object.Destroy(fullScreen);
}
}
finally
{
Object.Destroy(fullScreen);
foreach (var g in disabledGraphics) if (g != null) g.enabled = true;
foreach (var c in disabledCanvases) if (c != null) c.enabled = true;
if (cam != null)
{
cam.clearFlags = prevFlags;
cam.backgroundColor = prevBg;
}
paperCanvas.renderMode = prevMode;
paperCanvas.worldCamera = prevWorldCam;
paperCanvas.planeDistance = prevPlaneDist;
cam.targetTexture = prevTarget;
cam.clearFlags = prevFlags;
cam.backgroundColor = prevBg;
RenderTexture.ReleaseTemporary(rt);
foreach (var g in disabledGraphics)
if (g != null)
g.enabled = true;
foreach (var c in disabledCanvases)
if (c != null)
c.enabled = true;
}
}
@@ -104,6 +130,7 @@ namespace Darkmatter.Services.Capture
c.enabled = false;
disabled.Add(c);
}
return disabled;
}
@@ -119,6 +146,7 @@ namespace Darkmatter.Services.Capture
g.enabled = false;
disabled.Add(g);
}
return disabled;
}
@@ -129,7 +157,6 @@ namespace Darkmatter.Services.Capture
var corners = new Vector3[4];
rt.GetWorldCorners(corners);
// ScreenSpaceOverlay canvas: world corners are already in screen pixels.
float minX = Mathf.Clamp(corners[0].x, 0, screenW);
float minY = Mathf.Clamp(corners[0].y, 0, screenH);
float maxX = Mathf.Clamp(corners[2].x, 0, screenW);
@@ -137,18 +164,6 @@ namespace Darkmatter.Services.Capture
return Rect.MinMaxRect(minX, minY, maxX, maxY);
}
private static void FlipVertical(Texture2D tex)
{
int w = tex.width, h = tex.height;
var pixels = tex.GetPixels();
var flipped = new Color[pixels.Length];
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
flipped[(h - 1 - y) * w + x] = pixels[y * w + x];
tex.SetPixels(flipped);
tex.Apply();
}
private static Texture2D Resize(Texture2D src, int width, int height)
{
var rt = RenderTexture.GetTemporary(width, height);
@@ -169,4 +184,4 @@ namespace Darkmatter.Services.Capture
}
}
}
}
}

View File

@@ -0,0 +1,22 @@
using Darkmatter.Core.Contracts.Services.Music;
using Darkmatter.Libs.Installers;
using Darkmatter.Services.Music.Systems;
using UnityEngine;
using VContainer;
using VContainer.Unity;
namespace Darkmatter.Services.Music.Installers
{
public class MusicServiceModule : MonoBehaviour, IModule
{
[SerializeField] private AudioClip defaultTrack;
[SerializeField, Range(0f, 1f)] private float defaultVolume = 0.7f;
[SerializeField, Min(0f)] private float crossFadeSeconds = 0.4f;
public void Register(IContainerBuilder builder)
{
builder.RegisterInstance(new MusicConfig(defaultTrack, defaultVolume, crossFadeSeconds));
builder.RegisterEntryPoint<MusicService>(Lifetime.Singleton).As<IMusicService>();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 47cce31d8e31341a0ae46684e87cbd95

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4c15bf1ed26cf4c8f8339b22cb57efc6

View File

@@ -0,0 +1,70 @@
using System;
using Darkmatter.Core.Contracts.Services.Audio;
using Darkmatter.Core.Contracts.Services.Music;
using Darkmatter.Core.Data.Dynamic.Services.Audio;
using Darkmatter.Core.Data.Signals.Features.AppBoot;
using Darkmatter.Core.Enums.Services.Audio;
using Darkmatter.Libs.Observer;
using UnityEngine;
using VContainer.Unity;
namespace Darkmatter.Services.Music.Systems
{
public class MusicService : IMusicService, IStartable, IDisposable
{
private readonly IAudioService _audio;
private readonly IEventBus _bus;
private readonly MusicConfig _config;
private AudioHandle _current;
private IDisposable _introSub;
public MusicService(IAudioService audio, IEventBus bus, MusicConfig config)
{
_audio = audio;
_bus = bus;
_config = config;
}
public void Start()
{
_introSub = _bus.Subscribe<IntroCompletedSignal>(OnIntroCompleted);
}
private void OnIntroCompleted(IntroCompletedSignal _)
{
if (_config.DefaultTrack != null && !_current.IsValid)
Play(_config.DefaultTrack, _config.DefaultVolume, loop: true);
}
public void Play(AudioClip clip, float volume01 = 1f, bool loop = true)
{
if (clip == null) return;
if (_current.IsValid) _audio.Stop(_current, _config.CrossFadeSeconds);
var req = new AudioRequest(
clip: clip,
channel: AudioChannel.Music,
mode: loop ? AudioPlayMode.Loop : AudioPlayMode.OneShot,
stopChannelBeforePlay: true,
volume01: Mathf.Clamp01(volume01));
_current = _audio.Play(req);
}
public void Stop(float fadeOutSeconds = 0f)
{
if (_current.IsValid) _audio.Stop(_current, fadeOutSeconds);
_current = AudioHandle.Invalid;
}
public void SetVolume(float volume01)
{
if (_current.IsValid) _audio.SetVolume(_current, Mathf.Clamp01(volume01));
}
public void Dispose()
{
_introSub?.Dispose();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8b4c07c26f9a44409bd43d2777b9951d