diff --git a/Assets/MagnetismHeroes/Editor/VisualUpgradeAssets.cs b/Assets/MagnetismHeroes/Editor/VisualUpgradeAssets.cs index 9deaa35..2e7cf5b 100644 --- a/Assets/MagnetismHeroes/Editor/VisualUpgradeAssets.cs +++ b/Assets/MagnetismHeroes/Editor/VisualUpgradeAssets.cs @@ -38,6 +38,25 @@ namespace MagnetismHeroes.Editor public string[] unchangedNonvisualStageSignatures; } + public static void ApplyDaylightShadows() + { + var app = PrefabUtility.LoadPrefabContents(FirstSliceAssets.AppPrefabPath); + try + { + var sun = app.transform.Find("Sun").GetComponent(); + sun.shadows = LightShadows.Soft; sun.shadowStrength = .55f; + sun.color = new Color(1, .97f, .91f); sun.intensity = 1.3f; + PrefabUtility.SaveAsPrefabAsset(app, FirstSliceAssets.AppPrefabPath); + } + finally { PrefabUtility.UnloadPrefabContents(app); } + var pipeline = new SerializedObject(AssetDatabase.LoadMainAssetAtPath("Assets/Settings/Mobile_RPAsset.asset")); + pipeline.FindProperty("m_SoftShadowsSupported").boolValue = true; + pipeline.FindProperty("m_SoftShadowQuality").intValue = 1; + pipeline.FindProperty("m_ShadowDistance").floatValue = 30; + pipeline.ApplyModifiedPropertiesWithoutUndo(); AssetDatabase.SaveAssets(); + Debug.Log("[VisualUpgrade] Existing daylight light and mobile URP: soft .55-strength shadows, low soft-shadow quality, one 1024 cascade, 30m distance; model/material/stage bytes untouched."); + } + public static void Apply() { string approvalPath = Environment.GetEnvironmentVariable("MAGNETISM_VISUAL_APPROVAL"); diff --git a/Assets/MagnetismHeroes/Tests/EditMode/ContentAssetTests.cs b/Assets/MagnetismHeroes/Tests/EditMode/ContentAssetTests.cs index 31f31ec..73a57af 100644 --- a/Assets/MagnetismHeroes/Tests/EditMode/ContentAssetTests.cs +++ b/Assets/MagnetismHeroes/Tests/EditMode/ContentAssetTests.cs @@ -30,8 +30,19 @@ namespace MagnetismHeroes.Tests Assert.That(definition.Total, Is.EqualTo(definition.TargetGroups.Sum())); Assert.That(definition.Total, Is.InRange(1,10)); Assert.That(definition.BallPositions.Count, Is.EqualTo(definition.Total)); Assert.That(asset.Theme, Is.EqualTo(id<=10 ? "Garden" : id<=20 ? "Cloud" : "Workshop")); - Assert.That(asset.Decorations.Count, Is.EqualTo(7)); foreach (var placement in asset.Decorations) Assert.That(placement.prefab, Is.Not.Null); + var expectedDecorations = new[] { "MeadowClearing", "OpenStoneArch", "MossStoneSteps", "PebbleScatter" } + .Concat(Enumerable.Repeat("FarHorizon", 4)).Concat(Enumerable.Repeat("DistantRidge", 4)) + .Concat(Enumerable.Repeat("GentleHill", 4)) + .Concat(Enumerable.Repeat(id <= 10 ? "RoundCanopyTree" : id <= 20 ? "SageCanopyTree" : "TallWindTree", 4)) + .Concat(new[] { id <= 10 ? "MintShrub" : "SageShrub" }) + .Concat(id <= 10 ? new[] { "DaisyColony", "FernCluster", "GrassTuftMint", "GrassTuftGold" } + : id <= 20 ? new[] { "PinkCosmosColony", "LavenderLupineColony", "DaisyColony", "GrassTuftGold" } + : new[] { "SlateBoulder", "RoundLimestone", "LowLimestone", "FernCluster" }); + Assert.That(asset.Decorations.Select(placement => placement.prefab.name), Is.EquivalentTo(expectedDecorations), $"Stage {id} natural theme composition"); + foreach (string name in new[] { "FarHorizon", "DistantRidge", "GentleHill" }) + Assert.That(asset.Decorations.Where(placement => placement.prefab.name == name).Select(placement => placement.eulerAngles.y), + Is.EquivalentTo(new[] { 0f, 90f, 180f, 270f }), $"Stage {id} {name} surrounds all four directions"); string before=EditorJsonUtility.ToJson(asset); var board=new BoardRules(definition); board.TryMoveGroup(board.Groups[0].id,new Position3(1,0,0)); Assert.That(EditorJsonUtility.ToJson(asset), Is.EqualTo(before), "Runtime rules never mutate their source asset."); diff --git a/Assets/MagnetismHeroes/Tests/EditMode/VisualUpgradeAssetTests.cs b/Assets/MagnetismHeroes/Tests/EditMode/VisualUpgradeAssetTests.cs index ac4d253..119381e 100644 --- a/Assets/MagnetismHeroes/Tests/EditMode/VisualUpgradeAssetTests.cs +++ b/Assets/MagnetismHeroes/Tests/EditMode/VisualUpgradeAssetTests.cs @@ -10,6 +10,23 @@ namespace MagnetismHeroes.Tests public sealed class VisualUpgradeAssetTests { private const string Root = "Assets/MagnetismHeroes/UnityRuntime/Presentation/"; + [Test] public void MobileDaylightSupportsSoftGroundShadowsWithoutReplacingThePlayableFloor() + { + var app = AssetDatabase.LoadAssetAtPath(Root + "Prefabs/GameApp.prefab"); + var sun = app.transform.Find("Sun").GetComponent(); + Assert.That(sun.shadows, Is.EqualTo(LightShadows.Soft)); + Assert.That(sun.shadowStrength, Is.InRange(.3f, .7f)); + Assert.That(sun.intensity, Is.InRange(1.1f, 1.5f)); + var pipeline = new SerializedObject(AssetDatabase.LoadMainAssetAtPath("Assets/Settings/Mobile_RPAsset.asset")); + Assert.That(pipeline.FindProperty("m_MainLightShadowsSupported").boolValue, Is.True); + Assert.That(pipeline.FindProperty("m_SoftShadowsSupported").boolValue, Is.True); + Assert.That(pipeline.FindProperty("m_MainLightShadowmapResolution").intValue, Is.EqualTo(1024)); + Assert.That(pipeline.FindProperty("m_ShadowCascadeCount").intValue, Is.EqualTo(1)); + Assert.That(pipeline.FindProperty("m_ShadowDistance").floatValue, Is.InRange(20f, 35f)); + var floor = app.transform.Find("Playground/PlaygroundIsland"); + Assert.That(floor.GetComponent(), Is.Not.Null); + Assert.That(floor.GetComponent().enabled, Is.False); + } [Test] public void ApprovedBodyAndOneEquipmentAssemblyAreConnectedWithoutChangingSelectionSphere() { var ball = AssetDatabase.LoadAssetAtPath(Root + "Prefabs/MagnetFriend.prefab"); diff --git a/Assets/MagnetismHeroes/Tests/PlayMode/ContentPlayTests.cs b/Assets/MagnetismHeroes/Tests/PlayMode/ContentPlayTests.cs index cee329c..aa260b3 100644 --- a/Assets/MagnetismHeroes/Tests/PlayMode/ContentPlayTests.cs +++ b/Assets/MagnetismHeroes/Tests/PlayMode/ContentPlayTests.cs @@ -272,9 +272,13 @@ namespace MagnetismHeroes.Tests [UnityTest] public IEnumerator MeshArchReallyHasAnOpenGapAndHelpHighlightsOnlyCurrentIdsWithoutExecuting() { _app.StartStage(14); yield return null; Physics.SyncTransforms(); - var arch=_app.Environment.GetComponentsInChildren().First(collider=>collider.sharedMesh.name.Contains("BlockArch")); - var local=new Ray(new Vector3(0,1,-3),Vector3.forward); - Assert.That(arch.Raycast(new Ray(arch.transform.parent.TransformPoint(local.origin),Vector3.forward),out _,6), Is.False,"No box collider can close the Blender arch gap."); + var arch=_app.Environment.GetComponentsInChildren().Single(item=>item.name=="OpenStoneArch(Clone)"); + var colliders=arch.GetComponentsInChildren(); + Assert.That(colliders, Is.Not.Empty); + Assert.That(arch.GetComponentsInChildren(), Is.Empty); + Ray ArchRay(float x) => new Ray(arch.TransformPoint(new Vector3(x,1,-3)),arch.TransformDirection(Vector3.forward)); + Assert.That(colliders.Any(collider=>collider.Raycast(ArchRay(0),out _,6)), Is.False,"Actual approved arch gap remains open."); + Assert.That(colliders.Any(collider=>collider.Raycast(ArchRay(1.1f),out _,6)), Is.True,"Actual approved arch side blocks input rays."); var before=JsonUtility.ToJson(_store.Saved); var hint=_app.Session.Hint; _app.ShowHelp(); Assert.That(JsonUtility.ToJson(_store.Saved), Is.EqualTo(before)); Assert.That(_app.Presenter.Balls.Values.Where(ball=>ball.Hinted).Select(ball=>ball.BallId), Is.EquivalentTo(hint.BallIds)); diff --git a/Assets/MagnetismHeroes/Tests/PlayMode/VisualUpgradePlayTests.cs b/Assets/MagnetismHeroes/Tests/PlayMode/VisualUpgradePlayTests.cs index b0f31e5..b18c7e4 100644 --- a/Assets/MagnetismHeroes/Tests/PlayMode/VisualUpgradePlayTests.cs +++ b/Assets/MagnetismHeroes/Tests/PlayMode/VisualUpgradePlayTests.cs @@ -28,8 +28,9 @@ namespace MagnetismHeroes.Tests [Serializable] private sealed class CaptureRecord { public string name, sourceCommit, sourceDirty, boardOrigin, theme, utc, method; - public int width, height, stage, balls, groups, equipmentAssemblies, meshInstances, triangles, sharedMaterials, editorFrameDrawCalls; + public int width, height, stage, balls, groups, equipmentAssemblies, meshInstances, triangles, sharedMaterials; public long textureMemory, allocatedMemory; + public float editorCameraRenderMilliseconds; public Vector3 cameraPosition; public Quaternion cameraRotation; } @@ -54,6 +55,11 @@ namespace MagnetismHeroes.Tests _app.Presenter.SendMessage("LateUpdate"); var badges = _app.Presenter.GetComponentsInChildren(); Assert.That(badges.Length, Is.EqualTo(board.Groups.Length)); + Assert.That(_app.Presenter.Balls.Keys, Is.EquivalentTo(board.Balls.Select(ball => ball.id))); + Assert.That(board.Groups.SelectMany(group => group.ballIds), Is.EquivalentTo(board.Balls.Select(ball => ball.id))); + Assert.That(_app.Presenter.GetComponentsInChildren().Length, Is.EqualTo(board.Groups.Length)); + Assert.That(_app.Presenter.GetComponentsInChildren().Select(label => label.text), + Is.EqualTo(board.Groups.Select(group => group.ballIds.Length.ToString()))); for (int index = 0; index < badges.Length; index++) { var gear = badges[index].transform.Find("KnightEquipment"); @@ -63,6 +69,150 @@ namespace MagnetismHeroes.Tests Assert.That(Vector3.Distance(gear.position, representative), Is.LessThan(.0001f)); Assert.That(Quaternion.Angle(gear.rotation, _app.Presenter.Camera.transform.rotation), Is.LessThan(.001f)); Assert.That(gear.GetComponentsInChildren(), Is.Empty); + Assert.That(badges[index].transform.Find("RibbonCape"), Is.Not.Null); + foreach (string id in board.Groups[index].ballIds) + Assert.That(_app.Presenter.Balls[id].GroupId, Is.EqualTo(board.Groups[index].id)); + } + } + + private void CaptureViews(string name, string origin) + { + var saved = _app.Orbit.Snapshot(); + Capture(name + "-phone-default", origin, 2400, 1080); + Capture(name + "-tablet-default", origin, 1600, 1000); + var rotation = Quaternion.Euler(25, 95, 0); + _app.Orbit.Apply(new CameraState { target = saved.target, distance = 9, + rotation = new Rotation4(rotation.x, rotation.y, rotation.z, rotation.w) }); + Capture(name + "-phone-orbitzoom", origin, 2400, 1080); + Capture(name + "-tablet-orbitzoom", origin, 1600, 1000); + _app.Orbit.Apply(saved); + } + + [UnityTest] public IEnumerator FiveBallMergeDisconnectedSplitAndZeroResupplyKeepActualIdsAcrossAllWorlds() + { + foreach (int stage in new[] { 9, 11, 21 }) + { + // Boundary fixture: a completed-stage store with a validated five-ball free board, + // not evidence of normal puzzle progression. All operations below use the real app/session. + var balls = Enumerable.Range(0, 5).Select(index => new BallState { id = "five-" + index, + position = new Position3(index - 2, .8f, 0) }).ToArray(); + _store.Saved.latestFreePlay = new FreePlayState { sourceStageId = stage, + camera = _app.Orbit.Snapshot(), balls = balls, + groups = new[] { new GroupState { id = "two", ballIds = balls.Take(2).Select(ball => ball.id).ToArray() }, + new GroupState { id = "three", ballIds = balls.Skip(2).Select(ball => ball.id).ToArray() } } }; + _app.Initialize(_store); _app.ContinueFreePlay(); _app.Voice.Clear(); yield return null; + var ids = _app.Session.Board.Balls.Select(ball => ball.id).ToArray(); + AssertEquipmentFollowsRepresentatives(_app.Session.Board); + CaptureViews("world" + stage + "-two-plus-three", "Validated five-ball 2+3 free-play store fixture; actual Main app/session"); + var before = JsonUtility.ToJson(_app.Session.Board.ToFreePlayState(_app.Orbit.Snapshot())); + _app.Presenter.PreviewGroup("two", new Vector3(.3f, .2f, 0)); + AssertEquipmentFollowsRepresentatives(_app.Session.Board); _app.Presenter.CancelPreview(); + Assert.That(JsonUtility.ToJson(_app.Session.Board.ToFreePlayState(_app.Orbit.Snapshot())), Is.EqualTo(before)); + _app.CommitMove("two", new Position3(.1f, 0, 0), "three"); yield return null; + Assert.That(_app.Session.Board.Groups.Single().ballIds.Length, Is.EqualTo(5)); + Assert.That(_app.Session.Board.Balls.Select(ball => ball.id), Is.EquivalentTo(ids)); + AssertEquipmentFollowsRepresentatives(_app.Session.Board); + CaptureViews("world" + stage + "-merged-five", "Actual app/session 2+3 merge from labeled boundary fixture"); + var selected = new[] { ids[0], ids[4] }; + var points = selected.Select(id => BoardPresenter.ToVector(_app.Session.Board.Balls.Single(ball => ball.id == id).position)).ToArray(); + Assert.That(Vector3.Distance(points[0], points[1]), Is.GreaterThan(2f), "Selected endpoints are disconnected."); + var merged = _app.Session.Board.Groups.Single(); + before = JsonUtility.ToJson(_app.Session.Board.ToFreePlayState(_app.Orbit.Snapshot())); + Assert.That(_app.Presenter.PreviewSplit(merged.id, selected, new Vector3(0, 1.5f, 0)), Is.True); + AssertEquipmentFollowsRepresentatives(_app.Session.Board); _app.Presenter.CancelPreview(); + Assert.That(JsonUtility.ToJson(_app.Session.Board.ToFreePlayState(_app.Orbit.Snapshot())), Is.EqualTo(before)); + Assert.That(_app.CommitSplit(merged.id, selected, new Position3(0, 1.5f, 0)), Is.True); yield return null; + Assert.That(_app.Session.Board.Groups.Select(group => group.ballIds.Length), Is.EquivalentTo(new[] { 2, 3 })); + Assert.That(_app.Session.Board.Groups.Single(group => group.ballIds.Length == 2).ballIds, Is.EquivalentTo(selected)); + Assert.That(_app.Session.Board.Balls.Select(ball => ball.id), Is.EquivalentTo(ids)); + AssertEquipmentFollowsRepresentatives(_app.Session.Board); + CaptureViews("world" + stage + "-disconnected-split-two-three", "Actual app/session split of disconnected five-group endpoints"); + before = JsonUtility.ToJson(_app.Session.Board.ToFreePlayState(_app.Orbit.Snapshot())); + _app.ShowHome(); yield return null; + Assert.That(_app.Presenter.GetComponentsInChildren(), Is.Empty); + _app.ContinueFreePlay(); yield return null; + Assert.That(JsonUtility.ToJson(_app.Session.Board.ToFreePlayState(_app.Orbit.Snapshot())), Is.EqualTo(before)); + AssertEquipmentFollowsRepresentatives(_app.Session.Board); + foreach (var group in _app.Session.Board.Groups) Assert.That(_app.Session.ReturnGroup(group.id).Accepted, Is.True); + _app.ContinueFreePlay(); yield return null; + Assert.That(_app.Session.Board.BallCount, Is.Zero); AssertEquipmentFollowsRepresentatives(_app.Session.Board); + CaptureViews("world" + stage + "-zero", "Actual app/session return of both groups; empty free play"); + _app.SpawnFriend(); yield return null; + Assert.That(_app.Session.Board.BallCount, Is.EqualTo(1)); AssertEquipmentFollowsRepresentatives(_app.Session.Board); + CaptureViews("world" + stage + "-single-resupplied", "Actual app/session resupply after empty board"); + } + } + + [UnityTest] public IEnumerator BelowGroundComparisonKeepsOriginalPhysicalOcclusionAndActualBoardUnchanged() + { + _app.StartStage(21); _app.Voice.Clear(); yield return null; + var rotation = Quaternion.Euler(-45, 210, 0); + _app.Orbit.Apply(new CameraState { target = _app.Orbit.Snapshot().target, distance = 15, + rotation = new Rotation4(rotation.x, rotation.y, rotation.z, rotation.w) }); + Physics.SyncTransforms(); + var camera = _app.Presenter.Camera; + var floor = _app.transform.Find("Playground/PlaygroundIsland"); + var originalRenderer = floor.GetComponent(); var originalCollider = floor.GetComponent(); + var clearing = _app.Environment.GetComponentsInChildren().Single(item => item.name == "MeadowClearing(Clone)"); + var groundRenderers = clearing.GetComponentsInChildren(); + var board = JsonUtility.ToJson(_app.Session.Board.ToPuzzleState(_app.Orbit.Snapshot())); + var proof = new System.Collections.Generic.List(); + var rays = _app.Session.Board.Balls.Select(ball => new Ray(camera.transform.position, + (BoardPresenter.ToVector(ball.position) - camera.transform.position).normalized)).ToArray(); + var priorHits = rays.Select(ray => { Assert.That(Physics.Raycast(ray, out var hit, 100), Is.True); return hit; }).ToArray(); + Capture("belowground-approved-clearing", "Actual stage21 board/camera; approved ground rendered, unchanged original collider"); + try + { + foreach (var renderer in groundRenderers) renderer.enabled = false; + originalRenderer.enabled = true; + Capture("belowground-original-island", "Same actual board/camera/colliders; temporary original floor renderer instead of clearing"); + for (int index = 0; index < rays.Length; index++) + { + Assert.That(Physics.Raycast(rays[index], out var hit, 100), Is.True); + Assert.That(hit.collider, Is.SameAs(priorHits[index].collider)); + Assert.That(hit.distance, Is.EqualTo(priorHits[index].distance).Within(.0001f)); + Assert.That(originalCollider.Raycast(rays[index], out var floorHit, 100), Is.True, + "Original floor physically occludes this below-ground board ray."); + var sphere = _app.Presenter.Balls[_app.Session.Board.Balls[index].id].GetComponent(); + Assert.That(sphere.Raycast(rays[index], out var sphereHit, 100), Is.True); + Assert.That(floorHit.distance, Is.LessThan(sphereHit.distance)); + proof.Add($"id={_app.Session.Board.Balls[index].id}; firstHit={hit.collider.name}; floorDepth={floorHit.distance}; sphereDepth={sphereHit.distance}; originalBounds={originalCollider.bounds}; clearingBounds={groundRenderers[0].bounds}"); + } + Assert.That(JsonUtility.ToJson(_app.Session.Board.ToPuzzleState(_app.Orbit.Snapshot())), Is.EqualTo(board)); + File.WriteAllLines(Path.Combine(_evidence, "belowground-raycast-comparison.txt"), proof); + } + finally { foreach (var renderer in groundRenderers) renderer.enabled = true; originalRenderer.enabled = false; } + } + + [UnityTest] public IEnumerator AllWorldsProduceActualSoftShadowPixelsWithUnchangedBoardAndMaterials() + { + var sun = _app.transform.Find("Sun").GetComponent(); + _app.ShowHome(); yield return null; + Capture("home-cyan-readiness", "Actual Main home UI for Android sky readiness predicate", 2400, 1080); + foreach (int stage in new[] { 1, 11, 21 }) + { + _app.StartStage(stage); _app.Voice.Clear(); yield return null; + string before = JsonUtility.ToJson(_app.Session.Board.ToPuzzleState(_app.Orbit.Snapshot())); + Capture("world" + stage + "-soft-shadow", "Actual Main daylight with soft shadows"); + try { sun.shadows = LightShadows.None; Capture("world" + stage + "-shadow-off", "Same board/camera/materials; light shadow toggle comparison only"); } + finally { sun.shadows = LightShadows.Soft; } + var on = new Texture2D(2, 2); var off = new Texture2D(2, 2); + try + { + on.LoadImage(File.ReadAllBytes(Path.Combine(_evidence, "world" + stage + "-soft-shadow.png"))); + off.LoadImage(File.ReadAllBytes(Path.Combine(_evidence, "world" + stage + "-shadow-off.png"))); + var a = on.GetPixels32(); var b = off.GetPixels32(); int darker = 0; + // The central lower ground excludes the board, badge labels, scenery crowns and HUD. + for (int y = 140; y < 300; y++) for (int x = 350; x < 1000; x++) + { + int index = y * on.width + x; + if (a[index].r + a[index].g + a[index].b + 12 < b[index].r + b[index].g + b[index].b) darker++; + } + File.WriteAllText(Path.Combine(_evidence, "world" + stage + "-shadow-pixels.txt"), $"same-board/camera/materials; shadow-on darker ground ROI pixels={darker}; groundROI=350,140,650,160; viewport=1280x720; Editor RT only"); + Assert.That(darker, Is.GreaterThan(100), "Actual rendered soft shadows must darken a meaningful ground area."); + } + finally { Object.Destroy(on); Object.Destroy(off); } + Assert.That(JsonUtility.ToJson(_app.Session.Board.ToPuzzleState(_app.Orbit.Snapshot())), Is.EqualTo(before)); } } [UnityTest] public IEnumerator ActualMainShowsThreeThemesAndEquipmentFollowsPreviewCancelMergeSplitAndReentry() @@ -118,7 +268,7 @@ namespace MagnetismHeroes.Tests var board = BoardRules.CreateFreePlay(new FreePlayState { sourceStageId = 21, balls = balls, groups = groups }); _app.Presenter.Render(board); yield return null; AssertEquipmentFollowsRepresentatives(board); - Capture(singles ? "ten-single-groups" : "ten-one-group", "Validated synthetic Core FreePlay render fixture; session UI remains stage 21", 2400, 1080); + CaptureViews(singles ? "ten-single-groups" : "ten-one-group", "Validated synthetic Core FreePlay render fixture; session UI remains stage 21"); } } private void Capture(string name, string origin, int width = 1280, int height = 720) @@ -132,22 +282,27 @@ namespace MagnetismHeroes.Tests camera.targetTexture = target; camera.aspect = (float)width / height; canvas.renderMode = RenderMode.ScreenSpaceCamera; canvas.worldCamera = camera; canvas.planeDistance = .5f; foreach (var ball in _app.Presenter.Balls.Values) ball.SendMessage("Update"); - _app.Presenter.SendMessage("LateUpdate"); Canvas.ForceUpdateCanvases(); camera.Render(); + _app.Presenter.SendMessage("LateUpdate"); Canvas.ForceUpdateCanvases(); + var timer = System.Diagnostics.Stopwatch.StartNew(); camera.Render(); timer.Stop(); RenderTexture.active = target; image.ReadPixels(new Rect(0, 0, width, height), 0, 0); image.Apply(); File.WriteAllBytes(Path.Combine(_evidence, name + ".png"), image.EncodeToPNG()); var meshes = _app.GetComponentsInChildren(); var badges = _app.Presenter.GetComponentsInChildren(); var record = new CaptureRecord { name = name, sourceCommit = Environment.GetEnvironmentVariable("MAGNETISM_PROOF_COMMIT"), sourceDirty = Environment.GetEnvironmentVariable("MAGNETISM_PROOF_DIRTY"), utc = DateTime.UtcNow.ToString("o"), - boardOrigin = origin, theme = _app.Environment.Theme, stage = _app.Session.Board.StageId, + boardOrigin = origin, theme = _app.Environment.Theme, stage = _app.Session.Board?.StageId ?? 0, width = width, height = height, balls = _app.Presenter.Balls.Count, groups = badges.Length, equipmentAssemblies = badges.Count(item => item.transform.Find("KnightEquipment") != null), meshInstances = meshes.Length, triangles = meshes.Sum(item => Enumerable.Range(0, item.sharedMesh.subMeshCount).Sum(submesh => (int)item.sharedMesh.GetIndexCount(submesh) / 3)), sharedMaterials = _app.GetComponentsInChildren().SelectMany(item => item.sharedMaterials).Where(item => item != null).Distinct().Count(), - editorFrameDrawCalls = UnityStats.drawCalls, textureMemory = Resources.FindObjectsOfTypeAll().Sum(item => Profiler.GetRuntimeMemorySizeLong(item)), + editorCameraRenderMilliseconds = (float)timer.Elapsed.TotalMilliseconds, + textureMemory = Resources.FindObjectsOfTypeAll().Sum(item => Profiler.GetRuntimeMemorySizeLong(item)), allocatedMemory = Profiler.GetTotalAllocatedMemoryLong(), cameraPosition = camera.transform.position, cameraRotation = camera.transform.rotation, - method = "Actual Main scene PlayMode Camera.Render to RT, ScreenSpaceCamera canvas; simulated viewport, not Android capture. Draw calls are Editor frame statistics." }; + method = "Actual Main scene PlayMode Camera.Render to RT, ScreenSpaceCamera canvas; simulated viewport, not Android capture. Triangles are resident mesh-instance index counts, including inactive retained scene objects; Camera.Render elapsed is Editor CPU wall time, not GPU time or device FPS. Texture memory includes all Editor-loaded textures." }; File.WriteAllText(Path.Combine(_evidence, name + ".json"), JsonUtility.ToJson(record, true)); + if (name == "ten-single-groups-phone-default") + File.WriteAllLines(Path.Combine(_evidence, "editor-loaded-textures.txt"), Resources.FindObjectsOfTypeAll() + .Select(item => $"{AssetDatabase.GetAssetPath(item)};name={item.name};dimensions={item.width}x{item.height};format={(item is Texture2D texture ? texture.format.ToString() : item.GetType().Name)};runtimeBytes={Profiler.GetRuntimeMemorySizeLong(item)}")); } finally { diff --git a/Assets/MagnetismHeroes/UnityRuntime/Presentation/Prefabs/GameApp.prefab b/Assets/MagnetismHeroes/UnityRuntime/Presentation/Prefabs/GameApp.prefab index 39a0b2c..dc958e9 100644 --- a/Assets/MagnetismHeroes/UnityRuntime/Presentation/Prefabs/GameApp.prefab +++ b/Assets/MagnetismHeroes/UnityRuntime/Presentation/Prefabs/GameApp.prefab @@ -8551,17 +8551,17 @@ Light: m_Enabled: 1 serializedVersion: 13 m_Type: 1 - m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Color: {r: 1, g: 0.97, b: 0.91, a: 1} m_Intensity: 1.3 m_Range: 10 m_SpotAngle: 30 m_InnerSpotAngle: 21.80208 m_CookieSize2D: {x: 0.5, y: 0.5} m_Shadows: - m_Type: 0 + m_Type: 2 m_Resolution: -1 m_CustomResolution: -1 - m_Strength: 1 + m_Strength: 0.55 m_Bias: 0.05 m_NormalBias: 0.4 m_NearPlane: 0.2 diff --git a/Assets/Settings/Mobile_RPAsset.asset b/Assets/Settings/Mobile_RPAsset.asset index 199dc88..c1c4124 100644 --- a/Assets/Settings/Mobile_RPAsset.asset +++ b/Assets/Settings/Mobile_RPAsset.asset @@ -54,7 +54,7 @@ MonoBehaviour: m_ReflectionProbeBlending: 1 m_ReflectionProbeBoxProjection: 1 m_ReflectionProbeAtlas: 1 - m_ShadowDistance: 50 + m_ShadowDistance: 30 m_ShadowCascadeCount: 1 m_Cascade2Split: 0.25 m_Cascade3Split: {x: 0.1, y: 0.3} @@ -63,10 +63,10 @@ MonoBehaviour: m_ShadowDepthBias: 1 m_ShadowNormalBias: 1 m_AnyShadowsSupported: 1 - m_SoftShadowsSupported: 0 + m_SoftShadowsSupported: 1 m_ConservativeEnclosingSphere: 1 m_NumIterationsEnclosingSphere: 64 - m_SoftShadowQuality: 2 + m_SoftShadowQuality: 1 m_AdditionalLightsCookieResolution: 1024 m_AdditionalLightsCookieFormat: 1 m_UseSRPBatcher: 1 diff --git a/tools/android/exercise-content.ps1 b/tools/android/exercise-content.ps1 index 707d0ef..a8ced7d 100644 --- a/tools/android/exercise-content.ps1 +++ b/tools/android/exercise-content.ps1 @@ -60,7 +60,7 @@ function StartHome([string]$Name) { try { for($y=1;$y-lt20;$y++){for($x=1;$x-lt30;$x++) { $pixel=$image.GetPixel([int]($image.Width*$x/30),[int]($image.Height*$y/20)) - if($pixel.R-ge150-and$pixel.G-ge180-and$pixel.B-ge210){$sky++} + if($pixel.R-ge135-and$pixel.R-le163-and$pixel.G-ge197-and$pixel.G-le225-and$pixel.B-ge210-and$pixel.B-le239){$sky++} if($pixel.R-ge200-and$pixel.G-ge130-and$pixel.B-le120){$gold++} if($y-le6-and$pixel.R-lt110-and$pixel.G-lt110-and$pixel.B-lt110){$dark++} }} diff --git a/tools/android/final-common.ps1 b/tools/android/final-common.ps1 index 709bb42..2de30ab 100644 --- a/tools/android/final-common.ps1 +++ b/tools/android/final-common.ps1 @@ -101,7 +101,7 @@ function StartHome([string]$Name) { try { for($y=1;$y-lt20;$y++){for($x=1;$x-lt30;$x++) { $pixel=$image.GetPixel([int]($image.Width*$x/30),[int]($image.Height*$y/20)) - if($pixel.R-ge150-and$pixel.G-ge180-and$pixel.B-ge210){$sky++} + if($pixel.R-ge135-and$pixel.R-le163-and$pixel.G-ge197-and$pixel.G-le225-and$pixel.B-ge210-and$pixel.B-le239){$sky++} if($pixel.R-ge200-and$pixel.G-ge130-and$pixel.B-le120){$gold++} if($y-le6-and$pixel.R-lt110-and$pixel.G-lt110-and$pixel.B-lt110){$dark++} }}