Java:使用仿射变换时游戏速度变慢

时间:2018-07-01 10:39:19

标签: java affinetransform

我想创建一个类似于SpaceInvaders的游戏,但是外星人没有射击屏幕底部,而是射击弹丸。我想创建的一种外星人(在下面的代码中)变成 到45度并返回。我使用仿射变换进行了尝试,但是每次他们转弯时,游戏速度都会减慢一半。玩家和弹丸以一半的速度移动。下面的代码是创建JPanel的类 并绘制所有内容。

import javax.swing.Timer;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.*;
import java.util.*;
import javax.swing.JPanel;
import javax.imageio.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.io.IOException;
import javax.swing.*;

public class WELTZEICHNER2 extends JPanel implements  ActionListener , 
KeyListener
{
Player p;
Timer t = new Timer (5, this);
ArrayList<ANGRIFF> ziele = new ArrayList<ANGRIFF>();
ArrayList<ANGRIFF> ziele2 = new ArrayList<ANGRIFF>();
ArrayList<ALIEN1> aliens1 = new ArrayList<ALIEN1>();
private boolean left,right,space;
private int lastshot = 100;
private int score =0;
BufferedImage image;
BufferedImage image2;
BufferedImage image3;
BufferedImage image4;
int count = 0;
int count2 = 0;
int d = 0;

public WELTZEICHNER2()
{
    setDoubleBuffered(true);
    p = new Player(500,900,100000);
    t.start();
    addKeyListener(this);
    setFocusable(true);

    URL resource = getClass().getResource("alien2.png");
    URL resource2 = getClass().getResource("background.png");  
    URL resource3 = getClass().getResource("raumschifftest.png"); 
    URL resource4 = getClass().getResource("kreislertest.png");

    try {
        image = ImageIO.read(resource);
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        image2 = ImageIO.read(resource2);
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        image3 = ImageIO.read(resource3);
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        image4 = ImageIO.read(resource4);
    } catch (IOException e) {
        e.printStackTrace();
    }
    for (int i= 0;i < 20;i++)
    {
        for (int j =0;j <5;j++)
        {

            aliens1.add(new ALIEN1(70+i*90,80+j*70,1));

        }
    }

}

public void erzeugeANGRIFF()
{
    ANGRIFF b = new ANGRIFF(p.getxN() + 11, p.getyN(),true);
    ziele2.add(b);

}

public void paintComponent(Graphics g)
{
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    g.drawImage(image2,1,1,this); //background image
    g.drawImage(image3,p.getxN(),p.getyN(),this); //player image

    for (ANGRIFF b : ziele)
    {
        g2.setColor(Color.RED);
        g2.fill( new Ellipse2D.Double(b.getxN(),b.getyN(),5,10)); //alien´s projectiles
    }
    for (ANGRIFF b : ziele2)
    {
        g2.setColor(Color.GREEN);
        g2.fill( new Ellipse2D.Double(b.getxN(),b.getyN(),5,10)); // player´s projectiles
    }

    for (ALIEN1 i : aliens1) //draw alien images
    {

        if(count2 > 10000)
        {
            AffineTransform trans = new AffineTransform();
            trans.rotate(Math.toRadians(45), image4.getWidth() / 2, image4.getHeight() / 2);
            BufferedImage rotated = new BufferedImage(image4.getWidth(),
                    image4.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
            Graphics2D g3 = rotated.createGraphics();
            g3.drawImage(image4, trans, null);
            g2.drawImage(rotated,i.getxN(),i.getyN(),null);

        }
        else
        {
            g.drawImage(image4,i.getxN(),i.getyN(),this);
        }

    }
    g2.setColor(Color.RED);
    g2.drawString("Score:"+ score,5,15);
    g2.drawString("Health:"+ p.health,5,30);
    g2.drawString("Count:"+ count,5,45);

    if(p.health == 0) //Game Over screen
    {
        g2.setColor(Color.BLACK);
        g2.fill(new Rectangle2D.Double(1,1,1920,1080));
        g2.setColor(Color.RED);
        String text = "Game Over";
        Font endtext = new Font("TimesNewRoman",Font.PLAIN, 200 );
        g2.setFont(endtext);
        g2.drawString(text,450,540);
    }

}

public void actionPerformed(ActionEvent e)
{   
    if ( right == true)
    {
        p.right();
    }
    if (left == true)
    {
        p.left();
    }

    if(space == true && lastshot < 0)
    {
        erzeugeANGRIFF();
        lastshot = 100;
    }

    lastshot -=1;
    int bulletCount =ziele.size();
    int bulletCount2 =ziele2.size();

    int Alien1Count = aliens1.size();
    ArrayList<Integer> remANGRIFF= new ArrayList<Integer>();
    ArrayList<Integer> remANGRIFF2= new ArrayList<Integer>();
    ArrayList<Integer>remAlien1=new ArrayList<Integer>();

    for( int i = 0; i < bulletCount2;i++)
    {
        ANGRIFF b = ziele2.get(i);
        b.bewegeANGRIFF();
        if (b.getyN() >1000 )
        {
            remANGRIFF2.add(i);
        }

        for (int j =0;j< Alien1Count;j++ )
        {

            ALIEN1 n = aliens1.get(j);
            if (b.checkCollision(n) && b.player == true)
            {

                n.health -=1;
                score +=50;
                if (n.health <= 0)
                {
                    remAlien1.add(j);
                    score +=100;

                }
                remANGRIFF2.add(i);
            }
        }

    }
    for( int i = 0; i < bulletCount;i++)
    {
        ANGRIFF b = ziele.get(i);
        b.bewegeANGRIFF();
        if (b.getyN() < -100 )
        {
            remANGRIFF.add(i);
        }
        if (b.checkCollision(p) && b.player == false)
        {

            p.health -=50;

            if (p.health <= 0)
            {
                p.health = 0;

            }
            remANGRIFF.add(i);
        }
    }

    for (ALIEN1 i : aliens1)
    {
        // i.Bewegungsmuster();
        count2++;
        if(count2 > 20000)
        {
            count2 = 0;
        }
        if (i.newANGRIFF())
        {
            ziele.add(new ANGRIFF(i.getxN()+50,i.getyN()+50,false));
        }

    }
    for (int i: remANGRIFF)
    {
        if(i < ziele.size())
        {
            ziele.remove(i);
        }
    }
    for (int i: remANGRIFF2)
    {
        if(i < ziele2.size())
        {
            ziele2.remove(i);
        }
    }

    for (int i: remAlien1)
    {
        if (i<aliens1.size())
        {
            aliens1.remove(i);
        }

    }

    repaint();
}

public void keyPressed(KeyEvent e)
{
    int code = e.getKeyCode();

    if ( code == KeyEvent.VK_RIGHT)
    {
        right = true;
    }
    if ( code == KeyEvent.VK_LEFT)
    {
        left = true;
    }

    if ( code == KeyEvent.VK_SPACE)
    {
        space = true;
    }
}

public void keyReleased(KeyEvent e)
{
    int code = e.getKeyCode();

    if ( code == KeyEvent.VK_RIGHT)
    {
        right = false;
    }
    if ( code == KeyEvent.VK_LEFT)
    {
        left = false;
    }
    if ( code == KeyEvent.VK_SPACE)
    {
        space = false;
        lastshot =0;
    }
}

public void keyTyped(KeyEvent e)
{
    int code = e.getKeyCode();
    if ( code == KeyEvent.VK_SPACE)
    {
        erzeugeANGRIFF();
    }

}

}

这是开始游戏的类。

import javax.swing.*;

public class start
{

  public static void main(String[] args)
  {
    //System.setProperty("sun.java2d.d3d", "true");
    //System.setProperty("sun.java2d.noddraw", "false");
    //-Dsun.java2d.noddraw=false;
    JFrame f = new JFrame();
    WELTZEICHNER2 d = new WELTZEICHNER2();
    f.setSize(1920,1080);
    f.setTitle("BlueJ Space Invader");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     f.add(d);
    f.setVisible(true);


  }   
}

感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

您是“核心”问题在这里...

if(count2 > 10000)
{
    AffineTransform trans = new AffineTransform();
    trans.rotate(Math.toRadians(45), image4.getWidth() / 2, image4.getHeight() / 2);
    BufferedImage rotated = new BufferedImage(image4.getWidth(),
            image4.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
    Graphics2D g3 = rotated.createGraphics();
    g3.drawImage(image4, trans, null);
    g2.drawImage(rotated,i.getxN(),i.getyN(),null);

}

这会在每个绘制周期中创建许多寿命很短的对象,这给GC带来了额外的负担,从而降低了程序运行速度-更不用说创建BufferedImage的时间了

更好的解决方案是简单地旋转当前Graphics上下文。问题是,它很快就会变得非常复杂。

因此,基本上,我会做的是使用AffineTransform将原点/偏移量转换为要绘制的对象的位置。这样,旋转就变得非常简单,只需围绕图像的中心点旋转,然后在0x0处绘制图像即可。

技巧是在完成后重置转换。在这里,手动创建Graphics上下文的另一个副本,对其进行转换,绘制图像,然后处置该副本非常方便。

if (count2 > 10000) {
    AffineTransform trans = new AffineTransform();
    trans.translate(i.getxN(), i.getyN());
    trans.rotate(Math.toRadians(45), image4.getWidth() / 2, image4.getHeight() / 2);
    //BufferedImage rotated = new BufferedImage(image4.getWidth(),
    //image4.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
    Graphics2D g3 = (Graphics2D) g2.create();
    g3.setTransform(trans);
    //g3.drawImage(image4, trans, null);
    g3.drawImage(image4, 0, 0, null);
    g3.dispose();

}

在测试您的代码时,我目睹了帧速率不均。 ActionListener的间隔被调用的时间间隔远远大于5毫秒。到执行30,000个周期时,它已经平均为75毫秒并且正在缓慢增加,这表明您还有更多问题要处理。

着重ArrayList以及对象的创建/处置,并考虑使用对象的“池”以在可能的情况下进一步减少GC开销

您可以查看Swing animation running extremely slow作为示例。

PS @大约300,000个周期,每次更新的平均更新周期为200毫秒:P