moving vertices dynamicly relative to other Object
Hi, I have a cube, let's call it player, under that i have a procedurally generated plain, the palyer is going to move or rather float above and along that plain, I want to: A) get the vertices around the cube that are in the radius of the player. now i have achived that by looping through all vertices in that plain but that massivly cuts performance. i want to be able to get the vertices that are in the radius with somthing like physics.overlapsphere
without having to loop through all the vertices, this obviusly doesn't work since overlapsphere ruturns a collider array and i want vertices which are a verctor3. B) raise those vertices on the y axis relative to the players position (the closer the vertex is to the player the higher it is), now i have also gotten this to work but its not as nice and smooth as i want it to be, the vertices just lerp the Y axis to 3 if they are in the radius, and if not the don't move at all. i want it to more dependent on more of where the player is rather than if its in the radius, If you get what i mean.
Here is my current code:
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
[RequireComponent(typeof(MeshFilter))]
public class MeshGenerator : MonoBehaviour
{
//player's refrence
public Transform player;
//minimum distance for the vertex from the player to be affected and raised up
public float minDistance;
//limit for the vertices on the Y axis
public float meshOffset;
//mesh generation stuff
Mesh mesh;
Vector3[] vertices;
int[] triangles;
public int xSize = 20;
public int zSize = 20;
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
UpdateMesh();
}
void Update()
{
MoveVertices();
}
//Basically creating a plain at runtime to have a refrence to the vertices
void CreateShape()
{
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
vertices[i] = new Vector3(x, 0, z);
i++;
}
}
triangles = new int[xSize * zSize * 6];
int vert = 0;
int tris = 0;
for (int z = 0; z < zSize; z++)
{
for (int x = 0; x < xSize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xSize + 1;
triangles[tris + 5] = vert + xSize + 2;
vert++;
tris += 6;
}
vert++;
}
}
//updating the meshes vertices and triangles and recalculating lighting on the surface of it
void UpdateMesh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
}
//Using the players refrence to get his pos and looping through the vertices array to find which vertices are in the redius of the player
// This is the part that i am not certain of
void MoveVertices()
{
for (int i = 0; i < vertices.Length; i++)
{
float distance = Mathf.Sqrt((player.position - vertices[i]).sqrMagnitude);
if (distance < minDistance)
{
vertices[i].y = Mathf.Lerp(vertices[i].y, meshOffset, Time.deltaTime * distance);
UpdateMesh();
}else
{
vertices[i].y = Mathf.Lerp(vertices[i].y, 0, Time.deltaTime * distance);
UpdateMesh();
}
}
}
private void OnDrawGizmos()
{
if (vertices == null)
return;
for (int i = 0; i < vertices.Length; i++)
{
Gizmos.DrawSphere(vertices[i], 0.1f);
Gizmos.color = Color.red;
}
}
}
for visualization, i am basically triying to mimic geometry wars meshs and how they react to objects positions and what's happening on the battle field
https://www.youtube.com/watch?v=x9eT6RWZ--0
As always, any help is appreciated Thanks in advance!
Answer by Bunny83 · May 14, 2020 at 01:59 PM
Well since your vertices are at a known location in your vertices array you can directly calculate backwards to find the indices of the vertices around you. This of course requires you to have your parameters for your plane generation at hand. From your code it looks like you just create the vertices at integer locations. Keep in mind that vertices are specified in local space. So it's generally simpler to work completely in local space. That means you would first convert your "player's" position into local space of your plane object. Now we have everything in the same coordinate space.
Since the vertices are located at integer positions we can simply floor the player position to get the integer offsets in each axis. To get the index of the "lower left" vertex next to the player position you just do
Vector3 playerPos = planeObj.transform.InverseTransformPoint(player.position);
int x = Mathf.FloorToInt(playerPos.x);
int z = Mathf.FloorToInt(playerPos.z);
int index = x + y * (xSize + 1);
NOTE: this code has no bound checking / clamping. So this breaks down as soon as the "player" gets off your plane.
Of course now that you have the closest vertex index you can simply walk in both directions around that vertex. For example if you need a 7x7 area around your lower vertex you can simply use a nested for loop
for (int dy = -range; dy <= range; dy++)
{
int ty = y + dy;
if (ty < 0 || ty > ySize) // out of bounds
continue;
for (int dx = -range; dx <= range; dx++)
{
int tx = x + dx;
if (tx < 0 || tx > xSize) // out of bounds
continue;
int index = tx + ty * (xSize+1);
// check the vertex at "index" here
}
}
Note that you really shouldn't call your UpdateMesh method every iteration. Call it once at the end of your loop.
Finally note that you don't move your vertex "relatively" but you slowly move it to an absolute position. In your case the value of "meshOffset" is used as a "target value" to move towards. Your usage of Time.deltaTime and distance looks a bit weird to me. You essentially wil move the vertices the furtherst away from the player the most while those closest to the player would get almost no effect at all. All points outside your "minDistance" (which seems more like a maxDistance ^^) would actually be moved towards the absolute position of "0". I'm not sure if that's what you wanted to do but the result would look really strange. At the boundary of your "minDistance" you get a sharp drop from your meshOffset height down to 0.
Though since your question was focused on how to cut down the number of vertices you have to process I think the question is answered ^^. If you need further help (with whatever you're actually doing), please ask a seperate question (you can refer to this one with a link) and be more clear what this is all about. To me it looks a bit like a messed up terrain editor tool ^^. But hey, we can only judge based on what we actually know from the description.
So.... first thanks for your help with the " cutting down verices processed " part. But you don't seem like you got what i was hoping you'd get, and that totally my fault, (just to clarify I wrote this script before going to sleep so you now know why the veriables are named this wierdly). so about the penultimate paragraph: 1 - meshOffset was absolutely used as a terget i just miss named it 2 - usage of the time.deltatime is absolutely not accepable in this community, but i was experamenting with it in hopes of maybe...just maybe get it to work in the way i wanted 3 - You essentially wil move the vertices the furtherst away from the player the most while those closest to the player would get almost no effect at all, you got this part the wrong way around, furthest = almost no effect while the closest have the most effect (Hight) almost like the vertices get attracted to the player 4 - "$$anonymous$$Distance" this should've been max distance or somthing like radius like you said 5 - I really get that sharp difference in hight between vertices which isn't what i want
Your answer
Follow this Question
Related Questions
Why vertex positions appear (0.0, 0.0, 0.0) ? 0 Answers
Change vertex position not working? 2 Answers
Point on a triangle. 1 Answer
How can I create a plane at any given vertex position? 0 Answers
How to get triangle index of meshe's desired part? 0 Answers