DIABLO 3 CLONE - SPITE

This project was loaded with a lot of new challanges, i really got out of my comfort zone in this project. The game was the first game made in our groups own engine, that means an undefined pipeline at pre production, no tools from the get-go, which to me seems scary and challanging. But it turned out to be immensely satisfying to work with, and very educative.

The movement of diablo was something i wanted to get working as fast as possible. I started this project with a basic collision system with raycasts to AABB colliders. I also crated empty interface for a player system, that could recive an event whenever you clicked, with data from the raycast.

I replaced this aabb raycast movement with navmesh movement later

With all this in place i can actually start iterating movement and gameplay



This is the code for AABB vs ray collision shown in the gif above, in hindsight i could have done this with planes instead.

PLAYER ABILITIES

LEAP

I started with the leap abillity, this was very core to our game, and if we got it working as soon as possible, the level designers could design all their level's knowing you can leap from platform to platform. This abillity is also super useful when you get stuck under the navmesh during debugging

All the leap is, is a quadradic bezier curve, with the end point being the raycast hit point when you press 2 on your keyboard. When in the air, all other movement is disabled.

The best way of doing the leap at this stage was to use a quadradic bezier curve i thought. p0 is my position, p1 is between p0 and p2 but a few units up in the air, and p2 is where my raycast hits the navmesh.

This is the leap abillity in the game. It's located in it's own state, so that no other input can be done while he is in the air.

You might see that i actually lerp to the bezier point, this is because the bezier curve that was generated didint look organic when you traveled through it, but lerping to that position definetly helped.

There is also an ugly fail safe, that says that if the distance between the player and the last point in the bezier is < 100, we should exit leap. If i wouldnt do this, the player would fall through the world. It's not the best solution, but hey! it's only a few lines, and the leap works pretty good other than that.

Basic attack

Basic attack became pretty easy with the basic collision system in place. Also added some knockback to get some feedback.

Looping through all entities that are enemies and have colldiers, getting the collider, and doing the attack on the enemycomponent if the collision has happened. There are 2 init methods shown here, they are quite badly named unfortunatly. What they actually do is set the collider size and position at that frame, so we only do the collision check once.

Whirlwind

For the next abillity, whirlwind, it's pretty much the same code as the basic attack, but with a larger hit box, in a diffrent state in the state machine, and fired more often. Also when you're in the whirlwind state the character spins around it's up axis, and slows the movement speed down.

Having an aoe abillity like this, plus the leap gives alot of that great hack and slash action you love from diablo 3.

Shard blast

We needed a ranged abillity. Copy and pasting a little bit of code from the raycast in leap to get the target for the shards, then shooting an arc of shards with sphere colliders in that direction

To get the shards to shoot in an arc, i created this "Rotate around" method in my lerp class, fairly simple, you choose a forward, an axis relative to the forward, and a float in radians that determine how much you rotate around that axis. When using it on the player the axis became the global up vector, the toRot became his forward, and theta became the current number of shard to fire * player's arcdegrees / 180.

player's arcdegrees is a value that the leveldesigners can set so they can decide how wide the shardblast is.

Navmesh

There is alot of code for the navmesh, and this took a long time to get working. but once we got it working, all the abillities and movement was easily transferable to using navmesh instead of aabb colliders.


The level designers export the navmesh from unity to an obj file, That tool is from unity themselves. That obj file unfortunatley has some normals that we do not want to use. We changed that export tool to not export the normals, so that we can load the navmesh faster in game.

This is the code for loading in the vertices. When you work with an obj file, it is very easy to read the data compared to an fbx. So for every line with "v" we can find a vertex point on the navmesh.

The commented lines, called trash, trash2, trash3 etc, are those normal values that we didint want. This code is not the for the vertices themselvs, but for their neighbours.

This is as far as i worked with importing the navmesh, my good friend fredrick, who i've worked with alot, started working on getting the neighbours connected, so the ai can pathfind to diffrent parts on the mesh.

While he did that, i needed to be able to raycast to the navmesh triangles for the movement

Currently this method returns a path because the player actually travels a path, but in the beginning it did not do that, it just returned a point on the navmesh. I loop through the entire graph and i do IntersectRayTriangle with every triangle on the navmesh (not very efficient).

Raycasting against a triangle in theory is quite easy.

  • Create a plane from the 3 points on the triangle

  • Raycast against that plane

  • Check if the raycast point is inside the triangle

Because i already did a raycast against a AABB, i thought it was going to be easy. It took me a long time to actually get this working though.

After alot of googling and reading articles, i managed to get it working.

Back then, i made some comments in the code for me to have a better understanding when i look back at this in the future.

When it returns true, the outposition reference is changed, and i have a point to walk to!

When we change this to A*, we just set that point as the destination, and did the pathfinding towards that triangle we hit.