Newer
Older
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using UnityEditorInternal;
using UnityEngine.Rendering;
private Vector2 position, attractor, repulsor, prefDirection;
private int actionRadius;
private bool useCustomRadius = false;
private List<Vector2> borderPart;
private System.Func<Vector2, bool> borderFilter;
public int Tokens { get => tokens; }
public int ActionRadius { get => actionRadius; }
public CoastlineAgent(CoastlineGenerator generator, Vector2 position, int tokens, List<Vector2> borderPart, int actionRadius = -1)
this.tokens = tokens;
this.tokensRemaining = tokens;
this.position = position;
this.actionRadius = actionRadius;
this.borderPart = borderPart;
// calculate preferred direction
Vector2 tangent = (borderPart[borderPart.Count - 1] - borderPart[0]).normalized;
Vector2 bitangent = new Vector2(-tangent.y, tangent.x);
if(bitangent == Vector2.zero) // in case of the first agent, just create any random pref direction
{
prefDirection = Random.insideUnitCircle;
prefDirection.Normalize();
}
else
{
prefDirection = RotateVector(bitangent, Random.Range(-70.0f, 70.0f));
}
if (this.actionRadius > 0)
{
useCustomRadius = true;
borderFilter = p => (p - position).sqrMagnitude <= actionRadius * actionRadius;
}
else
{
borderFilter = p => (p - position).sqrMagnitude <= coastlineGenerator.SqrActionRadius;
}
// create random attractor / repulsor and make sure they point at different directions
Vector2 direction = Random.insideUnitCircle * coastlineGenerator.ActionRadius;
attractor = position + direction;
repulsor = position + RotateVector(-direction, Random.Range(-90f, 90));
}
public bool UpdateLand()
{
if (tokensRemaining <= 0) return false;
Vector2 pos = GetRandomPosition();
if(pos == new Vector2(0, 0))
{
tokensRemaining--;
return true;
}
Vector2 initPos = new Vector2(-1, -1);
Vector2 highestPos = initPos;
float highestScore = 0.0f;
// score
for (int x = -1; x <= 1; x++)
for (int y = -1; y <= 1; y++)
{
Vector2 currPos = new Vector2((int)pos.x + x, (int)pos.y + y);
if (!PositionInBounds(currPos)) continue;
int currIndex = (int)(currPos.x + currPos.y * coastlineGenerator.TerrainSize);
if (coastlineGenerator.landBitField[currIndex]) continue;
float score = Score(currPos);
if (score > highestScore || highestPos == initPos)
{
highestScore = score;
highestPos = currPos;
}
}
if (highestPos == initPos || pos == Vector2.zero)
{
// couldn't find a decent neighbor
tokensRemaining--;
return true;
}
{
coastlineGenerator.coastText.SetPixel((int)highestPos.x, (int)highestPos.y, Color.grey);
int highestIndex = (int)highestPos.x + (int)highestPos.y * coastlineGenerator.TerrainSize;
coastlineGenerator.landBitField[highestIndex] = true;
coastlineGenerator.borderPoints.Add(highestPos);
if (!borderPart.Contains(highestPos)) borderPart.Add(highestPos);
coastlineGenerator.RemoveNonBoundaryPixels(highestPos);
}
int terrainSize = coastlineGenerator.TerrainSize;
float mapHorizDist = (pos.x < terrainSize * 0.5f ? pos.x : terrainSize - pos.x);
float mapVertDist = (pos.y < terrainSize * 0.5f ? pos.y : terrainSize - pos.y);
float mapDist = Mathf.Min(mapHorizDist, mapVertDist);
return ((repulsor - pos).sqrMagnitude * 1f - (attractor - pos).sqrMagnitude * 1f) + 3 * mapDist * mapDist;
}
private bool PositionInBounds(Vector2 pos)
{
return pos.x >= 0 || pos.y >= 0 || pos.x <= (coastlineGenerator.TerrainSize - 1) || pos.y <= (coastlineGenerator.TerrainSize - 1);
}
private bool PositionOnEdge(Vector2 pos)
{
return pos.x == 0 || pos.y == 0 || pos.x == (coastlineGenerator.TerrainSize - 1) || pos.y == (coastlineGenerator.TerrainSize - 1);
}
public Vector2 GetRandomPosition()
{
if (borderPart.Count == 0)
{
//Debug.Log("borderpart count is zero!");
return Vector2.zero;
}
int randIndex = Random.Range(0, borderPart.Count);
Vector2 pos = borderPart[randIndex];
if (PositionOnEdge(pos)) return Vector2.zero;
bool inland = true;
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
if (x == 0 && y == 0) continue;
if (!PositionInBounds(pos + new Vector2(x, y))) continue;
int index = (int)((pos.x + x) + (pos.y + y) * coastlineGenerator.TerrainSize);
if (!coastlineGenerator.landBitField[index])
{
inland = false;
break;
}
}
if(!inland)
{
break;
}
}
if (inland)
{
// not a valid border point anymore, remove
borderPart.Remove(pos);
coastlineGenerator.borderPoints.Remove(pos);
Vector2 oldPos = pos;
// find new border point in preferred direction
pos = TraverseToCoastline(oldPos);
//pos = new Vector2(-1, -1);
if(pos == new Vector2(-1, -1))
{
return Vector2.zero;
}
else if (!borderPart.Contains(pos)) borderPart.Add(pos);
}
return pos;
}
public Vector2 TraverseToCoastline(Vector2 startPos)
{
Vector2 pos = new Vector2(-1, -1);
if (PositionOnEdge(startPos)) return pos;
Vector2 prevPos = pos;
bool foundCoastPoint = false;
int maxCounter = 0;
Vector2Int newPos = new Vector2Int((int) (oldPos.x + prefDirection.x), (int) (oldPos.y + prefDirection.y));
if (!PositionInBounds(pos)) return pos;
int idx = coastlineGenerator.TerrainSize * newPos.y + newPos.x;
if(!coastlineGenerator.landBitField[idx])
{
foundCoastPoint = true;
pos = prevPos;
}
else
{
//Debug.Log("not a coastline!");
//Debug.Log("pref dir x: " + prefDirection.x);
//Debug.Log("pref dir y: " + prefDirection.y);
}
prevPos = newPos;
oldPos = oldPos + prefDirection;
//foundCoastPoint = coastlineGenerator.borderPoints.Contains(possibleCoastPoint);
//if (foundCoastPoint) pos = possibleCoastPoint;
while (!foundCoastPoint && maxCounter < 100);
return pos;
}
private static Vector2 RotateVector(Vector2 v, float degrees)
{
float rad = degrees * Mathf.Deg2Rad;
float ca = Mathf.Cos(rad);
float sa = Mathf.Sin(rad);
return new Vector2(ca * v.x - sa * v.y, sa * v.x + ca * v.y);