Issues with Fog of War when multiple units move at the same time

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package SelectUnit.Hominini;

import CameraAndMouse.RayMap;
import FindPath.FindPath;
import FindPath.Tools;
import static SelectUnit.Hominini.T100.i;
import SelectUnit.UnitControl;
import SelectUnit.UnitGlobalData;
import com.dongbat.walkable.FloatArray;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.collision.ContactListener;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.GhostControl;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import mygame.Main;
import org.dyn4j.geometry.Vector2;
import physicsState.MinieBulletAppState;
import physicsState.dyn4j.BodyControl;
import scenario.scenarioState;
import static scenario.scenarioState.mapGrid3D;

/**
 *
 * @author icyboxs
 */
public class T100Control extends AbstractControl implements ContactListener, UnitControl,PhysicsCollisionListener {

    //停止距离
    private float STOPPING_DISTANCE = 2.5f;
    public SimpleApplication simpleApp;



    List<Vector3f> move = new ArrayList<Vector3f>();
    int imove = 0;
    private Vector3f moveTo = null;
    private float moveSpeed = 0.5f; // 设置移动速度
    Quaternion targetQua = new Quaternion();
    private Vector3f previousGridPosition=new Vector3f();
    private Vector3f spatialPreviousGridPosition = new Vector3f();
    private float length;
    private int Cellsize = (int) mapGrid3D.getCellsize();

    private int CurrentState = 0;
    private final int MoveState = 1;
    private final int WaitState = 2;
    private float AverageDistance=0;
    
    /**
     * @return the CurrentState
     */
    public int getCurrentState() {
        return CurrentState;
    }

    public T100Control(SimpleApplication app,Vector3f v3f,Spatial Role) {
        this.simpleApp = app;
        this.setSpatial(Role);
        this.spatial.setLocalScale(3f, 3f, 3f);
        this.spatial.setLocalTranslation(v3f);
        this.spatial.setUserData("CombatantsUnitNode", i);
        this.spatial.setUserData("Type", "CombatUnit");
        this.spatial.setShadowMode(RenderQueue.ShadowMode.Cast);
        this.spatial.setQueueBucket(RenderQueue.Bucket.Transparent);
        Node GhostPhysicsNode = new Node();
        GhostControl ghostControl = new GhostControl(new CapsuleCollisionShape(8f, 4f));
        MinieBulletAppState.bulletAppState.getPhysicsSpace().add(ghostControl);
        GhostPhysicsNode.addControl(ghostControl);
        GhostPhysicsNode.move(0, 0.9f, 0);
        ((Node)this.spatial).attachChild(GhostPhysicsNode);
    }

    public void setPath(FloatArray floatArray) {

        this.move.clear();
        time = 0;
        for (int i = 0; i < floatArray.size; i += 2) {
            float x = (float) floatArray.get(i);
            float y = 0.0f;
            float z = (float) floatArray.get(i + 1);
            Vector3f vector = new Vector3f(x, y, z);
            this.move.add(vector);
        }

    }

    public void setPath(FloatArray floatArray,float AverageDistance) {
        this.AverageDistance=AverageDistance;
        setPath(floatArray);
    }
    
    
    float FOWtime = 0;

    @Override
    protected void controlUpdate(float tpf) {
        if (spatial != null) {

            // if (imove < move.size() - 1) {
            if (!move.isEmpty()) {
                CurrentState = MoveState;
//                var s=spatial.getUserData("CombatantsUnitNode");
//                System.err.println(s);
                // 获取当前空间位置
                Vector3f currentWorldPosition = spatial.getLocalTranslation();
                // 将世界位置转换为网格坐标
                Vector3f currentGridPosition = new Vector3f((int) currentWorldPosition.x / Cellsize, 0, (int) currentWorldPosition.z / Cellsize);

                // 判断位置是否改变
               // this.FOWtime += tpf;

              //  if (this.FOWtime > 0.25) {
                    if (!currentGridPosition.equals(previousGridPosition)) {
                        // 位置已更改,执行相应的操作
                        // ...


                simpleApp.enqueue(new Runnable() {
                    @Override
                    public void run() {
                        scenarioState.setFowImagAlpha( (int) previousGridPosition.x*Cellsize, (int) previousGridPosition.z*Cellsize, scenarioState.raster);
                        scenarioState.setFowImag((int) spatial.getLocalTranslation().x, (int) spatial.getLocalTranslation().z, scenarioState.raster);
                    }
                });

                System.out.println(spatial.getLocalTranslation().x+","+(int) spatial.getLocalTranslation().z+"----spatial");
                System.out.println(previousGridPosition.x+","+previousGridPosition.z+"----previousGridPosition");
                

                        // 更新前一网格坐标
                        previousGridPosition = currentGridPosition;

                    }
//                    this.FOWtime = 0;
//                }

                MoveWork(this.spatial, tpf);

            } else {
                CurrentState = WaitState;
            }

        }
    }

    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {
    }

    @Override
    public void setSpatial(Spatial spatial) {
        super.setSpatial(spatial);
        MinieBulletAppState.bulletAppState.getPhysicsSpace().addContactListener(this);
        MinieBulletAppState.bulletAppState.getPhysicsSpace().addCollisionListener(this);

    }

    /**
     * 让物体按照寻路给出的坐标进行移动
     *
     * @param spatial
     * @param tpf
     */
    float time = 0;
    private float interp = 0.0f;
    
    protected void MoveWork(Spatial spatial, float tpf) {
        Vector3f currentPoint = spatial.getLocalTranslation();
        Vector3f nextPoint = this.move.get(0);
        // 计算节点朝下一个点移动的步进
        
        Vector3f step = nextPoint.subtract(currentPoint).normalizeLocal().multLocal(tpf).multLocal(50f);
        
        //Vector3f step = currentPoint.interpolateLocal(nextPoint, 1.5f*tpf);
        
        step.setY(0);//设置高度
        //旋转
        Quaternion rotation = spatial.getLocalRotation();
        targetQua.lookAt(nextPoint.subtract(currentPoint).normalizeLocal(), new Vector3f(0, 1, 0));
        rotation.nlerp(targetQua, 10f*tpf);
        //移动
        spatial.move(step);
    
        
        // 检查是否已经到达下一个点getLocalTranslation
        float distanceToNextPoint = spatial.getWorldTranslation().distance(nextPoint);
        

        
        //System.err.println(distanceToNextPoint);
        if (distanceToNextPoint < STOPPING_DISTANCE+(UnitGlobalData.getInstance().getGroup().size()*0.25f)) {
            // 如果已经到达下一个点,更新当前路径索引
            //imove++;
            this.move.remove(0);
  
        }

//        this.time += tpf;
//
//        if (this.time > 0.5) {
//            System.err.println(this.spatial.getLocalTranslation()+"1秒内");
//            
//            float distance = this.spatial.getWorldTranslation().distance(this.spatialPreviousGridPosition);
//            System.err.println(distance + ",distance123");
//            if (distance<3f && getCurrentState()==1) {
//                
//              System.err.println("停止移动");
//              this.move.clear();
//            } else {
//
//            }
//            
//            this.spatialPreviousGridPosition.set(this.spatial.getWorldTranslation()) ;
//            System.err.println(this.spatialPreviousGridPosition);
//            this.time = 0;
//        }
    }


    public void collision(T100Control controlA, T100Control controlB, Spatial objectAParent, Spatial objectBParent, Vector3f worldTranslationA, Vector3f worldTranslationB){
            if(controlB.getCurrentState() == 2 || controlA.getCurrentState() == 2){
        if (controlB.getCurrentState() == 2 && controlB == this) {
            Vector3f local = worldTranslationB.subtract(worldTranslationA).normalizeLocal();
            try {
                FloatArray path = FindPath.getInstance().PathFinding(worldTranslationB.x, worldTranslationB.z, worldTranslationB.add(local.mult(20f)).x, worldTranslationB.add(local.mult(20f)).z, 2);
                controlB.setPath(path);
            } catch (Exception e) {

            }
            if (UnitGlobalData.getInstance().getGroup().contains((Node) objectBParent) && UnitGlobalData.getInstance().getGroup().contains((Node) objectAParent)) {
                if (UnitGlobalData.getInstance().getGroup().contains((Node) objectBParent) && UnitGlobalData.getInstance().getGroup().contains((Node) objectAParent)) {
                    controlA.move.clear();
                }
            }
        } else if (controlA.getCurrentState() == 2 && controlA == this) {

            Vector3f local = worldTranslationA.subtract(worldTranslationB).normalizeLocal();
            //((Spatial) pcoA.getUserObject()).getParent().setLocalTranslation(this.spatial.getWorldTranslation().add(local.setY(0)));
            try {
                FloatArray path = FindPath.getInstance().PathFinding(worldTranslationA.x, worldTranslationA.z, worldTranslationA.add(local.mult(20f)).x, worldTranslationA.add(local.mult(20f)).z, 2);
                controlA.setPath(path);
            } catch (Exception e) {

            }
            if (UnitGlobalData.getInstance().getGroup().contains((Node) objectBParent) && UnitGlobalData.getInstance().getGroup().contains((Node) objectAParent)) {
                if (UnitGlobalData.getInstance().getGroup().contains((Node) objectBParent) && UnitGlobalData.getInstance().getGroup().contains((Node) objectAParent)) {
                    controlB.move.clear();
                }
            }
        }
}

//        if (controlA.getCurrentState() == 2 && controlA == this) {
//            Vector3f localB = worldTranslationA.subtract(worldTranslationB).normalizeLocal();
//            simpleApp.enqueue(new Runnable() {
//                @Override
//                public void run() {
//                    System.out.println(localB+",localB");
//                    objectAParent.move(localB.setY(0));
//                }
//            });
//        } else if (controlB.getCurrentState() == 2 && controlB == this) {
//            Vector3f localA = worldTranslationB.subtract(worldTranslationA).normalizeLocal();
//
//            simpleApp.enqueue(new Runnable() {
//                @Override
//                public void run() {
//                     System.out.println(localA+",localA");
//                    objectBParent.move(localA.setY(0));
//                }
//            });
//        }
 
            if (controlB.getCurrentState() == 1 && controlB == this) {
                Vector3f localB = worldTranslationA.subtract(worldTranslationB).normalizeLocal();
                simpleApp.enqueue(new Runnable() {
                    @Override
                    public void run() {
                        objectAParent.move(localB.setY(0));
                    }
                });
            } else if (controlA.getCurrentState() == 1 && controlA == this) {
                Vector3f localA = worldTranslationB.subtract(worldTranslationA).normalizeLocal();

                simpleApp.enqueue(new Runnable() {
                    @Override
                    public void run() {
                        objectBParent.move(localA.setY(0));
                    }
                });
            }

        }
    
    
    

    @Override
    public void onContactEnded(long manifoldId) {

    }

    @Override
    public void onContactProcessed(PhysicsCollisionObject pcoA, PhysicsCollisionObject pcoB, long manifoldPointId) {

        T100Control controlA = ((Spatial) pcoA.getUserObject()).getParent().getControl(T100Control.class);
        T100Control controlB = ((Spatial) pcoB.getUserObject()).getParent().getControl(T100Control.class);
        Spatial objectAParent = ((Spatial) pcoA.getUserObject()).getParent();
        Spatial objectBParent = ((Spatial) pcoB.getUserObject()).getParent();
        Vector3f worldTranslationA = objectAParent.getWorldTranslation();
        Vector3f worldTranslationB = objectBParent.getWorldTranslation();
        
        
        int PhysicsSpace = MinieBulletAppState.bulletAppState.getPhysicsSpace().pairTest(pcoA, pcoB, new PhysicsCollisionListener() {
            @Override
            public void collision(PhysicsCollisionEvent event) {
            }
        });
        
        if(PhysicsSpace>0){
        collision(controlA,controlB,objectAParent,objectBParent,worldTranslationA,worldTranslationB);
        }


    }

    @Override
    public void onContactStarted(long manifoldId) {

    }
   
    public void path(float AverageDistance,Vector3f average,RayMap rayMap, Vector3f RightClickMapLocation) {

        FloatArray path;
            try {

                UnitGlobalData.getInstance().addGroup(UnitGlobalData.getInstance().getSelectedUnit());
                Vector3f Nodeoffset = this.spatial.getWorldTranslation().subtract(average);
                float distance = average.distance(RightClickMapLocation);
                System.err.println(distance + ",distance");
                if (distance <= 30f) {
                    Nodeoffset.set(0, 0, 0);
                }

                if (average.distance(this.spatial.getWorldTranslation()) < AverageDistance * 1.8) {
                    path = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.add(Nodeoffset).x, RightClickMapLocation.add(Nodeoffset).z, 2);
                    this.setPath(path, AverageDistance);
                } else {
                    path = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.x, RightClickMapLocation.z, 2);
                    this.setPath(path, AverageDistance);
                }

                if (path.size <= 0) {
                    FloatArray path1 = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.x, RightClickMapLocation.z, 2);
                    this.setPath(path1, AverageDistance);
                    if (path1.size <= 0) {
                        Vector3f vectors = this.spatial.getWorldTranslation().subtract(RightClickMapLocation).normalizeLocal();
                        //findPath(i,vectors,RightClickMapLocation);
                        FloatArray path2 = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.add(vectors).x, RightClickMapLocation.add(vectors).z, 2);
                        int p = 0;

                        while (true) {
                            p++;
                            System.err.println(p);
                            if (path2.size <= 0) {
                                vectors.addLocal(vectors);
                                path2 = FindPath.getInstance().PathFinding(this.spatial.getWorldTranslation().x, this.spatial.getWorldTranslation().z, RightClickMapLocation.add(vectors).x, RightClickMapLocation.add(vectors).z, 2);
                                if (p > 500) {
                                    break; // 跳出循环
                                }

                            } else if (path2.size > 0) {
                                this.setPath(path2);
                                break; // 跳出循环
                            }

                        }

                    }
                }

            } catch (Exception e) {
                System.err.println(e);
            }

    }
    
    @Override
    public void sendRightClickCommand(float AverageDistances,Vector3f average,RayMap rayMap, Vector3f RightClickMapLocation) {
    path(AverageDistances,average,rayMap,RightClickMapLocation);
    }

    @Override
    public void collision(PhysicsCollisionEvent event) {

    }



}

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package scenario;

import CameraAndMouse.CamAndMouseCur;
import FindPath.FindPath;
import GUI.UI;
import physicsState.MinieBulletAppState;
import static physicsState.MinieBulletAppState.bulletAppState;
import com.jme3.app.Application;
import com.jme3.app.LostFocusBehavior;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.asset.AssetManager;
import com.jme3.asset.TextureKey;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.Box2dShape;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
import com.jme3.bullet.collision.shapes.MeshCollisionShape;
import com.jme3.bullet.collision.shapes.PlaneCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.light.DirectionalLight;
import com.jme3.material.MatParamOverride;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.BatchNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.debug.Grid;
import com.jme3.scene.instancing.InstancedNode;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Quad;
import com.jme3.shader.VarType;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.shadow.EdgeFilteringMode;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.HillHeightMap;
import com.jme3.texture.Image;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.texture.image.ColorSpace;
import com.jme3.texture.image.ImageRaster;
import com.jme3.util.BufferUtils;
import com.jme3.util.TangentBinormalGenerator;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import jme3utilities.minie.PhysicsDumper;
import mygame.Main;
import scenario.Grid.MapGrid3D;
import scenario.Grid.meshGrid;

/**
 *
 * @author Icyboxs
 */
public class scenarioState extends BaseAppState {

    public static ImageRaster raster;
    public String ShadersFow = "Shaders/Fow/FowLighting.j3md";

    private AssetManager assetManager;
    private SimpleApplication simpleApp;
    private Node ScenarioRoot = new Node("ScenarioRoot");
//  private Node MapNode = new Node("MapNode");
    private Node batch = new BatchNode("MapNode");
    private Material boxMaterial1;
    private Material Material1;
    public static MapGrid3D mapGrid3D = new MapGrid3D(2048, 2048, 8f);

    private Node cliff = new Node("cliff");

    meshGrid meshGrid = new meshGrid();
    private boolean DisplayGrid = false;
    public static HashMap<Vector2f, Integer> MapArrays = new HashMap<Vector2f, Integer>();

    @Override
    protected void initialize(Application app) {

        simpleApp = (SimpleApplication) app;
        assetManager = app.getAssetManager();

        boxMaterial1 = new Material(assetManager, ShadersFow);
        batch.setShadowMode(ShadowMode.Receive);  
        
        /* 产生阴影 */
//    final int SHADOWMAP_SIZE=1024*2;
//
//    DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(assetManager, SHADOWMAP_SIZE, 3);
//    dlsf.setLight((DirectionalLight) simpleApp.getRootNode().getLocalLightList().get(1));
//    dlsf.setEnabled(true);
//    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
//    fpp.addFilter(dlsf);
//    simpleApp.getViewPort().addProcessor(fpp);
//if(DisplayGrid){
//    for(int i=0;i<mapGrid3D.getGridPositionDb().size();i++){
//    quadBOX(mapGrid3D.getGridPositionDb().get(i));
//    }
//}
        quadBOX(new Vector3f(0,0,0));

        Geometry gridGeometry = meshGrid.GameMap(mapGrid3D.getXLength(), 0, mapGrid3D.getZWidth(), mapGrid3D.getCellsize());

//        Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
//        material.setColor("Color", ColorRGBA.Red);
//        material.getAdditionalRenderState().setWireframe(true);
//        material.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
        // 创建 Material
        Material1 = new Material(assetManager, "Shaders/Fow/FowLighting.j3md");
        Material1.setTexture("DiffuseMap", assetManager.loadTexture("Textures/map/dirt.png"));
       // Material1.getAdditionalRenderState().setWireframe(true);
        Material1.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
        // 加载纹理
//        Texture texture = assetManager.loadTexture(new TextureKey("Textures/map/vehicule01_apresUVW.png", true));
//        material.setTexture("ColorMap",texture);
        gridGeometry.setMaterial(Material1);
        batch.attachChild(gridGeometry);
        simpleApp.getRootNode().attachChild(batch);

        FindPath.getInstance().setPathHelper(mapGrid3D.getXLength(), mapGrid3D.getZWidth());
        initializeFowImag((int) mapGrid3D.getXLength(), (int) mapGrid3D.getZWidth());
    }


    @Override
    protected void cleanup(Application aplctn) {

    }

    @Override
    protected void onEnable() {
//simpleApp.setPauseOnLostFocus(false);
simpleApp.setLostFocusBehavior(LostFocusBehavior.ThrottleOnLostFocus);

//        CollisionShape sceneShape = CollisionShapeFactory.createMeshShape(batch);
//        RigidBodyControl landscape = new RigidBodyControl(sceneShape, 0);
//        batch.addControl(landscape);
//        MinieBulletAppState.bulletAppState.getPhysicsSpace().add(landscape);
//        MinieBulletAppState.bulletAppState.startPhysics();
    }

    @Override
    protected void onDisable() {
        ScenarioRoot.removeFromParent();
    }

    
      float FOWtime = 0;
       @Override
    public void update(float tpf) {

//           FOWtime += tpf;
//           if (FOWtime > 0.5) {
//
//               for (int i = 0; i < (int) mapGrid3D.getXLength(); i++) {
//                   for (int j = 0; j < (int) mapGrid3D.getZWidth(); j++) {
//                                 ColorRGBA color = new ColorRGBA(0.2f, 0.2f, 0.2f, 1.0f);
//                                raster.setPixel(j, i, color);
//
//                   }
//               }
//               
//               FOWtime = 0;
//           }
        
        
        
    }
    
    
    public void quadBOX(Vector3f loc) {
        Quad quad;
        // 创建一个蓝色四边形
        if (DisplayGrid) {
            quad = new Quad(mapGrid3D.getCellsize()*2, mapGrid3D.getCellsize()*2);
        } else {
            quad = new Quad(mapGrid3D.getXLength()*2, mapGrid3D.getZWidth()*2);
        }

        //System.err.println(mapGrid3D.getXLength()+","+mapGrid3D.getZWidth());
        Geometry quadBoxGeometry = new Geometry("quadBox", quad);
        // 创建一个蓝色材质
        Material boxMaterial1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        boxMaterial1.setColor("Color", ColorRGBA.Blue);
       // boxMaterial1.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
//        boxMaterial1.getAdditionalRenderState().setWireframe(true);
        boxMaterial1.getAdditionalRenderState().setFaceCullMode(FaceCullMode.FrontAndBack);
        quadBoxGeometry.setMaterial(boxMaterial1);

        quadBoxGeometry.setLocalTranslation(loc);
        Quaternion y = new Quaternion().fromAngleAxis(90 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X);
        quadBoxGeometry.setLocalRotation(y);
        quadBoxGeometry.move(-1024, 0, -1024);
        // 将盒子添加到场景中
        // instancedNode.onTransformChange(quadBoxGeometry);
        Main.MapUIBox.attachChild(quadBoxGeometry);

        // MapNode.attachChild(quadBoxGeometry);
    }

    public void BOX(Vector3f loc) {

        // 创建一个蓝色盒子
        Box box = new Box(1, 1, 1);
        Geometry boxGeometry = new Geometry("Box", box);

        // 创建一个蓝色材质
        Material boxMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        boxMaterial.setColor("Color", ColorRGBA.White);
        boxGeometry.setMaterial(boxMaterial);
        boxGeometry.setLocalTranslation(loc);
        // 将盒子添加到场景中
        simpleApp.getRootNode().attachChild(boxGeometry);
    }

    /**
     * @return the DisplayGrid
     */
    public boolean getDisplayGrid() {
        return DisplayGrid;
    }

    /**
     * @param DisplayGrid the DisplayGrid to set
     */
    public void setDisplayGrid(boolean DisplayGrid) {
        this.DisplayGrid = DisplayGrid;
    }

    /**
     * 初始化战争迷雾
     *
     * @param imageSizeW
     * @param imageSizeH
     */
    public void initializeFowImag(int imageSizeW, int imageSizeH) {
        // Create the raw FOW overlay image

        ByteBuffer data = BufferUtils.createByteBuffer(imageSizeW * imageSizeH * 4); // square image, four bytes per color
        Image image = new Image(Image.Format.ABGR8, imageSizeW, imageSizeH, data, ColorSpace.Linear);
        // Create a raster that we can draw to
        raster = ImageRaster.create(image);
        // Create the texture to set to the FowMap on materials
        Texture2D fowMap = new Texture2D(image);

        // Set the texture for any material that will take it.
        // This is a JME trick that keeps us from having to remember to set it
        // on every material.
        simpleApp.getRootNode().addMatParamOverride(new MatParamOverride(VarType.Texture2D, "FowMap", fowMap));

        // Put some test data into the fog of war texture
        for( int i = 0; i < imageSizeW; i++ ) {
            for( int j = 0; j < imageSizeH; j++ ) { 
                MapArrays.put(new Vector2f(j,i), 0);
            }
        }
    }

    public static void setFowImag(int givenX, int givenY, ImageRaster raster) {
       // long s = Instant.now().toEpochMilli();
        int radius = 40;

        // 计算给定点所在的格子cellsize
        int gridX = (int) (givenX / mapGrid3D.getCellsize());
        int gridY = (int) (givenY / mapGrid3D.getCellsize());
        // 遍历半径范围内的格子
        for (int x = Math.max(0, gridX - radius); x <= Math.min(mapGrid3D.getXLength() - 1, gridX + radius); x++) {
            for (int y = Math.max(0, gridY - radius); y <= Math.min(mapGrid3D.getZWidth() - 1, gridY + radius); y++) {
                // 计算格子的实际坐标
                int currentGridX = (int) (x * mapGrid3D.getCellsize());
                int currentGridY = (int) (y * mapGrid3D.getCellsize());

                // 计算给定点到当前格子的距离
                double distanceToGivenPoint = Math.sqrt(
                        Math.pow(givenX - currentGridX, 2) + Math.pow(givenY - currentGridY, 2)
                );

                // 如果距离小于或等于半径,输出坐标
                if (distanceToGivenPoint <= radius) {
                    for (int i = 0; i <= (int) mapGrid3D.getCellsize(); i++) {
                        for (int j = 0; j <= (int) mapGrid3D.getCellsize(); j++) {
                            if (currentGridX + j >= 0 && currentGridX + j < raster.getWidth() && currentGridY + i >= 0 && currentGridY + i < raster.getHeight()) {
                                
                                ColorRGBA color = new ColorRGBA(1, 1, 1, 0.5f);
                                raster.setPixel(currentGridX + j, currentGridY + i, color);
//                                MapArrays.put(new Vector2f(j,i), 1);
                                
                            }

                        }

                    }

                }
            }
        }
       // System.err.println(" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
        
    }

        public static void setFowImagAlpha(int givenX, int givenY, ImageRaster raster) {
       // long s = Instant.now().toEpochMilli();
        int radius = 55;

        // 计算给定点所在的格子cellsize
        int gridX = (int) (givenX / mapGrid3D.getCellsize());
        int gridY = (int) (givenY / mapGrid3D.getCellsize());
        // 遍历半径范围内的格子
        for (int x = Math.max(0, gridX - radius); x <= Math.min(mapGrid3D.getXLength() - 1, gridX + radius); x++) {
            for (int y = Math.max(0, gridY - radius); y <= Math.min(mapGrid3D.getZWidth() - 1, gridY + radius); y++) {
                // 计算格子的实际坐标
                int currentGridX = (int) (x * mapGrid3D.getCellsize());
                int currentGridY = (int) (y * mapGrid3D.getCellsize());

                // 计算给定点到当前格子的距离
                double distanceToGivenPoint = Math.sqrt(
                        Math.pow(givenX - currentGridX, 2) + Math.pow(givenY - currentGridY, 2)
                );

                // 如果距离小于或等于半径,输出坐标
                if (distanceToGivenPoint <= radius) {
                    for (int i = 0; i <= (int) mapGrid3D.getCellsize(); i++) {
                        for (int j = 0; j <= (int) mapGrid3D.getCellsize(); j++) {
                            if (currentGridX + j >= 0 && currentGridX + j < raster.getWidth() && currentGridY + i >= 0 && currentGridY + i < raster.getHeight()) {
                                
                                ColorRGBA color = new ColorRGBA(0.2f, 0.2f, 0.2f, 1.0f);
                                raster.setPixel(currentGridX + j, currentGridY + i, color);
                                
                            }

                        }

                    }

                }
            }
        }
       // System.err.println(" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
        
    }
}

I used this code to populate the previous position of the unit

scenarioState.setFowImagAlpha( (int) previousGridPosition.x*Cellsize, (int) previousGridPosition.z*Cellsize, scenarioState.raster);

Use this code to populate the current unit’s position

scenarioState.setFowImag((int) spatial.getLocalTranslation().x, (int) spatial.getLocalTranslation().z, scenarioState.raster);

That way you can refill the areas you’ve travelled。
And of course that’s when the problem arises。
Looks good when there is one unit, but not so good when there are multiple units (shown in the video)。

I did another idea of trying everyupdate(tpf) on the map in general
Sorting the map but processing the whole map at the same time doesn’t seem to be a good decision it makes the screen laggy and jerky

How should I fill the fog of war when multiple units are moving at the same time?

1 Like

After a few days of agonisingly struggling I haven’t come up with a sensible way for units to get the fog of war back after clearing it, so I’m going to use a very stupid but effective approach which is to reduce the grid

void Update()
{
   for i, j in grid {
       colors[i + j * width] = black
       if (visionGrid.IsVisible(i, j, activePlayers))
           colors[pixel] = white
       else if (visionGrid.WasVisible(i, j, activePlayers))
           colors[pixel] = grey // 这里用于处理之前的视野。
   }
   texture.SetPixels(colors)
}

Logically it looks like this
With this approach you can’t use a large range of pixel grids

Currently I’m using the refresh per frame idea to restore the fog of war, if there is another better way please leave a comment here thank you very much for your reply!

I am dooing all the work on the gpu but the logic should stay the same:

Step 1: add some amount to every pixel
Step 2: set every pixel in the area around a unit to 0

Thank you for your reply I made it!
I made a lot of fatal mistakes before that.

These are my changes to the logic

      float FOWtime = 0;
    @Override
    public void update(float tpf) {

        FOWtime += tpf;
        if (FOWtime > 0.25f) {
            for (int i = 0; i < MapArrays.size(); i++) {
                MapArrays.set(i, 0);
            }
            for (Node i : UnitGlobalData.getInstance().getCombatantsUnitList()) {

                UnitControl control = (UnitControl) i.getControl(0);
                if (!control.getVisionRange().isEmpty()) {
                    for (int j = 0; j < control.getVisionRange().size(); j++) {
                        SetVisible(control.getVisionRange().get(j),1);
                    }
                }
            }
            
            for (int i = 0; i <raster.getWidth(); i++) {
                for (int j = 0; j <raster.getHeight(); j++) {
                    int index = i * (int) raster.getWidth() + j;
                    ColorRGBA color=null;

                    // 检查当前位置是否已探索或正在探索
                if (IsVisible(index,1)) {
                    color = new ColorRGBA(1f, 1f, 1f, 1f); // 已探索区域
                }else if(WasVisible(index,1)){
                    color = new ColorRGBA(.5f, .5f, .5f, 1f);
                }else{
                    color = new ColorRGBA(0f, 0f, 0f, 1f);
                }
 
                raster.setPixel(j, i, color);
            }
        }

            
            FOWtime = 0;
        }

    }
public static void SetVisible(int index, int players) {
    // 获取索引处的元素
    int oldValue = MapArrays.get(index);
    // 将元素与 players 进行按位或操作
    int newValue = oldValue | players;
    // 将结果写回列表中
    MapArrays.set(index, newValue);
        // 获取索引处的元素
    int oldvisited = visited.get(index);
    // 将元素与 players 进行按位或操作
    int newvisited = oldvisited | players;
    // 将结果写回列表中
    visited.set(index, newvisited);
}

public static boolean IsVisible(int index, int players) {
    return (MapArrays.get(index) & players) > 0;
    
}

public static boolean WasVisible(int index, int players) {

    return (visited.get(index) & players) > 0;
}
}
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package scenario;

import CameraAndMouse.CamAndMouseCur;
import FindPath.FindPath;
import GUI.UI;
import SelectUnit.UnitControl;
import SelectUnit.UnitGlobalData;
import physicsState.MinieBulletAppState;
import static physicsState.MinieBulletAppState.bulletAppState;
import com.jme3.app.Application;
import com.jme3.app.LostFocusBehavior;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.asset.AssetManager;
import com.jme3.asset.TextureKey;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.Box2dShape;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
import com.jme3.bullet.collision.shapes.MeshCollisionShape;
import com.jme3.bullet.collision.shapes.PlaneCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.light.DirectionalLight;
import com.jme3.material.MatParamOverride;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.BatchNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.debug.Grid;
import com.jme3.scene.instancing.InstancedNode;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Quad;
import com.jme3.shader.VarType;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.shadow.EdgeFilteringMode;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.HillHeightMap;
import com.jme3.texture.Image;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.texture.image.ColorSpace;
import com.jme3.texture.image.ImageRaster;
import com.jme3.util.BufferUtils;
import com.jme3.util.TangentBinormalGenerator;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import jme3utilities.minie.PhysicsDumper;
import mygame.Main;
import scenario.Grid.MapGrid3D;
import scenario.Grid.meshGrid;

/**
 *
 * @author Icyboxs
 */
public class scenarioState extends BaseAppState {

    public static ImageRaster raster;
    public String ShadersFow = "Shaders/Fow/FowLighting.j3md";

    private AssetManager assetManager;
    private SimpleApplication simpleApp;
    private Node ScenarioRoot = new Node("ScenarioRoot");
//  private Node MapNode = new Node("MapNode");
    private Node batch = new BatchNode("MapNode");
    private Material boxMaterial1;
    public static Material Material1;
    public static MapGrid3D mapGrid3D = new MapGrid3D(2048, 2048, 8f);

    private Node cliff = new Node("cliff");

    meshGrid meshGrid = new meshGrid();
    private boolean DisplayGrid = false;
    public static ArrayList<Integer> MapArrays = new ArrayList<Integer>();
    public static ArrayList<Integer> visited = new ArrayList<Integer>();
    public static ArrayList<Integer> IsVisible = new ArrayList<Integer>();
    
    @Override
    protected void initialize(Application app) {

        simpleApp = (SimpleApplication) app;
        assetManager = app.getAssetManager();

        boxMaterial1 = new Material(assetManager, ShadersFow);
        batch.setShadowMode(ShadowMode.Receive);  
        
        /* 产生阴影 */
//    final int SHADOWMAP_SIZE=1024*2;
//
//    DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(assetManager, SHADOWMAP_SIZE, 3);
//    dlsf.setLight((DirectionalLight) simpleApp.getRootNode().getLocalLightList().get(1));
//    dlsf.setEnabled(true);
//    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
//    fpp.addFilter(dlsf);
//    simpleApp.getViewPort().addProcessor(fpp);
//if(DisplayGrid){
//    for(int i=0;i<mapGrid3D.getGridPositionDb().size();i++){
//    quadBOX(mapGrid3D.getGridPositionDb().get(i));
//    }
//}
        quadBOX(new Vector3f(0,0,0));

        Geometry gridGeometry = meshGrid.GameMap(mapGrid3D.getXLength(), 0, mapGrid3D.getZWidth(), mapGrid3D.getCellsize());

//        Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
//        material.setColor("Color", ColorRGBA.Red);
//        material.getAdditionalRenderState().setWireframe(true);
//        material.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
        // 创建 Material
        Material1 = new Material(assetManager, "Shaders/Fow/FowLighting.j3md");
        Material1.setTexture("DiffuseMap", assetManager.loadTexture("Textures/map/dirt.png"));
        //Material1.getAdditionalRenderState().setWireframe(true);
        Material1.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
        // 加载纹理
//        Texture texture = assetManager.loadTexture(new TextureKey("Textures/map/vehicule01_apresUVW.png", true));
//        material.setTexture("ColorMap",texture);
        gridGeometry.setMaterial(Material1);

        batch.attachChild(gridGeometry);
        simpleApp.getRootNode().attachChild(batch);

        FindPath.getInstance().setPathHelper(mapGrid3D.getXLength(), mapGrid3D.getZWidth());
        initializeFowImag(512, 512);
    }


    @Override
    protected void cleanup(Application aplctn) {

    }

    @Override
    protected void onEnable() {
//simpleApp.setPauseOnLostFocus(false);
simpleApp.setLostFocusBehavior(LostFocusBehavior.ThrottleOnLostFocus);

//        CollisionShape sceneShape = CollisionShapeFactory.createMeshShape(batch);
//        RigidBodyControl landscape = new RigidBodyControl(sceneShape, 0);
//        batch.addControl(landscape);
//        MinieBulletAppState.bulletAppState.getPhysicsSpace().add(landscape);
//        MinieBulletAppState.bulletAppState.startPhysics();
    }

    @Override
    protected void onDisable() {
        ScenarioRoot.removeFromParent();
    }

    
      float FOWtime = 0;
    @Override
    public void update(float tpf) {

        FOWtime += tpf;
        if (FOWtime > 0.25f) {
            for (int i = 0; i < MapArrays.size(); i++) {
                MapArrays.set(i, 0);
            }
            for (Node i : UnitGlobalData.getInstance().getCombatantsUnitList()) {

                UnitControl control = (UnitControl) i.getControl(0);
                if (!control.getVisionRange().isEmpty()) {
                    for (int j = 0; j < control.getVisionRange().size(); j++) {
                        SetVisible(control.getVisionRange().get(j),1);
                    }
                }
            }
            
            for (int i = 0; i <raster.getWidth(); i++) {
                for (int j = 0; j <raster.getHeight(); j++) {
                    int index = i * (int) raster.getWidth() + j;
                    ColorRGBA color=null;

                    // 检查当前位置是否已探索或正在探索
                if (IsVisible(index,1)) {
                    color = new ColorRGBA(1f, 1f, 1f, 1f); // 已探索区域
                }else if(WasVisible(index,1)){
                    color = new ColorRGBA(.5f, .5f, .5f, 1f);
                }else{
                    color = new ColorRGBA(0f, 0f, 0f, 1f);
                }
 
                raster.setPixel(j, i, color);
            }
        }

            
            FOWtime = 0;
        }

    }
    
    
    public void quadBOX(Vector3f loc) {
        Quad quad;
        // 创建一个蓝色四边形
        if (DisplayGrid) {
            quad = new Quad(mapGrid3D.getCellsize()*2, mapGrid3D.getCellsize()*2);
        } else {
            quad = new Quad(mapGrid3D.getXLength()*2, mapGrid3D.getZWidth()*2);
        }

        //System.err.println(mapGrid3D.getXLength()+","+mapGrid3D.getZWidth());
        Geometry quadBoxGeometry = new Geometry("quadBox", quad);
        // 创建一个蓝色材质
        Material boxMaterial1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        boxMaterial1.setColor("Color", ColorRGBA.Blue);
       // boxMaterial1.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
//        boxMaterial1.getAdditionalRenderState().setWireframe(true);
        boxMaterial1.getAdditionalRenderState().setFaceCullMode(FaceCullMode.FrontAndBack);
        quadBoxGeometry.setMaterial(boxMaterial1);

        quadBoxGeometry.setLocalTranslation(loc);
        Quaternion y = new Quaternion().fromAngleAxis(90 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X);
        quadBoxGeometry.setLocalRotation(y);
        quadBoxGeometry.move(-1024, 0, -1024);
        // 将盒子添加到场景中
        // instancedNode.onTransformChange(quadBoxGeometry);
        Main.MapUIBox.attachChild(quadBoxGeometry);

        // MapNode.attachChild(quadBoxGeometry);
    }

    public void BOX(Vector3f loc) {

        // 创建一个蓝色盒子
        Box box = new Box(1, 1, 1);
        Geometry boxGeometry = new Geometry("Box", box);

        // 创建一个蓝色材质
        Material boxMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        boxMaterial.setColor("Color", ColorRGBA.White);
        boxGeometry.setMaterial(boxMaterial);
        boxGeometry.setLocalTranslation(loc);
        // 将盒子添加到场景中
        simpleApp.getRootNode().attachChild(boxGeometry);
    }

    /**
     * @return the DisplayGrid
     */
    public boolean getDisplayGrid() {
        return DisplayGrid;
    }

    /**
     * @param DisplayGrid the DisplayGrid to set
     */
    public void setDisplayGrid(boolean DisplayGrid) {
        this.DisplayGrid = DisplayGrid;
    }

    /**
     * 初始化战争迷雾
     *
     * @param imageSizeW
     * @param imageSizeH
     */
    public void initializeFowImag(int imageSizeW, int imageSizeH) {
        // Create the raw FOW overlay image

        ByteBuffer data = BufferUtils.createByteBuffer(imageSizeW * imageSizeH * 4); // square image, four bytes per color
        Image image = new Image(Image.Format.ABGR8, imageSizeW, imageSizeH, data, ColorSpace.Linear);
        // Create a raster that we can draw to
        raster = ImageRaster.create(image);
        // Create the texture to set to the FowMap on materials
        Texture2D fowMap = new Texture2D(image);

        // Set the texture for any material that will take it.
        // This is a JME trick that keeps us from having to remember to set it
        // on every material.
        simpleApp.getRootNode().addMatParamOverride(new MatParamOverride(VarType.Texture2D, "FowMap", fowMap));

        // Put some test data into the fog of war texture
        for( int i = 0; i <imageSizeW; i++ ) {
            for( int j = 0; j <imageSizeH; j++ ) { 
                MapArrays.add(0); 
            }
        }
        
        for (int i = 0; i < imageSizeW; i++) {
            for (int j = 0; j < imageSizeH; j++) {
                visited.add(0);
            }
        }
         for (int i = 0; i < imageSizeW; i++) {
            for (int j = 0; j < imageSizeH; j++) {
                IsVisible.add(0);
            }
        }
        System.out.println(MapArrays.size());
        
    }

    public static ArrayList setFowImag(int givenX, int givenY, ImageRaster raster) {
        long s = Instant.now().toEpochMilli();
        int radius = 128;
        
        float scale = (float)raster.getWidth() / mapGrid3D.getXLength(); // 计算缩放比例
        
            // 缩放给定点坐标
        int scaledX = (int)(givenX * scale);
        int scaledY = (int)(givenY * scale);
            // 缩放半径
        int scaledRadius = (int)(radius * scale);
        
        // 计算给定点所在的格子cellsize
        int gridX = (int)(scaledX / mapGrid3D.getCellsize());
        int gridY = (int)(scaledY / mapGrid3D.getCellsize());
    // 遍历半径范围内的格子
        ArrayList<Integer> VisionRange = new ArrayList<>();
        for (int x = Math.max(0, gridX - scaledRadius); x <= Math.min(raster.getHeight() - 1, gridX + scaledRadius); x++) {
            for (int y = Math.max(0, gridY - scaledRadius); y <= Math.min(raster.getWidth() - 1, gridY + scaledRadius); y++) {
                // 计算格子的实际坐标(在纹理网格中的位置坐标)
                int currentGridX = (int)(x * mapGrid3D.getCellsize());
                int currentGridY = (int)(y * mapGrid3D.getCellsize());

                // 计算给定点到当前格子的距离
                double distanceToGivenPoint = Math.sqrt(Math.pow(scaledX - currentGridX, 2) + Math.pow(scaledY - currentGridY, 2));

                // 如果距离小于或等于缩放后的半径,输出坐标
                if (distanceToGivenPoint <= scaledRadius) {
                    for (int i = 0; i <= mapGrid3D.getCellsize(); i++) {
                        for (int j = 0; j <= mapGrid3D.getCellsize(); j++) {
                            if (currentGridX + j >= 0 && currentGridX + j < raster.getWidth() && currentGridY + i >= 0 && currentGridY + i < raster.getHeight()) {
                                int q = currentGridX + j;
                                int w = currentGridY + i;
                                int index =  w * (int) raster.getWidth() + q;
                                //MapArrays.set(index, 1);
                                //SetVisible(index,1);
                                VisionRange.add(index);
                                //raster.setPixel(currentGridX + j, currentGridY + i, color);
                            }
                        }
                    }
                }
            }
        }
        System.err.println(" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
        
        return VisionRange;
        
    }
    
    public static void setFowImagAlpha(int givenX, int givenY, ImageRaster raster) {
        long s = Instant.now().toEpochMilli();
        int radius = 75;

        // 计算给定点所在的格子cellsize
        int gridX = (int) (givenX / mapGrid3D.getCellsize());
        int gridY = (int) (givenY / mapGrid3D.getCellsize());
        // 遍历半径范围内的格子
        for (int x = Math.max(0, gridX - radius); x <= Math.min(raster.getHeight() - 1, gridX + radius); x++) {
            for (int y = Math.max(0, gridY - radius); y <= Math.min(raster.getWidth() - 1, gridY + radius); y++) {
                // 计算格子的实际坐标(在空间中的位置坐标)
                int currentGridX = (int) (x * mapGrid3D.getCellsize());
                int currentGridY = (int) (y * mapGrid3D.getCellsize());

                // 计算给定点到当前格子的距离
                double distanceToGivenPoint = Math.sqrt(
                        Math.pow(givenX - currentGridX, 2) + Math.pow(givenY - currentGridY, 2)
                );

                // 如果距离小于或等于半径,输出坐标
                if (distanceToGivenPoint <= radius) {
                    for (int i = 0; i <= (int) mapGrid3D.getCellsize(); i++) {
                        for (int j = 0; j <= (int) mapGrid3D.getCellsize(); j++) {
                            if (currentGridX + j >= 0 && currentGridX + j < raster.getWidth() && currentGridY + i >= 0 && currentGridY + i < raster.getHeight()) {

                                ColorRGBA color = new ColorRGBA(0, 0, 0, 1f);
                                raster.setPixel(currentGridX + j, currentGridY + i, color);

//                                MapArrays.put(new Vector2f(j,i), 1);
                                
                            }

                        }

                    }

                }
            }
        }
        System.err.println(" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
        
    }
     
    
        public static void setFowImagAlphai(int givenX, int givenY, ImageRaster raster) {
        long s = Instant.now().toEpochMilli();
        int radius = 138;

        float scale = (float)raster.getWidth() / mapGrid3D.getXLength(); // 计算缩放比例
        
            // 缩放给定点坐标
        int scaledX = (int)(givenX * scale);
        int scaledY = (int)(givenY * scale);
            // 缩放半径
        int scaledRadius = (int)(radius * scale);
        
        // 计算给定点所在的格子cellsize
        int gridX = (int)(scaledX / mapGrid3D.getCellsize());
        int gridY = (int)(scaledY / mapGrid3D.getCellsize());
    // 遍历半径范围内的格子
        for (int x = Math.max(0, gridX - scaledRadius); x <= Math.min(raster.getHeight() - 1, gridX + scaledRadius); x++) {
            for (int y = Math.max(0, gridY - scaledRadius); y <= Math.min(raster.getWidth() - 1, gridY + scaledRadius); y++) {
                // 计算格子的实际坐标(在纹理网格中的位置坐标)
                int currentGridX = (int)(x * mapGrid3D.getCellsize());
                int currentGridY = (int)(y * mapGrid3D.getCellsize());

                // 计算给定点到当前格子的距离
                double distanceToGivenPoint = Math.sqrt(Math.pow(scaledX - currentGridX, 2) + Math.pow(scaledY - currentGridY, 2));

                // 如果距离小于或等于缩放后的半径,输出坐标
                if (distanceToGivenPoint <= scaledRadius) {
                    for (int i = 0; i <= mapGrid3D.getCellsize(); i++) {
                        for (int j = 0; j <= mapGrid3D.getCellsize(); j++) {
                            if (currentGridX + j >= 0 && currentGridX + j < raster.getWidth() && currentGridY + i >= 0 && currentGridY + i < raster.getHeight()) {
                                int q = currentGridX + j;
                                int w = currentGridY + i;
                                int index =  w * (int) raster.getWidth() + q;
                                visited.set(index, 2);
                                //raster.setPixel(currentGridX + j, currentGridY + i, color);
                            }
                        }
                    }
                }
            }
        }
        System.err.println(" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
        
    }
       // System.err.println(" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
        
public static void SetVisible(int index, int players) {
    // 获取索引处的元素
    int oldValue = MapArrays.get(index);
    // 将元素与 players 进行按位或操作
    int newValue = oldValue | players;
    // 将结果写回列表中
    MapArrays.set(index, newValue);
        // 获取索引处的元素
    int oldvisited = visited.get(index);
    // 将元素与 players 进行按位或操作
    int newvisited = oldvisited | players;
    // 将结果写回列表中
    visited.set(index, newvisited);
}

public static boolean IsVisible(int index, int players) {
    return (MapArrays.get(index) & players) > 0;
    
}

public static boolean WasVisible(int index, int players) {

    return (visited.get(index) & players) > 0;
}
}

Now it looks like this.

There’s nothing wrong with that in general, but there’s a lot more specifics than that :wink:

Glad you got it working.

I have not provided any specifics because in my case there are none.

1: fullscreen pass with additive blending and some value
2: draw a quad for each unit with a circle texture as filter and write 0 or discard

2 Likes