I added an animated blast radius for grenade, a line path for the throw, and icons for inventory switching and action selection.
Good job! That looks really polished.
I LOVE the line/ghost.
Can i ask what you are using to actually draw the line? I can picture the pathfinding being used to calculate where to draw it, but are you using a prefab of something to draw the line in play mode?
You can use the line renderer to draw lines. What Jonathan has done here is really nice and would probably not be the first thing you achieve, but the line renderer is quite powerful and with a but of practice you’d get there
This is the functional?
Yup, that’s the one.
I have used the line renderer in many different ways. It is quite versatile.
Perhaps @Jonathan_Borchardt will give more details on exactly what he did to achieve his effect.
I’ve actually never seen/used line renderer. So much of this is ‘you don’t know what you don’t know’, got a direction to play with it now.
Thanks very much.
Correct. I use the line renderer for both the ghost line and the projectile arc. If you want some code, I can post.
For the ghost line, I do this on mousemove:
List<GridPosition> path =
Pathfinding
.Instance
.FindPath(selectedUnit.GetGridPosition(),
mouseGridPosition,
out int pathLength);
if (path == null || path.Count() < 2)
{
return;
}
CopyUnitAsGhostIfNew (selectedUnit);
PositionGhost(path[path.Count() - 1].ToVector3(),
path[path.Count() - 2].ToVector3());
pathLineRenderer.positionCount = path.Count();
pathLineRenderer
.SetPositions(path
.Select(p =>
{
var v = p.ToVector3();
v.y += 0.1f;
return v;
})
.ToArray());
where pathLineRenderer is a serialized reference to a styled linerederer.
And for grenade arc:
public void PositionGrenadeSphere(
Vector3 start,
Vector3 end,
float size
)
{
isActive = true;
grenadeSphere.gameObject.SetActive(true);
grenadeSphere.transform.position = new Vector3(end.x, 0.25f, end.z);
grenadeSphere.localScale =
new Vector3(size * 2, size * 2, size * 2);
var pathPoints = new List<Vector3>();
var pointCount = 20f;
var endToStart = (end - start);
var dist = endToStart.magnitude + 0.5f;
var piece = endToStart / pointCount;
float maxHeight =
dist /
(1.93f + (-0.0714f * dist) + 0.0204f * Mathf.Pow(dist, 2));
for (int i = 0; i <= pointCount; i++)
{
var inner = start + piece * i;
// simple math for an arc that goes from 1 to 1 on x and y, starts a bit above the ground
inner.y =
Explosion
.Parabla(maxHeight, i / pointCount, 1f / maxHeight);
pathPoints.Add (inner);
}
grenadeLineRenderer.positionCount = pathPoints.Count;
grenadeLineRenderer.SetPositions(pathPoints.ToArray());
}
with:
public static float
Parabla(
float maxHeight,
//0-1 over time... this is x
float percentTraveled,
// allows starting a bit higher... 0.2f is waist high
float startHeightFactor = 0f
)
{
return maxHeight *
(
-Mathf
.Pow((
(percentTraveled * (2f - startHeightFactor)) -
(1f - startHeightFactor)
),
2) +
1
);
}
Where grenadesphere is a gameobject that rotates its texture.
The grenade ghost looks awesome, great job!
Not to ask you for reveal ALL your secrets, but how in the world do you get the outline around your range, is that also with the line renderer?
Are you doing it mathematically in the code?
i use line renderer to draw an arc using math to show where the grenade will fly.
i use a sphere to show the blast radius
i use the tile boundary as a game object to show the bounds ow where i could throw.
Is tile boundary a unity tool?
Like the outline around the edge of your walkable/throwable range?
ah…
the tile game object has 4 hidden sub objects that get turned on or off depending on if the tile next to it is inside or outside the range…
foreach (var gridPosition in boundsGridPositionList)
{
// adding +1's to shift to middle of array to deal with out of bounds
bool showTop =
gridPositionArray[gridPosition.x + 1, gridPosition.z + 2] ==
GridPositionContent.Empty;
bool showRight =
gridPositionArray[gridPosition.x + 2, gridPosition.z + 1] ==
GridPositionContent.Empty;
bool showBottom =
gridPositionArray[gridPosition.x + 1, gridPosition.z] ==
GridPositionContent.Empty;
bool showLeft =
gridPositionArray[gridPosition.x, gridPosition.z + 1] ==
GridPositionContent.Empty;
bool isTarget =
gridPositionArray[gridPosition.x + 1, gridPosition.z + 1] ==
GridPositionContent.Target;
gridSystemVisualSingleArray
.value[gridPosition.x,
gridPosition.z]
.Show(isTarget,
targetMaterial,
targetColor,
boundsMaterial,
boundsColor,
showTop,
showRight,
showBottom,
showLeft);
}
where show and hide are on sub objects of the tile object
public void Show(
bool isTarget,
Material targetMaterial,
Color targetColor,
Material boundsMaterial,
Color boundsColor,
bool showTop = false,
bool showRight = false,
bool showBottom = false,
bool showLeft = false
)
{
if (isTarget)
{
if (targetMaterial)
{
meshRenderer.material = targetMaterial;
}
meshRenderer.material.SetColor("_BaseColor", targetColor);
/*var targetParticleSystemMain = targetParticleSystem.main;
targetParticleSystemMain.startColor = targetColor;
targetParticleSystem.Play();
targetParticleSystem
.GetComponent<ParticleSystemRenderer>()
.renderMode = ParticleSystemRenderMode.Billboard;*/
}
else
{
if (boundsMaterial)
{
meshRenderer.material = boundsMaterial;
}
meshRenderer
.material
.SetColor("_BaseColor", boundsColor / 1.9f);
}
meshRenderer.enabled = true;
this.boundsColor = boundsColor;
top.gameObject.SetActive (showTop);
right.gameObject.SetActive (showRight);
bottom.gameObject.SetActive (showBottom);
left.gameObject.SetActive (showLeft);
}
public void Hide()
{
meshRenderer.enabled = false;
targetParticleSystem.Stop();
targetParticleSystem
.GetComponent<ParticleSystemRenderer>()
.renderMode = ParticleSystemRenderMode.None;
top.gameObject.SetActive(false);
right.gameObject.SetActive(false);
bottom.gameObject.SetActive(false);
left.gameObject.SetActive(false);
}
}
but there are many ways you can do this