일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 필기
- 자바스크립트
- unity3d
- 정처기 필기
- 게임 제작
- portal
- 정처기
- 1인 게임
- 합격
- Vampire Survivors
- 게임 개발
- Unity #Unity2D #Portal
- 게임
- Pong
- 3회차
- 유니티3d
- 1인 게임 개발
- 퐁
- FPS
- 프로그래머스 #최소힙 #우선순위 큐
- 유니티 3D
- 정보처리기사
- 유니티
- 자바스크립트 게임
- 1인 게임 제작
- Unity
- 게임제작
- 토이 프로젝트
- Unity2D
- 1인 개발
- Today
- Total
Coding Feature.
[Unity 2D] Portal 같은 게임 만들기 #5 포탈건 매커니즘 구현하기 2 (포탈 생성하기) 본문
[Unity 2D] Portal 같은 게임 만들기 #5 포탈건 매커니즘 구현하기 2 (포탈 생성하기)
codingfeature 2024. 1. 7. 14:25앞서 포탈건의 조준선을 시각화했다면 이제는 본격적으로 포탈건으로 포탈을 만들수 있도록 구현해보겠습니다.
플레이어가 어떤 벽면에 포탈건을 조준하고 쏠 때 벽면의 각도와 위치에 따라서 포탈이 그에 알맞게 생성되도록 해야 합니다.
우선 포탈을 만들 수 있는 벽면을 "Portalable"이라고 명명하겠습니다.
그리고 앞서 만들었던 GameObject인 "Ground"의 앞면에 Portalable을 붙여놓습니다.
그래서 실제로 포탈이 만들어질 때 Ground가 아니라 Ground의 바로 앞에 있는 Portalable에 생성되도록 했습니다.
그 다음 Portalable에 포탈건을 쏠 때, 포탈건으로부터 쏠 때의 위치와 각도, 그리고 Portalable의 위치와 각도를 사용해서 포탈이 Portalable의 어느 위치에서 생성될 지 구해야 합니다.
이는 두 벡터의 교점을 구함으로써 구했습니다.
위에서 구한 식으로 포탈이 생성될 위치 좌표를 구하고, 각도는 Portalable 의 각도와 동일하게 생성되도록 코드를 짜면 되겠네요!
위에서 작성한 식을 코드로 표현하면 아래와 같습니다!
private void OnTriggerStay2D(Collider2D collision)
{
if (collision.gameObject.tag == "Portalable")
{
m_pointedGameObject = collision.gameObject;
float beta1, beta2;
beta1 = player.transform.position.y - (m_mouseDir.y / m_mouseDir.x) * player.transform.position.x;
beta2 = m_pointedGameObject.transform.position.y - Mathf.Tan(m_pointedGameObject.transform.rotation.eulerAngles.z * Mathf.Deg2Rad) * m_pointedGameObject.transform.position.x;
portal_X = (beta2 - beta1) / ((m_mouseDir.y / m_mouseDir.x) - Mathf.Tan(m_pointedGameObject.transform.rotation.eulerAngles.z * Mathf.Deg2Rad));
portal_Y = (m_mouseDir.y / m_mouseDir.x) * portal_X + beta1;
}
}
위에서는 OnTriggerStay2D 함수를 사용했습니다.
이를 위해서 Edge Collision과 Portalable의 Box Collision에 Is Trigger 항목을 체크하고 Portalable에 RigidBody 2D 속성을 추가했습니다.
void DrawAimLine()
{
m_colliderpoints = edgeCollider.points;
m_mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition); // 화면상 마우스 좌표 -> 게임상 좌표로 변환.
m_startPos = player.transform.position;
m_startPos.z = 0;
m_endPos = m_mousePos;
m_endPos.z = 0;
m_mouseDir = (m_endPos - m_startPos);
m_mouseDir.Normalize(); // 캐릭터 To 마우스까지 방향의 단위벡터.
lineRenderer.SetPosition(0, m_mouseDir * aimLineStartLength + m_startPos); // 캐릭터 좌표에서 선 시작.
lineRenderer.SetPosition(1, m_mousePos); // 마우스 좌표에서 선 끝.
m_colliderpoints[0] = m_mouseDir * aimLineStartLength + m_startPos; // Edge Collider 선 시작.
m_colliderpoints[1] = m_mouseDir * aimLineLength + m_startPos; // Edge Collider 선 끝.
edgeCollider.points = m_colliderpoints; // Edge Collider Points 설정.
}
m_colliderpoints를 보면 Edge Collider의 시작점에 aimLineStartLength만큼 곱해서 캐릭터와 어느정도 간격을 두었는데 이는 캐릭터의 Box Collider와 겹치는 것을 막기 위해서 입니다.
그리고 마우스 왼쪽 버튼을 입력했을 때 블루 포탈, 오른쪽 버튼을 입력했을 때 오렌지 포탈을 생성하도록 코드를 짰습니다.
if (Input.GetKeyDown(KeyCode.Mouse0))
{
bluePortal.transform.position = new Vector3(portal_X, portal_Y, 0);
bluePortal.transform.rotation = m_pointedGameObject.transform.rotation;
}
if (Input.GetKeyDown(KeyCode.Mouse1))
{
orangePortal.transform.position = new Vector3(portal_X, portal_Y, 0);
orangePortal.transform.rotation = m_pointedGameObject.transform.rotation;
}
위 내용을 코드 전체로 보면 다음과 같습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PortalGunScript : MonoBehaviour
{
public LineRenderer lineRenderer;
public GameObject player;
public EdgeCollider2D edgeCollider;
public GameObject bluePortal;
public GameObject orangePortal;
public float aimLineLength;
public float aimLineStartLength;
Vector3 m_startPos;
Vector3 m_endPos;
Vector3 m_mousePos;
Vector3 m_mouseDir;
Vector2[] m_colliderpoints;
GameObject m_pointedGameObject;
float portal_X, portal_Y;
Vector3 portal_XYZ;
// Start is called before the first frame update
void Start()
{
aimLineLength = 30.0f; // Aim Line의 길이.
aimLineStartLength = 0.5f; // Aim Line의 시작점과 플레이어간의 간격.
edgeCollider.enabled = true;
lineRenderer.enabled = true;
}
// Update is called once per frame
void Update()
{
DrawAimLine();
if (Input.GetKeyDown(KeyCode.Mouse0))
{
bluePortal.transform.position = new Vector3(portal_X, portal_Y, 0);
bluePortal.transform.rotation = m_pointedGameObject.transform.rotation;
}
if (Input.GetKeyDown(KeyCode.Mouse1))
{
orangePortal.transform.position = new Vector3(portal_X, portal_Y, 0);
orangePortal.transform.rotation = m_pointedGameObject.transform.rotation;
}
}
void DrawAimLine()
{
m_colliderpoints = edgeCollider.points;
m_mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition); // 화면상 마우스 좌표 -> 게임상 좌표로 변환.
m_startPos = player.transform.position;
m_startPos.z = 0;
m_endPos = m_mousePos;
m_endPos.z = 0;
m_mouseDir = (m_endPos - m_startPos);
m_mouseDir.Normalize(); // 캐릭터 To 마우스까지 방향의 단위벡터.
lineRenderer.SetPosition(0, m_mouseDir * aimLineStartLength + m_startPos); // 캐릭터 좌표에서 선 시작.
lineRenderer.SetPosition(1, m_mousePos); // 마우스 좌표에서 선 끝.
m_colliderpoints[0] = m_mouseDir * aimLineStartLength + m_startPos; // Edge Collider 선 시작.
m_colliderpoints[1] = m_mouseDir * aimLineLength + m_startPos; // Edge Collider 선 끝.
edgeCollider.points = m_colliderpoints; // Edge Collider Points 설정.
}
private void OnTriggerStay2D(Collider2D collision)
{
if (collision.gameObject.tag == "Portalable")
{
m_pointedGameObject = collision.gameObject;
float beta1, beta2;
beta1 = player.transform.position.y - (m_mouseDir.y / m_mouseDir.x) * player.transform.position.x;
beta2 = m_pointedGameObject.transform.position.y - Mathf.Tan(m_pointedGameObject.transform.rotation.eulerAngles.z * Mathf.Deg2Rad) * m_pointedGameObject.transform.position.x;
portal_X = (beta2 - beta1) / ((m_mouseDir.y / m_mouseDir.x) - Mathf.Tan(m_pointedGameObject.transform.rotation.eulerAngles.z * Mathf.Deg2Rad));
portal_Y = (m_mouseDir.y / m_mouseDir.x) * portal_X + beta1;
}
}
}
결과는 다음과 같습니다.
위 코드를 작성하는데 있어서 여러 버그 및 수정 사항이 발생했었습니다.
하나씩 정리해보고자 합니다.
문제1)
사용자가 Portalable 벽면에 생긴 포탈에서 나올 때 Portalable 뒷면에 있는 Ground 벽면의 Collider 때문에 끼여 관성을 잃는 버그가 발생했었습니다.
해결)
사용자가 Portal에 나올 때 잠시동안 Is Trigger 속성을 true로 바꿔서 플레이어의 물리적 속성을 제거함으로써 Ground 벽면의 Collider에 끼이는 버그를 해결했습니다. 다만 이 문제는 추후에 다른 문제점을 발생시켰으나 이 부분은 바로 다음에 해결해보기로 하겠습니다!
if (isTouchingOrange && !m_EnteredPortal) // 오렌지 포탈에 닿은 경우 + 이미 들어간 적이 없는 경우.
{
m_EnteredPortal = true;
player.transform.position = bluePortal.transform.position; // 오렌지 포탈로 위치 변환.
player.GetComponent<Rigidbody2D>().velocity = desiredDirection.normalized * m_playerVelocity.magnitude; // 포탈을 나갈때 속도의 방향 변환.
player.GetComponent<BoxCollider2D>().isTrigger = true; // 잠시동안 플레이어 collider 트리거로 변환.
}
if (isTouchingBlue && !m_EnteredPortal) // 블루 포탈에 닿은 경우 + 이미 들어간 적이 없는 경우.
{
m_EnteredPortal = true;
player.transform.position = orangePortal.transform.position; // 블루 포탈로 위치 변환.
player.GetComponent<Rigidbody2D>().velocity = desiredDirection.normalized * m_playerVelocity.magnitude; // 포탈을 나갈때 속도의 방향 변환.
player.GetComponent<BoxCollider2D>().isTrigger = true; // 잠시동안 플레이어 collider 트리거로 변환.
}
문제2)
포탈건을 쏘고 난 뒤에 포탈에 들어가면 포탈에 들어간 각도에 맞게 관성으로 나오지 않는 버그가 발생했습니다.
해결)
이 부분은 Portal 관련 Script에서 Update 함수를 통해 지속적으로 포탈의 각도를 업데이트하지 않아서 생기는 버그였습니다. 아래처럼 지속적으로 각도를 업데이트 해주는 코드를 추가하여 해결했습니다.
void Update()
{
m_playerVelocity = player.GetComponent<Rigidbody2D>().velocity;
// 포탈 각도 업데이트.
m_orangePortalRotation = orangePortal.transform.rotation.eulerAngles.z;
m_bluePortalRotation = bluePortal.transform.rotation.eulerAngles.z;
...
다음에는 지금까지 구현했던 포탈, 포탈건 매커니즘에서 발생하는 버그 등을 보수해보도록 하겠습니다!
2024-01-13 수정)
지금까지 구현했던 포탈 건의 매커니즘은 Raycast2D 방식으로 수정되었습니다!
아래 글을 참고해주세요!
https://codingfeature.tistory.com/86
'Toy Project > mini-portal [Unity2D]' 카테고리의 다른 글
[Unity 2D] Portal 같은 게임 만들기 #7 레벨 1 디자인, Scene Controller (0) | 2024.01.09 |
---|---|
[Unity 2D] Portal 같은 게임 만들기 #6 포탈, 포탈건, 캐릭터의 버그 해결 (0) | 2024.01.08 |
[Unity 2D] Portal 같은 게임 만들기 #4 포탈건 매커니즘 구현하기 1 (조준선 그리기) (0) | 2024.01.06 |
[Unity 2D] Portal 같은 게임 만들기 #3 포탈 매커니즘 구현하기 2(속도 변환) (0) | 2024.01.05 |
[Unity 2D] Portal 같은 게임 만들기 #2 포탈 매커니즘 구현하기 1(위치 변환) (0) | 2024.01.05 |