-
(21) 아이템 #아이템 밸런스 조절 & 기능구현Galaxy Ball/1. 멀티플레이 - 대전모드 2024. 4. 11. 02:33
이제 아이템 아이콘을 클릭하면 해당 아이콘과 일치하는 프리팹이 다음 발사구체로 생성 되는것까지 구현하였다
그전에 간단한 수정 하나만 하고 넘어가자
한가지 문제가 생겼다. 위 동영상처럼 아이템을 한번 사용하면 다음차례에도, 그 다음 차례에도
계속 해당 아이템만 생성된다는것. (BallController 스크립트는 빼놓은 상태라 날아가진 않는다)
if (fireitem != null) { Instantiate(fireitem, clickPosition, Quaternion.identity); P1FireMode = false; P2FireMode = true; isDragging = true; P1firezone.gameObject.SetActive(false); P2firezone.gameObject.SetActive(true); P1Itemsave.gameObject.SetActive(false); P2Itemsave.gameObject.SetActive(true); // 추가 fireitem = P1ballPrefab; break; }
그래서 fireitem을 얻어 생성한다 하더라도 무조건 그 뒤엔 각자의 기본구체로 fireitem을 초기화 해주었더니
딱 한번씩만 아이템이 사용되고 그 다음부터는 다시 기본 구체로 돌아오는것을 확인할 수 있었다
<밸런스 조절>
그리고 밸런스 조절을 하려한다
떠오르는 아이디어들은 많았지만, 이 11개의 아이템들을 전부 집어넣자니 너무 게임이 산만해진다는 생각이 들었다
이 게임의 특징은 심플하고 복잡한 설명없이도 곧바로 즐길수 있게 하자는게 주 목표였는데
아이템만 11가지가 들어가게 되면 일일이 해줘야하는 설명에, 중간중간에 뭐가뭔지 헷갈리기도 쉽고
내 개인적인 실력부족도 있기 때문에 눈물을 머금고
이 3가지의 아이템은 최종 제거하기로 했다. 물론 후에 다른 아이템들도 더 제거될수도, 더 추가될수 있겠지만
우선 이 3가지는 제외하도록 하겠다
+
추가로 랜덤스킬까지 제거하겠다. 디자인이나 설정은 맘에 들지만 너무 게임이 난해해진다
----------------------------------------------------------------------------------------------
이제 정말 본격적인 아이템 기능 구현을 시작해보자.
using System.Collections; using TMPro; using UnityEngine; public class BallController : MonoBehaviour { Rigidbody2D rigid; Vector2 lastVelocity; float deceleration = 2f; public float increase = 4f; private bool iscolliding = false; public bool hasExpanded = false; private bool isStopped = false; private int randomNumber; private TextMeshPro textMesh; private bool hasBeenReleased = false; public AudioSource HitSound; public AudioSource SwellSound; private void Start() { rigid = GetComponent<Rigidbody2D>(); GameObject textObject = new GameObject("TextMeshPro"); textObject.transform.parent = transform; textMesh = textObject.AddComponent<TextMeshPro>(); randomNumber = Random.Range(1, 6); textMesh.text = randomNumber.ToString(); textMesh.fontSize = 4; textMesh.alignment = TextAlignmentOptions.Center; textMesh.autoSizeTextContainer = true; textMesh.rectTransform.localPosition = Vector3.zero; textMesh.sortingOrder = 1; } private void Update() { // 최초 클릭 이후에만 힘이 가해지도록 설정 if (Input.GetMouseButtonUp(0) && rigid != null && !hasBeenReleased) { rigid.velocity = GameManager.shotDirection * GameManager.shotDistance; // GameManager에서 값 가져와서 구체 발사 hasBeenReleased = true; // 최초 클릭이 되었음을 표시 } Move(); expand(); } void Move() { if (rigid == null || isStopped) return; lastVelocity = rigid.velocity; rigid.velocity -= rigid.velocity.normalized * deceleration * Time.deltaTime; if (rigid.velocity.magnitude <= 0.01f && hasExpanded) { isStopped = true; StartCoroutine(DestroyRigidbodyDelayed()); } } void expand() { if (rigid == null || iscolliding) return; if (rigid.velocity.magnitude > 0.01f) return; if (Input.GetMouseButton(0)) return; if (!hasExpanded) { SwellSound.Play(); } transform.localScale += Vector3.one * increase * Time.deltaTime; hasExpanded = true; } private void OnCollisionEnter2D(Collision2D coll) { if (!hasExpanded) { HitSound.Play(); } if ((coll.gameObject.tag == "P1ball" || coll.gameObject.tag == "P2ball") && rigid == null) { if (randomNumber > 0) { randomNumber--; textMesh.text = randomNumber.ToString(); } if (randomNumber <= 0) { Destroy(gameObject); } } if (coll.contacts != null && coll.contacts.Length > 0) { Vector2 dir = Vector2.Reflect(lastVelocity.normalized, coll.contacts[0].normal); if (rigid != null) rigid.velocity = dir * Mathf.Max(lastVelocity.magnitude, 0f); // 감속하지 않고 반사만 진행 } this.iscolliding = true; } private void OnCollisionExit2D(Collision2D collision) { this.iscolliding = false; } IEnumerator DestroyRigidbodyDelayed() { yield return new WaitForSeconds(0.8f); if (rigid != null) Destroy(rigid); } }
아이템의 기능구현은 기본적으로 P1,2ballPrefab 을 관리하는 BallController 스크립트를 각각 기능에 맞게 수정하여
각각의 아이템에 부착시켜줄 생각이다. 기본적으로 이동,반사,확장까지는 같은 기능을 공유하기 때문이다
우선 가장 간단한 난이도인 Durability 아이템부터 기능구현을 해보겠다
<Durability>
내구도를 30으로 고정시켜준뒤 생성하는 아이
private void Start() { rigid = GetComponent<Rigidbody2D>(); GameObject textObject = new GameObject("TextMeshPro"); textObject.transform.parent = transform; textMesh = textObject.AddComponent<TextMeshPro>(); //랜덤값이 아닌 30을 집어넣음 randomNumber = 30; textMesh.text = randomNumber.ToString(); textMesh.fontSize = 4; textMesh.alignment = TextAlignmentOptions.Center; textMesh.autoSizeTextContainer = true; textMesh.rectTransform.localPosition = Vector3.zero; textMesh.sortingOrder = 1; }
딱 한줄만 수정하면 된다. 원래는 1~5사이 들어가는 랜덤 내구도를 30으로 고정시켜주기만 하면 된다
그리고 프리팹 이미지를 아무런 텍스트가 없도록 수정해준뒤 프리팹안에 스크립트를 부착해준다
이제 적용하는 원리를 알았으니 이제 모든 아이템에 수정된 스크립트만 기재하겠다
<BlackHole>
닿는 즉시, 본인에게 닿는 모든 구체들을 파괴시키는 아이템
밸런스를 위해 10~25초 사이 시간안에 자동으로 삭제되도록 설계했다
private void OnCollisionEnter2D(Collision2D coll) { if (!hasExpanded) { HitSound.Play(); } if ((coll.gameObject.tag == "P1ball" || coll.gameObject.tag == "P2ball") && rigid == null) { Destroy(coll.gameObject); }
핵심 기능은 충돌이 일어났을때 내구도가 닳는것이 아닌 충돌한 구체(coll.gameobject)를 파괴시킨다는 것이다
private void Start() { rigid = GetComponent<Rigidbody2D>(); StartCoroutine(DestroyObjectDelayed(Random.Range(10f, 25f))); } ........ IEnumerator DestroyObjectDelayed(float delayTime) { yield return new WaitForSeconds(delayTime); Destroy(gameObject); }
한가지 밸런스 기능을 추가해 10~25초 사이 랜덤한 시간안에 알아서 아이템이 파괴되도록 구현하였다
<Fasten>
쉽게 말해 고정구체를 직접 발사하게 해주는 아이템
블랙홀과 똑같이 충돌에 영향을 받지 않지만 차이점이 있다면
블랙홀은 충돌하는 물체를 삭제시키는 대신 10~25초 사이 자동삭제가 되지만
고정구체는 충돌하는 물체를 팅겨내기만 하는 대신 자동삭제가 되지 않는다
유일하게 고정구체가 삭제되는 조건은 후에 나올 무적아이템과 충돌했을때만 가능하다
private void OnCollisionEnter2D(Collision2D coll) { if (!hasExpanded) { HitSound.Play(); } if ((coll.gameObject.tag == "Item_Invincible") && rigid == null) { Destroy(gameObject); }
이것 역시 간단하다. 파괴 조건을 "무적 아이템의 태그가 달린 구체와의 충돌"로 걸어두면 된다
물론 블랙홀과 마찬가지로 텍스트 기능들은 전부 제외시켰다
<Force>
기본구체와 똑같지만 다른점이라면 내구도가 충돌횟수가 아닌, 충돌세기로 기준을 잡는다는것
private void Start() { rigid = GetComponent<Rigidbody2D>(); GameObject textObject = new GameObject("TextMeshPro"); textObject.transform.parent = transform; textMesh = textObject.AddComponent<TextMeshPro>(); //내구도는 1~5까지가 아닌 10.0~30.0 number = Random.Range(10f,30f); textMesh.text = number.ToString(); // 소수점까지 출력해야하므로 폰트 사이즈 조절 textMesh.fontSize = 2.5f; textMesh.alignment = TextAlignmentOptions.Center; textMesh.autoSizeTextContainer = true; textMesh.rectTransform.localPosition = Vector3.zero; textMesh.sortingOrder = 1; //소수점을 한자리까지만 출력시켜주는 메서드 UpdateText(); }
if ((coll.gameObject.tag == "P1ball" || coll.gameObject.tag == "P2ball") && rigid == null) { if (number > 0) { number -= coll.relativeVelocity.magnitude; // 부딪힌 세기만큼 값을 감소시킴 //textMesh.text = number.ToString(); UpdateText(); } if (number <= 0) { Destroy(gameObject); } }
private void UpdateText() { // 숫자를 소수점 한 자리까지 문자열로 변환하여 출력 textMesh.text = number.ToString("F1"); }
코드에 대한 설명들은 전부 주석에 달아두었다. 특별한 점이라면 자동으로 5~6자리까지 소수점 밑자리 출력을 하여
화면을 잡아먹는 현상을 막기위해 UpdateText라는 새로운 메서드로 한자리까지만 출력되도록 잡았다는것이다
<Random_number>
랜덤 내구도. 1~30까지의 랜덤 내구도로 남은 내구도를 보여주지 않는다는것이 특징이다
private void Start() { rigid = GetComponent<Rigidbody2D>(); randomNumber = Random.Range(1, 30); }
아주 간단하다. 내구도를 1~30사이 랜덤으로 잡은뒤 이 내구도를 출력해주지만 않으면 된다
<Reduction>
충돌할때마다 구체가 점점 작아진다. 일정 크기 이상으로 줄어든다면 파괴된다
private void OnCollisionEnter2D(Collision2D coll) { if (!hasExpanded) { HitSound.Play(); } if ((coll.gameObject.tag == "P1ball" || coll.gameObject.tag == "P2ball") && rigid == null) { Shrink(); }
구체가 충돌할때마다 내구도가 줄어드는게 아닌 Shrink라는 메서드를 호출한다
private void Shrink() { Vector3 currentScale = transform.localScale; currentScale.x -= 0.5f; currentScale.y -= 0.5f; currentScale.x = Mathf.Max(currentScale.x, 0.4f); currentScale.y = Mathf.Max(currentScale.y, 0.4f); transform.localScale = currentScale; if (currentScale.x <= 0.4f && currentScale.y <= 0.4f) { Destroy(gameObject); } }
그리고 이 Shrink라는 메서드에서는 구체의 크기를 일정하게 줄여주고, 일정크기 이상 작아지면
구체를 삭제시키는 역할을 수행중이
<Invincible>
무적 아이템. 정확히 고정구체의 반대개념이라 생각하면 된다
정지하기전, 이 구체와 닿은 모든 오브젝트들은 내구도와 상관없이 전부 삭제된다
원래는 닿는 오브젝트 족족 전부 삭제시켜버릴 생각이었으나 너무 밸런스적인 부분에서 어긋난것같아
어떠한 오브젝트든 상관없이 삭제시켜버리는것까진 맞지만, 오브젝트 파괴와 함께 본인도 함께
파괴되는것으로 기능을 수정하였다
private void OnCollisionEnter2D(Collision2D coll) { if (!hasExpanded) { HitSound.Play(); } if ((coll.gameObject.tag == "P1ball" || coll.gameObject.tag == "P2ball")) { Destroy(coll.gameObject); Destroy(gameObject); }
콜라이더와 닿는 순간 충돌한 오브젝트와 본인 오브젝트까지 동시에 삭제시켜버리면 된다
구정구체 아이템에게 대항할 수 있는 유일한 아이템이라고 봐도 무방하다
현재 코드에는 임시로 태그 조건을 P1ball과 P2ball만 달았지만 다음글에서 전체적인 수정을 하도록 하겠다
이로써 7가지 아이템의 모든 기능구현을 마쳤다
하지만 지금은 어디까지나 기능구현만 했을뿐, 아이템이 아이템을 획득하고, 아이템이 패배판정에도 영향을 주는
기능까지는 구현하지 못했다. 이런 전체적인 수정에 관해서는 다음글에서 다루도록 하겠다
'Galaxy Ball > 1. 멀티플레이 - 대전모드' 카테고리의 다른 글
(23) 일시정지 기능 & 인트로씬 보완 (1) 2024.05.01 (22) 인트로씬 & Json 응용 연습 (0) 2024.04.30 (20) 아이템 #사용구현 (0) 2024.04.08 (19) 아이템 #UI (1) 2024.04.04 (18) 아이템 #랜덤 생성 (1) 2024.04.03