Scope and Limitation

This tutorial page will cover such topics as:

Input and input bindings

Getting started with EmotionFX

Getting started with Lua scripting

Expected Results

At the end of this tutorial, we’ll have created a simple, Lua-controlled, motion-based, forward-backward-moving character controller using the art and animation assets from the official Lumberyard StarterGame project.

By motion-based, we mean that we won’t impart an impulse or velocity on the player character to make it move. Rather, the animations will drive the motion itself. This makes for a more natural-looking walk animation because the speed of the translation matches the animation perfectly.

Those are free! Feel free to use a complete set of humanoid locomotion for your own game!

Methodology

For this project, I’m working on the MultiplayerSample project, which I set up in the previous tutorial. But by all means, you can work on your own new project.

Setting up input bindings

The first thing we’ll want to do is create the input bindings file so we can assign controls to the movement.

Right click on the Perspective view and click on Create Entity on the context menu. You can rename this as Player as this will be our player character entity. On the newly created Entity, add an Input component via the Entity Inspector.

Click on the little joystick button on the Input to event bindings field of this component to bring up the asset editor for input bindings. Click on File/New and select a directory where you’d like to save your input bindings file (I saved mine at dev\MultiplayerSample\inputbindings\character_movement.inputbindings).

Click on the + button on the top-right to create a new input event. The first event we’ll be making is for moving forward. We’ll name it accordingly as move_forward. This is how we’ll be referring to the event in the Lua scripts later, so keep it in mind!

Next, create event generators for it, which are essentially the button presses on the keyboard or gamepad or other input device which will trigger the input event.

Create an event generator for the keyboard W button. You can leave the Event value multiplier to 1.0 as we’ll consider moving forward as a positive signal. The dead zone value is for configuring the dead zone of analog input, such as thumbsticks. If you would like to add gamepad controls, you can configure that here.

Create another event generator for the keyboard S button, but this time set the event value multiplier to -1 to denote that when this button is pressed, you’re doing the opposite of moving forward. This will essentially be the move backwards button.

Click on File/Save.

Once the file is set up, you can assign the input bindings file to the Input component added to the player character entity created earlier.

Setting up the player character entity

Next, we’ll set up the character entity’s other components. But before proceeding you’ll want to be sure you have art assets to work with. Thankfully, there’s a full set that’s ready to use c/o the StarterGame project.

Copy the folder dev/StarterGame/Objects/Characters and paste it into your project folder (I pasted mine into dev/MultiplayerSample/objects so that the Characters folder is inside the objects folder). Inside this folder, you’ll find the Jack folder containing the complete assets for the Jack sample character, including animations and meshes.

Important note: One of the issues of Lumberyard right now is that it can’t track files when you move them (i.e. if a component is referencing a file and you move that file to another directory, Lumberyard won’t know about it, so you’ll have missing references). So be sure that you’re putting your files where you want them for good!

Next, we’ll be adding the rest of the components the character controller will need:

Add an Actor component to the player entity using the Entity Inspector. This component will be performing the rendering functionality of your character entity. Assign the Jack fbx file under the Jack folder we copied earlier to the Actor asset field (mine’s set to dev\MultiplayerSample\objects\Characters\Jack\Jack.fbx). If your version of Jack has missing textures, you may have to set the material to the correct one, jack_matGroup.mtl. Add a Character Physics to simulate character physics on this character. This component will allow you to specify the physical attributes of your character, such as the dimensions of the capsule collider, mass, etc. Add an Anim Graph component to use the EmotionFX system on this character. Create a new child entity for the character entity and add a Camera component to it. This will be our third-person perspective camera. Position it over Jack’s shoulder as you see fit (I have mine positioned at 0.4m,-1.0m,1.6m).

Setting up the EmotionFX Motions and AnimGraph

We’ll be needing the animation files from the StarterGame project. Copy the folder dev\StarterGame\animations into your project (mine’s copied into dev\MultiplayerSample\Animations). In here, you’ll find all the animation files and Anim Graphs used in the StarterGame project. Feel free to study these. Once copied, the Asset Processor will go through them to prepare them for your project.

Now that the entity is properly set up and we have the animation files, it’s time to create an Anim Graph for the player character to use. Go to Tools/Animation Editor to launch the EmotionFX editor.

I won’t go into detail about this tool, as it’s far too complex to cover in one sitting. We’ll only cover the basics and need-to-know’s for the scope of this tutorial.

First, we need to import an actor into the tool so there’s a preview of the animations we’re defining. Right click on the Perspective window and click on Open Actor. Select Jack.fbx.

Next, we need to define a set of animations which will be used for the Anim Graph we’re preparing. This set of animations is also called the Motion Set.

Click on the Motion Sets tab. Click on the green plus button to create a new Motion Set. Right click on the new Motion Set to bring up the context menu and rename it to “HumanoidCharacter”. Click on HumanoidCharacter to begin adding new motions to this Motion Set. Click on the green plus button to define a new motion for this Motion Set. Double-click on the name field to modify the name of this motion and set it to “walk_forward” to define the forward walking motion. Double-click on the filename field to modify it and in the ensuing asset browser, look for “Jack_Strafe_Walk_Forwards.fbx”. Create a new motion for the backward walking motion and give it the name “walk_backward” and animation file “Jack_AimStrafe_Backwards.fbx”. Create a new motion for the backward walking motion and give it the name “stand_idle” and animation file “Jack_Idle.fbx”. Now would also be a good time to save your progress. Go to File on the top left of the screen and Save All.

Next step is to create the Anim Graph.

Click on the Anim Graphs tab on the right side of the screen. Click on the green plus button to create a new Anim Graph. You may have to specify a Motion Set in the Motion Set dropdown first to enable the button. Specify the HumanoidCharacter Motion Set. Right-click on the newly-created Anim Graph to bring up the context menu and rename it to “HumanoidCharacter”. Double-click on the HumanoidCharacter Anim Graph to make it the active Anim Graph.

Next step is to create the Anim Graph parameters which will be evaluated by the Anim Graph to run its state machine. We’ll be modifying these parameters later on with a Lua script.

Click on the green plus button on the Parameters pane to create a new parameter. Set the name to “speed”. Set the value type to Spinner (or whichever you prefer. I like the precision of the spinner!). Leave the default at 0. Set the minimum at -1, maximum at 1.

We’ll be modifying this speed parameter with a Lua script depending on the player’s input. A value of 0 means there’s no input, 1 means the character is going forward, and -1 means the character is going backward.

Next step is to create the actual Anim Graph state machine. Click on the Anim Graph tab above the Parameters pane. In the words of Anakin Skywalker, this is where the fun begins.

In the Anim Graph pane

First, we need to add in the different motion states to play: Right-click on the canvas, click on Create Node/Sources/Motion. Click on the newly-created motion state to modify its attributes in the Attributes pane. We’ll first make the motion state for the idle animation. So set the Node Name to “idle”. Set the Motion to the “stand_idle” motion. Do this two more times for the forward walking and backward walking animations. Right click the idle state and set it to be the entry state. You should already see Jack animating in idle. If not, double-click on the HumanoidCharacter Anim Graph to refresh it. Next, we need to connect the different motion states so jack can transition between them. Click and hold on the idle state and drag the line to the walk_forward state to connect them. This will create a transition from idle to walk_forward. Click on the transition to modify its attributes. In the Attributes pane, scroll all the way down to the bottom to find the Add Condition button. Click on this to add a new condition that needs to be met before the Anim Graph can transition from idle to walk_forward. In the new Condition popup, select Parameter Condition. Look for the newly-created condition and set the parameter to speed. Set the Test Value to 0.0, and the Test Function to “param > testValue”. This means that if Jack’s speed is greater than 0.0, he will start playing the walk_forward motion. Repeat these steps to create the rest of the transitions: idle to walk_forward if speed > 0.0 walk_forward to idle if speed == 0.0 idle to walk_backward if speed < 0.0 walk_backward to idle if speed == 0.0 Now the Anim Graph is complete! Jack can now walk back and forth as his speed changes. Save all in the Animation Editor and close it to get back to the Lumberyard Editor.

Select the Player entity and assign the Anim Graph we created to the Player entity’s Anim Graph component. Once the Anim Graph is properly referenced by the Animation component, you should be able to see the character play our idle animation when you test the game in the Editor with Ctrl+G. You can modify the speed value manually in the Entity Inspector to change the initial behavior of the Anim Graph and see the changes when you hit Ctrl+G.

Connecting the input events to the animation events

Next, we’ll modify the speed parameter of the Anim Graph as signaled by input events through a Lua script.

Add a new Lua script component to the Player entity. Click on the {} button on this component to launch the Lua IDE. This is where we’ll do our Lua coding, but if you’d rather use another program to create your Lua scripts, such as Notepad++, that’s fine too!

Create a new Lua script called PlayerController.lua. We’ll start with the Lua script boilerplate stub. The code is documented in the comments:

-- This is how you write a one-line comment. -- This local definition defines the metatable for this -- Lua script, where you can add properties and fields -- such as a health property, armor, speed, etc. local PlayerController = { Properties = { -- Insert properties here } } function PlayerController:OnActivate() -- Add activation code here. -- The code here gets invoked when the script activates for the first time. -- Code here only gets called once. end function PlayerController:OnDeactivate() -- Add deactivation code here. -- The code here gets invoked before the object gets destroyed. -- Code here only gets called once. end return PlayerController -- This return line is required.

Save this PlayerController.lua script in the project folder (I have mine saved at 1.13.0.0/dev/MultiplayerSample/Scripts). Assign this newly-created script to the Script field of the Lua Script component of our Player entity.

Continuing work on the code, we’ll first want to be able to listen to input events coming in from the Input component. To do so, we need to setup our script so that it’s subscribed to input event notifications. We’ll do this on the OnActivate() function:

function PlayerController:OnActivate() -- Create the InputEventNotificationId we want to subscribe to -- Subscribe to the move_forward input event as defined in the inputbindings file self.forwardBusId = InputEventNotificationId("move_forward") -- Connect to the InputEventNotificationBus so that we can listen -- for the event we specified in self.forwardBusId self.forwardBus = InputEventNotificationBus.Connect(self, self.forwardBusId ) end

Once subscribed to the InputEventNotificationBus, you can now define specific functions which will be invoked whenever a notification for the specified input event gets triggered. We can define the OnPressed, OnHeld, and OnRelease functions:

function PlayerController:OnPressed(floatValue) -- Cache the currently generated input event ID local currentBusId = InputEventNotificationBus.GetCurrentBusId() -- Check if the currentBusId is for the forward input event if currentBusId == self.forwardBusId then -- If so, send a request to the AnimGraphComponent to modify the -- speed parameter so that its value is the value sent to us by -- the InputEventNotificationBus, which can either be 1 or -1 if -- the input comes from W or S, or an analog value in between if you configured -- thumbstick input AnimGraphComponentRequestBus.Event.SetNamedParameterFloat(self.entityId, "speed", floatValue) end end function PlayerController:OnHeld(floatValue) -- The same thing happens on the OnHeld local currentBusId = InputEventNotificationBus.GetCurrentBusId() if currentBusId == self.forwardBusId then AnimGraphComponentRequestBus.Event.SetNamedParameterFloat(self.entityId, "speed", floatValue) end end function PlayerController:OnReleased(floatValue) -- You also want to catch the OnReleased event -- because this is where the floatValue gets set to 0, -- indicating that the player stopped pressing buttons, -- so the speed parameter must be modified accordingly -- to stop Jack from walking local currentBusId = InputEventNotificationBus.GetCurrentBusId() if currentBusId == self.forwardBusId then AnimGraphComponentRequestBus.Event.SetNamedParameterFloat(self.entityId, "speed", floatValue) end end

Save the Lua script. Once that’s all set, return to the Editor and press Ctrl+G to test.

For your copy-pasta convenience, here’s the full script!

local PlayerController = { Properties = { } } function PlayerController:OnActivate() self.forwardBusId = InputEventNotificationId("move_forward") self.forwardBus = InputEventNotificationBus.Connect(self, self.forwardBusId ) end function PlayerController:OnDeactivate() -- Don't forget to disconnect your buses promptly when you -- no longer need them to avoid unwarranted behavior. -- The best place to do this is OnDeactivate() self.forwardBus:Disconnect() end function PlayerController:OnPressed(floatValue) local currentBusId = InputEventNotificationBus.GetCurrentBusId() if currentBusId == self.forwardBusId then AnimGraphComponentRequestBus.Event.SetNamedParameterFloat(self.entityId, "speed", floatValue) end end function PlayerController:OnHeld(floatValue) local currentBusId = InputEventNotificationBus.GetCurrentBusId() if currentBusId == self.forwardBusId then AnimGraphComponentRequestBus.Event.SetNamedParameterFloat(self.entityId, "speed", floatValue) end end function PlayerController:OnReleased(floatValue) local currentBusId = InputEventNotificationBus.GetCurrentBusId() if currentBusId == self.forwardBusId then AnimGraphComponentRequestBus.Event.SetNamedParameterFloat(self.entityId, "speed", floatValue) end end return PlayerController

Save your work and return to the Editor to test. A red exclamation point will appear on the Lua script component if there are any syntax errors in your script.

Results and Discussion

Now when you hit Ctrl+G you should have a character that walks forward and backward when you hit the controls specified in the input bindings file.

Needless to say, the set up we have here is pretty simple. For example, a proper implementation would have you blending between different walk/run/sprint animations depending on the character speed. For more complex AnimGraph setups, the official Amazon Games Tech channel has a great tutorial series discussing EmotionFX. Check it out!

Additionally, EmotionFX is also a system that I think you’ll learn better by experience rather than following a tutorial. I recommend just getting your feet wet in it, making all the awful mistakes and learn from them.

As always, please feel free to send any questions my way! Any feedback about how the content of these tutorials and how they are presented is also welcome. Thanks for reading!

The next tutorial I’ll be making is for using SpeedTree with Lumberyard! Please look forward to it!