Unity: успешно создавать сетку из ввода игрока как индикаторC#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Unity: успешно создавать сетку из ввода игрока как индикатор

Сообщение Anonymous »

Я ударил голову о стене здесь->
Задача: для моего маленького проекта (изометрический с наклонной камерой) я хотел бы позволить игроку создать индивидуальную область для его способностей, Это означает, что откуда он активирует навык, он может перетаскивать мышь и создать пользовательскую область (например, индикатор, чтобы указать, какую область будет покрывать навык), с фиксированной шириной и максимальной длиной, связанной с максимальной длиной навыка. Начальная точка должна быть внутри его максимального радиуса (но он может продлить его далеко). Я создал из него сборник.

Код: Выделить всё

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 = false, TotalLength = totalLength };
}

return new FreeAOEIndicatorResult { IsDrawingFinished = true, TotalLength = totalLength };
}
< /code>
Теперь следуют методам создания кривой и создания сетки вокруг нее < /p>
public List GenerateCatmullRomCurve(List controlPoints, int resolution)
{
List curvePoints = new ();

for (int i = 0; i < controlPoints.Count - 3; i++)
{
Vector3 p0 = controlPoints;
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).normalized :
(curvePoints - curvePoints).normalized;

Vector3 offset = Vector3.Cross(direction, Vector3.up) * (width / 2);
vertices[i * 2] = curvePoints - offset;
vertices[i * 2 + 1] = curvePoints + 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;
}
< /code>
Теперь я совершенно уверен, что способ создания кривой и сетки верны, все же, когда я запускаю игру, Raycast правильно попадает в плоскость, как на следующем изображении:

Как видите, желтая сфера правильно расположена Полем Но когда я перетаскиваю мышь, сетка создана намного далекая с этого момента, как на следующем изображении:
< /p>
Теперь я тоже пытался с Chatgpt, но через несколько часов я не могу понять, почему это ведет себя так. Я подозреваю, что это привязано к первым 4 баллам: на самом деле первая кривая создается после того, как записано 4 балла. Причина, по которой он начинается там, а не в отправной точке, мне не подходит. Ты поможешь мне, пожалуйста? Если вам нужна дополнительная информация, я могу интегрировать ... Я думаю, что поместил сюда ядро.
Большое спасибо! < /P>

Подробнее здесь: https://stackoverflow.com/questions/793 ... -indicator
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»