Let's add in an attack animation! I also introduce a simple state machine and talk about input maps (Add WASD controls). Here is the Github repo for this project (under MIT license): github.com/uheartbeast/youtube-tutorials/tree/master/Action%20RPG This video was made possible by my wonderful Kickstarter backers. If you are interested in taking a deeper dive into the Godot game engine you can buy my 1-bit Godot Course at this link: www.heartgamedev.com/1-bit-godot-course-youtube
@@uheartbeast Thanks, i notice what did you do when i review your video, it makes sense this solution because of the loop animation, but thanks for reply
edit: FINALLY I SOLVED THE PROBLEM. I just need to re-type the Attack parameters, and it WORKED! finally Hey @HeartBeast , I Have A Problem In RPG Action And I’m Stuck in it Too, The Problem Is Where I Fight In the Game. And When I Try To Fight In Down Direction It Fights In Up Direction, Same as Left & Right Direction. If You Know How To Solve it, Please msg Me. and Here's The Code extends KinematicBody2D const ACCELERATION = 500 const MAX_SPEED = 80 const FRICTION = 500 enum { MOVE, ROLL, ATTACK } var state = MOVE var velocity = Vector2.ZERO onready var animationPlayer = $AnimationPlayer onready var animationTree = $AnimationTree onready var animationState = animationTree.get("parameters/playback") func _ready(): animationTree.active = true func _physics_process(delta): match state: MOVE: move_state(delta)
If you put something like: func attacking(delta): animationState.travel("Attack") velocity = velocity.move_toward(Vector2.ZERO, FRICTION/2 * delta) velocity = move_and_slide(velocity) In the attack function, it will continue to slide a bit while you attack :D
While it was not a part of this series, he did do a tutorial on an inventory system. Part 1: ua-cam.com/video/rdUgf6r7w2Q/v-deo.html Part 2: ua-cam.com/video/l28ui3KfeS0/v-deo.html
These tutorials are so simple, and Godot is so easy to use that whenever I make a mistake, I'm easily able to figure out what it is and how to fix it. Thanks in part to the debug console which details what the problem might be in simple terminology, even giving suggestions instead of saying "ayo the game brokey"
Another advantage of match I guess is, that you make shure, that every case excludes the other one. while if else statements could overlap in boolean values. I really love every tutorial session. Hope you all are save.
I haven't even watched the video yet but the possibility for timed hits makes me very excited as I've always wanted to make a game where blocking enemy attacks is the primary combat mechanic, which is similar to timed hits. Very very cool
Guys remember: WHAT YOU CALL STUFF MATTERS In the Animation tree thing, when I created the Blend Space 2D I called it "Attacking," but in the code I just copied the video and wrote it as animationTree.set("parameters/Attack/blend_position", input_vector) when it should have been animationTree.set("parameters/Attacking/blend_position", input_vector). Also, for the input map, I named the attack key (space/J) "Attacking," but again I just copied him and wrote the code as if Input.is_action_just_pressed("attack"): when it should have been if Input.is_action_just_pressed("Attacking"): So A) make sure you follow him exactly, or B) if you deviate slightly make sure to account for it in the code! I know this is probably super obvious to most people but if there are any beginners like me out there I figured I would let you know just in case lol
i just spent some time searching for an error that was exactly caused by distraction in my Animation Tree name. I wish I read your comment earlier lmao
I have a bug in (if Input.is_just_pressed("attack"):) it might be that very problem. Thanks. Not at my computer right now but I will give it a try when I get back to it!
instead of making the attack_animation_finished function you can attach a signal from the animationTree> beside the Inspector click on the Node tab > choose the signal animation finished and connect it to player from there you can make the signal function look like this: func _on_animation_tree_animation_finished(Attack): state = MOVE Make sure to change names according to what you have and make sure to make all the attack animations not on loop from the AnimationPlayer
I got to this episode and stopped for three days while I remade all the sprites and assets myself because I wanted it to have a personal touch and I'm ready to continue. I don't know whether I should say it here or further in but I might as well just say it, I've learned so much in such a short amount of time that I'm just like... Damn. You're a good teacher.
Bruh... 2 days of searching for adequate state management delivered in an understandable format for the code structure I am using, and bupkiss. THEN, I find your videos... you are a freaking legend my friend and I cant say thank you enough! This really assisted in rounding out my Dodge and Attack animations and implementing them with the movement correctly. Thank you again and will be regularly visiting!
I personally found the player character stopping in it's tracks when attacking to be a bit annoying, so I added these lines to attack_state to make the player slide a bit when they attack: velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta) velocity = move_and_slide(velocity) You'll also need to pass in delta for it to work properly.
For those of you who use Godot 4 and have this weird flickering between the Idle-State and the Attack-State and also get freezes like me: Make sure to turn AutoMode off! You do this byclicking on the tansitions between Idle and Attack and go to the right tab -> Advance -> Mode -> Enable (don't disable this). If you play the game it should work like in the video :) Actually people in the comments mentioned this for Godot 4 in the run video but I forgot that when watching this video xD
Even though I have been closely following Godot since 2014 and I know most of the stuff you show I still watch this series because it's well executed and you show a lot of "hidden" features. Great job. :)
for the transition from attack to move states and vice-versa, you can also connect the animation_finished signal to the player script. (this means once an animation has finished, a signal will sent and that function will be called.) Then, you can add a condition based on what state the player was when that animation played (in this case it will be ATTACK), so then you can set the player's state to MOVE. This avoids having to add a call_method() track to every attack animation
Yeah, very easy and quick way of doing it, just make sure its the animation_finished signal from the animation tree and not the animation player and its super simple. It automatically sets up the method for you and all ya gotta do is add state=MOVE and you are done.
@@blakealanfoster How did you make this work? I have setup the same way you describe it but when I press attack I get stuck in the current Run or Idle animation instead.
@@dard1515 I believe you have to turn off the looping in each animation (AttackUp, AttackDown, etc...). I am currently trying to figure out how I can make this work while still moving, though, since doing it this way stops your character movement until the animation is finished.
instead of making animations on the left and right, you can just make an animation on one side and add a line of code "flip_h = true" and "flip_h = false"
though, it only is useful, if the sprite looks the same- e.g. there is a scar or an eyepatch on the left/right side, you then need another image for it
in func attack_state(delta) I repeated the lines "velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta) " then "velocity = move_and_slide(velocity)" to make the player slide to a halt instead of awkwardly stopping.
On the technical side of if vs switch I just wanted to add that switch cases are faster than if statements since they're implemented with a combination of binary trees and jump tables. Basically with a long if/else list, if you're in the last state, you will have to run all the IFs until you reach the last one, while the switch/case will "jump around", if you run it several times each frame (multiple state machines, check the state in _process, _physics_process and _draw, etc.) it does make a difference at the end. Great video again HeartBeast, I find it funny that you mentioned using nodes for each state as that's exactly what I did for my pushdown automata. Still, I'm gonna follow what you did for the animation tree.
I love the good morning, afternoon or evening, where ever you and when ever you are my name is Benjamin! It's so satisfying to hear after finishing one of these tutorials and starting another.
dear god this is an amazing tutorial, far better than most of those out there that I did watch but never got a full grasp over the topics at hand, I even managed to add other functions just to test the waters, 11/10 Edit: Also, you dont really need to add much collusion either, remember the move_and_slide method? well he showed exactly how to draw block areas with it if you want to add areas with hills you can walk on, or even draw buildings on top of them and adding a collusion map in needed areas if im not mistaken
Hi HeartBeast, Even though I don't usually watch these tutorials completely, it helps so much when trying to solve some problems in my game. I am currently making my own rpg with my own assets and gameplay, and so far it has helped tremendously (especially some of the key concepts such as match statements). Thank you so much for these videos.
Also: something I did slightly different, I didn't really like the idea of having momentum in the physics and then just forgetting about it when the attack is launched, so in my attack code I included instead the velocity = velocity.move_toward(Vector2.ZERO, friction * delta) velocity = move_and_slide(velocity) Since it takes no inputs and keeps the velocity moving back towards Vector2.ZERO its still smooth and you can't change any movement until its done animating but you will still slide a bit if you initiate an attack while running. It seems to be working well and I like it.
Yo, I tried out a little bit and in the attack state function I did not set the velocity to zero, I copied "velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)" from the move state func and added "velocity = move_and_slide(velocity)" into the attack state func. This makes the Player stop not so abrupt and it looks a bit smoother. Hope I do not run into problems with that in future videos. I am currently binge-watching this tutorial series and I am learning so much! I started a month ago with Godot and this tutorial is the best I found yet by far. Good work, dude :)
This tutorial series is fantastic so far, thank you kindly for taking the time to put these videos together. I would also like to mention that in addition to passing delta to your state functions, you could also use 'get_physics_process_delta_time()' to retrieve the delta as well.
Just one note regarding `attack_animation_finished()`: You basically recreated a signal that's already present in the AnimationPlayer. There's one firing whenever animations are done, so I'd just reuse that instead. This saves one from setting up all those callbacks, especially if there are more to come later.
Unfortunately, AnimationPlayer signals are not emitted when animation is played from AnimationTree in Godot 3.2, see github.com/godotengine/godot/issues/28311. However, there is already PR for that github.com/godotengine/godot/pull/30508/files, but it is still open, what's more it is as of now set as 4.0 milestone, so dunno when will it be implemented.
You don't even have to do that. What you could do is just put in auto-advance so that it transitions back to the moving state at the end of the animation.
@@compartirlavida That is what auto-advance is for in the animation state machine node. Another thing you could do is to just check if an animation is playing with the get_node function for the playback parameter
@@Mriganka2S When i do that, the node isn't changed immediately, there's a short delay between switching to the "Attack" node, so basically checking if get_current_node() == "Idle" on the next frame just resets the state back immediately. Having an animation_completed signal or something would make it much easier than having to add an intermediary variable to test if the state has changed or not.
Hope there will be a Video about "changing scenes"... so you could go back to them with the player in the correct position. When I enter "Scene B" from the Top of "Scene A" and I'm going back to "Scene A" I want, that the player is in the same position as he was before. Is there an easy way to do this? (btw. sorry if there are any grammatical issues.. I am not a native speaker.)
I've had struggles with that. The way I've found was to use a Singleton. You see, when you change a scene, the later one doesn't exist anymore, but that never happens with singletons, they just keeps everything. Basically I've created a variable in there, which is a Vector2 with the next player position (actually, it's an export var, they are great 'cause you can change at will and they will work accordingly with the scene, not the script). When the player goes into a door, for example, the game will change the var to what you want before changing the scene, then on the _ready function you make the Player go there. Kinda hard of explain, but really, really easy, I swear. Probably he'll explain better. There is other ways too, of course. Oh, in my game I've made a way where the Player keeps his direction too.
Started this series following you and programming in C#. You make it very clear how everything clicks and is so easy too follow. Thank you a lot for this!
Awesome content! BTW, I noticed that you forgot to add the +0.1 to the animation tree y axis for attack so that it favors attacking right or left over up or down. Also, I find that when I add the velocity calculations for friction to the attack_state that the player costs to a stop more naturally while attacking. This makes it feel just a bit more fluid IMO.
Awesome tip! Without that, I found that the player would stop suddenly, then keep sliding a bit AFTER the attack like the momentum was still there. I love the way the player now kind of slides to a stop while attacking, it looks cool! =)
One minor issue I noticed is that if you hold in a different direction while/immediately after the attack animation is playing, the attack animation of that new direction will play briefly (approx 2 frames) before/after transitioning to the move state.
I was able to fix it for me (and hopefully for you): In the AnimationTree editor, I had two node connectors between "Idle" and "Attack". I removed the "Attack" -> "Idle" connector, which had a "Switch Mode" of "At End".
I couldn't wait until today so i set up the attack animation on my own, the process was easy enough. I didnt' think about adding the state machine though so I had an if statement checking for my attack key but your setup was much better, either way great video. Keep up the good work!
Same for me. Actually added roll as well but I need to see how the position will be updated. I can only think of the function call strategy as of now. EDIT: Ok finished Roll without function call and it looks pretty good. Maybe there is a better way but we will see.
@@ss2cire am i thinking right about this? just add all attack animations and just bind the animation on a certain key and it will do it when pressed? :D
@@marktriestolive1781 sure but you still need states to make sure you can go from attack to roll ... unless that's the intent... for a smple tame you'd probablly want mke sure all modes shkft from idle. but it's up to you! that's the nice rhing about the setup
@@ss2cire yes, yes ofc. still a newb on the engine and i will apply what i just learned. now just waiting for dmg part and enemy ai to be added. oso, camera thing :D
Great vids again :) There's a bunch of things that triggered me as a programmer though. In order of importance in my opinion. The first one being VERY important. 1) NEVER ADD A PARAMETER TO A METHOD IF YOU DON'T USE IT. (the delta in the attack_state method) It is really a bad practice to declare parameters in a method signature if you don't use it, even if you plan to use it. In fact, I'm quite sure the warning you have right now in your debugger is actually saying so. This is a great way of adding confusing, useless complexity to your code. Basically, if you need it, put it. If you don't, don't put it. 2) DON'T MIX LOGIC IN YOUR METHODS TO ACCOMPLISH SOMETHING YOU WANT (adding the animationTree.set("parameters/Attack/blend_position", input_vector) in the move_state) Your reasoning behind it was very good, but the way you implemented it is somewhat hackish. It doesn't make sense that your move_state would do attack logic and it is also a great way to make your code very confusing as you scale up. You're saying you don't want the player to change direction when he is attacking and you don't have access to input_vector outside move_state. Good! Then find a way to access it. A really easy and intuitive manner to do this, and this is what I've done, is to declare a variable outside the move_state method that actually keep track of what you want --> --at top of the Player script var player_orientation_vector = Vector2.ZERO --inside the if statement that checks if you're pressing an input if input_vector != Vector2.ZERO: player_orientation_vector = input_vector animationTree.set("parameters/Idle/blend_position", player_orientation_vector) ... --then you can write your attack_state method as follow func attack_state(): animationTree.set("parameters/Attack/blend_position", player_orientation_vector) animationState.travel("Attack") 3) METHODS SHOULD ONLY DO WHAT IT SAYS IT DOES (move_state should not handle change of states.) Ok so this might be arguable since the attack button is pressed when in the MOVE state but in my opinion, it should only be aware of what it does when in MOVE state and not handle the switch between states. I'd put the check on the Attack button outside of it. (check code below in number 4) 4) This one is probably cause I'm too perfectionist, but I hate it when built_in methods starts to do complex logic. Like, _physics_process handling player states. Doing something like this, will make your code more readable and easier to scale --> func _physics_process(_delta): _handle_player_state() func _handle_player_state(): match state: MOVE: move_state() if Input.is_action_just_pressed("attack"): state = ATTACK ATTACK: attack_state() Hope it helps some people improve their code. Take care! :)
I'm semi following along with his code, and am now fixing according to your notes, but I have a couple questions cuz I'm a noob at this. so can delta be taken out of the code completely, if it's taken out of action_state and move_state? also, unrelated but something I've been struggling weeks with; I want a function that allows my player to place a tile beneath them. I have a 16x16 auto tile ready, but don't know how to code that function in Godot. Would appreciate any assistance!!
here's the updated code. My action animation doesn't return to walk or idle, thus the button is pressed, the action animation triggers, and then you can't interact with the player. Help! enum { WALK, ACTION } var state = WALK var motion = Vector2.ZERO var player_orientation_vector = Vector2.ZERO onready var animationPlayer = $AnimationPlayer onready var animationTree = $AnimationTree onready var animationState = animationTree.get("parameters/playback") func _ready(): animationTree.active = true func _physics_process(_delta): _handle_player_state() func _handle_player_state(): match state: WALK: move_state() if Input.is_action_just_pressed("Action"): state = ACTION ACTION: action_state() func move_state(): var input_vector = Vector2.ZERO input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left") input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up") input_vector = input_vector.normalized() if input_vector != Vector2.ZERO: player_orientation_vector = input_vector animationTree.set("parameters/Idle/blend_position", input_vector) animationTree.set("parameters/Walk/blend_position", input_vector) animationState.travel("Walk") motion = motion.move_toward(input_vector * MAX_SPEED, ACCELERATION) else: animationState.travel("Idle") motion = motion.move_toward(Vector2.ZERO, FRICTION) motion = move_and_slide(motion) func action_state(): animationTree.set("parameters/Action/blend_position", player_orientation_vector) animationState.travel("Action") func action_animation_finished(): state = WALK
actually thanks for this comment, i was checking my code because it crashed and it was literally the delta in attack_state() so yeah great thank you edited yet again: i kinda get it but when i try to use func _handle_player_state(): match state: MOVE: move_state() if Input.is_action_just_pressed("attack"): state = ATTACK godot returns an error "Too few arguments for "move_state()" call and if i try to add (delta) as an argument it says the delta identifier hasn't been declared. so what's going on?
@@plankcaller sorry that was my mistake, you need it in the move_state method. You're method move state must declare it in its signature(method declaration).
Nice tutorial man, your tutorial series are awesome. Note: You could've been used the auto-advance condition to use as check if the state/animation was done. You only needed to set on _ready() call to set the initial condition value and only a if would be necessary to state back to idle for example.
Not sure if it is mentioned in a later video but I wanted to share some advice for y'all regardless: Please, if possible, do yourself a favour and use enums instead of if statements. Not only is it easier to type out, more importantly, it's more efficient. Try to think of it this way: With if statements, you go down a list of options and check each step if the statement is true (is he called Carl? no? is he called Daniel then? no? is he called Edward then? yes. ok!). With an enumerator, your basically jumping to the correct location in the list instead of looking up all previous ones (I'm looking for Edward, so I'm going to skip the names starting with C and D). It might not seem like much at first thought but it adds up over time and it's a good habit to avoid this from the get-go.
For Godot 4, I couldn't figure out how to make the attack animation finished function to work properly. So I spent three days working on a bunch of stuff, but I landed on how to make it work with Signals. You learn the basics of Signals in the next video, but here's what I did. First, you should know that the next video explains how to connect a signal to a node, that will then generate the function for you. For this code, I used the animation_finished signal, so it generated a function with an appropriate name for me, and has pass in it by default for me to replace with my code. Now the actual code. func _on_animation_tree_animation_finished(anim_name): if anim_name in ["AttackRight", "AttackLeft", "AttackUp", "Attack Down", "Roll Down"]: state = MOVESTATE The function that Beastheart made in this video, in combination with the Call Method track, recreates the functionality of connecting signals. But if it's not working then I hope this helps you.
Hi, I just discovered how to make it work in Godot 4. The problem is that on the method line, on the editor, it has a small green ball in front of it. When you click there, it will turn off and then you can move it on the timeline. You need to position this ball on the last time of the track (when you create it, it appears to create after the time the animation is finished, so it never gets called).
I don't know if it will break anything later, but I really didn't like adding the function call to the animation, so I developed a different approach. I changed the Attack animation to auto transition in to the Idle animation, added a change_state function which handles everything that needs to move to a new state, and changed the attack_state to detect when the animation is "Idle". Instead of directly setting the state, you call the change_state function with the new state. Here's the code and it works exactly as expected: if Input.is_action_just_pressed("attack"): change_state(ATTACK) func change_state(new_state): match new_state: ATTACK: animationState.travel("Attack") state = new_state func attack_state(delta: float): if (animationState.get_current_node() == "Idle"): change_state(MOVE)
Noticed a weird issue on my end where if I try to go Up+Left and press the Space key to play the attack it doesn't play, yet if I use another key (such as the 'Z' key) instead the animation plays correctly, and the Space key seems to work just fine for any other angle.
Hi, I just wanted to say that I REALLY REALLY love your Godot tutorial and the way you explain things are just useful since you point out errors as well. ^^ I also have a request: could you please make a tutorial on how to make a customizable player and apply the customized player to animation and various movements?
Would love to see a rpg tutorial for godot 4 using your components approach in the ship game but I’m using both video series bits and pieces and it seems to do the trick! Love your tutorial videos
Just wanted to say thanks, doing a couple videos in this series a day and out of the 3 other game tutorials for this engine I have followed, while they were great in their own right. This is the only one were I feel empowered to go back and comment my code so I ingrain the information more and able to have my own reference game to look back at if I ever get a RPG itch. You can just tell you care about people actually taking something away from these videos and not just trying to finish a cookie cutter game in under X hours.
For anyone that's getting a bunch of errors after assigning the "Call Method" track at the end of each attack animation, the fix is simple. Animation Tree -> Inspector -> Callback Mode -> Change "Method" from "Deferred" to "Immediate".
At 15:10 when you wanted the idle and attack transitions to be perfectly vertical it made me think of this- I looked but didn't see an option for it, maybe theres something like it I didn't see or it could potentially be a nice feature in future versions: In my video editor (DaVinci Resolve) when you're doing effects, theres a nodal workspace like the one in the animation tree. Theres an option to have all of the nodes snap to a grid, and theres another option called "orthagonal pipes", which makes all of the connecting lines between your nodes only appear as perfectly horizontal and vertical straight lines with 90 degree elbows where necessary. When these two options are turned on it can totally help keep your project organized looking and easy to follow when you start adding a lot of nodes.
When you add the attack_animation_finished() function to the call stack, make sure the green diamond lines up with the line between 0.3 and 0.4. When I added the function it was adding it just after that line and it was confusing me why it wasn't run.
If you want the attack animation to depend on your mouse position instead you can create a variable in the move state function like this[var mouse_position = get_local_mouse_position()] and change the var in animationTree.set to mouse_position instead of input_vector
also take out animation_tree.set("parameters/Attack/blend_position",mouse_position) and paste outside the if statement so it will work even when ur not moving
also for the bug where your character will continues to attack if u change direction u can fix it by changing the dotted line inside the animationtree node to the line
Awesome teaching skills you have here HeartBeast, keep going with your great tutorials. Do you have some way that we can tip or support you (patreon, etc..)?
Remember: when you're using Godot 4, velocity during the attack animation (the sliding bug he describes) is not an issue for you to resolve. Should work out of the box.
That's what I did, the sword strike in this video was wayyy too slow. put "0.01" in the snap textbox to make it easier and take each frame down by how fast you want the animation. I made mine 2x faster
Ok maybe this is just my version of godot or something but when i click in the spacebar and down arrow and left arrow at the same time it just doesn't register the input for some reason not only it doesnt change animation but it doesnt even stop the character and i even wrote this check: func _physics_process(delta): if(Input.is_action_just_pressed("attack")): print( "attack") ... and it prints when i click up + right + space, , up + left + space , down + right + space but just doesn't print anything when i press down+left+space EDIT: i added J to the attack inpu and IT works for every direction... maybe its just some weird quirk with my keyboard? does anyone have a similar problem or is my godot just weird? if anyone knows what is happening please help me
If anyone has a problem with the AnimationPlayer call method track and not being able to find your function in the popup window just restart Godot. Looks like there's a bit of a bug or something (I'm on version 3.4.2) where the "Insert Key" window isn't picking up changes to scripts. A restart fixed the issue though
If you add an idle sprite to the attack animation, you will not need to add the function attack_animation_finished. In all I'm impressed we can add methods to the line of animation to detonate scripts along with said animations. It was worth to see that alternative technique just to open that venue of creative possibilities with the animation track.
Now now I just have several questions. 1. How do u attack while moving. 2. How do u make combo. (Let's say we have 3 attack combo and then each attack is in different direction). Here the state mechanic won't help. Any idea?
Thanks Sifu. Btw It is better to watch twice before first time pause and follow the setup, which may counter the same error of author itself.. but is good to experience the error for boosting the confident on debug.
its sad how heartbeast isn't recgonized on youtube ,he is the best on godot programing so i think that he"s no that recgonized here I wish he had at least 1 million that is what he worth =)
The only additional issue here is the fact that (probably due to the lack of depth) the attack animation goes through bushes (actually cool) and cliffs (looks as if the sword is being swung above the cliff). But I guess for a beginning we need not be picky. :P
I ran into a weird issue adding the attack animations. Somehow the left attack "update mode" was set to "continuous" and not "discrete" which resulted in the animation skipping the last frame (I don't recall changing this, and it's a drop down so doesn't seem like a likely mis-click). Oddly, some of the run animations (which I hadn't touched since that part of the tutorial) were changed as well. I haven't been able to replicate the issue once I changed them all back to discrete, but it was a bit of a head-scratcher, just thought I'd share.
i usually use switch statements a ton in C++ and actually wondered why they weren't in godot , it turns out they were always here after all ; in form of that match statement syntax . Thanks for letting me know !
first of all THANK YOU, for showing me how to call methods at the end of animations. I'd been searching, for days, for a way to allow single animations to repeat with the animation tree. Do you know of any other ways of doing this? Improvements or alternatives to the workflow of doing repeatable animations.
All of my attack animations work except for the down one. When I try to hit the input key to attack while moving down my player just goes idle and I can't move. Does anyone have any idea why this is? I've looked at my AttackDown animation and it looks correct.
I can't figure out why i'm getting this error Invalid call. Nonexistent function 'move_toward' in base 'int'. on this line : velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATE * delta) I've went back to copy the videos again but it keep happening and no luck on google
Another way to change the state at the end of the animation would be using Signals (which would be a new topic and maybe that's why you didnt use it in this video), connecting the AnimationPlayer's animation_finished signal to the Player script, then checking if the state is equal to ATTACK, or using a "match" in there too.
wow input mapping is cool got my controller mapped too this is starting to look like a real game. Still confused on tile maps since its so different in version 4+
Btw switch-case in c++ can use the enum names ;) E.g enum class serialKillers { TedBundy, PedroLópez, JohnWayneGacy } and switch(serialKillers) { case serialKillers::TedBundy: break; }
What a shame that I have NEVER heard in my 2 year developement in godot of the AnimationTree, or just never used it. I think it makes so much stuff so much easier!
Let's add in an attack animation! I also introduce a simple state machine and talk about input maps (Add WASD controls).
Here is the Github repo for this project (under MIT license): github.com/uheartbeast/youtube-tutorials/tree/master/Action%20RPG
This video was made possible by my wonderful Kickstarter backers. If you are interested in taking a deeper dive into the Godot game engine you can buy my 1-bit Godot Course at this link: www.heartgamedev.com/1-bit-godot-course-youtube
@@MrRenanZX give it a try! There are lots of different ways to set things up. Use whichever you feel is best for you.
@@uheartbeast Thanks, i notice what did you do when i review your video, it makes sense this solution because of the loop animation, but thanks for reply
edit: FINALLY I SOLVED THE PROBLEM.
I just need to re-type the Attack parameters, and it WORKED!
finally
Hey @HeartBeast ,
I Have A Problem In RPG Action And I’m Stuck in it Too, The Problem Is Where I Fight In the Game.
And When I Try To Fight In Down Direction It Fights In Up Direction, Same as Left & Right Direction.
If You Know How To Solve it, Please msg Me.
and Here's The Code
extends KinematicBody2D
const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500
enum {
MOVE,
ROLL,
ATTACK
}
var state = MOVE
var velocity = Vector2.ZERO
onready var animationPlayer = $AnimationPlayer
onready var animationTree = $AnimationTree
onready var animationState = animationTree.get("parameters/playback")
func _ready():
animationTree.active = true
func _physics_process(delta):
match state:
MOVE:
move_state(delta)
ROLL:
pass
ATTACK:
attack_state(delta)
func move_state(delta):
var input_vector = Vector2.ZERO
input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
input_vector = input_vector.normalized()
if input_vector != Vector2.ZERO:
animationTree.set("parameters/idle/blend_position", input_vector)
animationTree.set("parameters/Run/blend_position", input_vector)
animationTree.set("parameters/ِِِِAttack/blend_position", input_vector)
animationState.travel("Run")
velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
else:
animationState.travel("idle")
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
velocity = move_and_slide(velocity)
if Input.is_action_just_pressed("attack"):
state = ATTACK
func attack_state(delta):
velocity = Vector2.ZERO
animationState.travel("Attack")
func attack_animation_finished():
state = MOVE
i have them working, but i notice they will not attack when moving diagonally?
pleeease help, my code is like the video, but something i did wrong makes the game absolutely not responsive, my character no longer moves or attacks
Who else agree that this is one of the best GODOT tutorials on UA-cam?
we all..
🙋♂️🙋♂️🙋♂️
Yep :>
Indeed.
So say we all.
If you put something like:
func attacking(delta):
animationState.travel("Attack")
velocity = velocity.move_toward(Vector2.ZERO, FRICTION/2 * delta)
velocity = move_and_slide(velocity)
In the attack function, it will continue to slide a bit while you attack :D
Great idea. I already had the friction in there, but it really needs that extra little slide by decreasing friction.
15:35
HeartBeast: "Remember that Up is down in here..."
Me: *Pirates of the Caribbean 3 flashbacks*
Will there be a tutorial about an inventory system? I have been loving these videos, thanks you!
While it was not a part of this series, he did do a tutorial on an inventory system.
Part 1: ua-cam.com/video/rdUgf6r7w2Q/v-deo.html
Part 2: ua-cam.com/video/l28ui3KfeS0/v-deo.html
These tutorials are so simple, and Godot is so easy to use that whenever I make a mistake, I'm easily able to figure out what it is and how to fix it. Thanks in part to the debug console which details what the problem might be in simple terminology, even giving suggestions instead of saying "ayo the game brokey"
Another advantage of match I guess is, that you make shure, that every case excludes the other one. while if else statements could overlap in boolean values.
I really love every tutorial session. Hope you all are save.
Call Method track looks really powerful. It just screams potential for fighting game mechanics or timed hits.
true
jesus
for real
I haven't even watched the video yet but the possibility for timed hits makes me very excited as I've always wanted to make a game where blocking enemy attacks is the primary combat mechanic, which is similar to timed hits. Very very cool
Guys remember: WHAT YOU CALL STUFF MATTERS
In the Animation tree thing, when I created the Blend Space 2D I called it "Attacking," but in the code I just copied the video and wrote it as
animationTree.set("parameters/Attack/blend_position", input_vector)
when it should have been
animationTree.set("parameters/Attacking/blend_position", input_vector).
Also, for the input map, I named the attack key (space/J) "Attacking," but again I just copied him and wrote the code as
if Input.is_action_just_pressed("attack"):
when it should have been
if Input.is_action_just_pressed("Attacking"):
So A) make sure you follow him exactly, or B) if you deviate slightly make sure to account for it in the code!
I know this is probably super obvious to most people but if there are any beginners like me out there I figured I would let you know just in case lol
Yeah, Thanks dude.
If only i read your comment before my panic attack.
i just spent some time searching for an error that was exactly caused by distraction in my Animation Tree name. I wish I read your comment earlier lmao
I have a bug in (if Input.is_just_pressed("attack"):) it might be that very problem. Thanks. Not at my computer right now but I will give it a try when I get back to it!
God-fkng-dmn-it
instead of making the attack_animation_finished function you can attach a signal from the animationTree> beside the Inspector click on the Node tab > choose the signal animation finished and connect it to player from there you can make the signal function look like this:
func _on_animation_tree_animation_finished(Attack):
state = MOVE
Make sure to change names according to what you have and make sure to make all the attack animations not on loop from the AnimationPlayer
I got to this episode and stopped for three days while I remade all the sprites and assets myself because I wanted it to have a personal touch and I'm ready to continue.
I don't know whether I should say it here or further in but I might as well just say it, I've learned so much in such a short amount of time that I'm just like... Damn. You're a good teacher.
Bruh... 2 days of searching for adequate state management delivered in an understandable format for the code structure I am using, and bupkiss. THEN, I find your videos... you are a freaking legend my friend and I cant say thank you enough! This really assisted in rounding out my Dodge and Attack animations and implementing them with the movement correctly. Thank you again and will be regularly visiting!
Was trying to figure out for almost an hour why i couldn't move.
Turns out I forgot to add = MOVE to var state = MOVE. lol
Great videos!
Dude...same... I panicked
I did the same dam thing.
Thanks dude you really saved me!
saved me a lot of time, thank you
I had the same issue, I was like rewatching the damn thing to find if I had forgotten to add something. Thanks mate!
I personally found the player character stopping in it's tracks when attacking to be a bit annoying, so I added these lines to attack_state to make the player slide a bit when they attack:
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
velocity = move_and_slide(velocity)
You'll also need to pass in delta for it to work properly.
Thank you, good idea..
This does literally nothing for me, anyone get this to work?
@Nikola Zivlak I figured it out a different way (:
@PlayStation Japan I don't remember lmao
@PlayStation Japan figure it out
Also make more Astro games 😂
For those of you who use Godot 4 and have this weird flickering between the Idle-State and the Attack-State and also get freezes like me:
Make sure to turn AutoMode off! You do this byclicking on the tansitions between Idle and Attack and go to the right tab -> Advance -> Mode -> Enable (don't disable this). If you play the game it should work like in the video :)
Actually people in the comments mentioned this for Godot 4 in the run video but I forgot that when watching this video xD
I had forgotten about it too... So thank you for reminding me :p
This one! This was so helpful!
Yes! Thank you!
thanks!
Even though I have been closely following Godot since 2014 and I know most of the stuff you show I still watch this series because it's well executed and you show a lot of "hidden" features. Great job. :)
for the transition from attack to move states and vice-versa, you can also connect the animation_finished signal to the player script. (this means once an animation has finished, a signal will sent and that function will be called.) Then, you can add a condition based on what state the player was when that animation played (in this case it will be ATTACK), so then you can set the player's state to MOVE. This avoids having to add a call_method() track to every attack animation
Ahh... Thank you man for this!!!
Yeah, very easy and quick way of doing it, just make sure its the animation_finished signal from the animation tree and not the animation player and its super simple. It automatically sets up the method for you and all ya gotta do is add state=MOVE and you are done.
@@blakealanfoster How did you make this work? I have setup the same way you describe it but when I press attack I get stuck in the current Run or Idle animation instead.
@@dard1515 I believe you have to turn off the looping in each animation (AttackUp, AttackDown, etc...). I am currently trying to figure out how I can make this work while still moving, though, since doing it this way stops your character movement until the animation is finished.
instead of making animations on the left and right, you can just make an animation on one side and add a line of code "flip_h = true" and "flip_h = false"
flip_h is just for the sprites, use scale -1 instead to flip all of your nodes (including collisions)
@@Reloecc i have a feeling this will come in handy some day. Thanks.
I am doing that, but considering it wont flip future hit boxes and hurt boxes, I think I'm going to follow Jan idea
though, it only is useful, if the sprite looks the same-
e.g. there is a scar or an eyepatch on the left/right side, you then need another image for it
in func attack_state(delta) I repeated the lines "velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
" then "velocity = move_and_slide(velocity)" to make the player slide to a halt instead of awkwardly stopping.
very nice, appreciate you sharing this!
This does literally nothing for me, anyone get this to work?
@@TimTacTV If you change the friction to a smaller value (like 50) you'll 'see' it working. At a higher friction value it isn't really noticeable
@@cozmovortex I got it to work another way :D
@@TimTacTV HOW, I'd really like to know.
On the technical side of if vs switch I just wanted to add that switch cases are faster than if statements since they're implemented with a combination of binary trees and jump tables.
Basically with a long if/else list, if you're in the last state, you will have to run all the IFs until you reach the last one, while the switch/case will "jump around", if you run it several times each frame (multiple state machines, check the state in _process, _physics_process and _draw, etc.) it does make a difference at the end.
Great video again HeartBeast, I find it funny that you mentioned using nodes for each state as that's exactly what I did for my pushdown automata. Still, I'm gonna follow what you did for the animation tree.
I love the good morning, afternoon or evening, where ever you and when ever you are my name is Benjamin! It's so satisfying to hear after finishing one of these tutorials and starting another.
dear god this is an amazing tutorial, far better than most of those out there that I did watch but never got a full grasp over the topics at hand, I even managed to add other functions just to test the waters, 11/10
Edit:
Also, you dont really need to add much collusion either, remember the move_and_slide method? well he showed exactly how to draw block areas with it if you want to add areas with hills you can walk on, or even draw buildings on top of them and adding a collusion map in needed areas if im not mistaken
Hi HeartBeast,
Even though I don't usually watch these tutorials completely, it helps so much when trying to solve some problems in my game. I am currently making my own rpg with my own assets and gameplay, and so far it has helped tremendously (especially some of the key concepts such as match statements). Thank you so much for these videos.
Also: something I did slightly different,
I didn't really like the idea of having momentum in the physics and then just forgetting about it when the attack is launched, so in my attack code I included instead the
velocity = velocity.move_toward(Vector2.ZERO, friction * delta)
velocity = move_and_slide(velocity)
Since it takes no inputs and keeps the velocity moving back towards Vector2.ZERO its still smooth and you can't change any movement until its done animating but you will still slide a bit if you initiate an attack while running. It seems to be working well and I like it.
Yo,
I tried out a little bit and in the attack state function I did not set the velocity to zero, I copied
"velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)"
from the move state func and added
"velocity = move_and_slide(velocity)"
into the attack state func. This makes the Player stop not so abrupt and it looks a bit smoother.
Hope I do not run into problems with that in future videos. I am currently binge-watching this tutorial series and I am learning so much!
I started a month ago with Godot and this tutorial is the best I found yet by far.
Good work, dude :)
NIce
6:40 I just love how you corrected your words middle of the sentence. Great series! Thank you so much.
This tutorial series is fantastic so far, thank you kindly for taking the time to put these videos together.
I would also like to mention that in addition to passing delta to your state functions, you could also use 'get_physics_process_delta_time()' to retrieve the delta as well.
Just one note regarding `attack_animation_finished()`: You basically recreated a signal that's already present in the AnimationPlayer. There's one firing whenever animations are done, so I'd just reuse that instead. This saves one from setting up all those callbacks, especially if there are more to come later.
Unfortunately, AnimationPlayer signals are not emitted when animation is played from AnimationTree in Godot 3.2, see github.com/godotengine/godot/issues/28311. However, there is already PR for that github.com/godotengine/godot/pull/30508/files, but it is still open, what's more it is as of now set as 4.0 milestone, so dunno when will it be implemented.
You don't even have to do that. What you could do is just put in auto-advance so that it transitions back to the moving state at the end of the animation.
@@Mriganka2S How would you change the state back to MOVE, though? The animation will stop but you'll still be in the attack state.
@@compartirlavida That is what auto-advance is for in the animation state machine node. Another thing you could do is to just check if an animation is playing with the get_node function for the playback parameter
@@Mriganka2S When i do that, the node isn't changed immediately, there's a short delay between switching to the "Attack" node, so basically checking if get_current_node() == "Idle" on the next frame just resets the state back immediately. Having an animation_completed signal or something would make it much easier than having to add an intermediary variable to test if the state has changed or not.
If your attack animation is stuck, make sure you put the "Call Method Track" is inserted exactly where the last frame is.
Thanks bud
how to fix iy my attack animation is stuck i follow all huhu
Hope there will be a Video about "changing scenes"... so you could go back to them with the player in the correct position.
When I enter "Scene B" from the Top of "Scene A" and I'm going back to "Scene A" I want, that the player is in the same position as he was before.
Is there an easy way to do this?
(btw. sorry if there are any grammatical issues.. I am not a native speaker.)
I've had struggles with that. The way I've found was to use a Singleton. You see, when you change a scene, the later one doesn't exist anymore, but that never happens with singletons, they just keeps everything.
Basically I've created a variable in there, which is a Vector2 with the next player position (actually, it's an export var, they are great 'cause you can change at will and they will work accordingly with the scene, not the script). When the player goes into a door, for example, the game will change the var to what you want before changing the scene, then on the _ready function you make the Player go there. Kinda hard of explain, but really, really easy, I swear. Probably he'll explain better. There is other ways too, of course.
Oh, in my game I've made a way where the Player keeps his direction too.
@@jeffvenancius Thanks a lot!
You can simply call remove_child, and then when the new scene (or level) is loaded, call add_child
Started this series following you and programming in C#. You make it very clear how everything clicks and is so easy too follow. Thank you a lot for this!
Very nice artwork and released under MIT? Awesome work!
State machines always confused me but this finally put the pieces in place. *Thanks @HeartBeast!*
Awesome content!
BTW, I noticed that you forgot to add the +0.1 to the animation tree y axis for attack so that it favors attacking right or left over up or down. Also, I find that when I add the velocity calculations for friction to the attack_state that the player costs to a stop more naturally while attacking. This makes it feel just a bit more fluid IMO.
Awesome tip! Without that, I found that the player would stop suddenly, then keep sliding a bit AFTER the attack like the momentum was still there. I love the way the player now kind of slides to a stop while attacking, it looks cool! =)
One minor issue I noticed is that if you hold in a different direction while/immediately after the attack animation is playing, the attack animation of that new direction will play briefly (approx 2 frames) before/after transitioning to the move state.
Did you find a fix for that ?
If someone have a fix that will be cool to share it
I was able to fix it for me (and hopefully for you): In the AnimationTree editor, I had two node connectors between "Idle" and "Attack". I removed the "Attack" -> "Idle" connector, which had a "Switch Mode" of "At End".
I couldn't wait until today so i set up the attack animation on my own, the process was easy enough.
I didnt' think about adding the state machine though so I had an if statement checking for my attack key but your setup was much better, either way great video. Keep up the good work!
Same for me. Actually added roll as well but I need to see how the position will be updated. I can only think of the function call strategy as of now.
EDIT: Ok finished Roll without function call and it looks pretty good. Maybe there is a better way but we will see.
@@mindstreamx yup, I added Roll too =) I'm currently working on trying to add the "special" attack (spin) but cant figure it out :-\
@@ss2cire am i thinking right about this? just add all attack animations and just bind the animation on a certain key and it will do it when pressed? :D
@@marktriestolive1781 sure but you still need states to make sure you can go from attack to roll ... unless that's the intent... for a smple tame you'd probablly want mke sure all modes shkft from idle.
but it's up to you! that's the nice rhing about the setup
@@ss2cire yes, yes ofc. still a newb on the engine and i will apply what i just learned. now just waiting for dmg part and enemy ai to be added. oso, camera thing :D
This is probably the best tutorial series for learning GODOT basics and good practices. Excellent work!
25July'23: Completed part 9. Noted everything down in my notebook. Very informative...!
I'm really enjoying all your tutorials man! I watched a few other people teaching Godot, but amongst them, your style fits me the best!!
Was previously stuck trying to understand enums, your tutorial explained it perfectly in a couple of minutes. Thanks!
These are the best goody tutorials I’ve ever seen, thank you
Great vids again :) There's a bunch of things that triggered me as a programmer though. In order of importance in my opinion. The first one being VERY important.
1) NEVER ADD A PARAMETER TO A METHOD IF YOU DON'T USE IT. (the delta in the attack_state method)
It is really a bad practice to declare parameters in a method signature if you don't use it, even if you plan to use it. In fact, I'm quite sure the warning you have right now in your debugger is actually saying so. This is a great way of adding confusing, useless complexity to your code.
Basically, if you need it, put it. If you don't, don't put it.
2) DON'T MIX LOGIC IN YOUR METHODS TO ACCOMPLISH SOMETHING YOU WANT (adding the animationTree.set("parameters/Attack/blend_position", input_vector) in the move_state)
Your reasoning behind it was very good, but the way you implemented it is somewhat hackish.
It doesn't make sense that your move_state would do attack logic and it is also a great way to make your code very confusing as you scale up.
You're saying you don't want the player to change direction when he is attacking and you don't have access to input_vector outside move_state. Good! Then find a way to access it. A really easy and intuitive manner to do this, and this is what I've done, is to declare a variable outside the move_state method that actually keep track of what you want -->
--at top of the Player script
var player_orientation_vector = Vector2.ZERO
--inside the if statement that checks if you're pressing an input
if input_vector != Vector2.ZERO:
player_orientation_vector = input_vector
animationTree.set("parameters/Idle/blend_position", player_orientation_vector)
...
--then you can write your attack_state method as follow
func attack_state():
animationTree.set("parameters/Attack/blend_position", player_orientation_vector)
animationState.travel("Attack")
3) METHODS SHOULD ONLY DO WHAT IT SAYS IT DOES (move_state should not handle change of states.)
Ok so this might be arguable since the attack button is pressed when in the MOVE state but in my opinion, it should only be aware of what it does when in MOVE state and not handle the switch between states. I'd put the check on the Attack button outside of it. (check code below in number 4)
4) This one is probably cause I'm too perfectionist, but I hate it when built_in methods starts to do complex logic. Like, _physics_process handling player states.
Doing something like this, will make your code more readable and easier to scale -->
func _physics_process(_delta):
_handle_player_state()
func _handle_player_state():
match state:
MOVE:
move_state()
if Input.is_action_just_pressed("attack"):
state = ATTACK
ATTACK:
attack_state()
Hope it helps some people improve their code. Take care! :)
I'm semi following along with his code, and am now fixing according to your notes, but I have a couple questions cuz I'm a noob at this.
so can delta be taken out of the code completely, if it's taken out of action_state and move_state?
also, unrelated but something I've been struggling weeks with; I want a function that allows my player to place a tile beneath them. I have a 16x16 auto tile ready, but don't know how to code that function in Godot. Would appreciate any assistance!!
Can you post full improved code pls?
here's the updated code. My action animation doesn't return to walk or idle, thus the button is pressed, the action animation triggers, and then you can't interact with the player. Help!
enum {
WALK,
ACTION
}
var state = WALK
var motion = Vector2.ZERO
var player_orientation_vector = Vector2.ZERO
onready var animationPlayer = $AnimationPlayer
onready var animationTree = $AnimationTree
onready var animationState = animationTree.get("parameters/playback")
func _ready():
animationTree.active = true
func _physics_process(_delta):
_handle_player_state()
func _handle_player_state():
match state:
WALK:
move_state()
if Input.is_action_just_pressed("Action"):
state = ACTION
ACTION:
action_state()
func move_state():
var input_vector = Vector2.ZERO
input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
input_vector = input_vector.normalized()
if input_vector != Vector2.ZERO:
player_orientation_vector = input_vector
animationTree.set("parameters/Idle/blend_position", input_vector)
animationTree.set("parameters/Walk/blend_position", input_vector)
animationState.travel("Walk")
motion = motion.move_toward(input_vector * MAX_SPEED, ACCELERATION)
else:
animationState.travel("Idle")
motion = motion.move_toward(Vector2.ZERO, FRICTION)
motion = move_and_slide(motion)
func action_state():
animationTree.set("parameters/Action/blend_position", player_orientation_vector)
animationState.travel("Action")
func action_animation_finished():
state = WALK
actually thanks for this comment, i was checking my code because it crashed and it was literally the delta in attack_state() so yeah great thank you
edited yet again: i kinda get it but when i try to use
func _handle_player_state():
match state:
MOVE:
move_state()
if Input.is_action_just_pressed("attack"):
state = ATTACK
godot returns an error "Too few arguments for "move_state()" call and if i try to add (delta) as an argument it says the delta identifier hasn't been declared. so what's going on?
@@plankcaller sorry that was my mistake, you need it in the move_state method. You're method move state must declare it in its signature(method declaration).
Nice tutorial man, your tutorial series are awesome.
Note: You could've been used the auto-advance condition to use as check if the state/animation was done. You only needed to set on _ready() call to set the initial condition value and only a if would be necessary to state back to idle for example.
Not sure if it is mentioned in a later video but I wanted to share some advice for y'all regardless:
Please, if possible, do yourself a favour and use enums instead of if statements. Not only is it easier to type out, more importantly, it's more efficient.
Try to think of it this way: With if statements, you go down a list of options and check each step if the statement is true (is he called Carl? no? is he called Daniel then? no? is he called Edward then? yes. ok!). With an enumerator, your basically jumping to the correct location in the list instead of looking up all previous ones (I'm looking for Edward, so I'm going to skip the names starting with C and D). It might not seem like much at first thought but it adds up over time and it's a good habit to avoid this from the get-go.
Should have watcher this series WAYYY sooner. Great stuff!
For Godot 4, I couldn't figure out how to make the attack animation finished function to work properly. So I spent three days working on a bunch of stuff, but I landed on how to make it work with Signals. You learn the basics of Signals in the next video, but here's what I did.
First, you should know that the next video explains how to connect a signal to a node, that will then generate the function for you. For this code, I used the animation_finished signal, so it generated a function with an appropriate name for me, and has pass in it by default for me to replace with my code. Now the actual code.
func _on_animation_tree_animation_finished(anim_name):
if anim_name in ["AttackRight", "AttackLeft", "AttackUp", "Attack Down", "Roll Down"]:
state = MOVESTATE
The function that Beastheart made in this video, in combination with the Call Method track, recreates the functionality of connecting signals. But if it's not working then I hope this helps you.
Hi, I just discovered how to make it work in Godot 4. The problem is that on the method line, on the editor, it has a small green ball in front of it. When you click there, it will turn off and then you can move it on the timeline. You need to position this ball on the last time of the track (when you create it, it appears to create after the time the animation is finished, so it never gets called).
16:27 the fox became a jojo character
Mudamudamudamudamudamuda
hahaha
I don't know if it will break anything later, but I really didn't like adding the function call to the animation, so I developed a different approach. I changed the Attack animation to auto transition in to the Idle animation, added a change_state function which handles everything that needs to move to a new state, and changed the attack_state to detect when the animation is "Idle". Instead of directly setting the state, you call the change_state function with the new state. Here's the code and it works exactly as expected:
if Input.is_action_just_pressed("attack"):
change_state(ATTACK)
func change_state(new_state):
match new_state:
ATTACK:
animationState.travel("Attack")
state = new_state
func attack_state(delta: float):
if (animationState.get_current_node() == "Idle"):
change_state(MOVE)
iam finish the code and repet the video many time but there is lag when player move can you help me
Noticed a weird issue on my end where if I try to go Up+Left and press the Space key to play the attack it doesn't play, yet if I use another key (such as the 'Z' key) instead the animation plays correctly, and the Space key seems to work just fine for any other angle.
Hi, I just wanted to say that I REALLY REALLY love your Godot tutorial and the way you explain things are just useful since you point out errors as well. ^^
I also have a request: could you please make a tutorial on how to make a customizable player and apply the customized player to animation and various movements?
DUDE THANK YOU SO MUCH YOUR VIDEO IS THE ONLY ONE THAT HELPED ME DO THE FULL ANIMATION
Best tutorial if seen so far, exactly what I need for my next project, thanks so much.
Love following this series... Keep it coming HeartBeast
Would love to see a rpg tutorial for godot 4 using your components approach in the ship game but I’m using both video series bits and pieces and it seems to do the trick! Love your tutorial videos
As always, great video, Mr. Benjamin! Looking forward for more!
Just wanted to say thanks, doing a couple videos in this series a day and out of the 3 other game tutorials for this engine I have followed, while they were great in their own right. This is the only one were I feel empowered to go back and comment my code so I ingrain the information more and able to have my own reference game to look back at if I ever get a RPG itch. You can just tell you care about people actually taking something away from these videos and not just trying to finish a cookie cutter game in under X hours.
Inputs maps in Godot are great. So easy.
For anyone that's getting a bunch of errors after assigning the "Call Method" track at the end of each attack animation, the fix is simple.
Animation Tree -> Inspector -> Callback Mode -> Change "Method" from "Deferred" to "Immediate".
Awesome content, thanks!
At 15:10 when you wanted the idle and attack transitions to be perfectly vertical it made me think of this- I looked but didn't see an option for it, maybe theres something like it I didn't see or it could potentially be a nice feature in future versions:
In my video editor (DaVinci Resolve) when you're doing effects, theres a nodal workspace like the one in the animation tree. Theres an option to have all of the nodes snap to a grid, and theres another option called "orthagonal pipes", which makes all of the connecting lines between your nodes only appear as perfectly horizontal and vertical straight lines with 90 degree elbows where necessary. When these two options are turned on it can totally help keep your project organized looking and easy to follow when you start adding a lot of nodes.
Hey Benjamin, I just want to let u know that I'm really enjoying ur tutorials, keep on rocking. Greetings :)
As always, great content! Just picked up your 1-bit course too :D
When you add the attack_animation_finished() function to the call stack, make sure the green diamond lines up with the line between 0.3 and 0.4. When I added the function it was adding it just after that line and it was confusing me why it wasn't run.
haha same
God bless you, sir! Could not figure out why my animations were stopping short.
If you want the attack animation to depend on your mouse position instead you can create a variable in the move state function like this[var mouse_position = get_local_mouse_position()] and change the var in animationTree.set to mouse_position instead of input_vector
also take out animation_tree.set("parameters/Attack/blend_position",mouse_position) and paste outside the if statement so it will work even when ur not moving
also for the bug where your character will continues to attack if u change direction u can fix it by changing the dotted line inside the animationtree node to the line
Awesome teaching skills you have here HeartBeast, keep going with your great tutorials. Do you have some way that we can tip or support you (patreon, etc..)?
Remember: when you're using Godot 4, velocity during the attack animation (the sliding bug he describes) is not an issue for you to resolve. Should work out of the box.
for this to increase the sword swing speed you would just have to change the 0.4 to 0.2 or lower the time it takes to complete the animation, right?
That's what I did, the sword strike in this video was wayyy too slow. put "0.01" in the snap textbox to make it easier and take each frame down by how fast you want the animation. I made mine 2x faster
Excellent vid as always! Thank you!
For those who are following along in C#, make sure you build your project (via mono) if your "functions" do not appear in the "Select Method" list.
Ok maybe this is just my version of godot or something but
when i click in the spacebar and down arrow and left arrow at the same time it just doesn't register the input for some reason
not only it doesnt change animation but it doesnt even stop the character and i even wrote this check:
func _physics_process(delta):
if(Input.is_action_just_pressed("attack")):
print( "attack")
...
and it prints when i click up + right + space, , up + left + space , down + right + space but just doesn't print anything when i press down+left+space
EDIT: i added J to the attack inpu and IT works for every direction... maybe its just some weird quirk with my keyboard?
does anyone have a similar problem or is my godot just weird?
if anyone knows what is happening please help me
How do you not have more subs. You're the literal Brackey's of Godot. Keep it up!
If anyone has a problem with the AnimationPlayer call method track and not being able to find your function in the popup window just restart Godot. Looks like there's a bit of a bug or something (I'm on version 3.4.2) where the "Insert Key" window isn't picking up changes to scripts. A restart fixed the issue though
You're a god damn legend thanks!
help it doesn't work
If you add an idle sprite to the attack animation, you will not need to add the function attack_animation_finished. In all I'm impressed we can add methods to the line of animation to detonate scripts along with said animations. It was worth to see that alternative technique just to open that venue of creative possibilities with the animation track.
Now now I just have several questions.
1. How do u attack while moving.
2. How do u make combo. (Let's say we have 3 attack combo and then each attack is in different direction).
Here the state mechanic won't help. Any idea?
Thanks Sifu. Btw It is better to watch twice before first time pause and follow the setup, which may counter the same error of author itself.. but is good to experience the error for boosting the confident on debug.
Absolutely fantastic. Quality content. Thank you.
It's only 2 minutes but still I wish you more viewers :)
"thats not what we want, well lest juat do that" 😂 I know your mind you want state machine to play animation based on direction too.
its sad how heartbeast isn't recgonized on youtube ,he is the best on godot programing so i think that he"s no that recgonized here
I wish he had at least 1 million that is what he worth
=)
iam finish the code and repet the video many time but there is lag whem player move can you help me
absolutely incredible. thank you.
The only additional issue here is the fact that (probably due to the lack of depth) the attack animation goes through bushes (actually cool) and cliffs (looks as if the sword is being swung above the cliff). But I guess for a beginning we need not be picky. :P
I ran into a weird issue adding the attack animations. Somehow the left attack "update mode" was set to "continuous" and not "discrete" which resulted in the animation skipping the last frame (I don't recall changing this, and it's a drop down so doesn't seem like a likely mis-click). Oddly, some of the run animations (which I hadn't touched since that part of the tutorial) were changed as well. I haven't been able to replicate the issue once I changed them all back to discrete, but it was a bit of a head-scratcher, just thought I'd share.
iam finish the code and repet the video many time but there is lag whem player move can you help me
Helped me :D
i usually use switch statements a ton in C++ and actually wondered why they weren't in godot , it turns out they were always here after all ; in form of that match statement syntax . Thanks for letting me know !
Your videos are helping me a lot! Thanks bro!
Awesome videos dude.
Is it possible? if we don't used the Animation Tree.
I have printed and the states are not changing.
(Edited Section.)
I fixed it by making "Input.action_pressed" to "Input.is_action_just_pressed."
Hey, will this game have a menu and? And could you make a level-up system? Some bosses maybe?
Anyways, great video as always!
first of all THANK YOU, for showing me how to call methods at the end of animations. I'd been searching, for days, for a way to allow single animations to repeat with the animation tree. Do you know of any other ways of doing this? Improvements or alternatives to the workflow of doing repeatable animations.
All of my attack animations work except for the down one. When I try to hit the input key to attack while moving down my player just goes idle and I can't move. Does anyone have any idea why this is? I've looked at my AttackDown animation and it looks correct.
I had the same issue, and somehow had ended up coupling IdleDown instead of AttackDown in the AnimationTree.
I can't figure out why i'm getting this error
Invalid call. Nonexistent function 'move_toward' in base 'int'.
on this line :
velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATE * delta)
I've went back to copy the videos again but it keep happening and no luck on google
Another way to change the state at the end of the animation would be using Signals (which would be a new topic and maybe that's why you didnt use it in this video), connecting the AnimationPlayer's animation_finished signal to the Player script, then checking if the state is equal to ATTACK, or using a "match" in there too.
You didn't need to set the attack animations on loop to see them in the AnimationTree vector graph just needed to change the Blend to the dots :)
Dude even a bear could understand this, 11/10
wow input mapping is cool got my controller mapped too this is starting to look like a real game. Still confused on tile maps since its so different in version 4+
6:43 : When you fu. have errors ...
Love the commitment
Btw switch-case in c++ can use the enum names ;)
E.g
enum class serialKillers { TedBundy, PedroLópez, JohnWayneGacy } and switch(serialKillers) { case serialKillers::TedBundy: break; }
20:56 i need help, it isn't finding attack animation finished
edit: i closed and reopened godot and now it works
What a shame that I have NEVER heard in my 2 year developement in godot of the AnimationTree, or just never used it. I think it makes so much stuff so much easier!
6:45 You were just about to throw the f-bomb young man 😂
Nah, I've never said the f word in my life.
@@uheartbeast I believe you :) I just thought you were about to say when you f*** up, then immediately said have errors and stuff.