[ 목차 ]
1945 1번째
https://ganggang1008.tistory.com/21
1945 2번째
https://ganggang1008.tistory.com/22
오늘은 지난 글에 이어서 1945 3번째이다.
미사일에 관한 것과 보스를 추가해 볼 것이다.
1. 미사일 파워업
(1) 미사일 4종류 추가하기
이제 아이템을 먹으면 미사일이 진화하는 것을 구현하겠다.
우선 기존에 만들어뒀던 플레이어의 미사일이 있을 것이다.
프리팹의 Sprite Renderer의 Sprite를 다른 원하는 미사일 이미지로 바꾼다.
이름을 바꾸고 다시 프리팹 폴더에 드래그해
Original Prefab을 하면 기존 것 외에 새로 추가로 생성된다.
그렇게 총 4개의 미사일을 만들었다.
이제 미사일을 구현할 스크립트를 수정할 것이다.
어떤 스크립트냐면
플레이어 오브젝트에 붙어있는 PlayerController 스크립트이다.
미사일이 하나로 고정된 코드에서 4개로 늘어나니 배열로 변경한다.
주석이 전에 썼던 코드이고, 아래가 바뀐 코드이다.
//public GameObject bullet; //총알 추후 4개 배열로 만들 예정
public GameObject[] bullet; //배열로 변경
새로운 정수형 변수를 추가한다.
아이템을 먹을 때 1씩 증가할 예정이다.
public int power = 0;
배열로 변경하면 오류가 하나 뜰 것이다.
스페이스를 누를 때의 동작하는 부분에서
bullet에서 bullet[power]로 변경해 준다.
if(Input.GetKeyDown(KeyCode.Space))
{
//프리팹 위치 방향 넣고 생성
//Instantiate(bullet, pos.position, Quaternion.identity);
Instantiate(bullet[power], pos.position, Quaternion.identity);
}
충돌처리 코드를 생성한다.
트리거 코드를 생성해 Tag가 Item인 오브젝트에 충돌했을 때
power가 1씩 증가하고, 3개를 먹으면 3 이상 올라가지 않도록 하였다.
3이 되기 전까지는 1씩 증가하는 게 진행되는 것이다.
그리고 아이템을 먹으면 아이템 오브젝트가 삭제된다.
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Item"))
{
power += 1; //아이템과 충돌했을 때 1씩 증가
if(power >= 3)
{
power = 3; //더 먹어도 3이상 올라가지 않음
}
else
{
//3이 되기 전까지는 파워업 함
}
//아이템 먹은 처리
Destroy(collision.gameObject);
}
}
그렇게 전체코드이다.
using UnityEngine;
using static UnityEditor.Progress;
public class PlayerController : MonoBehaviour
{
public float speed = 5f;
private Vector2 minBounds;
private Vector2 maxBounds;
Animator ani; //애니메이터를 가져올 변수
//public GameObject bullet; //총알 추후 4개 배열로 만들 예정
public GameObject[] bullet; //배열로 변경
public Transform pos = null;
public int power = 0;
//public GameObject UpGrade_bullet;
//bool UpGrade1 = false;
//아이템
//레이저
void Start()
{
ani = GetComponent<Animator>();
// 화면의 경계를 설정
Camera cam = Camera.main;
Vector3 bottomLeft = cam.ViewportToWorldPoint(new Vector3(0, 0, 0));
Vector3 topRight = cam.ViewportToWorldPoint(new Vector3(1, 1, 0));
minBounds = new Vector2(bottomLeft.x, bottomLeft.y);
maxBounds = new Vector2(topRight.x, topRight.y);
}
void Update()
{
// 플레이어 이동
float moveX = Input.GetAxis("Horizontal") * speed * Time.deltaTime;
float moveY = Input.GetAxis("Vertical") * speed * Time.deltaTime;
//애니메이션 조건문
// -1 0 1
if (Input.GetAxis("Horizontal") <= -0.1f)
{
ani.SetBool("left", true);
}
else
ani.SetBool("left", false);
if (Input.GetAxis("Horizontal") >= 0.1f)
{
ani.SetBool("right", true);
}
else
ani.SetBool("right", false);
if (Input.GetAxis("Vertical") >= 0.1f)
{
ani.SetBool("up", true);
}
else
{
ani.SetBool("up", false);
}
if(Input.GetKeyDown(KeyCode.Space))
{
//프리팹 위치 방향 넣고 생성
//Instantiate(bullet, pos.position, Quaternion.identity);
Instantiate(bullet[power], pos.position, Quaternion.identity);
}
//Vector3 newPosition = transform.position + new Vector3(moveX, moveY, 0);
//// 경계를 벗어나지 않도록 위치 제한
//newPosition.x = Mathf.Clamp(newPosition.x, minBounds.x, maxBounds.x);
//newPosition.y = Mathf.Clamp(newPosition.y, minBounds.y, maxBounds.y);
//transform.position = newPosition;
transform.Translate(moveX, moveY, 0);
//캐릭터의 월드 좌표를 뷰포트 좌표계로 변환해준다.
Vector3 viewPos = Camera.main.WorldToViewportPoint(transform.position);
viewPos.x = Mathf.Clamp01(viewPos.x); //x값을 0이상, 1이하로 제한한다.
viewPos.y = Mathf.Clamp01(viewPos.y); //y값을 0이상, 1이하로 제한한다.
Vector3 worldPos = Camera.main.ViewportToWorldPoint(viewPos);//다시월드좌표로 변환
transform.position = worldPos; //좌표를 적용한다.
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Item"))
{
power += 1; //아이템과 충돌했을 때 1씩 증가
if(power >= 3)
{
power = 3; //더 먹어도 3이상 올라가지 않음
}
else
{
//3이 되기 전까지는 파워업 함
}
//아이템 먹은 처리
Destroy(collision.gameObject);
}
}
}
아이템 오브젝트(프리팹)의 태그를 Item으로 생성해 바꿔준다.
플레이어 오브젝트에 붙어있는 PlayerController Inspector가 바뀌어 있을 것이다.
미사일을 전부 추가해야 하기 때문에 프리팹에 있는 모든 미사일을
배열의 이름 ‘Bullet’ 에 드래그하면 생성된다.
주의할 점은 List is empty가 아니라, Bullet 이름에 가져다 붙어야 한다.
그렇게 4개가 전부 들어간 모습이다.
(2) 아이템 생성
아이템을 가져오는 변수 선언이다.
//아이템 가져오기
public GameObject Item;
아이템이 생성되는 코드이다.
public void ItemDrop()
{
//아이템 생성
Instantiate(Item, transform.position, Quaternion.identity);
}
미사일에 따른 데미지를 입는 함수이다.
//미사일에 따른 데미지 입는 함수
public void Damage(int attack)
{
ItemDrop();
Destroy(gameObject);
}
이제 다른 클래스에서 가져오는 것을 할 것이다.
PBullet 스크립트에 간다.
Monster 스크립트의 있는 함수를 여기에 불러올 것이다.
Monster에 있는 public void Damage에 있는 함수를 호출하는 것이다.
위의 주석이 전에 쓰던 함수이고,
아래가 변경한 함수이다.
//몬스터
//Destroy(collision.gameObject);
collision.gameObject.GetComponent<Monster>().Damage(1); //다른 클래스에 있는 함수 호출
저 코드를 어디에 사용하느냐?
PBullet 스크립트의 충돌처리에 가서 if 조건문 안에 넣어주면 된다.
//충돌처리
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.CompareTag("Enemy"))
{
//이펙트 생성
GameObject go = Instantiate(effect, transform.position, Quaternion.identity);
//1초 뒤에 지우기
Destroy(go, 1);
//몬스터
//Destroy(collision.gameObject);
collision.gameObject.GetComponent<Monster>().Damage(1); //다른 클래스에 있는 함수 호출
//미사일 삭제
Destroy(gameObject);
}
}
아이템을 먹었을 때 업그레이드가 됐다는 UI를 표시해 줄 것이다.
해당 UI 이미지는 프리팹으로 만든 뒤,
코드를 수정할 것이다.
“플레이어”가 “아이템”을 먹었을 때이기 때문에
PlayerController 스크립트를 수정해 줄 것이다.
필드를 선언해 주되, private은 inspector에 보이지 않게 된다.
하지만 보이게 하는 방법도 있다.
[SerializeField]하면 inspector에서 보이게 되어 사용할 수 있다.
아래의 필드를 PlayerController 스크립트에 넣어준다.
[SerializeField] //이 코드를 붙이면 private를 써도 inspector에서 사용할 수 있다.
private GameObject powerup;
이제 아이템을 먹었을 때이기 때문에
코드의 충돌처리 부분으로 간다.
충돌처리의 기존 Item 조건문 안의 조건에서
else에서 3 이상일 때는 호출하지 않고, 3 전까지 UI를 호출하는 것이다.
Destroy를 통해 1초 뒤에 해당 UI가 없어지는 것으로 한다.
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Item"))
{
power += 1; //아이템과 충돌했을 때 1씩 증가
if(power >= 3)
{
power = 3; //더 먹어도 3이상 올라가지 않음
}
else
{
//3이 되기 전까지는 파워업 함
GameObject it = Instantiate(powerup, transform.position, Quaternion.identity);
Destroy(it, 1);
}
//아이템 먹은 처리
Destroy(collision.gameObject);
}
}
2. Boss
(1) Boss 생성
만일 보스의 sprite가 부위별로 분해가 되어있다면?
그 부위가 한 부위마다 여러 장이라고 하면?
방법은 똑같다.
부위별로 애니메이터, 애니메이션 클립 만드는 방식으로 넣어서
하나의 큰 오브젝트의 하위객체로 다 넣어준다.
그리고 Box Collider 2D 생성해 범위를 조정해 준다.
Pos를 추가해
보스에서 미사일이 나갈 위치를 조정한다.
Boss의 스크립트를 작성해 줄 것이다.
using System.Collections;
using UnityEngine;
public class Boss : MonoBehaviour
{
int flag = 1;
int speed = 2;
public GameObject mb;
public GameObject mb2;
public Transform pos1;
public Transform pos2;
void Start()
{
StartCoroutine("BossMissle");
}
//코루틴
IEnumerator BossMissle()
{
while(true)
{
//미사일 두개
Instantiate(mb, pos1.position, Quaternion.identity);
Instantiate(mb, pos2.position, Quaternion.identity);
yield return new WaitForSeconds(0.5f);
}
}
}
이제 플레이어의 미사일 스크립트 PBullet 에서
Boss 충돌처리를 할 것이다.
추가할 코드는 Tag를 Boss인 것과 충돌처리를 하는 것이다.
if (collision.CompareTag("Boss"))
{
//이펙트 생성
GameObject go = Instantiate(effect, transform.position, Quaternion.identity);
//1초 뒤에 지우기
Destroy(go, 1);
//미사일 삭제
Destroy(gameObject);
}
그렇게 전체코드이다.
using UnityEngine;
public class PBullet : MonoBehaviour
{
public float Speed = 4.0f;
//공격력
//이펙트
public GameObject effect;
//아이템
public GameObject item;
void Update()
{
//미사일 위쪽방향으로 움직이기
//위의 방향 * 스피드 * 타임
transform.Translate(Vector2.up * Speed * Time.deltaTime);
}
//화면밖으로 나갈경우
private void OnBecameInvisible()
{
//자기 자신 지우기
Destroy(gameObject);
}
//충돌처리
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.CompareTag("Enemy"))
{
//이펙트 생성
GameObject go = Instantiate(effect, transform.position, Quaternion.identity);
//1초 뒤에 지우기
Destroy(go, 1);
//몬스터
Destroy(collision.gameObject);
//아이템 생성
GameObject it = Instantiate(item, transform.position, Quaternion.identity);
//5초 뒤에 지우기
Destroy(it, 5);
//미사일 삭제
Destroy(gameObject);
}
if (collision.CompareTag("Boss"))
{
//이펙트 생성
GameObject go = Instantiate(effect, transform.position, Quaternion.identity);
//1초 뒤에 지우기
Destroy(go, 1);
//미사일 삭제
Destroy(gameObject);
}
}
}
Boss의 오브젝트 Tag를 변경해 준다.
여기서 중요한 점은 부위별로 나누어져 하위객체로 있는 것도 Tag를 Boss로 변경해주어야 한다.
그렇지만 이건 선택사항이다.
특정 부위만 충돌처리를 하고 싶다면 특정 부위만 변경하면 된다.
Pos는 변경 안 해줘도 괜찮다.
(2) Boss 미사일
보스의 미사일 이미지를 객체로 만들고,
Inspector는 이렇게 지정해 준다.
사진에는 체크가 되어있지 않지만, is Trigger를 체크해 준다.
이제 보스 미사일 스크립트를 새로 생성할 것이다.
using UnityEngine;
public class BossBullet : MonoBehaviour
{
public float Speed = 3f;
Vector2 vec2 = Vector2.down;
void Update()
{
transform.Translate(vec2 * Speed * Time.deltaTime);
}
public void Move(Vector2 vec)
{
vec2 = vec;
}
private void OnBecameInvisible()
{
Destroy(gameObject);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
//미사일 지움
Destroy(gameObject);
}
}
}
이 스크립트를 BossBullet 오브젝트에 붙이고 Prefab화 한다.
아까 Boss 오브젝트의 Boss 스크립트를 넣었을 때 미사일을 임시로 다른 몬스터 미사일을 넣었었다.
이제 새로 생성된 BossBullet 프리팹을 바꿔 넣어준다.
(3) Boss 원형 미사일
라디안과 디그리
일상적으로 사용하는 각의 단위는 디그리이다. 90도 180도 등 이런 것이 디그리(degree)이다.
그런데, 각을 표현하는 다른 방법으로 라디안(radian)이 있다.
1 radin = 57.3도
2 radin = 114.6
라디안에서 디그리, 디그리에서 라디안으로 변경 가능하다.
180 degree = π radian
1 degree = π / 180 radian
x degree = x * π / 180 radian
π redian = 180 degree
1 radian = 180 / π degree
x radian = x * 180 / π degree
삼각함수(사인)
sin 0 → 1
0 → 90
cos 1 → 0
0 → 90
갑자기 이게 무슨 수학이냐? 할 수 있겠지만,
원형으로 쏘는 미사일을 만들려면 개념을 알아야 응용을 하기에
어떠한 원리로 코드를 구성하는지에 대한 개념 설명이다.
using System.Collections;
using UnityEngine;
public class Boss : MonoBehaviour
{
int flag = 1;
int speed = 2;
public GameObject mb;
public GameObject mb2;
public Transform pos1;
public Transform pos2;
void Start()
{
StartCoroutine(BossMissle());
StartCoroutine(CircleFire());
}
IEnumerator BossMissle()
{
while (true)
{
//미사일 두개
Instantiate(mb, pos1.position, Quaternion.identity);
Instantiate(mb, pos2.position, Quaternion.identity);
yield return new WaitForSeconds(0.5f);
}
}
//sin 0 -> 1
//각도 0 ->90
//cos 1 -> 0
//각도 0 -> 90
//원방향으로 미사일 발사
IEnumerator CircleFire()
{
//공격주기
float attackRate = 3;
//발사체 생성갯수
int count = 30;
//발사체 사이의 각도
float intervalAngle = 360 / count;
//가중되는 각도(항상 같은 위치로 발사하지 않도록 설정
float weightAngle = 0f;
//원 형태로 방사하는 발사체 생성(count 갯수 만큼)
while (true)
{
for (int i = 0; i < count; ++i)
{
//발사체 생성
GameObject clone = Instantiate(mb2, transform.position, Quaternion.identity);
//발사체 이동 방향(각도)
float angle = weightAngle + intervalAngle * i;
//발사체 이동 방향(벡터)
//Cos(각도)라디안 단위의 각도 표현을 위해 pi/180을 곱함
float x = Mathf.Cos(angle * Mathf.Deg2Rad);
//sin(각도)라디안 단위의 각도 표현을 위해 pi/180을 곱함
float y = Mathf.Sin(angle * Mathf.Deg2Rad);
//발사체 이동 방향 설정
clone.GetComponent<BossBullet>().Move(new Vector2(x, y));
}
//발사체가 생성되는 시작 각도 설정을 위한변수
weightAngle += 1;
//3초마다 미사일 발사
yield return new WaitForSeconds(attackRate);
}
}
private void Update()
{
if (transform.position.x >= 1)
flag *= -1;
if (transform.position.x <= -1)
flag *= -1;
transform.Translate(flag * speed * Time.deltaTime, 0, 0);
}
}
(4) Boss Head Animation
애니메이션을 함수로 사용하는 것을 해볼 것이다.
보스의 머리 움직임에 따라 각도가 다르게 미사일을 쏘는 것이다.
using UnityEngine;
public class BossHead : MonoBehaviour
{
[SerializeField] //직렬화
GameObject bossbullet; //보스 미사일
//애니메이션에서 함수 사용
public void RightDownLaunch()
{
GameObject go = Instantiate(bossbullet, transform.position, Quaternion.identity);
go.GetComponent<BossBullet>().Move(new Vector2(1, -1));
}
public void LeftDownLaunch()
{
GameObject go = Instantiate(bossbullet, transform.position, Quaternion.identity);
go.GetComponent<BossBullet>().Move(new Vector2(-1, -1));
}
public void DownLaunch()
{
GameObject go = Instantiate(bossbullet, transform.position, Quaternion.identity);
go.GetComponent<BossBullet>().Move(new Vector2(0, -1));
}
}
스크립트를 다 작성한 후에
이 스크립트를 BossHead 오브젝트에 연결해 준다.
애니메이션 창을 연다.
BossHead 클릭 - window - Animation - Animation
단축키로는 Ctrl + 6
켜진 Animation 창에서
화살표 아이콘을 클릭하면 sprite 이미지가 보일 것이다.
Animation을 조금 수정할 것이다.
왼쪽 2컷 / 정면 2컷 / 오른쪽 2컷
이렇게 배치를 한다.
이제 함수를 적용해 줄 것이다.
원하는 컷에 선을 가져다 놓고
왼쪽에 아이콘이 보일 것인데, 기다라면서 아래가 뾰족한 아이콘을 누르면 Add event가 보일 것이다.
그것을 클릭!
그러면 Inspector 창에 Animation Event가 뜰 것이다.
Function 항목을 누르면
BossHead - Methods - 방향에 맞는 Launch 클릭 (왼쪽이면 왼쪽)
그렇게 왼쪽 하나 정면 하나 오른쪽 하나의 이벤트를 적용해 주었다.
이렇게 되면 완성이다!
몬스터를 없애면 아이템이 생기고,
아이템을 먹으면 미사일이 강화되는 것!
그리고 Boss를 만드는 것까지..
갑자기 삼각함수가 나올 때에는 많이 당황했다..
고등학교 수포자인 나에게 좀 어려운 개념이었다.
역시 개발을 하려면 수학을 알아야 한다는 사실이...
수학 놓지 말 걸 그랬나 봐....
그래도.. 잘 모르겠지만 신기하게 재밌었다.
아직도 수학은 어렵지만 원형으로 나올 땐 정말 신기했다.
이러한 많은 기능을 만나 볼 수 있어서 좋았다.
'Development > 멋쟁이사자처럼 게임개발 부트캠프' 카테고리의 다른 글
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 18일차 - Tilemap (0) | 2025.03.25 |
---|---|
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 17일차 - 1945Game (4) (2) | 2025.03.23 |
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 15일차 - 1945Game (2) (0) | 2025.03.20 |
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 14일차 - 1945Game (1) (2) | 2025.03.19 |
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 13일차 - Dragon Flight (2) (2) | 2025.03.19 |