


development diary
When: 1학년 10월 27일
Developed: 앵그리버드에 나오는 점으로 된 포물선
- 프로젝트 : (수학 과제)
- 소요 시간 : 공부 3일, 구현 1일
- 설명 : 중력, 각도, 힘 등을 직접 설정해서 물체를 날렸을 때, 그 물체가 날아가는 궤적을 점선으로
그리는 포물선 그래프를 구현하기 위해 과학 시간에 배운 가속도, 중력 가속도와 수학 시간에
배운 사인, 코사인, Lerp를 이용했습니다. 속도에 따른 x를 구하고
중력 가속도에 영향받는 y를 구하는 식에 x를 넣으면 y가 나와서 x, y를 쓰는 구조입니다. - 코드 설명(x값 구하기): 물체를 던졌을 때, 공기 저항이 없다면 x값은 일정한 속도로 계속 증가하거나
감소하기 때문에 벡터로 된 초기 속도를 구한 다음, 1초에 벡터의 x값 만큼 간다고 생각하시면 됩니다.
그래서 아래와 같은 메서드를 만들어 각도에 맞는 라디안 값을 구해 코사인 함수와 사인 함수에 넣어
원하는 방향의 벡터를 만든 다음, 그 벡터에 힘을 곱해 방향과 힘 모두 있는 속도를 반환하게 하였습니다.
처음 속도를 v0Vec이라고 했을 때, x값은 x0 + v0Vec.x * t으로 처음 위치와 시간 * 속도로
구한 거리를 더해 구할 수 있습니다.
private Vector2 GetInitialVelocity()
{
float rad = angle * Mathf.Deg2Rad;
return new Vector2(Mathf.Cos(rad), Mathf.Sin(rad)) * power;
}
- 설명(y값 구하기): y값은 중력 가속도의 영향받기 때문에 시간에 따른 y값은 처음 위치에서
시간, 속도 그래프의 면적으로 구한 거리를 더해준 값입니다. 시간, 속도 그래프에서 면적은
처음 속도 * 시간 + (가속도 * 시간) * 시간 / 2를 하여 구할 수 있는데, 초기 속도를 v0Vec이라고 했을 때,
중력에 영향받는 y값은 가속도가 9.8이기 때문에 y0 + v0Vec.y * t - 9.8 * t^2 / 2가 됩니다.
시간에 따른 x, y값을 모두 구했는데, x값을 구하는 식과 y값을 구하는 식에 t가 있기 때문에 이항하여
y = ax^2 + b 꼴의 식으로 나타낼 수 있습니다. 위에서 구한 두 식을 y에 대한 하나의 식으로 바꾸어
아래와 같이 나타낼 수 있습니다.
y = y0 + v0Vec.y * (x - x0) / v0Vec.x - g * ((x - x0) / v0Vec.x * (x - x0) / v0Vec.x) / 2
- 설명(그리기): 이제 위의 식으로 x의 값에 대한 y의 값을 알 수 있게 되었습니다. 제가 조절할 수 있는 설정은
각도, 힘, 중력, 몇 초 후의 위치까지 보여줄지, 얼마나 자세하게 보여줄지가 있는데,
여기서 마지막 위치(n초 후의 위치)와 얼마나 자세하게 보여줄지(점을 몇 개 찍을지)로
x의 위치를 for문으로 증가시키며 y의 위치도 구해 Gizmos로 점을 찍어보았습니다.
아래의 코드로 자세히 설명하자면 일단 세팅에 따른 속도를 구하고, 처음 위치, 중력을 미리 변수로 선언하여
위의 식에 바로 쓸 수 있도록 합니다. 그리고 마지막 위치를 구하기 위해선 마지막 x값을 구해야 합니다.
x는 간단하게 속도의 x값 * 시간으로 거리를 구할 수 있고 처음 위치에 거리를 더하면 되기 때문에
3번과 같이 마지막 x를 구할 수 있는데, 처음 x값부터 마지막 x값까지 원하는 만큼의 점을 찍어야 하므로
for문을 써서 점의 개수만큼 반복을 시켜주고, 첫 번째 점의 x값은 처음 x값과 마지막 x값을 선으로 그렸을 때,
그 선의 1/(점 개수) 위치에 찍고, 두 번째 점은 2/(점 개수) 위치에 찍는 방식으로 점의 x값을 구했습니다.
x값을 구했으니 y값은 아까 구한 식에 x값을 넣어 구해주고, x와 y 모두 구했으니 그 위치에 점을 찍는 방식으로
구현했습니다.
Vector2 p0 = transform.position;
Vector2 v0Vec = GetInitialVelocity(); //1. 속도를 구한다.
float x0 = p0.x; //2. x0를 구한다.
float y0 = p0.y;
float g = gravity;
float xDistance = x0 + v0Vec.x * time; //3. v0Vec.x는 x의 속도, 속도 * time = 거리
for (int i = 1; i <= pointCount; i++)
{
float t = i / (float)pointCount;
float x = x0 + (xDistance - x0) * t; // 간이 Lerp
//float x = Mathf.Lerp(x0, xDistance, t);
float y = 0;
y = y0 + v0Vec.y * (x - x0) / v0Vec.x - g * ((x - x0) / v0Vec.x * (x - x0) / v0Vec.x) / 2; // y를 구하는 식
Vector2 pos = new Vector2(x, y);
Gizmos.DrawSphere(pos, 0.1f);
}
- 사용한 코드 :
더보기
using UnityEngine;
using UnityEngine.InputSystem;
public class DrawLine : MonoBehaviour
{
[Header("Prefab & Launch")]
[SerializeField] private GameObject circlePrefab;
[Tooltip("발사 각도(도). 0도=오른쪽, 90도=위쪽")]
[SerializeField, Range(0f, 90f)]
private float angle = 45f;
[Tooltip("초기 속력 v0 (단위: m/s)")]
[SerializeField, Range(0f, 50f)]
private float power = 10f;
[Header("Gravity & Preview")]
[Tooltip("아래로 향하는 중력 크기 (단위: m/s², 예: 9.81)")]
[SerializeField, Range(0f, 30f)]
private float gravity = 9.81f;
[Tooltip("미리보기 총시간(초). x 범위를 v0x * time 만큼 그립니다.")]
[SerializeField, Range(0.1f, 10f)]
private float time = 2.0f;
[Tooltip("미리보기 점 개수 (많을수록 부드러움)")]
[SerializeField, Range(0, 200)]
private int pointCount = 30;
[Header("나머지 세팅")]
[SerializeField, Range(0f, 3f)]
private float timeScale = 1f;
private void Start()
{
Time.timeScale = timeScale;
}
private void Update()
{
if (Keyboard.current != null && Keyboard.current.spaceKey.wasPressedThisFrame)
{
var go = Instantiate(circlePrefab, transform.position, Quaternion.identity);
var rb = go.GetComponent<Rigidbody2D>();
Vector2 v0Vec = GetInitialVelocity();
float engineG = Mathf.Abs(Physics2D.gravity.y);
rb.gravityScale = gravity / engineG;
rb.linearVelocity = v0Vec;
}
Time.timeScale = timeScale;
}
private void OnDrawGizmos()
{
Gizmos.color = Color.white;
Vector2 p0 = transform.position;
Vector2 v0Vec = GetInitialVelocity(); //1. 속도를 구한다.
float x0 = p0.x; //2. x0를 구한다.
float y0 = p0.y;
float g = gravity;
float xDistance = x0 + v0Vec.x * time; //3. v0Vec.x는 x의 속도, 속도 * time = 거리
for (int i = 1; i <= pointCount; i++)
{
float t = i / (float)pointCount;
float x = x0 + (xDistance - x0) * t; // 간이 Lerp
//float x = Mathf.Lerp(x0, xDistance, t);
float y = 0;
y = y0 + v0Vec.y * (x - x0) / v0Vec.x - g * ((x - x0) / v0Vec.x * (x - x0) / v0Vec.x) / 2; // y를 구하는 식
Vector2 pos = new Vector2(x, y);
Gizmos.DrawSphere(pos, 0.1f);
}
//파란 선(각도 표시)
Gizmos.color = Color.blue;
Vector2 dir = GetInitialVelocity().normalized;
Gizmos.DrawLine(transform.position, transform.position + (Vector3)(dir * (xDistance / dir.x)));
}
private Vector2 GetInitialVelocity()
{
float rad = angle * Mathf.Deg2Rad;
return new Vector2(Mathf.Cos(rad), Mathf.Sin(rad)) * power;
}
}
Result: 수학 과제이긴 하지만 지금까지 구현한 것 중 가장 학교에서 배운 것을 많이 써먹었는데,
수학은 그래도 Mathf가 많은 것을 해줘서 익숙해졌습니다. 그런데 속도와 중력만 정해서
공을 Rigidbody2D의 AddForce로 날렸는데, 과학 시간에 배우는 가속도가 있을 때 이동 거리를 구하는 공식을
그대로 적용하니까 Unity 엔진에서 정확히 맞아떨어지는 것을 보았을 때 유니티의 현실적인 물리 연산도 신기했지만,
그 현실적인 물리 현상을 수식으로 풀어서 적용했을 때, 이론상으론 맞겠지만 실제로 정확한 포물선을
그리는 것을 보니 수업 시간에 배운 물리와 수학이 전혀 헛되지 않았다는 것을 느껴 좋은 경험이었던 것 같습니다.
'포트폴리오 > 개발 일기' 카테고리의 다른 글
| [개발 일기] 공격 범위 표시하기 (0) | 2025.11.04 |
|---|---|
| [개발 일기] 프로토타입 만들기 (0) | 2025.10.27 |
| [개발 일기] 카드 야바위 뽑기 구현 (0) | 2025.09.10 |
| [개발 일기] 스테이지 선택 화면 만들기 (2) | 2025.08.15 |