저번 게시글에 이어서, 마우스 위치로 포탄을 쏘는 코드를 작성하겠다. 이 부분은 작성자도 처음 보는 부분이라 조금 깊이 작성해야할 것 같아서 따로 떼어서 작성하게 되었다.
마우스 위치로 포탄을 발사하려면
- 현재 플레이어 위치
- 현재 마우스 위치
- 포탄의 속도
가 필요하다. 현재 플레이어 위치와 현재 마우스 위치로 방향 벡터를 구할 수 있고, 이전 게시글에서 불러온 포탄을 위에서 구한 방향 벡터의 방향으로 포탄의 속도로 밀어내면 구현해낼 수 있을 것이다.
우선 현재 플레이어의 위치를 생각하기 전에, 탱크에서 발사하는 것보단 탱크 자체에 발사대를 만드는 것이 더 직관적이라고 판단하여 탱크 오브젝트 밑에 빈 오브젝트를 만들어, shotPos라고 명했다. 이제는 탱크 위치에서 포탄을 생성하는 것이 아닌, 빈 오브젝트인 shotPos에서 포탄을 만들어서 쏠 것이다. 이러면 현재 플레이어 위치는 곧 shotPos의 위치가 된다.
현재 마우스 위치는 이렇게 구한다
Vector3 dir;
Camera cam;
void Start()
{
cam = Camera.main;
}
void Update()
{
dir = (cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -cam.transform.position.z)) - shotPos.transform.position).normalized;
}
Start() 메서드에 있는 명령어부터 보겠다.
cam = Camera.main;
Camera.main이란, 현재 게임에 있는 카메라 중 메인 카메라를 찾는 함수이다. 찾은 메인 카메라를 cam이라는 전역변수에 저장한다.
void Update()
{
dir = (cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -cam.transform.position.z)) - shotPos.transform.position).normalized;
}
코드를 하나하나 나눠서 보겠다.
cam.ScreenToWorldPoint
Camera.main의 함수 중 하나인 ScreenToWorldPoint는, Unity Document에 의하면
Transforms position from screen space into world space.
라고 적혀있다. 간단하게, 오브젝트의 월드 좌표를 화면의 좌표로 변환해주는 함수이다.
그 오브젝트의 월드 좌표가
(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -cam.transform.position.z)
이 부분이 된다. 새로운 Vector3 변수를 만들고, Input.mousePosition을 이용하여 x와 y좌표를 찾는다. 본인이 만드는 게임은 2D이기 때문에 z좌표는 cam의 z좌표를 이용하였다.
- shotPos.transform.position
위의 계산식으로 마우스의 위치를 구했으면, 발사대의 위치벡터를 빼준다. 이러면 마우스와 발사대 사이의 벡터가 나오게 된다.
하지만 이렇게 나온 벡터는 크기를 가지고 있기 때문에, 단위를 1로 만들어줘야 한다. 이를 위해 적은 것이
.normalized
이다. 벡터 뒤에 normalized를 붙이면 방향은 그대로 남긴, 단위가 1인 벡터가 나오게 된다. 이러한 과정을 정규화라고 한다.
위 과정을 한 문장으로 합치면
마우스의 위치에서 발사대의 위치를 빼준 후, 남은 벡터를 정규화한다.
가 되겠다.
이렇게 단위벡터를 만드는데 성공했다면, 다음은 이 벡터로 포탄을 쏴 줄 차례이다.
bullet.gameObject.GetComponent<Rigidbody2D>().AddForce(dir * bulletSpeed, ForceMode2D.Impulse);
이전 게시글에서 만든 bullet은 이미 shotPos로 옮겼다. bullet.gameObject.GetComponent로 bullet이 가지고 있는 모든 컴포넌트를 들고 오는데, 그 중 RigidBody2D에만 변화를 줄 것이다.
RigidBody는 게임오브젝트가 물리엔진으로 작동하게 하는 물리 레퍼런스 중 하나이다. 리지드바디가 포함되어 있으면 중력의 영향을 받고, 힘과 토크를 받아 오브젝트가 사실적으로 움직이게 해준다.
이러한 리지드바디에, AddForce를 써서 힘을 추가해준다. AddForce는 RigidBody의 Public 함수이며, 위에 적은 것처럼 RigidBody에 힘을 더해주는 함수이다.
public void AddForce (Vector3 force, ForceMode mode= ForceMode.Force);
첫 인수는 힘의 방향, 두 번째 인수는 힘을 주는 방법이다. ForceMode라는 옵션을 사용하여 쓰고, 각 종류는 링크를 참조하기 바란다. 작성자는 힘을 한 번만 주고 싶었기 때문에, Impulse를 이용하였다.
전체 함수는 이렇게 될 것이다. 본인은 탱크를 이동시키는 스크립트에 같이 작성하였고, 다른 스크립트에서 작성한 후 GetComponent로 들고 오는 방법도 있으니 그렇게 작성하여도 된다.
[SerializeField] float moveSpeed = 1f;
[SerializeField] float steerSpeed = 1f;
[SerializeField] GameObject bulletPrefab;
[SerializeField] GameObject shotPos;
[SerializeField] float bulletSpeed = 5f;
Vector3 dir;
Camera cam;
void Start()
{
cam = Camera.main;
}
void Update()
{
float steerAmount = Input.GetAxis("Horizontal") * steerSpeed * Time.deltaTime;
float moveAmount = Input.GetAxis("Vertical") * moveSpeed * Time.deltaTime;
transform.Rotate(0, 0, -1 * steerAmount);
transform.Translate(0, moveAmount, 0);
Shoot();
dir = (cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Inㅍput.mousePosition.y, -cam.transform.position.z)) - shotPos.transform.position).normalized;
}
void Shoot(){
if (Input.GetMouseButtonUp(0))
{
GameObject bullet = Instantiate(bulletPrefab);
bullet.transform.position = shotPos.transform.position;
bullet.gameObject.GetComponent<Rigidbody2D>().AddForce(dir * bulletSpeed, ForceMode2D.Impulse);
}
}
작동 영상은 여기서 확인할 수 있다. 다음 게시글엔, 이미 적용되어 있지만 이펙트의 사용 방법을 다룰 예정이다.