Hi, everyone! 👋
In the previous part, I created models for tiles and tile sets, and set up animations for the Hydra sprite 🐉. In this article, we will continue exploring game development on .Net MAUI, focusing on movement mechanics for our Hydra 🎮. We’ll implement logic to select the correct animation based on movement direction, add realistic shadows for depth, and enhance the overall visual polish of the game ✨. By the end, our Hydra will feel more dynamic and responsive, bringing us one step closer to a complete game 🚀.
![main image](https://static.wixstatic.com/media/1c5355_e7117bf158914b09ba7d0229bc01a7f0~mv2.png/v1/fill/w_980,h_980,al_c,q_90,usm_0.66_1.00_0.01,enc_avif,quality_auto/1c5355_e7117bf158914b09ba7d0229bc01a7f0~mv2.png)
First, let’s move our character to the point on the screen where the user tapped. To do this, we need to detect the exact point of the tap. Then, we calculate the center of our sprite and gradually update its global coordinates to align with the tapped point. Additionally, we can implement smooth interpolation or easing functions to make the movement more natural.
To move it in the correct direction, I will calculate the direction by dividing the difference between the tapped point and the current point by their absolute differences. To calculate the xDirection, I use the following code, which is similar for yDirection but with Y coordinates. Becouse of calculation in a float value, center point must have a size greater than one pixel. For checking size of this point need DeadZoneMin and DeadZoneMax. XTranslate is the offset of the sprite in global coordinates.
var direction = (tapPoint.X - CurrentPoint.X) / Abs(tapPoint.X - CurrentPoint.X);
var difference = Abs(tapPoint.X) - Abs(CurrentPoint.X);
if (difference is < DeadZoneMin or > DeadZoneMax)
{
XTranslate += direction * TranslationSpeed * animationCycleTime;
CurrentPoint.X = (XTranslate + ScaledSize / 2) /
(float)DeviceDisplay.MainDisplayInfo.Density;
XDirection = (int)Ceiling(direction);
}
else
{
XDirection = 0;
}
return XTranslate
To draw our character in a new position at the screen we can use DrawBitmap method with offsets:
canvas.DrawBitmap(_hydra.CurrentTileSets.Body.TilesBitmap, _hydra.CurrentTileSets.TilesData[_hydra.AnimationIndex].TileRect,
new SKRect(xTranslate, yTranslate, _hydra.ScaledSize + xTranslate,
_hydra.ScaledSize + yTranslate));
![Hydra on tap animation](https://static.wixstatic.com/media/1c5355_eeb7c178f3164bce9fb8caed1b480be3~mv2.gif/v1/fill/w_600,h_597,al_c,pstr/1c5355_eeb7c178f3164bce9fb8caed1b480be3~mv2.gif)
To set the Hydra in the center of the screen on startup, I add a SetPosition method to the ViewModel that will be called in the OnSizeAllocated method in the code behind for the GamePage. This ensures that the character's initial position adapts dynamically to different screen sizes and orientations, providing a consistent user experience across devices.
internal void SetPosition(float width, float height)
{
TapPoint = _hydra.CurrentPoint = new Vector2(width / 2,
height / 2);
_hydra.XTranslate = (float)(TapPoint.X *
DeviceDisplay.MainDisplayInfo.Density -
_hydra.ScaledSize / 2);
_hydra.YTranslate = (float)(TapPoint.Y *
DeviceDisplay.MainDisplayInfo.Density -
_hydra.ScaledSize / 2);
_gameFieldHeight = height;
_gameFieldWidth = width;
}
![Hydra on the center of the screen](https://static.wixstatic.com/media/0164f8_a0187b7b6160430b8d7f13811c493a3b~mv2.jpg/v1/fill/w_980,h_2178,al_c,q_90,usm_0.66_1.00_0.01,enc_avif,quality_auto/0164f8_a0187b7b6160430b8d7f13811c493a3b~mv2.jpg)
To choose the correct animation, we need to know the current state, direction, and list of animations. To manage the character's state, I added the HydraState enum:
internal enum HydraState
{
Idle,
Move,
Attack,
}
To determine the current animation, I added the HydraAnimationStates enum, which categorizes all possible animation states. This enum simplifies the process of selecting the correct animation by serving as a single source of truth for the Hydra's state, such as walking, idle, or attacking, across different directions. To make the next code clearer to understand, you should closely look at the following image.
![Possible directions for Hydra](https://static.wixstatic.com/media/0164f8_222fc32dfe5e4032b99af29b164faa09~mv2.png/v1/fill/w_980,h_980,al_c,q_90,usm_0.66_1.00_0.01,enc_avif,quality_auto/0164f8_222fc32dfe5e4032b99af29b164faa09~mv2.png)
internal enum HydraAnimationStates
{
ZeroDegreesWalk,
FortyFiveDegreesWalk,
NinetyDegreesWalk,
OneHundredThirtyFiveDegreesWalk,
OneHundredEightyDegreesWalk,
TwoHundredTwentyFiveDegreesWalk,
TwoHundredSeventyDegreesWalk,
TreeHundredFifteenDegreesWalk,
ZeroDegreesIdle,
FortyFiveDegreesIdle,
NinetyDegreesIdle,
OneHundredThirtyFiveDegreesIdle,
OneHundredEightyDegreesIdle,
TwoHundredTwentyFiveDegreesIdle,
TwoHundredSeventyDegreesIdle,
TreeHundredFifteenDegreesIdle,
ZeroDegreesAttack,
FortyFiveDegreesAttack,
NinetyDegreesAttack,
OneHundredThirtyFiveDegreesAttack,
OneHundredEightyDegreesAttack,
TwoHundredTwentyFiveDegreesAttack,
TwoHundredSeventyDegreesAttack,
TreeHundredFifteenDegreesAttack
}
To fetch the needed animations, I added HydraTileSetManager, which inherits from BaseTileSetManager. This manager handles various animation states for the Hydra character, such as walking, idling, and attacking, and contains public methods that return tile sets with corresponding animations and their shadow counterparts.
internal abstract class BaseTileSetManager
{
protected List<TileSet> TileSets { get; private set; }
protected readonly List<string> FileNames;
protected BaseTileSetManager(IEnumerable<string> fileNames, int tileWidth, int tileHeight)
{
TileSets = new List<TileSet>();
FileNames = fileNames.ToList();
foreach (var fileName in FileNames)
{
var imageData = Task.Run(() =>
FileReader.GetImageData(fileName)).Result;
TileSets.Add(TileSetFactory.CreateTileSet(tileWidth,
tileHeight,
imageData));
}
}
}
internal class HydraTileSetManager : BaseTileSetManager
{
private HydraAnimationStates _currentState;
private static readonly string[] TileSetsImagePaths = new[]
{
ZeroHydraWalk, FortyFiveHydraWalk, NinetyHydraWalk,
OneHundredThirtyFiveHydraWalk, OneHundredEightyHydraWalk,
TwoHundredTwentyFiveHydraWalk, TwoHundredSeventyHydraWalk,
TreeHundredFifteenHydraWalk, ZeroHydraIdle, FortyFiveHydraIdle,
NinetyHydraIdle, OneHundredThirtyFiveHydraIdle,
OneHundredEightyHydraIdle, TwoHundredTwentyFiveHydraIdle,
TwoHundredSeventyHydraIdle, TreeHundredFifteenHydraIdle,
ZeroHydraWalkShadow, FortyFiveHydraWalkShadow,
NinetyHydraWalkShadow, OneHundredThirtyFiveHydraWalkShadow,
OneHundredEightyHydraWalkShadow,
TwoHundredTwentyFiveHydraWalkShadow,
TwoHundredSeventyHydraWalkShadow, TreeHundredFifteenHydraWalkShadow,
ZeroHydraIdleShadow, FortyFiveHydraIdleShadow,
NinetyHydraIdleShadow, OneHundredThirtyFiveHydraIdleShadow,
OneHundredEightyHydraIdleShadow,
TwoHundredTwentyFiveHydraIdleShadow,
TwoHundredSeventyHydraIdleShadow, TreeHundredFifteenHydraIdleShadow,
ZeroHydraAttack, FortyFiveHydraAttack, NinetyHydraAttack,
OneHundredThirtyFiveHydraAttack, OneHundredEightyHydraAttack,
TwoHundredTwentyFiveHydraAttack, TwoHundredSeventyHydraAttack,
TreeHundredFifteenHydraAttack, ZeroHydraAttackShadow,
FortyFiveHydraAttackShadow, NinetyHydraAttackShadow,
OneHundredThirtyFiveHydraAttackShadow,
OneHundredEightyHydraAttackShadow,
TwoHundredTwentyFiveHydraAttackShadow,
TwoHundredSeventyHydraAttackShadow,
TreeHundredFifteenHydraAttackShadow
};
The HydraTileSetManager class has become quite large, so a detailed overview can be found in the repository. Here are the main aspects of its implementation:
Fetching animation tiles: Methods such as GetWalkAnimationTileSets, GetIdleAnimationTileSets, and GetAttackAnimationTileSets return the appropriate tiles for different character states.
Determining animation state: Methods like SetWalkState, SetIdleState, and SetAttackState define the animation state based on the current direction of movement or attack.
Selecting tiles: Each animation type corresponds to specific tiles, which are retrieved from the TileSets array by index.
Handling state transitions: The SetIdleState method manages transitions between animation states depending on the previous state.
Now, each tile set has an associated shadow tile set. This allows us to draw shadows below the Hydra sprite to give the effect of depth, making the character's movements and actions appear more realistic in a 2D environment.
![Hydra on tap animation with directions](https://static.wixstatic.com/media/1c5355_33db0c7024ac49dd9e3542f6426f5f64~mv2.gif/v1/fill/w_800,h_838,al_c,pstr/1c5355_33db0c7024ac49dd9e3542f6426f5f64~mv2.gif)
Summary
In this article, we implemented key movement mechanics and animation management for the Hydra character in .NET MAUI 🐉💻. We explored how to detect tap points, calculate movement directions, and implement smooth interpolation to make the movement feel natural and responsive 🎯✨. Additionally, we enhanced the visual depth of the game by adding shadows to the Hydra sprite, creating a more immersive experience 🌌🎨.
The HydraTileSetManager played a crucial role in streamlining animation handling, allowing us to define, retrieve, and transition between animation states such as walking, idling, and attacking ⚔️. By integrating state-dependent shadows, we further polished the visual quality of the game, ensuring that each movement and action feels realistic and dynamic. These improvements made the gameplay more dynamic and visually appealing, showcasing the potential of game development on MAUI 🚀🎮.
In the next part, we will shift our focus to implementing enemies 👾. This will include designing enemy behaviours, setting up animations, and integrating interactions between the Hydra and its adversaries, further advancing the gameplay experience 🔥. Stay tuned!
The full code from this article will be here 📂.
Commentaires