구현 완료
1. 이동구현
2. 이동 애니메이션
3. 카메라 무빙
4. 대각 이동시 원을 그리게 변경해야함
불안전 요소
1. 카메라 이동 속도가 빠를시 생기는 계단현상
2. 벽넘기가 불안정함
현재 완성까지 필요한 요소
1. 엄폐기능
2. 총기 조작 기능 <- 현재 부분
3. 시네마씬을 이용한 게임 씬 제작
- 2024 11 24 01 10 시작 -
이전 글에서 조준과정을 거쳐 Ray 충돌을 이용한 히트스캔 방식을 구현했다
이제 여기에 총기 조준 애니메이션과 장전 기능을 추가하려 한다
언제나 그렇듯, 설계를 대충이라도 하고 간다
가볍게 내가 무엇을 하고자 하는지 설계해 보는것은 중요한 습관중 1개가 될 것 같다
이제 들어가기전 AI에게 한번 검증을 받아보자
이때동안 사용한 코드와, 이 이미지를 주면서 검증을 시켜 보는것이다
이하는 GPT의 이야기이
1. 총기 애니메이션 처리
현재 설계:
- Animator에 isAiming이라는 bool 매개변수를 전달하여 조준 상태에 따라 애니메이션 전환.
검토:
- 이 접근 방식은 괜찮습니다. 특히 Any State로부터의 전환을 설정해 isAiming이 true일 때 aiming으로 이동하고, false일 때 원래 상태로 돌아가는 설계는 적합합니다.
- 하지만 전환 시의 조건(Has Exit Time)을 세밀히 조정하여 애니메이션이 부드럽게 연결되는지 확인해야 합니다.
제안:
- Animator 상태에 조준 중 발사(aimShoot)나 조준 해제 후 이동(shootingIdle → move)와 같은 추가 전환을 고려하면 더 세부적인 애니메이션 처리가 가능합니다.
- 발사 애니메이션을 처리할 때, trigger 매개변수를 사용해 발사 단위 이벤트를 구현하는 것도 유용합니다.
2. 장전 관련 기능
현재 설계:
- _leftBullet 변수를 사용해 탄약을 감소시키고 발사 가능 여부를 체크.
검토:
- 이 구조는 합리적이며 탄약 수를 효과적으로 관리할 수 있습니다.
- 하지만 장전 기능(reload)을 추가적으로 구현해야 실제로 장전 상태를 관리할 수 있습니다.
결론
당신이 설계한 방향은 총기 시스템, 애니메이션, 적중 판정, 장전 로직 등 기본적인 구조를 잘 잡아두고 있습니다. 위의 제안을 반영하면 더욱 완성도 높은 시스템으로 개선할 수 있을 것입니다. 특히, 애니메이션과 장전 로직의 연결성, UI와 피드백 시스템을 추가로 보완하면 사용자 경험이 더욱 풍부해질 것입니다.
라고 한다
일단 GPT는 설계에 동의하는 것 같으니 한번 개발에 들어가보도록 하자
애니메이션은 mixamo에서 적당한걸 가져와서 사용한다
현재 플레이어의 애니메이터와 파라미터 상태이다
여기에 조준 애니메이션을 추가한다
파라미터와 애니메이션을 추가해 주었다
코드를 수정하자
private void Aiming()
{
if (Input.GetMouseButtonDown(1))
{
isAiming = !isAiming;
}
//isAiming = Input.GetMouseButton(1);
_aimIndicator.SetActive(isAiming);
if (isAiming)
{
cameraOffset = new Vector3(0f, 1.5f, -2f);
shoot();
}
else
{
cameraOffset = new Vector3(0f, 2f, -3f);
}
}
총기 조준과 관련되어 있는 메서드이다 여기에 앞에서 설계한 구조를 적용한다
_animator.SetBool("IsAiming", isAiming);
이 한줄을 넣고 테스트 한다
캐릭터가 공중에 뜨는 현상이 있다
파라미터는 정상 작동하는걸로 보아서는 코드에 문제가 아니라, 애니메이션이 이상한 것 같다
애니메이션이 바닥을 뚫고 들어갔다....
수정하기 귀찮으니 대충 유니티짱에 있는 다른 애니메이션을 가져와서 테스트한다
ExitTime에 불안정이 보이지만 일단 작동한다
이는 나중에 더 정확한 애니메이션을 얻으면 수정하는게 좋아보인다
이제 2번인 장전관련 기능을 구현한다
앞서 설계한 구조에
GPT가 조언한 장전중 예외처리를 반영해서 코드를 작성한다
여기에 장전을 추가할려면 bool타입 변수가 하나 더 필요하니 추가한다
public bool isAiming = false;
public bool isReload = false;
이제 이에 맞춰 스크립트를 작성한다
private int _leftBullet = 30;
장탄수를 계산할 변수값도 추가한다
if (Input.GetMouseButton(0))
{
if (Physics.Raycast(muzzlePosition, directionToTarget, out RaycastHit enemy, maxDistance))
{
if (enemy.collider.CompareTag("Enemy"))
{
// 이후 코딩 상황 맞춰서 처리
for (int i = 0; i < 61; i++)
{
if (i == 60) Debug.Log("적 맞춤");
}
}
}
}
현재 히트스캔을 하는 방식이다. 이때 for문으로 초당 발사수를 계산하는데 이는 불안정한 방식이다
원래는 Time.deltatime을 += 하는 방식을 사용해야 한다
근데, 딱히 생각나는 설계가 없으니 GPT에게 조언을 구해본다
private float fireRate = 1f / 3f; // 초당 3발, 한 발마다 0.33초 간격
private float lastFireTime = 0f;
private void shoot()
{
if (Input.GetMouseButton(0) && Time.time >= lastFireTime + fireRate)
{
lastFireTime = Time.time; // 마지막 발사 시간 갱신
// 발사 로직 실행
if (Physics.Raycast(muzzlePosition, directionToTarget, out RaycastHit enemy, maxDistance))
{
if (enemy.collider.CompareTag("Enemy"))
{
Debug.Log("적 맞춤: " + enemy.collider.gameObject.name);
// 적 체력 감소 등 처리
EnemyHealth enemyHealth = enemy.collider.GetComponent<EnemyHealth>();
if (enemyHealth != null)
{
enemyHealth.TakeDamage(10); // 예: 적에게 10의 데미지
}
}
}
// 시각적 디버그 Ray
Debug.DrawRay(muzzlePosition, directionToTarget * maxDistance, Color.red, 0.5f);
}
}
동작 원리:
- fireRate는 초당 발사 간격을 설정 (1 / 3 → 0.33초).
- lastFireTime을 통해 마지막 발사 시간을 기록.
- Time.time을 활용해 현재 시간이 마지막 발사 시간 + 발사 간격보다 크거나 같을 때만 발사
2. Coroutine을 사용한 발사 제어
Coroutine을 사용하여 초당 3발의 발사를 구현할 수 있습니다. 이는 버튼을 누르고 있는 동안 일정한 간격으로 발사하도록 만듭니다.
private float fireRate = 1f / 3f; // 초당 3발
private bool isShooting = false;
private void Update()
{
if (Input.GetMouseButtonDown(0) && !isShooting)
{
// 발사 시작
StartCoroutine(ShootContinuously());
}
else if (Input.GetMouseButtonUp(0))
{
// 발사 중지
isShooting = false;
}
}
private IEnumerator ShootContinuously()
{
isShooting = true;
while (isShooting)
{
// 발사 로직
if (Physics.Raycast(muzzlePosition, directionToTarget, out RaycastHit enemy, maxDistance))
{
if (enemy.collider.CompareTag("Enemy"))
{
Debug.Log("적 맞춤: " + enemy.collider.gameObject.name);
// 적 체력 감소 등 처리
EnemyHealth enemyHealth = enemy.collider.GetComponent<EnemyHealth>();
if (enemyHealth != null)
{
enemyHealth.TakeDamage(10); // 예: 적에게 10의 데미지
}
}
}
// 디버그 Ray
Debug.DrawRay(muzzlePosition, directionToTarget * maxDistance, Color.red, 0.5f);
// 발사 간격 대기
yield return new WaitForSeconds(fireRate);
}
}
동작 원리:
- Input.GetMouseButtonDown(0)으로 발사 시작을 감지하고 Coroutine을 실행.
- while (isShooting) 루프를 통해 버튼을 누르고 있는 동안 발사.
- yield return new WaitForSeconds(fireRate)로 발사 간격을 설정.
- Input.GetMouseButtonUp(0)으로 발사 종료.
이러한 방법을 제시한다
나는 1번의 Time.deltatime를 사용하는 방식을 선호하니 이 설계를 참고해서 작성하겠다
if (Input.GetMouseButton(0) && Time.time >= lastFireTime + fireRate)
{
lastFireTime = Time.time;
if (Physics.Raycast(muzzlePosition, directionToTarget, out RaycastHit enemy, maxDistance))
{
if (enemy.collider.CompareTag("Enemy"))
{
Debug.Log("적 맞춤");
}
}
}
최종적으로 이러한 형태가 되었다
가 나타내는것은
현재 시간이 마지막으로 발사된 시간 + 지정한 시간보다 클시 추가적인 발사가 실행되게 하는것이다
로그를 보면 정상적으로 발사되고 있는것을 볼 수 있다
이제 여기에 장탄수 시스템을 추가한다
이때 추가하는것은
1. if문에 장탄수가 남아있는가?
2. 장전중인가?
이다
우선 장탄수부터 구현한다
if (Input.GetMouseButton(0) && Time.time >= lastFireTime + fireRate && _leftBullet > 0 && !isReload)
{
lastFireTime = Time.time;
--_leftBullet;
if (Physics.Raycast(muzzlePosition, directionToTarget, out RaycastHit enemy, maxDistance))
{
if (enemy.collider.CompareTag("Enemy"))
{
Debug.Log("적 맞춤");
}
}
}
else if (Input.GetMouseButton(0) && _leftBullet <= 0)
{
Debug.Log("No left bullet");
}
위에서 설계한데로 --연산을 사용한 뒤, if문에 조건을 걸어준다
정상적으로 장탄수가 부족할때 log가 뜨는것을 확인할 수 있다
이제 장전 시스템을 추가한다
private void ReloadSystem()
{
_animator.SetBool("IsReload", isReload);
if (Input.GetKeyDown(KeyCode.R))
{
StartCoroutine(ReLoadStart());
Debug.Log("left bullet : " + _leftBullet);
}
}
private IEnumerator ReLoadStart()
{
isReload = true;
yield return new WaitForSeconds (2f);
Debug.Log("Reloaded");
_leftBullet = 30;
isReload = false;
}
이렇게 일반함수와 코루틴을 사용해서 구현한다
animator도 이에 맞추어 수정해준다
대충 의도한데로 작동하기는 하는데, 문제가 있다
any에서 애니메이션을 재생하다보니, 무한반복되는 문제가 있다
그냥 any에서 나오는 transfer들을 전부 일반 transfer로 정리했다
거미줄이 되기는 하는데, 가장 정확한 방법이다
이상없이 작동하는 것을 확인할 수 있다
나중에 상반신 애니메이션만 따로 작동하게 하면 좀더 자연스러워 질것이다.
'개발일지 > 게임개발' 카테고리의 다른 글
유니티 3D 게임 개발 - 체력, 총알 수 UI 제작 (1) | 2024.12.04 |
---|---|
유니티 3D 게임 개발 - VFX 넣기 (0) | 2024.11.26 |
유니티 3D 게임 개발 - TPS 조작 개발(Part 1 대각 이동) (0) | 2024.11.23 |
[유니티 게임 개발] 동방을 걷다 프로젝트 15 - 레이무 상애 엔딩 2024 05 30(PM 08:30) (0) | 2024.05.31 |
[유니티 게임 개발] 동방을 걷다 프로젝트 14 - 레이무 상애 엔딩 2024 05 30(AM 01:30) (0) | 2024.05.30 |