Just a little break from me for a while.
Indie Devs: Best Practices
Hello, again, indies!
Really quick, I wanted to talk about how to rotate the player. Maybe you don't have a character that needs this, but mine does unless I want him to look this way while he's going left or right:
I don't want that. How can we fix this issue?
Let's look at the player's animated sprite, as that's what is in charge of player animation.
FlipH and FlipV? What does that do? Let's try one.
Okay, all that really did was flip his hair the other way. What else do we have?
We have the values under Transform... and in that list, we have Position, Rotation Degrees, and Scale...
Rotation Degrees? What's that?
Let's move its slider and find out.
Nice!
Now, can we use the rotation degrees in code?
If you actually barely type rotation_degrees in the code, you will see it pop up.
Go ahead, and play with it!
Now, this works fine if we use, say 0, 90, 180, and -90 when we go left, right, up, and down... How do we set the character to look sideways for diagonals?
Let's look back at the code.
Input.is_action_pressed allows them to move in any of the four main directions... Is there a way to use that knowledge to make the player look at an angle while moving in a diagonal?
That's my challenge for you this time. I will see you in the next one.
As I was working on my game, I couldn't help but notice the gray background. I mean, it looks as "Blah" as my old project name. :p
How do we fix this?
If you go to Project -> Project Settings, there is a tab called Rendering. Almost at the very bottom of that, there is a button titled Environment. The first thing that pops up is Default Clear Color.
I have changed the color to black:
Finally, you can now get rid of that boring, gray color! =)
I know I said that my last few videos are going to finish up this game, but very quickly, there is something else I should show you.
I just saw a question on the forums, asking how to change the text in the editor. How do you change the editor, and customize it? In case you haven't noticed, mine is not blue and white.
So, how do you do that?
Under Editor -> Editor Settings, there is a tab called Interface. Under that, there is a tab called Theme. If you click on that, you will see Settings for Base Color, and Accent Color. You can change those around to suit your needs/wants.
Now, for the crazy part.
If you want to change the text, go farther down. There is a Text Editor button. If you click on Highlighting, you will find a massive selection of different things you can change.
So go ahead, and customize your game engine to your heart's content.
- Edited
Remember when I said order of operation matters? Here is another example of that.
In case somebody hasn't figured out how to shorten the script on the animated sprite, it's surprisingly easy.
in func _process(delta): in the player, we have this code:
So what?
So, what the code is doing when you use one of those inputs, it changes the value of velocity.
For this, you might want to add comments to remind you which direction you need to go in the script.. You could get lost very easily. :)
Now, we could do something like this for each diagonal:
if Input.is_action_pressed("ui_down") and Input.is_action_pressed("ui_left"):
rotation_degrees = -45
However, who wants to write all that out four more times?
Let's look back at the player code again. From this code, we can see that velocity.x is equal to 1 when we go right, and -1 when we go left. Additionally, velocity is set to 1 when going down, and -1 when going up.
This seems a bit backwards, doesn't it? This means that the positive numbers do not go right and up as in a grid in math, but right and down.
However, we can still use that information.
However, you have to be a little careful here. if you check if velocity.x or velocity.y is 1, it will never happen.
Why?
Because you will only get the velocity after it has been normalized().
Instead, let's us a new Vector2() in the player script called dir to represent direction.
Next, to check if we are moving in a direction, we can pass code like this into the animated sprite:
And it works!
This will also work for diagonals!
Now here's my next challenge- set the player to move in a certain direction based on diagonals.
Now, before you get all irritated and say things like, "I don't know what numbers I need to use for my diagonals, and I told you I hate math, and blah, blah, blah--"
Yeah, before all that... :)
I have already done your math homework for you!
The numbers you need are -135, -45, 45, and 135.
I will see you in the next one.
Okay, now for some real stuff that involves the game.
Now, this part is totally optional, you can skip this entire challenge if you want, but I'm going to go over the Timer node.
Now, as you can see here, I have already set up a very basic UI system, and I actually already attached it to the player, here is how it is set up, it is nothing fancy.
So, basically, a I have it doing right now is displaying "Time Left: 60". However, I need it to subtract 1 from the time left after each real second.
How do we do that?
With Timeout!
Connect the signal from the timer called Timeout to the Control script, and inside the script add this line of code to it.
Now let's press play...
...And the words are huge, and hidden by the CanvasModulate!
Don't worry.
To fix the problem with the big CM, we go into the player and select the Control node. From there, we go into the label, and then the inspector.
Visibility... What does that do?
It doesn't look like it would be helpful. All the colors are set to white, and the visible toggle is enabled.
Huh.
What about... Material?
It has two things on it, what do they mean? What is CanvasItemMaterial vs... Shader?
A shader is something we have to code... Let's see if CanvasItemMaterial spares us of that. Let's create a new Material, and see what it is.
The only thing here that says anything about light is the Light Mode. Let's see...
It has Normal, Unshaded, and Only Light settings.
We have no light, so let's try unshaded.
Okay, so now, I will resize my letters and put them in the top-left corner of the screen, and then see if the timer is doing its thing, after I toggle the Autoplay button on.
Okay, so, if you want your game to be timed, go ahead, and make your own timer, and set it up however you like. I will see you in the next one.
Hello, indie devs! Who's ready to do some more coding!
I know this is getting long, so I will try to make the last few sections quick. :)
I have re-vamped my game with new graphics and a better player. Additionally, I swapped out the label with a progress bar which will serve as the oxygen level, as the game now has you exploring a space craft that was on a mission which went horribly wrong:
However, how do we make progress bars?
First off, this one was from Itch.io and was free, but use whatever health bar you want. I set it up exactly as I did with the label for the canvas shader so we can see it.
I have also changed the code on the Control node which is in charge of the countdown:
However, if one set this up exactly as I did, like so:
The oxygen sprite, which is the blue one in the middle, is not lining up with the scale- everything is shrinking from the center.
This is because, if you go into the inspector for the sprite, under Offset, we see a toggle called centered. Turn that off. You will have to re-position the sprite, but now it should work just fine.
So, my challenge for you is to add a progress bar- it could be an XP system, a health system, whatever, and play with it to see how you can make it do what you want.
Wait, XP system? Why?
This is because we are also going to implement taps and enemies. The enemies will have a basic AI system using a very simple state machine (a special thanks to @fire7side for this concept). State machines are not that complex, unless you need them to be. However, we have to set up a few more things before that. So, I will see you in the next one.
Alright, who enjoys adding the exact same music to every single level you make?
... Crickets?
Okay, so how do we fix that?
If you go to Project -> Project Settings, you will notice a tab called Autoload. Click on that, and then click on the little file tab, and select your music. Then click on add. It should look something like this when you're done:
Now you can sefely remove/not add the music/ambience into the levels.
- Edited
Hello once again, indie devs!
Now that we have our games almost done, and we want to release them into the world, there is a huge feature that we may need in order to say they are completed- and that is the ability to save and load things.
Now, in my game, I currently only have one variable that gets saved which is the level number. It started as a variable that did not change, and would make you always start at level 1. However, what if you want to load an old game without starting over?
So, what I did was I removed the level = 1 line of code, and inside the Player, I added a Node2D called SaveData, and all it has is a script.
It's pretty much straightforward. Now, if I ever need to go to the door at the end of the level to go to the next one, I set up the door script like this:
And, as the player is the parent of the SaveData, I connected the signal to the player and added these lines of code under the signal-created function:
Now, whenever I start the game, it will automatically start on the level which I left off.
Edit: so I will admit it, I have just gotten into some issues with saving and loading, but fear not- the community here has helped me with the issue.
https://godotforums.org/d/30199-resolving-save-and-load/37
Hey, we all make mistakes, right? However, I'm glad I came across this bug so I can tell you about it.
However, the method I've been shown by the community here works very well- thank you, Godot forums! You guys and gals are incredibly helpful!
Now, after you examine the way to save multiple things at once- and we'll cover the for loop that we saw in this later on- what if you want to, say, save your current data for a scene- like, say you started with ten bullets in the first level, but you want to keep those ten bullets for each time you restart that level?
Instead of saving the bullets just in SaveData, you can save the bullets as an integer in the player script. If the scene you load is not your main menu, have your player bullets set to the amount of bullets in SaveData:
Now we change the script for shooting:
Almost done. Now, suppose we go to a new scene and want to save the amount of bullets from that one?
It's as simple as changing your scene loading script to something like this:
And, by the way, I know this method does work.
Now, why did I add the part where I made a mistake in? I feel there is another thing you can take away from this.
The forums here are incredibly helpful. Do not hesitate to ask for help. So long as you don't spam the forums, the members here are willing to help you out. I don't know where you're coming from, but in my experience, building games is never about getting the first project done. I mean, it is, but there is a greater purpose here- and that is to learn how to code- finishing projects will come after this. If you are stuck on one tutorial- even this one- feel free to admit it. Others may direct you to other tuts that you can follow.
Also, if you get overwhelmed by all the variables, and signals, and whatever, it does not mean you are a bad programmer. It only means you are in need of help, or perhaps just need a break. I've been there. All that you shouldn't do in this state is let it make you quit. Programming is not easy, but it is rewarding and gives you a skill that not everyone has.
So, hopefully, I didn't just bore you to death with me rambling. I will see you in the next one.
Hello, indie devs! Who's ready to code?
Actually, we're not coding in this section, but it's coming.
I just got done with my main menu for Maze Escape, and I wanted to show you how I did some of this in case you ever need to use these techniques. Yeah, it has some blood on it- but give me a break, this is a zombie game.
First off, how do you make the buttons have a different appearance?
For that, you use the node called a texture button.
If you grab one of them, you will see its settings in the inspector. The main properties to change its looks can be found here:
I also added a Label over the buttons so I could type in what I want on them.
The other thing is, I actually saved my font to reuse it, but the font on the buttons are a different size than the one used at the top of the screen.
To give you the ability to change features on the font being used on one thing and not the other, inside the label, go to your font, and click on the little drop-down arrow beside it, and you should see something called Make Unique:
After selecting that, you can change the features on that font however you want, and even save it as its own font so you can reuse the modified version as you see fit.
And, as I mentioned zombies, very soon, we will be dealing with enemies in the game. We have to set up a few things on the character first, but eventually, I will show you how to make and spawn basic enemies.
Hello, indie devs! Now, let's get into the coding that I promised earlier.
Today, we are covering the state machine for the enemies, I'm currently using a kinematic body as I want the player to be able to collide with it, but I don't need the enemy to respond to physics like a rigidbody- and the enemy has to move, so it can't be a static body.
Here is the code I have for the enemy so far- but all it does right now is it changes the number that will trigger different states. (Thank you, @fire7side for this)
Now, first off, what in the world is export var? Actually, the answer is visible from this shot. The variables with export var in front of them are visible in the inspector, which means you can change them from there. In a later section, I will show you that you can actually change these variables for each enemy.
As for this very small starting point of a state machine, how is that a state machine? All it does is spits out numbers. What good is that? Well, you can then use another function that checks the states- if state == 1, the character is idle, if it's state 2, the character is moving, state 3 is turning, and so on. However, there is an easier way to do this, and that is by using match.
Now we have a decent, easy-to-read, and very simple state machine!
Now what is the rest of the stuff on the code? new_dir, and all that? I'll talk more about this later, but long story short, if you want to randomize a variable, you have to initialize it inside of a function. The problem with this is that, since you are putting it in a function, other functions may not have access to it. To keep the needed variables available for use in other areas, simply set a variable that is outside of the functions to have the same value as the variable within the function that you need. This isn't always necessary, as you can pass a variable as a parameter, but it's easier this way if you need that variable in multiple functions.
So now, my challenge for you is to create your state machines, for your enemy AI, and check to make sure your code does in fact can randomly pick any of the states you need. I will see you in the next one.
Alright, let's make these enemies move. Note that I changed the velocities for the different directions and the max speed into their own variables for ease of use.
So, how do you make characters move in seemingly random directions at random times? Actually, it's pretty simple.
A new state machine checks which number was randomly generated for the direction, and chooses a direction based on the chosen number.
Now it's your turn.
Hi, indie devs!
Now, for the enemies, chances are, that you want to animate them, correct? You could use the animation player, but there is another way to do this. It's not a better method, per se, if you only have very basic animations- but for more advanced animations, you may want to use an animation player. I have added a light to the scene that will serve as our example here in the lower right corner:
So, if we add an animation player, we will see a panel at the bottom. Select the animation tab, and select new and then name the new animation. Now, in that same panel, under edit, we see something that looks like a little clock. You can use the number beside it t adjust how long you want this animation to be.
After that, go to the point in the animation in the timeline where you want something to happen. After that, adjust whatever you need animated to be how you want it animated, and then click on the key next to the property(s) that you changed. I want the light to flicker and change colors, so I will go to my light2D and change the settings, and then select the keys on the properties:
Now my animation player looks like this in its panel.
I am currently playing the animation, which is why the red line has moved. To see an animation play, select your animation from the Current Animation tab.
Also, if you want the animation to loop, there is an icon right next to the time setting, just click on that and it will loop.
Now, if you press play, we have one little issue- the animation doesn't play.
To fix this, add a script to the animation player, and simply type play("whatever_animation"). This script is also going to be responsible for your enemy animations as well, assuming its used for your enemies. Again, do it how you see fit.
Since I am already here babbling, I'll tell you a little about how to make something translucent or change color. I will use this victim for this:
Within a sprite, we go under visibility -> Self Modulate, and then change the settings however we want. This could be useful for if you want to show an enemy has damage. Then you might change the a (for alpha) setting:
Or you could turn the player's skin red when it takes damage by turning the visibility color to straight-up red. And, as we saw with the light, we can animate this in the animation player.
So, enjoy animating your enemies. I will see you in the next one.
Hello, indie devs!
The next thing on the list is having the enemy chase the player. Now, first, the enemy has to determine what the player is. However, the player is not the parent, or the child, of the enemy- so how can we have the enemy determine what the player is?
Assuming you have things set up where the parent is the world, and the children of that are the zombie and the player, you could call the parent by typing in "..".get_node("name_of_node). However, that's a bit sloppy. Instead, you could get the parent, and from there, get the target, as I did here.
Now, what is the rest of this stuff?
The function hostile needs delta to work, but it won't be able to access it on its own, so instead we give it a parameter, and then in _process, pass delta as that argument.
Now, this enemy will chase the player no matter what while set like this- if you don't have the states changed and only have hostile, it will chase the player forever. How do we fix it so that the player is only chased when it is in range, and how do we stop the change_state function?
For the trigger, we could add an area2D, and check if the body entering it is the player, and then switch over to the hostile function.
As for the change_state(), it goes off based on a timer, so you could simply add the name of the timer responsible for state changing, and after that type .stop().
Now, this enemy doesn't move around obstacles, but doing so would be beyond the scope of this tutorial. However, I encourage you to research ways to make the enemies travel in this manner- for a hint, search for information on path finding. For the purposes of this tut, all you're trying to learn is how to build very basic mechanics. Besides, zombies are usually pretty stupid- it wouldn't surprise me if they decide to slam into walls.
Now let's see if you can make your enemies chase the player. I will see you in the next one.
Hello, indie devs!
Has anyone noticed if one enemy detects the player, the entire gang of them from that level comes charging? Let's fix that next. This will also help export var become a lot more helpful.
An easy way to do this is to put somewhere above _ready(), type var enemy_name = self.name. Now, why do that? If you would then have a print function in _ready() for your enemies, you will notice that each enemy has its own name. This means that we can determine which enemy we need to do something. If zombie1 notices the player in its area, it will check if its own name is the one being called while the player is in that collision. This means, even though zombie2 gets the same message, since his name isn't zombie1, he will not go charging after the player.
Alright, we are almost ready to wrap up. We still need a pause menu and a system to interact with our small inventory system, and then we can go through the steps of beta testing and releasing. I will see you in the next one.
Hello, indie devs!
Now, before we get deeper into things and I lose this idea to show you another node.... well, you guessed it- I'm going to show you another node.
This time, it is the particles2D.
It's not much, but, say you are building a game that has a dripping faucet, or a leaky grate in a sewer, this could come in handy. This could also serve as another method to represent damage.
Let's grab a Particles2D, and in the inspector, under Process Material, select New ParticlesMaterial. Now, it will not be very big when you first see it.
From there, what you do with it is up to you. Just have some fun with it and see what all you can do with it. I will see you in the next one.
Hello, indie devs!
Now, before we go crush zombies, the player needs a weapon, right?
Fear not! I am going to show you how to make lasers.
I am using an area2D, as it isn't supposed to react to any physics, but it needs to detect collision. I added a collision shape and a collision shape to match it.
Now, the laser only has to do three things- move in one direction, damage an enemy if it hits one, and leave the screen once it hits something. Here is the basic code I have for that, minus the enemy attacks, that is coming.
You can adjust the speed of the laser as needed, or whatevr in the inspector. Now how do we shoot them?
Remember how the player move on button pressed ui up, ui down, and all that stuff? We can add a new Input for attacking, and assign it to the lasers. To make an input, go under Project -> Project Settings, and then Input Map.
From there, at the top of the panel, there is a text bar next to the word Action. You can type in a name for your action, and click on add to add it as a new input.
After that, locate the new input in the list, and select the little plus button to the right of the new input.
There are different settings for each type of input. I'm going to use the space bar, so I will click on key. It asks me to press the key that I want to use- space. After that, I click on OK. Do not press enter, or else you will switch your input to the enter key.
And, there you have it! Your very own custom input!
But how do we use it?
First, we need to make the laser its own scene, so right click on it and add the branch as new scene.
Next, in the Player Script, add this line of code in the get_input function:
However, we have one issue. When we use Attack after pressing play, the laser follows the player's movements. How do we fix that? If we now use remove_child, it does not work.
The issue is that the child is following its parent node, so we need to find a parent that is not going to move... like the root node.
Now, when you shoot the lasers, in your SaveData, you would want to subtract a "bullet" from your stash.
Your code would then look something like this.
Also, a nice little trick you can do with lasers is add a shadeless canvas material, and a light2D.
Have fun making your weapons, and I will see you in the next one.
Hello, Indie devs!
Has anyone been having issues with their lasers, you know, only shooting in one direction, and not keeping track of how many lasers you actually have?
Let's fix that.
The easiest thing to fix is the weird countdown. Instead of using Input.is_action_pressed(), we can use Input.is_action_just_pressed(). This will keep the action subject to only firing once, whereas the previous method just keeps spamming the action.
Okay, next, we'll tackle the directions.
For directions, we can add pre_dir to the player, and set it equal to Vector2(0, 1), or whichever way your player is facing. Mine is initially facing down.
Now, we change the get_input() to this:
Then, we adjust our laser function:
This will allow you to zap enemies in all directions, but it still doesn't change the direction of the sprite.
The sprite, however, is easy. if the laser's laser_dir.y is not equal to 1, the sprite's rotation_degrees is equal to 90.
That's my challenge for now. I will see you in the next one.
Hello, once again, indie devs!
Have you notice, lately, how the player is being crowded? My zombie will not leave the player along yet refuses to fight him. Chances are, your orcs, zombies, robots, and whatever, are not letting your player sleep, either. The player knows it has a weapon, but it is useless at this point.
Let's change that.
Going back to the enemy, the enemy has an area2D, correct? We'll use that to have it detect damage.
First, let's put the laser's collision shape in a group- I'll call it laser. Then we go to the enemy, and get the signal on_area_entered(area).
From there, we add this:
And we can see at the bottom that it worked!
Now, things are not so fair for the enemies, are they?
Well, they will have to wait until next time!
Hello, indie devs!
So, how do we make the enemy attack the player- ya know, to get a little revenge for them being helplessly attacked first?
The method I used was to have an animation player connected to the zombie, and I then attached another area2D as well. The animation player has an Attack animation, and an idle animation. (This was unintentional, as it caused some problems later on.) After that, I set the area2D to be called Attack, and, in the animation player, I disabled/enabled the Attack's collision shape.
For the test, I left the speed at 1, but I will change that later.
For the zombie script, I added the signal on Area Entered, and set it up like this:
And, as you can see, it works!
Now, why did I multiply the damage by 0.01? Well, if you remember, we had a progress bar in an earlier section, which had a scale of 1. The health bar, most likely, is also set up this way.
Now, I made one little mistake here at first, and I'm addressing this as this is so easy to do. I accidentally made the idle animation "idle", instead of "Idle". It still works as an animation, but if you call the animation "Idle", it will not work as it is case sensitive.
Now, we have one more problem- the attack only attacks once per hitting the player. To fix this, make sure your attack animation is being looped.
Now the player and enemy can fight each other!
So, what's next?
The pause menu.
I will see you in the next one.