The basics of creating a 2D character in Godot. Part 2: compiling templates, a little about GDScript, movement and animation of the hero
In the previous article we looked at the basics of creating a new project in Godot . And with this superficial knowledge, one can only look at the demo versions of games. In the second part of the agenda we have: 1) Export the finished project into binary files for the selected architecture. 2) New animations. Character options. 3) Management. 3) GDScript. Welcome to real coding! 4) Import the simplest Tilesets. 5) Bonus: analysis of the device of the simplest backs.
Well, as usual, a lot of pictures!
Compilation....?
')
In the last article I mentioned that the authors did not provide binaries for linux x86. At the time of writing, the binary files were still not ready. But I already wanted to export finished projects, demo or just tests. But what if there are no export templates? Right! Compile them yourself!
Go to the source directory and compile
cd ./godot
Debugging Tools:
scons bin/godot target=release_debug tools=no
After successful compilation, rename the newly received file to linux_x11_32_debug
Export template itself:
scons bin/godot_rel target=release tools=no
Rename to linux_x11_32_release
We pack in a zip-archive.
find ./* -name "linux_x11_32_*" -exec zip ./linux_x11_32_templates.zip "{}" +
Feed the Godot archive. Voila, now you can export a project for GNU / Linux OS of any digit capacity.
Yes, even under the OS and bit, different from yours. Binary files for Windows without problems could get in wine, the main thing is not to forget to remove or put a tick 64bits, and turn off debugging, if not needed.
More information about export settings can be read on the project website in English. If it is relevant and in demand, it will be possible to think about the Russian localization of articles and the creation of a Russian-language wiki on Godot.
Animation running and jumping
In the last lesson, I prepared a texture in advance with the sprites of running and jumping our Captain. So I think to create two animations for jumping and run is not difficult. I think it will not be difficult to do. Let me just say the parameters:
1) I remind you. Check whether the Camera2D / Current parameter is checked, which is responsible for linking the camera object to the player object.
2) Add Node CollisionShape2D. This "Noda" is responsible for the position of our Captain in 2D-space. In the Inspector choose CollisionShape2D -> Shape -> New RayShape2D. In 2 millimeters a small arrow will appear to the right - click on it - the RayShape2D parameters will open. The RayShape2D / Lenght parameter will leave 20, but Custom Solver Bias will be set to 0.5 (this will allow a small offset) Now in the game itself in the future, if the character “does not hit the feet on the floor,” his position can be adjusted with this arrow. She's the one who checks "whether the sprite touches the floor."
3) With geometry more fun. First you need to add Node CollisionPolygon2D. Now we need to draw this same polygon, giving the character a “mass.” That is, it is necessary to draw what the hero will “beat against the walls” when confronting them. Otherwise, it will simply pass through the walls. Choose a pencil. Now left-click "set" 3 vertices of the triangle. Two on each of the shoulders, and one in the abdomen. And right-click (just click on the workspace) draw a triangle. It is desirable that he eventually turned out isosceles or equilateral. With the bottom vertex on the Y axis. Now we will increase it. Little. Otherwise, the character will pass through the ceiling for example.
Done, our character - the dummy is ready. Now the easiest - management.
Creating control keys
Standard management.
Creating this is pretty easy. Go to Scene -> Project Settings -> Input Map Create 3 control keys - move_left, move_right and jump. Assign them keys. Done! As you can see, Godot can work with gamepads, though for everyone it will be necessary to set up their own controls. But more about that some other time.
Office with touchscreen. Here, too, especially nothing complicated. We are tied to our hero Node CanvasLayer with the name "ui". And to her in turn - 3 copies of the TouchScreenButton. We call them left, right and jump. To each bind the image of the key. They will immediately appear in the working area of ​​the program. We arrange as it seems to us more convenient. Do not forget that the blue window - this is the "display area of ​​the camera." In the Action sections we enter the parameters move_left, move_right and jump, respectively. Well, set the parameter Visibility Mode in TouchScreen Only. Done!
GDScript
Here I am a bit in a stupor. The fact is that of me the programmer is honestly not very. I myself do not understand much. And I read books on Python to better understand the issue. Because GDScript is very similar to it.
Attention!
The engine does not understand Cyrillic. But if you accidentally write something on it, it simply does not display it. And just marks the entire line with it, as one big mistake.
Indentation is very important. Not there is an indent - and the hero can fall into the floor, the wrong functions can be performed.
I repeat once again, I am a noob in programming, but I will try to explain everything as simply as possible.
A small request - if you know how to arrange better - please write. I will be glad to take into account all the comments.
Let me remind you of the previous code for displaying the simplest animation:
What happened
extends RigidBody2D var anim="" func _integrate_forces(s): var new_anim=anim new_anim=«idle» if (new_anim!=anim): anim=new_anim get_node(«anim»).play(anim)
Add and modify code:
Declare variables
extends RigidBody2D var anim=""# var siding_left=false # var jumping=false # var stopping_jump=false # var WALK_ACCEL = 800.0 # var WALK_DEACCEL= 800.0 # var WALK_MAX_VELOCITY= 800.0 # var GRAVITY = 900.0 # var AIR_ACCEL = 200.0 # var AIR_DEACCEL= 200.0 # ( ) . , Mario Bros' var JUMP_VELOCITY=460 # var STOP_JUMP_FORCE=900.0 # var MAX_FLOOR_AIRBORNE_TIME = 0.15 # , . - , . , "" . var airborne_time=1e20 # var floor_h_velocity=0.0
We start the function integrate_forces. Here is what Help says: Modify the function to use your interaction physics. Well, that's what we'll do. PS I do not know how to comment on the code further. So just leave it like this:
We write interaction function
func _integrate_forces(s): var lv = s.get_linear_velocity() var step = s.get_step() var new_anim=anim var new_siding_left=siding_left
Get control
var move_left = Input.is_action_pressed("move_left") var move_right = Input.is_action_pressed("move_right") var jump = Input.is_action_pressed("jump") # x () lv.x-=floor_h_velocity floor_h_velocity=0.0
Land search (check the contact of the texture with the floor)
var found_floor=false var floor_index=-1for x in range(s.get_contact_count()): var ci = s.get_contact_local_normal(x) if (ci.dot(Vector2(0,-1))>0.6): found_floor=true floor_index=x if (found_floor): airborne_time=0.0else: airborne_time+=step #, var on_floor = airborne_time < MAX_FLOOR_AIRBORNE_TIME
Jump process
if (jumping): if (lv.y>0): # ( ) jumping=false elif (not jump): stopping_jump=true if (stopping_jump): lv.y+=STOP_JUMP_FORCE*step
Movement of the character on the ground
if (on_floor): if (move_left andnot move_right): if (lv.x > -WALK_MAX_VELOCITY): lv.x-=WALK_ACCEL*step elif (move_right andnot move_left): if (lv.x < WALK_MAX_VELOCITY): lv.x+=WALK_ACCEL*step else: var xv = abs(lv.x) xv-=WALK_DEACCEL*step if (xv<0): xv=0 lv.x=sign(lv.x)*xv # if (not jumping and jump): lv.y=-JUMP_VELOCITY jumping=true stopping_jump=false # if (lv.x < 0 and move_left): new_siding_left=true elif (lv.x > 0 and move_right): new_siding_left=false if (jumping): new_anim="jumping" elif (abs(lv.x)<0.1): new_anim="idle" else: new_anim="run"
Movement of the character in the air
else: if (move_left andnot move_right): if (lv.x > -WALK_MAX_VELOCITY): lv.x-=AIR_ACCEL*step elif (move_right andnot move_left): if (lv.x < WALK_MAX_VELOCITY): lv.x+=AIR_ACCEL*step else: var xv = abs(lv.x) xv-=AIR_DEACCEL*step if (xv<0): xv=0 lv.x=sign(lv.x)*xv if (lv.y<0): new_anim="jumping"else: new_anim="falling"
Moving a character
if (new_siding_left!=siding_left): if (new_siding_left): get_node("sprite").set_scale( Vector2(-1,1) ) else: get_node("sprite").set_scale( Vector2(1,1) ) siding_left=new_siding_left
Change animation
if (new_anim!=anim): anim=new_anim get_node("anim").play(anim)
Applying ground speed
if (found_floor): floor_h_velocity=s.get_contact_collider_velocity_at_pos(floor_index).x lv.x+=floor_h_velocity
extends RigidBody2D var anim="" var siding_left=false var jumping=false var stopping_jump=false var WALK_ACCEL = 300.0 var WALK_DEACCEL= 300.0 var WALK_MAX_VELOCITY= 400.0 var GRAVITY = 900.0 var AIR_ACCEL = 300.0 var AIR_DEACCEL= 300.0 var JUMP_VELOCITY=460 var STOP_JUMP_FORCE=200.0 var MAX_FLOOR_AIRBORNE_TIME = 0.15 var airborne_time=1e20 var floor_h_velocity=0.0 func _integrate_forces(s): var lv = s.get_linear_velocity() var step = s.get_step() var new_anim=anim var new_siding_left=siding_left var move_left = Input.is_action_pressed("move_left") var move_right = Input.is_action_pressed("move_right") var jump = Input.is_action_pressed("jump") lv.x-=floor_h_velocity floor_h_velocity=0.0 var found_floor=false var floor_index=-1for x in range(s.get_contact_count()): var ci = s.get_contact_local_normal(x) if (ci.dot(Vector2(0,-1))>0.6): found_floor=true floor_index=x if (found_floor): airborne_time=0.0else: airborne_time+=step var on_floor = airborne_time < MAX_FLOOR_AIRBORNE_TIME if (jumping): if (lv.y>0): jumping=false elif (not jump): stopping_jump=true if (stopping_jump): lv.y+=STOP_JUMP_FORCE*step if (on_floor): if (move_left andnot move_right): if (lv.x > -WALK_MAX_VELOCITY): lv.x-=WALK_ACCEL*step elif (move_right andnot move_left): if (lv.x < WALK_MAX_VELOCITY): lv.x+=WALK_ACCEL*step else: var xv = abs(lv.x) xv-=WALK_DEACCEL*step if (xv<0): xv=0 lv.x=sign(lv.x)*xv if (not jumping and jump): lv.y=-JUMP_VELOCITY jumping=true stopping_jump=false if (lv.x < 0and move_left): new_siding_left=true elif (lv.x > 0and move_right): new_siding_left=false if (jumping): new_anim="jumping"elif (abs(lv.x)<0.1): new_anim="idle"else: new_anim="run"else: if (move_left andnot move_right): if (lv.x > -WALK_MAX_VELOCITY): lv.x-=AIR_ACCEL*step elif (move_right andnot move_left): if (lv.x < WALK_MAX_VELOCITY): lv.x+=AIR_ACCEL*step else: var xv = abs(lv.x) xv-=AIR_DEACCEL*step if (xv<0): xv=0 lv.x=sign(lv.x)*xv if (lv.y<0): new_anim="jumping"else: new_anim="falling"if (new_siding_left!=siding_left): if (new_siding_left): get_node("sprite").set_scale( Vector2(-1,1) ) else: get_node("sprite").set_scale( Vector2(1,1) ) siding_left=new_siding_left if (new_anim!=anim): anim=new_anim get_node("anim").play(anim) if (found_floor): floor_h_velocity=s.get_contact_collider_velocity_at_pos(floor_index).x lv.x+=floor_h_velocity lv+=s.get_total_gravity()*step s.set_linear_velocity(lv)
PS In order for gravity to work, you need to tick Scene -> Project Settings -> Physics2D -> Default Gravity
Import TileSets
I don’t have time to tell in detail about the tileset. I can only offer my finished scene (from my project) or you can create your own from TileSet. You can download the set from the link below, and then add to the Node TileMap project. In the settings you need to add a ready TileSet to it (TileSet -> Load -> tileset.xml). The simplest floor / earth can draw. How to draw levels, TileSets I will explain in detail in the next lesson.
Bonus! Adding Backdrops!
Create a new scene. Add “Node” Parallax Background to it. We call it parallax_bg and save it as parallax_bg.xml. To her create ParallaxLayer 4 pieces. We call them sky, clouds, mount_1, mount_2. Accordingly, the sky, clouds, and mountains. The resolution of our game is 800x600 (Look in the settings Scene -> Project Settings -> Display). Therefore, we set the Mirroring 800.0 parameter to each "Node" - mirroring along the Y axis. Change the Scale parameters of the "Nodes" clouds, mount_1, mount_2 to 0.1.1; 0.2.1 and 0.4.1 respectively.
Add the sky itself to the Node sky. I have already drawn my own in advance. And you can download all the links below. On the working area immediately appeared 2 strips. We transfer to the working area of ​​the image, we scale. We see that changing the main sprite is changing and its mirror image.
Add clouds to Node clouds. I think it will be possible to duplicate each of the 3 so that in the end it was 6, the mirror duplicates again, so we will have as many as 12 clouds!
Now add mount to Node mount_1 and mount_2. But we will raise the mount_2 layer above mount_1.
Save the scene. Open stage.xml. And to our Stage we add through the "plus" scene with backs.
And as usual a small video gameplay:
Small FAQ (Will be updated):
If the character does not move - check whether the control keys have added and added correctly.
Problems with animation - whether the animation was added correctly. Check out the looping animations for idle, run and falling.
The character falls through the floor / passes through walls, ceilings - increase the triangle CollisionPolygon2D.
Character partially in textures / above them - work with the arrow CollisionShape2D