Hello there, recently I decided making 2D games was way to much work for me (especially with 2D animations) that I chose to swap to doing 3D because unlike 2D, I know what Im doing (art wise), I then realized I have never made a 3D game in godot before and for the life of me, cannot figure out how to make player movement, I've looked at over 12 tutorials now and I've gotten no where.

So here I am!
All I need for said system is just to make the player node (which is a CharacterBody3D) be able to move in 4 directions: forwards, backwards, left, and right. And if you feel like helping me a little more a jump mechanic along with some gravity.

Im hoping that you're not just gonna give me a bunch of code and would prefer if you could teach me how it works, and why it works.

Thanks~
~ Froyo

    Frodev Can't believe there are no usable tutorials on such a basic thing.

      xyz
      I'd agree with you but I could not find any for the life of me that actually worked

        Frodev Are you serious? What do you mean not work? Im devving in godot in 3d, and how come i found tutorials that "WORKS"?

        Anyways,
        Next create char3d node and add script

        extends CharacterBody3D
        
        @onready var camera_3d = $Camera3D
        
        var h_sensitivity = 0.20
        var v_sensitivity = 0.20
        
        @export_category("Camera vertical angles")
        @export var cam_v_min = -90.0 # degrees
        @export var cam_v_max = 90.0 # degrees
        var camrot_h = 0.0
        var camrot_v = 0.0
        
        const SPEED = 5.0
        const JUMP_VELOCITY = 4.5
        
        # Get the gravity from the project settings to be synced with RigidBody nodes.
        var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
        
        func _ready():
        	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
        	
        
        func _input(event):
        
        	if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
        		#rotate_y(deg_to_rad(-event.relative.x * h_sensitivity))
        		camera_3d.rotate_y(deg_to_rad(-event.relative.x * h_sensitivity))
        		camera_3d.rotate_x(deg_to_rad(-event.relative.y * h_sensitivity))
        		camera_3d.rotation.x = clampf(camera_3d.rotation.x, deg_to_rad(cam_v_min), deg_to_rad(cam_v_max))
        
        func _physics_process(delta):
        	# Add the gravity.
        	if not is_on_floor():
        		velocity.y -= gravity * delta
        
        	# Handle jump.
        	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        		velocity.y = JUMP_VELOCITY
        
        	# Get the input direction and handle the movement/deceleration.
        	# As good practice, you should replace UI actions with custom gameplay actions.
        	var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
        	var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
        	if direction:
        		velocity.x = direction.x * SPEED
        		velocity.z = direction.z * SPEED
        	else:
        		velocity.x = move_toward(velocity.x, 0, SPEED)
        		velocity.z = move_toward(velocity.z, 0, SPEED)
        
        	move_and_slide()

        Add shapes to collision and meshinstnace3d

        Add camera

        You should be able to move with arrow keys now and mouse.

        Create test world with floor or something and add collision layer

        Put your char above floor

        Press play

        Its dirty, the camera rotation is not correct, but you can find tutorials for camera. Besides, loot at assets in godot, there are addons - ready to use templates so to speak where peopl ealready made the character thing, you just add it to your scene..

          Frodev All I need for said system is just to make the player node (which is a CharacterBody3D) be able to move in 4 directions: forwards, backwards, left, and right.

          It's exactly the same as for a 2D character, just replace y axis movement with z axis.

          Frodev Hello there, recently I decided making 2D games was way to much work for me (especially with 2D animations) that I chose to swap to doing 3D because unlike 2D, I know what Im doing (art wise), I then realized I have never made a 3D game in godot before and for the life of me, cannot figure out how to make player movement, I've looked at over 12 tutorials now and I've gotten no where.

          So here I am!

          If I may approach this from a different angle...

          If this is a question of comfort(as in confidence in your skillset) and you mostly have experience with 3D when it comes to art and 2D when it comes to logic systems design... have you considered giving a game with prerendered 3D graphics a try?

          But I also think it's not a bad idea to expand your knowledge a bit with programming for 3D. As @xyz said, it's just one extra axis. Albeit there is a bit of a complexity increase with regards to things like gimbal lock. Though as an experienced 3D artist that shouldn't be an alien concept to you.

            Frodev I'd agree with you but I could not find any for the life of me that actually worked

            How about this, pick any character controller tutorial that looks decent, and when things "stop working" ask here for help with a specific thing.

              kuligs2

              kuligs2 Are you serious? What do you mean not work? Im devving in godot in 3d, and how come i found tutorials that "WORKS"?

              I think its cause I'm probably just bad and broke it looking back on it, but the tutorials didnt really explain it really well so I couldnt fix it.
              Ps: I've tried this tutorial and it ended up with me only being able to move left and right, and that the gravity thing didnt work at all, but I'll try it again

              xyz
              Yeah you're right I probably should've done that instead, sorry!

              Megalomaniak
              I never thought of this before! That is actually a pretty cool idea, sometime I'll have to try it out! Thanks!

              trizZzle
              I didnt realize they made the documents with all of these wonderful tutorials! Ill probably end up trying this cause well its in the docs, as long as it isnt outdated it should work (and sense the code is right there I shouldnt manage to break it), not to mention I love reading the docs!

              Thanks!

                Frodev I did that tutorial too. It helped me a lot to understand the engine. 🙂

                trizZzle
                Ok the tutorials really nice! I got the movement for the most part ready to go! One problem I'm facing is the camera, I want to make in first person so I've followed some tutorials and again I seem to be breaking it (or maybe they are out dated) right now I do have some code, it doesnt seem to work and for the life of me I dont know why (cause I dont know how it would work but here goes!)

                func _unhandled_input(event):
                	if event is InputEventMouseButton : # lock camera and what not
                		Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
                	elif event.is_action_pressed('ui_cancel') :
                		Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
                	
                	if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and not GameValues.inMenu : # make sure we are in first person and not in a menu
                		if event is InputEventMouseMotion : # if the event is mouse motion, rotate!
                			neck.rotate_y(-event.relative.x * 0.01)
                			camera.rotate_x(-event.relative.y * 0.01)
                			camera.rotation.x = clamp(camera.rotation.x,deg_to_rad(-30),deg_to_rad(60))

                now from doing some debugging I can say that whatever InputEventMouseMotion is never triggered, ever.
                Although any code before it is indeed triggering so something goes wrong there, as to how to fix it I'm early in learning this stuff and so far have not been able to figure it out. So your help would be nice, thanks!
                Thanks!

                • xyz replied to this.

                  Frodev Start with properly formatting your pasted code using ``` tags 😉
                  Also post your scene structure.

                  Print the value of gameValues.inMenu. If it's true, the motion event block won't be executed.

                  don't use an elif for the ui_cancel just a regular if.

                  func _unhandled_input(event):
                  	if event is InputEventMouseButton : # lock camera and what not
                  		Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
                  	if event.is_action_pressed('ui_cancel') :
                  		if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
                  			Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
                  
                  	if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
                  		print("mouse is captured")
                  		if not GameValues.inMenu : # make sure we are in first person and not in a menu
                  			print("passed through inMenu related check")
                  			if event is InputEventMouseMotion : # if the event is mouse motion, rotate!
                  				neck.rotate_y(-event.relative.x * 0.01)
                  				camera.rotate_x(-event.relative.y * 0.01)
                  				camera.rotation.x = clamp(camera.rotation.x,deg_to_rad(-30),deg_to_rad(60))

                  I'd probably also not put the checks for MOUSE_MODE_CAPTURED and GameValues.inMenu both onto the same line. Right now you have a hard time telling which of those checks causes you problems.

                  edit: Actually thinking about it I can see why you wanted to use elif for the ui_cancel but in that case maybe its worth keeping track of menu state and use a match based on that, while also being able to use the same state variable check also for rest of the relevant code...

                    Megalomaniak
                    Ok so I tried the code you suggested (PS: thanks for the pointers) but I have some problems, I realized that this only updates anytime I press a button and not the mouse actually moving, probably because its locked in the center of the screen and doesn't move, therefore the event is never mouse motion, so I was going to plug it into a process function but I realized that the when turning the camera:

                    Megalomaniak neck.rotate_y(-event.relative.x * 0.01)
                    camera.rotate_x(-event.relative.y * 0.01)

                    I use the event variable that was defined inside of func _unhandled_input(event): of course in a process function I cant get this (well to my knowledge) so what can I do instead? Cause I have no idea what I can use instead, probably because I for the most part dont even know what it does!

                    So yup
                    If you know how to fix it and would lend me your knowledge that would be great! If you need anything else about my project like info or something, I'm more than happy to provide.

                    Thanks again!

                      Frodev I realized that this only updates anytime I press a button and not the mouse actually moving

                      The _unhandled_input(event): is just one of the loops for doing input stuff. You should maybe try the normal _input(event): loop too. Some things will work better in one than the other.

                      Frodev I use the event variable that was defined inside of func _unhandled_input(event): of course in a process function I cant get this (well to my knowledge) so what can I do instead?

                      Pretty sure you can call on the Input and InputEvent classes from process loops, so you might be able to make that work for you. For an example, read the velocity property from the InputEventMouseMotion class.

                        Megalomaniak
                        Hi there quick question, I just realized that the direction you move in is not based on the camera direction.
                        I've tried a couple of things, I was gonna try vector3.Forward and just use the camera position until I realize that from my knowledge, it isnt possible (or well I cant figure it out it probably is possible but what would I know).

                        So here I am asking for help!
                        How I have it now the camera is rotated only on the X and Z access where as the Y is rotated in another node, this node being the parent of the camera but the child of the player:

                        Anyhow this is how I am currently moving the player:

                        func _physics_process(delta):
                        	# movement ----------------------------------------------------------------- 
                        	var direction = Vector3.ZERO
                        	
                        	if Input.is_action_pressed('WalkRight') :
                        		direction.x += 1
                        	if Input.is_action_pressed('WalkLeft') :
                        		direction.x -= 1
                        	if Input.is_action_pressed('WalkBackwards') :
                        		direction.z += 1
                        	if Input.is_action_pressed('WalkForward') :
                        		direction.z -= 1
                        	
                        	if is_on_floor() and Input.is_action_just_pressed("Jump") :
                        		targetVelocity.y = jumpImpulse
                        	
                        	if direction != Vector3.ZERO :
                        		direction = direction.normalized()
                        		$Pivot.look_at(position + direction, Vector3.UP)
                        	
                        	
                        	
                        	targetVelocity.x = (direction.x * speed) * GameValues.gameSpeed
                        	targetVelocity.z = (direction.z * speed) * GameValues.gameSpeed
                        	
                        	if not is_on_floor() :
                        		targetVelocity.y = (targetVelocity.y - (fallAcceleration * delta)) * GameValues.gameSpeed
                        	
                        	velocity = targetVelocity
                        	move_and_slide()

                        As we can see I just add to either the X or the Z or whatever, but obviously that isnt going to work but I currently dont know a way to do otherwise, if you do know a way please tell me, I've already spent 30+ minutes researching this topic but nothing so far.

                        Thanks so much!