[ 목차 ]
지난번 14일 차에 이어서 1945를 이어서 할 것이다.
배울 것은 미사일과 적 랜덤 생성, 유도 미사일
그리고 충돌에 의한 이펙트 생성까지
다른 게임을 제작할 때에도 도움이 많이 되는 기능이니
잘 익히고 가자!
1. 미사일 추가
미사일을 만들 오브젝트를 만들고,
Order in Layer 1
Rigidbody 2D - Gravity Scale 0
Box Collider 2D - 콜라이더 범위 조정
미사일 스크립트를 작성한다.
using UnityEngine;
public class PBullet : MonoBehaviour
{
public float Speed = 4.0f;
//공격력
//이펙트
void Update()
{
//미사일 위쪽방향으로 움직이기
//위의 방향 * 스피드 * 타임
transform.Translate(Vector2.up * Speed * Time.deltaTime);
}
//화면밖으로 나갈경우
private void OnBecameInvisible()
{
//자기 자신 지우기
Destroy(gameObject);
}
}
PlayerController 스크립트에
public GameObject bullet; //총알 추후 4개 배열로 만들 예정
public Transform pos = null;
Update 안에 생성
if(Input.GetKeyDown(KeyCode.Space))
{
//프리팹 위치 방향 넣고 생성
Instantiate(bullet, pos.position, Quaternion.identity);
}
카메라 영역 갖고와서 바깥으로 나가지 못하는 코드 추가
//캐릭터의 월드 좌표를 뷰포트 좌표계로 변환해준다.
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; //좌표를 적용한다.
그렇게 PlayerController 전체 스크립트
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float speed = 5f;
private Vector2 minBounds;
private Vector2 maxBounds;
Animator ani; //애니메이터를 가져올 변수
public GameObject bullet; //총알 추후 4개 배열로 만들 예정
public Transform pos = null;
//아이템
//레이저
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);
}
//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; //좌표를 적용한다.
}
}
2. 적 움직이기 & 미사일
Tag를 Enemy 생성 후 Enemy로 적용하기
미사일이 아래 방향으로 움직이는 코드를 작성할 것이다.
추가로 후에 적이 미사일을 쏘는 것도 이 스크립트에서 구현할 예정이다.
PBullet 에서는 Vector2.up을 사용했는데 Monster 스크립트에는 왜 Vector3를 쓰는 걸까?
사실 관계없고, 상관없다. z값을 추가하느냐 안 하느냐의 차이여서
2D게임은 Vector2를 쓰는 경우가 많다.
using UnityEngine;
public class Monster : MonoBehaviour
{
public float Speed = 3;
//딜레이 1초마다 미사일 발사
public float Delay = 1f;
//미사일
//public Transform ms1;
//public Transform ms2;
//public GameObject bullet;
void Start()
{
}
void Update()
{
//아래 방향으로 움직여라
transform.Translate(Vector3.down * Speed * Time.deltaTime);
}
private void OnBecameInvisible()
{
Destroy(gameObject);
}
}
몬스터 미사일을 추가할 것이다.
누끼가 따지고 sprite의 이미지가 여러개일 경우,
slice를 한 뒤에 모두 선택하고 Hierarchy에 드래그해 애니메이터 생성
생성된 몬스터 미사일의 Inspector 설정을 해준다.
Order in Layer 1
Rigidbody2D 추가 및 Gravity Scale 0
Circle Collider 2D 추가 및 IsTrigger 체크! (사진에는 없음… 잘못 캡쳐)
몬스터 미사일 스크립트
using UnityEngine;
public class MBullet : MonoBehaviour
{
public float Speed = 3f;
void Start()
{
}
void Update()
{
transform.Translate(Vector3.down * Speed * Time.deltaTime);
}
private void OnBecameInvisible()
{
Destroy(gameObject);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Player")
{
//미사일 지우기
Destroy(gameObject);
}
}
}
몬스터가 미사일을 보낼 위치를 추가한다.
아이콘도 바꾸어주고 위치를 배치한다.
이제 Monster 스크립트에서 미사일이 나가도록 수정할 것이다.
Invoke를 활용한다.
using UnityEngine;
public class Monster : MonoBehaviour
{
public float Speed = 3;
//딜레이 1초마다 미사일 발사
public float Delay = 1f;
//미사일
public Transform ms1;
public Transform ms2;
public GameObject bullet;
void Start()
{
//한 번 함수 호출
Invoke("CreateBullet", Delay);
}
void CreateBullet()
{
Instantiate(bullet, ms1.position, Quaternion.identity);
Instantiate(bullet, ms2.position, Quaternion.identity);
//재귀호출
Invoke("CreateBullet", Delay);
}
void Update()
{
//아래 방향으로 움직여라
transform.Translate(Vector3.down * Speed * Time.deltaTime);
}
private void OnBecameInvisible()
{
Destroy(gameObject);
}
}
생성된 스크립트 공간에 미사일 위치와 미사일을 넣는다.
몬스터 Prefab 생성한다.
3. 플레이어와 적의 미사일 충돌
Tag 변경
Box Collider 2D 추가 및 Is Trigger 체크!
Player와 적의 미사일 충돌체크를 위한 Trigger 체크이다.
몬스터가 플레이어 미사일 공격에 없어지는 것 구현을 할 것이다.
PBullet 스크립트에 추가한다.
//충돌처리
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.CompareTag("Enemy"))
{
//몬스터
Destroy(collision.gameObject);
//미사일 삭제
Destroy(gameObject);
}
}
추가된 전체 코드
using UnityEngine;
public class PBullet : MonoBehaviour
{
public float Speed = 4.0f;
//공격력
//이펙트
void Update()
{
//미사일 위쪽방향으로 움직이기
//위의 방향 * 스피드 * 타임
transform.Translate(Vector2.up * Speed * Time.deltaTime);
}
//화면밖으로 나갈경우
private void OnBecameInvisible()
{
//자기 자신 지우기
Destroy(gameObject);
}
//충돌처리
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.CompareTag("Enemy"))
{
//몬스터
Destroy(collision.gameObject);
//미사일 삭제
Destroy(gameObject);
}
}
}
4. 이펙트
이제 미사일이 적이 맞았을 때 이펙트를 소환할 것이다.
sprite 이펙트를 Hierarchy에 옮겨
애니메이터와 애니메이션 클립을 만든 오브젝트를 생성해
프리팹까지 완료.
플레이어 미사일 스크립트 수정한다.
필드 선언
public GameObject effect;
충돌처리에서 이팩트 생성하고
1초 뒤에 지우기 생성
//충돌처리
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.CompareTag("Enemy"))
{
//이펙트 생성
GameObject go = Instantiate(effect, transform.position, Quaternion.identity);
//1초 뒤에 지우기
Destroy(go, 1);
//몬스터
Destroy(collision.gameObject);
//미사일 삭제
Destroy(gameObject);
}
}
5. 몬스터 랜덤 생성
몬스터가 랜덤으로 소환하는 것을 구현할 것이다.
Create Empty를 생성하고 이름을 SpawnManager로 변경한 후
생성될 위치를 조정해 준다.
스폰 스크립트를 작성한다.
using System.Collections;
using UnityEngine;
public class Spawn : MonoBehaviour
{
//x값 ~2 부터 2 사이까지 생성
public float ss = -2; //몬스터 생성 x값 처음
public float es = 2; //몬스터 생성 x값 끝
public float StartTime = 1; //시작
public float SpawnStop = 10; //스폰 끝나는 시간
public GameObject enemy;
bool swi = true;
void Start()
{
StartCoroutine("RandomSpawn");
Invoke("Stop", SpawnStop); //10초 뒤에 끝남
}
//코루틴으로 랜덤하게 생성
IEnumerator RandomSpawn()
{
while(swi)
{
//1초마다
yield return new WaitForSeconds(StartTime);
//x값 랜덤
float x = Random.Range(ss, es);
//x값은 랜덤 y값은 자기자신 값
Vector2 r = new Vector2(x, transform.position.y);
//몬스터 생성
Instantiate(enemy, r, Quaternion.identity);
}
}
void Stop()
{
swi = false;
//두 번째 몬스터 코루틴
}
}
몬스터를 Spawn 스크립트에 넣는다. (몬스터가 랜덤 x값으로 생성된다.)
6. 유도 미사일
일단 초반은 똑같다.
이제 여러 번 하였으니 익숙하다고 느낀다.
sprite 이펙트를 Hierarchy에 옮겨
애니메이터와 애니메이션 클립을 만든 오브젝트를 생성해
Order in Layer 1
Rigidbody 2D - Gravity Scale 0
Circle Collider 2D - 콜라이더 범위 조정
Homing_Hullet 스크립트
플레이어가 있는 곳으로 미사일이 따라가는 스크립트이다.
using UnityEngine;
public class Homing_Bullet : MonoBehaviour
{
public GameObject target; //플레이어
public float Speed = 3f;
Vector2 dir; //방향
Vector2 dirno;
void Start()
{
//플레이어 태그로 찾기
target = GameObject.FindGameObjectWithTag("Player");
//A - B -> A를 바라보는 벡터 플레이어 - 미사일
dir = target.transform.position - transform.position;
//방향벡터만 구하기 단위백터 정규화 노말 1의 크기로 만든다.
dirno = dir.normalized;
}
void Update()
{
transform.Translate(dirno * Speed * Time.deltaTime);
}
//충돌처리
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag == "Player")
{
Destroy(gameObject);
}
}
private void OnBecameInvisible()
{
Destroy(gameObject);
}
}
이 스크립트를 적용할 다른 몬스터를 생성하는 것이다.
원하는 다른 sprite를 쓰고,
전에 있는 Monster 스크립트를 그대로 적용해 준다.
미사일이 나갈 부분을
몬스터의 하위객체로 넣은 다음
ms1, ms2로 위치를 잡아준다.
Monster 스크립트의 public 항목에 들어갈 Ms1과 Ms2는
하위객체로 넣었던 ms1, ms2를 넣어주면 된다.
Bullet은 Homing_Bullet 스크립트 작성했던 것을 넣어주면 된다.
이제 두 번째 몬스터도 랜덤으로 소환되는 스크립트를 만들어야 한다.
이것은 기존의 Spwan 스크립트에 추가할 것이다.
필드를 선언한다.
public GameObject enemy2; //두 번째 몬스터
bool swi2 = true;
코루틴으로 랜덤생성한 것을 복붙 해 변수를 변경해 준다.
using System.Collections;
using UnityEngine;
public class Spawn : MonoBehaviour
{
public float ss = -2; //몬스터 생성 x값 처음
public float es = 2; //몬스터 생성 x값 끝
public float StartTime = 1; //시작
public float SpawnStop = 10; //스폰끝나는시간
public GameObject enemy;
public GameObject enemy2;
bool swi = true;
bool swi2 = true;
void Start()
{
StartCoroutine("RandomSpawn");
Invoke("Stop", SpawnStop);
}
//코루틴으로 랜덤하게 생성하기
IEnumerator RandomSpawn()
{
while (swi)
{
//1초마다
yield return new WaitForSeconds(StartTime);
//x값 랜덤
float x = Random.Range(ss, es);
//x값은 랜덤 y값은 자기자신값
Vector2 r = new Vector2(x, transform.position.y);
//몬스터 생성
Instantiate(enemy, r, Quaternion.identity);
}
}
//코루틴으로 랜덤하게 생성하기
IEnumerator RandomSpawn2()
{
while (swi2)
{
//1초마다
yield return new WaitForSeconds(StartTime + 2);
//x값 랜덤
float x = Random.Range(ss, es);
//x값은 랜덤 y값은 자기자신값
Vector2 r = new Vector2(x, transform.position.y);
//몬스터 생성
Instantiate(enemy2, r, Quaternion.identity);
}
}
void Stop()
{
swi = false;
StopCoroutine("RandomSpawn");
//두번째 몬스터 코루틴
StartCoroutine("RandomSpawn2");
//30초뒤에 2번째 몬스터 호출멈추기
Invoke("Stop2", SpawnStop + 20);
}
void Stop2()
{
swi2 = false;
StopCoroutine("RandomSpawn2");
//보스
}
}
그리고 Enemy2에 두 번째 몬스터 추가하기
이렇게 되면 30초 뒤에 두 번째 몬스터가 호출된다.
오늘은 1945 두 번째 날이었다.
점점 복잡해지긴 하는데 반복 작업이 많아서
이젠 외운 기능들도 있어서 뿌듯하다!
하지만 아직 스크립트가 헷갈려서 문제다.
이것을 어디에 써야 하고, 무엇을 작성하는지..
아직은 역시 코드랑 덜 친해져서 그런가 보다.😢
친근하게 대할 정도로 파이팅..!
'Development > 멋쟁이사자처럼 게임개발 부트캠프' 카테고리의 다른 글
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 17일차 - 1945Game (4) (2) | 2025.03.23 |
---|---|
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 16일차 - 1945Game (3) (0) | 2025.03.21 |
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 14일차 - 1945Game (1) (2) | 2025.03.19 |
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 13일차 - Dragon Flight (2) (2) | 2025.03.19 |
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 13일차 - Dragon Flight (1) (2) | 2025.03.18 |