- Edited
NJL64 When it bounces up it prints "Facing: (1, -1)". and if it bounces down it says (-1, 1).
What's the value of velocity when that happens, when get_direction() is called?
NJL64 When it bounces up it prints "Facing: (1, -1)". and if it bounces down it says (-1, 1).
What's the value of velocity when that happens, when get_direction() is called?
DaveTheCoder
From the print statements:
When it started bouncing: (394.4125, -295.7835)
Before it reached zero: (-0.000315, 3.000107)
get_direction() is called in the Physics Process function, so I can't put a red dot next to it. I'm not sure how to determine what value velocity is (besides zero) when get_direction is called, because it's called every frame if I understand correctly.
This is currently the script:
extends CharacterBody2D
# state
enum States {IDLE, MOVE, BLAST, BOUNCE, SPIN}
var state = States.IDLE # default state
# exportable variables (can be changed in editor)
@export var acceleration = 1.5 # increase in speed by this much
@export var deceleration = 0 # default
@export var deceleration_move = 15 # deceleration when moving with input
@export var deceleration_blast = 210 # deceleration when blasting
@export var dir_last = Vector2(0, 1) # note: needs to be global to work
@export var speed = 0 # default
@export var speed_blast = 500 # speed when blast is performed
@export var speed_move = 100 # speed when moving with input
# functions
func _physics_process(delta): # the delta variable has the value of the frame time that the engine passes to the function.
# local variables
var collision = move_and_collide(velocity * delta)
var dir_input = Input.get_vector("left", "right", "up", "down")
var facing = dir_last # see get_directon() function to see what dir_last equals
match state:
States.IDLE:
if Input.is_action_pressed("move"):
state = States.MOVE # change state
elif Input.is_action_just_released("blast"):
state = States.BLAST # change state
elif Input.is_action_pressed("spin"):
state = States.SPIN
else:
velocity = Vector2.ZERO # idle
speed = 0
States.MOVE:
if ! Input.is_action_pressed("move") and velocity.is_zero_approx():
state = States.IDLE # change state
elif Input.is_action_just_released("blast"):
state = States.BLAST # change state
else:
velocity = dir_input * speed
speed = speed_move
move_and_slide()
States.BLAST:
if Input.is_action_pressed("move"): # if input JUST pressed, not ALREADY pressed
state = States.MOVE
elif collision:
state = States.BOUNCE # change state
else:
velocity = facing * speed # blast in direction facing
velocity = velocity.normalized() * speed # avoid going faster when diagonal
deceleration = deceleration_blast
speed = speed_blast
apply_deceleration(delta)
States.BOUNCE:
if ! collision:
if velocity.is_zero_approx():
state = States.IDLE # change state
elif Input.is_action_pressed("move"):
state = States.MOVE # change state
elif Input.is_action_just_released("blast"):
state = States.BLAST # change state
elif ! Input.is_action_pressed("move"):
apply_deceleration(delta)
deceleration = deceleration_blast
else:
velocity = velocity.bounce(collision.get_normal()) # bounce (get the vector's normal)
# note: only faces diagonal after bouncing, even if moving cardinal
# Experimental state only when 'A' is pressed on keyboard:
States.SPIN:
if Input.is_action_pressed("spin"):
state = States.IDLE
elif Input.is_action_just_released("blast"):
state = States.BLAST # change state
elif Input.is_action_just_released("move"):
deceleration = deceleration_move
apply_deceleration(delta)
elif collision: # says null instance without "collision"
velocity = velocity.bounce(collision.get_normal())
else:
apply_acceleration(dir_input)
move_and_slide()
get_direction()
print("State: ", state)
print("Facing: ", facing)
print("Speed: ", speed)
print("Velocity: ", velocity)
func apply_acceleration(dir_input):
velocity = velocity.move_toward(dir_input * speed_move, acceleration)
func apply_deceleration(delta):
velocity = velocity.move_toward(Vector2.ZERO, deceleration * delta)
func get_direction():
if is_zero_approx(velocity.y):
if velocity.x < 0:
dir_last = Vector2.LEFT
return
elif velocity.x > 0:
dir_last = Vector2.RIGHT
return
if is_zero_approx(velocity.x):
if velocity.y < 0:
dir_last = Vector2.UP
return
elif velocity.y > 0:
dir_last = Vector2.DOWN
return
# diagonal
if velocity.x < 0 and velocity.y < 0:
dir_last = Vector2.LEFT + Vector2.UP #NW when bouncing up?
return
elif velocity.x > 0 and velocity.y < 0:
dir_last = Vector2.RIGHT + Vector2.UP #NE when bouncing up?
return
elif velocity.x < 0 and velocity.y > 0:
dir_last = Vector2.LEFT + Vector2.DOWN #SW # when bouncing down?
return
elif velocity.x > 0 and velocity.y > 0:
dir_last = Vector2.RIGHT + Vector2.DOWN #SE # when bouncing down?
return
In _physics_process, you could accumulate the values of velocity
and dir_last
in arrays. For example, store 100 values before and after a bounce. Then you could print and look at them after a bounce, to see what happened.
NJL64 Your logic is flawed here. If the thing bounces perceptually (but not perfectly) in the cardinal direction both velocity components will be non-zero although one of them may be very small, but since you only check for a perfect cardinal bounce all non perfect ones will be registered as "diagonal".
xyz I understand. That makes perfect sense. I think that's what's wrong actually. Thank you!
Is there a way I could possibly check for directions better, so that I can check for imperfect cardinal bounces?
I know my homemade get_direction() function isn't the best and probably only accounts for 8 perfect directions.
NJL64 Get the angle of the velocity vector and snap it to 45 deg increments.
I think I answered very similar question(s) couple of times already in the past
Take a look here:
https://godotforums.org/d/38606-8-directional-input/2
xyz Sorry. I know you said you answered similar questions already, but I was wondering if you could help me understand a way to apply this to my function?
Mine's not for inputs, but rather just the character moving in general.
I just thought I'd ask ahead of time in case I have trouble. I'm researching some of this stuff right now and thinking of how to apply it to my script.
When I get the angle of the velocity vector and snap it to 45 degrees, what should I do with it then? (I'm still brushing up on my Vector math)
I would really appreciate it.
xyz So I tried this:
func get_dir():
var dir = velocity
var angle = posmod(rad_to_deg(dir.angle()), 360)
var sector = wrapi(snapped(angle, 45) / 45, 0, 8)
return ["E", "SE", "S", "SW", "W", "NW", "N", "NE"] [sector]
and this:
dir_last = get_dir()
and it works for input and seems to work for bouncing off of things! But if I could fix one last issue, it should work how I want it.
Unfortunately I can no longer say
velocity = dir_last * speed
when shooting my character in the direction facing using the "BLAST" state.
If you would be so, SO generous to help me with with an alternative so that I can still shoot the character in the direction they are facing, it would really help me out.
Thank you!
xyz @DaveTheCoder Thank you!