Design of Player.tscn - leiget/Godot_FPC_Base GitHub Wiki
The main player scene file is βPlayer.tscnβ. It is simply linked inside any scene you want to use it in. The βPlayer.gdβ script is linked to it inside the player scene file, but you could link another script to it inside the referencing scene, if you wanted to.
Player Scene Overview
Hereβs the overview of the playerβs scene file:
- Player (KinematicBody)
* The main part (obviously) of the scene. Is linked against the βPlayer.gdβ script.
- FPC_Base_Mesh (MeshInstance)
- The mesh.
- Uses βFPC_Base_Mesh.daeβ.
- Shape_Capsule (CollisionShape)
- The collision shape of the player.
- It is a capsule primitive shape.
- Iβve tried making a convex collision shape based on a cylinder, but it didnβt work very well. The character would keep going through walls.
- Hereβs a quote from the main programmer of Godot about this when someone asked for Cone/Pyramid/Cylinder primitive types for collision/physics bounds:
- βPyramid is not a problem, but the others are not common at all on physics engines, given the SAT algorithm can't be used. They only exist in Bullet, which uses a different collision algorithm called GJK+EPA. I don't like this algorithm much because for games it's a little imprecise and requires users to set-up margins manually to all convex shapes. As such, this can't be done. Even PhysX does not support cylinders.β - Juan Linietsky, January 2016.
- So, basically, the algorithm used in the physics engine (Iβm guessing Bullet, too, as this was written in January of 2016 and things may have changed since) best supports kinematic bodies with primitive shapes, as they fit better with the way the physics engine works. Itβs usually best to use whatever the engine gives you, in general.
- Camera_Main (Camera)
- The 3D camera that is the eyes of the player.
- Camera2D (Camera2D)
- The 2D camera used for the HUD and debug information.
- Crosshair (TextureRect)
- The cross hair texture node.
- Crosshair_Useable (TextureRect)
- The red circle that shows up when the player is looking at something he can use.
- DEBUG (Control)
- The parent node for all debug information.
- Gets shown/hidden when the user presses F3.
- Debug_Label (Label)
- The text that can be used for debug information, like showing the playerβs velocity or direction.
- Can be edited in the player script using βDebug_Label_String = str(Whatever)β.
- Can be set in the player script using βDebug_Label.set_text(Whatever)β.
- This already exist at the end of the file, so you really donβt need to set it twice.
- Instruction_Label
- Shows the player all the actions and what they do.
- FPC_Base_Mesh (MeshInstance)
Design of Player.tscn
The design is simple.
The βPlayerβ kinematic body serves as the base node. This will do all the movement and physics. The visible 3D part of the player scene is just βFPC_Base_Meshβ. Note that in this nodeβs settings, this mesh is only visible on layer 20. In the βCamera_Mainβ node, the cull mask allows every layer but layer 20 to be shown, therefore culling out the player mesh. This is done so the player mesh doesnβt get in the way of the camera and look weird.
The βShape_Capsuleβ node is a capsule primitive shape. This is because if a player had a box collision shape and where to turn while in a confined space, the collision shape would get stuck inside the bodies that make up the confined space.
Also, if the character were standing in a crevice with two angled walls and no floor, rotation will cause him to move up and down as the corners of the box push against the walls.
The βCamera_Mainβ node is simply the main node through which the player views the world. Itβs local position in this scene is interpolated when moving up steps.
The βCamera2Dβ node is the main view of whatever 2D elements are on the screen, which in this case are the cross hair and debug information.
Scripts Inside Player.tscn
There are 4 scripts attached to node inside the player scene.
- Player.gd
- Crosshair.gd
- DEBUG.gd
- Debug_Label.gd
Player.gd
βPlayer.gdβ is further detailed in "Section 2: Player.gd Script", but the basic idea is this:
- There are a bunch of variables at the top.
- It has 4 custom functions for use within the script.
- It has the β_ready()β function.
- It has the β_physics_process()β function, of which the overview is:
- Check for input.
- Set states according to input.
- Check if the player is looking at something usable and react accordingly.
- Calculate horizontal movement.
- Calculate vertical movement according to if the player is:
- On a floor.
- Jumping.
- Falling
- On a slope.
- Apply movement using βmove_and_slide()β.
- Get states once again.
- Execute βStep_Player_Up()β function.
- Check for camera interpolation and react accordingly.
- Execute βTouch_CheckAndExecute()β function.
- Set the debug label text.
Crosshair.gd
βCrosshair.gdβ has a β_ready()β function and a βViewportSizeChanged()β function. In the β_ready()β function it calls βViewportSizeChanged()β and then connects it to the main viewport nodeβs βsized_changedβ function, so that whenever the size of the game window changes, so it can update the size and position of the crosshair and the red circle that denotes when a usable object is pointed at.
βViewportSizeChanged()β changes the size of the crosshair and its child according to the size of the game window. It first set size ratio to be applied to the cross hair.
βY_Size_Ratioβ (inside the script) is β25.0/1080.0β. This means on a 1080p screen, I want the size of the crosshair to be 25 pixels wide. So lets say that the viewport size is not 1080p but instead is 1024x768. How will this work? Well, βY_Size_Ratioβ equals β25.0/1080.0β, which ultimately equals β0.023148148β. So, we take that number and multiply the X-axis size of the viewport to get our crosshair size number: βCrosshair_Size = int(0.023148148 * 1024) = 23β. This way, we can have the size we want and not have to worry about the window size.
Now we have a problem. In order to have a perfectly centered crosshair it needs to have a even numbered size, and 23 is odd. So what do we do?
First, we must check to see if βCrosshair_Sizeβ is odd in the first place. Remember that many times you need to check a variable or condition before you modify. If you werenβt to check things you may cause an error or modify something that doesnβt need to be modified.
We use the modulo operator for this, which looks like this β%β. What this does is simply divide the first number against the second, and then returns the remainder. So in our code we have this:
if(Crosshair_Size % 2 != 0):
What this does is this: It takes βCrosshair_Sizeβ, which is β23β in this case, and divides it by two. Then, it returns the remainder, which is β1β. What this is basically telling us is that it isnβt even, or else the remainder would be β0β because dividing an even number by 2 doesnβt produce any remainders.
Since weβve found out that our proposed crosshair size isnβt even, we make it even by simply adding β1β to it. Easy!
After this, what we now do is simply set the size and position of both the crosshair and itβs red circle child. We set the size by simply calling:
set_size(Vector2(Crosshair_Size, Crosshair_Size))
For each node. Then, we set the position of these nodes to half their size, like this:
set_position( Vector2( (Viewport_Size.x/2 - Crosshair_Size/2) , (Viewport_Size.y/2 - Crosshair_Size/2) ) )
The reason we set the position to the center of the node instead of to the upper right corner is because in Godot I set the anchor of the βTextureRectβ to the center of whatever picture I use. Check the Godot manual for information on βControlβ node anchors. In this case, I simply selected the crosshair node and clicked on the βLayoutβ menu inside main viewport, went down to βAnchors onlyβ and selected βCenterβ. You can also manually set the anchor of the node manually in the node inspector. Itβs simply called βAnchorβ and itβs in the range of β0.0 β 1.0β.
We do this to the red circle node, as well.
DEBUG.gd
This script simply toggles the visibility of the debug nodes on or off. Itβs pretty straight forward. All it does is set the β_unhandled_process()β to true, and whenever the βPlayer_ToggleDebugβ action is activated, it checks to see if the debugging information is visible or not, and it changes it to whatever it currently is not.
One note is that I use βInput.is_action_just_pressed()β instead of βInput.is_action_pressed()β because the first function only activates when you initially press the button/key, and the second function keeps activating for as long as the button or key is held. This way the debug information is toggled on or off 60 times a second when you just press it once.
Debug_Label.gd
This basically does the same thing as βCrosshair.gdβ, but just a different size and position.
First, in the β_ready()β function, it runs βViewportSizeChanged()β, and then it connects that to βsize_changed()β inside the main viewport of the game, the one that is the senior-most node, so that it is activated whenever the game window changes size, either by going fullscreen or by dragging the sides of the window.
In βViewportSizeChanged()β, it sets the font size and the rectangle size of the label.
First it gets the size that I want the font to be relative to the screen:
Font_Size_Rel = 50.0/1080.0
So that makes βFont_Size_Relβ equal β0.046296296β, or ~4.6% of the horizontal screen size. Then, we set the relative size of the labelβs rectangle to six times the width of the relative size and 3 times the height of the previous variable. That will end up being about β(0.27, 0.14)β. So what that means is that the labels total rectangle size will be about 27% of the screen horizontally and 14% of the screen vertically. That will be the area that the text inside the label will be shown in.
After that we make a variable which will hold the size of the viewport. We use that to set the size of the font itself and then set the size of the label rectangle according to the variable we made above, which looks like this:
get_font("font").set("size", Viewport_Size.y*Font_Size_Rel)
rect_size = Vector2( Viewport_Size.x * Font_RectSize_Rel.x , Viewport_Size.y * Font_RectSize_Rel.y )