I know that this isn't a problem that is holding me back but, I fear that not understanding my own code could cause problems in the future; I also can't get this topic of my mind right now. So I'm just going to show the movement code and explain why I can't understand it fully.
This is the movement code for my player character:

func movement_code(delta):
	var horizontal_velocity = Input.get_vector("move_left","move_right","move_forward","move_backward").normalized() * player.speed
	var global_transform_basis = player.global_transform.basis
	player.velocity = horizontal_velocity.x * global_transform_basis.x + horizontal_velocity.y * global_transform_basis.z
	if player.is_on_floor():
		player.velocity_y = 0
		if Input.is_action_just_pressed("Jump"):
			player.velocity_y = jump_velocity
	else:player.velocity_y -= gravity * delta
	player.velocity.y = player.velocity_y
	player.move_and_slide()

It works well and it does what it supposed to do. I got the code from a Youtube tutorial titled "Godot 4 First-Person Controller with 22 Lines of Code". Here it is:

A challenge that I have had with this code, is when I tried to edit it for enemy AI patrol movement; I just don't know where to start. Now I do have code that works well for the enemy AI patrolling. However, that code wasn't a modification of the player movement code. Instead, this is what I got.

func move_forward():
	enemy.position -= enemy.transform.basis.z * speed
	enemy.move_and_slide()

The code makes the enemy move forward, while another function makes the enemy turn away from obstracles it detects with the raycast. It works well but, it doesn't involve manipulating the enemy's velocity property while the movement code for the player does involve manipulating the player's velocity property.
Everytime when I try to get the enemy to move by manipulating it's velocity property, I don't seem to get the results I want; this is probably indicative of my ignorance regarding how the player movement code works or how the velocity property works in Godot 4.

The documentation says that velocity is a Vector3 for CharacterBody3D nodes. However, my enemy wouldn't move at all if I were to make the code like this:

func move_forward():
	enemy.velocity.z += 5
	enemy.move_and_slide()

I'm not sure if anything I'm saying here even makes any sense to anyone else. Could anyone explain to me what is going on?

    Audiobellum Add comments to every line in your player movement code, describing your understanding of what is happening in that line. Post that commented code up for discussion.

      xyz Okay. Here's what I added.

      func movement_code(delta):
      	var horizontal_velocity = Input.get_vector("move_left","move_right","move_forward","move_backward").normalized() * player.speed
      	#var horizontal_velocity is a vector2 manipulated by player inputs; it's being multipled by speed and normalized to make horizontal movement consistent.
      	var global_transform_basis = player.global_transform.basis
      	#I don't understand what var global_transform_basis means completely but, I think it makes local movement possible. If that's the case, I'd find it useful for steering/patrolling.
      	player.velocity = horizontal_velocity.x * global_transform_basis.x + horizontal_velocity.y * global_transform_basis.z
      	#I know that velocity property is a Vector3. 
      	#I don't know how the horizontal_velocity.x * global_transform_basis.x + horizontal_velocity.y * global_transform_basis.z is also a Vector3.
      	#The gravity code seems easy and straight forward. I think I understand it but, I also suspect it's irrelevant to what I'm concerned about right now.
      	if player.is_on_floor():
      		player.velocity_y = 0
      		if Input.is_action_just_pressed("Jump"):
      			player.velocity_y = jump_velocity
      	else:player.velocity_y -= gravity * delta
      	player.velocity.y = player.velocity_y
      	player.move_and_slide()
      • xyz replied to this.

        Audiobellum
        Nice.
        Firstly, just a stylistic remark; there's a widely accepted convention to place comments above the block/line they refer to, or at the end of the line. As a general advice, get into habit of commenting as much as possible. It makes code more readable and easier to get into for other persons as well as yourself after not seeing it for a long time. When implementing an algorithm it's often a good approach to first write the whole function/class just as a series of comments, and then implement the actual code piece by piece, leaving relevant comments in place.

        From comments in your code 😉 it seems that the only thing you actually don't understand is basis and how it's used for calculating the velocity vector.

        Basis is a collection of 3 vectors. Each of those vectors is one coordinate axis of your object's local coordinate system. The basis is needed to represent the orientation of an object in 3D space.

        You can visualize it like this:

        Make the depicted gesture with your right hand.
        If the hand is your object then each finger represents one of it local coordinate axes: x, y, and z. Those coordinate axes are the basis vectors. The length of each basis vector is always 1.0 (i.e. they are unit vectors)

        Imagine that your room is the global space. If you align your fingers with the room walls then the basis vectors will be the same as global coordinate system axes; your x basis will be (1, 0, 0) etc...

        However if you rotate your hand into any other random position, then your basis vectors may become some arbitrary vectors (in respect to global space of the room).

        Now if you want to tell your hand to move in the direction of your index finger, the velocity vector will need to be aligned with that index finger, meaning the velocity vector should be set to have the direction of your basis x vector, and magnitude of the movement speed.

        Audiobellum it seems like all your movement code is separated from your main script.
        xyz explained it pretty well, so just gonna provide another script.

        extends CharacterBody3D
        
        var speed = 200
        var gravity = 40
        
        func get_input(): 
        	var input_vec : Vector3 #create a vector3 and assign each direction to it. (
        	if Input.is_action_pressed("forward"):
        		input_vec += Vector3.FORWARD
        	if Input.is_action_pressed("backward"):
        		input_vec += Vector3.BACK
        	if Input.is_action_pressed("left"):
        		input_vec += Vector3.LEFT
        	if Input.is_action_pressed("right"):
        		input_vec += Vector3.RIGHT
        	input_vec = input_vec.normalized() #normalize it so going diagonally doesn't go faster 
        	input_vec = input_vec.rotated(Vector3.UP, rotation.y) #rotate the input vec depending on the rotation of the player 
        	return input_vec #return the vector
        
        func _physics_process(delta):
        	if !is_on_floor():
        		velocity.y -= gravity * delta 
        	velocity = get_input() * delta * speed #velocity is a vector3, you add delta and some speed to it to control it how you want 
        	
        	move_and_slide() #call move and slide at the end

          Loxyrak

          Okay. So this is what I came up with for my enemy.

          func enemy_forward_direction():
          	var forward : Vector3 = Vector3.FORWARD #Vector representing forward movement.
          	forward = forward.normalized()# This prevents diagonal movement from being faster.
          	forward = forward.rotated(Vector3.UP,enemy.rotation.y) #This ensures that the rotation of the forward vector is always the same as the enemy.
          	return forward
          
          func move_forward(delta):
          	enemy.velocity = enemy_forward_direction() * speed * delta
          	enemy.move_and_slide()

          I didn't get any errors but, I don't see any movements. I don't know what I'm doing work here.

            Audiobellum
            I guess my explanation wasn't really understood 😉

            Your forward vector should be one of the basis vectors in object's global transform. You decide which one. That vector is already "properly" rotated, so no need to do any additional rotation. Just take that vector. You can normalize it just in case the object is scaled. It really takes only one line of code to calculate the forward velocity vector.

            There isn't any visible movement probably because you multiply your velocity vector by delta. Delta is typically a very small number. This makes velocity magnitude small as well, resulting in object barely moving 🙂

            Besides, according to movement equations in physics, velocity multiplied by time equals distance travelled. You only need to specify the velocity. The distance travelled is calculated/applied by move_and_slide()

              xyz

              So I did this:

              func enemy_forward_direction():
              	var forward  = enemy.transform.basis.z #Vector representing forward movement.
              	forward = forward.normalized()# This prevents diagonal movement from being faster.
              	return forward
              
              func move_forward(delta):
              #	enemy.position -= enemy.transform.basis.z * speed
              	enemy.velocity = enemy_forward_direction() * speed
              	enemy.move_and_slide()

              I still don't have any error, which is good. However, I still don't see any movement. The speed is at 7.

              • xyz replied to this.

                spaceyjase I place it in another function for patrolling and then that's running somewhere else in the state machine:

                func patrol_demo(delta):
                	if raycast.is_colliding():
                		var target = raycast.get_collision_point()
                		if enemy.position.distance_to(target) < 25:
                			if raycast.rotation_degrees.y >= 1:
                				enemy.rotation_degrees.y -= 1
                			elif raycast.rotation_degrees.y <= 1:
                				enemy.rotation_degrees.y += 1
                	move_forward(delta)

                and here's where all of that is being called:

                func on_state_update(delta):#Function for running the state.
                	sweeping()
                	patrol_demo(delta)

                Audiobellum Don't touch the position. That's move_and_slide() 's job. Also you'd typically want to take the basis from global_transform, not transform. But this really depends on your node setup. And make sure move_forward() is actually called from _process() or _physics_process() as others have pointed out. Put a print statement in there just to be sure it's called.

                  xyz I'm still a bit confused. I don't seem to have this problem with the movement code for the player, despite it having a similar finite state machine to the enemy. The code for the player movement is within a function that's being called from _process() and it works. However, similarly doesn't seem to work for the enemy movement. Here's what I have now (I commented out what I was using before just in case I need to go back to it):

                  func move_forward():
                  #	enemy.position -= enemy.transform.basis.z * speed
                  	enemy.velocity =enemy.global_transform.basis.z.normalized() * speed
                  	enemy.move_and_slide()
                  • xyz replied to this.

                    Audiobellum Does enemy have a state machine? From where do you call enemy's move_and_slide()? It surely doesn't make sense to call it from player's state machine. Put the enemy code into a script attached to enemy's character body node.

                      xyz The enemy has it's own state machine, similar to that of the player. All the states of the enemy inherit from a class I call "enemy_class"; this makes them have access to the enemy node.
                      Here's the code that runs the current_state:

                      func _process(_delta): #All this does is run the code for the new state.
                      	current_state.on_state_update(_delta)

                      Here's the code for the patrol state.

                      func on_state_update(delta):#Function for running the state.
                      	sweeping()
                      	patrol_demo(delta)
                      	move_forward()

                      and here is the movement code.

                      func move_forward():
                      #	enemy.position -= enemy.transform.basis.z * speed
                      	enemy.velocity -= enemy.global_transform.basis.z.normalized() * speed
                      	enemy.move_and_slide()
                      • xyz replied to this.

                        xyz Damn! It's not doing anything at all! 😆 I got back a vector with zero on all axis when I printed.

                        func move_forward():
                        #	enemy.position -= enemy.transform.basis.z * speed
                        	enemy.velocity += enemy.global_transform.basis.z.normalized() * speed
                        	print(velocity)
                        	enemy.move_and_slide()

                        Edit: Okay. I started to get a number back when I made this change:

                        func move_forward():
                        #	enemy.position -= enemy.transform.basis.z * speed
                        	enemy.velocity = enemy.global_transform.basis.z.normalized() * speed
                        	print(enemy.velocity)
                        	enemy.move_and_slide()

                        Edit again. I wasn't printing the enemy's velocity specifically. Now that I have, I do get back a value.

                        func move_forward():
                        #	enemy.position -= enemy.transform.basis.z * speed
                        	enemy.velocity += enemy.global_transform.basis.z.normalized() * speed
                        	print(enemy.velocity)
                        	enemy.move_and_slide()

                        The output keeps saying (0,0,7). However, I'm still seeing no movement from the enemy.

                          Audiobellum There's likely a problem with your setup. Try to do this in a clean project with just a couple of nodes, without any state machine stuff.

                            xyz Add comments to every line in your player movement code, describing your understand of what is happening in that line.

                            One of the laws of programming: If code is too complicated to describe, then it's too complicated.

                            xyz You've got a point. The code seems to work well without the state machine stuff when I tried it out on another node. Now I just need to find a way to make it work with the state machine! 😆