2016-09-30 5 views
0

Ich mache gerade einen RPG Hack und slash in Java mit nur den Standardbibliotheken.Überlappung von Buttons mit Leinwand für Mauseingabe

Für diese Spiele hatte ich eine CustomButton Klasse in einem unabhängigen Paket, das ich einfach kopieren und in fast alle meine Spiele einfügen und dann einige spielspezifische Änderungen vornehmen.

Dies ist die Klasse ::

import java.awt.Canvas; 
import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.Point; 
import java.awt.Rectangle; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.image.BufferedImage; 
import java.awt.image.ColorModel; 
import java.awt.image.WritableRaster; 

import objects.Collectable; 

public final class CustomButton implements MouseListener { 

    // private BufferedImage image; 
    private String text = ""; 
    private boolean pressed = false; 
    private int x; 
    private int y; 
    private int width; 
    private int height; 
    private Color currentColor, defaultColor, hoverColor, pressColor; 
    private Point mousePoint = new Point(0, 0); 
    private ButtonListener btnListener = null; 
    private Canvas canvasObject; 
    private BufferedImage image; 
    private BufferedImage darkImage; 
    private String actionCommand = "default"; 
    private Collectable object; 

    private boolean enabled; 
    /* 
    * private CustomButton(Canvas canvasObject,JFrame frame) { this.x=100; 
    * this.y=100; this.width=100; this.height=100; 
    * 
    * canvasObject.addMouseListener(this); currentColor=new Color(255,255,255); 
    * defaultColor=new Color(255,255,255); hoverColor=new Color(255,255,255); 
    * pressColor=new Color(255,255,255); } 
    */ 

    public CustomButton(int x, int y, int width, int height, Canvas canvasObject) { 
     this.x = x; 
     this.y = y; 
     this.width = width; 
     this.height = height; 
     this.canvasObject = canvasObject; 
     canvasObject.addMouseListener(this); 
     currentColor = Color.GREEN; 
     currentColor = new Color(255, 255, 255); 
     defaultColor = new Color(255, 255, 255); 
     hoverColor = new Color(255, 255, 255); 
     pressColor = new Color(255, 255, 255); 
     enabled = true; 
    } 

    public CustomButton(int x, int y, int width, int height, Canvas canvasObject, BufferedImage image, 
      Collectable object) { 
     this.image = image; 
     this.darkImage = darkenImage(image); 
     this.x = x; 
     this.y = y; 
     this.width = width; 
     this.height = height; 
     canvasObject.addMouseListener(this); 
     currentColor = Color.GREEN; 
     currentColor = new Color(255, 255, 255); 
     defaultColor = new Color(255, 255, 255); 
     hoverColor = new Color(255, 255, 255); 
     pressColor = new Color(255, 255, 255); 
     this.canvasObject = canvasObject; 
     this.object = object; 

     enabled = true; 
    } 

    public void render(Graphics g) { 
     if (image == null) { 
      g.setColor(currentColor); 
      if (!pressed) 
       g.fillRect(this.x, this.y, width, height); 
      else 
       g.fill3DRect(this.x, this.y, width, height, true); 
      g.setColor(Color.BLACK); 
      g.drawString(text, this.x + 10, this.y + 15); 
     } else { 
      if (enabled) { 
       g.drawImage(image, x, y, width, height, null); 
      } else { 
       g.drawImage(darkImage, x, y, width, height, null); 
      } 
     } 
    } 

    public Rectangle getBounds() { 
     return new Rectangle(x, y, width, height); 

    } 

    public void tick() { 
     mousePoint = getMouseLocation(); 
     changeColor(); 
    } 

    private Point getMouseLocation() { 
     int x = 0; 
     int y = 0; 

     try { 
      x = (int) (canvasObject.getMousePosition().getX()); 
      y = (int) (canvasObject.getMousePosition().getY()); 
     } catch (NullPointerException nl) { 
     } 

     return new Point(x, y); 
    } 

    public void changeColor() { 

     if (!pressed) { 
      if (getBounds().contains(mousePoint)) 
       currentColor = hoverColor; 
      else 
       currentColor = defaultColor; 
     } else { 
      currentColor = pressColor; 
     } 

    } 

    public void addButtonListener(ButtonListener btnListener) { 
     this.btnListener = btnListener; 
    } 

    public void mouseEntered(MouseEvent e) { 
    } 

    public void mouseExited(MouseEvent e) { 
    } 

    public void mouseClicked(MouseEvent e) { 
     if (enabled) { 
      if (btnListener != null) { 

       if (getBounds().contains(mousePoint)) { 
        ButtonID id = ButtonID.UNDETERMINABLE; 
        if (e.getButton() == MouseEvent.BUTTON1) id = ButtonID.LEFT; 
        if (e.getButton() == MouseEvent.BUTTON2) id = ButtonID.RIGHT; 

        btnListener.buttonClicked(new ButtonEvent(id, object, actionCommand)); 
       } 
      } 
     } 
    } 

    public void mousePressed(MouseEvent e) { 
     if (getBounds().contains(mousePoint)) pressed = true; 
    } 

    public void mouseReleased(MouseEvent e) { 
     pressed = false; 
    } 

    public void setActionCommand(String actionCommand) { 
     this.actionCommand = actionCommand; 
    } 

    public String getText() { 
     return text; 
    } 

    public void setText(String text) { 
     this.text = text; 
    } 

    public void setDefaultColor(Color c) { 
     defaultColor = c; 
    } 

    public void setHoverColor(Color c) { 
     hoverColor = c; 
    } 

    public void setPressColor(Color c) { 
     pressColor = c; 
    } 

    public Collectable getObject() { 
     return object; 
    } 

    public void setObject(Collectable object) { 
     this.object = object; 
    } 

    public void destroy() { 
     canvasObject.removeMouseListener(this); 
    } 

    public void disable() { 
     enabled = false; 
    } 

    public void enable() { 
     enabled = true; 
    } 

    public boolean isEnabled() { 
     return enabled; 
    } 

    private BufferedImage darkenImage(BufferedImage image) { 
     int width = image.getWidth(); 
     int height = image.getHeight(); 
     image = deepCopy(image); 

     WritableRaster raster = image.getRaster(); 

     for (int xx = 0; xx < width; xx++) { 
      for (int yy = 0; yy < height; yy++) { 
       int[] pixels = raster.getPixel(xx, yy, (int[]) null); 
       pixels[0] -= 50; 
       pixels[1] -= 50; 
       pixels[2] -= 50; 

       pixels[0] = Math.max(pixels[0], 0); 
       pixels[1] = Math.max(pixels[0], 0); 
       pixels[2] = Math.max(pixels[0], 0); 

       raster.setPixel(xx, yy, pixels); 
      } 
     } 
     return image; 
    } 

    private BufferedImage deepCopy(BufferedImage bi) { 
     ColorModel cm = bi.getColorModel(); 
     boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); 
     WritableRaster raster = bi.copyData(null); 
     return new BufferedImage(cm, raster, isAlphaPremultiplied, null); 
    } 

} 

Wie Sie wahrscheinlich aus der mouseClicked() Methode, die ein Ereignis an den ButtonListener class.The ButtonListener Schnittstelle deklariert wird auch im Paket gesendet wird, sehen können.

Diese Schaltfläche wird auf der Zeichenfläche selbst gezeichnet. In der Levelmap befindet sich beispielsweise eine Schaltfläche in der unteren rechten Ecke, die beim Klicken das Inventar öffnet. Diese Schaltfläche heißt btn_INV.

Bis jetzt habe ich Eingaben für das Bewegen des Spielers durch die Tastatur genommen. Aber ich plane, keyInput zur Mauseingabe zu ändern, wo der Spieler zu der Fliese sich bewegt, auf die der Benutzer klickt.

Dafür habe ich eine Klasse machen muß, sagen MouseInput die MouseListener implementiert .Jetzt das Problem, dass, wenn ich btn_INV klicken, wird nicht nur die Taste ein Ereignis erzeugen, sondern auch, weil die Taste tatsächlich auf der Leinwand gezeichnet , die MouseInput Klasse wird auch ein Ereignis betreffs der Fliese erhalten, zu der der Spieler sich bewegen will. Jetzt dachte ich, dass wenn die MouseInput Klasse eine MouseEvent erhält, es mit den Knöpfen überprüfen wird, als wenn es einen Mausklick auf der Leinwand gibt , die Tasten immer wird informiert, obwohl es keine ButtonEvent erzeugen kann, wie Sie aus dem Code sehen können.Aber das ist eine ziemlich schlechte Methode und sehr ineffizient.So will ich eine andere Methode dafür.

HINWEIS: Ich dachte, eine andere Leinwand für die Anzeige der HUD und der btn_INV und andere solche Schaltflächen erstellen, aber das löst nicht wirklich das Problem so gut wie umgehen.

Antwort

1

Ich denke, es gibt zwei Möglichkeiten, dieses Problem zu lösen:

  • Die erste wird Ihr Spiel-Bildschirm in zwei Teile zu teilen: eine für die Tasten, die andere für die Fliesen, so können Sie testen whilecatching ein MouseEvent, wenn der Klick auf den Kacheln positioniert ist oder nicht Beachten Sie, dass die Kacheln auf einer neuen Schaltfläche platziert werden können. Diese Lösung ist einfach zu implementieren, Sie können jedoch keine Schaltflächen auf dem Bereich Ihrer Kachel platzieren.

  • Die zweite wird sein, einen "ButtonManager" zu erstellen. Ihre Spielklasse wird diesen Button-Manager haben, der nach einem Mausereignis lauscht und dann zu den Buttons sendet. Tasten werden nicht direkt auf dieses Ereignis hören. Sie sagen eins nach dem anderen, wenn der Klick auf ihren Grenzen ist und wenn kein Knopf gefeuert wurde, bedeutet dies, dass der Klick auf den Kacheln erfolgte. Diese Methode ist ein wenig schwieriger zu implementieren, aber ermöglicht es Ihnen, eine Priorität zwischen den Schaltflächen zu erstellen, so dass Schaltflächen die Grenzen überschreiten können!

Ich hoffe, es hat dir geholfen!

1

Im Allgemeinen werden Konzepte wie Schaltflächenlistener in Anwendungen verwendet, nicht in Spielen.

Spiele funktionieren im Allgemeinen mit einer Spielschleife, die eine Update- und eine Draw-Phase hat. Während der Update-Phase wird die Benutzereingabe abgefangen (gut ... mehr überprüft als abgefangen, aber ich komme gleich darauf) und interpretiert.

Sie hätten also nur einen großen Maus-Listener, der dann prüfen würde, welche Taste gedrückt werden könnte oder ob sich die Maus innerhalb des Spielbereichs befindet, damit der Charakter den Bewegungsbefehl erhalten sollte.

Natürlich sollte der Maus-Listener nicht direkt etwas tun, da er an den Swing-Thread gebunden ist. Es sollte nur einige Variablen ändern (wie: "linke Maustaste ist gedrückt, Position ist x, y"), die dann in deiner Update-Phase überprüft wird, die dann tatsächlich damit arbeiten wird. Auf diese Weise sind Sie nicht länger auf den Swing-Thread angewiesen (was ohnehin nicht ideal für Spiele ist) und - fast genauso wichtig - Ihre Spiellogik ist vollständig vorhersehbar.

Sie müssen jedoch sicherstellen, dass Ihre Zeichenmethode regelmäßig aufgerufen wird und tatsächlich malt, was und wann Sie malen möchten. Das ist aber auch die Frage, deshalb werde ich nicht näher darauf eingehen.

Denken Sie daran, dass auch Swing nicht irgendein magisches Konstrukt ist. Wenn Sie mit der Maus klicken, durchläuft swing auch alle Elemente, die Sie haben, und prüft, ob einer von ihnen ein Ereignis erhalten soll. Wenn du deine eigenen Buttons schreibst, musst du das auch tun.

Geben Sie jeder Taste ihren eigenen Maus Listener wird nur verwirrend, je größer Ihr Spiel bekommt. Es wird auch Ihre Spielschleife zerstören, da Mausereignisse jederzeit ausgelöst und gefangen werden können, unabhängig davon, was Sie gerade tun. Wenn Ihre Spiellogik in einem separaten Thread von Swing passiert (was es sollte), ist es sogar möglich, dass Sie ein Ereignis erhalten, während Sie etwas verarbeiten. Das wird dein Ergebnis vermasseln.

Dies kann auch zu ein paar sehr seltsame einmalige Fehler führen, die Sie nicht verstehen und nicht reproduzieren können. Also sei sehr vorsichtig damit.

+0

Vielen Dank für Ihre Zeit und Beratung.Ich werde mein a = Programm entsprechend ändern, wie das, was Sie erwähnt haben, gültig ist. Eine Sache würde ich sagen, dass die 'btn_INV' nur den' GameState' ändert, also ist es egal wenn ich diese Methode während des Ausführens irgendeines anderen Updates auf dem Hauptspielthread anrufe. Jedoch, was Sie über den 'MouseListener'-Faden sagten, gerade einige" ActionCommands "zu schaffen, ist ein guter Punkt und ich werde versuchen, es zu implementieren. Danke. –