• Edited

DaveTheCoder Sorry. I have a hard time when using the "insert code" feature. It always displays it improperly. I didn't know you had to highlight it and THEN press "insert code".

func get_direction():
	# cardinal
	if velocity.x < 0:
		dir_last = Vector2.LEFT
	elif velocity.x > 0:
		dir_last = Vector2.RIGHT
	if velocity.y < 0:
		dir_last = Vector2.UP
	elif velocity.y > 0:
		dir_last = Vector2.DOWN
	
	# diagonal
	if velocity.x < 0 and velocity.y < 0:
		dir_last = Vector2.LEFT + Vector2.UP #NW when bouncing down?
	elif velocity.x > 0 and velocity.y < 0:
		dir_last = Vector2.RIGHT + Vector2.UP #NE when bouncing up?
	elif velocity.x < 0 and velocity.y > 0:
		dir_last = Vector2.LEFT + Vector2.DOWN #SW # when bouncing up?
	elif velocity.x > 0 and velocity.y > 0:
		dir_last = Vector2.RIGHT + Vector2.DOWN #SE # when bouncing down?

Here it is if you want to show me. I'm a visual learner, so I appreciate it. 🙂

If by merging the two blocks, you mean merging the diagonal ifs into the cardinal ifs, I've tried that, but let me try the zero aprox thing. Thank you btw.

    NJL64 "insert code" feature
    That doesn't work reliably. Paste the code like normal text, and place ~~~ and ~~~ on lines before and after it.

    # cardinal
    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
    ...

    The multiple return's are a little messy, but if dir_last is assigned a value in the first section, you don't want it changed in the second section.

    There may be a more elegant way of doing this.

      • Edited

      DaveTheCoder Thanks! And no worries. I wanted this get_direction function to be more concise as it was, but I'm still learning.

      How would you do the second section?

      It still correctly updates the direction the character is facing when applying input, but it still only updates with diagonal directions when bouncing, again, even if the character velocity is moving in a cardinal direction as a result of the bounce.

      When it bounces up it prints "Facing: (1, -1)". and if it bounces down it says (-1, 1).

      So far, it still works exactly the same with this code change. I just don't know what it could be.

        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?

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