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:

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()