-
Notifications
You must be signed in to change notification settings - Fork 8
Design of Player.tscn
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.
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)
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.
There are 4 scripts attached to node inside the player scene.
- Player.gd
- Crosshair.gd
- DEBUG.gd
- Debug_Label.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” 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.
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.
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 )