

์ค๋์ ํฑํฌ๋ฅผ ์์ง์ด๊ณ ๋ํฌ๋ฅผ ์๋ ๊ธฐ๋ฅ์
์ง์ ๊ตฌํํ ์ค๋ธ์ ํธ ํ๋ง๊ณผ ๋ด์ฅ๋ ObjectPool<T> ํด๋์ค๋ฅผ ์ด์ฉํ์ฌ ๊ตฌํํด๋ณผ ์๊ฐ์ด๋ค.

์ฐ์ ํฑํฌ๊ฐ ์์ง์ผ ์ ์๊ฒ ๋ ๋ฐํด์ Mesh Collider ์ปดํฌ๋ํธ๋ฅผ ์ถ๊ฐํ๋ค.
Mesh Collider์ Convex๋ฅผ ์ฒดํฌํ๋ฉด 3D ๋ชจ๋ธ์ ํํ๋๋ก ์ฝ๋ผ์ด๋๊ฐ ์์ฑ๋๋๋ฐ,
์ฑ๋ฅ ๋ถ๋ด์ด ํฌ๋ฏ๋ก ์๊ฐํ๊ณ ์ฌ์ฉํด์ผ ํ๋ค.โ
๋ค๋ง, ์ด๋ฒ์๋ ์์ง์ด๋ ์ค๋ธ์ ํธ๋ ์ด ํฑํฌ๋ฟ์ด๋ผ ๊ถ๊ธํด์ ์ ์ฉํด๋ดค๋ค.
public float moveSpeed = 5f;
public float turnSpeed = 100f;
void Update()
{
// ํ์
float bodyH = 0f;
if (Input.GetKey(KeyCode.LeftArrow))
bodyH = -1f;
else if (Input.GetKey(KeyCode.RightArrow))
bodyH = 1f;
transform.Rotate(Vector3.up, bodyH * turnSpeed * Time.deltaTime);
// ์ด๋
float bodyV = 0f;
if (Input.GetKey(KeyCode.UpArrow))
bodyV = 1f;
else if (Input.GetKey(KeyCode.DownArrow))
bodyV = -1f;
transform.Translate(Vector3.forward * bodyV * moveSpeed * Time.deltaTime);
}
// ๋จธ๋ฆฌ ํ์
float headH = 0f;
if (Input.GetKey(KeyCode.A))
headH = -1f;
else if (Input.GetKey(KeyCode.D))
headH = 1f;
head.transform.Rotate(Vector3.up, headH * turnSpeed / 2 * Time.deltaTime);
// ๋จธ๋ฆฌ ๊ฐ๋ x๊ฐ๋ง ์กฐ์
float headV = 0f;
if (Input.GetKey(KeyCode.W))
headV = -1f;
else if (Input.GetKey(KeyCode.S))
headV = 1f;
head.transform.Rotate(Vector3.right, headV * turnSpeed / 2 * Time.deltaTime);
์ด ์ฝ๋๋ Update() ํจ์์์ ํธ์ถ๋๋ค.
WASD๋ก ๋จธ๋ฆฌ ๋ฐฉํฅ์ ์กฐ์ ํ๊ณ ๋ฐฉํฅํค๋ก๋ ํฑํฌ๋ฅผ ์์ง์ด๋ ๋ฐฉ์์ผ๋ก ๋ง๋ค์๋ค.
Rotate๋ ํ์ฌ ํ์ ๊ฐ์ ๊ณ์ ๋์ ์ํค๋ ํจ์๋ผ ๋จธ๋ฆฌ๊ฐ ์
๋ ฅํ ๋ฐฉํฅ์ชฝ์ผ๋ก ์์ํ ๋๋ค.
Translate๋ ๊ฒ์ ์ค๋ธ์ ํธ๋ฅผ ํน์ ๋ฐฉํฅ์ผ๋ก ์ด๋์ํค๋ ํจ์๋ผ
Vector3.foward์ ์ฌ์ฉํด ์ค๋ธ์ ํธ๊ฐ ๋ฐ๋ผ๋ณด๋ ๋ฐฉํฅ์ผ๋ก ์ด๋์ํจ๋ค.
ํฌํ์ ๋ฐ์ฌํ๊ธฐ ์ํด์ ํ์ํ ๊ฒ์ ๋ฌด์์ผ๊น?

๊ทธ์ผ ๋น์ฐํ ํฌํ์ด๋ค.
ํฌํ์ ์ปค์คํ
ํ๋ง์ ์ฌ์ฉํ Bullet๊ณผ ๋ด์ฅ ํ๋ง์ ์ฌ์ฉํ NewBullet์ ๋ง๋ค์๋ค.
public class Bullet : MonoBehaviour
{
public Tank tank;
Rigidbody rigid;
void Awake()
{
rigid = GetComponent<Rigidbody>();
}
void OnEnable()
{
// 3์ด ์ญ์
Invoke("SetTimerOff", 3f);
}
void SetTimerOff()
{
gameObject.SetActive(false);
rigid.velocity = Vector3.zero;
}
void OnTriggerEnter(Collider other)
{
gameObject.SetActive(false);
rigid.velocity = Vector3.zero;
GameObject effect = tank.GetEffect();
if (effect != null)
{
effect.transform.position = transform.position;
effect.transform.rotation = Quaternion.identity;
effect.SetActive(true);
}
}
}
public class NewBullet : MonoBehaviour
{
public ObjectPool<NewBullet> objPool;
void OnTriggerEnter(Collider other)
{
// ์ถฉ๋์ ๋ฆด๋ฆฌ์ฆ ๋ค์ ํ๋ก
objPool.Release(this);
}
}
ํฌํ ํ๋ฆฌํฉ๋ค์ ๋ง๋ค๊ณ ์์ ์ฝ๋๋ค์ ์ถ๊ฐํ๋ค.
๋ด์ฅ ํ๋ง์ด ๋ฒ์จ ์ฝ๋ ๊ธธ์ด๊ฐ ํ ๋๋ค. ๐ฎ
// ์ด์, ์ดํํธ
public GameObject bullet;
public GameObject bulletEffect;
// ์ด์ ๋ฐฐ์ด, ์ดํํธ ๋ฐฐ์ด
public GameObject[] bullets;
public GameObject[] bulletEffects;
// ์ปค์คํ
ํ
// Awake()
bullets = new GameObject[5];
for (int i = 0; i < bullets.Length; i++)
{
bullets[i] = Instantiate(bullet, bulletBox.transform);
bullets[i].SetActive(false);
}
bulletEffects = new GameObject[15];
for (int i = 0; i < bulletEffects.Length; i++)
{
bulletEffects[i] = Instantiate(bulletEffect, effectBox.transform);
bulletEffects[i].SetActive(false);
}
// ๋ฐํ ํจ์
GameObject GetBullet()
{
for (int i = 0; i < bullets.Length; i++)
{
if (!bullets[i].activeSelf)
return bullets[i];
}
return null;
}
public GameObject GetEffect()
{
for (int i = 0; i < bulletEffects.Length; i++)
{
if (!bulletEffects[i].activeSelf)
return bulletEffects[i];
}
return null;
}
๋ฐ์ฌํ๊ธฐ ์ , Tank ํด๋์ค์ ์ด์๊ณผ ์ดํํธ๋ฅผ ๋ณด๊ด, ๋ฐํํ๋ ํ๋์ ํจ์๋ฅผ ๋ง๋ค์๋ค.
bullet ํฌํ ํ๋ฆฌํฉ
bullets ๋ฏธ๋ฆฌ ์์ฑํ ํฌํ ์ค๋ธ์ ํธ๋ค์ ๋ด์ ๋ฐฐ์ด
bulletEffect ํฌํ ํฐ์ง๋ ์ดํํธ
bulletEffects ๋ฏธ๋ฆฌ ์์ฑํ ํฌํ ์ดํํธ ์ค๋ธ์ ํธ๋ค์ ๋ด์ ๋ฐฐ์ด
GetBullet()์ ์ฌ์ฉ์ค์ด์ง ์์ ํฌํ ์ค๋ธ์ ํธ๋ฅผ ๋ฐํ
GetEffect()๋ ์ฌ์ฉ์ค์ด์ง ์์ ์ดํํธ ์ค๋ธ์ ํธ๋ฅผ ๋ฐํ
Awake() ํจ์์์ ์ด๊ธฐํ๋ ๋ Instantiate() ํจ์๋ก ์ธ์คํด์คํํ ํ ๋ณด์ด์ง ์๊ฒ ๋นํ์ฑํ ํด๋๋ค.
// ๋ด์ฅ ํ
newBulletPool = new ObjectPool<NewBullet>(
// Func < T > createFunc
() => Instantiate(newBullet, bulletStart.transform.position, bulletStart.transform.rotation),
// Action<T> actionOnGet
bullet => {
// ์ด์์ ์ค๋ธ์ ํธ
GameObject newBulletObject = bullet.gameObject;
// NewBullet ์ปดํฌ๋ํธ
NewBullet newBulletLogic = bullet.GetComponent<NewBullet>();
// ์์น ์ง์
newBulletObject.transform.position = bulletStart.transform.position;
newBulletObject.transform.rotation = bulletStart.transform.rotation;
// NewBullet ํ๋์ ํฑํฌ์ Release๋ฅผ ์ํ ํ ์ ๋ฌ(๊ฒ์๋งค๋์ ๊ฐ์์ด์)
newBulletLogic.objPool = newBulletPool;
// ์ด์ ํ์ฑํ
newBulletObject.SetActive(true);
// 30f ํ์๋ก ๋ฐ์ฌ
Rigidbody bulletRigid = newBulletObject.GetComponent<Rigidbody>();
bulletRigid.AddForce(newBulletObject.transform.forward * 30f, ForceMode.Impulse);
},
// Action<T> actionOnRelease
bullet => {
// ์๋ 0๋ก ๋ค์์ ํ์ฑํ๋ ๋ ์ค์ฒฉ๋จ
bullet.GetComponent<Rigidbody>().velocity = Vector3.zero;
// ์ดํํธ
GameObject effect = GetEffect();
if (effect != null)
{
effect.transform.position = bullet.transform.position;
effect.transform.rotation = bullet.transform.rotation;
effect.SetActive(true); // ์ดํํธ์์ ์๋์ผ๋ก ๋นํ์ฑํ
}
// ์ด์ ๋นํ์ฑํ
bullet.gameObject.SetActive(false);
},
// Action<T> actionOnDestroy
bullet => Destroy(bullet.gameObject),
// bool Collection Check ์ด๋์ ์ฐ์ง?
true,
// int defaultCapacity, int maxSize
5, 5
);

๋ด์ฅ ์ค๋ธ์ ํธ ํ๋ง์ ํ๋ง์ ์ด๊ธฐํํ ๋ ๋๋ถ๋ถ์ ์ํฉ? ์ ์ ํด๋๋ ๋๋์ธ ๊ฒ ๊ฐ๋ค.
Func T createFunc : ํ์ ์ค๋ธ์ ํธ๊ฐ ์์ ๋
Action T actionOnGet : Get()์ผ๋ก ๊บผ๋ผ ๋
Action T actionOnRelease : Release()๋ก ๋ฐํํ ๋
Action T actionOnDestroy : ์นด์ดํธ๋ฅผ ์ด๊ณผํ์ฌ ํ๊ดดํ ๋
collectionCheck : ๋๋ฒ๊น ์ฉ์ด๋ผ๋๋ฐ ์ด๋ป๊ฒ ์ฐ๋์ง ๋ชจ๋ฅด๊ฒ ๋ค..
defaultCapacity : ์ต์ ๊ฐ์
maxSize : ์ต๋ ๊ฐ์
// Update()
// ์ปค์คํ
ํ ํฌํ ๋ฐ์ฌ
if (Input.GetKey(KeyCode.Space))
{
// ์ฟจ์ผ๋๋ ํ์ ๊ณ์ ์ด๊ธฐํ = ์ฅ์ ๋ชปํด๋๊ฒ
if (curCool < maxCool)
curPower = 0;
if (curPower < maxPower)
curPower += Time.deltaTime * 30f; // ๋๋ฅด๋๋์ ํ์์์น
}
if (Input.GetKeyUp(KeyCode.Space))
{
if (curCool < maxCool)
{
curPower = 0;
return;
}
// ์ต์ ํ์ 20
if (curPower < 20f)
curPower = 20f;
// ํฌํ ๋ฐ์ฌ
GameObject newbullet = GetBullet();
if (newbullet != null)
{
Bullet bulletLogic = newbullet.GetComponent<Bullet>();
newbullet.transform.position = bulletStart.transform.position;
newbullet.transform.rotation = bulletStart.transform.rotation;
newbullet.SetActive(true);
bulletLogic.tank = this;
Rigidbody bulletRigid = newbullet.GetComponent<Rigidbody>();
bulletRigid.AddForce(newbullet.transform.forward * curPower, ForceMode.Impulse);
}
// ํ์ ์ฟจ ์ด๊ธฐํ
curPower = 0;
curCool = 0;
}
ํฑํฌ TankTurret์ ์์์ผ๋ก ๋น ์ค๋ธ์ ํธ๋ฅผ ์์ฑ, ์ด ์์น์ ํฌํ์ ์์ฑํ๋ค.

์คํ์ด์ค๋ฐ๋ฅผ ๋๋ฅด๊ณ ์์ผ๋ฉด ์ต๋ 50f ๊น์ง ํ์๊ฐ ์์ด๊ณ ํค๋ฅผ ๋ผ๋ฉด ๋ฐ์ฌํ๋ ์ฐจ์ง ๊ธฐ๋ฅ๊ณผ
์ฟจํ์ ์ค์๋ ๋ฐ์ฌ๋ฅผ ๋ชปํ๊ฒ ํ๋ ์ฟจํ์ ๊ธฐ๋ฅ๋ ์ถ๊ฐํ๋ค.
๊ธฐ๋ณธ์ ์ธ ๋ฐ์ฌ ํ๋ฆ์
1. ์คํ์ด์ค๋ฐ ํค๋ฅผ ๋๋ฆ
2. ํฑํฌ์ ํฌ์ ์ ํ๊ณต์ ์์น๋ฅผ ๊ณ์ฐํด์ ๋ณ์๋ก ์ ์ฅ spawnPos, spawnRot
3. Getbullet() ํจ์์์ ์ฌ์ฉ์ค์ด์ง ์์ ํฌํ ์ค๋ธ์ ํธ๋ฅผ ๋ฐ์์ด
4. ํฌํ ์ค๋ธ์ ํธ์ ์์น, ํ์ ๋ณ๊ฒฝ
5. ํฌํ์ Rigidbody๋ฅผ ๊ฐ์ ธ์ AddForce() ๋ก ํ ์ ์ฉ
6. ์~ ๐

์์ ์ ์ด๋ Bullet ํด๋์ค์ ์ฝ๋์์
void OnTriggerEnter(Collider other)
{
gameObject.SetActive(false);
rigid.velocity = Vector3.zero;
GameObject effect = tank.GetEffect();
if (effect != null)
{
effect.transform.position = transform.position;
effect.transform.rotation = Quaternion.identity;
effect.SetActive(true);
}
}
ํฌํ์ด ๋ค๋ฅธ ์ค๋ธ์ ํธ์ ๋ถ๋ช์น๋ฉด ํฌํ์ ๋นํ์ฑํํ๊ณ , ์๋๋ฅผ 0์ผ๋ก ๋ง๋ ๋ค.
๊ทธ๋ฆฌ๊ณ ๊ทธ ์์น์ ์ค๋ธ์ ํธ ํ๋ง์์ ์ดํํธ๋ฅผ ๋ฐํ๋ฐ์ ํฌํ์ด ํฐ์ง๋ ์ดํํธ๋ฅผ ์คํํ๋ค.
// ๋ด์ฅ ํ ๋ฐ์ฌ
if (Input.GetKeyDown(KeyCode.LeftControl))
{
// ์ค๋ธ์ ํธ ํ์์ ๊ฐ์ ธ์ด
newBulletPool.Get();
}
๋ด์ฅ ์ค๋ธ์ ํธ ํ๋ง์ ์ด๊ธฐํํ ๋ ์ํฉ์ ๋ฐ๋ฅธ ์ฝ๋๋ฅผ ๋ฃ์๊ธฐ์,
์ปค์คํ ํ๋ง์ ๋นํ๋ฉด ์์ฒญ๋๊ฒ ์งง๋ค.

์์ง ๋ถ์กฑํด ๋ณด์ด์ง๋ง ์ปค์คํ ์ค๋ธ์ ํธ ํ๋ง๊ณผ ๋ด์ฅ ์ค๋ธ์ ํธ ํ๋ง์ผ๋ก ๋ฐ์ฌ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ค.
ํ์คํ ์ค๊ณ๋ฅผ ํ๊ณ ์ฝ๋ฉ์ ํ๋ค๋ฉด ๋ฏธ๋ฆฌ ์ํฉ์ ๋ฐ๋ฅธ ์ฝ๋๋ฅผ ๋ง๋ค๊ณ ,
๊ธฐ๋ฅ ์์ ์ ์ด๊ธฐํํ๋ ์์น์์ ํ๋ฉด๋๋ ๋ด์ฅ ์ค๋ธ์ ํธ ํ๋ง์ด ํธํด๋ณด์ธ๋ค.