Java 2 d游戏。在平铺的图像上移动精灵

时间:2022-09-10 16:44:41

->Short Introduction, you can SKIP this part

I'm glad to be able to finally post on this platform, because I for myself have gained so much knowledge through this community; just by reading. So I wanted to say "Hello everybody, and thank you!

我很高兴终于能够在这个平台上发表文章,因为我自己通过这个社区获得了这么多的知识;仅仅通过阅读。所以我想说“大家好,谢谢大家!”

Actual Content(prologue):

Though I'm developing in Objective-C in my company, I am utterly interested in JAVA development. I am quite comfortable with the syntax but have some major troubles with awt/swing/Graphics2D.

虽然我是在Objective-C中开发的,但是我对JAVA开发非常感兴趣。我对语法很熟悉,但是有一些awt/swing/Graphics2D的主要问题。

The Concept:

JAVA Application with a 800*600 Frame. On this Frame you can see 16*12 tiles (i.e. gras.jpg or tree.jpg) with a size of 50px². Above this Frame a .png "player" is moving. The field is generated with a 2 dimensional int array[16][12] where a 0 symbolizes "gras" and 1 means "tree".

具有800*600帧的JAVA应用程序。在这个框架可以看到16 * 12块的大小(即gras.jpg或tree.jpg)50 px²。在这一帧的上方。png“播放器”正在移动。该字段生成的是一个二维整数数组[16][12],其中0代表“gras”,1表示“树”。

-> This is actually "working".

->这实际上是“工作”。

First Try:

Adding (frame.add(...)) 16*12 JLabels with imageIcon "gras" or "tree" to a JLayeredPane
Adding (frame.add(...)) class "entityLayer" wich is updated at 5ms with paint(Graphics g)
{
g.drawImage(imageIcon.getImage());
}
This Version "works" if I add either the field with tiles or the entity layer. In every case else either the entityLayer overlays the field with a grey background or the "player" is not visible under the field;

在JLayeredPane中添加(frame.add(…))16*12个带有imageIcon“gras”或“tree”的jlabel,添加(frame.add(…)类“entityLayer”,其更新时间为5ms,使用paint(Graphics g) {g.drawImage(imageIcon.getImage());如果我添加了块或实体层的字段,则该版本“工作”。在其他情况下,实体层或用灰色背景覆盖字段,或“player”在字段下不可见;

Second Try:

Making all the drawings in the entityLayer paint(Graphics g) method. But thats not what I intended. I want the field to be drawn once and the "player" with, like a translucent background, drawn above the field.

在实体层绘制(图形g)方法中绘制所有的图纸。但那不是我想要的。我想要绘制一次字段,而“player”则像半透明的背景,绘制在字段之上。

Question:

How can I get myself in a position to actually have two seperate layers, that are both equally independent with out having one overlapping the other? Im nearly 99% shure my attempt is wrong in some way.

我怎么才能让自己有两个独立的层,它们都是独立的,而不是互相重叠的?我几乎99%的确信我的尝试在某些方面是错误的。

Thank you right away.

谢谢你。

1 个解决方案

#1


1  

Instead of adding/drawing 16*12 JLabels, just draw the tile image 16*12 times?

不添加/绘制16*12的jlabel,只绘制16*12次的tile image ?

For things like this, I usually add a single JPanel to a JFrame, and override the JPanel's paint() method. Actually, I don't add a JPanel, I add a subclass of JPanel that I have created, you should do the same, as I will show you will need to add a field to the JPanel and override the paint method

对于这样的事情,我通常向JFrame添加一个JPanel,并覆盖JPanel的paint()方法。实际上,我没有添加JPanel,我添加了我创建的JPanel的子类,您也应该这样做,因为我将向您展示,您将需要向JPanel添加一个字段并覆盖paint方法

Your background tile drawing code might look similar to:

您的背景磁砖绘图代码可能类似于:

int tileWidth = 50;
int tileHeight = 50;
for ( int x = 0; x < 16; x++ )
{
    for ( int y = 0; y < 12; y++ )
    {
        Image tileImage;
        int tileType = fieldArray[x][y];

        switch ( tileType )
        {
            case 0:
            {
                tileImage = myGrassImage;
                break;
            }
            case 2:
            {
                tileImage = myTreeImage;
                break;
            }
        }

        Graphics2D g;
        g.drawImage(tileImage, x * tileWidth, y * tileHeight, null);
    }
}

A technique that works well for me is to separate the drawing logic of each layer into a separate class that implements the Painter interface (or a similar interface you define).

一种对我来说很有用的技术是将每个层的绘图逻辑分离到一个单独的类中,这个类实现Painter接口(或者您定义的类似接口)。

public class TilePainter implements Painter
{

    @Override
    public void paint( Graphics2D g, Object thePanel, int width, int height )
    {
        //The tile painting code I posted above would go here
        //Note you may need to add a constructor to this class
        //that provides references to the needed resources
        //Eg: images/arrays/etc
        //Or provides a reference to a object where those resources can be accessed
    }

}

Then for the player painter:

然后对球员油漆工说:

public class EntityPainter implements Painter
{

    @Override
    public void paint( Graphics2D g, Object thePanel, int width, int height )
    {
        g.drawImage(player.getImage(), player.getX(), player.getY(), null);
    }

}

If you are wondering why I am passing NULLs into the g.drawImage() function, its because for that overloaded function call the last parameter is an ImageObserver, which is something we do not need for this purpose.

如果您想知道为什么我要将NULLs传递到g.drawImage()函数中,那是因为对于重载的函数调用,最后一个参数是ImageObserver,这是我们不需要的。

Ok, so once you have your painters separated into different classes we need to make the JPanel capable of using them!

好,一旦你把你的油漆工分成不同的类,我们就需要让JPanel能够使用它们!

This is the field that you need to add to your JPanel:

这是您需要添加到JPanel的字段:

List<Painter> layerPainters;

Your constructor should look something like this:

你的构造函数应该是这样的:

public MyExtendedJPanel()
    {
        //I use an ArrayList because it will keep the Painters in order
        List<Painter> layerPainters = new ArrayList<Painter>();

        TilePainter tilePainter = new TilePainter(Does,This,Need,Args);
        EntityPainter entityPainter = new EntityPainter(Does,This,Need,Args);

        //Layers will be painted IN THE ORDER THEY ARE ADDED
        layerPainters.add(tilePainter);
        layerPainters.add(entityPainter);

    }

Now for the last but most important part:

最后也是最重要的部分:

@Override
public void paint( Graphics g )
{
    int width = getWidth();
    int height = getHeight();
    //Loops through all the layers in the order they were added
    //And tells them to paint onto this panels graphic context
    for(Painter painter : layerPainters)
    {
        painter.paint(g,this,width,height);
    }
}

#1


1  

Instead of adding/drawing 16*12 JLabels, just draw the tile image 16*12 times?

不添加/绘制16*12的jlabel,只绘制16*12次的tile image ?

For things like this, I usually add a single JPanel to a JFrame, and override the JPanel's paint() method. Actually, I don't add a JPanel, I add a subclass of JPanel that I have created, you should do the same, as I will show you will need to add a field to the JPanel and override the paint method

对于这样的事情,我通常向JFrame添加一个JPanel,并覆盖JPanel的paint()方法。实际上,我没有添加JPanel,我添加了我创建的JPanel的子类,您也应该这样做,因为我将向您展示,您将需要向JPanel添加一个字段并覆盖paint方法

Your background tile drawing code might look similar to:

您的背景磁砖绘图代码可能类似于:

int tileWidth = 50;
int tileHeight = 50;
for ( int x = 0; x < 16; x++ )
{
    for ( int y = 0; y < 12; y++ )
    {
        Image tileImage;
        int tileType = fieldArray[x][y];

        switch ( tileType )
        {
            case 0:
            {
                tileImage = myGrassImage;
                break;
            }
            case 2:
            {
                tileImage = myTreeImage;
                break;
            }
        }

        Graphics2D g;
        g.drawImage(tileImage, x * tileWidth, y * tileHeight, null);
    }
}

A technique that works well for me is to separate the drawing logic of each layer into a separate class that implements the Painter interface (or a similar interface you define).

一种对我来说很有用的技术是将每个层的绘图逻辑分离到一个单独的类中,这个类实现Painter接口(或者您定义的类似接口)。

public class TilePainter implements Painter
{

    @Override
    public void paint( Graphics2D g, Object thePanel, int width, int height )
    {
        //The tile painting code I posted above would go here
        //Note you may need to add a constructor to this class
        //that provides references to the needed resources
        //Eg: images/arrays/etc
        //Or provides a reference to a object where those resources can be accessed
    }

}

Then for the player painter:

然后对球员油漆工说:

public class EntityPainter implements Painter
{

    @Override
    public void paint( Graphics2D g, Object thePanel, int width, int height )
    {
        g.drawImage(player.getImage(), player.getX(), player.getY(), null);
    }

}

If you are wondering why I am passing NULLs into the g.drawImage() function, its because for that overloaded function call the last parameter is an ImageObserver, which is something we do not need for this purpose.

如果您想知道为什么我要将NULLs传递到g.drawImage()函数中,那是因为对于重载的函数调用,最后一个参数是ImageObserver,这是我们不需要的。

Ok, so once you have your painters separated into different classes we need to make the JPanel capable of using them!

好,一旦你把你的油漆工分成不同的类,我们就需要让JPanel能够使用它们!

This is the field that you need to add to your JPanel:

这是您需要添加到JPanel的字段:

List<Painter> layerPainters;

Your constructor should look something like this:

你的构造函数应该是这样的:

public MyExtendedJPanel()
    {
        //I use an ArrayList because it will keep the Painters in order
        List<Painter> layerPainters = new ArrayList<Painter>();

        TilePainter tilePainter = new TilePainter(Does,This,Need,Args);
        EntityPainter entityPainter = new EntityPainter(Does,This,Need,Args);

        //Layers will be painted IN THE ORDER THEY ARE ADDED
        layerPainters.add(tilePainter);
        layerPainters.add(entityPainter);

    }

Now for the last but most important part:

最后也是最重要的部分:

@Override
public void paint( Graphics g )
{
    int width = getWidth();
    int height = getHeight();
    //Loops through all the layers in the order they were added
    //And tells them to paint onto this panels graphic context
    for(Painter painter : layerPainters)
    {
        painter.paint(g,this,width,height);
    }
}