Destroy part of tilemap on projectile hit

Godot Version

Godot_v4.2.2

Question

Hi, so i have a problem with my projectile
Sometimes when i hit part of tilemap it does not destroy that part of the tile map, especially on the edges.
Also i need to set the speed of the bullet to extremely low values to even destroy the block that the bullet hit first and not 4 tiles behind it
Here is the code for the projectile2.gd:

extends Area2D

@export var speed: float = 300.0
@export var lifetime: float = 10.0  # Time until bullet is destroyed
var velocity: Vector2 = Vector2.ZERO  # Default position
var hit_registered: bool = false  # Hit flag
@onready var tile_map = $TileMap  # Reference to the TileMap
@onready var sprite = $Sprite2D  # Reference to the sprite
@onready var collision_shape = $CollisionShape2D  # Reference to the collision shape

const DEATH_PARTICLE_SCENE = preload("res://scenes/DeathParticle.tscn")  # Death particle effect

func _ready():
	# Set up the timer to destroy the bullet after lifetime expires
	var timer = Timer.new()
	timer.wait_time = lifetime
	timer.one_shot = true
	timer.timeout.connect(_on_timeout)
	add_child(timer)
	timer.start()
	# Connect the body_entered signal for collision detection
	connect("body_entered", _on_body_entered)

func _process(delta):
	# Move the bullet according to its velocity
	position += velocity * delta  # Manual movement of the bullet

func destroy_tile(tilemap: TileMap):
	var tile_position = tilemap.local_to_map(position.round())  # Zaokrąglij pozycję pocisku
	var tile_source_id = tilemap.get_cell_source_id(0, tile_position)  # Pobierz źródło kafelka w warstwie 0

	if tile_source_id != -1:  # check if it is actually a part of tilemap
		tilemap.set_cell(0, tile_position, -1)  # est to -1, to delete tile

func _on_body_entered(body):
	if hit_registered:
		return  # Ignore further collisions if already hit

	if body.is_class("TileMap"):
		# Call tile destruction method
		destroy_tile(body)  # Pass the TileMap as an argument
		queue_free()  # Remove the bullet
	elif body.name == "CharacterBody2D":
		# Ignore collision with CharacterBody2D and continue the bullet's path
		return
	else:
		# Bullet hit an enemy or other object
		hit_registered = true  # Register the hit
		sprite.visible = false  # Hide bullet sprite
		collision_shape.disabled = true  # Disable bullet collision

		# Check if the object has apply_damage method
		if body.has_method("apply_damage"):
			body.apply_damage(20)  # Deal 20 damage

			# Add death particle effect if bullet hits an enemy
			var death_particle = DEATH_PARTICLE_SCENE.instantiate()
			add_child(death_particle)
			death_particle.global_position = body.global_position
			death_particle.emitting = true

		# Wait 0.2 seconds before removing the bullet
		await get_tree().create_timer(0.2).timeout
		queue_free()  # Remove the bullet

func _on_timeout():
	# Remove the bullet after lifetime expires
	queue_free()

and here is code for the player(main_character):

extends CharacterBody2D

var double_jump = 0
var max_jumps = 2
const SPEED = 375.0
const RUN_SPEED = 500.0
const JUMP_VELOCITY = -760.0
var current_speed = SPEED
var last_direction = 1
var is_weapon_active = false
var shooting = false
var shoot_timer = 0.0
var shoot_interval = 0.05
var fire_mode = "single"  # Fire mode: 'single' or 'auto'
var initial_visibility = {}  # Map to store visibility state

@onready var sprite_2d_3 = $Sprite2D3
@onready var sprite_2d_4 = $Sprite2D4
@onready var sprite_2d_5 = $Sprite2D5
@onready var sprite_2d_6 = $Sprite2D6  # New sprite

@export var death_particle: PackedScene
@onready var sprite_2d = $Sprite2D
@onready var kalach_sprite = $Sprite2D2
@onready var collectable = $"../coll/Collectable"
@onready var jump_sound = $jump
@onready var bieganie = $bieganie
@onready var bljaaaaa = $bljaaaaa
@onready var kill1 = $kill1
@onready var randomk = $randomk
@export var particle: PackedScene
@export var projectile_scene: PackedScene

# Magazine
var magazine_size = 30  # Magazine capacity
var current_ammo: int = magazine_size  # Current ammo in the magazine
var reload_time: float = 5.0  # Reload time in seconds
var is_reloading: bool = false  # Flag to control if the weapon is reloading

var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

func _ready():
	kalach_sprite.visible = false
	set_weapon_sprites_visibility()

func jump():
	velocity.y = JUMP_VELOCITY
	spawn_particle()

func spawn_particle():
	var particle_node = particle.instantiate()
	particle_node.position = position
	get_parent().add_child(particle_node)
	await get_tree().create_timer(0.3).timeout
	particle_node.queue_free()

func _physics_process(delta):
	var speed = SPEED

	# Check if weapon is active and aiming with the weapon
	if is_weapon_active:
		aim_weapon()

	# Adding gravity
	if not is_on_floor():
		velocity.y += gravity * delta
		if double_jump == 2:
			sprite_2d.animation = "test"
		else:
			sprite_2d.animation = "jumping"
	else:
		double_jump = 0
		if velocity.x == 0:
			sprite_2d.animation = "default"
		else:
			sprite_2d.animation = "running"

	# Jump input handling
	if Input.is_action_just_pressed("jump") and double_jump < max_jumps:
		jump_sound.play()
		jump()
		double_jump += 1
		if double_jump == 2:
			spawn_particle()

	# Direction handling
	var direction = Input.get_axis("left", "right")

	# Running handling
	if Input.is_action_pressed("run") and is_on_floor():
		current_speed = RUN_SPEED
		speed = RUN_SPEED
		if direction != 0:
			sprite_2d.animation = "running"
		else:
			sprite_2d.animation = "default"
	elif is_on_floor():
		current_speed = SPEED

	# Character movement
	if direction != 0:
		velocity.x = direction * current_speed
		last_direction = direction
	else:
		velocity.x = move_toward(velocity.x, 0, 70)

	move_and_slide()

	# Flip the sprite depending on the last movement direction
	if last_direction < 0:
		sprite_2d.flip_h = true
	elif last_direction > 0:
		sprite_2d.flip_h = false

	# Toggle weapon activation
	if Input.is_action_just_pressed("weapon_toggle"):
		toggle_weapon()

	# Toggle fire mode
	if Input.is_action_just_pressed("toggle_fire_mode"):
		toggle_fire_mode()
		set_weapon_sprites_visibility()  # Set weapon sprites visibility after switching fire mode

	# Shooting logic
	if is_weapon_active:
		if is_reloading:
			shooting = false
		else:
			if fire_mode == "auto" and Input.is_action_pressed("shoot"):
				if current_ammo > 0:
					shooting = true
				else:
					start_reloading()  # Start reloading if no ammo
			elif fire_mode == "single" and Input.is_action_just_pressed("shoot"):
				if current_ammo > 0:
					shoot_projectile()  # Single shot
					current_ammo -= 1  # Decrease ammo count
				else:
					start_reloading()  # Start reloading if no ammo
			else:
				shooting = false
				shoot_timer = shoot_interval  # Reset shoot timer after shooting ends

		# Continuous shooting in auto mode
		if shooting and fire_mode == "auto":
			shoot_timer -= delta
			if shoot_timer <= 0:
				shoot_projectile()
				current_ammo -= 1  # Decrease ammo count
				shoot_timer = shoot_interval

	# Set weapon sprites visibility based on the kalach_sprite state
	set_weapon_sprites_visibility()

# Function to toggle fire mode
func toggle_fire_mode():
	if fire_mode == "single":
		fire_mode = "auto"
		print("Fire mode switched to automatic.")
	else:
		fire_mode = "single"
		print("Fire mode switched to single shot.")

# Function to toggle weapon visibility (kalach)
func toggle_weapon():
	is_weapon_active = not is_weapon_active
	kalach_sprite.visible = is_weapon_active
	set_weapon_sprites_visibility()  # Set weapon sprites visibility

# Function to set weapon sprites visibility
func set_weapon_sprites_visibility():
	var is_kalach_visible = kalach_sprite.visible
	if is_kalach_visible:  # Only if the weapon is visible
		sprite_2d_3.visible = is_weapon_active && fire_mode == "single"  # Visible only in single mode
		sprite_2d_4.visible = is_weapon_active && fire_mode == "auto"   # Visible in auto mode
		sprite_2d_5.visible = is_weapon_active && fire_mode == "auto"   # Visible in auto mode
		sprite_2d_6.visible = is_weapon_active && fire_mode == "auto"   # Visible in auto mode
	else:
		sprite_2d_3.visible = false
		sprite_2d_4.visible = false
		sprite_2d_5.visible = false
		sprite_2d_6.visible = false

# Function to make the weapon follow the player's cursor
func aim_weapon():
	var mouse_position = get_global_mouse_position()
	var weapon_position = kalach_sprite.global_position
	var angle = (mouse_position - weapon_position).angle()
	kalach_sprite.rotation = angle

	if abs(angle) > PI / 2:
		kalach_sprite.flip_v = true
	else:
		kalach_sprite.flip_v = false

# Function to shoot a projectile
func shoot_projectile():
	if projectile_scene == null:
		print("projectile_scene is null! Please assign it in the inspector.")
		return

	var projectile = projectile_scene.instantiate()
	var mouse_position = get_global_mouse_position()
	projectile.position = kalach_sprite.global_position

	var direction = (mouse_position - projectile.position).normalized()
	projectile.velocity = direction * projectile.speed

	var angle = direction.angle()
	projectile.rotation = angle

	get_parent().add_child(projectile)

# Function to start reloading
func start_reloading():
	if not is_reloading:  # Only if it's not already reloading
		is_reloading = true
		print("Reloading...")
		await get_tree().create_timer(reload_time).timeout  # Wait for reload to finish
		current_ammo = magazine_size  # Refill the ammo
		is_reloading = false
		print("Reloading complete.")

Thank you in advance!!!

This is due to any movement being basically a teleportation. Usually the speed of objects is so slow that it looks smooth, but with high speeds this changes.

You could workaround this by casting a raycast beforehand to determine the collisionpoint and then have it disappear as soon as the collisionpoint is reached