How can i make a rotating cross bullet pattern?

Godot Version

gd4

Question

should be self explanatory, daze. a simple cross bullet hell pattern that spins. but when i tried coding it in(with help from a friend) , this was the result.


bullets are being spawned from left to right. thats not the pattern that’s supposed to come out…

this is the pattern im trying to make for comparison


please dont mind the inconsistency with the arrows

spawnpoint code:

extends Node2D

## this spawnpoint will spawn cross rotating shaped patterns.

const DANMAKU_BULLET_1 = preload("res://scenes/danmaku_bullet/danmaku_bullet_1.tscn")
@onready var danmaku_timer = $DanmakuTimer

@onready var spawner4 = $Spawner4
@onready var spawner3 = $Spawner3
@onready var spawner2 = $Spawner2
@onready var spawner1 = $Spawner1

var pr : Node2D

# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta):
	self.rotation_degrees += 4

func spawn_projectile():
	var clone1 = DANMAKU_BULLET_1.instantiate()
	owner.add_child(clone1)
	clone1.bulletlifetime = 2
	clone1.transform = spawner1.global_transform
	
	var clone2 = DANMAKU_BULLET_1.instantiate()
	owner.add_child(clone2)
	clone2.bulletlifetime = 2
	clone2.transform = spawner2.global_transform

	var clone3 = DANMAKU_BULLET_1.instantiate()
	owner.add_child(clone3)
	clone3.bulletlifetime = 2
	clone3.transform = spawner3.global_transform

	var clone4 = DANMAKU_BULLET_1.instantiate()
	owner.add_child(clone4)
	clone4.bulletlifetime = 2
	clone4.transform = spawner4.global_transform
	
func _on_danmaku_timer_timeout():
	spawn_projectile()

bullet code:

extends Node2D

## FOR FUTURE ME: UNLESS SOMETHING *NEEDS* TO BE CHANGED OR FIXED, pls dont
## do anything to this code. it works well enough already.

@export var bulletlifetime = 1
@export var speed = 100

@onready var lifetime = $Lifetime

func _ready():
	lifetime.wait_time = bulletlifetime
	lifetime.start()

func _physics_process(delta):
	position.x += 1 * speed * delta

func _on_lifetime_timeout():
	queue_free()

I believe you will need rotational speed added to the bullet code. Currently your speed only applies to position.x. There are a couple ways to do this, I will example using godot’s built-in rotation property; this will also rotate the sprite. Your system was taking advantage of inherting rotation, but I believe it will be easier to reason about each bullet’s rotation.

@export var rotational_speed: float = 0

func _physics_process(delta: float) -> void:
	rotation += rotational_speed * delta

	var direction := Vector2.RIGHT.rotated(rotation)
	# applies to both axis
	position += direction * speed * delta

Now calculating how much rotational_speed would be based on the circumference of each layer. I got nerd sniped on this one, ended up making this script to spawn said bullets in a rotating cross

extends Marker2D

@export var projectile: PackedScene
@export var layer_radius: float = 20
@export var layer_count: int = 4
@export var total_layers: int = 6
@export var rotations_per_second: float = 0.5

func _ready() -> void:
	for i in range(layer_count * total_layers):
		# spawn grid coordinates
		var x := i % layer_count
		var y := floori(i / layer_count)

		# convert grid coordinates to circular/polar
		var spawn_rotation := x * TAU / layer_count
		var spawn_radius := (y + 1) * layer_radius

		var bullet := projectile.instantiate() as Bullet
		# start with rotation facing inward
		bullet.rotation = spawn_rotation + PI / 2

		# speed calculations, based on rotations per second
		bullet.rotational_speed = TAU * rotations_per_second
		var layer_circumference := TAU * spawn_radius
		bullet.speed = layer_circumference * rotations_per_second

		# debug modulate so I can track each bullet
		bullet.modulate = Color8(x * 255 / layer_count, y * 255 / total_layers, 255)

		# circular spawn position
		var xpos := cos(spawn_rotation) * spawn_radius
		var ypos := sin(spawn_rotation) * spawn_radius

		# offset by spawner's `self.position` 
		bullet.position = Vector2(xpos, ypos) + self.position
		add_sibling.call_deferred(bullet)

Here’s a screenshot of my tests, with varying layer radius, and counts.

1 Like