Is my GDScript good?

hello peoples, ive been working on a godot project for a bit now, and have the basics done, such as player movement, and i dont have very many friends irl that can give me feedback other than “i wish i could move faster” does anyone have any advice, tips, or feedback on my gdscript, things i can improve, or anything?

the script itself:

extends CharacterBody2D

const SPEED: float = 115.0
const SWIM_SPEED: float = 70.0
const JUMP_FORCE: float = -210.0
const GRAVITY: float = 1000.0
const DASH_SPEED: float = 250.0
const DASH_DURATION: float = 0.15
const COYOTE_TIME: float = 0.12
const DASH_COOLDOWN: float = 0.6

enum State { NORMAL, JUMPING, DASHING, SWIMMING }

@onready var body: AnimatedSprite2D = $PlayerRoot/body
@onready var head: AnimatedSprite2D = $PlayerRoot/head
@onready var mask: AnimatedSprite2D = $PlayerRoot/mask
@onready var particles: AnimatedSprite2D = $PlayerRoot/particles

@onready var shadow: Sprite2D = $Shadow
@onready var object_hitbox: CollisionShape2D = $ObjectHitbox
@onready var feet: Node2D = $Feet

@onready var water_layers := get_tree().get_nodes_in_group("water")
@onready var ground_layers := get_tree().get_nodes_in_group("ground")

var state: State = State.NORMAL
var facing_direction: Vector2 = Vector2.DOWN
var can_dash: bool = true
var in_water: bool = false

var jump_velocity: float = 0.0
var coyote_timer: float = 0.0
var dash_cooldown_timer: float = 0.0
var dash_timer: float = 0.0
var dash_velocity: Vector2 = Vector2.ZERO

var last_feet_pos: Vector2 = Vector2.INF


# WATER DETECTION
func is_in_water_now() -> bool:
	var feet_pos: Vector2 = feet.global_position

	for layer in ground_layers:
		var tile_pos: Vector2i = layer.local_to_map(feet_pos)
		if layer.get_cell_tile_data(tile_pos) != null:
			return false

	for layer in water_layers:
		var tile_pos: Vector2i = layer.local_to_map(feet_pos)
		if layer.get_cell_tile_data(tile_pos) != null:
			return true

	return false


#


# 8 DIRECTION SNAP FOR DASHING
func snap_to_8_dir(vec: Vector2) -> Vector2:
	if vec == Vector2.ZERO:
		return Vector2.DOWN
	var angle: float = vec.angle()
	var snapped_angle: float = round(angle / (PI / 4.0)) * (PI / 4.0)
	return Vector2.RIGHT.rotated(snapped_angle).normalized()


func _ready() -> void:
	last_feet_pos = feet.global_position
	in_water = is_in_water_now()
	state = State.SWIMMING if in_water else State.NORMAL


func _is_airborne() -> bool:
	return state == State.JUMPING or state == State.DASHING


# MAIN LOOP
func _physics_process(delta: float) -> void:
	var direction: Vector2 = Input.get_vector("left", "right", "up", "down").normalized()
	var grounded: bool = not _is_airborne() and body.position.y >= 0.0

	# Event base water update
	var current_feet_pos := feet.global_position
	if current_feet_pos != last_feet_pos:
		in_water = is_in_water_now()
		last_feet_pos = current_feet_pos

	# State sync with water when on ground
	if not _is_airborne():
		state = State.SWIMMING if in_water else State.NORMAL

	# Coyote time
	if grounded:
		coyote_timer = COYOTE_TIME
	else:
		coyote_timer = max(coyote_timer - delta, 0.0)

	# Dash cooldown
	if dash_cooldown_timer > 0.0:
		dash_cooldown_timer = max(dash_cooldown_timer - delta, 0.0)

	# Jump
	if Input.is_action_just_pressed("jump") and coyote_timer > 0.0:
		state = State.JUMPING
		can_dash = true
		jump_velocity = JUMP_FORCE
		coyote_timer = 0.0
		object_hitbox.disabled = true

	# Dash
	if _is_airborne() \
	and Input.is_action_just_pressed("leftclick") \
	and can_dash \
	and dash_cooldown_timer <= 0.0:

		state = State.DASHING
		can_dash = false
		dash_cooldown_timer = DASH_COOLDOWN
		dash_timer = DASH_DURATION
		object_hitbox.disabled = true

		var mouse_dir: Vector2 = global_position.direction_to(get_global_mouse_position())
		var snapped_dir: Vector2 = snap_to_8_dir(facing_direction)
		var final_dash_dir: Vector2 = snapped_dir.lerp(mouse_dir, 0.5).normalized()
		dash_velocity = final_dash_dir * DASH_SPEED

	# Dash update
	if state == State.DASHING:
		dash_timer -= delta
		if dash_timer <= 0.0:
			state = State.JUMPING  # back to normal airborne after dash

	# 2d Z-axis jump
	if _is_airborne():
		body.position.y += jump_velocity * delta
		head.position.y = body.position.y
		mask.position.y = body.position.y
		particles.position.y = body.position.y

		jump_velocity += GRAVITY * delta

		if body.position.y >= 0.0:
			body.position.y = 0.0
			head.position.y = 0.0
			mask.position.y = 0.0
			particles.position.y = 0.0

			state = State.SWIMMING if in_water else State.NORMAL
			can_dash = true
			object_hitbox.disabled = false

			if particles.sprite_frames.has_animation("land"):
				particles.play("land")

	# Movement
	if state == State.DASHING:
		velocity = dash_velocity
	else:
		var current_speed = SWIM_SPEED if in_water else SPEED
		velocity = direction * current_speed

	# Shadow
	shadow.visible = _is_airborne() or not in_water

	# Animation
	_play_animation(direction)

	move_and_slide()


# LAYERED ANIMATION SYSTEM
func _play_animation(direction: Vector2) -> void:
	var anim := ""

	# Jump + Dash
	if _is_airborne():
		if abs(facing_direction.x) < 0.2:
			anim = "jumpup" if facing_direction.y < 0.0 else "jumpdown"
		else:
			anim = "jumpsideup" if facing_direction.y < 0.0 else "jumpsidedown"
		play_all(anim, direction)
		return

	# Update facing direction
	if direction != Vector2.ZERO:
		facing_direction = direction

	# Swimming
	if in_water:
		if direction == Vector2.ZERO:
			anim = "swimidleup" if facing_direction.y < 0.0 else "swimidledown"
		else:
			if abs(direction.x) > abs(direction.y):
				anim = "swimwalksideup" if direction.y < 0.0 else "swimwalksidedown"
			else:
				anim = "swimwalkup" if direction.y < 0.0 else "swimwalkdown"
		play_all(anim, direction)
		return

	# Land movement
	if direction == Vector2.ZERO:
		anim = "idleup" if facing_direction.y < 0.0 else "idledown"
	else:
		if abs(direction.x) > abs(direction.y):
			anim = "walksideup" if direction.y < 0.0 else "walksidedown"
		else:
			anim = "walkup" if direction.y < 0.0 else "walkdown"

	play_all(anim, direction)


func play_all(anim: String, direction: Vector2) -> void:
	body.play(anim)
	head.play(anim)
	mask.play(anim)

	if particles.sprite_frames.has_animation(anim):
		particles.play(anim)
	else:
		particles.stop()

	var flip_dir := direction
	if flip_dir == Vector2.ZERO:
		flip_dir = facing_direction

	var flip := flip_dir.x < 0.0
	body.flip_h = flip
	head.flip_h = flip
	mask.flip_h = flip
	particles.flip_h = flip

i love all your feedback and would love to also give others feedback to!

I will not read the code, but in turn offer you an opinion.

As a designer this is probably what i would focus on.

At the end of the day there is a mentality that is “whatever works is good.” And i think this is important.

why? because once you release this software it will probably get forgotten and you will move on to write another program, and another, and another.

If you make the same games, sure you can just copy paste this same script, regardless if its good or bad wont matter when it works. If it works for you copy paste. if it doesnt, something will need to change.

When it comes to software all that matters in the end is does it work or not? And in order for something to work it needs to conform to the organization of the surrounding software design. And there is no golden rule to that, so whatever works is good.

Even if it works it might not feel good, and this is what play testers will tell you and this has its own importance.

If i were to read your code i may be able to tell you that you could make XYZ logical optimization. But there is no point to making that optimization on such a singular part of the game, especially if the game is still in production. Things are still changing and anything may need to refactored.

If you want to get better at coding then you need to start reading other peoples code and understand what it does. You can also get into programming patterns to see what structurally exists in Godot and other software.

Other then that those higher level things. code quality mostly doesnt matter. (Maybe if you need to share your code in an organization but code reviews will adress those things.)

1 Like