简体   繁体   中英

Repaint not being called

I'm making a simple maze solving algorithm (A* but that's not pertinent) and a GUI to display the agent navigating through the maze. I have a MapModel class that deals with the state and a MapView class that draws everything to a JPanel. I have the MapModel class call repaint everytime the state changes, but when I need my unit to animate (in the moveAgent() method), repaint isn't getting called. I've debugged the program and the repaint method is being reached every "frame" but MapView's paintComponent method isn't being called.

I'm relatively new to UI and animation, I haven't coded much else other than scripts and C programs, so I might be doing this completely wrong. Let me know if I should redesign any of my classes to fit better with standard java swing practices!

Here's my code. There are other classes, but I'm quite sure they're unrelated to the problem. I can post them as well if you'd like.

EDIT: I forgot to mention that repaint does work sometimes, just not in moveAgent(). When I call it from createRandomObstacles(), it paints correctly.

MapModel.java:

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.awt.Point;
import java.util.HashMap;
public class MapModel {
    int width, height, start_diameter, speed;
    ArrayList<Obstacle> obstacle_list = new ArrayList<Obstacle>();
    Agent agent;
    GridSpaceManager grid_manager;
    String current_algorithm = "NONE";
    ArrayList<Point> subgoal_list;
    MapView map_view;
    boolean animate = false;
    AnimationManager animation_manager = new AnimationManager();

    public MapModel(int size, MapView map_view)
    {
        this.width = size;
        this.height = size;
        this.map_view = map_view;
    }

    public void updateMapModel (){
        map_view.repaint();
    }
    //if obstacles can't fit then method returns
    public void createRandomObstacles(int num_obstacles, int start_diameter)
    {
        this.start_diameter = start_diameter;
        obstacle_list.clear();
        ArrayList<Point> point_list = new ArrayList<Point>();
        for(int i = 0; i < num_obstacles; i++) {
            int rand_x = (int)(Math.random()*(width-start_diameter))+start_diameter;
            int rand_y = (int)(Math.random()*(height-start_diameter))+start_diameter;
            point_list.add(new Point(rand_x, rand_y));

        }


        for(int i = 0; i < num_obstacles; i++) {


            double min_distance = Double.MAX_VALUE;
            Point p1 = point_list.get(i);
            for(int j = 0; j < num_obstacles; j++) {
                Point p2 = point_list.get(j);
                if(j != i && p1.distance(p2) < min_distance)
                    min_distance = p1.distance(p2);
            }
            min_distance = Math.sqrt(min_distance*min_distance/2);
            int min_int = (int)(min_distance/2);
            if(min_int > 1) {
                int width = (int)(Math.random()*(min_int)); 
                width = Math.min(width, Math.max(p1.x, this.width - p1.x));
                int height = (int)(Math.random()*(min_int));
                height = Math.min(height, Math.max(p1.y, this.height - p1.y));
                if(p1.x + width > this.width - start_diameter && p1.y + height > this.height - start_diameter) {
                    width = this.width - start_diameter - p1.x; 
                    height = this.height - start_diameter - p1.y;
                }

                if(!(width < 0 && height < 0))
                    obstacle_list.add(new Obstacle(p1.x, p1.y, width, height));
            }
        }

        updateMapModel();
    }

    public void moveAgent() {
        if(animation_manager.running) {
            animation_manager.act();

            double speed = Math.sqrt(width*height/10);
            Point p = subgoal_list.get(0);
            if(p.x == agent.x && p.y == agent.y) {
                if(subgoal_list.size() == 1) {
                    animate = false;
                    return;
                }
                subgoal_list.remove(0);
                p = subgoal_list.get(0);
            }

            double distance = Point.distance(agent.x, agent.y, p.x, p.y);
            double delta_x = (p.x - agent.x)/distance;
            double delta_y = (p.y - agent.y)/distance;
            double distance2 = Math.sqrt(delta_x*delta_x + delta_y+delta_y);
            if(distance2 > distance) {
                agent.x = p.x;
                agent.y = p.y;
            }
            else {
                agent.x += delta_x;
                agent.y += delta_y;
            }
            long sleep_time = animation_manager.timeTillNextFrame();
            if(sleep_time > 0)
                try {
                    Thread.sleep(sleep_time);
                }
                catch(InterruptedException e) {

                }
            updateMapModel();
            moveAgent();
        }
        else {
            endPathfinding();
        }
    }

    public void startAnimation(int agent_diameter, boolean animate) {
        agent = new Agent(0,0,agent_diameter);
        this.animate = animate;

        animation_manager.start();
        moveAgent();
    }

    public void startPathfinding(String algorithm, int agent_diameter, boolean animate) {

        if(algorithm.equals("A*")) {
            startAStar(agent_diameter);
            this.current_algorithm = "A*";
        }
        if(subgoal_list == null) {
            this.current_algorithm = "NONE";
        }
        startAnimation(agent_diameter, animate);
    }

    public void endPathfinding() {
        agent = null;
        subgoal_list = null;
        animation_manager.end();
        this.current_algorithm = "NONE";
    }

    public void startAStar(int agent_diameter) {
        grid_manager = new GridSpaceManager(obstacle_list, width, height);
        if(agent_diameter > start_diameter) return;
        agent = new Agent(agent_diameter/2, agent_diameter/2, agent_diameter);
        subgoal_list = AStar(grid_manager.getNode(0, 0), grid_manager.getNode(width - 1, height - 1));
    }

    public ArrayList<Point> AStar(SearchSpaceNode start, SearchSpaceNode goal) {
        Point start_point = start.point_list[0];
        Point goal_point = goal.point_list[0];
        PriorityQueue<SearchSpaceNode> open_set = new PriorityQueue<SearchSpaceNode>();
        ArrayList<SearchSpaceNode> closed_set = new ArrayList<SearchSpaceNode>();

        HashMap<SearchSpaceNode, SearchSpaceNode> came_from = new HashMap<SearchSpaceNode, SearchSpaceNode>();
        HashMap<SearchSpaceNode, Integer> g_score = new HashMap<SearchSpaceNode, Integer>();
        HashMap<SearchSpaceNode, Integer> f_score = new HashMap<SearchSpaceNode, Integer>();

        int start_f_value = (int)manhattan_distance(start,goal);

        g_score.put(start, 0);
        f_score.put(start, start_f_value);

        start.f_value = start_f_value;
        open_set.add(start);

        while(open_set.size() > 0) {
            SearchSpaceNode current = open_set.poll();


            if(current == goal) {
                ArrayList<Point> subgoal_list = reconstructPath(came_from, current);
                subgoal_list.add(goal_point);
                return subgoal_list;
            }

            closed_set.add(current);
            ArrayList<SearchSpaceNode> neighbors = current.getNeighbors();
            for(int i = 0; i < neighbors.size(); i++) {
                SearchSpaceNode neighbor = neighbors.get(i);
                if(closed_set.contains(neighbor))
                    continue;

                int tentative_g_score = g_score.get(current) + 1;

                if(!open_set.contains(neighbor) || !g_score.containsKey(neighbor) 
                        || tentative_g_score < g_score.get(neighbor)) {
                    int tentative_f_score = tentative_g_score + (int)manhattan_distance(neighbor, goal);

                    came_from.put(neighbor, current);
                    g_score.put(neighbor, tentative_g_score);
                    f_score.put(neighbor, tentative_f_score);
                    neighbor.f_value = tentative_f_score;
                    if(!open_set.contains(neighbor))
                        open_set.add(neighbor);
                }

            }
        }



        return null;
    }

    public ArrayList<Point> reconstructPath(
            HashMap<SearchSpaceNode,SearchSpaceNode> came_from,SearchSpaceNode current_node) {
        ArrayList<Point> subgoal_list = new ArrayList<Point>();


        while(came_from.containsKey(current_node)) {
            SearchSpaceNode temp = came_from.get(current_node); 
            subgoal_list.add(0, temp.point_list[0]);
            came_from.remove(current_node);
            current_node = temp;
        }
        return subgoal_list;
    }

    public double manhattan_distance(SearchSpaceNode start, SearchSpaceNode goal) {
        Point start_point = start.point_list[0];
        Point goal_point = goal.point_list[0];
        return Math.sqrt(Math.pow(goal_point.x - start_point.x,2) + Math.pow(goal_point.x - start_point.x,2));
        //return Math.abs(goal_point.x - start_point.x) + Math.abs(goal_point.y - start_point.y);
    }

    public String algorithmRunning(){return current_algorithm;}
}

class AnimationManager {
    boolean running = false;
    int frame_number = -1;
    int milliseconds_per_frame = 500;
    long start_time = -1;


    public void start() {
        running  = true;
        start_time = System.currentTimeMillis();
        frame_number = 0;
    }

    public void end() {
        running = false;
        start_time = frame_number = -1;
    }

    public void act() {
        frame_number++;
    }
    public long timeTillNextFrame() {
        return  (frame_number + 1)*milliseconds_per_frame - (System.currentTimeMillis() - start_time);
    }
}

MapView.java:

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import java.util.ArrayList;

public class MapView extends JPanel{

    private int size, actual_map_size;
    MapModel map_model;
    Point current_subgoal = null;

    int frame_number = 0;
    long start_time;
    public MapView(int size)
    {
        this.setBounds(0,200,600,600);

        this.size = size;
    }
    boolean starting_print = true;
    public void paintComponent(Graphics g) {

        int x_offset = 10, y_offset = 10;
        super.paintComponent(g);
        g.setColor(Color.BLACK);
        g.fillRect((600-size)/2 + x_offset - 2, y_offset - 2,size + 5, size + 5);
        g.setColor(Color.WHITE);
        g.fillRect((600-size)/2 + x_offset - 1, y_offset - 1,size+3,size+3);

        g.setColor(Color.GREEN);

        if(this.map_model != null) {
            ArrayList<Obstacle> obstacle_list = map_model.obstacle_list;
            for(int i = 0; i < obstacle_list.size(); i++) {
                Obstacle o = obstacle_list.get(i);
                g.fillRect(scaleX(o.x) + x_offset,scale(o.y) + y_offset,
                        scale(o.width),scale(o.height));
            }

            g.setColor(Color.RED);
            if(map_model.current_algorithm.equals("A*")) {
                GridSpaceManager grid = map_model.grid_manager;

                for(int i = 0; i < map_model.width; i++) {
                    for(int j = 0; j< map_model.height; j++) {
                        SearchSpaceNode node = grid.getNode(i, j);
                        if(node != null) {
                            Point p = node.point_list[0];
                            g.drawRect(scaleX(p.x) + x_offset, scale(p.y) + y_offset, scale(1), scale(1));
                        }
                    }
                }
            }

            g.setColor(Color.BLUE);
            Agent agent = map_model.agent;


            if(agent != null) {
                g.fillOval(scaleX(agent.x) + x_offset, scale(agent.y) + y_offset,
                        Math.max(2,scale(agent.diameter)), Math.max(2,scale(agent.diameter)));
            }

        }

    }

    private int scaleX(int x) {
        double scale = ((double)size) / ((double)actual_map_size);
        return (int)(((double)x)*scale)+(600-size)/2;
    }
    private int scale(int val) {
        double scale = ((double)size) / ((double)actual_map_size);
        return (int)(((double)val)*scale);
    }

    public void setMapModel(MapModel map) {
        this.map_model = map;
    }

    public void setMapScale(int actual_map_size) {
        this.actual_map_size = actual_map_size;
    }
}

I redesigned my code to use the Timer class at the suggestion of MadProgrammer. It was a much neater way to get my animations working! Here's the new code below. The MapView class remains unchanged.

MapModel.java

import java.util.ArrayList;
import javax.swing.*;
import java.awt.event.*;

import java.util.PriorityQueue;
import java.awt.Point;
import java.util.HashMap;
public class MapModel implements ActionListener{
    boolean running = false;
    int milliseconds_per_frame = 500;
    Timer timer = new Timer(milliseconds_per_frame, this);

    int width, height, start_diameter, speed;
    ArrayList<Obstacle> obstacle_list = new ArrayList<Obstacle>();
    Agent agent;
    GridSpaceManager grid_manager;
    String current_algorithm = "NONE";
    ArrayList<Point> subgoal_list;
    MapView map_view;
    boolean animate = false;

    public MapModel(int size, MapView map_view)
    {
        this.width = size;
        this.height = size;
        this.map_view = map_view;
    }

    public void updateMapModel (){
        map_view.repaint();
    }
    //if obstacles can't fit then method returns
    public void createRandomObstacles(int num_obstacles, int start_diameter)
    {
        this.start_diameter = start_diameter;
        obstacle_list.clear();
        ArrayList<Point> point_list = new ArrayList<Point>();
        for(int i = 0; i < num_obstacles; i++) {
            int rand_x = (int)(Math.random()*(width-start_diameter))+start_diameter;
            int rand_y = (int)(Math.random()*(height-start_diameter))+start_diameter;
            point_list.add(new Point(rand_x, rand_y));
            /*
            double min_distance = Double.MAX_VALUE;
            Point p1 = point_list.get(i);
            for(int j = 0; j < point_list.size(); j++) {
                Point p2 = point_list.get(j);
                if(j != i && p1.distance(p2) < min_distance)
                    min_distance = p1.distance(p2);
            }
            min_distance = Math.sqrt(min_distance*min_distance/2);
            int min_int = (int)(min_distance/2);
            if(min_int > 1) {
                int width = (int)(Math.random()*(min_int)); 
                width = Math.min(width, Math.max(p1.x, this.width - p1.x));
                int height = (int)(Math.random()*(min_int));
                height = Math.min(height, Math.max(p1.y, this.height - p1.y));
                if(p1.x + width > this.width - start_diameter && p1.y + height > this.height - start_diameter) {
                    width = this.width - start_diameter - p1.x; 
                    height = this.height - start_diameter - p1.y;
                }

                if(!(width < 0 && height < 0))
                    obstacle_list.add(new Obstacle(p1.x, p1.y, width, height));
            }
            */
        }


        for(int i = 0; i < num_obstacles; i++) {


            double min_distance = Double.MAX_VALUE;
            Point p1 = point_list.get(i);
            for(int j = 0; j < num_obstacles; j++) {
                Point p2 = point_list.get(j);
                if(j != i && p1.distance(p2) < min_distance)
                    min_distance = p1.distance(p2);
            }
            min_distance = Math.sqrt(min_distance*min_distance/2);
            int min_int = (int)(min_distance/2);
            if(min_int > 1) {
                int width = (int)(Math.random()*(min_int)); 
                width = Math.min(width, Math.max(p1.x, this.width - p1.x));
                int height = (int)(Math.random()*(min_int));
                height = Math.min(height, Math.max(p1.y, this.height - p1.y));
                if(p1.x + width > this.width - start_diameter && p1.y + height > this.height - start_diameter) {
                    width = this.width - start_diameter - p1.x; 
                    height = this.height - start_diameter - p1.y;
                }

                if(!(width < 0 && height < 0))
                    obstacle_list.add(new Obstacle(p1.x, p1.y, width, height));
            }
        }

        updateMapModel();
    }

    public void moveAgent() {
        if(animate) {


            double speed = Math.sqrt(width*height/10);
            Point p = subgoal_list.get(0);
            if(p.x == agent.x && p.y == agent.y) {
                if(subgoal_list.size() == 1) {
                    animate = false;
                    return;
                }
                subgoal_list.remove(0);
                p = subgoal_list.get(0);
            }

            double distance = Point.distance(agent.x, agent.y, p.x, p.y);
            double delta_x = (p.x - agent.x)/distance;
            double delta_y = (p.y - agent.y)/distance;
            double distance2 = Math.sqrt(delta_x*delta_x + delta_y+delta_y);
            if(distance2 > distance) {
                agent.x = p.x;
                agent.y = p.y;
            }
            else {
                agent.x += delta_x;
                agent.y += delta_y;
            }
            updateMapModel();
        }
        else {
            endPathfinding();
        }
    }

    public void startAnimation(int agent_diameter, boolean animate) {
        agent = new Agent(0,0,agent_diameter);
        this.animate = animate;
        timer.start();
        moveAgent();
    }

    public void startPathfinding(String algorithm, int agent_diameter, boolean animate) {

        if(algorithm.equals("A*")) {
            startAStar(agent_diameter);
            this.current_algorithm = "A*";
        }
        if(subgoal_list == null) {
            this.current_algorithm = "NONE";
        }
        startAnimation(agent_diameter, animate);
    }

    public void endPathfinding() {
        agent = null;
        subgoal_list = null;
        timer.stop();
        this.current_algorithm = "NONE";
    }

    public void startAStar(int agent_diameter) {
        grid_manager = new GridSpaceManager(obstacle_list, width, height);
        if(agent_diameter > start_diameter) return;
        agent = new Agent(agent_diameter/2, agent_diameter/2, agent_diameter);
        subgoal_list = AStar(grid_manager.getNode(0, 0), grid_manager.getNode(width - 1, height - 1));
    }

    public ArrayList<Point> AStar(SearchSpaceNode start, SearchSpaceNode goal) {
        Point start_point = start.point_list[0];
        Point goal_point = goal.point_list[0];
        PriorityQueue<SearchSpaceNode> open_set = new PriorityQueue<SearchSpaceNode>();
        ArrayList<SearchSpaceNode> closed_set = new ArrayList<SearchSpaceNode>();

        HashMap<SearchSpaceNode, SearchSpaceNode> came_from = new HashMap<SearchSpaceNode, SearchSpaceNode>();
        HashMap<SearchSpaceNode, Integer> g_score = new HashMap<SearchSpaceNode, Integer>();
        HashMap<SearchSpaceNode, Integer> f_score = new HashMap<SearchSpaceNode, Integer>();

        int start_f_value = (int)manhattan_distance(start,goal);

        g_score.put(start, 0);
        f_score.put(start, start_f_value);

        start.f_value = start_f_value;
        open_set.add(start);

        while(open_set.size() > 0) {
            SearchSpaceNode current = open_set.poll();


            if(current == goal) {
                ArrayList<Point> subgoal_list = reconstructPath(came_from, current);
                subgoal_list.add(goal_point);
                return subgoal_list;
            }

            closed_set.add(current);
            ArrayList<SearchSpaceNode> neighbors = current.getNeighbors();
            for(int i = 0; i < neighbors.size(); i++) {
                SearchSpaceNode neighbor = neighbors.get(i);
                if(closed_set.contains(neighbor))
                    continue;

                int tentative_g_score = g_score.get(current) + 1;

                if(!open_set.contains(neighbor) || !g_score.containsKey(neighbor) 
                        || tentative_g_score < g_score.get(neighbor)) {
                    int tentative_f_score = tentative_g_score + (int)manhattan_distance(neighbor, goal);

                    came_from.put(neighbor, current);
                    g_score.put(neighbor, tentative_g_score);
                    f_score.put(neighbor, tentative_f_score);
                    neighbor.f_value = tentative_f_score;
                    if(!open_set.contains(neighbor))
                        open_set.add(neighbor);
                }

            }
        }



        return null;
    }

    public ArrayList<Point> reconstructPath(
            HashMap<SearchSpaceNode,SearchSpaceNode> came_from,SearchSpaceNode current_node) {
        ArrayList<Point> subgoal_list = new ArrayList<Point>();


        while(came_from.containsKey(current_node)) {
            SearchSpaceNode temp = came_from.get(current_node); 
            subgoal_list.add(0, temp.point_list[0]);
            came_from.remove(current_node);
            current_node = temp;
        }
        return subgoal_list;
    }

    public double manhattan_distance(SearchSpaceNode start, SearchSpaceNode goal) {
        Point start_point = start.point_list[0];
        Point goal_point = goal.point_list[0];
        return Math.sqrt(Math.pow(goal_point.x - start_point.x,2) + Math.pow(goal_point.x - start_point.x,2));
        //return Math.abs(goal_point.x - start_point.x) + Math.abs(goal_point.y - start_point.y);
    }

    public String algorithmRunning(){return current_algorithm;}

    public void actionPerformed(ActionEvent e) {
        if(animate) {
            moveAgent();
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM