Big Space Rocks Tutorial
In part 1 we created our player scene. If you press F6 or click the play scene button you should see a windows appear and our player ship in the top left corner (mostly off the screen). This is because nodes default to being centered at the upper left corner. We want to leave it that way but create a new scene to use as our main scene to place our player on.
So close the window. From the "Scene" menu select "New Scene" or click on the "+" next to the player scene tab to create a new scene. Click on "2D Scene" for the root node and then rename it to "Main". Save the scene (CTRL-S) and it will name it "Main.tscn".
Now in the FileSystem pane (bottom left) click and drag our "Player.tscn" onto the "Main" node. This will add our player scene as a child node of Main.
The Scene pane should now look like this
What we've done is instanced our Player scene into the Main scene. We could drag as many player scenes instances onto main as we want. The main definition of the player scene is still in the Player scene itself. If we go back to that scene and make changes, they will be reflected in all the instances that we create. We only want one instance here though. So now let's move the instance of the player that's on the Main scene to the center of the Main scene view. Click the player in the upper left corner and drag it to the center.
If we now run it (F6 or "Run Scene" icon) you should see the player in the center of the window.
- Note: When running scenes make sure the current scene to be run is currently selected (the scene tab is selected and scene shows in the Scene pabe in upper left of UI).
Now we want to be able to make our player move. Let's go back to the Player scene (click on the "Player" tab if it is open or double click the "Player.tscn" in the FileSystem pane)
With the "Player" node (root node of Player scene) selected, click the little "Add Script" icon.
Change the "Template" to "Empty" and click the "Create" button. This will attach a script named "Playuer.gd" to the Player scene.
A little script icon should now appear next to the Player node and the script editor should open with our new script.
We will add the following code which will be explained in detail.
extends Area2D export (float) var rotation_speed = 3.0 export (float) var max_speed = 20.0 export (float) var acceleration = 5.0 export (float) var friction = 0.5 var screen_size = Vector2.ZERO var velocity = Vector2.ZERO func _ready(): screen_size = get_viewport_rect().size func _physics_process(delta): var rotation_dir = 0 if Input.is_action_pressed("turn_left"): rotation_dir -= 1 if Input.is_action_pressed("turn_right"): rotation_dir += 1 rotation += rotation_dir * rotation_speed * delta if Input.is_action_pressed("thrust"): $AnimatedSprite.frame = 1 if velocity.length() < max_speed: velocity += Vector2(0, -acceleration).rotated(rotation) * delta else: $AnimatedSprite.frame = 0 if velocity.length() > 0.2: velocity -= velocity * friction * delta else: velocity = Vector2.ZERO position += velocity
We first define some variables for our player. Using the "export (float)" keywords we allow the variables to be edited inside the inspector pane in the UI. Using the "export" keyword is optional and just lets us play with the values in the UI versus changing the values in the code. The "rotation_speed" will determine how fast the ship turns when using our "turn_left" and "turn_right" actions. The "max_speed" will keep our player from continually accelerating forever. The "friction" variable is just to add some slowing so that wehn our player is not "thrusting" it will eventually slow down to a stop instead of going on forever at current speed. Now technically in space our player should just keep going without slowing but I thought it would make for a better playing experience and also good for learning how it is done. The "screen_size" will grab the current width and height of the game window and "velocity" will store the player's current velocity.
The "_ready" function is run when the scene is loaded and displayed for the first time. This is a safe place to grab the window size as we know the screen is displayed at this point.
Everything else concerning the motion of our player is handled in the "_physics_process" function. This function is called by the main loop and typically called every frame. So if the game is running at 30 frames per second we can expect that roughly it will call this function around 30 times per second. The "delta" variable that is passed in is the time (in seconds) since it was last called. Since we can't guarantee that it will actually be called every frame we can use this "delta" time do make our player movement more consistent even if the machine is sometime slower or faster (due to processing things).
The first thing we do is determine if the player should rotate (because we're pressing the left or right arrow keys). We set the "rotation_dir" to either positive or negative 1 to determine direction or zero if not turning. We then set the "rotation" property based on the direction, speed, and delta. The "rotation" property belongs to the Area2D node (which is the type of node we used to create our Player). It's actually part of the Node2D node but since Area2D inherits from Node2D it gets all of Node2D's properties.
We then check to see if the player is thrusting (pressing up arrow key). Here's where we take advantage of using the AnimatedSprite node with the two frames/images that we added earlier. Wehn we're pressing the up arrow we change the current frame of the AnimatedSprite to 1, which is the second sprite image. When we're not holding down the up arrow we set the frame back to 0 for the normal player image.
If the user is pressing the up arrow to thrust we want to move the player. First we check that the player is not already at max speed or higher and if so we just ignore the thrust command. Otherwise we modify the player's velocity. We want to accelerate the player rather then just set to a fixed speed. We use a Vector2 object for this since it represents both direction and speed. To increase the velocity by the acceleration we set the vector to the acceleration value and add it to the current velocity. The reason we're setting the second parameter of the Vector2 object is because that parameter is for the y axis. Recall our player/spaceship sprite is facing upwards. And in Godot 2D the y axis starts at the top of the window and increases downwards. The position x, y = 0, 0 is in the upper left corner of the game window. To move to the right we increase the value of x and to go down we increase the value of y. This is also why we are adding the negative acceleration. We want to increase velocity going straight up or north so we need to subtract from the current y position.
If we had imported a player sprite where it was facing to the right like below then we would use Vector2(acceleration, 0).
Because we can rotate our player we also need to account for that by also including the rotation. So the Vector will always assume the player is in the default orientation of facing up so any velocity changes we apply to the y axis and then we apply the rotation. And we multiply the whole thing by delta to account for how fast or slow it might be calling our function.
if velocity.length() < max_speed: velocity += Vector2(0, -acceleration).rotated(rotation) * delta
In the else condition (the user is not pressing the up arrow) we want to slow the spaceship slowly over time by applying a friction. We first check if the overall velocity is greater than 0.2 using the Vector2's length property. If the velocity is too close to zero we don't want to apply friction and possibly make it go backwards. If it's below the 0.2 threshhold we just set the velocity to zero. Otherwise we just take the player's current velocity and subtract the friction amount and multiply by delta. It's really just negative acceleration in the opposite direction the player is currently moving.
if velocity.length() > 0.2: velocity -= velocity * friction * delta else: velocity = Vector2.ZERO
Save the script (CTRL-S) and press F5 or click on the "play" icon in the upper right. The first time we do this it will ask us to choose a main scene so it knows which scene to play when we click play. We could also just switch to the main scene and click the "Play Scene" icon but let's get our main scene setup now. Click on "Select" and choose the "Main.tscn" and click "Open".
A game window should come up with our player in the center. Try rotating the player and thrusting.
In the next part we'll work on making the background of our main scene more interesting and also handling our player moving off the screen.