точно есть дэшм
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
extends State
|
||||
|
||||
class_name CrouchState
|
||||
|
||||
var stateName : String = "Crouch"
|
||||
|
||||
var cR : CharacterBody3D
|
||||
|
||||
func enter(charRef : CharacterBody3D):
|
||||
cR = charRef
|
||||
|
||||
verifications()
|
||||
|
||||
func verifications():
|
||||
cR.moveSpeed = cR.crouchSpeed
|
||||
cR.moveAccel = cR.crouchAccel
|
||||
cR.moveDeccel = cR.crouchDeccel
|
||||
|
||||
cR.floor_snap_length = 1.0
|
||||
if cR.jumpCooldown > 0.0: cR.jumpCooldown = -1.0
|
||||
if cR.nbJumpsInAirAllowed < cR.nbJumpsInAirAllowedRef: cR.nbJumpsInAirAllowed = cR.nbJumpsInAirAllowedRef
|
||||
if cR.coyoteJumpCooldown < cR.coyoteJumpCooldownRef: cR.coyoteJumpCooldown = cR.coyoteJumpCooldownRef
|
||||
|
||||
func physics_update(delta : float):
|
||||
checkIfFloor()
|
||||
|
||||
applies(delta)
|
||||
|
||||
cR.gravityApply(delta)
|
||||
|
||||
inputManagement()
|
||||
|
||||
move(delta)
|
||||
|
||||
func checkIfFloor():
|
||||
if !cR.is_on_floor() and !cR.is_on_wall():
|
||||
if cR.velocity.y < 0.0:
|
||||
transitioned.emit(self, "InairState")
|
||||
if cR.is_on_floor():
|
||||
if cR.jumpBuffOn:
|
||||
cR.bufferedJump = true
|
||||
cR.jumpBuffOn = false
|
||||
transitioned.emit(self, "JumpState")
|
||||
|
||||
func applies(delta : float):
|
||||
if cR.hitGroundCooldown > 0.0: cR.hitGroundCooldown -= delta
|
||||
|
||||
cR.hitbox.shape.height = lerp(cR.hitbox.shape.height, cR.crouchHitboxHeight, cR.heightChangeSpeed * delta)
|
||||
cR.model.scale.y = lerp(cR.model.scale.y, cR.crouchModelHeight, cR.heightChangeSpeed * delta)
|
||||
|
||||
func inputManagement():
|
||||
if Input.is_action_just_pressed(cR.jumpAction):
|
||||
if !raycastVerification(): #if nothing block the player character when it will leaves the crouch state
|
||||
transitioned.emit(self, "JumpState")
|
||||
|
||||
if cR.continiousCrouch:
|
||||
#has to press run button once to run
|
||||
if Input.is_action_just_pressed(cR.crouchAction):
|
||||
if !raycastVerification():
|
||||
cR.walkOrRun = "WalkState"
|
||||
transitioned.emit(self, "WalkState")
|
||||
else:
|
||||
#has to continuously press crouch button to crouch
|
||||
if !Input.is_action_pressed(cR.crouchAction):
|
||||
if !raycastVerification():
|
||||
cR.walkOrRun = "WalkState"
|
||||
transitioned.emit(self, "WalkState")
|
||||
|
||||
func raycastVerification():
|
||||
#check if the raycast used to check ceilings is colliding or not
|
||||
return cR.ceilingCheck.is_colliding()
|
||||
|
||||
func move(delta : float):
|
||||
cR.inputDirection = Input.get_vector(cR.moveLeftAction, cR.moveRightAction, cR.moveForwardAction, cR.moveBackwardAction)
|
||||
cR.moveDirection = (cR.camHolder.global_basis * Vector3(cR.inputDirection.x, 0.0, cR.inputDirection.y)).normalized()
|
||||
|
||||
if cR.moveDirection and cR.is_on_floor():
|
||||
cR.velocity.x = lerp(cR.velocity.x, cR.moveDirection.x * cR.moveSpeed, cR.moveAccel * delta)
|
||||
cR.velocity.z = lerp(cR.velocity.z, cR.moveDirection.z * cR.moveSpeed, cR.moveAccel * delta)
|
||||
else:
|
||||
cR.velocity.x = lerp(cR.velocity.x, 0.0, cR.moveDeccel * delta)
|
||||
cR.velocity.z = lerp(cR.velocity.z, 0.0, cR.moveDeccel * delta)
|
||||
|
||||
if cR.hitGroundCooldown <= 0: cR.desiredMoveSpeed = cR.velocity.length()
|
||||
|
||||
if cR.desiredMoveSpeed >= cR.maxSpeed: cR.desiredMoveSpeed = cR.maxSpeed
|
||||
@@ -0,0 +1 @@
|
||||
uid://bhoqhv7escpyx
|
||||
@@ -0,0 +1,59 @@
|
||||
extends State
|
||||
|
||||
class_name DashState
|
||||
|
||||
var stateName : String = "Dash"
|
||||
var cR : CharacterBody3D
|
||||
|
||||
var dash_timer : float = 0.0
|
||||
|
||||
func enter(charRef : CharacterBody3D):
|
||||
cR = charRef
|
||||
|
||||
# 1. Задаем таймер длительности дэша из настроек игрока
|
||||
dash_timer = cR.dash_duration
|
||||
cR.dash_cooldown_timer = cR.dash_cooldown
|
||||
cR.is_dashing = true
|
||||
|
||||
# 2. Определяем направление рывка
|
||||
# Если игрок бежал — дэшим в сторону бега. Если стоял — строго вперед, куда смотрит камера
|
||||
cR.dash_direction = cR.moveDirection
|
||||
if cR.dash_direction == Vector3.ZERO:
|
||||
cR.dash_direction = -cR.camHolder.global_transform.basis.z
|
||||
|
||||
cR.dash_direction.y = 0 # Чтобы не улетать в небо
|
||||
cR.dash_direction = cR.dash_direction.normalized()
|
||||
|
||||
# 3. Включаем сочный эффект FOV на камере игрока
|
||||
if cR.player_camera:
|
||||
var tween = create_tween()
|
||||
tween.tween_property(cR.player_camera, "fov", cR.normal_fov + 15.0, 0.1)
|
||||
|
||||
func physics_update(delta : float):
|
||||
# Отсчитываем время действия дэша
|
||||
dash_timer -= delta
|
||||
|
||||
if dash_timer <= 0:
|
||||
# Рывок окончен! Возвращаем FOV камеры назад
|
||||
cR.is_dashing = false
|
||||
if cR.player_camera:
|
||||
var tween = create_tween()
|
||||
tween.tween_property(cR.player_camera, "fov", cR.normal_fov, 0.15)
|
||||
|
||||
# Плавно переходим обратно в ходьбу, бег или воздух в зависимости от того, где мы оказались
|
||||
if cR.is_on_floor():
|
||||
if cR.walkOrRun == "RunState":
|
||||
transitioned.emit(self, "RunState")
|
||||
else:
|
||||
transitioned.emit(self, "WalkState")
|
||||
else:
|
||||
transitioned.emit(self, "InairState")
|
||||
return
|
||||
|
||||
# Принудительно толкаем физическое тело игрока во время рывка
|
||||
cR.velocity.x = cR.dash_direction.x * cR.dash_speed
|
||||
cR.velocity.z = cR.dash_direction.z * cR.dash_speed
|
||||
|
||||
# Применяем гравитацию (опционально, чтобы игрок мог дэшиться в воздухе и плавно падать)
|
||||
cR.gravityApply(delta)
|
||||
cR.modifyPhysicsProperties()
|
||||
@@ -0,0 +1 @@
|
||||
uid://t4bhhfuq3vda
|
||||
@@ -0,0 +1,83 @@
|
||||
extends State
|
||||
|
||||
class_name IdleState
|
||||
|
||||
var stateName : String = "Idle"
|
||||
|
||||
var cR : CharacterBody3D
|
||||
|
||||
func enter(char_ref : CharacterBody3D):
|
||||
#pass play char reference
|
||||
cR = char_ref
|
||||
|
||||
verifications()
|
||||
|
||||
func verifications():
|
||||
#manage the appliements that need to be set at the start of the state
|
||||
cR.floor_snap_length = 1.0
|
||||
if cR.jumpCooldown > 0.0: cR.jumpCooldown = -1.0
|
||||
if cR.nbJumpsInAirAllowed < cR.nbJumpsInAirAllowedRef: cR.nbJumpsInAirAllowed = cR.nbJumpsInAirAllowedRef
|
||||
if cR.coyoteJumpCooldown < cR.coyoteJumpCooldownRef: cR.coyoteJumpCooldown = cR.coyoteJumpCooldownRef
|
||||
|
||||
func physics_update(delta : float):
|
||||
checkIfFloor()
|
||||
|
||||
applies(delta)
|
||||
|
||||
cR.gravityApply(delta)
|
||||
|
||||
inputManagement()
|
||||
|
||||
move(delta)
|
||||
|
||||
func checkIfFloor():
|
||||
#manage the appliements and state transitions that needs to be sets/checked/performed
|
||||
#every time the play char pass through one of the following : floor-inair-onwall
|
||||
if !cR.is_on_floor() and !cR.is_on_wall():
|
||||
transitioned.emit(self, "InairState")
|
||||
if cR.is_on_floor():
|
||||
if cR.jumpBuffOn:
|
||||
cR.bufferedJump = true
|
||||
cR.jumpBuffOn = false
|
||||
transitioned.emit(self, "JumpState")
|
||||
|
||||
func applies(delta : float):
|
||||
#manage the appliements of things that needs to be set/checked/performed every frame
|
||||
if cR.hitGroundCooldown > 0.0: cR.hitGroundCooldown -= delta
|
||||
|
||||
cR.hitbox.shape.height = lerp(cR.hitbox.shape.height, cR.baseHitboxHeight, cR.heightChangeSpeed * delta)
|
||||
cR.model.scale.y = lerp(cR.model.scale.y, cR.baseModelHeight, cR.heightChangeSpeed * delta)
|
||||
|
||||
func inputManagement():
|
||||
#manage the state transitions depending on the actions inputs
|
||||
if Input.is_action_just_pressed(cR.jumpAction):
|
||||
transitioned.emit(self, "JumpState")
|
||||
|
||||
if Input.is_action_just_pressed(cR.crouchAction):
|
||||
transitioned.emit(self, "CrouchState")
|
||||
|
||||
if Input.is_action_just_pressed(cR.runAction):
|
||||
if cR.walkOrRun == "WalkState": cR.walkOrRun = "RunState"
|
||||
elif cR.walkOrRun == "RunState": cR.walkOrRun = "WalkState"
|
||||
|
||||
func move(delta : float):
|
||||
#manage the character movement
|
||||
|
||||
#direction input
|
||||
cR.inputDirection = Input.get_vector(cR.moveLeftAction, cR.moveRightAction, cR.moveForwardAction, cR.moveBackwardAction)
|
||||
#get the move direction depending on the input
|
||||
cR.moveDirection = (cR.camHolder.global_basis * Vector3(cR.inputDirection.x, 0.0, cR.inputDirection.y)).normalized()
|
||||
|
||||
if cR.moveDirection and cR.is_on_floor():
|
||||
#transition to corresponding state
|
||||
transitioned.emit(self, cR.walkOrRun)
|
||||
else:
|
||||
#apply smooth stop
|
||||
cR.velocity.x = lerp(cR.velocity.x, 0.0, cR.moveDeccel * delta)
|
||||
cR.velocity.z = lerp(cR.velocity.z, 0.0, cR.moveDeccel * delta)
|
||||
|
||||
#cancel desired move speed accumulation if the timer has elapsed (is up)
|
||||
if cR.hitGroundCooldown <= 0: cR.desiredMoveSpeed = cR.velocity.length()
|
||||
|
||||
#set to ensure the character don't exceed the max speed authorized
|
||||
if cR.desiredMoveSpeed >= cR.maxSpeed: cR.desiredMoveSpeed = cR.maxSpeed
|
||||
@@ -0,0 +1 @@
|
||||
uid://stil4xkf3tk0
|
||||
@@ -0,0 +1,77 @@
|
||||
extends State
|
||||
|
||||
class_name InairState
|
||||
|
||||
var stateName : String = "Inair"
|
||||
|
||||
var cR : CharacterBody3D
|
||||
|
||||
func enter(charRef : CharacterBody3D):
|
||||
cR = charRef
|
||||
|
||||
verifications()
|
||||
|
||||
func verifications():
|
||||
if cR.floor_snap_length != 0.0: cR.floor_snap_length = 0.0
|
||||
if cR.hitGroundCooldown != cR.hitGroundCooldownRef: cR.hitGroundCooldown = cR.hitGroundCooldownRef
|
||||
|
||||
func physics_update(delta : float):
|
||||
applies(delta)
|
||||
|
||||
cR.gravityApply(delta)
|
||||
|
||||
inputManagement()
|
||||
|
||||
checkIfFloor()
|
||||
|
||||
move(delta)
|
||||
|
||||
func applies(delta : float):
|
||||
if !cR.is_on_floor():
|
||||
if cR.jumpCooldown > 0.0: cR.jumpCooldown -= delta
|
||||
if cR.coyoteJumpCooldown > 0.0: cR.coyoteJumpCooldown -= delta
|
||||
|
||||
cR.hitbox.shape.height = lerp(cR.hitbox.shape.height, cR.baseHitboxHeight, cR.heightChangeSpeed * delta)
|
||||
cR.model.scale.y = lerp(cR.model.scale.y, cR.baseModelHeight, cR.heightChangeSpeed * delta)
|
||||
|
||||
func inputManagement():
|
||||
if Input.is_action_just_pressed(cR.jumpAction):
|
||||
#check if can jump buffer
|
||||
if cR.floorCheck.is_colliding() and cR.lastFramePosition.y > cR.position.y and cR.nbJumpsInAirAllowed <= 0: cR.jumpBuffOn = true
|
||||
#check if can coyote jump
|
||||
if cR.wasOnFloor and cR.coyoteJumpCooldown > 0.0 and cR.lastFramePosition.y > cR.position.y:
|
||||
cR.coyoteJumpOn = true
|
||||
transitioned.emit(self, "JumpState")
|
||||
transitioned.emit(self, "JumpState")
|
||||
|
||||
func checkIfFloor():
|
||||
if cR.is_on_floor():
|
||||
if cR.jumpBuffOn:
|
||||
cR.bufferedJump = true
|
||||
cR.jumpBuffOn = false
|
||||
transitioned.emit(self, "JumpState")
|
||||
else:
|
||||
if cR.moveDirection: transitioned.emit(self, cR.walkOrRun)
|
||||
else: transitioned.emit(self, "IdleState")
|
||||
|
||||
if cR.is_on_wall():
|
||||
cR.velocity.x = 0.0
|
||||
cR.velocity.z = 0.0
|
||||
|
||||
func move(delta : float):
|
||||
cR.inputDirection = Input.get_vector(cR.moveLeftAction, cR.moveRightAction, cR.moveForwardAction, cR.moveBackwardAction)
|
||||
cR.moveDirection = (cR.camHolder.global_basis * Vector3(cR.inputDirection.x, 0.0, cR.inputDirection.y)).normalized()
|
||||
|
||||
if !cR.is_on_floor():
|
||||
if cR.moveDirection:
|
||||
if cR.desiredMoveSpeed < cR.maxSpeed: cR.desiredMoveSpeed += cR.bunnyHopDmsIncre * delta
|
||||
|
||||
var contrdDesMoveSpeed : float = cR.desiredMoveSpeedCurve.sample(cR.desiredMoveSpeed)
|
||||
var contrdInAirMoveSpeed : float = cR.inAirMoveSpeedCurve.sample(cR.desiredMoveSpeed) * cR.inAirInputMultiplier
|
||||
|
||||
cR.velocity.x = lerp(cR.velocity.x, cR.moveDirection.x * contrdDesMoveSpeed, contrdInAirMoveSpeed * delta)
|
||||
cR.velocity.z = lerp(cR.velocity.z, cR.moveDirection.z * contrdDesMoveSpeed, contrdInAirMoveSpeed * delta)
|
||||
else:
|
||||
cR.desiredMoveSpeed = cR.velocity.length()
|
||||
|
||||
if cR.desiredMoveSpeed >= cR.maxSpeed: cR.desiredMoveSpeed = cR.maxSpeed
|
||||
@@ -0,0 +1 @@
|
||||
uid://bh3rdvokmrar1
|
||||
@@ -0,0 +1,111 @@
|
||||
extends State
|
||||
|
||||
class_name JumpState
|
||||
|
||||
var stateName : String = "Jump"
|
||||
|
||||
var cR : CharacterBody3D
|
||||
|
||||
func enter(charRef : CharacterBody3D):
|
||||
cR = charRef
|
||||
|
||||
verifications()
|
||||
|
||||
jump()
|
||||
|
||||
func verifications():
|
||||
if cR.floor_snap_length != 0.0: cR.floor_snap_length = 0.0
|
||||
if cR.jumpCooldown < cR.jumpCooldownRef: cR.jumpCooldown = cR.jumpCooldownRef
|
||||
if cR.hitGroundCooldown != cR.hitGroundCooldownRef: cR.hitGroundCooldown = cR.hitGroundCooldownRef
|
||||
|
||||
func physics_update(delta : float):
|
||||
|
||||
applies(delta)
|
||||
|
||||
cR.gravityApply(delta)
|
||||
|
||||
inputManagement()
|
||||
|
||||
checkIfFloor()
|
||||
|
||||
move(delta)
|
||||
|
||||
func applies(delta : float):
|
||||
if !cR.is_on_floor():
|
||||
if cR.jumpCooldown > 0.0: cR.jumpCooldown -= delta
|
||||
if cR.coyoteJumpCooldown > 0.0: cR.coyoteJumpCooldown -= delta
|
||||
|
||||
cR.hitbox.shape.height = lerp(cR.hitbox.shape.height, cR.baseHitboxHeight, cR.heightChangeSpeed * delta)
|
||||
cR.model.scale.y = lerp(cR.model.scale.y, cR.baseModelHeight, cR.heightChangeSpeed * delta)
|
||||
|
||||
func inputManagement():
|
||||
if Input.is_action_just_pressed(cR.jumpAction):
|
||||
jump()
|
||||
|
||||
func checkIfFloor():
|
||||
if !cR.is_on_floor() and cR.velocity.y < 0.0:
|
||||
transitioned.emit(self, "InairState")
|
||||
|
||||
if cR.is_on_floor():
|
||||
if cR.moveDirection: transitioned.emit(self, cR.walkOrRun)
|
||||
else: transitioned.emit(self, "IdleState")
|
||||
|
||||
#lose all velocity if play char hit a wall
|
||||
if cR.is_on_wall():
|
||||
cR.velocity.x = 0.0
|
||||
cR.velocity.z = 0.0
|
||||
|
||||
func move(delta : float):
|
||||
cR.inputDirection = Input.get_vector(cR.moveLeftAction, cR.moveRightAction, cR.moveForwardAction, cR.moveBackwardAction)
|
||||
cR.moveDirection = (cR.camHolder.global_basis * Vector3(cR.inputDirection.x, 0.0, cR.inputDirection.y)).normalized()
|
||||
|
||||
#move only apply when the character is not on the floor (so if he's in the air)
|
||||
if !cR.is_on_floor():
|
||||
if cR.moveDirection:
|
||||
if cR.desiredMoveSpeed < cR.maxSpeed: cR.desiredMoveSpeed += cR.bunnyHopDmsIncre * delta
|
||||
|
||||
var contrdDesMoveSpeed : float = cR.desiredMoveSpeedCurve.sample(cR.desiredMoveSpeed)
|
||||
var contrdInAirMoveSpeed : float = cR.inAirMoveSpeedCurve.sample(cR.desiredMoveSpeed) * cR.inAirInputMultiplier
|
||||
|
||||
cR.velocity.x = lerp(cR.velocity.x, cR.moveDirection.x * contrdDesMoveSpeed, contrdInAirMoveSpeed * delta)
|
||||
cR.velocity.z = lerp(cR.velocity.z, cR.moveDirection.z * contrdDesMoveSpeed, contrdInAirMoveSpeed * delta)
|
||||
|
||||
if cR.velocity.length() > cR.maxSpeed:
|
||||
cR.velocity = cR.velocity.normalized() * cR.maxSpeed
|
||||
else:
|
||||
#accumulate desired speed for bunny hopping
|
||||
cR.desiredMoveSpeed = cR.velocity.length()
|
||||
|
||||
if cR.desiredMoveSpeed >= cR.maxSpeed: cR.desiredMoveSpeed = cR.maxSpeed
|
||||
|
||||
func jump():
|
||||
#manage the jump behaviour, depending of the different variables and states the character is
|
||||
|
||||
var canJump : bool = false #jump condition
|
||||
|
||||
#in air jump
|
||||
if !cR.is_on_floor():
|
||||
if !cR.coyoteJumpOn and cR.nbJumpsInAirAllowed > 0:
|
||||
cR.nbJumpsInAirAllowed -= 1
|
||||
cR.jumpCooldown = cR.jumpCooldownRef
|
||||
canJump = true
|
||||
if cR.coyoteJumpOn:
|
||||
cR.jumpCooldown = cR.jumpCooldownRef
|
||||
cR.coyoteJumpCooldown = -1.0 #so that the character cannot immediately make another coyote jump
|
||||
cR.coyoteJumpOn = false
|
||||
canJump = true
|
||||
|
||||
#on floor jump
|
||||
if cR.is_on_floor():
|
||||
cR.jumpCooldown = cR.jumpCooldownRef
|
||||
canJump = true
|
||||
|
||||
#jump buffering
|
||||
if cR.bufferedJump:
|
||||
cR.bufferedJump = false
|
||||
cR.nbJumpsInAirAllowed = cR.nbJumpsInAirAllowedRef
|
||||
|
||||
#apply jump
|
||||
if canJump:
|
||||
cR.velocity.y = cR.jumpVelocity
|
||||
canJump = false
|
||||
@@ -0,0 +1 @@
|
||||
uid://dvu58wf01wils
|
||||
@@ -0,0 +1,215 @@
|
||||
extends CharacterBody3D
|
||||
|
||||
class_name PlayerCharacter
|
||||
|
||||
@export_group("Dash Mechanics")
|
||||
@export var dash_speed: float = 15.0 # Скорость игрока во время рывка
|
||||
@export var dash_duration: float = 0.45 # Длительность рывка в секундах
|
||||
@export var dash_cooldown: float = 2.0 # Перезарядка дэша (в секундах)
|
||||
|
||||
var is_dashing: bool = false
|
||||
var dash_timer: float = 0.0
|
||||
var dash_cooldown_timer: float = 0.0
|
||||
var dash_direction: Vector3 = Vector3.ZERO
|
||||
|
||||
@onready var player_camera: Camera3D = %Camera # Ссылка на камеру (убедись, что %Camera есть в сцене)
|
||||
var normal_fov: float = 75.0 # Стандартный угол обзора
|
||||
|
||||
@export_group("Movement variables")
|
||||
var moveSpeed : float
|
||||
var moveAccel : float
|
||||
var moveDeccel : float
|
||||
var desiredMoveSpeed : float
|
||||
@export var desiredMoveSpeedCurve : Curve
|
||||
@export var maxSpeed : float
|
||||
@export var inAirMoveSpeedCurve : Curve
|
||||
var inputDirection : Vector2
|
||||
var moveDirection : Vector3
|
||||
@export var hitGroundCooldown : float #amount of time the character keep his accumulated speed before losing it (while being on ground)
|
||||
var hitGroundCooldownRef : float
|
||||
@export var bunnyHopDmsIncre : float #bunny hopping desired move speed incrementer
|
||||
@export var autoBunnyHop : bool = false
|
||||
var lastFramePosition : Vector3
|
||||
var lastFrameVelocity : Vector3
|
||||
var wasOnFloor : bool
|
||||
var walkOrRun : String = "WalkState" #keep in memory if play char was walking or running before being in the air
|
||||
#for crouch visible changes
|
||||
@export var baseHitboxHeight : float
|
||||
@export var baseModelHeight : float
|
||||
@export var heightChangeSpeed : float
|
||||
|
||||
@export_group("Crouch variables")
|
||||
@export var crouchSpeed : float
|
||||
@export var crouchAccel : float
|
||||
@export var crouchDeccel : float
|
||||
@export var continiousCrouch : bool = false #if true, doesn't need to keep crouch button on to crouch
|
||||
@export var crouchHitboxHeight : float
|
||||
@export var crouchModelHeight : float
|
||||
|
||||
@export_group("Walk variables")
|
||||
@export var walkSpeed : float
|
||||
@export var walkAccel : float
|
||||
@export var walkDeccel : float
|
||||
|
||||
@export_group("Run variables")
|
||||
@export var runSpeed : float
|
||||
@export var runAccel : float
|
||||
@export var runDeccel : float
|
||||
@export var continiousRun : bool = false #if true, doesn't need to keep run button on to run
|
||||
|
||||
@export_group("Jump variables")
|
||||
@export var jumpHeight : float
|
||||
@export var jumpTimeToPeak : float
|
||||
@export var jumpTimeToFall : float
|
||||
@onready var jumpVelocity : float = (2.0 * jumpHeight) / jumpTimeToPeak
|
||||
@export_group("Jump variables continuation") # Разделили одинаковые группы, чтобы Godot не ругался
|
||||
@export var jumpCooldown : float
|
||||
var jumpCooldownRef : float
|
||||
@export var nbJumpsInAirAllowed : int
|
||||
var nbJumpsInAirAllowedRef : int
|
||||
var jumpBuffOn : bool = false
|
||||
var bufferedJump : bool = false
|
||||
@export var coyoteJumpCooldown : float
|
||||
var coyoteJumpCooldownRef : float
|
||||
var coyoteJumpOn : bool = false
|
||||
@export_range(0.1, 1.0, 0.05) var inAirInputMultiplier: float = 1.0
|
||||
|
||||
@export_group("Gravity variables")
|
||||
@onready var jumpGravity : float = (-2.0 * jumpHeight) / (jumpTimeToPeak * jumpTimeToPeak)
|
||||
@onready var fallGravity : float = (-2.0 * jumpHeight) / (jumpTimeToFall * jumpTimeToFall)
|
||||
|
||||
@export_group("Keybind variables")
|
||||
@export var moveForwardAction : String = ""
|
||||
@export var moveBackwardAction : String = ""
|
||||
@export var moveLeftAction : String = ""
|
||||
@export var moveRightAction : String = ""
|
||||
@export var runAction : String = ""
|
||||
@export var crouchAction : String = ""
|
||||
@export var jumpAction : String = ""
|
||||
|
||||
#references variables
|
||||
@onready var camHolder : Node3D = $CameraHolder
|
||||
@onready var model : MeshInstance3D = $Model
|
||||
@onready var hitbox : CollisionShape3D = $Hitbox
|
||||
@onready var stateMachine : Node = %StateMachine
|
||||
@onready var hud : CanvasLayer = $HUD
|
||||
@onready var ceilingCheck : RayCast3D = $Raycasts/CeilingCheck
|
||||
@onready var floorCheck : RayCast3D = $Raycasts/FloorCheck
|
||||
|
||||
func _ready():
|
||||
# Запоминаем дефолтный FOV камеры, если она подключена
|
||||
if player_camera:
|
||||
normal_fov = player_camera.fov
|
||||
|
||||
#set move variables, and value references
|
||||
moveSpeed = walkSpeed
|
||||
moveAccel = walkAccel
|
||||
moveDeccel = walkDeccel
|
||||
|
||||
hitGroundCooldownRef = hitGroundCooldown
|
||||
jumpCooldownRef = jumpCooldown
|
||||
nbJumpsInAirAllowedRef = nbJumpsInAirAllowed
|
||||
coyoteJumpCooldownRef = coyoteJumpCooldown
|
||||
|
||||
func _process(_delta: float):
|
||||
displayProperties()
|
||||
|
||||
func _physics_process(_delta : float):
|
||||
# 1. Отсчет кулдауна перезарядки дэша
|
||||
if dash_cooldown_timer > 0:
|
||||
dash_cooldown_timer -= _delta
|
||||
|
||||
# 2. ЕСЛИ ИДЕТ ДЭШ: двигаем игрока силой кода
|
||||
if is_dashing:
|
||||
dash_timer -= _delta
|
||||
if dash_timer <= 0:
|
||||
is_dashing = false
|
||||
# Возвращаем FOV камеры обратно
|
||||
if player_camera:
|
||||
var tween = create_tween()
|
||||
tween.tween_property(player_camera, "fov", normal_fov, 0.15)
|
||||
|
||||
# ВКЛЮЧАЕМ СТEЙТ-МАШИНУ ОБРАТНО
|
||||
if stateMachine:
|
||||
stateMachine.process_mode = Node.PROCESS_MODE_INHERIT
|
||||
else:
|
||||
# Принудительно толкаем персонажа со скоростью дэша
|
||||
velocity.x = dash_direction.x * dash_speed
|
||||
velocity.z = dash_direction.z * dash_speed
|
||||
|
||||
# Применяем перемещение и ВЫХОДИМ, полностью блокируя ходьбу ассета во время рывка
|
||||
modifyPhysicsProperties()
|
||||
move_and_slide()
|
||||
return
|
||||
|
||||
# 3. ПРОВЕРКА НАЖАТИЯ КНОПКИ ДЭША
|
||||
if Input.is_action_just_pressed("dash") and dash_cooldown_timer <= 0 and not is_dashing:
|
||||
# Если игрок бежит — берем направление moveDirection, если стоит — направление камеры
|
||||
var final_dir = moveDirection
|
||||
if final_dir == Vector3.ZERO:
|
||||
final_dir = -camHolder.global_transform.basis.z
|
||||
|
||||
final_dir.y = 0 # Чтобы не летать вверх/вниз
|
||||
final_dir = final_dir.normalized()
|
||||
|
||||
# Запускаем дэш
|
||||
is_dashing = true
|
||||
dash_timer = dash_duration
|
||||
dash_cooldown_timer = dash_cooldown
|
||||
dash_direction = final_dir
|
||||
|
||||
# Эффект наплыва камеры (FOV)
|
||||
if player_camera:
|
||||
var tween = create_tween()
|
||||
tween.tween_property(player_camera, "fov", normal_fov + 15.0, 0.1)
|
||||
|
||||
# ВЫКЛЮЧАЕМ СТEЙТ-МАШИНУ НА ВРЕМЯ РЫВКА, чтобы она не сбивала скорость!
|
||||
if stateMachine:
|
||||
stateMachine.process_mode = Node.PROCESS_MODE_DISABLED
|
||||
|
||||
# Стандартная физика ходьбы из твоего ассета (работает, когда дэша нет)
|
||||
modifyPhysicsProperties()
|
||||
move_and_slide()
|
||||
|
||||
# Проверяем нажатие на кнопку дэша (кнопка "dash" должна быть настроена в Input Map)
|
||||
if Input.is_action_just_pressed("dash") and dash_cooldown_timer <= 0 and not is_dashing:
|
||||
# Если игрок бежит — дэшимся в сторону бега, если стоит — строго вперед по взгляду камеры
|
||||
var final_dir = moveDirection
|
||||
if final_dir == Vector3.ZERO:
|
||||
final_dir = -camHolder.global_transform.basis.z # Берем направление взгляда камеры
|
||||
|
||||
final_dir.y = 0 # Обнуляем вертикальную ось, чтобы игрок не улетал в небо или под пол
|
||||
final_dir = final_dir.normalized()
|
||||
|
||||
# Активируем рывок
|
||||
is_dashing = true
|
||||
dash_timer = dash_duration
|
||||
dash_cooldown_timer = dash_cooldown
|
||||
dash_direction = final_dir
|
||||
|
||||
# Эффект скорости: плавно увеличиваем угол обзора (FOV) камеры на +15 градусов
|
||||
if player_camera:
|
||||
var tween = create_tween()
|
||||
tween.tween_property(player_camera, "fov", normal_fov + 15.0, 0.1)
|
||||
|
||||
# Стандартная физика ассета (вызывается, только когда мы не в дэше)
|
||||
modifyPhysicsProperties()
|
||||
move_and_slide()
|
||||
|
||||
func displayProperties():
|
||||
if hud != null:
|
||||
hud.displayCurrentState(stateMachine.currStateName)
|
||||
hud.displayCurrentDirection(moveDirection)
|
||||
hud.displayDesiredMoveSpeed(desiredMoveSpeed)
|
||||
hud.displayVelocity(velocity.length())
|
||||
hud.displayNbJumpsInAirAllowed(nbJumpsInAirAllowed)
|
||||
|
||||
func modifyPhysicsProperties():
|
||||
lastFramePosition = position
|
||||
lastFrameVelocity = velocity
|
||||
wasOnFloor = !is_on_floor()
|
||||
|
||||
func gravityApply(delta : float):
|
||||
if !is_on_floor():
|
||||
if velocity.y >= 0.0: velocity.y += jumpGravity * delta
|
||||
elif velocity.y < 0.0: velocity.y += fallGravity * delta
|
||||
@@ -0,0 +1 @@
|
||||
uid://d3s8yxuvpmekm
|
||||
@@ -0,0 +1,84 @@
|
||||
extends State
|
||||
|
||||
class_name RunState
|
||||
|
||||
var stateName : String = "Run"
|
||||
|
||||
var cR : CharacterBody3D
|
||||
|
||||
func enter(charRef : CharacterBody3D):
|
||||
cR = charRef
|
||||
|
||||
verifications()
|
||||
|
||||
func verifications():
|
||||
cR.moveSpeed = cR.runSpeed
|
||||
cR.moveAccel = cR.runAccel
|
||||
cR.moveDeccel = cR.runDeccel
|
||||
|
||||
cR.floor_snap_length = 1.0
|
||||
if cR.jumpCooldown > 0.0: cR.jumpCooldown = -1.0
|
||||
if cR.nbJumpsInAirAllowed < cR.nbJumpsInAirAllowedRef: cR.nbJumpsInAirAllowed = cR.nbJumpsInAirAllowedRef
|
||||
if cR.coyoteJumpCooldown < cR.coyoteJumpCooldownRef: cR.coyoteJumpCooldown = cR.coyoteJumpCooldownRef
|
||||
|
||||
func physics_update(delta : float):
|
||||
checkIfFloor()
|
||||
|
||||
applies(delta)
|
||||
|
||||
cR.gravityApply(delta)
|
||||
|
||||
inputManagement()
|
||||
|
||||
move(delta)
|
||||
|
||||
func checkIfFloor():
|
||||
if !cR.is_on_floor():
|
||||
if cR.velocity.y < 0.0:
|
||||
transitioned.emit(self, "InairState")
|
||||
if cR.is_on_floor():
|
||||
if cR.autoBunnyHop and cR.hitGroundCooldown > 0.0 and cR.inputDirection != Vector2.ZERO:
|
||||
transitioned.emit(self, "JumpState")
|
||||
if cR.jumpBuffOn:
|
||||
cR.bufferedJump = true
|
||||
cR.jumpBuffOn = false
|
||||
transitioned.emit(self, "JumpState")
|
||||
|
||||
func applies(delta : float):
|
||||
if cR.hitGroundCooldown > 0.0: cR.hitGroundCooldown -= delta
|
||||
|
||||
cR.hitbox.shape.height = lerp(cR.hitbox.shape.height, cR.baseHitboxHeight, cR.heightChangeSpeed * delta)
|
||||
cR.model.scale.y = lerp(cR.model.scale.y, cR.baseModelHeight, cR.heightChangeSpeed * delta)
|
||||
|
||||
func inputManagement():
|
||||
if Input.is_action_just_pressed(cR.jumpAction):
|
||||
transitioned.emit(self, "JumpState")
|
||||
|
||||
if Input.is_action_just_pressed(cR.crouchAction):
|
||||
transitioned.emit(self, "CrouchState")
|
||||
|
||||
if cR.continiousRun:
|
||||
#has to press run button once to run
|
||||
if Input.is_action_just_pressed(cR.runAction):
|
||||
cR.walkOrRun = "WalkState"
|
||||
transitioned.emit(self, "WalkState")
|
||||
else:
|
||||
#has to continuously press run button to run
|
||||
if !Input.is_action_pressed(cR.runAction):
|
||||
cR.walkOrRun = "WalkState"
|
||||
transitioned.emit(self, "WalkState")
|
||||
|
||||
|
||||
func move(delta : float):
|
||||
cR.inputDirection = Input.get_vector(cR.moveLeftAction, cR.moveRightAction, cR.moveForwardAction, cR.moveBackwardAction)
|
||||
cR.moveDirection = (cR.camHolder.global_basis * Vector3(cR.inputDirection.x, 0.0, cR.inputDirection.y)).normalized()
|
||||
|
||||
if cR.moveDirection and cR.is_on_floor():
|
||||
cR.velocity.x = lerp(cR.velocity.x, cR.moveDirection.x * cR.moveSpeed, cR.moveAccel * delta)
|
||||
cR.velocity.z = lerp(cR.velocity.z, cR.moveDirection.z * cR.moveSpeed, cR.moveAccel * delta)
|
||||
|
||||
if cR.hitGroundCooldown <= 0: cR.desiredMoveSpeed = cR.velocity.length()
|
||||
else:
|
||||
transitioned.emit(self, "IdleState")
|
||||
|
||||
if cR.desiredMoveSpeed >= cR.maxSpeed: cR.desiredMoveSpeed = cR.maxSpeed
|
||||
@@ -0,0 +1 @@
|
||||
uid://5gk47o641xgt
|
||||
@@ -0,0 +1,45 @@
|
||||
extends Node
|
||||
|
||||
@export var initialState : State
|
||||
|
||||
var currState : State
|
||||
var currStateName : String
|
||||
var states : Dictionary = {}
|
||||
|
||||
@onready var charRef : CharacterBody3D = $".."
|
||||
|
||||
func _ready():
|
||||
#get all the state childrens
|
||||
for child in get_children():
|
||||
if child is State:
|
||||
states[child.name.to_lower()] = child
|
||||
child.transitioned.connect(onStateChildTransition)
|
||||
|
||||
#if initial state, transition to it
|
||||
if initialState:
|
||||
initialState.enter(charRef)
|
||||
currState = initialState
|
||||
currStateName = currState.stateName
|
||||
|
||||
func _process(delta : float):
|
||||
if currState: currState.update(delta)
|
||||
|
||||
func _physics_process(delta: float):
|
||||
if currState: currState.physics_update(delta)
|
||||
|
||||
func onStateChildTransition(state : State, newStateName : String):
|
||||
#manage the transition from one state to another
|
||||
|
||||
if state != currState: return
|
||||
|
||||
var newState = states.get(newStateName.to_lower())
|
||||
if !newState: return
|
||||
|
||||
#exit the current state
|
||||
if currState: currState.exit()
|
||||
|
||||
#enter the new state
|
||||
newState.enter(charRef)
|
||||
|
||||
currState = newState
|
||||
currStateName = currState.stateName
|
||||
@@ -0,0 +1 @@
|
||||
uid://sca7ypsol83f
|
||||
@@ -0,0 +1,21 @@
|
||||
extends Node
|
||||
|
||||
class_name State
|
||||
|
||||
signal transitioned
|
||||
|
||||
func enter(_charReference : CharacterBody3D):
|
||||
#enter state
|
||||
pass
|
||||
|
||||
func exit():
|
||||
#exit state
|
||||
pass
|
||||
|
||||
func update(_delta : float):
|
||||
#process update
|
||||
pass
|
||||
|
||||
func physics_update(_delta : float):
|
||||
#physics_process update
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
uid://gjekmtw4eb1e
|
||||
@@ -0,0 +1,83 @@
|
||||
extends State
|
||||
|
||||
class_name WalkState
|
||||
|
||||
var stateName : String = "Walk"
|
||||
|
||||
var cR : CharacterBody3D
|
||||
|
||||
func enter(charRef : CharacterBody3D):
|
||||
cR = charRef
|
||||
|
||||
verifications()
|
||||
|
||||
func verifications():
|
||||
cR.moveSpeed = cR.walkSpeed
|
||||
cR.moveAccel = cR.walkAccel
|
||||
cR.moveDeccel = cR.walkDeccel
|
||||
|
||||
cR.floor_snap_length = 1.0
|
||||
if cR.jumpCooldown > 0.0: cR.jumpCooldown = -1.0
|
||||
if cR.nbJumpsInAirAllowed < cR.nbJumpsInAirAllowedRef: cR.nbJumpsInAirAllowed = cR.nbJumpsInAirAllowedRef
|
||||
if cR.coyoteJumpCooldown < cR.coyoteJumpCooldownRef: cR.coyoteJumpCooldown = cR.coyoteJumpCooldownRef
|
||||
|
||||
func physics_update(delta : float):
|
||||
checkIfFloor()
|
||||
|
||||
applies(delta)
|
||||
|
||||
cR.gravityApply(delta)
|
||||
|
||||
inputManagement()
|
||||
|
||||
move(delta)
|
||||
|
||||
func checkIfFloor():
|
||||
if !cR.is_on_floor() and !cR.is_on_wall():
|
||||
if cR.velocity.y < 0.0:
|
||||
transitioned.emit(self, "InairState")
|
||||
if cR.is_on_floor():
|
||||
#check if can auto bunny hop
|
||||
if cR.autoBunnyHop and cR.hitGroundCooldown > 0.0 and cR.inputDirection != Vector2.ZERO:
|
||||
transitioned.emit(self, "JumpState")
|
||||
if cR.jumpBuffOn:
|
||||
#apply jump buffering
|
||||
cR.bufferedJump = true
|
||||
cR.jumpBuffOn = false
|
||||
transitioned.emit(self, "JumpState")
|
||||
|
||||
func applies(delta : float):
|
||||
if cR.hitGroundCooldown > 0.0: cR.hitGroundCooldown -= delta
|
||||
|
||||
cR.hitbox.shape.height = lerp(cR.hitbox.shape.height, cR.baseHitboxHeight, cR.heightChangeSpeed * delta)
|
||||
cR.model.scale.y = lerp(cR.model.scale.y, cR.baseModelHeight, cR.heightChangeSpeed * delta)
|
||||
|
||||
func inputManagement():
|
||||
if Input.is_action_just_pressed("dash") and cR.dash_cooldown_timer <= 0:
|
||||
transitioned.emit(self, "DashState")
|
||||
return # Выходим из функции, чтобы не вызывать другие стейты
|
||||
if Input.is_action_just_pressed(cR.jumpAction):
|
||||
transitioned.emit(self, "JumpState")
|
||||
|
||||
if Input.is_action_just_pressed(cR.crouchAction):
|
||||
transitioned.emit(self, "CrouchState")
|
||||
|
||||
if Input.is_action_just_pressed(cR.runAction):
|
||||
cR.walkOrRun = "RunState"
|
||||
transitioned.emit(self, "RunState")
|
||||
|
||||
func move(delta : float):
|
||||
cR.inputDirection = Input.get_vector(cR.moveLeftAction, cR.moveRightAction, cR.moveForwardAction, cR.moveBackwardAction)
|
||||
cR.moveDirection = (cR.camHolder.global_basis * Vector3(cR.inputDirection.x, 0.0, cR.inputDirection.y)).normalized()
|
||||
|
||||
if cR.moveDirection and cR.is_on_floor():
|
||||
#apply smooth move
|
||||
cR.velocity.x = lerp(cR.velocity.x, cR.moveDirection.x * cR.moveSpeed, cR.moveAccel * delta)
|
||||
cR.velocity.z = lerp(cR.velocity.z, cR.moveDirection.z * cR.moveSpeed, cR.moveAccel * delta)
|
||||
|
||||
if cR.hitGroundCooldown <= 0: cR.desiredMoveSpeed = cR.velocity.length()
|
||||
|
||||
else:
|
||||
transitioned.emit(self, "IdleState")
|
||||
|
||||
if cR.desiredMoveSpeed >= cR.maxSpeed: cR.desiredMoveSpeed = cR.maxSpeed
|
||||
@@ -0,0 +1 @@
|
||||
uid://blt7dest23fk0
|
||||
Reference in New Issue
Block a user