using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.UI; using UnityEngine.Rendering; namespace Class03.Editor { // 에디터에서 씬과 에셋을 만드는 도구입니다. 게임 실행에는 사용하지 않습니다. public static class Class03SceneBuilder { private const string Root = "Assets/Class03"; [MenuItem("Class03/Create Scene")] public static void CreateScene() { if (AssetDatabase.LoadAssetAtPath("Assets/Scenes/Class03.unity") != null) throw new System.InvalidOperationException("Class03 scene already exists; refusing to overwrite it."); if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return; System.IO.Directory.CreateDirectory(Root + "/Materials"); System.IO.Directory.CreateDirectory(Root + "/Meshes"); System.IO.Directory.CreateDirectory(Root + "/Prefabs"); AssetDatabase.Refresh(); var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); var floor = Material("Floor", new Color(0.065f, 0.12f, 0.20f), 0.15f, 0.25f); var wall = Material("Walls", new Color(0.10f, 0.25f, 0.34f), 0.35f, 0.4f); var cyan = Material("Cyan", new Color(0.12f, 0.85f, 0.94f), 0.25f, 0.6f); var gold = Material("StarGold", new Color(1f, 0.67f, 0.08f), 0.55f, 0.65f); var ballMat = Material("Ball", new Color(0.83f, 0.96f, 1f), 0.65f, 0.55f); var grid = Material("Grid", new Color(0.12f, 0.22f, 0.31f), 0f, 0f); var arena = new GameObject("Arena").transform; Cube("Floor", new Vector3(0,-0.4f,0), new Vector3(20,0.8f,16), floor, arena); Cube("Wall North", new Vector3(0,1,8), new Vector3(21,2,0.6f), wall, arena); Cube("Wall South", new Vector3(0,1,-8), new Vector3(21,2,0.6f), wall, arena); Cube("Wall East", new Vector3(10,1,0), new Vector3(0.6f,2,16), wall, arena); Cube("Wall West", new Vector3(-10,1,0), new Vector3(0.6f,2,16), wall, arena); Cube("North Light Strip", new Vector3(0,2.015f,8), new Vector3(20.6f,0.035f,0.12f), cyan, arena, false); Cube("South Light Strip", new Vector3(0,2.015f,-8), new Vector3(20.6f,0.035f,0.12f), cyan, arena, false); Cube("East Light Strip", new Vector3(10,2.015f,0), new Vector3(0.12f,0.035f,16), cyan, arena, false); Cube("West Light Strip", new Vector3(-10,2.015f,0), new Vector3(0.12f,0.035f,16), cyan, arena, false); var lines = new GameObject("Floor Grid").transform; lines.SetParent(arena); for (int x=-8;x<=8;x+=2) Cube("Grid X " + x,new Vector3(x,0.006f,0),new Vector3(0.018f,0.01f,15.4f),grid,lines,false); for (int z=-6;z<=6;z+=2) Cube("Grid Z " + z,new Vector3(0,0.006f,z),new Vector3(19.4f,0.01f,0.018f),grid,lines,false); var ball = GameObject.CreatePrimitive(PrimitiveType.Sphere); ball.name = "Player Ball"; ball.transform.position = new Vector3(0,0.65f,-5.5f); ball.transform.localScale = Vector3.one * 1.2f; ball.GetComponent().sharedMaterial = ballMat; var body = ball.AddComponent(); body.mass = 1f; body.linearDamping = 1.25f; body.angularDamping = 0.25f; body.maxAngularVelocity = 20f; body.interpolation = RigidbodyInterpolation.Interpolate; body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic; ball.AddComponent(); ball.AddComponent(); // 작은 표식이 공과 함께 회전하여 실제 구름을 알아보기 쉽게 합니다. var marker = GameObject.CreatePrimitive(PrimitiveType.Sphere); marker.name = "Rolling Marker"; Object.DestroyImmediate(marker.GetComponent()); marker.transform.SetParent(ball.transform,false); marker.transform.localPosition = new Vector3(0,0.44f,0); marker.transform.localScale = new Vector3(0.3f,0.15f,0.3f); marker.GetComponent().sharedMaterial = cyan; var counter = new GameObject("Score Counter").AddComponent(); Mesh mesh = StarMesh(); AssetDatabase.CreateAsset(mesh,Root + "/Meshes/Star.asset"); var star = new GameObject("Star"); var trigger = star.AddComponent(); trigger.radius = 0.7f; trigger.isTrigger = true; star.AddComponent(); var visual = new GameObject("Visual"); visual.transform.SetParent(star.transform,false); visual.AddComponent().sharedMesh = mesh; visual.AddComponent().sharedMaterial = gold; visual.AddComponent(); var prefab = PrefabUtility.SaveAsPrefabAsset(star,Root + "/Prefabs/Star.prefab"); Object.DestroyImmediate(star); var stars = new GameObject("Stars").transform; Vector3[] positions = { new Vector3(-7,0.9f,-4.5f), new Vector3(-3.5f,0.9f,-1.5f), new Vector3(-7,0.9f,3.5f), new Vector3(-2.5f,0.9f,5), new Vector3(2.5f,0.9f,5), new Vector3(7,0.9f,3.5f), new Vector3(3.5f,0.9f,-1.5f), new Vector3(7,0.9f,-4.5f) }; for(int i=0;i().Initialize(counter); PrefabUtility.RecordPrefabInstancePropertyModifications(instance.GetComponent()); var motion = new SerializedObject(instance.GetComponentInChildren()); motion.FindProperty("phase").floatValue=i*0.65f; motion.ApplyModifiedPropertiesWithoutUndo(); } var camera = new GameObject("Main Camera").AddComponent(); camera.tag="MainCamera"; camera.transform.position=new Vector3(0,22,-20); camera.transform.LookAt(new Vector3(0,0,0)); camera.orthographic=true; camera.orthographicSize=11f; camera.clearFlags=CameraClearFlags.SolidColor; camera.backgroundColor=new Color(0.018f,0.035f,0.065f); camera.nearClipPlane=0.1f; camera.farClipPlane=100f; camera.gameObject.AddComponent(); var light=new GameObject("Directional Light").AddComponent(); light.type=LightType.Directional; light.intensity=1.5f; light.shadows=LightShadows.Soft; light.transform.rotation=Quaternion.Euler(45,-30,0); RenderSettings.ambientMode=AmbientMode.Flat; RenderSettings.ambientLight=new Color(0.48f,0.58f,0.72f); CreateUI(counter); EditorSceneManager.SaveScene(scene,"Assets/Scenes/Class03.unity"); AssetDatabase.SaveAssets(); Selection.activeGameObject=ball; Debug.Log("Class03 created: ball, 8 stars, 4 walls and score UI."); } private static Material Material(string name,Color color,float metallic,float smoothness) { Shader shader=Shader.Find("Universal Render Pipeline/Lit"); if(shader==null) throw new System.InvalidOperationException("URP Lit shader missing."); var material=new Material(shader){name=name}; material.SetColor("_BaseColor",color); material.SetFloat("_Metallic",metallic); material.SetFloat("_Smoothness",smoothness); AssetDatabase.CreateAsset(material,Root + "/Materials/" + name + ".mat"); return material; } private static void Cube(string name,Vector3 position,Vector3 scale,Material material,Transform parent,bool solid=true) { var go=GameObject.CreatePrimitive(PrimitiveType.Cube); go.name=name; go.transform.SetParent(parent); go.transform.position=position; go.transform.localScale=scale; go.GetComponent().sharedMaterial=material; if(!solid) Object.DestroyImmediate(go.GetComponent()); } private static Mesh StarMesh() { var vertices=new Vector3[22]; vertices[0]=new Vector3(0,0,-0.16f); vertices[11]=new Vector3(0,0,0.16f); for(int i=0;i<10;i++) { float angle=(90+i*36)*Mathf.Deg2Rad; float radius=i%2==0?0.65f:0.30f; vertices[i+1]=new Vector3(Mathf.Cos(angle)*radius,Mathf.Sin(angle)*radius,-0.16f); vertices[i+12]=new Vector3(Mathf.Cos(angle)*radius,Mathf.Sin(angle)*radius,0.16f); } var triangles=new System.Collections.Generic.List(); for(int i=0;i<10;i++) { int a=i+1,b=(i+1)%10+1; triangles.AddRange(new[]{0,b,a,11,a+11,b+11,a,b,b+11,a,b+11,a+11}); } var mesh=new Mesh{name="Extruded Five Point Star",vertices=vertices,triangles=triangles.ToArray()}; mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } private static void CreateUI(ScoreCounter counter) { var canvas=new GameObject("HUD",typeof(Canvas),typeof(CanvasScaler)); canvas.GetComponent().renderMode=RenderMode.ScreenSpaceOverlay; var scaler=canvas.GetComponent(); scaler.uiScaleMode=CanvasScaler.ScaleMode.ScaleWithScreenSize; scaler.referenceResolution=new Vector2(1600,900); scaler.matchWidthOrHeight=0.5f; var panel=new GameObject("Score Panel",typeof(RectTransform),typeof(Image)); panel.transform.SetParent(canvas.transform,false); var rect=panel.GetComponent(); rect.anchorMin=rect.anchorMax=new Vector2(0,1); rect.pivot=new Vector2(0,1); rect.anchoredPosition=new Vector2(32,-32); rect.sizeDelta=new Vector2(360,132); panel.GetComponent().color=new Color(0.035f,0.07f,0.12f,0.96f); TextLabel("Chapter",panel.transform,"CLASS 03 / STAR ROLL",new Vector2(22,-16),new Vector2(320,24),16,new Color(0.25f,0.85f,0.95f)); var score=TextLabel("Score",panel.transform,"STARS 00 / 08",new Vector2(20,-46),new Vector2(320,52),36,Color.white); var status=TextLabel("Status",panel.transform,"ROLL. EXPLORE. COLLECT.",new Vector2(22,-103),new Vector2(320,22),15,new Color(1,0.75f,0.25f)); var help=TextLabel("Controls",canvas.transform,"W A S D / ARROW KEYS • COLLECT ALL 8 STARS",Vector2.zero,new Vector2(900,44),20,new Color(0.68f,0.80f,0.9f)); var helpRect=help.rectTransform; helpRect.anchorMin=helpRect.anchorMax=new Vector2(0.5f,0); helpRect.pivot=new Vector2(0.5f,0); helpRect.anchoredPosition=new Vector2(0,22); help.alignment=TextAnchor.MiddleCenter; canvas.AddComponent().Initialize(counter,score,status); } private static Text TextLabel(string name,Transform parent,string content,Vector2 position,Vector2 size,int fontSize,Color color) { var go=new GameObject(name,typeof(RectTransform),typeof(Text)); go.transform.SetParent(parent,false); var text=go.GetComponent(); text.font=Resources.GetBuiltinResource("LegacyRuntime.ttf"); text.text=content; text.fontSize=fontSize; text.color=color; text.raycastTarget=false; text.fontStyle=FontStyle.Bold; text.alignment=TextAnchor.MiddleLeft; var rect=text.rectTransform; rect.anchorMin=rect.anchorMax=new Vector2(0,1); rect.pivot=new Vector2(0,1); rect.anchoredPosition=position; rect.sizeDelta=size; return text; } } }