Aufgabe: Für mein kleines Projekt (isometrisch mit geneigter Kamera) möchte ich dem Spieler einen benutzerdefinierten Wirkungsbereich für seine Fähigkeit erstellen lassen, Das heißt, wenn er die Fertigkeit aktiviert, kann er die Maus ziehen und einen benutzerdefinierten Bereich erstellen (z. B. einen Indikator, der angibt, welchen Bereich die Fertigkeit abdeckt), mit fester Breite und maximaler Länge, die an die maximale Länge der Fertigkeit gebunden ist. Der Startpunkt sollte innerhalb seines maximalen Radius liegen (er kann ihn aber weit erweitern).
Meine Idee war also, ein Mesh zu verwenden (ein Spielobjekt mit einem Mesh-Filter und einem Mesh-Renderer). Ich habe daraus ein vorgefertigtes Objekt erstellt.
Um die Schleife zu bewältigen, habe ich hier diesen asynchronen Task, der wartet, bis die Bedingungen erfüllt sind:
Code: Select all
public class FreeAOEState : SpellState
{
public override async Task ExcecuteState(SpellTemplate spellTemplate)
{
var spellRadius = spellTemplate.GetParameter(SpellParameterType.SpellRange);
var maxLength = spellTemplate.GetParameter(SpellParameterType.AreaSize);
var width = spellTemplate.GetTargetParameter(SpellTargetFeatures.Width);
bool isDrawningStarted = false;
MeshFilter meshFilter = new();
// control points for spline
List controlPoints = new();
float totalLength = 0f;
spellTemplate.spellIndicator.InitializeFreeAOE(spellRadius);
while(!isDrawningStarted)
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
if(spellTemplate.spellIndicator.StartFreeAOEDrawning(spellRadius,
out Vector3 startPoint, controlPoints))
{
spellTemplate.spellIndicator.InstantiateFreeAOE(startPoint, out meshFilter);
isDrawningStarted = true;
}
}
await Task.Yield();
}
var time = 0f;
while (time < 2f * Config.MAX_TIME_READY_SPELL)
{
time += Time.deltaTime;
var result = spellTemplate.spellIndicator.UpdateFreeAEOIndicatorWithSpline(
width,
maxLength,
ref totalLength,
controlPoints,
meshFilter);
if (Input.GetKeyUp(KeyCode.Mouse0) || result.IsDrawingFinished)
{
spellTemplate.SpellEffect();
await Task.Delay(400);
break;
}
await Task.Yield();
}
spellTemplate.spellIndicator.HideCursor();
}
}
Code: Select all
public struct FreeAOEIndicatorResult
{
public bool IsDrawingFinished;
public float TotalLength;
}
Das Gesuchte Das Verhalten ist wie folgt: Der Spieler klickt (vorerst) mit der linken Maustaste und instanziiert das vorgefertigte Netz, wenn der ausgewählte Punkt innerhalb des SpellRadius (Entfernung vom Spieler) liegt. Das ist die Bedingung, um zuerst die while-Schleife zu schließen. Die zweite Schleife sollte einfach die von der Mausbewegung aufgezeichneten Punkte hinzufügen und sie zur Liste „controlPoints“ hinzufügen, um das Netz aus dem Spline zu erstellen. Brechen Sie davon ab, wenn die linke Maustaste losgelassen wurde oder die Länge des Netzes größer als die maximale Länge des Zaubers ist.
Die verwendeten Methoden werden im Skript „spellTemplate.spellIndicator“ platziert und sind die folgenden:
Erste Methode: Überprüfen Sie, ob der Raycast einen Punkt innerhalb des Radius trifft, der auf dem Spieler mit Radius = SpellRadius zentriert ist
Code: Select all
public bool StartFreeAOEDrawning(float spellRadius, out Vector3 startPoint,
List controlPoints)
{
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
startPoint = Vector3.zero;
var playerPos = Player.Instance.playerTransform.position;
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.green, 2f); // visual debug
if (Physics.Raycast(ray, out RaycastHit hitInfo, Mathf.Infinity))
{
if (Vector3.Distance(hitInfo.point, playerPos) > spellRadius)
{
return false;
}
startPoint = hitInfo.point;
controlPoints.Add(startPoint);
DrawHitPoint(hitInfo.point);
return true;
}
return false;
}
Code: Select all
private void DrawHitPoint(Vector3 hitPoint)
{
// yellow sphere to check impact point
GameObject hitMarker = GameObject.CreatePrimitive(PrimitiveType.Sphere);
hitMarker.transform.position = hitPoint;
hitMarker.transform.localScale = Vector3.one * 0.3f;
hitMarker.GetComponent().material.color = Color.yellow;
Destroy(hitMarker, 2f);
}
Code: Select all
public void InstantiateFreeAOE(Vector3 startPoint, out MeshFilter meshFilter)
{
_currentArea = Instantiate(meshPrefabToInstantiate, startPoint, Quaternion.identity);
_currentArea.transform.position = startPoint;
if (_currentArea.TryGetComponent(out meshFilter))
{
meshFilter.mesh = new Mesh();
_currentMeshFilter = meshFilter;
}
else
{
meshFilter = null;
}
}
Code: Select all
public void InitializeFreeAOE(float spellRadius)
{
spellCastRadiusCanvas.SetActive(true);
_spellCastRadiusCanvasRectTransform.sizeDelta = new Vector2(spellRadius * 2f, spellRadius * 2f);
_spellCastRadiusImageRect.sizeDelta = new Vector2(spellRadius * 2f, spellRadius * 2f);
}
Code: Select all
public FreeAOEIndicatorResult UpdateFreeAEOIndicatorWithSpline(
float width,
float maxLength,
ref float totalLength,
List controlPoints,
MeshFilter meshFilter)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity))
{
Vector3 currentPoint = hit.point;
if (controlPoints.Count > 0)
{
float distance = Vector3.Distance(controlPoints[^1], currentPoint);
if (distance > 0.5f)
{
controlPoints.Add(currentPoint);
}
}
//here, we're generating the curve
var curvePoints = GenerateCatmullRomCurve(controlPoints, 20);
// here we draw the curve
DrawMeshFromCurve(curvePoints, width, meshFilter);
// retrieve the length
float length = 0f;
for (int i = 1; i < curvePoints.Count; i++)
{
length += Vector3.Distance(curvePoints[i - 1], curvePoints[i]);
}
totalLength = length;
// our condition to cut the loop
if (totalLength >= maxLength)
{
return new FreeAOEIndicatorResult { IsDrawingFinished = true, TotalLength = totalLength };
}
return new FreeAOEIndicatorResult { IsDrawingFinished = false, TotalLength = totalLength };
}
return new FreeAOEIndicatorResult { IsDrawingFinished = true, TotalLength = totalLength };
}
Code: Select all
public List GenerateCatmullRomCurve(List controlPoints, int resolution)
{
List curvePoints = new ();
for (int i = 0; i < controlPoints.Count - 3; i++)
{
Vector3 p0 = controlPoints[i];
Vector3 p1 = controlPoints[i + 1];
Vector3 p2 = controlPoints[i + 2];
Vector3 p3 = controlPoints[i + 3];
for (int j = 0; j < resolution; j++)
{
float t = j / (float)resolution;
curvePoints.Add(CatmullRom(p0, p1, p2, p3, t));
}
}
return curvePoints;
}
private Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
float t2 = t * t;
float t3 = t2 * t;
return 0.5f * ((2f * p1) +
(-p0 + p2) * t +
(2f * p0 - 5f * p1 + 4f * p2 - p3) * t2 +
(-p0 + 3f * p1 - 3f * p2 + p3) * t3);
}
void DrawMeshFromCurve(List curvePoints, float width, MeshFilter meshFilter)
{
if (curvePoints.Count < 2)
return;
int maxPoints = 1000;
if (curvePoints.Count > maxPoints)
{
curvePoints = curvePoints.GetRange(0, maxPoints);
}
int numSegments = curvePoints.Count - 1;
int[] triangles = new int[numSegments * 6];
Vector3[] vertices = new Vector3[curvePoints.Count * 2];
for (int i = 0; i < curvePoints.Count; i++)
{
Vector3 direction = (i < curvePoints.Count - 1) ?
(curvePoints[i + 1] - curvePoints[i]).normalized :
(curvePoints[i] - curvePoints[i - 1]).normalized;
Vector3 offset = Vector3.Cross(direction, Vector3.up) * (width / 2);
vertices[i * 2] = curvePoints[i] - offset;
vertices[i * 2 + 1] = curvePoints[i] + offset;
if (i < curvePoints.Count - 1)
{
int baseIndex = i * 2;
triangles[i * 6] = baseIndex;
triangles[i * 6 + 1] = baseIndex + 1;
triangles[i * 6 + 2] = baseIndex + 2;
triangles[i * 6 + 3] = baseIndex + 1;
triangles[i * 6 + 4] = baseIndex + 3;
triangles[i * 6 + 5] = baseIndex + 2;
}
}
Mesh mesh = new()
{
vertices = vertices,
triangles = triangles
};
mesh.RecalculateNormals();
meshFilter.mesh = mesh;
}

Wie Sie sehen können, ist die gelbe Kugel korrekt positioniert . Aber wenn ich die Maus ziehe, wird das Netz weit entfernt von diesem Punkt erstellt, wie im folgenden Bild:
Jetzt habe ich es auch mit chatgpt versucht, aber nach mehreren Stunden kann ich nicht verstehen, warum es sich so verhält. Ich vermute, dass es mit den ersten 4 Punkten zusammenhängt: Tatsächlich wird die erste Kurve erstellt, nachdem 4 Punkte aufgezeichnet wurden. Der Grund, warum es dort beginnt und nicht am Ausgangspunkt, ist mir ein Rätsel. Könnten Sie mir bitte helfen? Wenn Sie weitere Informationen benötigen, kann ich sie integrieren ... Ich glaube, ich habe hier den Kern eingefügt.
Vielen Dank!