Parasight ( A Metroidvania )

extends Sprite2D

@onready var shadow: Sprite2D = $shadow
@onready var ray_cast_2d: RayCast2D = $RayCast2D

var speed: float = 120.0
var direction: Vector2 = Vector2.RIGHT

func _ready() → void:

Make sure raycast faces the direction of travel

ray_cast_2d.rotation = 0

func _physics_process(delta: float) → void:

Move the bullet forward

global_position += direction.rotated(rotation) * speed * delta

# Update shadow position
shadow.position = Vector2(-2, 2).rotated(-rotation)

# --- CLEAN COLLISION LOGIC ---
if ray_cast_2d.is_colliding():
	var collider = ray_cast_2d.get_collider()



	# Enemy takes damage
	if collider.is_in_group("ENEMY"):
		if collider.has_method("take_hit"):
			collider.take_hit()
		else:
			collider.queue_free()  # fallback

	# destroy bullet after hit
	queue_free()

func _on_distance_timeout_timeout() → void:
queue_free()

Uhh Soo…

I’ve Been working on a New Enemy.

extends CharacterBody2D

@export var speed: float = 80.0
@export var stop_distance: float = 16
@export var health: int = 3

var player: Node2D = null
var is_following: bool = false
var is_flashing: bool = false

@onready var sprite: Sprite2D = $Sprite2D
@onready var detection_area: Area2D = $DetectionArea

func _ready() → void:
detection_area.connect(“body_entered”, Callable(self, “_on_detection_entered”))
detection_area.connect(“body_exited”, Callable(self, “_on_detection_exited”))

func _process(delta: float) → void:
if not is_following or not player:
return

var direction = (player.global_position - global_position)
var distance = direction.length()
if distance > stop_distance:
	direction = direction.normalized()
	global_position += direction * speed * delta  # manual movement

# Flip sprite
if direction.x != 0:
	sprite.flip_h = direction.x < 0

-------------------

Detection

-------------------

func _on_detection_entered(body: Node) → void:
if body.is_in_group(“Player”):
player = body
is_following = true

func _on_detection_exited(body: Node) → void:
if body == player:
player = null
is_following = false

-------------------

Damage + white flash

-------------------

func take_hit() → void:
if is_flashing:
return
health -= 1
print(“Enemy hit! Health =”, health)
flash_white()
if health <= 0:
die()

func flash_white() → void:
if is_flashing:
return
is_flashing = true
sprite.modulate = Color(1,1,1,1)
await get_tree().create_timer(0.1).timeout
sprite.modulate = Color(1,1,1,1)
is_flashing = false

func die() → void:
queue_free()

It always Glitches Out :frowning:

Enemy Code-

extends CharacterBody2D

const SPEED = 41

var direction = 1
var health = 3
var is_flashing = false

@onready var ray_cast_right: RayCast2D = $RayCastRight
@onready var ray_cast_left: RayCast2D = $RayCastLeft
@onready var animated_sprite = $AnimatedSprite2D
@onready var hit_sound: AudioStreamPlayer2D = $HitSound

func _ready():
add_to_group(“ENEMY”)

func _physics_process(delta: float) → void:

If dying, skip normal patrol movement

if direction == 0:

Gravity still applies

if not is_on_floor():
velocity += get_gravity() * delta
move_and_slide()
return

# Apply gravity
if not is_on_floor():
	velocity += get_gravity() * delta

# Edge detection (turn at edges)
if direction == 1 and not ray_cast_right.is_colliding():
	direction = -1
	animated_sprite.flip_h = true
elif direction == -1 and not ray_cast_left.is_colliding():
	direction = 1
	animated_sprite.flip_h = false

# Normal movement
velocity.x = direction * SPEED
move_and_slide()

----------------------------------------------------

DAMAGE + WHITE FLASH

----------------------------------------------------

func take_hit() → void:
if is_flashing:
return

health -= 1
print("Enemy hit! Health =", health)
hit_sound.play()
# Flash white asynchronously
flash_white()

if health <= 0:
	die()

-------------------

Async white flash (Godot 4.4)

-------------------

func flash_white() → void:
if is_flashing:
return
is_flashing = true
animated_sprite.modulate = Color(2, 2, 2)
await get_tree().create_timer(0.1).timeout
animated_sprite.modulate = Color(1, 1, 1)
is_flashing = false

----------------------------------------------------

DEATH: launch upward + fade out

----------------------------------------------------

func die() → void:

Stop normal movement

direction = 0

# Launch upward
velocity.y = -70

# Start fade-out
fade_out_and_free()

func fade_out_and_free() → void:
var fade_time := 0.4
var t := 0.0

while t < fade_time:
	t += get_process_delta_time()
	animated_sprite.modulate.a = 1.0 - (t / fade_time)
	await get_tree().process_frame

queue_free()

Other Enemy Code:

extends CharacterBody2D

const SPEED = 41

var direction = 1
var health = 3
var is_flashing = false

@onready var ray_cast_right = $RayCastRight
@onready var ray_cast_left = $RayCastLeft
@onready var animated_sprite = $AnimatedSprite2D
@onready var hit_sound: AudioStreamPlayer2D = $HitSound

func _ready():
add_to_group(“ENEMY”)

func _physics_process(delta: float) → void:

If dying, skip movement logic

if direction == 0:

Still apply gravity while flying

if not is_on_floor():
velocity += get_gravity() * delta
move_and_slide()
return

# Normal gravity
if not is_on_floor():
	velocity += get_gravity() * delta

# Edge detection
if ray_cast_right.is_colliding():
	direction = -1
	animated_sprite.flip_h = true
elif ray_cast_left.is_colliding():
	direction = 1
	animated_sprite.flip_h = false

# Normal movement
velocity.x = direction * SPEED
move_and_slide()

----------------------------------------------------

DAMAGE + WHITE FLASH

----------------------------------------------------

func take_hit() → void:
if is_flashing:
return # Prevent overlapping flashes

health -= 1
print("Enemy hit! Health =", health)
hit_sound.play()
flash_white()

if health <= 0:
	die()

-------------------

Async white flash

-------------------

func flash_white() → void:
if is_flashing:
return
is_flashing = true
animated_sprite.modulate = Color(2, 2, 2)
await get_tree().create_timer(0.1).timeout
animated_sprite.modulate = Color(1, 1, 1)
is_flashing = false

----------------------------------------------------

DEATH: fly upward + fade out

----------------------------------------------------

func die() → void:

Stop normal AI movement

direction = 0

# Launch upward
velocity.y = -50

# Begin fade-out
fade_out_and_free()

func fade_out_and_free() → void:
var fade_time := 0.4
var t := 0.0

while t < fade_time:
	t += get_process_delta_time()
	animated_sprite.modulate.a = 1.0 - (t / fade_time)
	await get_tree().process_frame

queue_free()

Are u Working on a new game now that ur done with WOMBO!

how did you go about the idea and the beginning of your game and development? I would like to develop a game myself so it would be nice to hear your thoughts

1 Like

Indeed I am, I’ll get something posted on the forum one of these days.

2 Likes

One evening, I was doomscrolling when my older brother suggested that I try programming. I initially said no, but then he mentioned game development. I thought about it and agreed to give it a try. He recommended using Godot or Lua, and I chose Godot. So, I sat down and watched a Brackeys tutorial. ALSO WATCH GOODGIS! His vids helped me ALOT.

2 Likes

Sherk,why you post every code of the game? are you making it open source?

if then don’t forget to give the sprites and whole godot project xD

2 Likes

Hey, you got discord? I am also tryna make a Metroidvania but then gave up due to being new to game dev, I would like to learn from you.

1 Like

I don’t have a Discord account and I won’t be getting one anytime soon because I’m 12 years old and btw i’m still a Noob One tip I can give is to not set overly ambitious goals but instead be creative and add new ideas whenever you can.

Well I post the code for criticism, and i will be posting sprites

1 Like

Very nice, mr Shrek. I am very into developing a game right now. Sometimes at my 9-5 job I think i don’t really like programming, but I just don’t like this corporate environment. I’m very pleased with the idea of creating a game.

1 Like

It would be great to know wut ur games about

The screenshot of the map and sprites look really cool dude!

I had issues with hit flashes and colored telegraphs on enemy windups/attacks before and landed on using a shader for it. It seems like you’ve got a fix but it might be something worth looking into in the future if you run I to more issues.

Can’t wait for a demo, I hope the swamp level is in it… 8|

1 Like

New boss sorry for delay…

2 Likes

Looks awesome! You got any gameplay?

1 Like

No not really idk how to make bosses so I just draw them and add more game play

1 Like

With mask or with out the mask

1 Like

Can’t tell which is which..

1 Like