[ 목차 ]
23일 차 오늘의 배운 내용은 Katana ZERO 4번째!!!
이번 포스팅에서는 벽 먼지 생성과 적을 배치하고
적이 플레이어를 감지해서 자동으로 공격하는 것을 실습한다.
드디어 막바지로 온다!
이전 포스팅은 아래 링크를 첨부해 두었으니, 필요하다면 참고하길 바랍니다!
Katana ZERO (1)
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 20일차 - Katana ZERO (1)
[ 목차 ] 1945에 이어서 다른 게임을 실습해 보는 가진다. 횡스크롤 종류의 KATANA ZERO 게임을 구현해 볼 것이다.2D 횡스크롤은 엄청나게 많은 종류이면서 게임의 기본 중 기본이라고도 볼 수 있
gang-design.com
Katana ZERO (2)
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 21일차 - Katana ZERO (2)
[ 목차 ] 지난 포스팅에서 Katana ZERO (1)에 이어서 2번째 포스팅이다. 이번 포스팅은 캐릭터 애니메이션에 추가적으로 이어서 하는 것과디테일과 이펙트를 위주로 진행한다. 전의 포스팅을 보고
gang-design.com
Katana ZERO (3)
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 22일차 - Katana ZERO (3)
[ 목차 ] 오늘의 포스팅은 Katana ZERO 3번째 실습이다. 기존에 배웠던 카메라 설정과배경과 캐릭터 추가 설정을 계속 진행해보는 것이다. 이전 포스팅은 아래 첨부로 걸어두었으니 참고 바랍니
gang-design.com
1. 벽 먼지 생성
떨어질 때 벽에 붙어도 떨어지는 모션만 나오는 버그에 대해서 수정
private void FixedUpdate()
{
Debug.DrawRay(pRig2D.position, Vector3.down, new Color(0, 1, 0));
//레이캐스트로 땅체크
RaycastHit2D rayHit = Physics2D.Raycast(pRig2D.position, Vector3.down, 1, LayerMask.GetMask("Ground"));
if (pRig2D.linearVelocityY < 0)
{
if (rayHit.collider != null)
{
if (rayHit.distance < 0.7f)
{
pAnimator.SetBool("Jump", false);
}
}
else
{
//떨어지고 있다.
if(!isWall) //벽이 아닐 때
{
//그냥 떨어지는 중 -> 점프 상태 유지
pAnimator.SetBool("Jump", true);
}
else
{
//벽티기 -> 벽타기 상태 유지
pAnimator.SetBool("Grab", true);
}
}
}
}
이제 벽을 탔을 때에도 이펙트가 있으면 디테일해지기 때문에 해당 이미지를 추가한다.
해당 이미지의 애니메이션을 Hierarchy에 옮겨 애니메이션 클립을 만든 뒤에, 스크립트를 작성할 것이다.
Player 스크립트로 이동한다.
벽 먼지에 대한 게임오브젝트 변수를 작성한다.
//벽먼지
public GameObject walldust;
점프 먼지 시에 이펙트를 넣는 것을 추가한다.
벽인지 아닌지를 구분하는 if 조건문을 추가해 벽이 아니면 점프 먼지 이펙트가 나오고,
벽이면 벽 먼지 이펙트를 생성하는 것으로 교체한다.
//점프 먼지 이펙트
public void JumpDust()
{
if(!isWall) //벽이 아닐 때
{
Instantiate(Jdust, transform.position, Quaternion.identity);
}
else //벽일 때
{
//벽 먼지
Instantiate(walldust, transform.position, Quaternion.identity);
}
}
이제 플레이어 속성에서 해당 프리팹을 넣어준다.
Update() 메서드 안에 벽점프 먼지 생성에 관한 코드를 추가한다.
//벽점프 먼지
GameObject go = Instantiate(walldust, transform.position + new Vector3(0.8f * isRight, 0, 0), Quaternion.identity);
go.GetComponent<SpriteRenderer>().flipX = sp.flipX;
이것을 캐릭터가 현재 벽에 붙어있는 상태인지 확인하는 if(isWall) 조건문안에 포함한다.
if(isWall)
{
isWallJump = false;
//벽점프상태
pRig2D.linearVelocity = new Vector2(pRig2D.linearVelocityX, pRig2D.linearVelocityY * slidingSpeed);
//벽을 잡고있는 상태에서 점프
if(Input.GetKeyDown(KeyCode.W))
{
isWallJump = true;
//벽점프 먼지
GameObject go = Instantiate(walldust, transform.position + new Vector3(0.8f * isRight, 0, 0), Quaternion.identity);
go.GetComponent<SpriteRenderer>().flipX = sp.flipX;
Invoke("FreezeX", 0.3f);
//물리
pRig2D.linearVelocity = new Vector2(-isRight * wallJumpPower, 0.9f * wallJumpPower);
sp.flipX = sp.flipX == false ? true : false;
isRight = -isRight;
}
}
2. 적 배치
적의 이미지를 가져와 배치한다.
idle 상태의 여러 이미지로 애니메이션 클립과 애니메이터를 만들어 적 오브젝트에 넣어준다.
그리고 Box Collider2D도 추가해 적의 이미지와 맞추어준다.
3. 적의 미사일 생성
적의 미사일 코드를 생성하겠다.
EnemyMissile 이름의 스크립트를 작성한다.
이 코드는 미사일 오브젝트에 붙여줄 예정이다.
변수를 선언한다.
public float speed = 5f; //미사일 속도
public float lifeTime = 3f; //미사일 생존 시간
public int damage = 10; //미사일 데미지
public Vector2 direction; //미사일 이동 방향
게임을 시작할 때 일정 시간 후 미사일을 제거하는 코드를 Start() 함수에 포함한다.
이때 lifeTime 변수를 할당 받는다.
void Start()
{
Destroy(gameObject, lifeTime); //일정 시간 후 미사일 제거
}
외부에서 방향을 지정할 때 사용한다.
normalized 정규화를 사용해서 방향만 추출한다. (속도는 일정하게)
public void SetDirection(Vector2 dir)
{
direction = dir.normalized;
}
매 프레임마다 미사일을 direction 방향으로 이동한다.
void Update()
{
transform.Translate(direction * speed * Time.deltaTime);
}
충돌 관련 코드를 작성한다.
Player 태그인 오브젝트와 부딪치면 미사일이 삭제된다.
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
// 여기에 플레이어 데미지 로직 추가
Destroy(gameObject);
}
}
그렇게 된 EnemyMissile 스크립트 전체 코드이다.
using UnityEngine;
public class EnemyMissile : MonoBehaviour
{
public float speed = 5f; //미사일 속도
public float lifeTime = 3f; //미사일 생존 시간
public int damage = 10; //미사일 데미지
public Vector2 direction; //미사일 이동 방향
void Start()
{
Destroy(gameObject, lifeTime); //일정 시간 후 미사일 제거
}
public void SetDirection(Vector2 dir)
{
direction = dir.normalized;
}
void Update()
{
transform.Translate(direction * speed * Time.deltaTime);
}
private void OnTriggerEnter2D(Collider2D other)
{
if(other.CompareTag("Player"))
{
//여기에 플레이어 데미지 로직 추가
Destroy(gameObject);
}
}
}
이제 미사일 오브젝트를 생성한다.
해당 미사일 이미지를 가져와 애니메이션 클립과 애니메이터를 만든다.
Animator와 만들어두었던 Enemy Missile 스크립트, Box Collider 2D를 적용한다.
여기에서 Is Trigger 체크해둔다.
그 미사일 오브젝트는 프리팹을 만들고 Hierachy에서는 삭제해 둔다.
4. 적 공격 AI
적이 일정 거리 내에 플레이어를 감지하면, 일정 간격으로 미사일을 발사하는 스크립트를 작성한다.
이제 적의 캐릭터 속성에 관한 것과 참조 컴포넌트를 할 변수를 선언한다.
[Header("적 캐릭터 속성")]
public float detectionRange = 10f; // 감지 거리
public float shootingInterval = 2f; // 미사일 간격
public GameObject missilePrefab; // 쏠 미사일 프리팹
[Header("참조 컴포넌트")]
public Transform firePoint; // 미사일이 나갈 위치
private Transform player; // 플레이어 위치 저장용
private float shootTimer; // 쿨타임 타이머
private SpriteRenderer spriteRenderer; // 방향 전환용 (좌우)
초기 설정으로 player 태그를 찾아서 추적할 오브젝트를 설정한다.
방향전환을 위한 SpriteRenderer
쿨타임 타이머 초기화이다.
void Start()
{
player = GameObject.FindGameObjectWithTag("Player").transform;
spriteRenderer = GetComponent<SpriteRenderer>();
shootTimer = shootingInterval;
}
Update() 메서드에서는
플레이어가 존재하는지 체크한 뒤에,
Distance로 현재 위치와 플레이어 위치 거리 계산을 한다.
플레이어 방향으로 이미지 회전을 적용하고,
쿨타임이 감소되면 자동으로 Shoot() 메서드 호출해 발사하는 것이다.
void Update()
{
if (player == null) return; //플레이어가 없으면 실행하지 않음
//플레이어와의 거리 계산
float distanceToPlayer = Vector2.Distance(transform.position, player.position);
if(distanceToPlayer <= detectionRange)
{
//플레이어 방향으로 스프라이트 회전
spriteRenderer.flipX = (player.position.x < transform.position.x);
//미사일 발사 로직
shootTimer -= Time.deltaTime; //타이머 감소
if(shootTimer <= 0)
{
Shoot(); //미사일 발사
shootTimer = shootingInterval; //타이머 리셋
}
}
}
미사일 발사 함수인 Shoot() 메서드이다.
플레이어를 향한 방향으로 일정한 속도를 유지하기 위한 정규화 normalized 사용한다.
미사일도 마찬가지로 방향을 바꾸는 함수를 적용한다.
void Shoot()
{
GameObject missile = Instantiate(missilePrefab, firePoint.position, Quaternion.identity);
Vector2 direction = (player.position - firePoint.position).normalized;
missile.GetComponent<EnemyMissile>().SetDirection(direction);
missile.GetComponent<SpriteRenderer>().flipX = (player.position.x < transform.position.x);
}
디버깅용 기즈모 함수이다.
게임 플레이에는 영향이 없으며, 개발을 위해 알아보기 쉽게 작성하는 것이다.
씬에서 적을 클릭하면 감지 범위를 원으로 표시하는 것이다.
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, detectionRange);
}
ShootingEnemy 전체 스크립트이다.
using UnityEngine;
public class ShootingEnemy : MonoBehaviour
{
[Header("적 캐릭터 속성")]
public float detectionRange = 10f; //플레이어를 감지할 수있는 최대 거리
public float shootingInterval = 2f; //미사일 발사 사이의 대기 시간
public GameObject missilePrefab; //발사할 미사일 프리팹
[Header("참조 컴포넌트")]
public Transform firePoint; //미사일이 발사될 위치
private Transform player; //플레이어의 위치 정보
private float shootTimer; //발사 타이머
private SpriteRenderer spriteRenderer; //스프라이트 방향 전환용
void Start()
{
//필요한 컴포넌트 초기화
player = GameObject.FindGameObjectWithTag("Player").transform;
spriteRenderer = GetComponent<SpriteRenderer>();
shootTimer = shootingInterval; //타이머 초기화
}
void Update()
{
if (player == null) return; //플레이어가 없으면 실행하지 않음
//플레이어와의 거리 계산
float distanceToPlayer = Vector2.Distance(transform.position, player.position);
if(distanceToPlayer <= detectionRange)
{
//플레이어 방향으로 스프라이트 회전
spriteRenderer.flipX = (player.position.x < transform.position.x);
//미사일 발사 로직
shootTimer -= Time.deltaTime; //타이머 감소
if(shootTimer <= 0)
{
Shoot(); //미사일 발사
shootTimer = shootingInterval; //타이머 리셋
}
}
}
//미사일 발사 함수
void Shoot()
{
//미사일 생성
GameObject missile = Instantiate(missilePrefab, firePoint.position, Quaternion.identity);
//플레이어 방향으로 발사 방향 설정
Vector2 direction = (player.position - firePoint.position).normalized;
missile.GetComponent<EnemyMissile>().SetDirection(direction);
missile.GetComponent<SpriteRenderer>().flipX = (player.position.x < transform.position.x);
}
//디버깅용 기즈모
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, detectionRange);
}
}
이제 몬스터 오브젝트에 Shooting Enemy 스크립트를 추가한다.
미사일 프리팹을 넣어주고,
firepoint (미사일이 나갈 위치)를 플레이어의 자식으로 만들어준 뒤에 스크립트에 참조한다.

이렇게 KatanaZERO를 마친다!!!!
상당히 기능이 많아서 그런가 전의 실습보단 더 힘들었던 기억이 남아있다.
그렇지만 횡스크롤의 재미를 느껴본 것 같다.
액션이 많으면 많을수록 힘들어진다는 것도 알았지만..
기억상으로 반절이상이 캐릭터 애니메이션이었던 것 같다..
만일에 이것을 직접 디자인을 했다면 얼마나 아찔했을까?!.. 라는 생각도 들긴 했다.
아무튼 4번째 포스팅으로 KatanaZERO를 끝냈다!
'Development > 멋쟁이사자처럼 게임개발 부트캠프' 카테고리의 다른 글
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 24일차 - URP 2D Light (0) | 2025.04.12 |
---|---|
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 23일차 - 유니티 게임 수학 & 물리 (0) | 2025.04.08 |
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 22일차 - Katana ZERO (3) (4) | 2025.04.07 |
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 21일차 - Katana ZERO (2) (0) | 2025.04.03 |
[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 20일차 - Katana ZERO (1) (0) | 2025.04.03 |