float2 center = 0.5;
float2 pos = i.uv.xy-center;
float2 dir = ((atan2(pos.x,-pos.y)*2/3.14)/4+0.75)%1;
p1,p2,r,center를 이용하여 p3를 구하는 공식 챗GPT가 짜줬다
float2 FindIntersection(float2 p1, float2 p2, float R, float2 center)
{
float2 dir = normalize(p2 - p1);
float2 diff = p1 - center;
// 원의 중심에서 선까지의 거리 d 계산
float d = abs(diff.x * dir.y - diff.y * dir.x);
// 만약 d가 R보다 크면 교점이 없음
if (d >= R)
return float2(0, 0);
// L 계산: L = sqrt(R^2 - d^2)
float L = sqrt(R * R - d * d);
// 원의 중심에서 교점까지의 거리 h 계산: h = sqrt(R^2 - L^2)
float h = sqrt(R * R - L * L);
float2 midpoint = p1 + dot(center - p1, dir) * dir; // 선 위의 원의 중심에 수직인 점
// 두 교점은 midpoint에서 ±L만큼 dir 방향으로 떨어져 있음
float2 intersection1 = midpoint + L * dir;
float2 intersection2 = midpoint - L * dir;
// 이 예제에서는 두 교점 중 하나만 반환합니다.
// 필요에 따라 두 교점 중 원하는 교점을 선택하여 반환하면 됩니다.
return intersection1;
}
/// <summary>
/// 이름을 GUI에 표시함
/// </summary>
/// <param name="wordDictionary">이름</param>
/// <param name="wid">가로크기</param>
/// <param name="hei">세로크기</param>
void DrawWordDictionary(string Name, int wid, int hei)
{
}
널은 안됨
class NotNullContainer<T> where T : notnull
{
}
new한정자
new가 붙은거만 넣을수 있음
public static T GetClass<T>(string text) where T : new()
{
if(text=="ok")
{
var targetClass = new T();
return targetClass;
}
else
{
return null;
}
}
is 연산자
어디서 온건지 알 수 없는 obj등을 처리할 때 쓰인다
if ((objData is Dictionary<string, object>)==false)
{
Debug.LogError("objData는 Dictionary<string, object>가 아닙니다");
return;
}
//아래와 동일함
if ((objData.GetType() == typeof(Dictionary<string, object>))==false)
{
Debug.LogError("objData는 Dictionary<string, object>가 아닙니다");
return;
}
as 연산자
캐스팅 연산자인데 좀 복잡함 아래 참고
서로 동일한 코드를 나타내고 있다
string text= obj as string;
//위아래 동일함
string text;
if (obj is string)
{
text = (string)obj;
}
else
{
text = null;
}
if else 간소화 (남용은 금지, 복수로 쓰게될경우 가독성이 개판된다)
bool A;
if (B)
{
A = C;
}
else
{
A = D;
}
//위아래 동일
bool A=B?C:D;
null체크 간소화
if (A != null)
{
if (B != null)
{
return C;
}
}
return null;
//위아래 동일
return A?.B?.C;
?[]연산 에러발생시 null반환
다만 인덱스 오류는 캐치가 안 된다
string text = null;
if (array!=null)
{
text = array[1];
}
//위아래 동일
var text = array?[1];
아래건 동일분기 처리를 좀 알아봐야 겠다
switch 간소화 (유니티 안됨, 비주얼스튜디오 2019 자동화 안됨)
switch(condition)
{
case 1:
A=B;
break;
case 2:
A=C;
break;
default:
A=D;
break;
}
//위아래 동일
condition switch
{
1 => A=B;
2 => A=C;
_ => A=D;
}
튜플
void Start()
{
var (min, max) = FindMinMax(1, 2, 3, 4);
Debug.Log("min: " + min);
Debug.Log("max: " + max);
}
(int min, int max) FindMinMax(params int[] input)
{
var min = Mathf.Min(input);
var max = Mathf.Max(input);
return (min, max);
}
여러개 리턴할때 사용
유니티도 된다
??연산
null처리할때 좋음
아래 네개 모두 동일한 코드이다
string a = null;
Debug.Log(a ?? "ABC"); //ABC반환
a = "a";
Debug.Log(a ?? "BCD"); //a반환
//위아래 동일
string a = null;
Debug.Log(a is string ? a:"ABC"); //ABC반환
a = "a";
Debug.Log(a is string ? a:"BCD"); //a반환
//위아래 동일
string a = null;
Debug.Log(a==null ? "ABC":a); //ABC반환
a = "a";
Debug.Log(a==null ? "BCD":a); //a반환
//위아래 동일
string a = null;
if(a!=null)
{
Debug.Log(a);
}
else
{
Debug.Log("ABC");
}
a = "a";
if(a!=null)
{
Debug.Log(a);
}
else
{
Debug.Log("BCD");
}
인덱스 체크 간소화
근데 어차피 쓰려면 null체크 추가로 들어가서 그렇게 효율적이진 않음 오히려 협업할때는 누구나 알기쉬운 위쪽이 나을수도 있겠다
if((index >= 0)&&(index < array.Length))
{
var item = array[index];
//작동할 코드
}
//위아래 동일
var item = array.ElementAtOrDefault(index);
if(item != null)
{
//작동할 코드
}
로컬함수
외부로 표출될 필요가 없는 함수 작성시 매우매우 유용하다.
특히 최장점은 같은 이름의 함수를 여러군데서 쓸수있다는 점
함수를 사용할때마다 생성하는 방식인지
성능을 절약하기 위해 static을 붙이라는 문구가 뜨는데 유니티 2019에서는 미지원이다
void SampleFunction()
{
Debug.Log(LocalFunction("test")); //long text
Debug.Log(LocalFunction("te")); //short text
Debug.Log(LocalFunction("")); //short text
string LocalFunction(string text)
{
if(text.Length>2)
{
return "long text";
}
return "short text";
}
}
모든 스크립트에 using을 적용
몰론 함부로 쓰면 ㅈ된다
global using System;
함수내부 조건식 Predicate
public static class Array
{
//Array.FindIndexAll(arr,x=>x>1);
//혹은 arr.FindIndexAll(x=>x>1);
public static int[] FindIndexAll<T>(this T[] array, System.Predicate<T> match)
{
var indexList = new List<int>();
for (int i = 0; i < array.Length; i++)
{
if (match.Invoke(array[i]))
{
indexList.Add(i);
}
}
return indexList.ToArray();
}
//2개인자
//Array.FindAll(arr,x=>x.Item1>x.Item2)
public static int[] FindAll<T>(T[] array, System.Predicate<(T previous,T target)> match)
{
var indexList = new List<int>();
for (int i = 0; i < array.Length; i++)
{
if (match.Invoke((array[i-1],array[i])))
{
indexList.Add(i);
}
}
return indexList.ToArray();
}
}
var methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
Debug.Log(methodName);
필드명들
string[] GetClassFieldNames<T>(T targetClass)
{
var fields = typeof(T).GetFields();
return System.Array.ConvertAll(fields, x => x.Name);
}
필드명들2
string[] GetClassFieldNames<T>(T targetClass)
{
var fields = typeof(T).GetFields();
return Array.ConvertAll(fields, x => $"{x.Name} - type: {x.FieldType}");
}
특정클래스의 필드명과 함수명에 접근
var methods = typeof(Class).GetMethods();
foreach (var method in methods)
{
EditorGUILayout.LabelField(method.Name);
}
var fields = typeof(Class).GetFields();
foreach (var field in fields)
{
EditorGUILayout.LabelField(field.Name);
}
클래스할당과 연동
구조체에는 씨알도 안 먹히니 헛고생 하지 말자
public class SampleClass
{
string numStr = "0.5";
int num = 1;
float value = 1.5f;
public SampleClass() { }
public SampleClass (string stringData)
{
var fields = typeof(SampleClass).GetFields();
var targetClass = this;
for (int i = 0; i < fields.Length; i++)
{
if (fields[i].FieldType == typeof(string))
{
fields[i].SetValue(targetClass, stringData);
}
else if (fields[i].FieldType == typeof(bool))
{
fields[i].SetValue(targetClass, bool.Parse(stringData));
}
else if (fields[i].FieldType == typeof(char))
{
fields[i].SetValue(targetClass, char.Parse(stringData));
}
else if (fields[i].FieldType == typeof(int))
{
fields[i].SetValue(targetClass, int.Parse(stringData));
}
else if (fields[i].FieldType == typeof(uint))
{
fields[i].SetValue(targetClass, uint.Parse(stringData));
}
else if (fields[i].FieldType == typeof(long))
{
fields[i].SetValue(targetClass, long.Parse(stringData));
}
else if (fields[i].FieldType == typeof(ulong))
{
fields[i].SetValue(targetClass, ulong.Parse(stringData));
}
else if (fields[i].FieldType == typeof(float))
{
fields[i].SetValue(targetClass, float.Parse(stringData));
}
else if (fields[i].FieldType == typeof(double))
{
fields[i].SetValue(targetClass, double.Parse(stringData));
}
else
{
fields[i].SetValue(targetClass, stringData);
}
}
}
public override string ToString()
{
var fields = GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
return string.Join("\n",System.Array.ConvertAll(fields, x => $"{x.Name}: {x.GetValue(x)}" ));
}
/*
public override string ToString()
{
var fields = typeof(SampleClass).GetFields();
string returnString = "";
for (int i = 0; i < fields.Length; i++)
{
if (i>0)
{
returnString += "\n";
}
returnString += $"{fields[i].Name}:{fields[i].GetValue(this)}";
}
return returnString;
}
*/
public static implicit operator string(SampleClass _this) //암시적 형변환, 명시적은 explicit
{
return _this.ToString();
}
}
public class MainClass : MonoBehaviour
{
void Start()
{
Debug.Log("new SampleClass()\n" + new SampleClass());
Debug.Log("new SampleClass(\"123\")\n" + new SampleClass("123"));
}
}
생성자 자동 할당
public class SampleClass
{
public int number;
public string name;
public SampleClass()//생성자(자동)
{
var targetClass = new SampleClass(123, "Name");
//this.number = targetClass.number;
//this.name = targetClass.name;
//이렇게 안쳐도 됨
var fields = GetType().GetFields();
foreach (var field in fields)
{
field.SetValue(this, field.GetValue(targetClass));
}
}
public SampleClass(int number, string name)//생성자
{
this.number = number;
this.name = name;
}
}
클래스 깊은복사, 간단한 형식만 된다. 다중 클래스등은 불가 (문자열도 안되는듯?)
public class SampleClass
{
public int number;
public string name;
public SampleClass Clone()
{
var targetClass = new SampleClass();
var fields = GetType().GetFields();
foreach (var field in fields)
{
field.SetValue(this, field.GetValue(targetClass));
}
return targetClass;
}
}
using System.Linq;
//오브젝트 리스트를 float 배열로 변환
var objList=new List<object>();
float[] floatArray=objList.Cast<float>().ToArray();
//오브젝트 배열을 string 배열로 변환
object[] objArray = new object[1];
string[] stringArray = objArray.Cast<string>().ToArray();
문자열을 FieldType으로 변환
var text = "105.0";
var converter = TypeDescriptor.GetConverter(field.FieldType);
var data = converter.ConvertFrom(text);
field.SetValue(tempClass, data);
string[] ArrayToFieldNames(object containingClassWithArray, object[] array)
{
var fields = containingClassWithArray.GetType().GetFields();
var fieldNames = System.Array.ConvertAll(array, x => fields.First(field => field.GetValue(containingClassWithArray) == x).Name);
return fieldNames;
}
/*
예시
public class Coordinator : MonoBehaviour
{
public BodyPart head;
public BodyPart top;
public BodyPart bottom;
public BodyPart gloves;
public BodyPart shoes;
public BodyPart hair;
public BodyPart eye;
public BodyPart skin;
public BodyPart[] bodyParts
{
get
{
return new BodyPart[] { head, top, bottom, gloves, shoes, hair, eye, skin };
}
}
}
이렇게 있을때
var bodyPartNames = ArrayToFieldNames(coordinator, coordinator.bodyParts);
Debug.Log(string.Join(",",bodyPartNames));
하면 head,top,bottom,gloves,shoes,hair,eye,skin로 나온다
*/
v2
v1과 구조상으로는 동일한데 클래스내부로 넣어버렸다.
이게 더 편함
string[] ArrayToFieldNames(object[] array)
{
var fields = GetType().GetFields();
var fieldNames = System.Array.ConvertAll(array, x => fields.First(field => field.GetValue(this) == x).Name);
return fieldNames;
}
/*
//샘플
public BodyPart head;
public BodyPart top;
public BodyPart bottom;
public BodyPart gloves;
public BodyPart shoes;
public BodyPart hair;
public BodyPart eye;
public BodyPart skin;
public BodyPart[] bodyParts
{
get
{
return new BodyPart[] { head, top, bottom, gloves, shoes, hair, eye, skin };
}
}
public string[] bodyPartNames
{
get
{
return ArrayToFieldNames(bodyParts);
}
}
*/
근데 이방법 쓸바엔 그냥 클래스 안에 name값 넣어서 써라
var value = field.GetValue(component);
if (value==null)
{
continue;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(PayloadRace))]
public class PayloadRaceEditor : Editor
{
private Vector3 position = Vector3.zero;
void OnSceneGUI()
{
var payloadRace = (PayloadRace)target;
var tracksParent = payloadRace.tracksParent;
if (tracksParent)
{
var childs = new List<Transform>();
for (int i = 0; i < tracksParent.childCount; i++)
{
childs.Add(tracksParent.GetChild(i));
}
foreach (var child in childs)
{
if (Vector3.Distance(payloadRace.transform.position, child.position) < 1)
{
//너무 가까우면 최상위 조작이 불가하므로
continue;
}
var newPosition = UnityEditor.Handles.PositionHandle(child.position, child.rotation);
if (newPosition != child.position)
{
UnityEditor.Undo.RecordObject(child, "Move Transform");
child.position = newPosition;
}
}
}
}
}
#endif
public class PayloadRace : MonoBehaviour
{
public int team = 0;
public GameObject payload;
public Transform tracksParent;
#if UNITY_EDITOR_WIN
void OnDrawGizmos()
{
var color = Color.red;
if (team == 0)
{
color = Color.blue;
}
Gizmos.color = color;
{
Gizmos.DrawWireSphere(transform.position,5);
}
Gizmos.color = Color.white;
if (tracksParent)
{
var childs = new List<Transform>();
for (int i = 0; i < tracksParent.childCount; i++)
{
childs.Add(tracksParent.GetChild(i));
}
foreach (var child in childs)
{
Gizmos.DrawSphere(child.position,0.1f);
if (Vector3.Distance(transform.position, child.position)<1)
{
continue;
}
}
for (int i = 0; i < childs.Count-1; i++)
{
Gizmos.DrawLine(childs[i].position, childs[i + 1].position);
}
}
}
#endif
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}