Skeletons go down!

Published January 11, 2017
Advertisement

But not without a fight...

A bit of gameplay video + code snippet

Sunday was to be the big day when I finally killed the first skeleton. Then I moved on to have skeletons fight back and kill players.
Still the AI simply walks around randomly while checking to see if any players are in melee range and then stops to hit them.
It actually surprised me how easy it was to make the skeletons attack and how few lines of code it took. Having the engine handle the death of a player character, "respawn" him (resetting health and position) and then making sure messages are sent correctly to the client was actually more of a challenge: even though that also took only a few lines of code it needed a bit more tinkering that I expected.
I'm thinking about trying out a behaviour tree for the AI but I really don't think I need it yet.
Instead I'm structuring my behaviour code in such a way that it can be converted to a tree with only a little work. Explicit methods are a bit easier to work with and I also expect them to be quite a bit faster. But code reuse is harder because the "tree" formed by the calling structure is hardcoded. While reuse of branching is just as easy you can't just make a new tree based on an old one by substituting a few child branches.

An example of the simple AI - a custom Behaviour class that is attached to an Entity - already somewhat structurally prepared for added pathfinding logic.
Notice this is server code, but I still have an animation class. It has a duration and an Animate() method that is called each game loop as long as it isn't finished. No actual "animation" actually takes place on the server of course but a running animation stops the player from thinking, and also enables running code at specified points of time during the animation. That way weapon attacks can be implemented that do hit checks halfway through the swing - or at the end - or do collision checks every frame while taking care not to hit the same entity twice.
In addition to MeleeAnimation I also had to implement BeenHitAnimation (simply prevents the player from thinking or acting while it plays) and DeathAnimation.

[source]
public override void Think(GameUpdate update)
{
if (Attack(update))
return;
if (MoveToPlayer(update))
return;
if (_entity.NextGeometry.Speed == 0 || _entity.Geometry.Pos.Equals(_entity.Geometry.Destination) && (_entity.Path == null || _entity.Path.Count == 0))
{
Point2D newDestination = GetRandomPosition();
try
{
paths++;
_entity.Path = _entity.Section.Spatial.ConnectivityIndex.GetShortestPathAStarWithGridLineOfSightMaxLengthGridReflex(_entity.Geometry.Pos, newDestination, maxDistance);
if (_entity.Path == null)
failedPaths++;
}
catch (Exception ex)
{
Console.WriteLine(_entity.Geometry.Pos + "->" + newDestination);
Console.WriteLine(ex);
}
_entity.NextGeometry.Speed = _entity.MaxSpeed;
}
}

private bool MoveToPlayer(GameUpdate update)
{
return false;//TODO: Implement
}

private bool Attack(GameUpdate update)
{
Entity victim = FindVictim();
if (victim == null)
return false;
_entity.TurnTo(victim.Geometry.Pos);
_entity.Animation = new MeleeAnimation(_entity);
return true;

}

private Entity FindVictim()
{
var victims = _entity.Section.Spatial.GetEntities(_entity.Geometry.Pos, _entity.AttackRange);

foreach (var victim in victims)
{
//check if enemy is targetable. Don't hit yourself...
if (victim == _entity || victim.IsDead)
continue;
if (victim is PlayerCharacter)
return victim;
}
return null;
}

private Point2D GetRandomPosition()
{
var grid = _entity.Section.Spatial.GridIndex;
int x = (int)(_entity.Geometry.Pos.X);
int y = (int)(_entity.Geometry.Pos.Y);
int xMin = Math.Max(x - maxDistance, grid.xMin);
int yMin = Math.Max(y - maxDistance, grid.yMin);
int xMax = Math.Min(x + maxDistance, grid.xMax);
int yMax = Math.Min(y + maxDistance, grid.yMax);

Point2D pos;
do
{
pos = new Point2D(r.Next(xMin, xMax), r.Next(yMin, yMax));
} while ( VectorMath.DistanceSquared(_entity.Geometry.Pos, pos) > maxDistance * maxDistance ||
!grid.IsOnMesh(pos));
return pos;
}

[/source]

I also went ahead and bought the Barbarian RPG pack from InfinityPBR - a big thank you to Fidelum Games for pointing me in that direction.
Mecanim is totally new for me and a bit confusing so I haven't worked on incorporating the characters into my game but it's gonna be great when it happens.

2 likes 4 comments

Comments

JEJoll

Looks like you're making quick progress, good job! And you're welcome :D. Thanks for the mention.

What are you using as an engine?

P.S. Holy cow that's a long method name: GetShortestPathAStarWithGridLineOfSightMaxLengthGridReflex(). Glad I'm not the only one who favors this kind of thing :p

January 18, 2017 04:25 PM
Polydone
Thanks - and you're also welcome:=)
I'm using Unity for the client, and the server is just C#.

Yeah I know the method name is long:) but I have several versions of my pathfinding pathfinding method. The worst part is actually not the A* algorithm itself, but rather the code that connects start and endpoints to viable points in the connectivity graph (similar to a navmesh but different concept) That's why I include everything in the names that sets them apart from the other versions. Eventually I will just keep the one that works the fastest and simplify the name:)
January 20, 2017 07:41 AM
Navyman

What are you using to record the screen?

Have you tried OBS?

February 02, 2017 03:40 AM
Polydone

What are you using to record the screen?

Have you tried OBS?

This is in fact recorded using OBS, but my computer is not the most powerful in the world, and the GPU is just the Intel on the MB.

February 02, 2017 07:16 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement