C# 복습하기
C# 복습 인트로
객체 지향

변수의 정리
변수

선언: 메모리공간 할당
해제: 자동
우리가 고려할 것: 빨간박스
stack메모리에 변수 저장 공간 생긴다.
정의: 자료형을 새로 정의
선언: 메모리공간 확보.
int num;->num이라는 메모리 공간을 예약
저장: num에 대입
해제: 언어 차원에서 지원.
콘솔 출력
void Start()
{
int num = 5;
int value;
value= num;
//1.
print(num);
//2.
Debug.Log(num);
//3. 유니티에서 안 뜬다.
Console.WriteLine(value);
}
함수의 정리
구조체의 메모리
구조체
정의도 한다.
public struct StructPos{//구조체 정의
public int x;//public: 외부에서 접근 가능.
public int y;
public int z;
public void Show(){
print(x+" "+y+" "+z);
}
}
생성자: 초기화를 쉽게 하기 위해 사용
public StructPos(int x,int y,int z){
this.x=x;this.y=y;this.z=z;
}
사용: 값이다-> 선언만 한다. (참조형변수x)
StructPos pos;//pos라는 구조체변수를 선언
pos.z=10;
print(pos.z);
pos.Show();
해제: 쓰고 나면 자동으로 해제된다.
구조체 생성자: 초기화를 쉽게 하려고. 필수아니다.
//생성자: 초기화를 쉽게하기 위함이다. 선언만 하고 써도 된다.
StructPos pos=new StructPos(1,2,3);
pos.Show();
pos.x = 5;//public 없으면 빨간줄
int x = pos.x;
int y= pos.y;
int z=pos.z;
print(x + " " + y + " " + z);
StructPos test;
test.x = 1;
print("걍 선언만 한 구조체 x: " + test.x);
클래스의 메모리
클래스
정의도 한다.

클래스: 힙에 생김.
실제 데이터: 힙에 저장된다.
참조변수: 참조하는 용도일 뿐이다.
메모리해제: 가비지 컬렉터 역할.
메모리 구조
참조 변수: 스택
실 데이터(객체): 힙
메인이 끝나면 스택 데이터 사라짐: 참조변수 사라짐
가비지컬렉터: 참조가 사라진 객체 해제
참조변수를 함수 안에서 선언하고 쓴다면?
다른 함수에서 못 쓴다.
참조변수를 클래스 내에서 선언하면?
클래스 내 다른 함수들에서 쓸 수 있다.
업데이트: 프레임마다 함수 호출.

참조를 잃어버린 객체->가비지 컬렉터가 해제한다.
클래스의 레퍼런스
객체지향1(객체지향의 기본적 의미)
객체 지향
객체를 중심으로 프로그래밍하는 사고방식
변수: 속성
함수: 기능 ==> 하나의 사물
객체의 틀을 만들어 놓고(클래스는 틀이다) 이것을 인스턴스로 만들어 사용.
인스턴스
붕어빵 틀->붕어빵
클래스->인스턴스(객체)
사용하는 클래스 형태
우리는 있는 클래스 잘 쓰는게 중요하다.

주황 박스 매우 복잡->강의에서 다루지 않는다.
캡슐화
객체의 외부에서는 기능만 사용, 내부 구조를 건드리지 않는다.

가져와서 쓰자. (캡슐화)
어떻게 쓰는지만 알면 된다.
GameObject클래스
유니티에서 물체를 관리하기 위해 사용하는 클래스


상속 받아 클래스 만듦(GameObject)
관리 위해 스크립트에서 만든 클래스
게임 오브젝트를 알고 사용해야 한다.
게임개발과 유니티
게임오브젝트
컴포넌트
게임오브젝트의 메모리
컴포넌트의 메모리
모노비헤이버의 name 변수

Object클래스: name변수 있다
내클래스: 상속받았다
->name변수 쓸 수 있다.
//Monobehaviour의 name변수: Object상속받아서 쓸 수 있다.
this.name = "잇힝";
//상위클래스 속성 볼 수 있다. name 있다.
//base.name;
/* Object a=new Object();
a.name= "a";*/
MonoBehaviour

//Monobehaviour의 name변수
this.name = "잇힝";

실행 시 바뀜
MonoBehaviour는 Object를 상속받는다.
Object클래스: name변수 있다
내클래스: 상속받았다
->name변수 쓸 수 있다.
Object클래스: name변수 있다
내클래스: 상속받았다
->name변수 쓸 수 있다.
Object클래스: name변수 있다
내클래스: 상속받았다
->name변수 쓸 수 있다.
Monobehaviour는 Object를 상속받는다.

Object: name변수 있다.
내 클래스: 쟤네 상속받았다
->name접근 가능하다.

name 변수는 Object 클래스에 있다.
상속받았다-> 접근가능하다.
클래스의 포함관계
GameObject와Monobehaviour
GameObject와 Monobehaviour


클래스는 역할이 분담되어 있다.
유니티 API->SetActive함수: GameObject의 메서드.
GameObject 클래스 안에 name과 SetActive()있다.
gameObject인스턴스를(변수를)통하여 게임오브젝트의 기능을 사용가능하게 함.

void Start()
{
this.gameObject.SetActive(false);
}
MonoBehaviour의 변수: gameObject
void Start()
{
this.gameObject.SetActive(false);
//스크립트를 AddComponent로 추가한다.
this.gameObject.AddComponent<AnotherScript>();
}

SetActive(false)-> 추가한 스크립트 내용 실행되지 않는다. 비활성화 되어있기 때문.
스크립트는 컴포넌트에 추가된게 맞음. 근데 실행은 안됨. //SetActive(false)
GameObject 의 name 변수
name 변수 정리
다른 오브젝트의 접근
API 문서 살펴보기
자기참조
GameObject 내부의 gameObject 변수
GameObject 내부의 gameObject 변수

오른쪽 예
: GameObject 의 gameObject: 자기참조 변수이다.
왜 있을까?
프로그램 시 가독성이나 일관성을 위한 것
왼쪽: 역 참조
오른쪽: 자기 참조
그냥 obj.name보다 obj.gameObject.name라고 하는게 가독성이 좋다
//자기자신 이름 변경
this.name = "큐브1";
this.gameObject.name = "큐브브1";
//다른 오브젝트 이름 변경
obj1.name = "큐브333";
obj1.gameObject.name = "큐브32323"; //자기참조

가독성을 위해서이다.
기본문법과 유니티
기본문법과 유니티 인트로
Static변수와 Static 함수
static

단 하나의 공간만을 가진다.


static: 전 객체가 공유한다.
static: 붕어빵 틀이다
붕어빵 틀은 붕어빵의 정보를 모른다.
따라서 멤버변수를 쓸 수 없다.
멤버변수: 각 객체의 멤버변수 정보 , 따라서 붕어빵 틀은 붕어빵이 어떻게 생길지 모름
public static int number;
// Start is called before the first frame update
void Start()
{
print(TestScript.number);//static멤버->바로 불러올 수 있다.
MyStaticFunc(10);
}
public static void MyStaticFunc(int a)
{
print(TestScript.number+a);//오류 없다.
}
static 메모리

Start 함수와 Update 함수
물체가 깜박거리는 예제
물체가 깜박거리는 예제
Time.deltaTime


1초 재는 로직

time.deltaTime더하자

오브젝트와 결합
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class 깜박임 : MonoBehaviour
{
bool flag = true;
float time = 0;//시간재는 로직
public GameObject obj1;
/* void Start()
{
//이 내용을 update로 옮겨보자.
bool flag=true;
for(int i = 0; i < 5; i++)
{
if (flag == true)
{
print("true");
flag= false;
}
else
{
print("false");
flag = true;
}
}
}
*/
void Update()
{
//shift+ tab: 정렬
//time += 0.03f; //0.03초보다 빠른 프레임이면 더 빠르게 실행됨
time += Time.deltaTime;//이러면 1초마다 출력된다.
if (time > 1)
{
if (flag == true)
{
flag = false;
}
else
{
flag = true;
}
print(flag);
obj1.SetActive(flag);
time = 0;
}
//깜박임의 기본적인 로직.
//bool flag = true;//이러면 논리적 오류가 생긴다. 지역변수이기 때문.
/*if (flag == true)
{
flag = false;
}
else
{
flag = true;
}
print(flag);*/
}
}
키보드 입력
키보드 입력
Enum(열거형)
숫자로 되어있는 값을 상수로 바꾸어 사용하는 타입
0,1,2,3,...
Input클래스 Getkey
입력을 위해 제공된다
키코드를 입력받는다.

//키코드입력
if(Input.GetKeyDown(KeyCode.Space))
{
print("스페이스다운");
}
if (Input.GetKey(KeyCode.Space))
{
print("스페이스입력중");
}
if (Input.GetKeyUp(KeyCode.Space))
{
print("스페이스업");
}
KeyCode
열거형이다
각 키보드 값이 정의되어있다.
GetKey의 다른형태

선호하지 않는 코드
GetButton 함수

가상 버튼 : Input Manager에서 내가 설정한 키.
GetKeyDown으로 대체할 수도 있다. (string 오버로딩)

마우스 입력
마우스
getMouseButton

//마우스
if(Input.GetMouseButtonDown(0))
{
print("GetMouseButtonDown");
}
if (Input.GetMouseButton(0))
{
print("GetMouseButton");
}
if (Input.GetMouseButtonUp(0))
{
print("GetMouseButtonUp");
}
API 문서에서 확인해볼 수 있다.
//마우스이벤트. 물체 위에서만 작동한다(마우스가 물체 위에 있을 경우에만)
private void OnMouseDown()
{
print("OnMousedown");
}
private void OnMouseUp() {
print("OnMouseUp");
}
private void OnMouseDrag()
{
print("OnMouseDrag");
}
//마우스이벤트. 물체 위에서만 작동한다(마우스가 물체 위에 있을 경우에만)
//마우스. 아무곳이나 클릭해도 됨. (GetMouseButtonDown, ...)
Generic
물체의 이동
Vector3 구조체
Vector3
유니티에서 제공하는 구조체

구조체->값 형태->생성 안 하고 선언만 해도 메모리에 생겨난다.
구조체 생성: 초기화용. 필수 아니다.
Vector3 vec;
vec.x= 1;
vec.y= 2;
vec.z= 3;
print(vec);
구조체->값->스택에 생긴다.
가장 많이 쓰는 방법: 생성과 동시 초기화
구조체->스택에 생긴다.
Vector3 vec=new Vector(1,2,3);
Vector3 vec=new Vector3(10,20,30);
Vector3 vec2=vec;
print(vec);//10,20,30
print(vec2);//10,20,30
vec.x = 999;
//구조체 : 값형식이라 값 복사-> vec.x바꿔도 vec2.x안 바뀐다.
print(vec2);//10,20,30
Vector3 어디에 사용될까?


//위치에 사용된다
this.transform.position = new Vector3(1,0,0);
this.transform.localScale= new Vector3(2,2,2);
Transform 과 position 읽어오기
프로퍼티

캡슐화
프로퍼티

private int hp;
public int GetHp()
{
return hp;
}
public void SetHp(int value) {
this.hp= value;
}
게터세터
프로퍼티(속성) 이용
//프로퍼티(속성)
private int _hp;
public int hp {
get { return _hp; }
set { _hp = value; }
}
//속성(프로퍼티)
Player player = new Player();
player.hp = 50;
int hp= player.hp;
print(hp);
Transform 과 position 변경하기
게임루프1
게임루프
게임을 만들 때 필요한 로직
움직이는 것처럼 보이려면?
* f를 누르면 물체 줌인
기본적 이동(z축 이동)
void Start()
{
transform.position = new Vector3(0, 0, 0); //position: 속성, getset에서 x,y,z,접근까지를 허용안함
//따라서 Vector 생성해준다. (x,y,z)
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.UpArrow))
{
this.transform.position += new Vector3(0, 0, 0.1f);//업데이트마다 참조하는 객체가 바뀐다->알아서 가비지컬렉터로 처리한다.
}
}
게임루프2
물체의 이동과 속도
방향을 가지는 벡터
방향을 가지는 Vector3

세 값을 필요로하는 모든 곳에 쓰인다.

둘 다 같은 벡터
float dist=Time.deltaTime* 2;//거리
Vector3 pos=transform.position;
pos.x = dist;
transform.position = pos;

//실제 방향의 벡터를 통해서 이동거리를 구하는 예
Vector3 v = new Vector3(1, 0, 0) * Time.deltaTime * 2;//방향*거리
transform.position += v;
물체의 관리와 충돌
게임오브젝트의 Find
게임오브젝트의 Find

GameObject other;
// Start is called before the first frame update
void Start()
{
//레퍼런스 변수를 선언하고 Start에서 Find함수 사용
other = GameObject.Find("OtherCube");
}
//레퍼런스 변수를 선언하고 Start에서 Find함수 사용
other = GameObject.Find("OtherCube");
other.GetComponent<MeshRenderer>().material.color = Color.blue;
큐브 색 변경
직접 연결하기
드래그앤 드롭
매니저 사용 예제(색변경)
매니저 사용 예제(색변경)

매니저클래스: 관리하는 클래스
점차적으로 테스트해가며 코드를 보완해나가야한다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Manager : MonoBehaviour
{
public GameObject cube0;
public GameObject cube1;
bool flag=true;
// Start is called before the first frame update
void Start()
{
cube0 = GameObject.Find("큐큐브");
cube1 = GameObject.Find("Cube");
ChangeColor(true);
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
flag = !flag;
ChangeColor(flag);
}
}
void ChangeColor(bool Flag)
{
if (flag == true)
{
cube0.GetComponent<MeshRenderer>().material.color = Color.red;
cube1.GetComponent<MeshRenderer>().material.color = Color.blue;
}
else
{
cube0.GetComponent<MeshRenderer>().material.color = Color.blue;
cube1.GetComponent<MeshRenderer>().material.color = Color.red;
}
}
}
충돌체와 강체
슈팅게임 프로토타입 만들기 1 (캐릭터와 배경)
유닛, 픽셀퍼유닛
유닛

세로 10 units
유닛 맞추려면

이런식으로 씬 비율, 해상도에서 맞추기
PixelPerUnit

이미지설정
비트맵이기 때문에 랩모드: 고정
필터모드: 포인트
이 이미지는 100의 크기를 갖기 때문에 최대크기: 128
압축x
압축을 하게 되면 이미지가 뭉개지는 경우가 발생할 수 있음.
이미지 추가
계층 뷰에 가져다 끌기

픽셀 퍼 유닛이 100이기 때문에 이 이미지는 1 유닛이 되는 것이다.

비행기: 48x48
따라서 1/4 크기를 가지게 되는 것
따라서 비행기의 픽셀 퍼 유닛을 48로 바꾸면 비행기가 1유닛을 차지하는 크기가 된다.
그러나 여전히 비행기가 작다
-> 픽셀퍼 유닛 20으로
트랜스폼의 scale?
이것은 보통 애니메이션이나, 실제로 크기가 커지는 물체를 표현할 때 사용한다.
픽셀 퍼 유닛을 써야 얼마만큼의 크기가 하나인지 가늠할 수 있다.
박스 콜라이더 추가
2D로
콜라이더 조절:
edit collider
픽셀 퍼 유닛
이미지마다 적용되는 내용이다
카메라사이즈1
카메라사이즈2
카메라 사이즈2
1080x1920
모바일에서 가장 많이 사용되는 9:16 해상도 중 하나해상도를 먼저 정하고 게임을 제작하는 게 좋다.


여기에서의 PixelPerUnit은 해상도를 위한 값
이 값은 프로젝트마다 하나로 고정
이미지의 PixelPerUnit은 상관하지 않고 이 공식을 사용해도 관계x
이것을 정해야 하는 이유: 해상도 따라 가로 사이즈가 자동 조절되기 때문.

9:16에 100을 곱한 값으로도 해상도를 사용할 수 있다
유니티가 알아서 확대 축소를 해주기 때문
타일맵 경우

타일맵의 사이즈를 고정하고(PixelPerUnit), 저렇게 하는 것도 좋다. 타일이 x축으로 40개, y축으로 22.5개 있다고 생각할 수 있다.
PixelPerUnit
이미지의 PixelPerUnit
카메라의 PixelPerUnit

하나의 값으로 게임 전체에서 고정해서 써야 한다.
게임 전체를 고려한 값
1080의 세로사이즈를 사용할 경우
1080/100->10.8(일반적으로 유니티에서 PixelPerUnit을 100으로 하기 때문에 여기선 그렇게 한다. )
10.8/2->5.4
우리는 16:9를 쓸 것
PixelPerUnit을 48로 잡았을 경우
1080/48/2->11.25

900사이즈로 하겠다 함(9:16에 100 곱한거, 어차피 유니티가 크기조정 해주니까)
900/100/2->4.5
이 화면이 정수로 나눠떨어지기에 이 크기를 사용.
우리가 쓸 화면

스프라이트 및 캐릭터 이동
스프라이트

SpriteRenderer

이동
GetAxisRaw
키보드 입력에서 축에 대한 값을 받아온다.
넣을 수 있는 인자: horizontal, vertical
-1에서 1까지의 값을 출력

중간값을 리턴하지 않고 -1,0,1 만 출력
이동 코드

화면 구조 바꾸기

콘솔 사용

계층 뷰 이름 바꾸기

F2
코드 뜯어보기
float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");
API 찾아보자
Input 검색

float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");
Vector3 dir=new Vector3(x,y,0).normalized;//방향을 나타내는 Vector
transform.position = transform.position + dir * Time.deltaTime * speed;
speed가 public 이므로 inspector에서 수정 가능
speed 조절해가며 게임 창에서 움직여볼 수 있다.
실행 중 인스펙터의 값을 수정하면 값이 원래대로 돌아감에 주의하자.
배출구 애니메이션 추가
배출구 애니메이션 추가
Sprite 수정

그 다음 Sprite Editor 클릭

적용 클릭
애니메이션
이미지 다중 선택 후 계층 뷰에 끌어오기

컨트롤러가 애니메이션 관리
플레이어에 추가.
저 화염 애니메이션을 비행기의 배출구로 사용할 것이다.
계층뷰의 화염을 Player로 끌어온다.
-> Player 가 thrusters를 소유.
이렇게 함으로써 플레이어가 이동할 때 thrusters가 따라온다.
Player는 GameObject.
GameObject: 폴더와 같은 역할
부모자식 관계를 만들어 플레이어에 추가.
플레이어가 이동될때마다 배출구가 따라옴
배출구: 애니메이션 갖고있다.
애니메이션 관리: 애니메이터 컴포넌트
세부적으로 보면
애니메이터: 컨트롤러 관리컨트롤러: 애니메이션 관리

추가-프로젝트 학습방법(개발자가 되기 위한 연습)
View Port와 월드좌표계
배경스크롤
배경 스크롤


배경을 계층뷰로 옮기기
레이어 문제
: sorting layer->이름 추가

v키를 클릭하고 옮기면 딱 맞게 이동 된다.
코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BGScript : MonoBehaviour
{
float speed = 2;
SpriteRenderer spr;
// Start is called before the first frame update
void Start()
{
spr=GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
transform.position += Vector3.left * Time.deltaTime * speed;
Vector3 pos=transform.position;
if (pos.x + spr.bounds.size.x / 2 < -8)
{
float size = spr.bounds.size.x * 2;//다시 스크롤 될 위치로 이동. (대기조)
pos.x += size;// x에 대입
transform.position = pos;//position에 대입함으로써 완전히 이동된 상태.
}
}
}
카메라의main (새로추가)
카메라의 main


Awake(): Start이전에 실행되는 함수
main=this; 코드->자기참조.
외부에 static변수를 노출
Player Script
void Start()
{
int value=ManagerScript.main.ManagerAdd(10, 20);
print(value);
}
Manager Script
//자기참조
public static ManagerScript main;
private void Awake()
{
main = this;
}
public int ManagerAdd(int a,int b)
{
print("Manager Add");
return a + b;
}
자기참조
: main이라고도, instance라고도 많이 함
캐릭터 위치 제한걸기
캐릭터 위치 제한 걸기

좌표: 센터 값
따라서 비행기 절반 사이즈가 필요한 것
Vector2 colSize;
Vector2 chrSize;
Vector3 min,max;
void Start()
{
min = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, 0));
print(min);
max = Camera.main.ViewportToWorldPoint(new Vector3(1, 1, 0));
print(max);
colSize=GetComponent<BoxCollider2D>().size;
chrSize = new Vector2(colSize.x / 2, colSize.y / 2);
}
Vector3 dir=new Vector3(x,y,0).normalized;//방향을 나타내는 Vector
transform.position = transform.position + dir * Time.deltaTime * speed;
float newX=transform.position.x;
float newY=transform.position.y;
if (newX < min.x + chrSize.x)
{
newX= min.x + chrSize.x;
}
if (newX > max.x - chrSize.x)
{
newX = max.x - chrSize.x;
}
if (newY < min.y + chrSize.y)
{
newY = min.y + chrSize.y;
}
if (newY > max.y - chrSize.y)
{
newY = max.y - chrSize.y;
}
transform.position = new Vector3(newX, newY, transform.position.z);
슈팅게임 프로토타입 만들기 2 (발사체와 회전)
프리펩및Instantiate
오일러각도와 쿼터니언
소행성 회전하면서 이동 - 추가
발사체 이펙트1 프리펩
발사체 이펙트2 사용
슈팅게임 프로토타입 만들기 3 (적과 발사체)
코인 생성
코인 프리펩 사용
코인 프리펩 사용

coin 변수 추가

파괴 애니메이션 사용
파괴 애니메이션 사용
메니저 생성
게임 매니저
오브젝트 생성 관리 목적

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject astroid;
public float time = 0;
public float maxTime = 2;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
time += Time.deltaTime;
if (time > maxTime)
{
time = 0;
Instantiate(astroid,new Vector3(10,Random.Range(-4.0f,4.0f),0),Quaternion.identity);//랜덤좌표 필요
}
}
}
적의 추가
적 추가

적의 생성
적 생성

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject astroid;
public float time = 0;
public float maxTime = 2;
public List<GameObject> enemies;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
time += Time.deltaTime;
if (time > maxTime)
{
int check=Random.Range(0, 2);//0,1
if (check == 0)
{
Instantiate(astroid, new Vector3(7, Random.Range(-4.0f, 4.0f), 0), Quaternion.identity);//랜덤좌표 필요
}
else
{
int type=Random.Range(0, 3);//0,1,2
Instantiate(enemies[type], new Vector3(7, Random.Range(-4.0f, 4.0f), 0), Quaternion.identity);//랜덤좌표 필요
}
time = 0;
}
}
}

적의충돌
적 충돌
발사체와 적의 충돌 추가
if (collision.gameObject.tag == "Enemy")
{
EnemyScript enemyScript = collision.gameObject.GetComponent<EnemyScript>();
enemyScript.hp -= 3;
Instantiate(shotEffect, transform.position, Quaternion.identity);//Quaternion.identity: 기본값
if (enemyScript.hp <= 0)
{
Instantiate(explosion, transform.position, Quaternion.identity);
Vector3 randomPos = new Vector3(Random.Range(-0.1f, 0.1f),
Random.Range(-0.1f, 0.1f), 0);
Instantiate(coin, transform.position + randomPos, Quaternion.identity);
Destroy(collision.gameObject);
}
}
//ShotScript
적 발사체 구현
적 발사체 구현

게임의 UI
캔버스 렌더모드
캔퍼스 렌더모드
오버레이모드

캔버스 Rect Transform 위치 변경 불가
각 UI들은 가능
즉 UI 수정되면 캔버스가 확대 축소 자동으로 조절됨: 유니티가 지원

카메라 모드
UI를 게임 뒤로도, 앞으로도 이동할 수 있는 모드
퍼즐게임에서 많이 사용
캔버스 위치: 카메라의 위치와 캔버스의 위치가 동일

오버레이모드와 거의 동일
다른점:
캔버스의 크기가 게임화면과 같음UI위치를 게임 화면 뒤로 보낼 수 있음
카메라모드로 변경하려면 카메라를 연결해주는 과정 필요하다

메인 카메라와 캔버스 위치 동일
버튼과 캐릭터 레이아웃 조절하는 법
z축 조절
월드 스페이스 모드

캔버스 조절해줘야 한다.
카메라모드와 다른점: 캔버스 자동으로 확대축소 되지 않는다.
월드 스페이스 좌표가 실제 UI 와 같은 평면에 존재
Rect Transform
스크린 좌표계
게임내 코인의 텍스트 UI 추가
게임내부UI의 구성
패널과 Pause Menu 추가
Pause Menu - Resume 추가
메인화면 구성
메인화면 버튼 추가
프로토타입의 오류 수정
프로토타입의 오류 수정

UI 위치 달라지는 문제
->앵커를 UI위치에 박는게 좋다.
//ViewportToWorldPoint 써야 위치를 정확히 알 수 있다.
min = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, 0));
//min = new Vector3(-8, -4.5f, 0);
print(min);
max = Camera.main.ViewportToWorldPoint(new Vector3(1, 1, 0));
//max = new Vector3(8, 4.5f, 0);

maxRight = Camera.main.ViewportToWorldPoint(new Vector3(1, 1, 0)).x;
//maxRight: float
슈팅게임 만들기 1 (프로토타입을 변경)
레터박스 - 정사각형에 가까운 화면
레터박스-정사각형에 가까운 화면
뷰포트를 수정하여 게임 화면의 크기를 수정한다.
내 화면보다 작은 화면의 크기를 원하는 화면 비율로 수정함.


값 비교 이유: 연두 화면이 더 작은지 보려고
두 화면 비율을 나눈 값을 scaleWidth 변수에 저장한다.
1보다 작다면, 그 화면이 내가 원하는 화면보다 작다는 것이다.
카메라 스크립트
Awake()함수 쓰는 이유: 카메라 함수가 게임에서 제일 먼저 실행되어야 하기 때문.
1. 우선 현재 화면 비율을 저장한다.

첫 줄: 현재 화면 비율 저장
두 번째 줄: 현재 화면 비율을 16:9 비율로 나눈 값을 저장-> 만약 이 값이 1보다 작다면, 16:9보다 작은 화면이 현재의 화면 비율이라는 뜻이다.

rect: 카메라 컴포넌트

Viewport Rect
0~1까지의 값
0: 시작점이 0
0.15: 15%에서 시작
H: 70프로로 축소한다는 뜻
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraScript : MonoBehaviour
{
private void Awake()
{
Camera camera=GetComponent<Camera>();
Rect rect = camera.rect;
float ratio=(float)Screen.width / Screen.height;//화면비율
float scaleWidth = ratio / (float)(16 / 9);//1이하인가?
print(scaleWidth);
if (scaleWidth < 1)
{
rect.height = scaleWidth;
rect.y = (1 - scaleWidth) / 2;
}
camera.rect = rect;
}
}
결론
상하를 잘라서 화면 비율을 맞춘다.
방법 : 카메라 rect 조정
레터박스 수정 - 가로로 긴화면
레터박스 수정-가로로 긴 화면

이번엔 scaleHeight이 필요(전 강의: scaleWidth)
해상도 수정: 카메라가 보여주는 영역을 바꾼다는 뜻.

코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraScript : MonoBehaviour
{
private void Awake()
{
Camera camera=GetComponent<Camera>();
Rect rect = camera.rect;
float ratio=(float)Screen.width / Screen.height;//화면비율
float scaleWidth = ratio / ((float)16 / 9);//1이하인가?
float scaleHeight = 1 / scaleWidth;
print(scaleHeight);//0.84. 화면을 바라보는 영역을 0.84로 축소하여 0.16을 보지 않겠다는 뜻. 0.16이 양쪽에 들어가야하니 /2하면 0.08씩 안 봄.
if (scaleWidth < 1)
{
rect.height = scaleWidth;
rect.y = (1 - scaleWidth) / 2;
}
else//가로를 자르는 경우
{
rect.width = scaleHeight;
rect.x=(1-scaleHeight) / 2;
}
camera.rect = rect;
}
}
WorldSpace UI 수정
WorldSpace UI 수정

캔버스의 오버레이모드를 월드스페이스로 조정한다.
이벤트카메라에 메인 카메라를 넣는다
소팅레이어: add sorting layer
-> UI추가,캔버스 리셋
rect transform의 width를 1600, height를 900으로
scale을 0.01로(x,y,z)
UI를 화면크기에 맞게
Title 애니메이션
canvas(* menu scene)
title-> scale 0.8, 0.8로->이래도 애니메이션이 화면을 벗어남(근데 난 화면 안 벗어남. 방법만 알자)
애니메이션을 변경해보자->anim폴더->titleanim
애니메이션 다이아몬드 위치를 새로 만들자
add property->anchored position
버튼
*transform 점 세개->copy component-> 위치복사할 곳에서 paste component values
캔버스수정(게임씬)
렌더러: 월드스페이스
메인카메라 대입
소팅레이어: UI
rect transform 초기화 후 1600, 900으로 변경, scale 0.01로 고정
DataScript 의 싱글톤
싱글톤에서 코인을 저장하고 로드
싱클톤에서 코인을 저장하고 로드

게임매니저의 coin : coinInGame(이름변경)
게임데이터 스크립트에서 전체코인 수정
이 값을 저장할 필요가 있다.PlayerPrefs라는 기능이 있다.
public float GetCoin()
{
this.coin=PlayerPrefs.GetFloat("TotalCoin", 0);
return this.coin;
}
public void AddCoin(float coin)
{
this.coin += coin;
PlayerPrefs.SetFloat("TotalCoin", this.coin);
}
//잠시 후 수정(전체코인 변경예정)
//coinText.text=coinInGame.ToString();
coinText.text=DataScript.instance.GetCoin().ToString();
//DataScript
DataScript.instance.AddCoin(coinScript.coinSize);
GameManager.instance.coinText.text = DataScript.instance.GetCoin().ToString();
//PlayerScript
캐릭터 고정데이터 로딩
캐릭터 구조체 생성 및 로드
저장로드하는 캐릭터 데이터
슈팅게임만들기 2 (케릭터로딩 및 메인메뉴 스크롤)
메뉴 UI 변경
Lerp 선형보간 사용
Lerp 선형보간 사용
선형보간
ab사이의 c를 구함.
세 번째 인수 0.5: 50퍼센트라는 뜻

업데이트문에서 선형보간 함수를 써보자 .
3번째 인자 0.2f인 경우
0.5f 처럼 급격한 것은 마찬가지.
물체 이동에 사용해보자.
x값을 변경하는 코드
a.x 업데이트
그리고 position 에 벡터값 a를 넣는다.
이 움직임은 플레이어에 사용할 수 없다.
따라서 일반적으로 UI에 사용한다.
너무 빨라서 캐릭터에 사용할 수 없다.

position을 받아서 다시 대입
스크롤뷰 스냅기능1
스크롤뷰 스냅기능2
스크롤뷰 스냅기능3
슈팅게임만들기 3
코인 테스트 버튼 추가
unlock Get Set의 방어코드 추가
함수와 AddListener
Delegate(대리자)
Delegate( 대리자 )

정의, 선언, 해제를 알아보자
정의
클래스: 정의 ,선언
딜리게이트: 마찬가지

선언

저장

사용

해제


void OnFuncType():
델리게이트로 정의하는 것. 타입이 됨onValueChange:
함수를 저장하는 변수+=:
onValueChange에 추가onValueChange():
추가한 두 함수가 실행됨.
딜리게이트 쓰는 이유
서로 다른 클래스에서 함수를 주고받기 위해서.
//정의
public delegate void OnFuncType();//void OnFuncType(): 타입
//선언. 선언할 떄 타입은 OnFuncType
//void OnFuncType()형태의 함수를 저장할 수 있게 되는 것임.
OnFuncType onValueChange;
void Print1()
{
print("Pritn1");
}
void Print2()
{
print("Pritn2");
}
void Start()
{
onValueChange += Print1;
onValueChange += Print2;
onValueChange();
print("-= 실행");
onValueChange -= Print1;
onValueChange();
}
메모리 구조
-= 안 쓴 이유: 스크립트 사라질 때 같이 사라지니까.

선언 : 함수를 저장할 수 있는 변수

Delegate 와 event로 가상 UI 테스트
두 클래스 간 딜리게이트 사용
Delegate와 event로 가상 UI 테스트

Event

event 는 하나의 키워드

event : 사용하지 않아도 동작
event 가 없을 경우 사용자에서도 사용 가능
사용자가 onClick()을 실행할 필요가 없다.
그렇기 때문에 이벤트가 있는 것.

UIScript
: 관리자==UI 만 관리ItemScript
: 사용자
사용자
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemScript : MonoBehaviour
{ //아이템스크립트: 사용자
//관리자에 등록할 함수 구현, 연결 역할
public UIScript uiscript;// 이 레퍼런스는 유니티에서 연결
// Start is called before the first frame update
void Start()
{
uiscript.onClick += ClickAction;//딜리게이트에 함수 등록
}
void ClickAction()
{
print("ClickAction");
}
// Update is called once per frame
void Update()
{
}
}
관리자
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIScript : MonoBehaviour
{
public delegate void OnClick();//딜리게이트 정의
public OnClick onClick;//딜리게이트 선언
//딜리게이트에 연결할 함수: 사용자에서 구현한다. //ItemScript
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
onClick();//UI스크립트: 실행 담당//관리자.
}
//UIManager에서 Key를 입력받음->아이템으로전달->클릭액션실행
}
}
delegate 변수를 private로 숨기기
delegate 변수를 private로 숨기기


사용자가 대기를 하는 입장
->따라서 Listener라는 이름 사용
->인자: 나 이거 기다릴래.onClick() 실행: UI 관리자
사용자: 대기하고 있다가 구현해놨던 함수 실행.

private event OnClick onClick
->private이기에 onClick변수를 외부에서 접근 불가.public void SetListener(OnClick listener){
onClick+=listerner;
}
->public이기에 외부에서 사용 가능하도록 열어줌
->onClick변수는 내부에서만 사용하도록.
다른 클래스에서는 onClick 사용 못 함.
SetListener에서 대입연산자 차이점

관리자
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIScript : MonoBehaviour
{
public delegate void OnClick();//딜리게이트 정의
private event OnClick onClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수 추가 못 함.
//딜리게이트에 연결할 함수: 사용자에서 구현한다. //ItemScript
public void SetListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void 이고 매개변수 () 인 함수만 넣겠다.
{
this.onClick += onClick;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
onClick();//UI스크립트: 실행 담당//관리자.
}
//UIManager에서 Key를 입력받음->아이템으로전달->클릭액션실행
}
}
사용자
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemScript : MonoBehaviour
{ //아이템스크립트: 사용자
//관리자에 등록할 함수 구현, 연결 역할
public UIScript uiscript;// 이 레퍼런스는 유니티에서 연결
// Start is called before the first frame update
void Start()
{
uiscript.SetListener(ClickAction);//딜리게이트에 함수 등록. ClickAction대기
uiscript.SetListener(ClickAction);//딜리게이트에 함수 등록. ClickAction대기
}
void ClickAction()
{
print("ClickAction");
}
// Update is called once per frame
void Update()
{
}
}
delegate 를 키입력이 두 개있는 형태로 확장
delegate를 키 입력이 두 개있는 형태로 확장


두 개의 이벤트//딜리게이트 변수: 이벤트
사용자
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemScript : MonoBehaviour
{ //아이템스크립트: 사용자
//관리자에 등록할 함수 구현, 연결 역할
public UIScript uiscript;// 이 레퍼런스는 유니티에서 연결
// Start is called before the first frame update
void Start()
{
uiscript.SetYesListener(YesAction);//딜리게이트에 함수 등록. ClickAction대기
uiscript.SetNoListener(NoAction);//딜리게이트에 함수 등록. ClickAction대기
}
void YesAction()
{
print("YesAction");
}
void NoAction()
{
print("NoAction");
}
// Update is called once per frame
void Update()
{
}
}
관리자
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIScript : MonoBehaviour
{
public delegate void OnClick();//딜리게이트 정의
private event OnClick onYesClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수 추가 못 함.
private event OnClick onNoClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수 추가 못 함.
//딜리게이트에 연결할 함수: 사용자에서 구현한다. //ItemScript
public void SetYesListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void 이고 매개변수 () 인 함수만 넣겠다.
{
this.onYesClick += onClick;
}
public void SetNoListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void 이고 매개변수 () 인 함수만 넣겠다.
{
this.onNoClick += onClick;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
onYesClick();//UI스크립트: 실행 담당//관리자.
}
else if (Input.GetKeyDown(KeyCode.B))
{
onNoClick();//UI스크립트: 실행 담당//관리자.
}
//UIManager에서 Key를 입력받음->아이템으로전달->클릭액션실행
}
}
버튼을 추가하여 팝업구현 1

아이템: 주
팝업: 부
버튼을 추가하여 팝업 구현1

아이템: 주
팝업: 부
아이템이 팝업 패널을 소유하고 있는 것과 마찬가지

관리자
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PopupScript : MonoBehaviour
{
public Button yesButton;
public Button noButton;
public delegate void OnClick();//딜리게이트 정의
private event OnClick onYesClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수 추가 못 함.
private event OnClick onNoClick;//딜리게이트 선언. private: 외부 접근 불가. 직접 이 변수 써서 함수 추가 못 함.
//딜리게이트에 연결할 함수: 사용자에서 구현한다. //ItemScript
public void SetYesListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void 이고 매개변수 () 인 함수만 넣겠다.
{
this.onYesClick += onClick;
}
public void SetNoListener(OnClick onClick)//매개변수 OnClick타입: 리턴 void 이고 매개변수 () 인 함수만 넣겠다.
{
this.onNoClick += onClick;
}
void Start()
{
//yes버튼과 no버튼의 액션 추가(인스펙터 창에서x 코드에서 클릭이벤트 연결
yesButton.onClick.AddListener(PopupYesAction);
noButton.onClick.AddListener(PopupNoAction);
}
void PopupYesAction()
{
//딜리게이트 실행하도록.
onYesClick();
}
void PopupNoAction()
{
onNoClick();
}
}
사용자
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemScript : MonoBehaviour
{ //아이템스크립트: 사용자
//관리자에 등록할 함수 구현, 연결 역할
public PopupScript popupScript;// 이 레퍼런스는 유니티에서 연결
// Start is called before the first frame update
void Start()
{
popupScript.SetYesListener(ItemYesAction);//딜리게이트에 함수 등록. ClickAction대기
popupScript.SetNoListener(ItemNoAction);//딜리게이트에 함수 등록. ClickAction대기
}
void ItemYesAction()
{
print("ItemYesAction");
}
void ItemNoAction()
{
print("ItemNoAction");
}
// Update is called once per frame
void Update()
{
}
}
버튼을 추가하여 팝업구현 2
슈팅게임만들기 4
슈팅게임 만들기 4 인트로
슈팅 게임 만들기 4

OnBecameInvisible 오류수정1
OnBecameInvisible 오류 수정1

확인하기 어려운 오류 실행
프리팹을 원하는 위치에 놓음
정지버튼 누르고 플레이->한 프레임씩 실행(맨 오른쪽)
