Implement the playable first counting puzzle with committed input, persistence and Korean voice

This commit is contained in:
NINI
2026-10-05 01:57:21 +09:00
parent 8d4ffb6550
commit ab63ef532d
157 changed files with 72108 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace MagnetismHeroes.Core
{
public enum SessionScreen { Home, StageSelection, Puzzle, FreePlay }
// Owns committed game state. A gesture preview never reaches this class.
public sealed class GameSession
{
private readonly StageDefinition[] _definitions;
private readonly IProgressStore _store;
private ProgressSave _progress;
public SessionScreen Screen { get; private set; } = SessionScreen.Home;
public BoardRules Board { get; private set; }
public CameraState Camera { get; private set; } = DefaultCamera();
public string LastSaveError { get; private set; }
public bool HasFreePlay => _progress.latestFreePlay != null;
public int[] CompletedStageIds => (int[])_progress.completedStageIds.Clone();
public IReadOnlyList<StageDefinition> Definitions => Array.AsReadOnly(_definitions);
public GameSession(IEnumerable<StageDefinition> definitions, IProgressStore store)
{
_definitions = definitions.ToArray();
_store = store ?? throw new ArgumentNullException(nameof(store));
if (!_store.TryLoad(out var loaded, out var error)) loaded = new ProgressSave();
LastSaveError = error;
if (!SaveValidator.TryValidate(loaded, _definitions, out error))
{ loaded = new ProgressSave(); LastSaveError = error; }
_progress = loaded;
}
public bool IsUnlocked(int stageId) => stageId == 1 || _progress.completedStageIds.Contains(stageId)
|| (stageId >= 2 && stageId <= 30 && _progress.completedStageIds.Contains(stageId - 1));
public void ShowSelection() { SaveCurrent(); Screen = SessionScreen.StageSelection; Board = null; }
public void ShowHome() { SaveCurrent(); Screen = SessionScreen.Home; Board = null; }
public bool StartStage(int stageId)
{
var definition = _definitions.FirstOrDefault(value => value.Id == stageId);
if (definition == null || !IsUnlocked(stageId)) return false;
SaveCurrent();
var restored = _progress.puzzleStates.FirstOrDefault(state => state.stageId == stageId);
Board = new BoardRules(definition, restored);
Camera = restored == null ? DefaultCamera() : CopyCamera(restored.camera);
Screen = SessionScreen.Puzzle;
SaveCurrent();
return true;
}
public bool ContinueFreePlay()
{
if (_progress.latestFreePlay == null) return false;
SaveCurrent();
Board = BoardRules.CreateFreePlay(_progress.latestFreePlay);
Camera = CopyCamera(_progress.latestFreePlay.camera);
Screen = SessionScreen.FreePlay;
return true;
}
public RuleResult MoveGroup(string groupId, Position3 delta) => Commit(() => Board.TryMoveGroup(groupId, delta));
public RuleResult Merge(string first, string second) => Commit(() => Board.TryMerge(first, second));
public RuleResult Count(string ballId) => Commit(() => Board.TryCount(ballId));
public void CommitCamera(CameraState camera)
{
// Board snapshot methods validate all camera coordinates and rotation.
Camera = Board == null ? CopyCamera(camera) : Board.ToFreePlayState(camera).camera;
SaveCurrent();
}
private RuleResult Commit(Func<RuleResult> mutation)
{
if (Board == null) return RuleResult.Reject(RejectionReason.InvalidGroup);
var result = mutation();
if (!result.Accepted) return result;
if (Screen == SessionScreen.Puzzle && Board.IsSolved)
{
// Completion, removing that unfinished puzzle, and creating free play are one saved update.
_progress.completedStageIds = _progress.completedStageIds.Concat(new[] { Board.StageId }).Distinct().OrderBy(id => id).ToArray();
_progress.puzzleStates = _progress.puzzleStates.Where(state => state.stageId != Board.StageId).ToArray();
_progress.latestFreePlay = Board.ToFreePlayState(Camera);
Board = BoardRules.CreateFreePlay(_progress.latestFreePlay);
Screen = SessionScreen.FreePlay;
}
SaveCurrent();
return result;
}
public bool SaveCurrent()
{
if (Board != null)
{
if (Screen == SessionScreen.Puzzle)
_progress.puzzleStates = _progress.puzzleStates.Where(state => state.stageId != Board.StageId)
.Concat(new[] { Board.ToPuzzleState(Camera) }).OrderBy(state => state.stageId).ToArray();
else if (Screen == SessionScreen.FreePlay) _progress.latestFreePlay = Board.ToFreePlayState(Camera);
}
var success = _store.TrySave(_progress, out var error);
LastSaveError = error;
return success;
}
public static CameraState DefaultCamera() => new CameraState {
target = new Position3(0, 0.6f, 0), rotation = new Rotation4(0, 0, 0, 1), distance = 8 };
private static CameraState CopyCamera(CameraState camera) => new CameraState {
target = camera.target, rotation = camera.rotation, distance = camera.distance };
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 85fdaf062aa4ff042b6d37c003441435
@@ -0,0 +1,251 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using MagnetismHeroes.UnityRuntime.Bootstrap;
using MagnetismHeroes.UnityRuntime.Interaction;
using MagnetismHeroes.UnityRuntime.Presentation;
using MagnetismHeroes.UnityRuntime.StageAssets;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.UI;
using UnityEngine.TextCore.LowLevel;
namespace MagnetismHeroes.Editor
{
public static class FirstSliceAssets
{
public const string AppPrefabPath = "Assets/MagnetismHeroes/UnityRuntime/Presentation/Prefabs/GameApp.prefab";
public const string StageOnePath = "Assets/MagnetismHeroes/UnityRuntime/StageAssets/Stage01.asset";
private const string Presentation = "Assets/MagnetismHeroes/UnityRuntime/Presentation/";
private static readonly Color Ink = new Color(0.12f, 0.2f, 0.31f);
private static TMP_FontAsset _font;
[MenuItem("Magnetism Heroes/Build First Slice Assets")]
public static void Build()
{
foreach (var directory in new[] { "Prefabs", "Materials", "Fonts", "Audio" }) Directory.CreateDirectory(Presentation + directory);
AssetDatabase.Refresh();
if (TMP_Settings.instance == null)
{
var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(TMP_Settings).Assembly);
// uGUI 2.6's canonical helper does not resolve registry PackageCache paths.
UnityEditor.AssetPackage.Package.Import(Path.Combine(package.resolvedPath, "Package Resources/TMP Essential Resources.unitypackage"), false);
EditorApplication.wantsToQuit += WaitForResources;
EditorApplication.update += ContinueAfterResources;
return;
}
AssetDatabase.Refresh();
_font = FontAsset();
var stage = AssetDatabase.LoadAssetAtPath<StageAsset>(StageOnePath);
if (stage == null) { stage = ScriptableObject.CreateInstance<StageAsset>(); AssetDatabase.CreateAsset(stage, StageOnePath); }
var sphereMaterial = Material("MagnetBlue", new Color(0.15f, 0.68f, 0.92f));
var markerMaterial = Material("CountGold", new Color(1, 0.76f, 0.14f));
var faceMaterial = Material("FaceInk", Ink);
var baseMaterial = Material("PlaygroundMint", new Color(0.65f, 0.88f, 0.73f));
var propMaterial = Material("ToyCoral", new Color(1, 0.57f, 0.43f));
var ballPrefab = BallPrefab(sphereMaterial, markerMaterial);
var badgePrefab = BadgePrefab(faceMaterial);
var root = new GameObject("MagnetismHeroesApp");
var app = root.AddComponent<GameApp>();
var world = new GameObject("Playground"); world.transform.SetParent(root.transform);
var cameraObject = new GameObject("GameCamera", typeof(Camera), typeof(AudioListener)); cameraObject.transform.SetParent(root.transform);
var camera = cameraObject.GetComponent<Camera>(); camera.tag = "MainCamera"; camera.backgroundColor = new Color(0.84f, 0.94f, 1);
camera.clearFlags = CameraClearFlags.SolidColor; camera.fieldOfView = 46;
var light = new GameObject("Sun", typeof(Light)); light.transform.SetParent(root.transform);
light.transform.rotation = Quaternion.Euler(45, -25, 0); light.GetComponent<Light>().type = LightType.Directional;
light.GetComponent<Light>().intensity = 1.3f;
var island = Prop("PlaygroundIsland", PrimitiveType.Cylinder, world.transform, new Vector3(0, -0.7f, 0), new Vector3(6, 0.35f, 4.5f), baseMaterial);
// The primitive capsule grows to the widest scaled radius and hides the floating sphere.
UnityEngine.Object.DestroyImmediate(island.GetComponent<Collider>());
island.AddComponent<MeshCollider>().sharedMesh = island.GetComponent<MeshFilter>().sharedMesh;
Prop("ToyStep", PrimitiveType.Cube, world.transform, new Vector3(1.8f, -0.1f, 1.1f), new Vector3(1, 0.5f, 1), propMaterial);
Prop("ToyArchPost", PrimitiveType.Cylinder, world.transform, new Vector3(-2.4f, -0.05f, 1), new Vector3(0.45f, 0.65f, 0.45f), propMaterial);
var boardObject = new GameObject("Friends"); boardObject.transform.SetParent(world.transform);
var presenter = boardObject.AddComponent<BoardPresenter>();
Set(presenter, "_ballPrefab", ballPrefab); Set(presenter, "_badgePrefab", badgePrefab); Set(presenter, "_camera", camera);
var orbit = root.AddComponent<OrbitCamera>(); Set(orbit, "_camera", camera);
var input = root.AddComponent<BoardInput>(); Set(input, "_app", app); Set(input, "_presenter", presenter); Set(input, "_orbit", orbit);
var voice = root.AddComponent<VoicePlayer>();
var source = root.AddComponent<AudioSource>(); source.playOnAwake = false; source.spatialBlend = 0;
Set(voice, "_source", source); ConfigureVoices(voice);
source.outputAudioMixerGroup = Mixer().FindMatchingGroups("Voice").Single();
var events = new GameObject("EventSystem", typeof(EventSystem), typeof(InputSystemUIInputModule)); events.transform.SetParent(root.transform);
events.GetComponent<InputSystemUIInputModule>().AssignDefaultActions();
var canvasObject = new GameObject("Interface", typeof(RectTransform), typeof(Canvas), typeof(UnityEngine.UI.CanvasScaler), typeof(UnityEngine.UI.GraphicRaycaster));
canvasObject.transform.SetParent(root.transform); canvasObject.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
var scaler = canvasObject.GetComponent<UnityEngine.UI.CanvasScaler>(); scaler.uiScaleMode = UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1280, 720); scaler.matchWidthOrHeight = 0.5f;
var safe = Panel("SafeArea", canvasObject.transform); safe.AddComponent<SafeAreaPanel>();
var home = Panel("Home", safe.transform); var selection = Panel("StageSelection", safe.transform); var play = Panel("Play", safe.transform);
Label("Title", home.transform, "자석 영웅", new Vector2(0.5f, 0.78f), new Vector2(660, 110), 66);
Label("Subtitle", home.transform, "숫자 친구와 입체 놀이터", new Vector2(0.5f, 0.63f), new Vector2(720, 70), 30);
var playButton = Button("PlayButton", home.transform, "친구 찾기", new Vector2(0.5f, 0.43f));
var continueButton = Button("ContinueButton", home.transform, "자유 놀이 이어하기", new Vector2(0.5f, 0.25f));
var saveNotice = Label("SaveNotice", home.transform, "", new Vector2(0.5f, 0.07f), new Vector2(1040, 55), 24);
Label("SelectionTitle", selection.transform, "숫자 친구를 만나 볼까?", new Vector2(0.5f, 0.78f), new Vector2(820, 90), 42);
var stageButton = Button("Stage01Button", selection.transform, "1 친구 찾기", new Vector2(0.5f, 0.48f));
var selectionHomeButton = Button("SelectionHomeButton", selection.transform, "처음으로", new Vector2(0.5f, 0.24f));
var stageTitle = Label("StageTitle", play.transform, "숫자 1 친구 찾기", new Vector2(0.5f, 0.92f), new Vector2(580, 60), 34);
var instruction = Label("Instruction", play.transform, "친구를 찾아 끌어와 볼까?", new Vector2(0.5f, 0.12f), new Vector2(960, 70), 32);
var counter = Label("Counter", play.transform, "센 친구 0 / 1", new Vector2(0.83f, 0.9f), new Vector2(330, 70), 28);
var homeButton = Button("HomeButton", play.transform, "처음으로", new Vector2(0.13f, 0.9f), new Vector2(230, 72), 28);
Label("CameraHelp", play.transform, "빈 곳을 쓸면 회전 · 두 손가락으로 확대", new Vector2(0.5f, 0.035f), new Vector2(1010, 36), 23);
Set(app, "_homePanel", home); Set(app, "_selectionPanel", selection); Set(app, "_playPanel", play); Set(app, "_world", world);
Set(app, "_instruction", instruction); Set(app, "_counter", counter); Set(app, "_stageTitle", stageTitle); Set(app, "_saveNotice", saveNotice);
Set(app, "_playButton", playButton); Set(app, "_continueButton", continueButton); Set(app, "_stageOneButton", stageButton);
Set(app, "_homeButton", homeButton); Set(app, "_selectionHomeButton", selectionHomeButton);
Set(app, "_presenter", presenter); Set(app, "_orbit", orbit); Set(app, "_input", input); Set(app, "_voice", voice);
var serialized = new SerializedObject(app); var stages = serialized.FindProperty("_stages"); stages.arraySize = 1;
stages.GetArrayElementAtIndex(0).objectReferenceValue = stage; serialized.ApplyModifiedPropertiesWithoutUndo();
home.SetActive(true); selection.SetActive(false); play.SetActive(false); world.SetActive(false);
PrefabUtility.SaveAsPrefabAsset(root, AppPrefabPath); UnityEngine.Object.DestroyImmediate(root);
AssetDatabase.SaveAssets(); Debug.Log("[FirstSlice] Real Stage01 and GameApp assets created. Main scene remains root-owned.");
}
private static bool WaitForResources() => false;
private static void ContinueAfterResources()
{
if (Resources.Load<TMP_Settings>("TMP Settings") == null || EditorApplication.isUpdating) return;
EditorApplication.update -= ContinueAfterResources; EditorApplication.wantsToQuit -= WaitForResources;
try { Build(); if (Application.isBatchMode) EditorApplication.Exit(0); }
catch (Exception exception) { Debug.LogException(exception); if (Application.isBatchMode) EditorApplication.Exit(1); }
}
private static TMP_FontAsset FontAsset()
{
const string path = Presentation + "Fonts/NotoSansCJKkr-FirstSlice.asset";
var font = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(path);
bool created = font == null;
if (created)
{
var source = AssetDatabase.LoadAssetAtPath<Font>(Presentation + "Fonts/NotoSansCJKkr-Regular.otf");
font = TMP_FontAsset.CreateFontAsset(source, 48, 5, GlyphRenderMode.SDFAA, 1024, 1024, AtlasPopulationMode.Dynamic, false);
}
if (font == null) throw new InvalidOperationException("Could not create Korean font.");
font.name = "NotoSansCJKkr-FirstSlice";
var characters = "0123456789 자석 영웅 숫자 친구와 입체 놀이터 친구 찾기 자유 놀이 이어하기 숫자 친구를 만나 볼까? 처음으로 숫자 1 친구 찾기 친구를 찾아 끌어와 볼까? 센 친구 0 / 1 빈 곳을 쓸면 회전 · 두 손가락으로 확대 이전 기록으로 이어서 시작할게요. 기록을 읽지 못해 처음부터 시작할게요. 친구를 먼저 끌어와 볼까? 자유 놀이터 해냈어! 친구와 자유롭게 놀아 보자. 모두 모았네! 하나씩 눌러 세어 보자.";
typeof(TMP_FontAsset).GetProperty("SourceFont_EditorRef", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(font, AssetDatabase.LoadAssetAtPath<Font>(Presentation + "Fonts/NotoSansCJKkr-Regular.otf"));
font.atlasPopulationMode = AtlasPopulationMode.Dynamic;
var serializedFont = new SerializedObject(font);
serializedFont.FindProperty("m_SourceFontFilePath").stringValue = Presentation + "Fonts/NotoSansCJKkr-Regular.otf";
serializedFont.ApplyModifiedPropertiesWithoutUndo();
if (!font.TryAddCharacters(characters, out var missing)) throw new InvalidOperationException("Missing font glyphs: " + missing);
font.atlasPopulationMode = AtlasPopulationMode.Static; font.faceInfo = SetFaceScale(font.faceInfo);
if (created)
{
AssetDatabase.CreateAsset(font, path); AssetDatabase.AddObjectToAsset(font.material, font);
foreach (var atlas in font.atlasTextures) AssetDatabase.AddObjectToAsset(atlas, font);
}
EditorUtility.SetDirty(font);
return font;
}
private static UnityEngine.TextCore.FaceInfo SetFaceScale(UnityEngine.TextCore.FaceInfo face) { face.scale = 1; return face; }
private static Material Material(string name, Color color)
{
string path = Presentation + "Materials/" + name + ".mat";
var material = AssetDatabase.LoadAssetAtPath<Material>(path);
if (material == null) { material = new Material(Shader.Find("Universal Render Pipeline/Simple Lit")); AssetDatabase.CreateAsset(material, path); }
material.color = color; return material;
}
private static BallView BallPrefab(Material body, Material marker)
{
var root = GameObject.CreatePrimitive(PrimitiveType.Sphere); root.name = "MagnetFriend";
root.GetComponent<Renderer>().sharedMaterial = body;
var view = root.AddComponent<BallView>(); Set(view, "_body", root.GetComponent<Renderer>());
var counted = Prop("CountMarker", PrimitiveType.Cube, root.transform, new Vector3(0, 0.55f, 0), new Vector3(0.24f, 0.08f, 0.24f), marker);
UnityEngine.Object.DestroyImmediate(counted.GetComponent<Collider>()); counted.SetActive(false); Set(view, "_countMarker", counted);
var prefab = PrefabUtility.SaveAsPrefabAsset(root, Presentation + "Prefabs/MagnetFriend.prefab"); UnityEngine.Object.DestroyImmediate(root);
return prefab.GetComponent<BallView>();
}
private static GroupBadge BadgePrefab(Material ink)
{
var root = new GameObject("GroupBadge"); var badge = root.AddComponent<GroupBadge>();
var label = new GameObject("Number", typeof(TextMeshPro)); label.transform.SetParent(root.transform);
var text = label.GetComponent<TextMeshPro>(); text.font = _font; text.fontSize = 5; text.alignment = TextAlignmentOptions.Center;
text.color = Ink; text.rectTransform.sizeDelta = new Vector2(2, 0.7f); text.text = "1"; text.enableAutoSizing = false;
var face = new GameObject("RepresentativeFace"); face.transform.SetParent(root.transform);
var left = Prop("LeftEye", PrimitiveType.Quad, face.transform, new Vector3(-0.15f, 0.05f, 0), new Vector3(0.08f, 0.11f, 1), ink);
var right = Prop("RightEye", PrimitiveType.Quad, face.transform, new Vector3(0.15f, 0.05f, 0), new Vector3(0.08f, 0.11f, 1), ink);
UnityEngine.Object.DestroyImmediate(left.GetComponent<Collider>()); UnityEngine.Object.DestroyImmediate(right.GetComponent<Collider>());
var smile = new GameObject("Smile", typeof(LineRenderer)); smile.transform.SetParent(face.transform); smile.transform.localPosition = Vector3.zero;
var line = smile.GetComponent<LineRenderer>(); line.sharedMaterial = ink; line.useWorldSpace = false; line.positionCount = 9; line.widthMultiplier = 0.035f;
for (int i = 0; i < 9; i++) { float x = (i - 4) * 0.035f; line.SetPosition(i, new Vector3(x, x * x * 3 - 0.14f, 0)); }
Set(badge, "_number", text); Set(badge, "_face", face.transform);
var prefab = PrefabUtility.SaveAsPrefabAsset(root, Presentation + "Prefabs/GroupBadge.prefab"); UnityEngine.Object.DestroyImmediate(root);
return prefab.GetComponent<GroupBadge>();
}
private static GameObject Prop(string name, PrimitiveType primitive, Transform parent, Vector3 position, Vector3 scale, Material material)
{
var prop = GameObject.CreatePrimitive(primitive); prop.name = name; prop.transform.SetParent(parent);
prop.transform.localPosition = position; prop.transform.localScale = scale; prop.GetComponent<Renderer>().sharedMaterial = material; return prop;
}
private static GameObject Panel(string name, Transform parent)
{
var panel = new GameObject(name, typeof(RectTransform)); panel.transform.SetParent(parent, false);
var rect = (RectTransform)panel.transform; rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = rect.offsetMax = Vector2.zero; return panel;
}
private static TMP_Text Label(string name, Transform parent, string content, Vector2 anchor, Vector2 size, int fontSize)
{
var label = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI)); label.transform.SetParent(parent, false);
var text = label.GetComponent<TextMeshProUGUI>(); text.font = _font; text.fontSize = fontSize; text.text = content;
text.color = Ink; text.alignment = TextAlignmentOptions.Center; text.raycastTarget = false; text.enableAutoSizing = false;
var rect = (RectTransform)label.transform; rect.anchorMin = rect.anchorMax = anchor; rect.anchoredPosition = Vector2.zero; rect.sizeDelta = size; return text;
}
private static UnityEngine.UI.Button Button(string name, Transform parent, string content, Vector2 anchor, Vector2? size = null, int fontSize = 34)
{
var buttonObject = new GameObject(name, typeof(RectTransform), typeof(UnityEngine.UI.Image), typeof(UnityEngine.UI.Button)); buttonObject.transform.SetParent(parent, false);
var rect = (RectTransform)buttonObject.transform; rect.anchorMin = rect.anchorMax = anchor; rect.sizeDelta = size ?? new Vector2(460, 90); rect.anchoredPosition = Vector2.zero;
var image = buttonObject.GetComponent<UnityEngine.UI.Image>(); image.color = new Color(1, 0.8f, 0.3f); image.raycastTarget = true;
var button = buttonObject.GetComponent<UnityEngine.UI.Button>(); button.targetGraphic = image;
var text = Label("Label", buttonObject.transform, content, new Vector2(0.5f, 0.5f), rect.sizeDelta, fontSize); return button;
}
private static void Set(UnityEngine.Object target, string field, UnityEngine.Object value)
{ var serialized = new SerializedObject(target); serialized.FindProperty(field).objectReferenceValue = value; serialized.ApplyModifiedPropertiesWithoutUndo(); }
private static void ConfigureVoices(VoicePlayer voice)
{
var clips = AssetDatabase.FindAssets("t:AudioClip", new[] { Presentation + "Audio" })
.Select(guid => AssetDatabase.LoadAssetAtPath<AudioClip>(AssetDatabase.GUIDToAssetPath(guid))).Where(clip => clip != null).ToArray();
foreach (var clip in clips)
{
var importer = (AudioImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(clip));
var settings = importer.defaultSampleSettings; settings.loadType = AudioClipLoadType.DecompressOnLoad;
settings.compressionFormat = AudioCompressionFormat.PCM; settings.preloadAudioData = true;
importer.defaultSampleSettings = settings; importer.forceToMono = true; importer.SaveAndReimport();
}
var serialized = new SerializedObject(voice); var property = serialized.FindProperty("_clips"); property.arraySize = clips.Length;
for (int i = 0; i < clips.Length; i++)
{ var entry = property.GetArrayElementAtIndex(i); entry.FindPropertyRelative("id").stringValue = clips[i].name; entry.FindPropertyRelative("clip").objectReferenceValue = clips[i]; }
serialized.ApplyModifiedPropertiesWithoutUndo();
}
private static AudioMixer Mixer()
{
const string path = Presentation + "Audio/GameAudio.mixer";
var mixer = AssetDatabase.LoadAssetAtPath<AudioMixer>(path);
var controllerType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.Audio.AudioMixerController");
var flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
if (mixer == null) mixer = (AudioMixer)controllerType.GetMethod("CreateMixerControllerAtPath", flags).Invoke(null, new object[] { path });
var instanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
var master = controllerType.GetProperty("masterGroup", instanceFlags).GetValue(mixer);
var childrenProperty = master.GetType().GetProperty("children", instanceFlags);
var children = (Array)childrenProperty.GetValue(master);
var newChildren = children.Cast<object>().ToList();
foreach (var groupName in new[] { "Voice", "SFX", "Music" })
{
if (mixer.FindMatchingGroups(groupName).Length != 0) continue;
var group = controllerType.GetMethod("CreateNewGroup", instanceFlags).Invoke(mixer, new object[] { groupName, false });
newChildren.Add(group);
}
var array = Array.CreateInstance(master.GetType(), newChildren.Count);
for (int index = 0; index < newChildren.Count; index++) array.SetValue(newChildren[index], index);
childrenProperty.SetValue(master, array); EditorUtility.SetDirty(mixer); EditorUtility.SetDirty((UnityEngine.Object)master);
return mixer;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c21a9cea2d948bd4185448d90d4da2e8
@@ -0,0 +1,41 @@
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using TMPro;
using MagnetismHeroes.UnityRuntime.StageAssets;
using MagnetismHeroes.UnityRuntime.Bootstrap;
namespace MagnetismHeroes.Tests
{
public sealed class FirstSliceAssetTests
{
[Test] public void FirstPuzzleHasAnExecutableAppPrefab()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/MagnetismHeroes/UnityRuntime/Presentation/Prefabs/GameApp.prefab");
Assert.That(prefab, Is.Not.Null, "The first puzzle must have a real Inspector-editable app prefab.");
Assert.That(prefab.GetComponent<GameApp>(), Is.Not.Null);
var serialized = new SerializedObject(prefab.GetComponent<GameApp>());
foreach (var field in new[] { "_homePanel", "_selectionPanel", "_playPanel", "_world", "_instruction", "_counter", "_stageTitle", "_saveNotice",
"_playButton", "_continueButton", "_stageOneButton", "_homeButton", "_selectionHomeButton", "_presenter", "_orbit", "_input", "_voice" })
Assert.That(serialized.FindProperty(field).objectReferenceValue, Is.Not.Null, field);
}
[Test] public void StaticKoreanFontContainsEveryVisibleFirstSliceCharacter()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/MagnetismHeroes/UnityRuntime/Presentation/Prefabs/GameApp.prefab");
var texts = prefab.GetComponentsInChildren<TMP_Text>(true);
foreach (var text in texts)
{
Assert.That(text.font.atlasPopulationMode, Is.EqualTo(AtlasPopulationMode.Static));
Assert.That(text.enableAutoSizing, Is.False);
foreach (var character in text.text) Assert.That(text.font.HasCharacter(character), Is.True, text.name + ": " + character);
}
}
[Test] public void StageAssetConvertsToAnIndependentDefinition()
{
var stage = AssetDatabase.LoadAssetAtPath<StageAsset>("Assets/MagnetismHeroes/UnityRuntime/StageAssets/Stage01.asset");
var before = EditorJsonUtility.ToJson(stage); var definition = stage.ToDefinition();
Assert.That(definition.Id, Is.EqualTo(1)); Assert.That(definition.Total, Is.EqualTo(1));
Assert.That(EditorJsonUtility.ToJson(stage), Is.EqualTo(before));
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 599347c36cd01ad4fa166c4c6b6f3b77
@@ -0,0 +1,72 @@
using System.Linq;
using MagnetismHeroes.Core;
using NUnit.Framework;
namespace MagnetismHeroes.Tests
{
public sealed class GameSessionTests
{
private sealed class MemoryStore : IProgressStore
{
public ProgressSave Save = new ProgressSave();
public string LoadWarning;
public bool TryLoad(out ProgressSave save, out string error) { save = Save; error = LoadWarning; return true; }
public bool TrySave(ProgressSave save, out string error) { Save = save; error = null; return true; }
}
private static StageDefinition One() => new StageDefinition(1, StageKind.Counting, new[] { 1 }, new[] { 1 });
[Test] public void LoadRecoveryWarningIsRetainedForDiagnostics()
{
var session = new GameSession(new[] { One() }, new MemoryStore { LoadWarning = "Recovered valid backup" });
Assert.That(session.LastSaveError, Is.EqualTo("Recovered valid backup"));
}
[Test] public void FirstStageCannotCompleteWithoutACommittedCollection()
{
var store = new MemoryStore(); var session = new GameSession(new[] { One() }, store);
Assert.That(session.StartStage(1), Is.True);
var ball = session.Board.Balls.Single(); var group = session.Board.Groups.Single();
Assert.That(session.Count(ball.id).Reason, Is.EqualTo(RejectionReason.NotCollected));
Assert.That(session.MoveGroup(group.id, new Position3()).Accepted, Is.False);
Assert.That(store.Save.completedStageIds, Is.Empty);
Assert.That(session.Screen, Is.EqualTo(SessionScreen.Puzzle));
}
[Test] public void MoveAndCountCreateOneAtomicCompletionAndKeepTheSameBoard()
{
var store = new MemoryStore(); var session = new GameSession(new[] { One() }, store); session.StartStage(1);
var ballId = session.Board.Balls.Single().id; var groupId = session.Board.Groups.Single().id;
session.MoveGroup(groupId, new Position3(1, 2, -1));
var position = session.Board.Balls.Single().position;
Assert.That(session.Count(ballId).Count, Is.EqualTo(1));
Assert.That(session.Screen, Is.EqualTo(SessionScreen.FreePlay));
Assert.That(store.Save.completedStageIds, Is.EqualTo(new[] { 1 }));
Assert.That(store.Save.puzzleStates, Is.Empty);
Assert.That(store.Save.latestFreePlay.balls.Single().position, Is.EqualTo(position));
Assert.That(session.Count(ballId).Accepted, Is.False);
Assert.That(store.Save.completedStageIds, Is.EqualTo(new[] { 1 }));
}
[Test] public void IncompleteBoardRestoresCommittedPositionCameraAndCollection()
{
var store = new MemoryStore(); var session = new GameSession(new[] { One() }, store); session.StartStage(1);
session.MoveGroup(session.Board.Groups.Single().id, new Position3(1, 2, 3));
var camera = GameSession.DefaultCamera(); camera.distance = 11;
session.CommitCamera(camera); session.ShowHome();
var resumed = new GameSession(new[] { One() }, store); resumed.StartStage(1);
Assert.That(resumed.Board.Balls.Single().position, Is.EqualTo(new Position3(1, 2, 3)));
Assert.That(resumed.Board.CollectedBallIds.Length, Is.EqualTo(1));
Assert.That(resumed.Board.CountedCount, Is.Zero);
Assert.That(resumed.Camera.distance, Is.EqualTo(11));
Assert.That(resumed.Screen, Is.EqualTo(SessionScreen.Puzzle));
}
[Test] public void CompletedStageReplayKeepsCompletionAndItsPreviousFreePlaySeparately()
{
var store = new MemoryStore(); var session = new GameSession(new[] { One() }, store); session.StartStage(1);
session.MoveGroup(session.Board.Groups.Single().id, new Position3(2, 0, 0)); session.Count(session.Board.Balls.Single().id);
var freePosition = store.Save.latestFreePlay.balls.Single().position;
Assert.That(session.IsUnlocked(2), Is.True); Assert.That(session.IsUnlocked(3), Is.False);
session.StartStage(1); session.MoveGroup(session.Board.Groups.Single().id, new Position3(1, 0, 0));
Assert.That(store.Save.completedStageIds, Is.EqualTo(new[] { 1 }));
Assert.That(store.Save.latestFreePlay.balls.Single().position, Is.EqualTo(freePosition));
Assert.That(store.Save.puzzleStates.Single().stageId, Is.EqualTo(1));
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 39eee6770356bc246bc854f34306e1fb
@@ -0,0 +1,103 @@
using System;
using System.IO;
using System.Linq;
using MagnetismHeroes.Core;
using MagnetismHeroes.UnityRuntime.Persistence;
using NUnit.Framework;
namespace MagnetismHeroes.Tests
{
public sealed class LocalProgressStoreTests
{
private string _directory, _path;
private StageDefinition _stage;
[SetUp] public void SetUp()
{
_directory = Path.Combine(Path.GetTempPath(), "MagnetismHeroes-" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_directory);
_path = Path.Combine(_directory, "progress.json");
_stage = new StageDefinition(1, StageKind.Counting, new[] { 1 }, new[] { 1 });
}
[TearDown] public void TearDown() => Directory.Delete(_directory, true);
private LocalProgressStore Store() => new LocalProgressStore(_path, new[] { _stage });
private ProgressSave Puzzle()
{ var board = new BoardRules(_stage); return new ProgressSave { puzzleStates = new[] { board.ToPuzzleState(GameSession.DefaultCamera()) } }; }
[Test] public void CommittedPositionAndCameraRoundTripWithoutPartialLoad()
{
var progress = Puzzle(); progress.puzzleStates[0].balls[0].position = new Position3(1, -2, 3);
progress.puzzleStates[0].camera.distance = 10;
Assert.That(Store().TrySave(progress, out var error), Is.True, error);
Assert.That(Store().TryLoad(out var loaded, out error), Is.True, error);
Assert.That(loaded.puzzleStates[0].balls[0].position, Is.EqualTo(new Position3(1, -2, 3)));
Assert.That(loaded.puzzleStates[0].camera.distance, Is.EqualTo(10));
}
[TestCase("position")]
[TestCase("x")]
[TestCase("camera")]
[TestCase("countedBallIds")]
[TestCase("latestFreePlay")]
public void MissingRequiredFieldsAreRejectedInsteadOfDefaulted(string field)
{
Assert.That(Store().TrySave(Puzzle(), out _), Is.True);
var json = File.ReadAllText(_path).Replace("\"" + field + "\":", "\"removed_" + field + "\":");
File.WriteAllText(_path, json);
Assert.That(Store().TryLoad(out var loaded, out var error), Is.False);
Assert.That(loaded, Is.Null); Assert.That(error, Does.Contain(field));
}
[Test] public void DuplicateJsonKeysAndTrailingContentAreRejected()
{
File.WriteAllText(_path, "{\"schemaVersion\":1,\"schemaVersion\":1}");
Assert.That(Store().TryLoad(out _, out _), Is.False);
File.Delete(_path); Assert.That(Store().TrySave(Puzzle(), out _), Is.True);
File.WriteAllText(_path, File.ReadAllText(_path) + " {}");
Assert.That(Store().TryLoad(out _, out _), Is.False);
}
[Test] public void ValidBackupRecoversWholeBoardAndDamagedOriginalSurvivesNextSave()
{
var store = Store(); var progress = Puzzle(); Assert.That(store.TrySave(progress, out _), Is.True);
progress.puzzleStates[0].camera.distance = 12; Assert.That(store.TrySave(progress, out _), Is.True);
File.WriteAllText(_path, "broken-primary");
Assert.That(store.TryLoad(out var loaded, out var error), Is.True, error);
Assert.That(loaded.puzzleStates[0].camera.distance, Is.EqualTo(8));
Assert.That(error, Does.Contain("Recovered"));
Assert.That(store.TrySave(loaded, out error), Is.True, error);
var damaged = Directory.GetFiles(_directory, "*.damaged-*").Single();
Assert.That(File.ReadAllText(damaged), Is.EqualTo("broken-primary"));
Assert.That(File.ReadAllText(_path + ".bak"), Does.Not.Contain("broken-primary"));
Assert.That(File.Exists(_path + ".tmp"), Is.False);
}
[Test] public void WithoutValidBackupLoaderDoesNotOverwriteDamagedEvidence()
{
File.WriteAllText(_path, "broken-primary"); File.WriteAllText(_path + ".bak", "broken-backup");
var store = Store(); Assert.That(store.TryLoad(out _, out _), Is.False);
Assert.That(File.ReadAllText(_path), Is.EqualTo("broken-primary"));
Assert.That(store.TrySave(new ProgressSave(), out var error), Is.True, error);
Assert.That(Directory.GetFiles(_directory, "*.damaged-*").Select(File.ReadAllText), Does.Contain("broken-primary"));
}
[Test] public void InvalidGroupMembershipDoesNotReplaceAValidSave()
{
var store = Store(); var progress = Puzzle(); Assert.That(store.TrySave(progress, out _), Is.True);
var before = File.ReadAllBytes(_path); progress.puzzleStates[0].groups[0].ballIds = new[] { "missing" };
Assert.That(store.TrySave(progress, out _), Is.False); Assert.That(File.ReadAllBytes(_path), Is.EqualTo(before));
}
[TestCase("schemaVersion", "null")]
[TestCase("schemaVersion", "\"1\"")]
[TestCase("schemaVersion", "1.5")]
[TestCase("distance", "null")]
[TestCase("distance", "\"8\"")]
[TestCase("x", "null")]
[TestCase("x", "\"0\"")]
public void WrongScalarTypesAreRejected(string field, string value)
{
Assert.That(Store().TrySave(Puzzle(), out _), Is.True);
var json = File.ReadAllText(_path);
json = System.Text.RegularExpressions.Regex.Replace(json, "\"" + field + "\":\\s*-?[0-9]+(?:\\.[0-9]+)?", "\"" + field + "\":" + value);
File.WriteAllText(_path, json); Assert.That(Store().TryLoad(out var loaded, out _), Is.False); Assert.That(loaded, Is.Null);
}
[Test] public void SavedJsonContainsOnlyContractFields()
{
Assert.That(Store().TrySave(Puzzle(), out _), Is.True);
Assert.That(File.ReadAllText(_path), Does.Not.Contain("IsValid"));
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1d3b301c187eab84086f1a6504b44942
@@ -0,0 +1,213 @@
#if UNITY_EDITOR
using System.Collections;
using System.Linq;
using MagnetismHeroes.Core;
using MagnetismHeroes.UnityRuntime.Bootstrap;
using MagnetismHeroes.UnityRuntime.StageAssets;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.TestTools;
namespace MagnetismHeroes.Tests
{
public sealed class FirstPuzzlePlayTests
{
private sealed class MemoryStore : IProgressStore
{
public ProgressSave Saved = new ProgressSave();
public bool TryLoad(out ProgressSave save, out string error) { save = Saved; error = null; return true; }
public bool TrySave(ProgressSave save, out string error) { Saved = save; error = null; return true; }
}
private GameApp _app; private MemoryStore _store; private Touchscreen _touchscreen;
private InputSettings _originalInputSettings, _testInputSettings;
private HideFlags _originalSettingsFlags;
[UnitySetUp] public IEnumerator SetUp()
{
// Batch tests have no focused Game View; route synthetic pointers into the actual player loop.
_originalInputSettings = InputSystem.settings; _originalSettingsFlags = _originalInputSettings.hideFlags;
// InputManager destroys its HideAndDontSave default on replacement; keep it for exact restoration.
_originalInputSettings.hideFlags = HideFlags.DontSave;
_testInputSettings = Object.Instantiate(_originalInputSettings); _testInputSettings.hideFlags = HideFlags.DontSave;
_testInputSettings.editorInputBehaviorInPlayMode = InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView;
_testInputSettings.backgroundBehavior = InputSettings.BackgroundBehavior.IgnoreFocus;
InputSystem.settings = _testInputSettings;
foreach (var listener in Object.FindObjectsByType<AudioListener>(FindObjectsSortMode.None)) listener.enabled = false;
foreach (var camera in Object.FindObjectsByType<Camera>(FindObjectsSortMode.None)) camera.enabled = false;
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/MagnetismHeroes/UnityRuntime/Presentation/Prefabs/GameApp.prefab");
Assert.That(prefab, Is.Not.Null); _app = Object.Instantiate(prefab).GetComponent<GameApp>();
_store = new MemoryStore(); _app.Initialize(_store); yield return null;
}
[UnityTearDown] public IEnumerator TearDown()
{
if (_touchscreen != null) InputSystem.RemoveDevice(_touchscreen); Object.Destroy(_app.gameObject);
InputSystem.settings = _originalInputSettings; _originalInputSettings.hideFlags = _originalSettingsFlags;
Object.Destroy(_testInputSettings); yield return null;
}
private Vector2 BallPoint()
{
var ball = _app.Presenter.Balls.Values.Single();
Vector2 point = _app.Presenter.Camera.WorldToScreenPoint(ball.transform.position);
var ray = _app.Presenter.Camera.ScreenPointToRay(point);
Assert.That(Physics.Raycast(ray, out var hit, 100), Is.True,
$"Ball ray miss: screen={Screen.width}x{Screen.height}, point={point}, ball={ball.transform.position}, camera={_app.Presenter.Camera.transform.position}, active={ball.gameObject.activeInHierarchy}");
Assert.That(hit.collider.gameObject, Is.EqualTo(ball.gameObject), $"First visible ball ray must hit sphere: hit={hit.collider.name} point={hit.point}, screen={Screen.width}x{Screen.height} ballPoint={point}, ray={ray}, ball={ball.transform.position}, camera={_app.Presenter.Camera.transform.position}, collider={ball.GetComponent<Collider>().bounds}, active={ball.gameObject.activeInHierarchy}");
return point;
}
private void Drag(Vector2 start, Vector2 end) { _app.Input.Begin(start, false); _app.Input.Move(end); _app.Input.End(end); }
private void Tap(Vector2 point) { _app.Input.Begin(point, false); _app.Input.End(point); }
[UnityTest] public IEnumerator InspectorButtonsNavigateAndHomeKeepsACameraAndListener()
{
Assert.That(_app.Session.Screen, Is.EqualTo(SessionScreen.Home));
Assert.That(_app.Presenter.Camera.isActiveAndEnabled, Is.True);
Assert.That(_app.GetComponentInChildren<AudioListener>().isActiveAndEnabled, Is.True);
_app.transform.Find("Interface/SafeArea/Home/PlayButton").GetComponent<UnityEngine.UI.Button>().onClick.Invoke();
Assert.That(_app.Session.Screen, Is.EqualTo(SessionScreen.StageSelection));
_app.transform.Find("Interface/SafeArea/StageSelection/Stage01Button").GetComponent<UnityEngine.UI.Button>().onClick.Invoke();
Assert.That(_app.Session.Screen, Is.EqualTo(SessionScreen.Puzzle));
Assert.That(_app.Presenter.Balls.Count, Is.EqualTo(1)); yield return null;
}
[UnityTest] public IEnumerator TapBeforeCollectionIsRejectedThenDragAndTapCompleteOnTheSameBoard()
{
_app.StartStage(1); yield return null; Physics.SyncTransforms();
var point = BallPoint(); Tap(point);
Assert.That(_app.Session.Board.CountedCount, Is.Zero); Assert.That(_app.Instruction, Does.Contain("먼저"));
Drag(point, point + new Vector2(90, 25)); yield return null; Physics.SyncTransforms();
Assert.That(_app.Session.Board.CollectedBallIds.Length, Is.EqualTo(1));
var ball = _app.Session.Board.Balls.Single(); Tap(BallPoint());
Assert.That(_app.Session.Screen, Is.EqualTo(SessionScreen.FreePlay));
Assert.That(_store.Saved.completedStageIds, Is.EqualTo(new[] { 1 })); Assert.That(_store.Saved.puzzleStates, Is.Empty);
Assert.That(_app.Session.Board.Balls.Single().position, Is.EqualTo(ball.position));
Assert.That(_app.Voice.PendingCount, Is.EqualTo(1), "The success boundary must keep the last count voice.");
Tap(BallPoint()); Assert.That(_app.Voice.PendingCount, Is.EqualTo(1));
yield return null;
}
[UnityTest] public IEnumerator CanceledAndOffscreenDragsRestoreOnlyCommittedPositions()
{
_app.StartStage(1); yield return null; Physics.SyncTransforms();
var original = _app.Session.Board.Balls.Single().position; var point = BallPoint();
_app.Input.Begin(point, false); _app.Input.Move(point + new Vector2(100, 30));
Assert.That(_app.Presenter.Balls.Values.Single().transform.position, Is.Not.EqualTo(new Vector3(original.x, original.y, original.z)));
_app.Session.SaveCurrent(); Assert.That(_store.Saved.puzzleStates.Single().balls.Single().position, Is.EqualTo(original));
_app.Input.End(point + new Vector2(100, 30), true);
Assert.That(_app.Session.Board.CollectedBallIds, Is.Empty);
Assert.That(_app.Presenter.Balls.Values.Single().transform.position, Is.EqualTo(new Vector3(original.x, original.y, original.z)));
_app.Input.Begin(point, false); _app.Input.Move(point + new Vector2(100, 30)); _app.Input.End(new Vector2(-1, point.y));
Assert.That(_app.Session.Board.Balls.Single().position, Is.EqualTo(original)); yield return null;
}
[UnityTest] public IEnumerator ButtonBlockedGestureAndPinchNeverCollectOrCountAFriend()
{
_app.StartStage(1); yield return null; Physics.SyncTransforms(); var point = BallPoint();
_app.Input.Begin(point, true); _app.Input.Move(point + new Vector2(100, 20)); _app.Input.End(point + new Vector2(100, 20));
Assert.That(_app.Session.Board.CollectedBallIds, Is.Empty);
_app.Input.Begin(point, false); _app.Input.Move(point + new Vector2(100, 20)); _app.Input.BeginPinch(100, false);
_app.Input.Pinch(200); _app.Input.End(point);
Assert.That(_app.Session.Board.CollectedBallIds, Is.Empty); Assert.That(_app.Session.Board.CountedCount, Is.Zero);
Assert.That(_app.Session.Camera.distance, Is.EqualTo(4).Within(0.01)); yield return null;
}
[UnityTest] public IEnumerator OrbitIsCommittedAtEndAndCanInspectEveryDirection()
{
_app.StartStage(1); yield return null;
var original = _app.Session.Camera.rotation; var empty = new Vector2(Screen.width * 0.5f, Screen.height * 0.6f);
_app.Input.Begin(empty, false); _app.Input.Move(empty + new Vector2(150, 80));
Assert.That(_app.Session.Camera.rotation, Is.EqualTo(original)); _app.Input.Cancel();
Assert.That(_app.Session.Camera.rotation, Is.EqualTo(original));
_app.Orbit.Orbit(new Vector2(720, 360)); _app.Session.CommitCamera(_app.Orbit.Snapshot());
Assert.That(_store.Saved.puzzleStates.Single().camera.rotation.IsValid, Is.True);
_app.Orbit.Zoom(1000); Assert.That(_app.Orbit.Snapshot().distance, Is.EqualTo(3));
_app.Orbit.Zoom(0.001f); Assert.That(_app.Orbit.Snapshot().distance, Is.EqualTo(15)); yield return null;
}
[UnityTest] public IEnumerator RestartRestoresOnlyCommittedPuzzleStateAndDoesNotReplayCounts()
{
var stage = AssetDatabase.LoadAssetAtPath<StageAsset>("Assets/MagnetismHeroes/UnityRuntime/StageAssets/Stage01.asset"); var assetBefore = EditorJsonUtility.ToJson(stage);
_app.StartStage(1); yield return null; Physics.SyncTransforms();
var point = BallPoint(); Drag(point, point + new Vector2(90, -15));
_app.Orbit.Zoom(0.8f); _app.Session.CommitCamera(_app.Orbit.Snapshot()); _app.ShowHome();
var position = _store.Saved.puzzleStates.Single().balls.Single().position;
Object.Destroy(_app.gameObject); yield return null;
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/MagnetismHeroes/UnityRuntime/Presentation/Prefabs/GameApp.prefab");
_app = Object.Instantiate(prefab).GetComponent<GameApp>(); _app.Initialize(_store); _app.StartStage(1); yield return null;
Assert.That(_app.Session.Board.Balls.Single().position, Is.EqualTo(position));
Assert.That(_app.Session.Board.CollectedBallIds.Length, Is.EqualTo(1)); Assert.That(_app.Session.Board.CountedCount, Is.Zero);
Assert.That(_app.Session.Camera.distance, Is.EqualTo(10).Within(0.01)); Assert.That(_app.Voice.PendingCount, Is.Zero);
Assert.That(EditorJsonUtility.ToJson(stage), Is.EqualTo(assetBefore));
}
[UnityTest] public IEnumerator ActualTouchPinchDoesNotStartANewDragUntilBothFingersAreLifted()
{
_touchscreen = InputSystem.AddDevice<Touchscreen>(); _app.StartStage(1); yield return null; Physics.SyncTransforms();
var point = BallPoint(); var second = point + new Vector2(100, 0);
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 1, phase = UnityEngine.InputSystem.TouchPhase.Began, position = point });
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 2, phase = UnityEngine.InputSystem.TouchPhase.Began, position = second }); yield return null; yield return null;
Assert.That(_touchscreen.touches.Count(touch => touch.press.isPressed), Is.EqualTo(2));
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 2, phase = UnityEngine.InputSystem.TouchPhase.Moved, position = second + new Vector2(70, 0) }); yield return null; yield return null;
Assert.That(_app.Orbit.Snapshot().distance, Is.LessThan(8));
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 2, phase = UnityEngine.InputSystem.TouchPhase.Ended, position = second }); yield return null; yield return null;
var committedCamera = _app.Session.Camera.rotation;
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 1, phase = UnityEngine.InputSystem.TouchPhase.Moved, position = point + new Vector2(90, 0) }); yield return null; yield return null;
Assert.That(_app.Orbit.Snapshot().rotation, Is.EqualTo(committedCamera));
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 1, phase = UnityEngine.InputSystem.TouchPhase.Ended, position = point + new Vector2(90, 0) }); yield return null;
Assert.That(_app.Session.Board.CollectedBallIds, Is.Empty); Assert.That(_app.Session.Board.CountedCount, Is.Zero);
}
[UnityTest] public IEnumerator ActualBundledVoicePlaysThroughVoiceMixerAndClearsOnNavigation()
{
var source = _app.GetComponent<AudioSource>(); Assert.That(source.outputAudioMixerGroup.name, Is.EqualTo("Voice"));
Assert.That(source.spatialBlend, Is.Zero);
_app.StartStage(1); yield return null; Assert.That(source.clip.name, Is.EqualTo("guide_collect")); Assert.That(source.isPlaying, Is.True);
Physics.SyncTransforms(); var point = BallPoint(); Drag(point, point + new Vector2(80, 0)); yield return null;
Physics.SyncTransforms(); Tap(BallPoint()); yield return null;
Assert.That(source.clip.name, Is.EqualTo("count_01")); Assert.That(source.isPlaying, Is.True);
_app.ShowHome(); Assert.That(source.isPlaying, Is.False); Assert.That(_app.Voice.PendingCount, Is.Zero);
}
[UnityTest] public IEnumerator CanceledPinchRestoresCommittedCameraAndIgnoresTheRemainingFinger()
{
_touchscreen = InputSystem.AddDevice<Touchscreen>(); _app.StartStage(1); yield return null; Physics.SyncTransforms();
var point = BallPoint(); var second = point + new Vector2(90, 0); float initial = _app.Session.Camera.distance;
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 11, phase = UnityEngine.InputSystem.TouchPhase.Began, position = point });
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 12, phase = UnityEngine.InputSystem.TouchPhase.Began, position = second }); yield return null; yield return null;
Assert.That(_touchscreen.touches.Count(touch => touch.press.isPressed), Is.EqualTo(2));
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 12, phase = UnityEngine.InputSystem.TouchPhase.Moved, position = second + new Vector2(90, 0) }); yield return null; yield return null;
Assert.That(_app.Orbit.Snapshot().distance, Is.LessThan(initial));
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 12, phase = UnityEngine.InputSystem.TouchPhase.Canceled, position = second }); yield return null; yield return null;
Assert.That(_app.Orbit.Snapshot().distance, Is.EqualTo(initial)); Assert.That(_app.Session.Camera.distance, Is.EqualTo(initial));
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 11, phase = UnityEngine.InputSystem.TouchPhase.Moved, position = point + new Vector2(70, 0) }); yield return null;
Assert.That(_app.Orbit.Snapshot().rotation, Is.EqualTo(_app.Session.Camera.rotation));
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 11, phase = UnityEngine.InputSystem.TouchPhase.Ended, position = point + new Vector2(70, 0) }); yield return null;
Assert.That(_app.Session.Board.CollectedBallIds, Is.Empty);
}
[UnityTest] public IEnumerator CountVoicesStayOrderedUnderFastInputAndNeverOverlapAGuide()
{
var source = _app.GetComponent<AudioSource>();
_app.Voice.Guide("guide_collect"); _app.Voice.EnqueueCount(1); _app.Voice.EnqueueCount(2); _app.Voice.EnqueueCount(3);
Assert.That(source.clip.name, Is.EqualTo("count_01")); Assert.That(_app.Voice.PendingCount, Is.EqualTo(3));
float deadline = Time.realtimeSinceStartup + 5;
while (source.clip.name == "count_01" && Time.realtimeSinceStartup < deadline) yield return null;
Assert.That(source.clip.name, Is.EqualTo("count_02")); Assert.That(source.isPlaying, Is.True);
while (source.clip.name == "count_02" && Time.realtimeSinceStartup < deadline) yield return null;
Assert.That(source.clip.name, Is.EqualTo("count_03")); Assert.That(source.isPlaying, Is.True);
while (_app.Voice.PendingCount > 0 && Time.realtimeSinceStartup < deadline) yield return null;
Assert.That(_app.Voice.PendingCount, Is.Zero); Assert.That(source.isPlaying, Is.False);
}
[UnityTest] public IEnumerator TouchReleaseOutsideTheScreenCancelsAnOtherwiseValidDrag()
{
_touchscreen = InputSystem.AddDevice<Touchscreen>(); _app.StartStage(1); yield return null; Physics.SyncTransforms();
var point = BallPoint(); var initial = _app.Session.Board.Balls.Single().position;
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 41, phase = UnityEngine.InputSystem.TouchPhase.Began, position = point }); yield return null; yield return null;
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 41, phase = UnityEngine.InputSystem.TouchPhase.Moved, position = point + new Vector2(50, 10) }); yield return null; yield return null;
Assert.That(_app.Presenter.Balls.Values.Single().transform.position, Is.Not.EqualTo(new Vector3(initial.x, initial.y, initial.z)));
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = 41, phase = UnityEngine.InputSystem.TouchPhase.Ended, position = new Vector2(-1, point.y) }); yield return null; yield return null;
Assert.That(_app.Session.Board.Balls.Single().position, Is.EqualTo(initial)); Assert.That(_app.Session.Board.CollectedBallIds, Is.Empty);
}
[UnityTest] public IEnumerator FlatFloorColliderMatchesTheVisibleMeshAndKeepsTheFriendReachable()
{
_app.StartStage(1); yield return null; Physics.SyncTransforms();
var floor = _app.transform.Find("Playground/PlaygroundIsland");
Assert.That(floor.GetComponent<Collider>(), Is.TypeOf<MeshCollider>());
Assert.That(floor.GetComponent<Collider>().bounds.max.y, Is.EqualTo(floor.GetComponent<Renderer>().bounds.max.y).Within(0.01));
Assert.That(floor.GetComponent<Collider>().bounds.max.y, Is.LessThan(0)); BallPoint();
}
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9f105e98652f6b64881959d4aed6a120
@@ -0,0 +1,114 @@
using System;
using System.Linq;
using System.IO;
using MagnetismHeroes.Core;
using MagnetismHeroes.UnityRuntime.Interaction;
using MagnetismHeroes.UnityRuntime.Persistence;
using MagnetismHeroes.UnityRuntime.Presentation;
using MagnetismHeroes.UnityRuntime.StageAssets;
using TMPro;
using UnityEngine;
namespace MagnetismHeroes.UnityRuntime.Bootstrap
{
public sealed class GameApp : MonoBehaviour
{
[SerializeField] private StageAsset[] _stages;
[SerializeField] private GameObject _homePanel;
[SerializeField] private GameObject _selectionPanel;
[SerializeField] private GameObject _playPanel;
[SerializeField] private GameObject _world;
[SerializeField] private TMP_Text _instruction;
[SerializeField] private TMP_Text _counter;
[SerializeField] private TMP_Text _stageTitle;
[SerializeField] private TMP_Text _saveNotice;
[SerializeField] private UnityEngine.UI.Button _playButton;
[SerializeField] private UnityEngine.UI.Button _continueButton;
[SerializeField] private UnityEngine.UI.Button _stageOneButton;
[SerializeField] private UnityEngine.UI.Button _homeButton;
[SerializeField] private UnityEngine.UI.Button _selectionHomeButton;
[SerializeField] private BoardPresenter _presenter;
[SerializeField] private OrbitCamera _orbit;
[SerializeField] private BoardInput _input;
[SerializeField] private VoicePlayer _voice;
public GameSession Session { get; private set; }
public BoardPresenter Presenter => _presenter;
public BoardInput Input => _input;
public OrbitCamera Orbit => _orbit;
public VoicePlayer Voice => _voice;
public string Instruction => _instruction.text;
public string Counter => _counter.text;
private void Awake()
{
_playButton.onClick.AddListener(ShowSelection);
_continueButton.onClick.AddListener(ContinueFreePlay);
_stageOneButton.onClick.AddListener(() => StartStage(1));
_homeButton.onClick.AddListener(ShowHome);
_selectionHomeButton.onClick.AddListener(ShowHome);
}
private void Start()
{
if (Session == null) Initialize(new LocalProgressStore(Path.Combine(Application.persistentDataPath, "progress.json"),
_stages.Select(stage => stage.ToDefinition()).ToArray()));
}
public void Initialize(IProgressStore store)
{
Session = new GameSession(_stages.Select(stage => stage.ToDefinition()), store);
_saveNotice.text = Session.LastSaveError == null ? "" : Session.LastSaveError.StartsWith("Recovered", StringComparison.Ordinal)
? "이전 기록으로 이어서 시작할게요." : "기록을 읽지 못해 처음부터 시작할게요.";
if (Session.LastSaveError != null) Debug.LogWarning("[ProgressLoad] " + Session.LastSaveError);
Refresh(true);
}
public void ShowSelection()
{ _input.Cancel(); _voice.Clear(); Session.ShowSelection(); Refresh(true); }
public void ShowHome()
{ _input.Cancel(); _voice.Clear(); Session.ShowHome(); Refresh(true); }
public void StartStage(int id)
{
_input.Cancel(); _voice.Clear();
if (!Session.StartStage(id)) return;
Refresh(true);
// Restoring never replays historical count/celebration clips.
if (Session.Board.CollectedBallIds.Length == 0) _voice.Guide("guide_collect");
}
public void ContinueFreePlay()
{ _input.Cancel(); _voice.Clear(); if (Session.ContinueFreePlay()) Refresh(true); }
public void CommitMove(string groupId, Position3 delta)
{
var result = Session.MoveGroup(groupId, delta);
Refresh(false);
if (result.Accepted && Session.Screen == SessionScreen.Puzzle && Session.Board.CanCount) _voice.Guide("guide_count");
}
public void CountBall(string ballId)
{
var result = Session.Count(ballId);
if (result.Accepted) _voice.EnqueueCount(result.Count);
if (result.Accepted) Refresh(false);
if (!result.Accepted && result.Reason == RejectionReason.NotCollected)
{
_instruction.text = "친구를 먼저 끌어와 볼까?";
if (_presenter.Balls.TryGetValue(ballId, out var friend)) friend.Encourage();
}
}
private void Refresh(bool applyCamera)
{
var screen = Session.Screen;
_homePanel.SetActive(screen == SessionScreen.Home);
_selectionPanel.SetActive(screen == SessionScreen.StageSelection);
bool playing = screen == SessionScreen.Puzzle || screen == SessionScreen.FreePlay;
_playPanel.SetActive(playing); _world.SetActive(playing);
_continueButton.gameObject.SetActive(Session.HasFreePlay);
if (applyCamera) _orbit.Apply(Session.Camera);
_presenter.Render(Session.Board);
if (!playing) return;
_stageTitle.text = screen == SessionScreen.FreePlay ? "자유 놀이터" : $"숫자 {Session.Board.BallCount} 친구 찾기";
_counter.text = screen == SessionScreen.FreePlay ? $"친구 {Session.Board.BallCount}" : $"센 친구 {Session.Board.CountedCount} / {Session.Board.BallCount}";
_instruction.text = screen == SessionScreen.FreePlay ? "해냈어! 친구와 자유롭게 놀아 보자."
: Session.Board.CanCount ? "모두 모았네! 하나씩 눌러 세어 보자." : "친구를 찾아 끌어와 볼까?";
}
private void OnApplicationPause(bool paused)
{ if (paused && Session != null) { _input.Cancel(); _voice.Clear(); Session.SaveCurrent(); } }
private void OnApplicationQuit() { if (Session != null) { _input.Cancel(); Session.SaveCurrent(); } }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d19ee043fa8f8da4e96eec5e9b810d96
@@ -0,0 +1,170 @@
using System.Collections.Generic;
using MagnetismHeroes.Core;
using MagnetismHeroes.UnityRuntime.Bootstrap;
using MagnetismHeroes.UnityRuntime.Presentation;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
namespace MagnetismHeroes.UnityRuntime.Interaction
{
public sealed class BoardInput : MonoBehaviour
{
[SerializeField] private GameApp _app;
[SerializeField] private BoardPresenter _presenter;
[SerializeField] private OrbitCamera _orbit;
[SerializeField] private float _dragThreshold = 14;
private enum Gesture { None, Blocked, Friend, Orbit, Pinch }
private Gesture _gesture;
private Vector2 _start, _last;
private string _ballId, _groupId;
private bool _dragged;
private Plane _dragPlane;
private Vector3 _grabPoint, _previewDelta;
private float _pinchDistance;
private int _fingerId = -1;
private bool _waitForAllTouchesReleased;
private int _pinchFirstId = -1, _pinchSecondId = -1;
public bool AcceptInput => _app.Session != null && _app.Session.Board != null;
public void Begin(Vector2 point, bool uiBlocked)
{
Cancel();
if (!AcceptInput) return;
_start = _last = point;
if (uiBlocked) { _gesture = Gesture.Blocked; return; }
Physics.SyncTransforms();
var ray = _orbit.Camera.ScreenPointToRay(point);
if (Physics.Raycast(ray, out var hit, 100) && hit.collider.TryGetComponent<BallView>(out var ball))
{
_gesture = Gesture.Friend; _ballId = ball.BallId; _groupId = ball.GroupId;
_dragPlane = new Plane(_orbit.Camera.transform.forward, hit.point);
_grabPoint = hit.point;
}
else _gesture = Gesture.Orbit;
}
public void Move(Vector2 point)
{
if (_gesture == Gesture.None || _gesture == Gesture.Blocked || _gesture == Gesture.Pinch) return;
_dragged |= (point - _start).sqrMagnitude >= _dragThreshold * _dragThreshold;
if (_dragged && _gesture == Gesture.Friend)
{
var ray = _orbit.Camera.ScreenPointToRay(point);
if (_dragPlane.Raycast(ray, out var distance)) { _previewDelta = ray.GetPoint(distance) - _grabPoint; _presenter.PreviewGroup(_groupId, _previewDelta); }
}
else if (_dragged && _gesture == Gesture.Orbit) _orbit.Orbit(point - _last);
_last = point;
}
public void End(Vector2 point, bool canceled = false)
{
bool inside = point.x >= 0 && point.x < Screen.width && point.y >= 0 && point.y < Screen.height;
if (canceled || !inside) { Cancel(); return; }
if (_gesture == Gesture.Friend)
{
if (_dragged) _app.CommitMove(_groupId, BoardPresenter.ToPosition(_previewDelta));
else _app.CountBall(_ballId);
}
else if (_gesture == Gesture.Orbit && _dragged) _app.Session.CommitCamera(_orbit.Snapshot());
else if (_gesture == Gesture.Pinch) _app.Session.CommitCamera(_orbit.Snapshot());
_gesture = Gesture.None; _dragged = false; _previewDelta = Vector3.zero; _fingerId = -1;
}
public void BeginPinch(float distance, bool uiBlocked)
{
Cancel();
_gesture = uiBlocked || !AcceptInput ? Gesture.Blocked : Gesture.Pinch;
_pinchDistance = distance;
}
public void Pinch(float distance)
{
if (_gesture != Gesture.Pinch || distance <= 0 || _pinchDistance <= 0) return;
_orbit.Zoom(distance / _pinchDistance); _pinchDistance = distance;
}
public void Cancel()
{
if (_gesture == Gesture.Friend) _presenter.CancelPreview();
if ((_gesture == Gesture.Orbit || _gesture == Gesture.Pinch) && _app.Session != null) _orbit.Apply(_app.Session.Camera);
_gesture = Gesture.None; _dragged = false; _previewDelta = Vector3.zero; _fingerId = -1;
_waitForAllTouchesReleased = false; _pinchFirstId = _pinchSecondId = -1;
}
private bool IsUI(Vector2 point)
{
if (EventSystem.current == null) return false;
var hits = new List<RaycastResult>(); EventSystem.current.RaycastAll(new PointerEventData(EventSystem.current) { position = point }, hits);
return hits.Count > 0;
}
private void Update()
{
if (!AcceptInput) return;
var touchscreen = Touchscreen.current;
if (touchscreen != null)
{
if (_gesture == Gesture.Pinch)
{
foreach (var touch in touchscreen.touches)
{
var id = touch.touchId.ReadValue();
if ((id == _pinchFirstId || id == _pinchSecondId) && touch.phase.ReadValue() == UnityEngine.InputSystem.TouchPhase.Canceled)
{ Cancel(); _waitForAllTouchesReleased = true; return; }
}
}
var active = new List<UnityEngine.InputSystem.Controls.TouchControl>();
foreach (var touch in touchscreen.touches) if (touch.press.isPressed) active.Add(touch);
// A canceled/finished pinch cannot restart when a third finger joins a remaining finger.
if (_waitForAllTouchesReleased && _gesture == Gesture.None && active.Count > 0) return;
if (active.Count >= 2)
{
var a = active[0].position.ReadValue(); var b = active[1].position.ReadValue();
if (_gesture != Gesture.Pinch && _gesture != Gesture.Blocked)
{
BeginPinch(Vector2.Distance(a, b), IsUI(a) || IsUI(b));
_pinchFirstId = active[0].touchId.ReadValue(); _pinchSecondId = active[1].touchId.ReadValue();
}
else Pinch(Vector2.Distance(a, b));
_waitForAllTouchesReleased = true;
return;
}
if (_waitForAllTouchesReleased)
{
if (_gesture == Gesture.Pinch) End(_last);
if (active.Count == 0) { _waitForAllTouchesReleased = false; _gesture = Gesture.None; _fingerId = -1; }
return;
}
if (_gesture == Gesture.Pinch || (active.Count == 0 && _gesture == Gesture.Blocked))
{ End(_last); _fingerId = -1; return; }
if (active.Count == 1)
{
var touch = active[0]; var point = touch.position.ReadValue();
if (_fingerId == -1)
{ Begin(point, IsUI(point)); _fingerId = touch.touchId.ReadValue(); }
else Move(point);
return;
}
if (_fingerId != -1)
{
bool canceled = false;
Vector2 releasePoint = _last;
foreach (var touch in touchscreen.touches)
if (touch.touchId.ReadValue() == _fingerId)
{ canceled = touch.phase.ReadValue() == UnityEngine.InputSystem.TouchPhase.Canceled; releasePoint = touch.position.ReadValue(); break; }
if (!canceled) Move(releasePoint);
End(releasePoint, canceled); return;
}
}
var mouse = Mouse.current;
if (mouse == null) return;
var position = mouse.position.ReadValue();
if (mouse.leftButton.wasPressedThisFrame) Begin(position, IsUI(position));
if (mouse.leftButton.isPressed) Move(position);
if (mouse.leftButton.wasReleasedThisFrame) End(position);
if (mouse.scroll.ReadValue().y != 0 && !IsUI(position))
{ _orbit.Zoom(Mathf.Exp(mouse.scroll.ReadValue().y * 0.001f)); _app.Session.CommitCamera(_orbit.Snapshot()); }
}
private void OnApplicationPause(bool paused) { if (paused) Cancel(); }
private void OnDisable() => Cancel();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 38cf3d2ad1224c244a0e1ad7b52602c1
@@ -0,0 +1,36 @@
using MagnetismHeroes.Core;
using UnityEngine;
namespace MagnetismHeroes.UnityRuntime.Interaction
{
public sealed class OrbitCamera : MonoBehaviour
{
[SerializeField] private Camera _camera;
[SerializeField] private float _minDistance = 3;
[SerializeField] private float _maxDistance = 15;
[SerializeField] private float _degreesPerPixel = 0.25f;
private Vector3 _target;
private Quaternion _rotation;
private float _distance;
public Camera Camera => _camera;
public void Apply(CameraState state)
{
_target = new Vector3(state.target.x, state.target.y, state.target.z);
_rotation = new Quaternion(state.rotation.x, state.rotation.y, state.rotation.z, state.rotation.w);
_distance = Mathf.Clamp(state.distance, _minDistance, _maxDistance); UpdateCamera();
}
public void Orbit(Vector2 delta)
{
_rotation = Quaternion.AngleAxis(delta.x * _degreesPerPixel, Vector3.up) * _rotation
* Quaternion.AngleAxis(-delta.y * _degreesPerPixel, Vector3.right);
UpdateCamera();
}
public void Zoom(float ratio)
{ if (ratio > 0 && float.IsFinite(ratio)) { _distance = Mathf.Clamp(_distance / ratio, _minDistance, _maxDistance); UpdateCamera(); } }
public CameraState Snapshot() => new CameraState { target = new Position3(_target.x, _target.y, _target.z),
rotation = new Rotation4(_rotation.x, _rotation.y, _rotation.z, _rotation.w), distance = _distance };
private void UpdateCamera()
{ _camera.transform.SetPositionAndRotation(_target + _rotation * new Vector3(0, 0, -_distance), _rotation); }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a79bcabf76823224fae318cd87bc186d
@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MagnetismHeroes.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace MagnetismHeroes.UnityRuntime.Persistence
{
public sealed class LocalProgressStore : IProgressStore
{
private readonly string _path;
private readonly IReadOnlyList<StageDefinition> _definitions;
private bool _preserveDamagedOriginal;
private sealed class SaveFieldsResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization serialization)
=> base.CreateProperties(type, serialization).Where(property => type.GetField(property.UnderlyingName) != null).ToList();
}
private static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings { ContractResolver = new SaveFieldsResolver() };
public LocalProgressStore(string path, IReadOnlyList<StageDefinition> definitions)
{ _path = path; _definitions = definitions; }
public bool TryLoad(out ProgressSave save, out string error)
{
save = null; error = null;
if (!File.Exists(_path)) { save = new ProgressSave(); return true; }
if (Read(_path, out save, out error)) return true;
var originalError = error;
if (Read(_path + ".bak", out save, out var backupError))
{ error = "Recovered valid backup: " + originalError; _preserveDamagedOriginal = true; return true; }
_preserveDamagedOriginal = true;
error = "Invalid primary and backup: " + originalError + "; " + backupError;
return false;
}
private bool Read(string path, out ProgressSave save, out string error)
{
save = null;
try
{
using var reader = new JsonTextReader(new StringReader(File.ReadAllText(path))) { DateParseHandling = DateParseHandling.None };
var root = JObject.Load(reader, new JsonLoadSettings { DuplicatePropertyNameHandling = DuplicatePropertyNameHandling.Error });
if (reader.Read()) throw new InvalidDataException("Trailing JSON content.");
Require(root, "schemaVersion", "completedStageIds", "seenTutorialIds", "puzzleStates", "latestFreePlay");
Scalar(root["schemaVersion"], JTokenType.Integer);
foreach (var id in Array(root["completedStageIds"])) Scalar(id, JTokenType.Integer);
foreach (var id in Array(root["seenTutorialIds"])) Scalar(id, JTokenType.String);
foreach (var state in Array(root["puzzleStates"]))
{
Require(state, "stageId", "balls", "groups", "collectedBallIds", "countedBallIds", "camera");
Scalar(state["stageId"], JTokenType.Integer);
foreach (var id in Array(state["collectedBallIds"])) Scalar(id, JTokenType.String);
foreach (var id in Array(state["countedBallIds"])) Scalar(id, JTokenType.String);
RequireBoard(state);
}
if (root["latestFreePlay"].Type != JTokenType.Null)
{ Require(root["latestFreePlay"], "sourceStageId", "balls", "groups", "camera"); Scalar(root["latestFreePlay"]["sourceStageId"], JTokenType.Integer); RequireBoard(root["latestFreePlay"]); }
save = root.ToObject<ProgressSave>(JsonSerializer.Create(SerializerSettings));
if (!SaveValidator.TryValidate(save, _definitions, out error)) { save = null; return false; }
return true;
}
catch (Exception exception) when (exception is IOException || exception is UnauthorizedAccessException
|| exception is JsonException || exception is InvalidDataException || exception is ArgumentException)
{ save = null; error = exception.Message; return false; }
}
private static void RequireBoard(JToken board)
{
foreach (var ball in Array(board["balls"]))
{ Require(ball, "id", "position"); Scalar(ball["id"], JTokenType.String); Vector(ball["position"], "x", "y", "z"); }
foreach (var group in Array(board["groups"]))
{ Require(group, "id", "ballIds"); Scalar(group["id"], JTokenType.String); foreach (var id in Array(group["ballIds"])) Scalar(id, JTokenType.String); }
Require(board["camera"], "target", "rotation", "distance");
Vector(board["camera"]["target"], "x", "y", "z");
Vector(board["camera"]["rotation"], "x", "y", "z", "w"); Number(board["camera"]["distance"]);
}
private static void Vector(JToken token, params string[] fields)
{ Require(token, fields); foreach (var field in fields) Number(token[field]); }
private static void Number(JToken token)
{ if (token == null || (token.Type != JTokenType.Integer && token.Type != JTokenType.Float)) throw new InvalidDataException("Expected a JSON number."); }
private static void Scalar(JToken token, JTokenType type)
{ if (token == null || token.Type != type) throw new InvalidDataException("Expected a JSON " + type + "."); }
private static JArray Array(JToken token) => token as JArray ?? throw new InvalidDataException("Expected a JSON array.");
private static void Require(JToken token, params string[] fields)
{
if (!(token is JObject objectValue)) throw new InvalidDataException("Expected a JSON object.");
foreach (var field in fields)
if (objectValue.Property(field) == null || (field != "latestFreePlay" && objectValue[field].Type == JTokenType.Null))
throw new InvalidDataException("Missing or null field: " + field);
}
public bool TrySave(ProgressSave save, out string error)
{
if (!SaveValidator.TryValidate(save, _definitions, out error)) return false;
string temporary = _path + ".tmp";
try
{
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(_path)));
if (_preserveDamagedOriginal && File.Exists(_path))
{
File.Copy(_path, _path + ".damaged-" + Guid.NewGuid().ToString("N"));
_preserveDamagedOriginal = false;
}
using (var stream = new FileStream(temporary, FileMode.Create, FileAccess.Write, FileShare.None))
using (var writer = new StreamWriter(stream))
{ writer.Write(JsonConvert.SerializeObject(save, Formatting.Indented, SerializerSettings)); writer.Flush(); stream.Flush(true); }
if (File.Exists(_path))
{
// Never replace a valid recovered backup with the invalid primary.
if (Read(_path, out _, out _)) File.Replace(temporary, _path, _path + ".bak");
else File.Replace(temporary, _path, null);
}
else File.Move(temporary, _path);
error = null; return true;
}
catch (Exception exception) when (exception is IOException || exception is UnauthorizedAccessException)
{ error = exception.Message; return false; }
finally { if (File.Exists(temporary)) File.Delete(temporary); }
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 59a278725854f2249a18d8377d8edfbb
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f93db43014b63214b9ed0141e2680cc4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,167 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!244 &-7089420846949506769
AudioMixerEffectController:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_EffectID: 98679871d90ec6b45a4b5d0705f80a00
m_EffectName: Attenuation
m_MixLevel: 2856aa1af04b0d04c808cc2a03ea79c0
m_Parameters: []
m_SendTarget: {fileID: 0}
m_EnableWetMix: 0
m_Bypass: 0
--- !u!244 &-6091608972580811098
AudioMixerEffectController:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_EffectID: 79485d7443da65e48bd63be2330046db
m_EffectName: Attenuation
m_MixLevel: 365c5e490a44a264cab018db1169d2eb
m_Parameters: []
m_SendTarget: {fileID: 0}
m_EnableWetMix: 0
m_Bypass: 0
--- !u!243 &-2216692613774908723
AudioMixerGroupController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Voice
m_AudioMixer: {fileID: 24100000}
m_GroupID: 71a8d7d591bfa734e9dbc6d11ca98e3f
m_Children: []
m_Volume: 18cb311bf320ef24d92494e0a399bc0b
m_Pitch: 8f38385d1f0287741b71cdf82405d317
m_Send: 00000000000000000000000000000000
m_Effects:
- {fileID: -6091608972580811098}
m_UserColorIndex: 0
m_Mute: 0
m_Solo: 0
m_BypassEffects: 0
--- !u!241 &24100000
AudioMixerController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: GameAudio
m_OutputGroup: {fileID: 0}
m_MasterGroup: {fileID: 24300002}
m_Snapshots:
- {fileID: 24500006}
m_StartSnapshot: {fileID: 24500006}
m_SuspendThreshold: -80
m_EnableSuspend: 1
m_UpdateMode: 0
m_ExposedParameters: []
m_AudioMixerGroupViews: []
m_CurrentViewIndex: 0
m_TargetSnapshot: {fileID: 24500006}
--- !u!243 &24300002
AudioMixerGroupController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Master
m_AudioMixer: {fileID: 24100000}
m_GroupID: 03ab91f07f4239546b51a659b80695bf
m_Children:
- {fileID: -2216692613774908723}
- {fileID: 3873177042007149833}
- {fileID: 7202041017813097757}
m_Volume: 07603f28db3be57459e8966a482457e6
m_Pitch: 01dbd86b7adabd944aa50c028a386ad3
m_Send: 00000000000000000000000000000000
m_Effects:
- {fileID: 24400004}
m_UserColorIndex: 0
m_Mute: 0
m_Solo: 0
m_BypassEffects: 0
--- !u!244 &24400004
AudioMixerEffectController:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_EffectID: a6bd621586c9d5d41ab4bc3993428ec0
m_EffectName: Attenuation
m_MixLevel: 34a768e8d80da35469c8274c4f00ade9
m_Parameters: []
m_SendTarget: {fileID: 0}
m_EnableWetMix: 0
m_Bypass: 0
--- !u!245 &24500006
AudioMixerSnapshotController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Snapshot
m_AudioMixer: {fileID: 24100000}
m_SnapshotID: 9e0c16a37459f9440a75e1c95ac0f2ff
m_FloatValues: {}
m_TransitionOverrides: {}
--- !u!244 &3402812816098948721
AudioMixerEffectController:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_EffectID: 7e808862be18fc8418bed7842bf2e425
m_EffectName: Attenuation
m_MixLevel: 7dcba25d01d1a9c4e8d7e7326a5f06e1
m_Parameters: []
m_SendTarget: {fileID: 0}
m_EnableWetMix: 0
m_Bypass: 0
--- !u!243 &3873177042007149833
AudioMixerGroupController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: SFX
m_AudioMixer: {fileID: 24100000}
m_GroupID: b16ec89eb1c98d54a9bdcebe9f3ab8f8
m_Children: []
m_Volume: 6bed3b15a512d0641ba0caedab6ace56
m_Pitch: cb707d37cbe34124abb1a93227b9e667
m_Send: 00000000000000000000000000000000
m_Effects:
- {fileID: 3402812816098948721}
m_UserColorIndex: 0
m_Mute: 0
m_Solo: 0
m_BypassEffects: 0
--- !u!243 &7202041017813097757
AudioMixerGroupController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Music
m_AudioMixer: {fileID: 24100000}
m_GroupID: a284af71a7b1edc44aae0a299dc13fcd
m_Children: []
m_Volume: b537c5ab5441fb543a9daf3118f75fe1
m_Pitch: 606a24fcea5e35d45aad10c9e6dce740
m_Send: 00000000000000000000000000000000
m_Effects:
- {fileID: -7089420846949506769}
m_UserColorIndex: 0
m_Mute: 0
m_Solo: 0
m_BypassEffects: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 65ecf35a597af1f4681307a83e447216
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 24100000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c58ab521f033fea4b82df3cffcc6bb44
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 2a76c0490e02b424d9aedcf995d86978
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: f4f0cc83afe60704fba158ceb7fa2a11
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 214880dd0fc0b8c45b1c89113aa637aa
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: dae96de5a95a00d42ba058ddff57a393
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: e90c4eabbbf54ca4ca89f10b7cee687b
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 2ed9016c0db818c4ca7c195ab8b1389f
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 8543174010591bd458648d7fe6bf0195
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: fbab9ad873226564fa721fccf627607a
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: a393e4fe5d01d634d92346fdb5e8e06f
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 5d4eedabd1a1acd42a53f841c8add870
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 20a08bc78ef7a2e4cb977aff160df2a5
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 0
quality: 1
conversionMode: 0
preloadAudioData: 1
platformSettingOverrides: {}
forceToMono: 1
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,24 @@
using UnityEngine;
namespace MagnetismHeroes.UnityRuntime.Presentation
{
public sealed class BallView : MonoBehaviour
{
[SerializeField] private Renderer _body;
[SerializeField] private GameObject _countMarker;
private MaterialPropertyBlock _colors;
private Color _normalColor;
private float _encourageUntil;
public string BallId { get; private set; }
public string GroupId { get; private set; }
public void Bind(string ballId, string groupId, bool counted)
{ BallId = ballId; GroupId = groupId; _countMarker.SetActive(counted); }
private void Awake() { _colors = new MaterialPropertyBlock(); _normalColor = _body.sharedMaterial.color; }
public void Encourage() => _encourageUntil = Time.unscaledTime + 0.35f;
private void Update()
{
float glow = Mathf.Clamp01((_encourageUntil - Time.unscaledTime) / 0.35f);
_colors.SetColor("_BaseColor", Color.Lerp(_normalColor, Color.white, glow * 0.7f)); _body.SetPropertyBlock(_colors);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ef4ac8f42ba5a5343a5f0474eef2a1af
@@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Linq;
using MagnetismHeroes.Core;
using UnityEngine;
namespace MagnetismHeroes.UnityRuntime.Presentation
{
public sealed class BoardPresenter : MonoBehaviour
{
[SerializeField] private BallView _ballPrefab;
[SerializeField] private GroupBadge _badgePrefab;
[SerializeField] private Camera _camera;
private readonly Dictionary<string, BallView> _balls = new Dictionary<string, BallView>();
private readonly List<GroupBadge> _badges = new List<GroupBadge>();
private BoardRules _board;
public Camera Camera => _camera;
public IReadOnlyDictionary<string, BallView> Balls => _balls;
public void Render(BoardRules board)
{
_board = board;
foreach (var view in _balls.Values) { view.gameObject.SetActive(false); Destroy(view.gameObject); }
foreach (var badge in _badges) { badge.gameObject.SetActive(false); Destroy(badge.gameObject); }
_balls.Clear(); _badges.Clear();
if (board == null) return;
foreach (var group in board.Groups)
{
foreach (var ballId in group.ballIds)
{
var state = board.Balls.First(ball => ball.id == ballId);
var ball = Instantiate(_ballPrefab, ToVector(state.position), Quaternion.identity, transform);
ball.Bind(ballId, group.id, board.CountedBallIds.Contains(ballId)); _balls.Add(ballId, ball);
}
_badges.Add(Instantiate(_badgePrefab, transform));
}
PositionBadges();
}
public void PreviewGroup(string groupId, Vector3 delta)
{
foreach (var state in _board.Balls)
_balls[state.id].transform.position = ToVector(state.position) + (_balls[state.id].GroupId == groupId ? delta : Vector3.zero);
PositionBadges();
}
public void CancelPreview()
{
if (_board == null) return;
foreach (var state in _board.Balls) _balls[state.id].transform.position = ToVector(state.position);
PositionBadges();
}
private void PositionBadges()
{
if (_board == null) return;
var groups = _board.Groups;
for (int index = 0; index < groups.Length; index++)
{
var group = groups[index];
var center = group.ballIds.Select(id => _balls[id].transform.position).Aggregate(Vector3.zero, (sum, value) => sum + value) / group.ballIds.Length;
var representative = _balls[group.ballIds[0]].transform.position;
_badges[index].Bind(group.ballIds.Length, center + Vector3.up * 0.95f,
representative - _camera.transform.forward * 0.49f);
_badges[index].FaceCamera(_camera);
}
}
private void LateUpdate() => PositionBadges();
public static Vector3 ToVector(Position3 position) => new Vector3(position.x, position.y, position.z);
public static Position3 ToPosition(Vector3 vector) => new Position3(vector.x, vector.y, vector.z);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e02cea646173d344589aa315d0582d6b
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 03488cd627303b84cb513583e6c9f0a0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a5bc77ef7ce7c7e4d85ac578cfcd6646
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: 1c7bde640ec97864e8ec7915c144fd63
TrueTypeFontImporter:
externalObjects: {}
serializedVersion: 4
fontSize: 16
forceTextureCase: -2
characterSpacing: 0
characterPadding: 1
includeFontData: 1
fontNames:
- Noto Sans CJK KR
fallbackFontReferences: []
customCharacters:
fontRenderingMode: 0
ascentCalculationMode: 1
useLegacyBoundsCalculation: 0
shouldRoundAdvanceValue: 1
subsetRecipes: []
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
using TMPro;
using UnityEngine;
namespace MagnetismHeroes.UnityRuntime.Presentation
{
public sealed class GroupBadge : MonoBehaviour
{
[SerializeField] private TMP_Text _number;
[SerializeField] private Transform _face;
public void Bind(int count, Vector3 numberPosition, Vector3 facePosition)
{ _number.text = count.ToString(); _number.transform.position = numberPosition; _face.position = facePosition; }
public void FaceCamera(Camera camera)
{ _number.transform.rotation = camera.transform.rotation; _face.rotation = camera.transform.rotation; }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5dfc933dbfe92d64f990d1aa289a03d4
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ef2acd91d910ab1429f59df134e7dd35
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,105 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-2381720020644801332
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: CountGold
m_Shader: {fileID: 4800000, guid: 8d2bb70cbf9db8d4da26e15b26e74248, 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:
RenderType: Opaque
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}
- _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}
- _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
- _BumpScale: 1
- _Cull: 2
- _Cutoff: 0.5
- _DstBlend: 0
- _DstBlendAlpha: 0
- _GlossinessSource: 0
- _QueueOffset: 0
- _ReceiveShadows: 1
- _ScreenSpaceReflectionsContributeTransparent: 1
- _Shininess: 0
- _Smoothness: 0.5
- _SmoothnessSource: 0
- _SpecSource: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 0.76, b: 0.14, a: 1}
- _Color: {r: 1, g: 0.76, b: 0.13999996, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e6eb301d5baaa3b4dba3ce2228f17fa3
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,105 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-4369384185313545078
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: FaceInk
m_Shader: {fileID: 4800000, guid: 8d2bb70cbf9db8d4da26e15b26e74248, 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:
RenderType: Opaque
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}
- _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}
- _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
- _BumpScale: 1
- _Cull: 2
- _Cutoff: 0.5
- _DstBlend: 0
- _DstBlendAlpha: 0
- _GlossinessSource: 0
- _QueueOffset: 0
- _ReceiveShadows: 1
- _ScreenSpaceReflectionsContributeTransparent: 1
- _Shininess: 0
- _Smoothness: 0.5
- _SmoothnessSource: 0
- _SpecSource: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.12, g: 0.2, b: 0.31, a: 1}
- _Color: {r: 0.11999995, g: 0.19999996, b: 0.30999997, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f625349e1f7edf249bc2f0006bde7ccf
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,105 @@
%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: MagnetBlue
m_Shader: {fileID: 4800000, guid: 8d2bb70cbf9db8d4da26e15b26e74248, 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:
RenderType: Opaque
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}
- _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}
- _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
- _BumpScale: 1
- _Cull: 2
- _Cutoff: 0.5
- _DstBlend: 0
- _DstBlendAlpha: 0
- _GlossinessSource: 0
- _QueueOffset: 0
- _ReceiveShadows: 1
- _ScreenSpaceReflectionsContributeTransparent: 1
- _Shininess: 0
- _Smoothness: 0.5
- _SmoothnessSource: 0
- _SpecSource: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.15, g: 0.68, b: 0.92, a: 1}
- _Color: {r: 0.14999998, g: 0.68, b: 0.92, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &3373645068005721598
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
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ec5c546652eacdf4f9aa3fa65eb0c385
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,105 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-5884834840082439187
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: PlaygroundMint
m_Shader: {fileID: 4800000, guid: 8d2bb70cbf9db8d4da26e15b26e74248, 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:
RenderType: Opaque
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}
- _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}
- _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
- _BumpScale: 1
- _Cull: 2
- _Cutoff: 0.5
- _DstBlend: 0
- _DstBlendAlpha: 0
- _GlossinessSource: 0
- _QueueOffset: 0
- _ReceiveShadows: 1
- _ScreenSpaceReflectionsContributeTransparent: 1
- _Shininess: 0
- _Smoothness: 0.5
- _SmoothnessSource: 0
- _SpecSource: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.65, g: 0.88, b: 0.73, a: 1}
- _Color: {r: 0.65, g: 0.88, b: 0.73, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aaf8256cf3ae3b74ca9f97bd370459f7
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,105 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-3881899038799102625
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: ToyCoral
m_Shader: {fileID: 4800000, guid: 8d2bb70cbf9db8d4da26e15b26e74248, 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:
RenderType: Opaque
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}
- _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}
- _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
- _BumpScale: 1
- _Cull: 2
- _Cutoff: 0.5
- _DstBlend: 0
- _DstBlendAlpha: 0
- _GlossinessSource: 0
- _QueueOffset: 0
- _ReceiveShadows: 1
- _ScreenSpaceReflectionsContributeTransparent: 1
- _Shininess: 0
- _Smoothness: 0.5
- _SmoothnessSource: 0
- _SpecSource: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 0.57, b: 0.43, a: 1}
- _Color: {r: 1, g: 0.56999993, b: 0.42999998, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a9ff8ebe8d9f4354e88bff9398bc9c03
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d659b3981a02de84d8aafc8eefa9db38
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 73c3215bc432df740b907a1edefa9ea1
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,594 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &249671451578993778
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 782296095116550680}
- component: {fileID: 7044309821208163747}
- component: {fileID: 1500535848956285383}
m_Layer: 0
m_Name: LeftEye
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &782296095116550680
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 249671451578993778}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: -0.15, y: 0.05, z: 0}
m_LocalScale: {x: 0.08, y: 0.11, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1885524188671059666}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &7044309821208163747
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 249671451578993778}
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &1500535848956285383
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 249671451578993778}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: f625349e1f7edf249bc2f0006bde7ccf, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 1
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &3900389932875835401
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8665407476105022746}
- component: {fileID: 2229194056938340699}
m_Layer: 0
m_Name: Smile
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8665407476105022746
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3900389932875835401}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1885524188671059666}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!120 &2229194056938340699
LineRenderer:
serializedVersion: 3
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3900389932875835401}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 0
m_LightProbeUsage: 0
m_ReflectionProbeUsage: 0
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: f625349e1f7edf249bc2f0006bde7ccf, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_Positions:
- {x: -0.14, y: -0.0812, z: 0}
- {x: -0.105000004, y: -0.106924996, z: 0}
- {x: -0.07, y: -0.1253, z: 0}
- {x: -0.035, y: -0.136325, z: 0}
- {x: 0, y: -0.14, z: 0}
- {x: 0.035, y: -0.136325, z: 0}
- {x: 0.07, y: -0.1253, z: 0}
- {x: 0.105000004, y: -0.106924996, z: 0}
- {x: 0.14, y: -0.0812, z: 0}
m_Parameters:
serializedVersion: 3
widthMultiplier: 0.035
widthCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
colorGradient:
serializedVersion: 2
key0: {r: 1, g: 1, b: 1, a: 1}
key1: {r: 1, g: 1, b: 1, a: 1}
key2: {r: 0, g: 0, b: 0, a: 0}
key3: {r: 0, g: 0, b: 0, a: 0}
key4: {r: 0, g: 0, b: 0, a: 0}
key5: {r: 0, g: 0, b: 0, a: 0}
key6: {r: 0, g: 0, b: 0, a: 0}
key7: {r: 0, g: 0, b: 0, a: 0}
ctime0: 0
ctime1: 65535
ctime2: 0
ctime3: 0
ctime4: 0
ctime5: 0
ctime6: 0
ctime7: 0
atime0: 0
atime1: 65535
atime2: 0
atime3: 0
atime4: 0
atime5: 0
atime6: 0
atime7: 0
m_Mode: 0
m_ColorSpace: -1
m_NumColorKeys: 2
m_NumAlphaKeys: 2
numCornerVertices: 0
numCapVertices: 0
alignment: 0
textureMode: 0
textureScale: {x: 1, y: 1}
shadowBias: 0.5
generateLightingData: 0
m_UseWorldSpace: 0
m_Loop: 0
m_ApplyActiveColorSpace: 1
--- !u!1 &6122023196935183107
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1885524188671059666}
m_Layer: 0
m_Name: RepresentativeFace
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1885524188671059666
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6122023196935183107}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 782296095116550680}
- {fileID: 9186485450251357337}
- {fileID: 8665407476105022746}
m_Father: {fileID: 3444733339480260935}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &7707652147950496847
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3444733339480260935}
- component: {fileID: 8586535435441923330}
m_Layer: 0
m_Name: GroupBadge
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3444733339480260935
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7707652147950496847}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2644936306256428015}
- {fileID: 1885524188671059666}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &8586535435441923330
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7707652147950496847}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5dfc933dbfe92d64f990d1aa289a03d4, type: 3}
m_Name:
m_EditorClassIdentifier: MagnetismHeroes.UnityRuntime::MagnetismHeroes.UnityRuntime.Presentation.GroupBadge
_number: {fileID: 7156837666543227830}
_face: {fileID: 1885524188671059666}
--- !u!1 &7995294815621610086
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2644936306256428015}
- component: {fileID: 5962027146007004898}
- component: {fileID: 7156837666543227830}
m_Layer: 0
m_Name: Number
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2644936306256428015
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7995294815621610086}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3444733339480260935}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 2, y: 0.7}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!23 &5962027146007004898
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7995294815621610086}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 8207275579423446409, guid: a5bc77ef7ce7c7e4d85ac578cfcd6646, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &7156837666543227830
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7995294815621610086}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshPro
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 1
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: a5bc77ef7ce7c7e4d85ac578cfcd6646, type: 2}
m_sharedMaterial: {fileID: 8207275579423446409, guid: a5bc77ef7ce7c7e4d85ac578cfcd6646, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 0.12, g: 0.2, b: 0.31, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: 0
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 5
m_fontSizeBase: 5
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656bu
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 0
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
_SortingLayer: 0
_SortingLayerID: 0
_SortingOrder: 0
m_hasFontAssetChanged: 0
m_renderer: {fileID: 5962027146007004898}
m_maskType: 0
--- !u!1 &8873238367880750306
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 9186485450251357337}
- component: {fileID: 1721263544024944440}
- component: {fileID: 4797747157284027217}
m_Layer: 0
m_Name: RightEye
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &9186485450251357337
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8873238367880750306}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0.15, y: 0.05, z: 0}
m_LocalScale: {x: 0.08, y: 0.11, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1885524188671059666}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &1721263544024944440
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8873238367880750306}
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &4797747157284027217
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8873238367880750306}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: f625349e1f7edf249bc2f0006bde7ccf, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 1
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f6475c2f7cafe4441a6f4706a3b2b6a4
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,220 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &3730473297310058759
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4419960402240753643}
- component: {fileID: 8749146759555874692}
- component: {fileID: 8655814190357286106}
m_Layer: 0
m_Name: CountMarker
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &4419960402240753643
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3730473297310058759}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0.55, z: 0}
m_LocalScale: {x: 0.24, y: 0.08, z: 0.24}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3913606793712318518}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &8749146759555874692
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3730473297310058759}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &8655814190357286106
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3730473297310058759}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: e6eb301d5baaa3b4dba3ce2228f17fa3, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 1
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &4222153517735652016
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3913606793712318518}
- component: {fileID: 3708566260475808937}
- component: {fileID: 4380635146609339731}
- component: {fileID: 4902335425252088244}
- component: {fileID: 1331864523212970899}
m_Layer: 0
m_Name: MagnetFriend
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3913606793712318518
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4222153517735652016}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4419960402240753643}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &3708566260475808937
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4222153517735652016}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!135 &4380635146609339731
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4222153517735652016}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}
--- !u!23 &4902335425252088244
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4222153517735652016}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: ec5c546652eacdf4f9aa3fa65eb0c385, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 1
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &1331864523212970899
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4222153517735652016}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ef4ac8f42ba5a5343a5f0474eef2a1af, type: 3}
m_Name:
m_EditorClassIdentifier: MagnetismHeroes.UnityRuntime::MagnetismHeroes.UnityRuntime.Presentation.BallView
_body: {fileID: 4902335425252088244}
_countMarker: {fileID: 3730473297310058759}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 91ae23d420859244686c2c654ab58bf3
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,19 @@
using UnityEngine;
namespace MagnetismHeroes.UnityRuntime.Presentation
{
public sealed class SafeAreaPanel : MonoBehaviour
{
private Rect _last;
private void Update()
{
var area = Screen.safeArea;
if (_last == area || Screen.width == 0 || Screen.height == 0) return;
_last = area;
var rect = (RectTransform)transform;
rect.anchorMin = area.min / new Vector2(Screen.width, Screen.height);
rect.anchorMax = area.max / new Vector2(Screen.width, Screen.height);
rect.offsetMin = Vector2.zero; rect.offsetMax = Vector2.zero;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 561d1b7e5d46162459220d9c68d8511e
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace MagnetismHeroes.UnityRuntime.Presentation
{
[Serializable] public sealed class VoiceClip { public string id; public AudioClip clip; }
public sealed class VoicePlayer : MonoBehaviour
{
[SerializeField] private AudioSource _source;
[SerializeField] private VoiceClip[] _clips = Array.Empty<VoiceClip>();
private readonly Queue<AudioClip> _counts = new Queue<AudioClip>();
private AudioClip _guide;
private bool _playingCount;
public int PendingCount => _counts.Count + (_playingCount ? 1 : 0);
public string CurrentClipId => _source.clip == null ? null : _source.clip.name;
public void EnqueueCount(int count)
{
var clip = Find($"count_{count:D2}");
if (clip == null) return;
// A guide must never postpone or overlap a newly committed number.
_guide = null;
if (!_playingCount) _source.Stop();
_counts.Enqueue(clip); Advance();
}
public void Guide(string id)
{ _guide = Find(id); if (!_playingCount) { _source.Stop(); Advance(); } }
public void Clear()
{ _counts.Clear(); _guide = null; _playingCount = false; _source.Stop(); _source.clip = null; }
private AudioClip Find(string id) { foreach (var voice in _clips) if (voice.id == id) return voice.clip; return null; }
private void Advance()
{
if (_source.isPlaying) return;
_playingCount = false;
if (_counts.Count > 0) { _source.clip = _counts.Dequeue(); _playingCount = true; _source.Play(); }
else if (_guide != null) { _source.clip = _guide; _guide = null; _source.Play(); }
}
private void Update() => Advance();
private void OnApplicationPause(bool paused) { if (paused) Clear(); }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: dee5bae88f250144c88bbd4995dbdcbb
@@ -0,0 +1,20 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
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: 25d91ab5f7c987840910f7a3683c8d3f, type: 3}
m_Name: Stage01
m_EditorClassIdentifier: MagnetismHeroes.UnityRuntime::MagnetismHeroes.UnityRuntime.StageAssets.StageAsset
_stageId: 1
_kind: 0
_initialGroups: 01000000i
_targetGroups: 01000000i
_ballPositions:
- {x: -1.7, y: 0.8, z: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1bd91d4fcb826e54e861dc2275fcbdee
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,20 @@
using System;
using MagnetismHeroes.Core;
using UnityEngine;
namespace MagnetismHeroes.UnityRuntime.StageAssets
{
[CreateAssetMenu(menuName = "Magnetism Heroes/Stage")]
public sealed class StageAsset : ScriptableObject
{
[SerializeField] private int _stageId = 1;
[SerializeField] private StageKind _kind;
[SerializeField] private int[] _initialGroups = { 1 };
[SerializeField] private int[] _targetGroups = { 1 };
[SerializeField] private Vector3[] _ballPositions = { new Vector3(-1.7f, 0.8f, 0) };
public int StageId => _stageId;
public StageDefinition ToDefinition() => new StageDefinition(_stageId, _kind, _initialGroups, _targetGroups,
Array.ConvertAll(_ballPositions, position => new Position3(position.x, position.y, position.z)));
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 25d91ab5f7c987840910f7a3683c8d3f
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f54d1bd14bd3ca042bd867b519fee8cc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6ab70aee4d56447429c680537fbf93ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,46 @@
Digitized data copyright (c) 2010 Google Corporation
with Reserved Font Arimo, Tinos and Cousine.
Copyright (c) 2012 Red Hat, Inc.
with Reserved Font Name Liberation.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6e59c59b81ab47f9b6ec5781fa725d2c
timeCreated: 1484171296
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: e3265ab4bf004d28a9537516768c1c75
timeCreated: 1484171297
licenseType: Pro
TrueTypeFontImporter:
serializedVersion: 2
fontSize: 16
forceTextureCase: -2
characterSpacing: 1
characterPadding: 0
includeFontData: 1
use2xBehaviour: 0
fontNames: []
fallbackFontReferences: []
customCharacters:
fontRenderingMode: 0
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 243e06394e614e5d99fab26083b707fa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 731f1baa9d144a9897cb1d341c2092b8
folderAsset: yes
timeCreated: 1442040525
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,106 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: LiberationSans SDF - Drop Shadow
m_Shader: {fileID: 4800000, guid: fe393ace9b354375a9cb14cdbbc28be4, type: 3}
m_ShaderKeywords: OUTLINE_ON UNDERLAY_ON
m_LightmapFlags: 5
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _Cube:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _FaceTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 28684132378477856, guid: 8f586378b4e144a9851e7b34d9b748ee,
type: 2}
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}
m_Floats:
- _Ambient: 0.5
- _Bevel: 0.5
- _BevelClamp: 0
- _BevelOffset: 0
- _BevelRoundness: 0
- _BevelWidth: 0
- _BumpFace: 0
- _BumpOutline: 0
- _ColorMask: 15
- _Diffuse: 0.5
- _DiffusePower: 1
- _FaceDilate: 0.1
- _FaceUVSpeedX: 0
- _FaceUVSpeedY: 0
- _GlowInner: 0.05
- _GlowOffset: 0
- _GlowOuter: 0.05
- _GlowPower: 0.75
- _GradientScale: 10
- _LightAngle: 3.1416
- _MaskSoftnessX: 0
- _MaskSoftnessY: 0
- _OutlineSoftness: 0
- _OutlineUVSpeedX: 0
- _OutlineUVSpeedY: 0
- _OutlineWidth: 0.1
- _PerspectiveFilter: 0.875
- _Reflectivity: 10
- _ScaleRatioA: 0.9
- _ScaleRatioB: 0.73125
- _ScaleRatioC: 0.64125
- _ScaleX: 1
- _ScaleY: 1
- _ShaderFlags: 0
- _Sharpness: 0
- _SpecularPower: 2
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _TextureHeight: 1024
- _TextureWidth: 1024
- _UnderlayDilate: 0
- _UnderlayOffsetX: 0.5
- _UnderlayOffsetY: -0.5
- _UnderlaySoftness: 0.05
- _VertexOffsetX: 0
- _VertexOffsetY: 0
- _WeightBold: 0.75
- _WeightNormal: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EnvMatrixRotation: {r: 0, g: 0, b: 0, a: 0}
- _FaceColor: {r: 1, g: 1, b: 1, a: 1}
- _GlowColor: {r: 0, g: 1, b: 0, a: 0.5}
- _MaskCoord: {r: 0, g: 0, b: 32767, a: 32767}
- _OutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectFaceColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectOutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
- _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e73a58f6e2794ae7b1b7e50b7fb811b0
timeCreated: 1484172806
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,348 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2180264
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: LiberationSans SDF Material
m_Shader: {fileID: 4800000, guid: fe393ace9b354375a9cb14cdbbc28be4, type: 3}
m_ShaderKeywords:
m_LightmapFlags: 1
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _Cube:
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}
- _FaceTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 28268798066460806}
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}
- _OutlineTex:
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}
m_Floats:
- _Ambient: 0.5
- _Bevel: 0.5
- _BevelClamp: 0
- _BevelOffset: 0
- _BevelRoundness: 0
- _BevelWidth: 0
- _BumpFace: 0
- _BumpOutline: 0
- _BumpScale: 1
- _ColorMask: 15
- _CullMode: 0
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _Diffuse: 0.5
- _DstBlend: 0
- _FaceDilate: 0
- _FaceUVSpeedX: 0
- _FaceUVSpeedY: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _GlowInner: 0.05
- _GlowOffset: 0
- _GlowOuter: 0.05
- _GlowPower: 0.75
- _GradientScale: 10
- _LightAngle: 3.1416
- _MaskSoftnessX: 0
- _MaskSoftnessY: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _OutlineSoftness: 0
- _OutlineUVSpeedX: 0
- _OutlineUVSpeedY: 0
- _OutlineWidth: 0
- _Parallax: 0.02
- _PerspectiveFilter: 0.875
- _Reflectivity: 10
- _ScaleRatioA: 0.90909094
- _ScaleRatioB: 0.73125
- _ScaleRatioC: 0.7386364
- _ScaleX: 1
- _ScaleY: 1
- _ShaderFlags: 0
- _Sharpness: 0
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SpecularPower: 2
- _SrcBlend: 1
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _TextureHeight: 512
- _TextureWidth: 512
- _UVSec: 0
- _UnderlayDilate: 0
- _UnderlayOffsetX: 0
- _UnderlayOffsetY: 0
- _UnderlaySoftness: 0
- _VertexOffsetX: 0
- _VertexOffsetY: 0
- _WeightBold: 0.75
- _WeightNormal: 0
- _ZWrite: 1
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _EnvMatrixRotation: {r: 0, g: 0, b: 0, a: 0}
- _FaceColor: {r: 1, g: 1, b: 1, a: 1}
- _GlowColor: {r: 0, g: 1, b: 0, a: 0.5}
- _MaskCoord: {r: 0, g: 0, b: 32767, a: 32767}
- _OutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectFaceColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectOutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
- _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5}
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
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: 71c1514a6bd24e1e882cebbe1904ce04, type: 3}
m_Name: LiberationSans SDF - Fallback
m_EditorClassIdentifier:
m_Version: 1.1.0
m_Material: {fileID: 2180264}
m_SourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75
m_SourceFontFile: {fileID: 12800000, guid: e3265ab4bf004d28a9537516768c1c75, type: 3}
m_AtlasPopulationMode: 1
InternalDynamicOS: 0
m_FaceInfo:
m_FaceIndex: 0
m_FamilyName: Liberation Sans
m_StyleName: Regular
m_PointSize: 86
m_Scale: 1
m_UnitsPerEM: 2048
m_LineHeight: 98.8916
m_AscentLine: 77.853516
m_CapLine: 59
m_MeanLine: 45
m_Baseline: 0
m_DescentLine: -18.22461
m_SuperscriptOffset: 77.853516
m_SuperscriptSize: 0.5
m_SubscriptOffset: -18.22461
m_SubscriptSize: 0.5
m_UnderlineOffset: -12.261719
m_UnderlineThickness: 6.298828
m_StrikethroughOffset: 18
m_StrikethroughThickness: 6.298828
m_TabWidth: 24
m_GlyphTable: []
m_CharacterTable: []
m_AtlasTextures:
- {fileID: 28268798066460806}
m_AtlasTextureIndex: 0
m_IsMultiAtlasTexturesEnabled: 1
m_ClearDynamicDataOnBuild: 1
m_UsedGlyphRects: []
m_FreeGlyphRects:
- m_X: 0
m_Y: 0
m_Width: 511
m_Height: 511
m_fontInfo:
Name: Liberation Sans
PointSize: 86
Scale: 1
CharacterCount: 250
LineHeight: 98.90625
Baseline: 0
Ascender: 77.84375
CapHeight: 59.1875
Descender: -18.21875
CenterLine: 0
SuperscriptOffset: 77.84375
SubscriptOffset: -12.261719
SubSize: 0.5
Underline: -12.261719
UnderlineThickness: 6.298828
strikethrough: 23.675
strikethroughThickness: 0
TabWidth: 239.0625
Padding: 9
AtlasWidth: 1024
AtlasHeight: 1024
atlas: {fileID: 0}
m_AtlasWidth: 512
m_AtlasHeight: 512
m_AtlasPadding: 9
m_AtlasRenderMode: 4169
m_glyphInfoList: []
m_KerningTable:
kerningPairs: []
m_FontFeatureTable:
m_MultipleSubstitutionRecords: []
m_LigatureSubstitutionRecords: []
m_GlyphPairAdjustmentRecords: []
m_MarkToBaseAdjustmentRecords: []
m_MarkToMarkAdjustmentRecords: []
fallbackFontAssets: []
m_FallbackFontAssetTable: []
m_CreationSettings:
sourceFontFileName:
sourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75
faceIndex: 0
pointSizeSamplingMode: 0
pointSize: 86
padding: 9
paddingMode: 0
packingMode: 4
atlasWidth: 512
atlasHeight: 512
characterSetSelectionMode: 1
characterSequence: 32 - 126, 160 - 255, 8192 - 8303, 8364, 8482, 9633
referencedFontAssetGUID: 8f586378b4e144a9851e7b34d9b748ee
referencedTextAssetGUID:
fontStyle: 0
fontStyleModifier: 0
renderMode: 4169
includeFontFeatures: 1
m_FontWeightTable:
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
fontWeights:
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
normalStyle: 0
normalSpacingOffset: 0
boldStyle: 0.75
boldSpacing: 7
italicStyle: 35
tabSize: 10
--- !u!28 &28268798066460806
Texture2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: LiberationSans SDF Atlas
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_ForcedFallbackFormat: 4
m_DownscaleFallback: 0
serializedVersion: 2
m_Width: 0
m_Height: 0
m_CompleteImageSize: 0
m_TextureFormat: 1
m_MipCount: 1
m_IsReadable: 1
m_StreamingMipmaps: 0
m_StreamingMipmapsPriority: 0
m_AlphaIsTransparency: 0
m_ImageCount: 1
m_TextureDimension: 2
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1
m_Aniso: 1
m_MipBias: 0
m_WrapU: 0
m_WrapV: 0
m_WrapW: 0
m_LightmapFormat: 0
m_ColorSpace: 0
image data: 0
_typelessdata:
m_StreamData:
offset: 0
size: 0
path:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e498d1c8094910479dc3e1b768306a4
timeCreated: 1484171803
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,104 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: LiberationSans SDF - Outline
m_Shader: {fileID: 4800000, guid: fe393ace9b354375a9cb14cdbbc28be4, type: 3}
m_ShaderKeywords: OUTLINE_ON
m_LightmapFlags: 5
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _Cube:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _FaceTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 28684132378477856, guid: 8f586378b4e144a9851e7b34d9b748ee,
type: 2}
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}
m_Floats:
- _Ambient: 0.5
- _Bevel: 0.5
- _BevelClamp: 0
- _BevelOffset: 0
- _BevelRoundness: 0
- _BevelWidth: 0
- _BumpFace: 0
- _BumpOutline: 0
- _ColorMask: 15
- _Diffuse: 0.5
- _FaceDilate: 0.1
- _FaceUVSpeedX: 0
- _FaceUVSpeedY: 0
- _GlowInner: 0.05
- _GlowOffset: 0
- _GlowOuter: 0.05
- _GlowPower: 0.75
- _GradientScale: 10
- _LightAngle: 3.1416
- _MaskSoftnessX: 0
- _MaskSoftnessY: 0
- _OutlineSoftness: 0
- _OutlineUVSpeedX: 0
- _OutlineUVSpeedY: 0
- _OutlineWidth: 0.1
- _PerspectiveFilter: 0.875
- _Reflectivity: 10
- _ScaleRatioA: 0.9
- _ScaleRatioB: 0.73125
- _ScaleRatioC: 0.64125
- _ScaleX: 1
- _ScaleY: 1
- _ShaderFlags: 0
- _Sharpness: 0
- _SpecularPower: 2
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _TextureHeight: 1024
- _TextureWidth: 1024
- _UnderlayDilate: 0
- _UnderlayOffsetX: 0
- _UnderlayOffsetY: 0
- _UnderlaySoftness: 0
- _VertexOffsetX: 0
- _VertexOffsetY: 0
- _WeightBold: 0.75
- _WeightNormal: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _EnvMatrixRotation: {r: 0, g: 0, b: 0, a: 0}
- _FaceColor: {r: 1, g: 1, b: 1, a: 1}
- _GlowColor: {r: 0, g: 1, b: 0, a: 0.5}
- _MaskCoord: {r: 0, g: 0, b: 32767, a: 32767}
- _OutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectFaceColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectOutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
- _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 79459efec17a4d00a321bdcc27bbc385
timeCreated: 1484172856
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8f586378b4e144a9851e7b34d9b748ee
timeCreated: 1484171803
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1 @@
)]}〕〉》」』】〙〗〟’”⦆»ヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻‐゠–〜?!‼⁇⁈⁉・、%,.:;。!?]):;=}¢°"†‡℃〆%,.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fade42e8bc714b018fac513c043d323b
timeCreated: 1425440388
licenseType: Store
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1 @@
([{〔〈《「『【〘〖〝‘“⦅«$—…‥〳〴〵\[({£¥"々〇$¥₩ #
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d82c1b31c7e74239bff1220585707d2b
timeCreated: 1425440388
licenseType: Store
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4aecb92fff08436c8303b10eab8da368
folderAsset: yes
timeCreated: 1441876950
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,81 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
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: ab2114bdc8544297b417dfefe9f1e410, type: 3}
m_Name: Default Style Sheet
m_EditorClassIdentifier:
m_StyleList:
- m_Name: Normal
m_HashCode: -1183493901
m_OpeningDefinition:
m_ClosingDefinition:
m_OpeningTagArray:
m_ClosingTagArray:
- m_Name: H1
m_HashCode: 2425
m_OpeningDefinition: <size=2em><b><#40ff80>*
m_ClosingDefinition: '*</size></b></color>'
m_OpeningTagArray: 3c00000073000000690000007a000000650000003d00000032000000650000006d0000003e0000003c000000620000003e0000003c000000230000003400000030000000660000006600000038000000300000003e0000002a000000
m_ClosingTagArray: 2a0000003c0000002f00000073000000690000007a000000650000003e0000003c0000002f000000620000003e0000003c0000002f000000630000006f0000006c0000006f000000720000003e000000
- m_Name: Quote
m_HashCode: 93368250
m_OpeningDefinition: <i><size=75%><margin=10%>
m_ClosingDefinition: </i></size></width></margin>
m_OpeningTagArray: 3c000000690000003e0000003c00000073000000690000007a000000650000003d0000003700000035000000250000003e0000003c0000006d000000610000007200000067000000690000006e0000003d0000003100000030000000250000003e000000
m_ClosingTagArray: 3c0000002f000000690000003e0000003c0000002f00000073000000690000007a000000650000003e0000003c0000002f00000077000000690000006400000074000000680000003e0000003c0000002f0000006d000000610000007200000067000000690000006e0000003e000000
- m_Name: A
m_HashCode: 65
m_OpeningDefinition: <color=#40a0ff><u>
m_ClosingDefinition: </u></color>
m_OpeningTagArray: 3c000000630000006f0000006c0000006f000000720000003d000000230000003400000030000000610000003000000066000000660000003e0000003c000000750000003e000000
m_ClosingTagArray: 3c0000002f000000750000003e0000003c0000002f000000630000006f0000006c0000006f000000720000003e000000
- m_Name: Link
m_HashCode: 2656128
m_OpeningDefinition: <u><#40a0ff><link="ID_01">
m_ClosingDefinition: </u></color></link>
m_OpeningTagArray: 3c000000750000003e0000003c000000230000003400000030000000610000003000000066000000660000003e0000003c0000006c000000690000006e0000006b0000003d0000002200000049000000440000005f0000003000000031000000220000003e000000
m_ClosingTagArray: 3c0000002f000000750000003e0000003c0000002f000000630000006f0000006c0000006f000000720000003e0000003c0000002f0000006c000000690000006e0000006b0000003e000000
- m_Name: Title
m_HashCode: 97690656
m_OpeningDefinition: <size=125%><b><align=center>
m_ClosingDefinition: </size></b></align>
m_OpeningTagArray: 3c00000073000000690000007a000000650000003d000000310000003200000035000000250000003e0000003c000000620000003e0000003c000000610000006c00000069000000670000006e0000003d00000063000000650000006e0000007400000065000000720000003e000000
m_ClosingTagArray: 3c0000002f00000073000000690000007a000000650000003e0000003c0000002f000000620000003e0000003c0000002f000000610000006c00000069000000670000006e0000003e000000
- m_Name: H2
m_HashCode: 2426
m_OpeningDefinition: <size=1.5em><b><#4080FF>
m_ClosingDefinition: </size></b></color>
m_OpeningTagArray: 3c00000073000000690000007a000000650000003d000000310000002e00000035000000650000006d0000003e0000003c000000620000003e0000003c000000230000003400000030000000380000003000000046000000460000003e000000
m_ClosingTagArray: 3c0000002f00000073000000690000007a000000650000003e0000003c0000002f000000620000003e0000003c0000002f000000630000006f0000006c0000006f000000720000003e000000
- m_Name: H3
m_HashCode: 2427
m_OpeningDefinition: <size=1.17em><b><#FF8040>
m_ClosingDefinition: </size></b></color>
m_OpeningTagArray: 3c00000073000000690000007a000000650000003d000000310000002e0000003100000037000000650000006d0000003e0000003c000000620000003e0000003c000000230000004600000046000000380000003000000034000000300000003e000000
m_ClosingTagArray: 3c0000002f00000073000000690000007a000000650000003e0000003c0000002f000000620000003e0000003c0000002f000000630000006f0000006c0000006f000000720000003e000000
- m_Name: C1
m_HashCode: 2194
m_OpeningDefinition: <color=#ffff40>
m_ClosingDefinition: </color>
m_OpeningTagArray: 3c000000630000006f0000006c0000006f000000720000003d000000230000006600000066000000660000006600000034000000300000003e000000
m_ClosingTagArray: 3c0000002f000000630000006f0000006c0000006f000000720000003e000000
- m_Name: C2
m_HashCode: 2193
m_OpeningDefinition: <color=#ff40FF><size=125%>
m_ClosingDefinition: </color></size>
m_OpeningTagArray: 3c000000630000006f0000006c0000006f000000720000003d000000230000006600000066000000340000003000000046000000460000003e0000003c00000073000000690000007a000000650000003d000000310000003200000035000000250000003e000000
m_ClosingTagArray: 3c0000002f000000630000006f0000006c0000006f000000720000003e0000003c0000002f00000073000000690000007a000000650000003e000000
- m_Name: C3
m_HashCode: 2192
m_OpeningDefinition: <color=#80A0FF><b>
m_ClosingDefinition: </color></b>
m_OpeningTagArray: 3c000000630000006f0000006c0000006f000000720000003d000000230000003800000030000000410000003000000046000000460000003e0000003c000000620000003e000000
m_ClosingTagArray: 3c0000002f000000630000006f0000006c0000006f000000720000003e0000003c0000002f000000620000003e000000
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f952c082cb03451daed3ee968ac6c63e
timeCreated: 1432805430
licenseType: Store
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,52 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
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: 2705215ac5b84b70bacc50632be6e391, type: 3}
m_Name: TMP Settings
m_EditorClassIdentifier:
assetVersion: 2
m_TextWrappingMode: 1
m_enableKerning: 1
m_ActiveFontFeatures: 00000000
m_enableExtraPadding: 0
m_enableTintAllSprites: 0
m_enableParseEscapeCharacters: 1
m_EnableRaycastTarget: 1
m_GetFontFeaturesAtRuntime: 1
m_missingGlyphCharacter: 0
m_ClearDynamicDataOnBuild: 1
m_warningsDisabled: 0
m_defaultFontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_defaultFontAssetPath: Fonts & Materials/
m_defaultFontSize: 36
m_defaultAutoSizeMinRatio: 0.5
m_defaultAutoSizeMaxRatio: 2
m_defaultTextMeshProTextContainerSize: {x: 20, y: 5}
m_defaultTextMeshProUITextContainerSize: {x: 200, y: 50}
m_autoSizeTextContainer: 0
m_IsTextObjectScaleStatic: 0
m_fallbackFontAssets: []
m_matchMaterialPreset: 1
m_HideSubTextObjects: 0
m_defaultSpriteAsset: {fileID: 11400000, guid: c41005c129ba4d66911b75229fd70b45,
type: 2}
m_defaultSpriteAssetPath: Sprite Assets/
m_enableEmojiSupport: 1
m_MissingCharacterSpriteUnicode: 0
m_EmojiFallbackTextAssets: []
m_defaultColorGradientPresetsPath: Color Gradient Presets/
m_defaultStyleSheet: {fileID: 11400000, guid: f952c082cb03451daed3ee968ac6c63e,
type: 2}
m_StyleSheetsResourcePath:
m_leadingCharacters: {fileID: 4900000, guid: d82c1b31c7e74239bff1220585707d2b, type: 3}
m_followingCharacters: {fileID: 4900000, guid: fade42e8bc714b018fac513c043d323b,
type: 3}
m_UseModernHangulLineBreakingRules: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3f5b5dff67a942289a9defa416b206f3
timeCreated: 1436653997
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e9f693669af91aa45ad615fc681ed29f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,178 @@
float2 UnpackUV(float uv)
{
float2 output;
output.x = floor(uv / 4096.0);
output.y = uv - 4096.0 * output.x;
return output * 0.001953125;
}
float4 BlendARGB(float4 overlying, float4 underlying)
{
overlying.rgb *= overlying.a;
underlying.rgb *= underlying.a;
float3 blended = overlying.rgb + ((1 - overlying.a) * underlying.rgb);
float alpha = underlying.a + (1 - underlying.a) * overlying.a;
return float4(blended / alpha, alpha);
}
float3 GetSpecular(float3 n, float3 l)
{
float spec = pow(max(0.0, dot(n, l)), _Reflectivity);
return _SpecularColor.rgb * spec * _SpecularPower;
}
void GetSurfaceNormal_float(texture2D atlas, float textureWidth, float textureHeight, float2 uv, bool isFront, out float3 nornmal)
{
float3 delta = float3(1.0 / textureWidth, 1.0 / textureHeight, 0.0);
// Read "height field"
float4 h = float4(
SAMPLE_TEXTURE2D(atlas, SamplerState_Linear_Clamp, uv - delta.xz).a,
SAMPLE_TEXTURE2D(atlas, SamplerState_Linear_Clamp, uv + delta.xz).a,
SAMPLE_TEXTURE2D(atlas, SamplerState_Linear_Clamp, uv - delta.zy).a,
SAMPLE_TEXTURE2D(atlas, SamplerState_Linear_Clamp, uv + delta.zy).a);
bool raisedBevel = _BevelType;
h += _BevelOffset;
float bevelWidth = max(.01, _BevelWidth);
// Track outline
h -= .5;
h /= bevelWidth;
h = saturate(h + .5);
if (raisedBevel) h = 1 - abs(h * 2.0 - 1.0);
h = lerp(h, sin(h * 3.141592 / 2.0), float4(_BevelRoundness, _BevelRoundness, _BevelRoundness, _BevelRoundness));
h = min(h, 1.0 - float4(_BevelClamp, _BevelClamp, _BevelClamp, _BevelClamp));
h *= _BevelAmount * bevelWidth * _GradientScale * -2.0;
float3 va = normalize(float3(-1.0, 0.0, h.y - h.x));
float3 vb = normalize(float3(0.0, 1.0, h.w - h.z));
float3 f = float3(1, 1, 1);
if (isFront) f = float3(1, 1, -1);
nornmal = cross(va, vb) * f;
}
void EvaluateLight_float(float4 faceColor, float3 n, out float4 color)
{
n.z = abs(n.z);
float3 light = normalize(float3(sin(_LightAngle), cos(_LightAngle), 1.0));
float3 col = max(faceColor.rgb, 0) + GetSpecular(n, light)* faceColor.a;
//faceColor.rgb += col * faceColor.a;
col *= 1 - (dot(n, light) * _Diffuse);
col *= lerp(_Ambient, 1, n.z * n.z);
//fixed4 reflcol = texCUBE(_Cube, reflect(input.viewDir, -n));
//faceColor.rgb += reflcol.rgb * lerp(_ReflectFaceColor.rgb, _ReflectOutlineColor.rgb, saturate(sd + outline * 0.5)) * faceColor.a;
color = float4(col, faceColor.a);
}
// Add custom function to handle time in HDRP
//
void GenerateUV_float(float2 inUV, float4 transform, float2 animSpeed, out float2 outUV)
{
outUV = inUV * transform.xy + transform.zw + (animSpeed * _Time.y);
}
void ComputeUVOffset_float(float texWidth, float texHeight, float2 offset, float SDR, out float2 uvOffset)
{
uvOffset = float2(-offset.x * SDR / texWidth, -offset.y * SDR / texHeight);
}
void ScreenSpaceRatio2_float(float4x4 projection, float4 position, float2 objectScale, float screenWidth, float screenHeight, float fontScale, out float SSR)
{
float2 pixelSize = position.w;
pixelSize /= (objectScale * mul((float2x2)projection, float2(screenWidth, screenHeight)));
SSR = rsqrt(dot(pixelSize, pixelSize)*2) * fontScale;
}
// UV : Texture coordinate of the source distance field texture
// TextureSize : Size of the source distance field texture
// Filter : Enable perspective filter (soften)
void ScreenSpaceRatio_float(float2 UV, float TextureSize, bool Filter, out float SSR)
{
if(Filter)
{
float2 a = float2(ddx(UV.x), ddy(UV.x));
float2 b = float2(ddx(UV.y), ddy(UV.y));
float s = lerp(dot(a,a), dot(b,b), 0.5);
SSR = rsqrt(s) / TextureSize;
}
else
{
float s = rsqrt(abs(ddx(UV.x) * ddy(UV.y) - ddy(UV.x) * ddx(UV.y)));
SSR = s / TextureSize;
}
}
// SSR : Screen Space Ratio
// SD : Signed Distance (encoded : Distance / SDR + .5)
// SDR : Signed Distance Ratio
//
// IsoPerimeter : Dilate / Contract the shape
void ComputeSDF_float(float SSR, float SD, float SDR, float isoPerimeter, float softness, out float outAlpha)
{
softness *= SSR * SDR;
float d = (SD - 0.5) * SDR; // Signed distance to edge, in Texture space
outAlpha = saturate((d * 2.0 * SSR + 0.5 + isoPerimeter * SDR * SSR + softness * 0.5) / (1.0 + softness)); // Screen pixel coverage (alpha)
}
void ComputeSDF2_float(float SSR, float SD, float SDR, float2 isoPerimeter, float2 softness, out float2 outAlpha)
{
softness *= SSR * SDR;
float d = (SD - 0.5f) * SDR;
outAlpha = saturate((d * 2.0f * SSR + 0.5f + isoPerimeter * SDR * SSR + softness * 0.5) / (1.0 + softness));
}
void ComputeSDF4_float(float SSR, float SD, float SDR, float4 isoPerimeter, float4 softness, out float4 outAlpha)
{
softness *= SSR * SDR;
float d = (SD - 0.5f) * SDR;
outAlpha = saturate((d * 2.0f * SSR + 0.5f + isoPerimeter * SDR * SSR + softness * 0.5) / (1.0 + softness));
}
void ComputeSDF44_float(float SSR, float4 SD, float SDR, float4 isoPerimeter, float4 softness, bool outline, out float4 outAlpha)
{
softness *= SSR * SDR;
float4 d = (SD - 0.5f) * SDR;
if(outline) d.w = max(max(d.x, d.y), d.z);
outAlpha = saturate((d * 2.0f * SSR + 0.5f + isoPerimeter * SDR * SSR + softness * 0.5) / (1.0 + softness));
}
void Composite_float(float4 overlying, float4 underlying, out float4 outColor)
{
outColor = BlendARGB(overlying, underlying);
}
// Face only
void Layer1_float(float alpha, float4 color0, out float4 outColor)
{
color0.a *= alpha;
outColor = color0;
}
// Face + 1 Outline
void Layer2_float(float2 alpha, float4 color0, float4 color1, out float4 outColor)
{
color1.a *= alpha.y;
color0.rgb *= color0.a; color1.rgb *= color1.a;
outColor = lerp(color1, color0, alpha.x);
outColor.rgb /= outColor.a;
}
// Face + 3 Outline
void Layer4_float(float4 alpha, float4 color0, float4 color1, float4 color2, float4 color3, out float4 outColor)
{
color3.a *= alpha.w;
color0.rgb *= color0.a; color1.rgb *= color1.a; color2.rgb *= color2.a; color3.rgb *= color3.a;
outColor = lerp(lerp(lerp(color3, color2, alpha.z), color1, alpha.y), color0, alpha.x);
outColor.rgb /= outColor.a;
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 96de908384869cd409c75efa351d5edf
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
preprocessorOverride: 0
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More