-
(7) Firezone, 발사구현Galaxy Ball/1. 멀티플레이 - 대전모드 2024. 3. 26. 16:40
전글까지는 이미 다 구현해놓은 프로젝트를 글로 옮기는 작업이었지만 지금부터는 실시간으로 진행하는대로
글에 옮겨적으려한다
드디어 미루고 미루다가 시작하는 발사구현. 아직 해보지 않았지만 이게 가장 까다로울것 같다
시작하기전 구현해야할 단계에 대해서 적어보겠다
1. Firezone 제작
2. Firezone에서만 클릭해야 구체가 생성되어 날아감
3-1. 클릭 : 구체생성
3-2. 클릭 상태로 잡아당김 : 구체가 생성된 위치와 마우스의 거리를 힘으로 환산 & 날리는 방향 결정
3-3. 클릭을 뗌 : 그 상황된 힘만큼 구체에 힘을 주어 발사
이렇게까지가 발사구현. 사실상 3번때문에 여태 미뤄온것이다
만약 여기까지 구현에 성공했다면 이제 서로의 턴마다 한번씩 주고받으며 공을 발사하게 해야한다
이번글에서는 1~3번까지 구현해볼 예정이다
4. 내 턴이 되었을때 firezone 활성화, 상대턴에는 firezone 비활성화
5. 내 턴에 단 한번의 공만 발사하게 하기
6. 공의 확장까지 끝나야 턴이 넘어가게 하기
7. 시작은 무조건 Player1부터 시작
(4~7번은 다음글에서 구현해볼 예정)
확실히 여태까지 한 모든것들중 가장 복잡해보인다.
하지만 여기까지 구현한다면 얼추 멀티플레이의 끝이 보인다고 할 수 있다
Firezone은 처음 그림에 설명한대로 적당한 크기로 잡아준뒤 Box Collider를 추가해준다.
물론 충돌처리는 하지 않아야하니 isTrigger에 체크해준다
<기존 GameManager 스크립트>
using UnityEngine; public class GameManager : MonoBehaviour { public GameObject P1ballPrefab; public GameObject P2ballPrefab; void Update() { if (Input.GetMouseButtonDown(0)) { Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); mousePosition.z = 0f; // z축을 0으로 설정하여 2D 공간에 배치합니다. Instantiate(P1ballPrefab, mousePosition, Quaternion.identity); } } }
원래는 위 코드처럼 클릭을 하는순간 어디가됐던 곧바로 구체를 생성하도록 만들었지만
이제 구체를 생성할 수 있는 유일한 장소는 Firezone이 되도록 해보겠다
using UnityEngine; public class GameManager : MonoBehaviour { public GameObject P1ballPrefab; public GameObject P1firezone; // P1firezone 게임 오브젝트 추가 void Update() { if (Input.GetMouseButtonDown(0)) { // 마우스 위치를 월드 좌표로 변환 Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); mousePosition.z = 0f; // z축을 0으로 설정하여 2D 공간에 배치합니다. // P1firezone 안에 있는지 확인 Collider2D[] colliders = Physics2D.OverlapPointAll(mousePosition); foreach (Collider2D collider in colliders) { if (collider.gameObject == P1firezone) { // P1firezone 안에 있는 경우에만 P1ballPrefab 생성 Instantiate(P1ballPrefab, mousePosition, Quaternion.identity); break; // 생성 후 루프 종료 } } } } }
우선 P1BallPrefab이 P1firezone에서만 발사가 되도록 만들어보았다
구체 프리팹과 firezone을 넣어준뒤 실행을 해보자
이제 P1firezone에서만 발사가 되는것을 확인할 수 있다
이번엔 그토록 밀어왔던 발사구현을 해보도록 하자
3-1. 클릭 : 구체생성
3-2. 클릭 상태로 잡아당김 : 구체가 생성된 위치와 마우스의 거리를 힘으로 상환 & 날리는 방향 결정
3-3. 클릭을 뗌 : 그 상황된 힘만큼 구체에 힘을 주어 발사
우선 이 순서대로 클릭할시 구체를 생성시켜보기만 하겠다
말 그대로 firezone 안에서 클릭하면 구체프리팹을 초기상태로 생성만 하는것이다
private void Start() { rigid = GetComponent<Rigidbody2D>(); //rigid.velocity = transform.up * 300 * Time.deltaTime; // 발사 정지 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() { Move(); //expand(); 확장 정지 }
그러기 위해서는 BallController에 있는 발사기능, 확장기능을 담당하는 2줄을 주석처리 해버리면 된다
그럼 클릭시 확장도, 이동도 하지 않고 생성만 되는것을 확인할 수 있다
3-2. 클릭 상태로 잡아당김 : 구체가 생성된 위치와 마우스의 거리를 힘으로 환산 & 날리는 방향 결정
3-3. 클릭을 뗌 : 그 상황된 힘만큼 구체에 힘을 주어 발사
남은건 2가지.
클릭 상태로 잡아당기고, 마우스 클릭버튼을 놓는 순간 구체와 마우스 위치 사이의 거리를 수치로 환산하는것이다
우선 이것부터 해보겠다
using UnityEngine; public class GameManager : MonoBehaviour { public GameObject P1ballPrefab; public GameObject P1firezone; private Vector3 clickPosition; // 클릭한 위치 저장 private bool isDragging = false; // 마우스를 끌고 있는지 여부 저장 void Update() { if (Input.GetMouseButtonDown(0)) { clickPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); clickPosition.z = 0f; Collider2D[] colliders = Physics2D.OverlapPointAll(clickPosition); foreach (Collider2D collider in colliders) { if (collider.gameObject == P1firezone) { Instantiate(P1ballPrefab, clickPosition, Quaternion.identity); isDragging = true; break; } } } if (isDragging && Input.GetMouseButtonUp(0)) { Vector3 currentPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); currentPosition.z = 0f; float distance = Vector3.Distance(clickPosition, currentPosition); Debug.Log("Distance: " + distance); isDragging = false; // 거리 출력 후 드래그 상태 해제 } } }
어떻게보면 드래그를 해야하니 당연히 GetMouseButton 기능을 사용해야 한다고 생각했다
하지만 굳이 그럴게 아니라 그냥 마우스를 현재 끌고 있는지(드래그)에 대한 여부를 판단하는
bool 타입 변수로 isDragging을 선언해준뒤 사용하는게 훨씬 더 편리하다
클릭을 하는순간 클릭한곳(구체가 생성되는곳)을 clickposition 변수안에 위치정보를 넣어주고
클릭을 떼는순간 클릭을 뗀곳을 currentPosition 변수안에 위치정보를 넣어준다
그리고 float 타입 변수 distance안에 둘 사이의 거리를 Vector3.Distance 를 이용하여 구해주기만 하면된다
눈에 보기 쉽게 하기 위해 클릭을 떼는순간 수치화 된 distance를 출력하게 만들었다
아그리고
clickPosition.z = 0f;
currentPosition.z = 0f;
이거 안해주면 화면에 구체 안뜨니까 잊지말고 꼭 초기화해주자
클릭을 하여 구체를 생성시키고, 드래그하여 잡아당기는 거리만큼 출력되는 Distance의 수치가 달라짐을 확인할 수 있다
하지만 발사하기 위해서 힘만 가지고는 안된다
3-2. 클릭 상태로 잡아당김 : 구체가 생성된 위치와 마우스의 거리를 힘으로 환산 & 날리는 방향 결정
이번엔 날리는 방향까지 얻어보겠다
using UnityEngine; public class GameManager : MonoBehaviour { public GameObject P1ballPrefab; public GameObject P1firezone; private Vector3 clickPosition; private bool isDragging = false; void Update() { if (Input.GetMouseButtonDown(0)) { clickPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); clickPosition.z = 0f; Collider2D[] colliders = Physics2D.OverlapPointAll(clickPosition); foreach (Collider2D collider in colliders) { if (collider.gameObject == P1firezone) { Instantiate(P1ballPrefab, clickPosition, Quaternion.identity); isDragging = true; break; } } } if (isDragging && Input.GetMouseButtonUp(0)) { Vector3 currentPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); currentPosition.z = 0f; float distance = Vector3.Distance(clickPosition, currentPosition); Debug.Log("힘 : " + distance); //추가된 코드 Vector3 dragDirection = (currentPosition - clickPosition).normalized; Vector3 oppositeDirection = -dragDirection; Debug.Log("드래그 한 방향 : " + dragDirection); Debug.Log("발사될 방향: " + oppositeDirection); isDragging = false; } } }
사실 이 방향 결정하는게 괜히 까다로워보였으나 그렇게 어렵지 않았다
Vector3 dragDirection = (currentPosition - clickPosition).normalized;
Vector3 oppositeDirection = -dragDirection;
추가된건 딱 이 두줄뿐. 근데 이렇게까지 쫀걸보면 내가 Vector와 방향쪽에 대해 굉장히 약하다는것을 알게 되었다
첫번째줄은 별거없이 그냥 두 위치 사이를 normalized 하여 방향을 구하는 코드이지만
두번째줄에서 이걸 한번더 꼬아 마이너스 해준 이유는 앵그리버드를 생각해보면 된다
앵그리버드를 보면 새를 위로 날리기 위해서 새를 밑으로 잡아당긴다
반대로 아래로 날리고 싶다면 새를 위로 잡아당긴뒤 놓을것이다
쉽게 말해 잡아당기는 방향과 대칭되는 방향으로 구체가 날아갈 예정이기에
Vector3 oppositeDirection = -dragDirection;
이렇게 드래그한것의 반대로 대칭시키는것이다
힘과 마찬가지로 방향 역시 출력되게 해보았다
클릭하고, 드래그를 한뒤, 클릭을 떼는 순간 [힘, 드래그한 방향, 발사될 방향] 이 세가지가 동시에 출력되는것을 볼 수 있다
3-3. 클릭을 뗌 : 그 상황된 힘만큼 구체에 힘을 주어 발사
이제 정말 거의 다 했다 이제 이 세가지중 힘과 발사될 방향 2개를 BallController에 적용시켜주기만 하면 된다
근데 이게 진짜 안된다. 제일 간단한 문제일 줄 알았는데
public class GameManager : MonoBehaviour { public GameObject P1ballPrefab; public GameObject P1firezone; private Vector3 clickPosition; private bool isDragging = false; // 전역 변수 선언 public static float shotDistance; public static Vector3 shotDirection; void Update() { if (Input.GetMouseButtonDown(0)) { clickPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); clickPosition.z = 0f; Collider2D[] colliders = Physics2D.OverlapPointAll(clickPosition); foreach (Collider2D collider in colliders) { if (collider.gameObject == P1firezone) { Instantiate(P1ballPrefab, clickPosition, Quaternion.identity); isDragging = true; break; } } } if (isDragging && Input.GetMouseButtonUp(0)) { Vector3 currentPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); currentPosition.z = 0f; GameManager.shotDistance = Vector3.Distance(clickPosition, currentPosition); // 전역 변수에 값 설정 Vector3 dragDirection = (currentPosition - clickPosition).normalized; GameManager.shotDirection = -dragDirection; // 전역 변수에 값 설정 isDragging = false; } } }
우선 distance와 oppositeDirection 변수명을 알아보기 쉽게 shotDistance, shotDirection으로 바꿔주었다
그리고 전역변수에 추가해준뒤 BallController에서 값을 가져와 발사하게 만들었다
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 void Start() { rigid = GetComponent<Rigidbody2D>(); rigid.velocity = GameManager.shotDirection * GameManager.shotDistance; // GameManager에서 값 가져와서 구체 발사 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() { Move(); } 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()); } } private void OnCollisionEnter2D(Collision2D coll) { if (coll.gameObject.tag == "P1ball" || coll.gameObject.tag == "P2ball") { 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); } }
딱봐도 잘 구동되야할것 같고, 실제로 안되는것도 아니다.
다만 영상에 보이듯이 날아가는데 한 템포 늦게 나간다는것.
예를들어 내가 오른쪽으로 쎄게 잡아당기면 마우스 클릭을 떼는 순간 왼쪽으로 빠르게 날아가야 하는데
해당 클릭으로 생성된 공이 아닌, 다음 클릭으로 생성된 공이 그렇게 날아간다는것이다
'Galaxy Ball > 1. 멀티플레이 - 대전모드' 카테고리의 다른 글
(9) 버그 개선#1 (0) 2024.03.27 (8) 발사구현 마무리 (0) 2024.03.27 (6) 내구도 표시 & 구체 파괴 (0) 2024.03.26 (5) 패배 판정 & 씬 변환 (0) 2024.03.25 (4) 구체 팽창 & 팽창 후 고정 (0) 2024.03.25