2023년 12월 13일 수요일

Unity making spreading bullet (방사 형태의 총알 발사 만들기)

2D 슈팅 게임을 만들어 보고 있는데 여러가지 형태의 총알 발사 형태가 필요해서 구현해 보았습니다.

Player 또는 적 -> 총알 발사하게 되는데 이것을 여기에서는 BulletGroupTest Object 생성하고 BulletGroupTest 는 여러발의 BulletTest(총알)을 생성 하도록 샘플을 만들어 보았습니다.


실제 동작 화면부터 보겠습니다.




가장 핵심의 되는 코드부터 설명하겠습니다.

BulletGroupTest -> BulletTest 를 생성 하는 코드

    public void Init()
    {
        // < 방사형
        if (bulletType == 0)
        {
            Vector3 targetPos = target.position;
            Vector3 dir = targetPos - transform.position;
            dir = dir.normalized;
            float baseAngle = 10f;
            float startAngle = -Mathf.Floor((bulletCount - 1) / 2) * (baseAngle) - baseAngle / 2 * (1 - bulletCount % 2);
            Vector3 curVec = Rotate_z(dir, startAngle);

            for (int i = 0; i < bulletCount; i++)
            {
                GameObject bullet = Instantiate(bulletPrefabPoolId, transform);
                bullet.transform.position = transform.position;
                bullet.transform.rotation = Quaternion.FromToRotation(Vector3.up, curVec);
                bullet.GetComponent<BulletTest>().Init(bulletDamage, bulletSpeed, bulletPiercing, curVec, bulletLifeTime);
                startAngle += baseAngle;
                curVec = Rotate_z(dir, startAngle);
            }
        }
        Destroy(gameObject,3f);
    }
    Vector3 Rotate_z(Vector3 dir, float angle)
    {
        // angle을 라디안으로 변환합니다.
        float radian = angle * Mathf.Deg2Rad;
        Vector3 ret = new Vector3(0, 0, 0);
        // 삼각함수를 이용하여 ret의 x, y, z 세 원소를 계산합니다.
        ret.x = dir.x * Mathf.Cos(radian) - dir.y * Mathf.Sin(radian); // x 방향으로 회전합니다.
        ret.y = dir.x * Mathf.Sin(radian) + dir.y * Mathf.Cos(radian); // y 방향으로 회전합니다.
        ret.z = dir.z; // z 방향은 변하지 않습니다.
        // ret를 결과로 반환합니다.
        return ret;
    }

이 부분이 하는 핵심 코드 입니다.

Bullet 를 생성하고 탄환을 일정 각도로 회전을 줍니다. 여기에서는 10 도 만큼 회전시키는 코드입니다. Rotate_z 함수는 Vector3 를 z축으로 특정 각도 만큼 회전시키는 함수입니다.


TestPlayer 코드 입니다.

해당 코드는 TestPlayer에 Componet로 추가 되어 있습니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestPlayer : MonoBehaviour
{
    public GameObject bulletGroup;
    public int bulletCount = 3;
    public Transform target;
    public float speed = 100f;

    Rigidbody2D rb;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
    }
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        float moveX;
        float moveY;

        moveX = Input.GetAxis("Horizontal");
        moveY = Input.GetAxis("Vertical");
        Vector3 moveVector = new Vector3(moveX, moveY, 0f);
        rb.velocity = moveVector.normalized * Time.fixedDeltaTime * speed;


        if (Input.GetButtonDown("Jump"))
        {
            GameObject bGroup = Instantiate(bulletGroup, transform);
            BulletGroupTest bGT = bGroup.GetComponent<BulletGroupTest>();
            bGT.bulletType = 0;
            bGT.bulletCount = bulletCount;
            bGT.bulletSpeed = 5;
            bGT.target = target;
            bGT.Init();
        }
    }
}

방향키로 움직이고 Jump키를 누르면 Bullet Group를 생성 시키는 함수 입니다.


앞서 BulletGroupTest의 핵심 코드를 설명하긴 했지만 전체 코드 입니다.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletGroupTest : MonoBehaviour
{
    public int bulletType; // 0:총알 형태
    public int bulletCount;
    public float bulletDamage;
    public float bulletSpeed;
    public int bulletPiercing;
    public float bulletLifeTime;
    public Transform target;
    public GameObject bulletPrefabPoolId;
    public float bulletSize;

    void Awake()
    {

    }
    public void Init()
    {
        // < 방사형
        if (bulletType == 0)
        {
            Vector3 targetPos = target.position;
            Vector3 dir = targetPos - transform.position;
            dir = dir.normalized;
            float baseAngle = 10f;
            float startAngle = -Mathf.Floor((bulletCount - 1) / 2) * (baseAngle) - baseAngle / 2 * (1 - bulletCount % 2);
            Vector3 curVec = Rotate_z(dir, startAngle);

            for (int i = 0; i < bulletCount; i++)
            {
                GameObject bullet = Instantiate(bulletPrefabPoolId, transform);
                bullet.transform.position = transform.position;
                bullet.transform.rotation = Quaternion.FromToRotation(Vector3.up, curVec);
                bullet.GetComponent<BulletTest>().Init(bulletDamage, bulletSpeed, bulletPiercing, curVec, bulletLifeTime);
                startAngle += baseAngle;
                curVec = Rotate_z(dir, startAngle);
            }
        }
        Destroy(gameObject,3f);
    }
    Vector3 Rotate_z(Vector3 dir, float angle)
    {
        // angle을 라디안으로 변환합니다.
        float radian = angle * Mathf.Deg2Rad;
        Vector3 ret = new Vector3(0, 0, 0);
        // 삼각함수를 이용하여 ret의 x, y, z 세 원소를 계산합니다.
        ret.x = dir.x * Mathf.Cos(radian) - dir.y * Mathf.Sin(radian); // x 방향으로 회전합니다.
        ret.y = dir.x * Mathf.Sin(radian) + dir.y * Mathf.Cos(radian); // y 방향으로 회전합니다.
        ret.z = dir.z; // z 방향은 변하지 않습니다.
        // ret를 결과로 반환합니다.
        return ret;
    }
}



이번에는 날아가는 총알에 대한 코드입니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletTest : MonoBehaviour
{
    public float damage;
    public float speed;
    public int piercing;
    public float lifeTime;

    Rigidbody2D rigid;
    Animator animator;
    float accTime;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
    }
    public void Init(float damage, float speed, int piercing, Vector3 dir, float lifeTime, bool autoDie = false)
    {
        Debug.Log(speed);
        Debug.Log(dir);
        this.damage = damage;
        this.piercing = piercing;
        this.speed = speed;
        this.lifeTime = lifeTime;
        this.accTime = 0;
        rigid.velocity = dir * speed;
    }
    public void FixedUpdate()
    {
        accTime += Time.fixedDeltaTime;
        if (accTime > lifeTime)
        {
            Destroy(gameObject);
        }
    }

}

BulletTest 는 총알 이미지로 Prefab화 시켰습니다.




댓글 없음:

댓글 쓰기