132 lines
3.3 KiB
GDScript
132 lines
3.3 KiB
GDScript
@tool
|
|
@icon("bullet.svg")
|
|
class_name Bullet
|
|
extends Area2D
|
|
|
|
|
|
## Emitted whenever a bullet is recycled.
|
|
signal recycled()
|
|
|
|
|
|
## The number of bullets to allocate at startup.
|
|
const INITIAL_ALLOCATED_BULLETS: int = 2000
|
|
|
|
|
|
## Texture to draw for the bullet.
|
|
@export var texture: Texture2D = null:
|
|
set(value):
|
|
texture = value
|
|
queue_redraw()
|
|
_update_visibility_notifier()
|
|
|
|
## Size of the bullet's collision box.
|
|
@export var hitbox_size: Vector2i = Vector2i.ZERO:
|
|
set(value):
|
|
hitbox_size = value
|
|
if not _hitbox_shapes.has(hitbox_size):
|
|
var new_shape = RectangleShape2D.new()
|
|
new_shape.size = hitbox_size
|
|
_hitbox_shapes[hitbox_size] = new_shape
|
|
_hitbox.shape = _hitbox_shapes[hitbox_size]
|
|
|
|
## The direction that the bullet is travelling.
|
|
@export_custom(0, "direction") var direction: Vector2 = Vector2.RIGHT
|
|
|
|
## If [code]true[/code], the bullet will always rotate to face toward [member direction].
|
|
@export var face_direction: bool = false
|
|
|
|
|
|
## The amount of time in seconds that the bullet has existed.
|
|
var time_elapsed: float = 0.0
|
|
|
|
|
|
static var _cached_bullets: Array[Bullet] = []
|
|
static var _hitbox_shapes: Dictionary[Vector2i, RectangleShape2D] = {}
|
|
|
|
|
|
var _hitbox: CollisionShape2D = CollisionShape2D.new()
|
|
|
|
|
|
## Returns a new [Bullet], which may be sourced from the cached bullets.
|
|
@warning_ignore("shadowed_variable")
|
|
static func create(
|
|
texture: Texture2D = null,
|
|
hitbox_size: Vector2i = Vector2i.ZERO,
|
|
direction: Vector2 = Vector2.RIGHT,
|
|
face_direction: bool = false,
|
|
) -> Bullet:
|
|
#var bullet: Bullet = _cached_bullets.pop_back()
|
|
#if not bullet:
|
|
#bullet = Bullet.new()
|
|
var bullet := Bullet.new()
|
|
|
|
bullet.texture = texture
|
|
bullet.hitbox_size = hitbox_size
|
|
bullet.direction = direction
|
|
bullet.face_direction = face_direction
|
|
bullet.time_elapsed = 0.0
|
|
|
|
return bullet
|
|
|
|
|
|
## Removes the bullet from the scene tree and returns it to the bullet cache to be
|
|
## re-used later.
|
|
func recycle() -> void:
|
|
#if is_inside_tree():
|
|
#get_parent().remove_child(self)
|
|
#_cached_bullets.append(self)
|
|
queue_free()
|
|
recycled.emit()
|
|
|
|
|
|
static func _static_init() -> void:
|
|
for _i in INITIAL_ALLOCATED_BULLETS:
|
|
_cached_bullets.append(Bullet.new())
|
|
|
|
|
|
func _init() -> void:
|
|
monitoring = false
|
|
add_to_group(&"bullets")
|
|
_hitbox.debug_color.a = 0.0
|
|
add_child(_hitbox)
|
|
|
|
|
|
func _enter_tree() -> void:
|
|
_update_visibility_notifier()
|
|
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
if not Engine.is_editor_hint():
|
|
time_elapsed += delta
|
|
|
|
if face_direction:
|
|
rotation = direction.angle()
|
|
|
|
|
|
func _draw() -> void:
|
|
if texture:
|
|
draw_texture(texture, -texture.get_size() * 0.5)
|
|
|
|
|
|
func _get_configuration_warnings() -> PackedStringArray:
|
|
return []
|
|
|
|
|
|
# sets the canvas item up to notify when it leaves the screen
|
|
# this essentially mimics `VisibleOnScreenNotifier` without an additional node
|
|
func _update_visibility_notifier() -> void:
|
|
if not texture or not is_inside_tree() or Engine.is_editor_hint():
|
|
return
|
|
|
|
# the visibility rect is set to twice the size of the texture to add a little margin
|
|
# (func(): pass) is the cleanest way i could think of to have a callback that does nothing
|
|
RenderingServer.canvas_item_set_visibility_notifier(
|
|
get_canvas_item(), true,
|
|
Rect2(-texture.get_size(), texture.get_size() * 2.0),
|
|
(func(): pass), _on_screen_exited
|
|
)
|
|
|
|
|
|
# called when the bullet leaves the screen.
|
|
func _on_screen_exited() -> void:
|
|
recycle()
|