Нормальный уровень (рисунок 1)
Квадраты - это плитки и красные точки - это вершины (рисунок 2)
Я пытаюсь создать набор блоков (серых предметов), которые можно уничтожить (удалить), чтобы вода могла через них go. Как показано на рисунке, это работает большую часть времени, некоторые блоки уже удалены, и вода течет через промежутки. Проблема в том, что это не работает все время. Иногда, когда блок снят, вода не заполняет пробел, и в воде появляется дыра.
Код
public class WaterManager(){
//level is the map, it is the parent containing the tile mesh and the water
public Node level;
Mesh water = new Mesh();
Geometry geo;
Vector3f[] vertices;
//subdivision is used so the mesh is more detailed, a subdivision of 2 means there are 9 vertices per tile (cube) as shown on picture 2
public int subdivision = 2;
//The amount of vertices on the x and z-axis
int xVertices, zVertices;
//The size of one tile, this is 10 in my case
float cubeSize;
//The amount of tiles on the x and z-axis
int xCubes, zCubes;
List<Integer> triangles = new ArrayList<>();
List<Vector3f> emptyCubes = new ArrayList<>();
List<Vector3f> newEmptyCubes = new ArrayList<>();
Dictionary<Vector3f, Tile> tiles = new Hashtable<Vector3f, Tile>();
public void Loop(Vector3f startPos){
newEmptyCubes.clear();
emptyCubes.clear();
boolean loop = true;
List<Vector3f> temp = new ArrayList<>();
//This is the first check to get all the connected blocks that should be filled with water
//including the block clicked on
GetConnectedEmptyTiles(startPos, newEmptyCubes);
//If there is more then 1 block which should be filled than loop through all the blocks
//and get the ones connected to those that should be filled with water
if (newEmptyCubes.size() > 1){
do{
for(Vector3f cube : newEmptyCubes){
GetConnectedEmptyTiles(cube, temp);
}
emptyCubes.addAll(newEmptyCubes);
newEmptyCubes.clear();
if (temp.size() <= 0)
loop = false;
newEmptyCubes.addAll(temp);
temp.clear();
}while(loop);
} else emptyCubes.addAll(newEmptyCubes);
//This creates the mesh
PaintEmptyTiles();
}
private void GetConnectedEmptyTiles(Vector3f pos, List<Vector3f> list){
//The blocks connected to a given posistion including the given position
Vector3f[] connectedTilesPlusPos = new Vector3f[]{
new Vector3f(pos.x + cubeSize, pos.y, pos.z),
new Vector3f(pos.x - cubeSize, pos.y, pos.z),
new Vector3f(pos.x, pos.y, pos.z + cubeSize),
new Vector3f(pos.x, pos.y, pos.z - cubeSize),
pos
};
//If the removed block is not connected to water then it shouldn't be filled
//with water
boolean connectedToWater = false;
List<Vector3f> tempList = new ArrayList<>();
//Check if the given position exists
if (tiles.get(pos) != null){
System.out.println("tile has water: " + tiles.get(pos).water);
//Loop through all the connected blocks including the given position
//and if they should be filled with water than add them to tempList
for(Vector3f tile : connectedTilesPlusPos){
if (tiles.get(tile) != null){
//Check if there is a block at the position (tile), if there
// is not than it can be filled with water
if (tiles.get(tile).tileObj == null){
//Check if the position already has water in it **(this is were it goes wrong)**
if (tiles.get(tile).water == false){
tempList.add(tile);
tiles.get(tile).water = true;
} else connectedToWater = true; //If there is already water attached to the
//the given position then it is connected to water
}
}
}
if (!connectedToWater)
tempList.clear();
else list.addAll(tempList);
}
}
private void PaintEmptyTiles(){
for(Vector3f cube : emptyCubes){
MakeTriangles(cube);
}
MakeMesh();
}
public void MakeTriangles(Vector3f pos){
//Gets an array of vertices that are used to create water at a given position as shown in picture 2
Vector3f[] verticesToUse = GetVerticesForPosition(pos);
List<Integer> indexes = new ArrayList<>();
for(int i = 0; i < vertices.length; i++){
for(Vector3f vert : verticesToUse){
if (vertices[i].equals(vert)){
indexes.add(i);
}
}
}
int xVerticesSize = rowSize(indexes, tiles.get(pos));
int zVerticesSize = (int)indexes.size() / xVerticesSize;
int verticesLenght = (int)Math.round(Math.sqrt(vertices.length));//The amount of vertices in one row.
int i = 0;
for(int x = 0; x < xVerticesSize; x++){
for (int z = 0; z < zVerticesSize; z++){
int vert = indexes.get(i);
if (indexes.contains(vert + verticesLenght + 1)){
//Creates a quad, since the water is build from small quads
triangles.add(vert);
triangles.add(vert + 1);
triangles.add(vert + verticesLenght + 1);
triangles.add(vert + verticesLenght + 1);
triangles.add(vert + verticesLenght);
triangles.add(vert);
}
i++;
}
}
}
private Vector3f[] GetVerticesForPosition(Vector3f pos){
List<Vector3f> tempVertices = new ArrayList<>();
for (Vector3f vertex : vertices) {
if (vertex != null){
float distance = DistanceOnXAndZ(pos, vertex);
float roundedDistance = roundedTo(distance, cubeSize / subdivision);
if (distance < cubeSize || (roundedDistance <= cubeSize && vertex.y <= -100 )){
tempVertices.add(vertex);
}
}
}
Vector3f[] returnVertices = new Vector3f[tempVertices.size()];
returnVertices = tempVertices.toArray(returnVertices);
return returnVertices;
}
//This calculates the distance from a point to another point but only using the x and z-axis
private float DistanceOnXAndZ(Vector3f origin, Vector3f target){
float originX = origin.x;
float originZ = origin.z;
float targetX = target.x;
float targetZ = target.z;
float xDistance = targetX - originX;
float zDistance = targetZ - originZ;
float distance = (float)Math.sqrt((xDistance * xDistance) + (zDistance * zDistance));
return distance;
}
private void MakeMesh(){
water.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
int[] tri = new int[triangles.size()];
for(int i = 0; i < triangles.size(); i++){tri[i] = triangles.get(i);}
water.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(tri));
water.updateBound();
geo = new Geometry("Water", water);
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Blue);
geo.setMaterial(mat);
level.attachChild(geo);
}
}
//The tile Object
public class Tile {
//not used anymore, but not deleted yet
public enum CellType {
LEFTORRIGHTSIDE, OTHER
}
public CellType cellType;
//The cube the Tile contains, if there is no cube then this is null
public Spatial tileObj;
//True if the tile contains water, false if not
public boolean water = false;
public Tile(CellType type, Spatial obj){
//Nothing is done with cellType
cellType = type;
tileObj = obj;
}
}
Причина
Причина, по которой это происходит, до сих пор неизвестна, но я думаю, что это как-то связано с тем, что булево значение воды в Tile уже установлено на true когда в нем есть куб (серая вещь).
Что нужно знать
- плитки - это список, содержащий все позиции, содержащие плитку, если он содержит блок, то tileObj не является нулевым Если в позиции содержится вода, то логическое значение воды должно быть истинным.
- PaintEmptyTiles (), в L oop () - функция, которая создает воду me sh
- Когда блок уничтожается нажатием на нее, вызывается функция L oop () и для tileObj в Tile в списке плиток устанавливается значение NULL
- Плитка - это квадрат с серым блоком и водой. или ничего.