Unity 2D Bullet Hell Tutorial | Unity Tutorial
In this tutorial, we'll explore how to create the iconic Bullet Hell effect in Unity 2D. You’ll learn how to design and implement complex bullet patterns that overwhelm the player, including techniques for precise bullet movement and spawning. We’ll cover how to use Unity's particle systems, script intricate bullet behaviors, and optimize performance for smooth gameplay. By the end of this tutorial, you'll be equipped to add intense, visually stunning Bullet Hell effects to your games, enhancing the challenge and excitement for players.
WATCH FULL TUTORIAL ON YOUTUBE
=========================================================
3 Unity Projects Only $50 - ON SALE!!!!
Contact: unitycoderz2022@gmail.com
=========================================================
Scripts:
Bullet.cs
using UnityEngine;
public class Bullet : MonoBehaviour
{
public float bulletSpeed = 10f; // Speed of the bullet
private Vector2 shootDirection; // Direction in which the bullet will move
void Start()
{
// Optionally initialize other properties if needed
}
private void OnEnable()
{
// Start the destroy countdown when the bullet is enabled
Invoke("Deactivate", 3f);
}
void Update()
{
// Move the bullet in the set direction with the defined speed
transform.Translate(shootDirection * bulletSpeed * Time.deltaTime);
}
// Method to set the direction of the bullet
public void SetShootDirection(Vector2 dir)
{
shootDirection = dir.normalized; // Set direction and normalize it
}
// Method to deactivate the bullet instead of using Unity's Destroy method
private void Deactivate()
{
gameObject.SetActive(false);
}
private void OnDisable()
{
// Cancel any pending invokes when the bullet is disabled
CancelInvoke();
}
}
--------------------------------------------------------------------------------------------------------------------------------
ObjectPool.cs
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool : MonoBehaviour
{
public static ObjectPool Instance;
[SerializeField]
private GameObject pooledBullet; // The bullet prefab to pool
private List<GameObject> bullets; // List to store pooled bullets
private bool notEnoughBulletsInPool = true; // Flag to indicate if we need to instantiate new bullets
void Awake()
{
// Set up singleton pattern
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject); // Optional: keep this instance across scenes
}
else
{
Destroy(gameObject);
}
}
void Start()
{
bullets = new List<GameObject>();
}
public GameObject LoadBullets()
{
// Check if we have any inactive bullets in the pool
for (int i = 0; i < bullets.Count; i++)
{
if (!bullets[i].activeInHierarchy)
{
// Return the first inactive bullet found
return bullets[i];
}
}
// If no inactive bullets were found, create a new one
if (notEnoughBulletsInPool)
{
GameObject newBullet = Instantiate(pooledBullet);
newBullet.SetActive(false);
bullets.Add(newBullet);
return newBullet;
}
return null; // No available bullets
}
}
-----------------------------------------------------------------------------------------------------------------------------------------
BulletShooter.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletShooter : MonoBehaviour
{
[SerializeField]
private int bulletAmount = 10; // Number of bullets to fire
[SerializeField]
private float startAngle = 90f; // Starting angle of the firing pattern
[SerializeField]
private float endAngle = 270f; // Ending angle of the firing pattern
void Update()
{
// Check if the Space bar is pressed
if (Input.GetKeyDown(KeyCode.Space))
{
Fire();
}
}
public void Fire()
{
float angleStep = (endAngle - startAngle) / bulletAmount;
float angle = startAngle;
for (int i = 0; i < bulletAmount; i++)
{
float bulDirX = transform.position.x + Mathf.Cos(angle * Mathf.Deg2Rad);
float bulDirY = transform.position.y + Mathf.Sin(angle * Mathf.Deg2Rad);
Vector2 bulMoveVector = new Vector2(bulDirX, bulDirY);
Vector2 bulDir = (bulMoveVector - (Vector2)transform.position).normalized;
GameObject bul = ObjectPool.Instance.LoadBullets();
if (bul != null)
{
bul.transform.position = transform.position;
bul.transform.rotation = Quaternion.identity;
Bullet bulletScript = bul.GetComponent<Bullet>();
if (bulletScript != null)
{
bulletScript.SetShootDirection(bulDir);
}
bul.SetActive(true);
angle += angleStep;
}
}
}
}