Я ударил голову о стене здесь->
Задача: для моего маленького проекта (изометрический с наклонной камерой) я хотел бы позволить игроку создать индивидуальную область для его способностей, Это означает, что откуда он активирует навык, он может перетаскивать мышь и создать пользовательскую область (например, индикатор, чтобы указать, какую область будет покрывать навык), с фиксированной шириной и максимальной длиной, связанной с максимальной длиной навыка. Начальная точка должна быть внутри его максимального радиуса (но он может продлить его далеко). Я создал из него сборник.
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>
Введение: < /p>
public struct FreeAOEIndicatorResult
{
public bool IsDrawingFinished;
public float TotalLength;
}
< /code>
Простая структура как oupput spelltemplate.spellindicator.updatefreeaeOndicatorWithSpline () Чтобы проверить, когда условие выполняется, и цикл может быть вырезан. < /p>
Поведение является следующим: игрок нажимает левую кнопку мыши (на данный момент) и создает экземпляры сетки Prefab [b], если [/b] выбранная точка находится внутри заклинания (расстояние от игрока). Это условие, чтобы закрыть сначала во время петли. Второй цикл должен просто добавить точки, записанные из движения мыши и добавить их в список контрольных точек, чтобы создать сетку из сплайна. Выпадение от него, откуда была выпущена левая мышь или длина сетки больше, чем максимальная длина заклинания.
используемые методы помещаются в скрипт SpellTemplate.spellindicator и являются следующими: < /p>
Первый метод: проверьте, попал ли Raycast точку внутри радиуса, сосредоточенного на игроке с Radius = spellRadius < /p>
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>
Создать визуальное представление точки, нажавшего Raycasting < /p>
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>
Просто создайте сетку сейчас < /p>
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>
Просто установите холст Maxradius для визуального представления максимального радиуса < /p>
public void InitializeFreeAOE(float spellRadius)
{
spellCastRadiusCanvas.SetActive(true);
_spellCastRadiusCanvasRectTransform.sizeDelta = new Vector2(spellRadius * 2f, spellRadius * 2f);
_spellCastRadiusImageRect.sizeDelta = new Vector2(spellRadius * 2f, spellRadius * 2f);
}
Теперь, в основном цикле, мы должны застрять в позиции мыши, сохранить точки и отправить их в создатель сплайна и создать сетку с шириной и вернуть, если длину> maxlength
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, curvePoints);
}
totalLength = length;
// our condition to cut the loop
if (totalLength >= maxLength)
{
return new FreeAOEIndicatorResult { IsDrawingFinished = true, TotalLength = totalLength };
}
return new FreeAOEIndicatorResult { IsDrawingFinished = true, TotalLength = totalLength };
}
< /code>
Теперь следуют методам создания кривой и создания сетки вокруг нее < /p>
public List GenerateCatmullRomCurve(List controlPoints, int resolution)
{
List curvePoints = new ();
meshFilter.mesh = mesh;
}
< /code>
Теперь я совершенно уверен, что способ создания кривой и сетки верны, все же, когда я запускаю игру, Raycast правильно попадает в плоскость, как на следующем изображении:
Как видите, желтая сфера правильно расположена Полем Но когда я перетаскиваю мышь, сетка создана намного далекая с этого момента, как на следующем изображении:
< /p>
Теперь я тоже пытался с Chatgpt, но через несколько часов я не могу понять, почему это ведет себя так. Я подозреваю, что это привязано к первым 4 баллам: на самом деле первая кривая создается после того, как записано 4 балла. Причина, по которой он начинается там, а не в отправной точке, мне не подходит. Ты поможешь мне, пожалуйста? Если вам нужна дополнительная информация, я могу интегрировать ... Я думаю, что поместил сюда ядро.
Большое спасибо! < /P>
Я ударил голову о стене здесь-> Задача: для моего маленького проекта (изометрический с наклонной камерой) я хотел бы позволить игроку создать индивидуальную область для его способностей, Это означает, что откуда он активирует навык, он может перетаскивать мышь и создать пользовательскую область (например, индикатор, чтобы указать, какую область будет покрывать навык), с фиксированной шириной и максимальной длиной, связанной с максимальной длиной навыка. Начальная точка должна быть внутри его максимального радиуса (но он может продлить его далеко). Я создал из него сборник.[code]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> Введение: < /p> public struct FreeAOEIndicatorResult { public bool IsDrawingFinished; public float TotalLength; } < /code> Простая структура как oupput spelltemplate.spellindicator.updatefreeaeOndicatorWithSpline () Чтобы проверить, когда условие выполняется, и цикл может быть вырезан. < /p> Поведение является следующим: игрок нажимает левую кнопку мыши (на данный момент) и создает экземпляры сетки Prefab [b], если [/b] выбранная точка находится внутри заклинания (расстояние от игрока). Это условие, чтобы закрыть сначала во время петли. Второй цикл должен просто добавить точки, записанные из движения мыши и добавить их в список контрольных точек, чтобы создать сетку из сплайна. Выпадение от него, откуда была выпущена левая мышь или длина сетки больше, чем максимальная длина заклинания. используемые методы помещаются в скрипт SpellTemplate.spellindicator и являются следующими: < /p> Первый метод: проверьте, попал ли Raycast точку внутри радиуса, сосредоточенного на игроке с Radius = spellRadius < /p> 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;
return false; } < /code> Создать визуальное представление точки, нажавшего Raycasting < /p> 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> Просто создайте сетку сейчас < /p> 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> Просто установите холст Maxradius для визуального представления максимального радиуса < /p> 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] Теперь, в основном цикле, мы должны застрять в позиции мыши, сохранить точки и отправить их в создатель сплайна и создать сетку с шириной и вернуть, если длину> maxlength 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 = true, TotalLength = totalLength }; } < /code> Теперь следуют методам создания кривой и создания сетки вокруг нее < /p> public List GenerateCatmullRomCurve(List controlPoints, int resolution) { List curvePoints = new ();
meshFilter.mesh = mesh; } < /code> Теперь я совершенно уверен, что способ создания кривой и сетки верны, все же, когда я запускаю игру, Raycast правильно попадает в плоскость, как на следующем изображении:
Как видите, желтая сфера правильно расположена Полем Но когда я перетаскиваю мышь, сетка создана намного далекая с этого момента, как на следующем изображении: < /p> Теперь я тоже пытался с Chatgpt, но через несколько часов я не могу понять, почему это ведет себя так. Я подозреваю, что это привязано к первым 4 баллам: на самом деле первая кривая создается после того, как записано 4 балла. Причина, по которой он начинается там, а не в отправной точке, мне не подходит. Ты поможешь мне, пожалуйста? Если вам нужна дополнительная информация, я могу интегрировать ... Я думаю, что поместил сюда ядро. Большое спасибо! < /P>