-
(8) 적 유닛 마무리 & 적 특수 유닛 제작 #1Galaxy Ball/2. 싱글플레이 - 스토리모드 2024. 5. 21. 17:49
이번엔 이동이 가능한 적 유닛과 특수 유닛 4가지를 만들어보도록 하겠다
기본 디자인이다. 센터는 기존 유닛과 동일하게 가져가되 양옆에 다리를 달아주면서 이동기능을 추가했다
using System.Collections; using TMPro; using UnityEngine; public class Enemy6LegControl : MonoBehaviour { public float moveSpeed = 1f; // 이동 속도 public float rotationSpeed = 180f; // 회전 속도 private TextMeshPro textMesh; private Transform parentTransform; private void Start() { parentTransform = transform.parent; // 상위 오브젝트의 Transform을 참조 StartCoroutine(MoveAndRotate()); } private IEnumerator MoveAndRotate() { while (true) { float targetAngle = Random.Range(-70f, 70f); float currentAngle = parentTransform.eulerAngles.z; // 상위 오브젝트의 현재 각도 float rotationTime = Mathf.Abs(targetAngle - currentAngle) / rotationSpeed; float elapsedTime = 0f; while (elapsedTime < rotationTime) { elapsedTime += Time.deltaTime; float angle = Mathf.LerpAngle(currentAngle, targetAngle, elapsedTime / rotationTime); parentTransform.eulerAngles = new Vector3(0, 0, angle); yield return null; } // 이동할 방향 벡터 계산 (바라보고 있는 방향으로 이동) Vector3 moveDirection = -parentTransform.up; // 부모 오브젝트의 위쪽 방향 (바라보는 방향) // 3~4초 동안 이동 float moveTime = Random.Range(3f, 4f); elapsedTime = 0f; while (elapsedTime < moveTime) { elapsedTime += Time.deltaTime; parentTransform.position += moveDirection * moveSpeed * Time.deltaTime; yield return null; } // 1~5초 동안 정지 상태로 대기 float waitTime = Random.Range(1f, 5f); yield return new WaitForSeconds(waitTime); } } private void OnCollisionEnter2D(Collision2D collision) { Enemy6center enemy6Center = GetComponent<Enemy6center>(); if (collision.gameObject.tag == "P1ball" || collision.gameObject.tag == "P2ball" || collision.gameObject.tag == "P1Item" || collision.gameObject.tag == "P2Item") { if (enemy6Center.randomNumber > 0) { enemy6Center.randomNumber--; textMesh.text = enemy6Center.randomNumber.ToString(); } if (enemy6Center.randomNumber <= 0) { Destroy(transform.parent.gameObject); // 부모 오브젝트 삭제 } } } }
1. 이동할 방향을 정하고 3~4초동안 이동
2. 정지한뒤 발사할 방향으로 각도를 회전
3. 1초뒤 총알 발사
4. 1~3번을 반복
오브젝트 Leg에 달린 이동 스크립트이다. 우선 밸런스를 위해 센터가 아니라 양옆에 달린 다리가 맞아도 내구도가 닳도록
설계하였다.
예상은 했지만 너무 어설프게 움직인다.
그렇게 수정의 수정을 거듭하여 최대한 개선시켜보았다
using System.Collections; using TMPro; using UnityEngine; public class Enemy6center : MonoBehaviour { Rigidbody2D rigid; public float increase = 4f; public bool hasExpanded = false; public int randomNumber; private TextMeshPro textMesh; public Enemy1Fire[] enemy1Fires; // 여러 Enemy1Fire 참조를 위한 배열 public int MaxHP; public int MinHP; public float MaxFireTime; public float MinFireTime; private void Start() { rigid = GetComponent<Rigidbody2D>(); GameObject textObject = new GameObject("TextMeshPro"); textObject.transform.parent = transform; textMesh = textObject.AddComponent<TextMeshPro>(); randomNumber = Random.Range(MinHP, MaxHP); textMesh.text = randomNumber.ToString(); textMesh.fontSize = 6; textMesh.alignment = TextAlignmentOptions.Center; textMesh.autoSizeTextContainer = true; textMesh.rectTransform.localPosition = Vector3.zero; textMesh.sortingOrder = 3; enemy1Fires = GetComponentsInChildren<Enemy1Fire>(); // Enemy1Fire 컴포넌트 배열 참조 StartCoroutine(RotateObject()); } private void OnCollisionEnter2D(Collision2D coll) { if (coll.gameObject.tag == "P1ball" || coll.gameObject.tag == "P2ball" || coll.gameObject.tag == "P1Item" || coll.gameObject.tag == "P2Item") { if (randomNumber > 0) { randomNumber--; textMesh.text = randomNumber.ToString(); } if (randomNumber <= 0) { Destroy(transform.parent.gameObject); // 부모 오브젝트 삭제 } } } private IEnumerator RotateObject() { while (true) { // 5초 동안 정지 yield return new WaitForSeconds(Random.Range(MinFireTime, MaxFireTime)); if (enemy1Fires != null) { foreach (var enemy1Fire in enemy1Fires) { enemy1Fire.SpawnBullet(); } } } } }
using System.Collections; using TMPro; using UnityEngine; public class Enemy6LegControl : MonoBehaviour { public float moveSpeed = 1f; // 이동 속도 public float rotationSpeed = 180f; // 회전 속도 private TextMeshPro textMesh; private Transform parentTransform; private void Start() { parentTransform = transform.parent; // 상위 오브젝트의 Transform을 참조 StartCoroutine(MoveAndRotate()); } private IEnumerator MoveAndRotate() { while (true) { float targetAngle = Random.Range(-70f, 70f); float currentAngle = parentTransform.eulerAngles.z; // 상위 오브젝트의 현재 각도 float rotationTime = Mathf.Abs(targetAngle - currentAngle) / rotationSpeed; float elapsedTime = 0f; while (elapsedTime < rotationTime) { elapsedTime += Time.deltaTime; float angle = Mathf.LerpAngle(currentAngle, targetAngle, elapsedTime / rotationTime); parentTransform.eulerAngles = new Vector3(0, 0, angle); yield return null; } // 이동할 방향 벡터 계산 (바라보고 있는 방향으로 이동) Vector3 moveDirection = Random.Range(0, 2) == 0 ? -parentTransform.up : parentTransform.up; // 부모 오브젝트의 위쪽 방향 또는 아래쪽 방향 중 랜덤으로 선택 // 3~4초 동안 이동 float moveTime = Random.Range(1.5f, 2.5f); elapsedTime = 0f; while (elapsedTime < moveTime) { elapsedTime += Time.deltaTime; parentTransform.position += moveDirection * moveSpeed * Time.deltaTime; yield return null; } // 1~5초 동안 정지 상태로 대기 float waitTime = Random.Range(2f, 3f); yield return new WaitForSeconds(waitTime); } } private void OnCollisionEnter2D(Collision2D collision) { Enemy6center enemy6Center = GetComponent<Enemy6center>(); if (collision.gameObject.tag == "P1ball" || collision.gameObject.tag == "P2ball" || collision.gameObject.tag == "P1Item" || collision.gameObject.tag == "P2Item") { if (enemy6Center.randomNumber > 0) { enemy6Center.randomNumber--; textMesh.text = enemy6Center.randomNumber.ToString(); } if (enemy6Center.randomNumber <= 0) { Destroy(transform.parent.gameObject); // 부모 오브젝트 삭제 } } } }
회전을 Leg에서만 담당하도록 하였고, 콜라이더와 충돌했을때 회전도 막아주고, 후진 기능도 추가했다
중간과정이 생략되서 그렇지 이정도면 정말 많이 발전한거다. 딥러닝을 쓰는것도 아니고 코딩인데 이 정도면 만족하련다
이름 : Enemy6
최소/최대 내구도 : 5/8
최소/최대 발사시간 : 4/7
최소/최대 발사속도 : 4/8
이동속도 : 0.8
1회 이동시간 : 1.5~2.5
회전속도 : 180
사용 총알 : Normal Bullet
여기까지가 기본유닛이었고 이 다음부터 나올 유닛은 특수 유닛으로 따로 분류하고 있다
첫번째 특수유닛인 투명유닛. 정확히는 색상이 하늘색이 아니라 백그라운드의 색상에 맞춘것이다
원래 처음에는 단순하게 몸통과 총구부분을 하늘색으로 잡을 생각이었으나, 개발하면서 백그라운드 색상이
어떻게 바뀔지도 모르는거고, 기왕이면 코드로 구현해놓는게 좋은 생각인것 같아 코드를 추가하였다
수정해야할 부분은 크게 2가지이다
1. 적 유닛의 모든 구성 오브젝트들을 BackGround 색상에 맞춰 변화
2. 체력을 보여주는 숫자 비활성화
public bool isHide; ..... if (isHide) { ChangeObjectColor(transform); } .... private void ChangeObjectColor(Transform parentTransform) { if (!isHide) return; // 자기 자신의 색상 변경 SpriteRenderer currentSpriteRenderer = parentTransform.GetComponent<SpriteRenderer>(); if (currentSpriteRenderer != null) { GameObject background = GameObject.Find("BackGround"); if (background != null) { SpriteRenderer backgroundSpriteRenderer = background.GetComponent<SpriteRenderer>(); if (backgroundSpriteRenderer != null) { currentSpriteRenderer.color = backgroundSpriteRenderer.color; } } } // 부모 오브젝트의 자식들을 검사하면서 색상을 변경 foreach (Transform child in parentTransform) { ChangeObjectColor(child); } }
Enemy1Center 스크립트의 수정본
단순히 색상을 변경시켜주는것이 아닌 BackGround라는 오브젝트를 찾아 해당 오브젝트의 색상에 맞춰주는것이다
여기서 주의할 점은 BackGround의 색상이 SpriteRenderer로 지정되어야 한다는것
bool 타입으로 isHide라는 변수를 만들어 주었고,
Start()에서 isHide가 참이라면 ChangeObjectColor라는 메서드를 돌리게 만들어주었다
중요한건 해당 코드는 Enemy1Center, 즉 몸통에 해당하는 오브젝트에 들어간다는것이다
하지만 투명해져야하는것은 몸통뿐만 아니라 총구까지 해당되기에 foreach문으로 자식 오브젝트들까지 한번
돌려 메서드를 실행시키도록 하였다
이제 선택지에 Is Hide 체크란이 추가된것을 확인할 수 있다
이번엔 두번째 수정으로 적 유닛의 체력을 나타내는 숫자도 감춰보도록 하자
우선 한가지 bool 타입 변수를 더 만들어주었고
사실상 체력을 나타내는 코드는 textMesh.text = randomNumber.ToString();
이거 한줄이기에 방금 만든 isShowHP 변수로 이것을 제어할 수 있도록 해주자
이러면 다 끝난게 아닌가 싶지만 한가지 더 남아있었다. 바로 충돌시 1 줄어든 체력을 띄워주는 코드 한줄이 남아있었던것
이것도 변경해주자
이제 결과를 확인해보자
외형이나 기능 자체는 둘다 동일한 스펙이지만 오른쪽은 Is Hide에 체크, Is Show HP에는 체크해제하였다
이름 : SEnemy1
최소/최대 내구도 : 3/5
최소/최대 발사시간 : 4/6
최소/최대 발사속도 : 4/8
사용 총알 : Weak Bullet
(특징 : 투명화 & 내구도 보여주지 않음)
그 다음 특수 유닛은 구상단계에는 없었지만 개발하면서 생각해본 초소화 유닛이다
별다른 차이는 없지만 사이즈가 일반 유닛의 1/10 사이즈라고 생각하면 되겠다
애초에 사이즈만 작아지는거니 유닛 자체에는 특별한 변화가 없지만 유닛에 맞는 총알을 따로 하나 더 만들어야겠다
따로 하나 더 만든 SmallBullet. 사이즈는 NormalBullet과 비교하면 거의 1/5 크기이다
하지만 이렇게 작은 총알을 위해서라면 폰트사이즈를 조절해주어야한다
그래서 이것도 역시 public으로 변수를 따로 하나 만들어 fontsize로 직접 조절할 수 있게 만들어주었다
참고로 일반 총알 폰트사이즈는 '2' 이다
이런 느낌이라 보면 되겠다
그리고 갑자기 든 생각인데 저 작은 유닛에 Is Hide 기능까지 추가시켜준다면 중간보스급의 난이도가 될것같다
이름 : SEnemy2
최소/최대 내구도 : 3/5
최소/최대 발사시간 : 4/6
최소/최대 발사속도 : 4/8
사용 총알 : Small Bullet
(특징 : 초소화)
'Galaxy Ball > 2. 싱글플레이 - 스토리모드' 카테고리의 다른 글
(10) 적 유닛 추가 제작 #3 & 총정리 (0) 2024.05.23 (9) 적 특수 유닛 제작 #2 (0) 2024.05.22 (7) 싱글플레이 아이템 오류 수정 (0) 2024.05.21 (6) 적 유닛 제작 #2 (0) 2024.05.20 (5) 적 유닛 스탯 조절 가능하게 만들기 (0) 2024.05.20