Updated tutorial_kinematic_char (markdown)

reduz 2014-04-20 18:19:16 -07:00
parent 6399e4196d
commit 7e5a0d0d37

@ -25,7 +25,6 @@ func _fixed_process(delta):
func _ready():
set_fixed_process(true)
```
### Scene Setup
@ -43,6 +42,125 @@ Finally, instance that character scene in the tilemap, and make the map scene th
<p align="center"><img src="images/kbinstance.png"></p>
### Creating the Kinematic Character
### Moving the Kinematic Character
Go back to the character scene, and open the script, the magic begins now!
Go back to the character scene, and open the script, the magic begins now! Kinematic body will do nothing by default, but it has a really useful function called [move](class_kinematicbody2d#move)(motion_vector:Vector2). This function takes a [Vector2](class_vector2) as an argument, and tries to apply that motion to the kinematic body. If a collision happens, it stops right at the moment of the collision.
So, let's move our sprite downwards until it hits the floor:
```python
extends KinematicBody2D
func _fixed_process(delta):
move( Vector2(0,1) ) #move down 1 pixel per physics frame
func _ready():
set_fixed_process(true)
```
The result is that the character will move, but stop right when hitting the floor. Pretty cool, huh?
The next step will be adding gravity to the mix, this way it behaves a little more like an actual game character:
```python
extends KinematicBody2D
const GRAVITY = 200.0
var velocity = Vector2()
func _fixed_process(delta):
velocity.y += delta * GRAVITY
var motion = velocity * delta
move( motion )
func _ready():
set_fixed_process(true)
```
Now the character falls smoothly. Let's make it walk to the sides, left and right when touching the directional keys. Remember that the values being used (for speed at least) is pixels/second.
This adds simple walking support by pressing left and right:
```python
extends KinematicBody2D
const GRAVITY = 200.0
const WALK_SPEED = 200
var velocity = Vector2()
func _fixed_process(delta):
velocity.y += delta * GRAVITY
if (Input.is_action_pressed("ui_left")):
velocity.x = -WALK_SPEED
elif (Input.is_action_pressed("ui_right")):
velocity.x = WALK_SPEED
else:
velocity.x = 0
var motion = velocity * delta
move( motion )
func _ready():
set_fixed_process(true)
```
And give it a try.
And.. it doesn't work very well. If you go to the left against a wall, it gets stuck unless you release the arrow key. Once it is on the floor, it also gets stuck and it won't walk. What is going on??
The answer is, what it seems like it should be simple, it isn't that simple in reality. If the motion can't be completed, the character will stop moving. It's as simple as that. This diagram should illustrate better what is going on:
<p align="center"><img src="images/motion_diagram.png"></p>
Basically, the desired motion vector will never complete because it hits the floor and the wall to early in the motion trajectory and that makes it stop there. Remember that even though the character is on the floor, the gravity is always turning the motion vector downwards.
The solution? This situation is solved by "sliding" by the collision normal. KinematicBody2D provides two useful functions:
* [KinematicBody2D.is_colliding](class_kinematicbody2d#is_colliding)()
* [KinematicBody2D.get_collision_normal](class_kinematicbody2d#get_collision_normal)()
So what we want to do is this:
<p align="center"><img src="images/motion_reflection.png"></p>
When colliding, the function move() returns the "remainder" of the motion vector. That means, if the motion vector is 40 pixels, but collision happened at 10 pixels, the same vector but 30 pixels long is returned.
The correct way to solve the motion is, then, to slide by the normal this way:
```python
func _fixed_process(delta):
velocity.y += delta * GRAVITY
if (Input.is_action_pressed("ui_left")):
velocity.x = - WALK_SPEED
elif (Input.is_action_pressed("ui_right")):
velocity.x = WALK_SPEED
else:
velocity.x = 0
var motion = velocity * delta
motion = move( motion )
if (is_colliding()):
var n = get_collision_normal()
motion = n.slide( motion )
velocity = n.slide( velocity )
move( motion )
func _ready():
set_fixed_process(true)
```
Note that not only the motion has been modified but also the velocity. This makes sense as it helps keep
the new direction too.
The normal can also be used to detect that the character is on floor, by checking the angle. If the normal points up (or at least, within a certain threshold), the character can be determined to be there.