平滑 Java 绘制动画 [英] Smooth out Java paint animations

查看:64
本文介绍了平滑 Java 绘制动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想让我的应用程序绘制移动图像比当前绘制它们的方式更平滑一些.我不知道该怎么做.

I would like to make my app draw moving image a little smoother than how it is currently drawing them. I am not sure what to do to do that.

这是我的主游戏线程的样子:

This is what my main game Thread looks like:

@Override
public void run(){
    int delay = 500; //milliseconds
    ActionListener taskPerformer = new ActionListener(){
        @Override
        public void actionPerformed(ActionEvent evt){
            Car car = new Car();
            int speed = (int)(3 + Math.floor(Math.random() * (6 - 3)));
            car.setSpeed(speed);
            MainLoop.this.gameObjects.vehicles.add(car.create("/Media/Graphics/blueCar.png", width - 20, 78));
            car.driveTo(0, 78);
        }
    };
    new Timer(delay, taskPerformer).start();
    try{
        while(true){
            this.repaint();
            for(GameObject go : this.gameObjects.vehicles){
                // loops through objects to move them
                Vehicle vh = (Vehicle) go;
                this.moveVehicle(vh);
                if(vh.getX() <= vh.getDestX()){
                    vh.markForDeletion(true);
                }
            }
            this.gameObjects.destroyVehicles();
            Thread.sleep(1);
        }
    }catch(Exception e){
        e.printStackTrace();
    }
}

这是计算下一个 x/y 位置的项目的方法

This is a method that calculates the items next x/y position

protected void moveVehicle(Vehicle vh){
    int cx = vh.getX();
    int dx = vh.getDestX();
    int cy = vh.getY();
    int dy = vh.getDestY();
    // move along x axis
    // getMaxSpeed() = Number between 3 and 6
    if(cx > dx && vh.movingX() == -1){
        vh.setX(cx - vh.getMaxSpeed());
    }else if(cx < dx && vh.movingX() == 1){
        vh.setX(cx + vh.getMaxSpeed());
    }else{
        vh.setX(dx);
    }

    // move along y axis
    // getMaxSpeed() = Number between 3 and 6
    if(cy > dy && vh.movingY() == -1){
        vh.setY(cy - vh.getMaxSpeed());
    }else if(cy < dy && vh.movingY() == 1){
        vh.setY(cy + vh.getMaxSpeed());
    }else{
        vh.setY(dy);
    }
}

这是我的绘画方法:

@Override
public void paintComponent(Graphics graphics){
    super.paintComponent(graphics);
    Graphics2D g = (Graphics2D) graphics;

    for(GameObject go : gameObjects.vehicles){
        g.drawImage(go.getSprite(), go.getX(), go.getY(), this);
    }
}

这可能比需要的信息多,但我想知道,我应该怎么做才能使项目从 left ->右 top -> 尽可能流畅,没有太大的性能损失?

This is possibly more info than needed, but I would like to know, what should I do to make items move from left -> right top -> bottom as smoothly as possible, without much of a performance loss?

请求 sscce:

package sscce;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Sscce extends JPanel implements Runnable{

    ArrayList<Square> squares = new ArrayList<>();

    public Sscce(){
        JFrame frame = new JFrame();
        frame.setSize(500, 500);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.add(this);
        Thread t = new Thread(this);
        t.start();
    }

    public static void main(String[] args){
        new Sscce();
    }

    @Override
    public void run(){
        int delay = 500; //milliseconds
        ActionListener taskPerformer = new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent evt){
                Square squ = new Square();
                Sscce.this.squares.add(squ);
                squ.moveTo(0);
            }
        };
        new Timer(delay, taskPerformer).start();
        while(true){
            try{
                for(Square s : this.squares){
                    int objX = s.getX();
                    int desX = s.getDestX();
                    if(objX <= desX){
                        System.out.println("removing");
                        this.squares.remove(s);
                    }else{
                        s.setX(s.getX() - 10);
                    }
                }
                this.repaint();
                Thread.sleep(30);
            }catch(Exception e){
            }
        }
    }

    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        for(Square s : squares){
            g.setColor(Color.blue);
            g.fillRect(s.getX(), s.getY(), 50, 50);
        }
    }
}

class Square{

    public int x = 0, y = 0, destX = 0;

    public Square(){
        this.x = 400;
        this.y = 100;
    }

    public void moveTo(int destX){
        this.destX = destX;
    }

    public int getX(){
        return this.x;
    }
    public int getDestX(){
        return this.destX;
    }

    public void setX(int x){
        this.x = x;
    }

    public int getY(){
        return this.y;
    }
}

推荐答案

首先要注意的是,出于某种原因,我在 JPanel 下运行时遇到了实现 Runnable 的问题MacOS - 我不知道为什么,但这就是我将其移出的原因.

First note, for some reason, I had issues with the JPanel implementing Runnable when running it under MacOS - I have no idea why, but that's why I moved it out.

您的 squares 在用于绘制时可能会更新,这将导致异常(此外,在迭代列表时从列表中删除元素并不是一个好方法想法要么;))

It is possible for your squares to be update while it is been used to paint which will cause an exception (also, removing elements from a list while you're iterating it isn't a good idea either ;))

相反,我有两个列表.我有一个模型,列表可以修改它,然后是 paint 方法可以使用的绘制列表.这允许线程在绘制过程正在进行时修改模型.

Instead, I have two lists. I have a model, which the list can modify and then the paint list which the paint method can use. This allows the thread to modifiy the model, while a paint process is underway.

为了防止任何冲突,我添加了一个锁,它可以防止一个线程修改/访问绘制列表,而另一个线程将它锁定.

To prevent any clash, I've added in a lock, which prevents one thread from modifying/accessing the paint list, while another thread has it locked.

现在.回到真正的问题.您遇到的主要问题不是更新之间的时间长短,而是您移动的距离.缩短距离(使其变慢)并使更新标准化.

Now. Down to the real problem. The main issue you're having isn't the amount of time between the updates, but the distance you are moving. Reduce the distance (to make it slower) and standarise the updates.

大多数人不会注意到超过 25fps 的任何东西,所以尝试做更多的事情只会浪费 CPU 周期并使重绘管理器饿死,阻止它实际更新屏幕.

Most people won't notice anything much over 25fps, so trying to do much more then that is just wasting CPU cycles and starving the repaint manager, preventing it from actually updating the screen.

这是一种平衡的做法,可以肯定......

It's a balancing act to be sure...

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestAnimation11 extends JPanel {

    private ArrayList<Square> squares = new ArrayList<>();
    private ReentrantLock lock;

    public TestAnimation11() {
        lock = new ReentrantLock();
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }
                JFrame frame = new JFrame();
                frame.setSize(500, 500);
                frame.setLocationRelativeTo(null);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
                frame.add(TestAnimation11.this);
                Thread t = new Thread(new UpdateEngine());
                t.start();
            }
        });
    }

    public static void main(String[] args) {
        new TestAnimation11();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Square[] paint = null;
        lock.lock();
        try {
            paint = squares.toArray(new Square[squares.size()]);
        } finally {
            lock.unlock();
        }
        for (Square s : paint) {
            g.setColor(Color.blue);
            g.fillRect(s.getX(), s.getY(), 50, 50);
        }
    }

    public class UpdateEngine implements Runnable {

        private List<Square> model = new ArrayList<>(squares);

        @Override
        public void run() {
            int ticks = 0;
            List<Square> dispose = new ArrayList<>(25);
            while (true) {
                ticks++;
                dispose.clear();
                for (Square s : model) {
                    int objX = s.getX();
                    int desX = s.getDestX();
                    if (objX <= desX) {
                        dispose.add(s);
                    } else {
                        s.setX(s.getX() - 2);
                    }
                }
                model.removeAll(dispose);
                if (ticks == 11) {
                    Square sqr = new Square();
                    sqr.moveTo(0);
                    model.add(sqr);
                } else if (ticks >= 25) {
                    ticks = 0;
                }
                lock.lock();
                try {
                    squares.clear();
                    squares.addAll(model);
                } finally {
                    lock.unlock();
                }
                repaint();
                try {
                    Thread.sleep(40);
                } catch (Exception e) {
                }
            }
        }
    }

    class Square {

        public int x = 0, y = 0, destX = 0;

        public Square() {
            this.x = 400;
            this.y = 100;
        }

        public void moveTo(int destX) {
            this.destX = destX;
        }

        public int getX() {
            return this.x;
        }

        public int getDestX() {
            return this.destX;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return this.y;
        }
    }
}

这篇关于平滑 Java 绘制动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆