rewrite so this player script use the same as the 2d so you easily can swap between them and also update the hp from the playerstate, and write it in .gd format:

using Godot;
using System;

public partial class Player : CharacterBody3D
{
public const uint MaxLifepoints = 200;

[Export]
public float Speed { get; private set; } = 5.0f;

[Export]
public float JumpVelocity { get; private set; } = 4.5f;

[Export]
private uint _lifepoints = MaxLifepoints;

// Get the gravity from the project settings to be synced with RigidBody nodes.
public float gravity = ProjectSettings.GetSetting(“physics/3d/default_gravity”).AsSingle();

private GameManager _gameManager;
private Timer _attackTimer;
private Node3D _visual;
private ProgressBar _playerLifebar;
private AnimationTree _animationTree;

public override void _Ready()
{
base._Ready();

_gameManager = GetNode<GameManager>(“/root/GameManager”);
_gameManager.Player = this;
_playerLifebar = GetTree().CurrentScene.GetNode<ProgressBar>(“HUD/PlayerLifeBar”);
_playerLifebar.MaxValue = MaxLifepoints;
_playerLifebar.Value = _lifepoints;

_attackTimer = GetNode<Timer>(“AttackCooldown”);
_visual = GetNode<Node3D>(“Visual”);
_animationTree = GetNode<AnimationTree>(“AnimationTree”);
}

public override void _Process(double delta)
{
base._Process(delta);

_playerLifebar.Value = _lifepoints;
}

public override void _PhysicsProcess(double delta)
{
Vector3 velocity = Velocity;

// Add the gravity.
if (!IsOnFloor())
velocity.Y -= gravity * (float)delta;

// Handle Jump.
//if (Input.IsActionJustPressed(“ui_accept”) && IsOnFloor())
// velocity.Y = JumpVelocity;

// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.
Vector2 inputDir = Input.GetVector(“left”, “right”, “up”, “down”);
Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized();
if (direction != Vector3.Zero)
{
velocity.X = direction.X * Speed;
velocity.Z = direction.Z * Speed;
}
else
{
velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed);
velocity.Z = Mathf.MoveToward(Velocity.Z, 0, Speed);
}

Velocity = velocity;
MoveAndSlide();

velocity.Y = 0;
_animationTree.Set(“parameters/walking/blend_amount”, velocity.LimitLength(1).Length());
if (Mathf.IsZeroApprox(velocity.Length())) return;

_visual.LookAt(Position + 10 * velocity, Vector3.Up, true);
//var angle = 0;
//var transform = _visual.Transform;
//transform.Basis = new(Vector3.Up, angle);
//_visual.Transform = transform;
}

public void OnAttackTimeOut()
{
_attackTimer.Start();

var nearestEnemy = _gameManager.GetNearestEnemy();
if (nearestEnemy == null) return;

//nearestEnemy.TakeDamages();
}

public void TakeDamages(uint damages)
{
_lifepoints -= Math.Clamp(damages, 0, _lifepoints);
}

internal void Heal(uint damages)
{
_lifepoints = Math.Clamp(_lifepoints + damages, 0, MaxLifepoints);
}
}


2dplayer:

extends CharacterBody2D
enum State {IDLE, RUN, JUMP, ATTACK}
var state = State.IDLE
#Data
var bullets_amount : int = 30
var attack_animation = “Attack1”
@export var movement_data : MovementData
@export var stats : Stats

#Refrences
@onready var animator : AnimatedSprite2D = AnimatedSprite2D
@onready var guns_animator : AnimationPlayer =ShootingAnimationPlayer
@onready var hit_animator : AnimationPlayer = HitAnimationPlayer
@onready var hand : Node2D =Hand
@onready var pistol : Sprite2D = Hand/Pivot/Pistol
@onready var pistol_bullet_marker : Marker2D =Hand/Pivot/Pistol/PistolBulletMarker
@onready var attacking : bool = false
@export var camera : Camera2D

#Load Scenes
@onready var muzzle_load : PackedScene = preload(“res://Scenes/Particles/muzzle.tscn”)
@onready var bullet_load : PackedScene = preload(“res://Scenes/Props/bullet.tscn”)
@onready var death_particle_load : PackedScene = preload(“res://Scenes/Particles/player_death_particle.tscn”)

func _ready():
stats.health = stats.max_health

EventManager.bullets_amount = bullets_amount
EventManager.update_bullet_ui.emit()

PlayerState.health = stats.health;


func _physics_process(delta):
apply_gravity(delta)

var input_vector = Input.get_axis(“move_left”,“move_right”)
if input_vector != 0:
apply_acceleration(input_vector, delta)
else:
apply_friction(delta)
if Input.is_action_just_pressed(“SwapPlane”):
_on_swapPlane()
if Input.is_action_just_pressed(“jump”) and is_on_floor():
jump()

if Input.is_action_just_pressed(“shoot”):
animator.play(“Attacking”)

if bullets_amount:
guns_animator.play(“Shoot”)


move_and_slide()
animate(input_vector)

func apply_acceleration(input_vector, delta):
velocity.x = move_toward(velocity.x, movement_data.max_speed * input_vector, movement_data.acceleration * delta)

func apply_friction(delta):
velocity.x = move_toward(velocity.x, 0, movement_data.friction * delta)

func apply_gravity(delta):
if not is_on_floor():
velocity.y += movement_data.gravity * movement_data.gravity_scale * delta

func knockback(vector):
velocity = velocity.move_toward(vector * movement_data.knockback_force, movement_data.acceleration)

func jump():
velocity.y = -movement_data.jump_strength
AudioManager.play_sound(AudioManager.JUMP)

func shoot():
state = State.ATTACK
attack_animation = “Attack” + str(randi_range(1, 2))
animator.play(attack_animation)
Hand/AttackArea/CollisionShape2D.disabled = false;
await animator.animation_looped
EventManager.update_bullet_ui.emit()
var mouse_position : Vector2 = (get_global_mouse_position() - global_position).normalized()
var muzzle = muzzle_load.instantiate()
var bullet = bullet_load.instantiate()
pistol_bullet_marker.add_child(muzzle)
bullet.global_position = pistol_bullet_marker.global_position
bullet.target_vector = mouse_position
bullet.rotation = mouse_position.angle()
get_tree().current_scene.add_child(bullet)
AudioManager.play_sound(AudioManager.SHOOT)

Hand/AttackArea/CollisionShape2D.disabled = true;

state = State.IDLE

func animate(input_vector):
var mouse_position : Vector2 = (get_global_mouse_position() - global_position).normalized()
if mouse_position.x > 0 and animator.flip_h:
animator.flip_h = false
elif mouse_position.x < 0 and not animator.flip_h:
animator.flip_h = true

hand.rotation = mouse_position.angle()
if hand.scale.y == 1 and mouse_position.x < 0:
hand.scale.y = -1
elif hand.scale.y == -1 and mouse_position.x > 0:
hand.scale.y = 1

if state != State.ATTACK:
if is_on_floor():
if input_vector != 0:
animator.play(“Run”)
else:
animator.play(“Idle”)
else:
if velocity.y > 0:
animator.play(“Jump”)
# Check if the current animation is the attack animation


func _on_hurtbox_area_entered(_area):
if not _area.is_in_group(“Bullet”):
hit_animator.play(“Hit”)
EventManager.update_health_ui.emit()
if stats.health <= 0:
die()
else:
AudioManager.play_sound(AudioManager.HURT)
EventManager.frame_freeze.emit()

func _on_swapPlane():
PlayerState.set_last_2d_position(global_position);
get_tree().change_scene_to_file(“res://demo/Scenes/main_scene.tscn”)


func die():
AudioManager.play_sound(AudioManager.DEATH)
animator.play(“Died”)
var death_particle = death_particle_load.instantiate()
death_particle.global_position = global_position
get_tree().current_scene.add_child(death_particle)
EventManager.player_died.emit()
queue_free()
In order to allow for a seamless swap between 3D and 2D player characters while keeping a consistent interface, you should abstract the common functionality into a shared interface or base class and then implement the specifics in the derived classes. For this refactoring, I’ll define an abstract class named PlayerBase which will include the common properties and methods. Both Player (for 3D) and Player2D (a new class for 2D) will inherit from this base class.

Below is an example of how you could organize the PlayerBase abstract class:

using Godot;
using System;

public abstract class PlayerBase : Node
{
    public const uint MaxHealth = 200;
    protected uint health = MaxHealth;

    public float Speed { get; protected set; } = 5.0f;
    public float JumpStrength { get; protected set; } = 4.5f;

    // … Other common properties and methods

    public abstract void TakeDamage(uint damage);
    public abstract void Heal(uint amount);
    public abstract void Attack();
    public abstract void Move(Vector2 inputDir);

    // Call this in _Ready or equivalent to set up UI.
    protected void InitializeUI()
    {
        var playerLifeBar = GetTree().CurrentScene.GetNode<ProgressBar>(“HUD/PlayerLifeBar”);
        playerLifeBar.MaxValue = MaxHealth;
        playerLifeBar.Value = health;
    }

    // Call this method to update the health in UI and player state.
    protected void UpdateHealthUI()
    {
        var playerLifeBar = GetTree().CurrentScene.GetNode<ProgressBar>(“HUD/PlayerLifeBar”);
        playerLifeBar.Value = health;

        // Update health in PlayerState here as needed
        // PlayerState.Health = health;
    }
}

With the PlayerBase defined, the 3D Player class would look something like this:

public partial class Player : PlayerBase, CharacterBody3D
{
    // Implement or override PlayerBase methods specifically for 3D.
    public override void TakeDamage(uint damage) { /* … / }
    public override void Heal(uint amount) { / … / }
    public override void Attack() { / … / }
    public override void Move(Vector2 inputDir) { / … / }
    
    // 3D-specific methods and properties

    // … Rest of the class …
}


For the 2D player (Player2D), it should inherit from PlayerBase and CharacterBody2D:

public class Player2D : PlayerBase, CharacterBody2D
{
    // Implement or override PlayerBase methods specifically for 2D.
    public override void TakeDamage(uint damage) { / … / }
    public override void Heal(uint amount) { / … / }
    public override void Attack() { / … / }
    public override void Move(Vector2 inputDir) { / … */ }
    
    // 2D-specific methods and properties

    // … Rest of the class …
}

You would define the methods like TakeDamage, Heal, Attack, and so on for both Player and Player2D as per the requirements of 3D and 2D gameplay.

This refactoring consolidates the common elements, such as health management, using an abstract base class (PlayerBase). Each specific implementation (Player for 3D and Player2D for 2D) then extends this base class while implementing additional specifics relevant to their dimension. You will need to update your PlayerState logic and UI code to work with the PlayerBase class instead of directly with Player or Player2D.

Please note that Godot’s actual scripting language is GDScript and the 2D player script you provided is written in GDScript which is very different from C#. If you want everything in C#, you’ll need to translate the GDScript code into C# and then apply the interface/base class approach as I described above.