Movement area outline
Hi
I´m working on a turn based strategy game. Right now I display the area a character can move using projectors (for terrain adaptability of the tiles). Every tile is an empty with a projector attached and every projector within the movement area is being turned on to display a single tile (see screenshot). You can imagine that this is really unperformant (I read something about that a mesh has to be rendered again for every projector it receives).
I wanted to just make an outline (by using a line renderer for example) of the area where a character can move (similar to XCOM 2) have no idea how to do that. Can anyone help? The area is stored within an array of transforms.
If it somehow helps here's the code which generates the area:
using UnityEngine;
using System.Collections;
namespace Grid
{
[System.Serializable]
public class AreaBuilder: MonoBehaviour, AreaBuilderInterface
{
public Grid grid;
public ArrayList traversed;
public LayerMask layerMask;
public void buildArea(Vector3 start, int min, int max)
{
GameObject startTile = this.grid.getTileAtPosition(start.x, start.z);
ArrayList openList = new ArrayList();
openList.Add(startTile);
this.traversed = new ArrayList();
ArrayList withinMinArea = new ArrayList();
this.traverseTiles(openList, withinMinArea, min, max, true);
if(this.traversed != null)
{
for (int i = 0; i < this.traversed.Count; i++)
{
GameObject currentCell = (GameObject)this.traversed[i];
currentCell.GetComponent<Projector>().enabled = true;
}
}
}
private void traverseTiles(ArrayList openList, ArrayList withinMinArea, int min, int max, bool useRaycast)
{
if(max == 0)
{
return;
} else
{
ArrayList tempOpenList = new ArrayList();
for (int i = 0; i < openList.Count; i++)
{
GameObject currentTile = (GameObject) openList[i];
Tile.AbstractTileController projectorTile = currentTile.GetComponent<Tile.AbstractTileController>();
for (int j = 0; j < projectorTile.neighbors.Count; j++)
{
GameObject currentNeighbor = (GameObject) projectorTile.neighbors[j];
if (currentNeighbor == null)
{
continue;
}
if (useRaycast)
{
RaycastHit hit;
Vector3 currentCorrectedPosition = new Vector3(currentTile.transform.position.x,
currentTile.transform.position.y + 3f, currentTile.transform.position.z);
Vector3 neighborCorrectedPosition = new Vector3(currentNeighbor.transform.position.x,
currentTile.transform.position.y + 3f, currentNeighbor.transform.position.z);
Debug.DrawLine(currentCorrectedPosition, neighborCorrectedPosition, Color.red, 60);
Vector3 checkDirection = neighborCorrectedPosition - currentCorrectedPosition;
int ignoreLayer = ~(1 << 9);
Ray ray = new Ray();
ray.origin = currentCorrectedPosition;
ray.direction = checkDirection;
if (!Physics.Raycast(ray, out hit, Vector3.Distance(currentCorrectedPosition, neighborCorrectedPosition), this.layerMask))
{
if (!this.traversed.Contains(currentNeighbor))
{
this.traversed.Add(currentNeighbor);
}
if (!withinMinArea.Contains(currentNeighbor) && min < max)
{
withinMinArea.Add(currentNeighbor);
}
if (!tempOpenList.Contains(currentNeighbor))
{
tempOpenList.Add(currentNeighbor);
}
}
}
}
}
max--;
this.traverseTiles(tempOpenList, withinMinArea, min, max, useRaycast);
}
}
public void dropArea()
{
foreach(GameObject tile in this.traversed)
{
tile.GetComponent<Projector>().enabled = false;
}
}
}
}
Answer by ricke44654 · Jun 01, 2016 at 03:54 AM
Is your array of transforms a series of point locations that define the perimeter of the area? If so, then it seems like you need a script that would loop through your transform array and call the LineRenderer.SetPosition method similar to the following:
lineRenderer.SetPosition(0, new Vector3(Position1.transform.position.x,Position1.transform.position.y,Position1.transform.position.z));
lineRenderer.SetPosition(1, new Vector3(Position2.transform.position.x,Position2.transform.position.y,Position2.transform.position.z));
So you'd probably end up with code like the following:
int posCount = 0;
foreach (var curTransform in transformArray) {
lineRenderer.SetPosition(posCount, new Vector3(curTransform.position.x, curTransform.position.y, curTransform.position.z));
posCount++;
}
According to the Unity docs, LineRenderer should be the only renderer on a game object, so you might need a child game object if your game object for your area has other responsibilities. Hope this helps.
I guess I didn't specify it enough. The array (or ArrayList in that case) containing the transforms contains every single tile the player can move to (it's simpler to check if the mouse is hovering over a valid position with movementArea.Contains(position)), not only the area edges. $$anonymous$$y main problem is how to detect those edges. I mean I could make an extra array and fill it every time i reach the maximum range or if not all tile neighbours are accessible but how do I guarantee the order of the tiles so that I get a nice straight outline and not a wild amalgamation of lines. Sure I could sort them but by what?
Ah, so it's the whole area... got it. :) One thought would be to go through the transforms array and find the transforms with the smallest / largest values on the x and z axes. That should be the tips of your area, correct? Then you'd loop through that list to use those points for rendering your lines.
$$anonymous$$ind of. But the area also adapts if there are obstacles within it. I think I got an idea. I'll test it on weekend and describe it if it worked.
So.. I made some progress:
But It's still not entirely what I want. I'll post the current algorithm later for other people with that problem. It's probably not the best approach but it's fast enough for me and it works (until I find a case when it won't wort xD) the area looks cut off on the left and down side because it hit the border of the grid.
Your answer
Follow this Question
Related Questions
Moving object inside another object's area 1 Answer
Turn Based Movement - Using Movement Points 0 Answers
turn-based movement 0 Answers
Somersault kick does not work 0 Answers
Problems syncing players in multiplayer 0 Answers