라이브강의
문자열처리
메서드
Trim() 앞뒤 공백제거 "제이 ".Trim() -> "제이"
Substring() 문자열자르기 "홍길동".Substring(0,2) -> "홍길"
Length 글자수확인
Split() 구분자 기준 나누기
startsWith()/EndsWith() 접두/접미확인
Contains() 포함여부검사 "공격 슬라임".Contains("공격") 파싱해서 공격슬라임을 공격메서드로 변경가능하는등
IndexOf()/LastIndexOf() 문자위치찾기
Replayce()/Remove()
$"{ }" 문자열보간
ToLower()/ToUpper() 전체문자 대소문자로 바꾸는 메서드
-------
예제:
string name = " 제이 "
name = name.Trim(); // 공백제거
if(name.Length >6) // 글자수 제한 체크 , 너무 길경우
name = name.Substring(0,6) + " ... " ; // 글자를 6까지만 자르고 ...로표현
예제2:
저장파일명 만들기 공백제거가능
예제3:
Replace() Remove() -> 배열로 정의한 비속어 를
1. 별표로 대체하거나 할수있음
new string ( '*', bad.Length)
-----
문자열보간 응용방법
$"{"이름", -10}
숫자로 패딩을 넣을수있음 -10은 오른쪽으로 10칸 공백
+10이면 왼쪽으로 10칸 공백 (실제로는 이름이라는 string 포함 10칸이니, 8칸공백임)
공백을지정하고, 그 문자열이 공백을 넘어간다고 짤리지는않음
------
?? -> null 대비 기본값 출력
(armor ?? "없음").PadRight()
없으면 "없음"출력 있다면 오른쪽줄맞춤
PadRight() -> 오른쪽줄맞춤
PadRight(30, '='); 하면 30개의 =가 오른쪽으로 추가됌.
패딩이 실제 공백은 같겠지만 폰트폭이 달라서 정렬이 안맞을수있음
고정폭을 사용하면 패딩을 사용해도 폭이 달라지지않을수있음
고정폭을 사용하면 내 pc에서 사용해서 보면 괜찮아보임
근데 웹에서 구현하면 사용자컴퓨터기반으로 폰트를 보이기때문에
폰트가 여전히 이상하게 정렬안될수있음
해결방법:
콘솔출력을 풍부하는 라이브러리 사용
Spectre.Console 등
표를 사용가능하다.
---
exp + 30 파싱
배열과 Split ('+') 을 써보자
각각 배열형태의 변수에 넣으면
분리가능하다
StartWith() -> 스트링체크
Substring() _> 접두사 제거 해서 뒤의 내용을 교체
StartWith()
Substring()
Contains
ToLower()/ToUpper()등은 많이 사용됩니다.
문자열을 체크하고 제한하고, 포함여부를확인하고
키입력값을받을때 대문자소문자 조건을체크하는등
각종 조건을 통해 받을때 많이쓰임
저장파일명은 사용할수없는 문자들이있는데 이것들을 제거하기위한 목적으로
많이씀
문자열리터치도 많이쓰임(비속어필터)
------------
깃허브 특강
readme file
프로젝트 설명 가능한 공간이므로 좋다.
ignore를 시작해주면좋음
visualstudio를 체크해주면
불필요한 파일들이 올라갈때 git ignore로 캐쉬파일들은 안올라가게가능함.
Unity는 존재함 따로 프로젝트때 체크하기.
public이어도 일반적으로 포크의 기능으로 외부에서 함부러 commit막기
setting - collaborators 탭으로 들어가면
내 팀원들의 깃헙 닉네임, 이메일을 검색가능하다.
이 레파지토리에 초대가 가능하다.
목록에 초대보낸 리스트가 뜬다.
pending invite은 아직 초대만보낸상태니까 체크가능
초대받은사람은 이메일로 드가보면 초대이메일이있어 입장가능
초대받은사람들도 커밋, 브랜치만들기등 다 가능해진다.
깃허브데스크탑에 땡겨오려면
code 눌러보면 다양하게 clone기능을 제공함
ssh는 보안작업이 요구될때 택하기도함
HTTPS로도 충분한 수준이라면 클립보드로 주소 복사하여서
깃헙데스크탑에서 clone으로 url 통해 복사가능하다.
깃헙데스크탑에서
commit 하면 로컬환경에만 저장되어있음
push origin 까지 해야 깃헙 서버에 올라감
충돌을 방지하기위해 branch를 만들어주자.
Current branch에 new branch로 branch를 만들어라.
main 코드를 그대로 복사해서 가져오는 branch이다.
publish branch 까지해서 서버에 올려주자.
login branch라고 만들었다쳐보자
login branch 환경에서
코드를 수정하면
changed에 수정한 파일이 뜨고 오른쪽에는 수정한 내용이뜬다.
제목에 feat : login 텍스트 추가
내용에 - 텍스트를 수정했습니다.
라고 하고 커밋해보자.
우선 로컬에만 반영된상태.
push origin을 하면 깃서버 login branch에 올라간다.
이제 main과 login branch 내용이 달라졌다.
rename으로 branch 이름을 바꿀수있는데, push같은걸안하면 딱히 서버에 먼저반영되진않는다
파일을 올리면 따라서 같이반영된다.
pr(pull request)
login branch에서 만든게 팀내에서 통과가 되었다면
main branch에 올릴수있게되는데,
웹 서버 페이지에
pull requests 탭에 들어가서
login branch로 들어가면
합치겠다고 할수있음
아래에 커밋 로그를 확인가능하다
create pull request를 누르면
충돌검사를한다
충돌이 base branch와 없다고 체크되면
보통 현업에서 주니어로가면 바로 못올리고 Main 가기전에
Main develope branch 같은 테스트용 브랜치가 존재하고 여기다 올려서 체크하고
댓글로 코드리뷰한다음에 수정까지 통과되면..
Merge pull request를 누른다.
Confirm Merge 까지하면
login branch 내용이
Main에 취합된다.
closed에 어떤 코드를 올렸고, 어떤 코드가 리뷰를 어떻게받았는지 기록이 다남는다.
---
꿀팁 visual stuido
ctrl a
ctrl k,f 누르면 줄정리해줌
--
충돌스 관리
main base로 branch를 하나더 만들어보자.
main-base-test1 이라는 branch를 만들었다.
다시한번
main을 베이스로
main-base-test2이라는 branch를 또 만들었다.
main-base-test1일때
코드에 나는 테스트1입니다라고 9번째 줄을 수정했다고치자.
fix : 누군가 버그일으켜 고침
하여 커밋하고 push origin까지했다.
main에 병합으로 합쳤다.
그다음에
main-base-test2 branch는
코드 9번째줄에 나는 테스트2입니다 라고 수정했다고 치자.
fix : main base1은 틀렸다
라고 커밋하고 push origin까지 올렸따.
PR을 하려하면 comflict 난다고 얘기해준다.
이부분을
수동으로 풀어줘야한다.
근본적인원인은 같은파일의 같은 줄을 수정했기때문임.
main2에서 main을 덮어씌우면 안되니까.. 체크해보라고 알려주는상태로 된다.
코드가 한줄만 수정되었다면 합의봐서 고쳐주면되니까 큰문제는아니다.
근데 줄이 길어지고 기능이 혼재되어있을때
여러군데에서 충돌이 나기 시작한다면? 뭐가 맞는지 틀린지도 알수없어짐.
이럴경우 보통 하나는 포기한다.
수정했던 코드중에 버리는것은 보통 코어한부분은 어딘가 로컬에 남겨둔다.
그리고 협의를 본 branch를 전체에 push 한다.
main, main test1, main test2 셋다 최신화된 상태에서, 새로 수정한다면 문제가 안생긴다.
작업하기전에 꼭 main 껄로 땡겨오는걸로 최신화 하자.
커밋은 미리하되, fetch origin , pull origin 을 하고 내껄 올리는 방식으로 많이해보자.
별도의 모듈을 작업할때는 겹칠일이 거의없다만
GameManager같은 공용 스크립트는 겹칠일이 생긴다. 이때는 룰을 세워서
충돌을 최소화해야한다.
작업중인 사람 확인하고,
있다면 작업자의 작업완료후에 들어가면됌
아니라면, 어디 몇번줄 작업들어간다 공지하고 들어가면된다.
실제현업에서도 작업중인 파일을 락을건다거나.. 하는방식으로 막을수있었다.
-----
gitignore를 더해보는방법없나?
깃헙 웹사이트에 gitignore를 잔뜩모아둔 곳이있다.
내파일의 gitignore 파일에 어떤걸 제할지 적어둔 파일이있음.
*. 어쩌고가 어쩌고파일을 제외할것이다란뜻
웹내용을 그대로 코드를 복사해서 아래에 적어넣어도되고.
깃헙데스크탑에선 단일파일에대해서, 오른쪽버튼 누르면 gitignore로 단일파일 제외도 가능하다.
내입맛에 맞게 다 추가가능하다.
------
브랜치 개인/기능단위로 나눠서 작업한다
main -완성,배포전용
develop - 모든기능 통합, 테스트단계
feature/login - 각종 기능, 개인 단위 작업공간
fix/bug-crash - 버그 수정 전용 브랜치
깔끔하게 잘완성된 기능에대한 branch는 main에 통합후 보통 삭제하는 룰을 세운다.
--------
pr 기반작업으로 협업하기.
Unity 깃허브
ignore 설정으로 unity를 해두면
새로만든 unity 파일내용은 실제로 1기가 넘지만
깃허브레포지토리 폴더에 연결해서 첫 커밋할때 1기가를 전부 집어넣지않고
필요한 파일들만(ignore로 제외안한) 커밋된다.
엔진 실행도 잘된다.
meta파일이 숨겨져있는데, 파일의 정보들이며 무시해도된다.
게임매니저 스크립트를 유니티에서 만들고
게임매니저 브랜치를 파서 만들어 올려보자.
main branch에는 아직아무것도없는상태
똑같이 pr을 해서 merge request하면 똑같이 main에서도 통합된다.
-----------
팀플과제
제너릭 메서드
public void ChangeScene<T>() where T : Scene
메서드 이름옆 <T>() 가 들어감
where T : Scene
제약조건
T는 반드시 Scene을 상속받은 타입이어야한다라는 의미
이렇게 쓰면, sceneController.ChangeScene<TemplateScene>()처럼
Scene을 상속받은 TemplateScene을 직접 타입 인자로 넘길 수 있다.
if ( item is T changedScene)
패턴매칭
"만약 item 이 T 타입(TemplateScene등)이면, changedScene 변수에 그 객체를 담아라.
이 changedScene는 바로 이후 바디안에서 사용가능
foreach (var item in sceneContainer)
{
if (item is T changedScene)
{
currentScene?.End();
currentScene = changedScene;
Console.Clear();
changedScene.Start();
return;
}
}
currentScene?.End()
null 조건부 연산자 ?.
currentScene이 null이 아니면 End() 호출하라는 뜻
최초씬 전환전에는 currentScene이 null이므로 호출을 건너띈다.
return;
일 끝나고 foreach 루프, ChangeScene 메서드를 즉시 종료한다.
-----
제너릭이란 무엇인가?
타입을 일반화해서 여러 자료형에 재사용 가능한 코드를 작성하는 기법이다.
c#에서는 제너릭 클래스나 제너릭 메서드 형태로 제공한다
List<T> 나 void Swap<T> ... 등
이렇게하면 코드 중복을 줄이면서도 타입안정성(컴파일 타임에 오류를 잡음)을 획보가능하다.
public void MeMethod<T>(T value)
{
Console.WriteLine(value.Tostring());
}
호출시
MyMethod<int>(123); // T 는 int 타입이 되고, 콘솔창에 "123" 을 출력한다.
MyMethod<string>("hi"); // T는 string이 되고 "hi"를 출력한다.
타입 매개변수 vs 타입 인자
타입매개변수는 메서드,클래스 정의시 쓰는이름 void Swap<T>(T a, T b) 에서 T
타입 인자는 호출할때 실제로 지정하는 타입 Swap<int>(x,y)에서 int
제약조건(constraints)라는게있다.
제너릭은 아무타입이든 받을 수 있는데, 메서드 내부에서 특정 기능(인터페이스 메서드,생성자 등)
을 사용하려면 제약조건을 걸어야한다.
문법: where T : 제약
where T : SomeBaseClass -> SomebaseClass를 상속한 타입만
where T : IsomeInterface -> 해당 인터페이스를 구현한 타입만
where T : new() -> 매개변수 없는 생성자가 있는 타입만
public T createInstance<T>() where T : new()
{
return new T();
}
// new() 제약을 걸어서 메서드 내부에서 new T()로 인스턴스 매개변수없이 생성가능.
// 보는 바와같이 void대신 T가 들어가서 T 타입을 반환하는 메서드다.
여기선 T타입 객체하나를 생성하는것을 반환한다.
실용예제
ChangeScene<T>()
public void ChangeScene <T> () where T : Scene
여기서 <T>는 T라는 타입자리를 나중에 받겠다라는 뜻.
where T : Scene 은 T는 반드시 Scene 클래스를 상속받은 타입이어야한다.
호출부에서
sceneController.Changescene<TemplateScene>();
이 순간 T가 Templatescene으로 바뀌어서
내부 if ( item is T changed Scene) 은
if ( item is TemplateScene changedScene)와 동일하게 동작
ChangeScene<int>() 같은 말도안되는 호출은 컴파일단계에서 막아주고
BattleScene등 여러 씬이 생겨도 메서드하나로 처리가능
어떤 타입의 모음을 다루는 컬랙션같은 것들을 다룰때,
여러 자료형을 같은로직으로 처리할때
타입간 캐스팅없이도 제대로된 타입인지 체크하고싶을때 쓰면좋다
------
패턴매칭
패턴매칭은 값이 어떤 형태(패턴)인지 검사하고 동시에 그 값을 새로운 변수에 담아
사용할 수 있게 해주는 c#의 강력한 기능이다.
예전에는 as나 (T)obj 같은 캐스팅과 if(obj !=null) 검사를 따로해야했다.
이제는 깔끔하게 한줄로 가능하다.
우선 is, as 연산자에대해서 한번더 알아보자
is 연산자는 객체가 특정 타입인지 검사해서 bool 값을 반환한다.
object obj = "hello";
if (obj is string)
console.WriteLine("문자열이네요!");
전통스타일 타입검사후 캐스팅
if( obj is string) // 먼저 타입검사
{
string s = (string)obj; // 그 다음에 강제 캐스팅
Console.WriteLine(s.Length);
}
변수바인딩(c# 7.0 이후)
if ( obj is string s) // 타입 검사와 동시에 string s에 캐스팅하여 담아줌
{
Console.WriteLine(s.Length);
}
as 연산자
객체를 안전하게 특정 타입으로 변환(캐스트)
변환할수 없으면 null 반환
예외(InvalidCastException)를 던지지않는다.
object obj = "world";
strign s = obj as string; // obj가 string으로 변환 가능하면 s에 담기, 아니면 s는 null
if( s != null)
Console.WriteLine(s.ToUpper());
else
Console.WriteLine("string으로 변환 불가");
as vs (타입)obj 캐스팅
(string)obj 는 변환 불가시 예외 발생
obj as string은 변환 불가시 null -> 안전하게 후속 처리가능
그래서 다시 돌아가 패턴매칭에서
is 연산자를 확장한 형태로 단순타입검사를 넘어 값,프로퍼티 비교, 조건조합등을 한번에 처리할 수 있따.
타입패턴
object obj = 123;
if( obj is int i) // obj 가 int면 i에 값 대입해
Console.WriteLine(i + 10); // 133
상수패턴
int x = 0;
if( x is 0)
Console.WriteLine("zero!")
논리패턴
object obj = -5;
if (obj is int n and > 0)
Console.WriteLIne(n);
else if ( obj is int m and < 0)
Console.WriteLine($"음수 : {n}");
// 음수 : -5 라고 출력
프로퍼티 패턴
class Person { public string Name; public int Age;}
object obj = new Person { Nam = "Lee", Age = 25};
if ( obj is Person { Age: >= 20 and <= 30} p)
Console.writeLine($"{p.Name} is in there 20s"); // Lee is in there 20s 라고 출력
스위치식
object o = "hi";
string result = o switch
{
int n => $"정수 {n}",
string s => $"문자열 \"{s}\"",
null => "null 값",
_ => "기타"
};
Console.WriteLine(result); // 문자열 "hi"
---------
값형 복사와
참조형 복사에서 헷갈리는 부분 짚어보기
객체 생성의 헷갈리는 부분 짚어보기
처음에 헷갈리는 지점은 이부분이었다.
클래스 인스턴스 변수(참조형)를 복사하면 → “참조”(주소)가 복사된다.
값형 필드·프로퍼티(int, float, struct 등)를 복사하면 → “값” 그 자체가 복사된다.
여기까지는 이해했었다.
그런데 “클래스 인스턴스에서 . 연산자로 필드나 프로퍼티를 꺼내오면,
그 결과가 값형이면 값이 복사된다”는 사실이 이상하게 느껴졌었다.
왜 obj.Field가 값형이면 값이 복사될까?
클래스 인스턴스(참조형) 안에는
값형 필드(예: int HP;)
참조형 필드(예: StatData Stat;)
이렇게 멤버들이 들어 있습니다.
obj.Field를 쓰면,
obj가 가리키는 객체(힙 메모리)를 찾아가서
그 안에 저장된 필드의 메모리 위치에서
값형이라면 “값”(예: 42)을 읽어서 반환
참조형이라면 “참조”(다른 객체의 주소, 예: 0x012345)를 읽어서 반환
즉, . 연산자는 “객체 내부” 어딘가에 저장된 데이터를 꺼내오는 것이고,
꺼내오는 대상(Field의 타입)이 값형인지 참조형인지에 따라
값형 → “값 그 자체”
참조형 → “참조(주소)”
가 돌아오는 겁니다.
.(닷)을 통해서 접근하는것은 참조하는것이아니라 말그대로 꺼내오는 뜻이었다.
그대상이 값형이면 값꺼내오는거고 참조형태라면 참조를 꺼내 이어오는것뿐이다.
/////연습예제////////
class StatData // 참조형
{
public int Attack; // 값형
public float Defense; // 값형
}
class Player // 참조형
{
public int Level; // 값형
public StatData Stat; // 참조형
}
Player p = new Player();
p.Level = 5;
p.Stat = new StatData { Attack = 10, Defense = 3.5f };
// 1) p.Level 은 값형 필드 → “값 5”를 복사해서 줌
int lvl = p.Level; // lvl == 5
lvl = 7;
Console.WriteLine(p.Level); // 여전히 5
// 2) p.Stat 은 참조형 필드 → StatData 인스턴스의 주소를 복사해서 줌
StatData s1 = p.Stat;
StatData s2 = p.Stat;
Console.WriteLine(object.ReferenceEquals(s1, s2)); // true
// 3) p.Stat.Attack 은 값형 필드 → “값”을 복사해서 줌
int atk = p.Stat.Attack; // atk == 10
atk = 15;
Console.WriteLine(p.Stat.Attack); // 여전히 10
//////오리지널 플레이어 데이터와 던전플레이어를 분리해보기//////
class DungeonPlayerData
{
private PlayerData dungeonPlayer;
private PlayerData originalPlayer;
private StatData dungeonStatData;
public PlayerData DungeonPlayer
{
get { return dungeonPlayer; }
private set { dungeonPlayer = value; }
}
public DungeonPlayerData(PlayerData player)
{
originalPlayer = player;
SetDungeonPlayer();
}
public void SetDungeonPlayer()
{
dungeonStatData = new StatData(
originalPlayer.Stat.Attack,
originalPlayer.Stat.MaxHealth,
originalPlayer.Stat.Defense,
originalPlayer.Stat.CurrentHealth
);
dungeonPlayer = new PlayerData(
originalPlayer.Name,
originalPlayer.ClassType,
originalPlayer.Level,
originalPlayer.Gold,
dungeonStatData
);
}
public void ApplyResult ()
{
originalPlayer.Name = dungeonPlayer.Name;
originalPlayer.ClassType = dungeonPlayer.ClassType;
originalPlayer.Level = dungeonPlayer.Level;
originalPlayer.Gold = dungeonPlayer.Gold;
originalPlayer.Stat = dungeonStatData;
dungeonPlayer = null;
dungeonStatData = null;
}
}
우선 어딘가에 Player 객체가 하나 생성되어있다는 전제하에,
의존성을 주입했다. originalPlayer라는 변수에 할당해 구분해줬다.
그다음
new 객체를 생성할때 같은 타입인 PlayerData 설계도를 써서
dungeonPlayer를 만들기는 했다.
그러니 PlayerData의 필드들에 접근해서 그 맴버들을 이용했다.
다만 완전히 다른 새로운 객체를 만들뿐더러
그러기위해 PlayerData의 생성자에 들어가는 맴버값 매개변수들을 똑같이
값형태로 불러와서 new 객체생성의 인자로 썼다.
이러면 어디까지나 .(닷)으로 originalPlayer 에 접근하여 값들을 꺼내온것이다
참조형이 아닌 값형태의 인자들을 가지고 new로 만든
dungeonPlayer은
originalPlayer와는 다른 또다른 객체 복사본이 된다.
StatData는 기존 PlayerData에도 맴버이긴하지만 자체로도 클래스고
맴버를 가지고있다. PlayerData는 생성자를 호출시에 이걸 참조형태로 가져온다.
원본 플레이어 객체도 StatData를 참조형태로 가지고있는것인데,
복사본에 넣어주려면,, 마찬가지로 StatData를 그냥 불러와서 연결시켜줘버리면
참조형태로 이어져버린다.
그러니 StatData까지 더 접근해서 그안의 맴버를 값형태로 가지고
new를 이용해 Dungeon 전용 Stat 객체를 생성해준다.
그리고 그 Dungeon 전용 복사된 Stat객체를 또다른 인자로
DungeonPlayer을 new 해주는것이다. (PlayerData 생성자 호출시에 StatData도 인자로 받고있다)
여기서 이제 헷갈렸던 다른 문제까지 해결됐다.
인스턴스 생성시에 매개변수를 잔뜩 적어주는 위의 모습을 볼수있는데
dungeonPlayer = new PlayerData(
originalPlayer.Name,
originalPlayer.ClassType,
originalPlayer.Level,
originalPlayer.Gold,
dungeonStatData
);
생성자의 받는 인자와 new할때 시그니처가 같아야하기때문이다.
호출자가 해당 클래스로 객체를 생성할때 생성자가 호출되며
값들을 초기화시켜줄텐데,, 그때 예를들어 사용자의 입력값들을 받기위해 생성자에
파라매터를 만들어놓았다치자.
new 구문은 "이타입의 생성자 중 이 타입/개수 매개변수를 받은 생성자를 호출하라는 뜻이기때문에
당연히 객체를 생성하는데 있어 적어놓아줘야하는 문법인것이다.
결론: 생성자안의 파라매터와 new 구문의 파라매터를 맞춰주자.
참고로 c#컴파일러는 생성자를 작성안하면 자동으로 파라미터없는 생성자를 만들어준다.
그런데,
하나라도 매개변수가 있는 생성자를 정의했다면 기본 생성자는 자동생성되지않는다.
또한 생성자 오버로드를 통해 같은 클래스 안에 여러 시그니처로 생성자를 둘수도있다.
public class PlayerData
{
// 1) 파라미터 없는 생성자
public PlayerData() { }
// 2) 주요 필드를 초기화하는 생성자
public PlayerData(string name, int level)
{
Name = name;
Level = level;
}
// 3) 모든 필드를 초기화하는 생성자
public PlayerData(string name, int level, StatData stat)
: this(name, level) // (2)번 생성자 재사용
{
Stat = stat;
}
}
이렇게 하면 원하는 방식으로 new PlayerData() 또는
new PlayerData("Alice", 1) 둘 다 쓸 수 있게된다.
또한 선택적 매개변수와 네임드 아규먼트라는 것도 가능해서
모든 조합의 오버로드를 정의하지않고도 유연하게 생성자를 설계할수도 있다.
public class MonsterData
{
public MonsterData(string type, int level = 1, float hp = 10f)
{
Type = type;
Level = level;
HP = hp;
}
}
// 호출 예시
new MonsterData("Goblin"); // level=1, hp=10
new MonsterData("Orc", level:5); // level=5, hp=10
new MonsterData("Dragon", hp:100f); // level=1, hp=100
여기서 매개변수 뒤에 = 기본값을 지정해서 생성자를 만들었기때문에
객체를 생성할때 완전히 파라매터의 종류를 다 쓰지않아도
기본값으로 지정한 나머지 값들은 자동으로 채워주기때문이다.
다만 기본값 지정이없다면
생성자를 오버로드해주거나 객체생성시 시그니처를 맞춰줘야만한다.
객체 초기화 구문도 존재한다.
public class Stats
{
public int Attack { get; set; }
public int Defense { get; set; }
}
// 생성자 없이...
var stat = new Stats { Attack = 5, Defense = 3 };
참고로 이럴려면
stats에 생성자에 매개변수가 없는 경우여야한다
초기화를 stats 생성자에서 파라매터를 만들어서 해버렸다면
오버로딩을 통해
빈 매개변수 생성자를 하나 더 만들면 해결된다.
명시적으로 생성자에 인자를 넘겨주려면
var stat = new Stats ( 5, 3)
이런식으로하면 빈생성자가 필요없을 수도있다.
'Unity 개발 공부' 카테고리의 다른 글
| [내배캠] 본캠 15일차. 상속의 원리, 객체지향 (1) | 2025.04.25 |
|---|---|
| [내배캠] 본캠 14일차. 데이타 클래스와 서브클래스, 배열과 리스트 (0) | 2025.04.24 |
| [내배캠] 본캠 12일차.스테이트 머신, 스테이트패턴, FSM, 제너릭 T, 디자인패턴종류 (0) | 2025.04.22 |
| [내배캠] 본캠 11일차.전역설정,싱글톤,의존성주입, 용어정리 (0) | 2025.04.21 |
| [내배캠] 본캠 10일차. 델리게이트, 클래스타입에 대한 복습 (0) | 2025.04.18 |