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.
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.