• Edited

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
  • xyz replied to this.

    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.

    • Best Answerset by NJL64

    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".

      • Edited

      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.

      • xyz replied to this.

        xyz Thank you so much! I'm gonna try that.

        • Edited

        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 replied to this.

          NJL64
          Just use your velocity vector instead of input direction vector. You can disregard the tolerance stuff as it doesn't apply to your case

            • Edited

            xyz So I wrote this function (although I admit I don't understand it myself):

            func get_dir():
            	var dir = velocity
            	var angle = posmod(rad_to_deg(dir.angle()), 360)
            	var sector = wrapi(snapped(angle, 45) / 45, 0, 8)

            But, how should I use it to update the value of "dir_last"? 🙁

            • xyz replied to this.
              • Edited

              NJL64 Return the value from the function and assign it to dir_last

                xyz What's the value from the function?

                • xyz replied to this.
                  • Edited

                  NJL64 Functions can return values. That's in fact one of their main purpose, to calculate something and then return it. Look at my code on the link again. The function there returns a direction string.

                    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 replied to this.

                      NJL64 Return direction vectors instead of strings.