This is my first post so if any other information is needed please let me know.
I'm making a game in a Java GUI and just have two hopefully quick questions about which. I can currently paint a map to the screen and pan around the map with the following code:
private class MoveMap implements MouseMotionListener{
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if(e.getX() > swidth-30){
if(xmod+(columns*30) > swidth){
xmod-=30;
repaint();
}
}
else if(e.getX() < 30){
if(xmod < 0){
xmod+=30;
repaint();
}
}
else if(e.getY() > sheight-30){
if(ymod+(rows*30) > sheight){
ymod-=30;
repaint();
}
}
else if(e.getY() < 30){
if(ymod < 0){
ymod+=30;
repaint();
}
}
else{
}
}
}
The only problem is the mouse must continually move at the edge of the screen to continually pan. My question is if there's a way to have the mouse at the edge of the screen and continually pan while updating the graphics? My second question would be if this could be applied to characters moving as well? I thought maybe a thread would be a possible solution but am not familiar with using them. Thank you!
I can think of two ways to achieve this.
You could...
Start a javax.swing.Timer that would update the x/y position as required and repaint the screen when ever the mouse entered within a given distance of the edge of the screen. You would, obviously stop the timer as the mouse moved back out ;)
You could...
Use a background Thread to monitor the mouse position and when it enters the "trigger" zone, would update the x/y values and trigger a repaint, making sure you re-sync the calls to the EDT ;)
Related
I've been working on this really basic Java RPG game, and when I tried using Thread.sleep for my monster to move slower, it seems to affect the player as well, in other words, the movement of the player had slow down when the monster appears, but turns back to normal when I move to another part of the map where the monster is not there
Here's the code for the monster's movement
int wolfRandNum;
if (isKilled() == false){
wolfRandNum = (int) (Math.random()*4);
if (wolfRandNum == 1){ // up
System.out.println("up");
if (playerRow -1 <= 0){
setCurrentEnemySprite(3);
} else {
moveUp();
playerRow--;
}
} else if (wolfRandNum == 2){ // down
System.out.println("down");
if (playerRow +1 >= 8){
setCurrentEnemySprite(0);
} else {
moveDown();
playerRow++;
}
} else if (wolfRandNum == 3){ // left
System.out.println("left");
if (playerColumn -1 <= 0){
setCurrentEnemySprite(1);
} else {
moveLeft();
playerColumn--;
}
} else{ // right
System.out.println("right");
if (playerColumn + 1 >= 9){
setCurrentEnemySprite(2);
} else {
moveRight();
playerColumn++;
}
}
}
The code above is in a method called update, which is from an enemy class.
Here's the where I call the method:
public void paintComponent(Graphics g) {
super.paintComponent(g); //required to ensure the panel is correctly redrawn
map.draw(g);
player.draw(g);
for (Character character : characterList) {
character.draw(g);
}
for (Enemy enemy : enemyList){
enemy.draw(g);
try {
enemy.update();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (Item item : bagList) {
item.draw(g);
}
repaint();
}
Is there anyway to use thread.sleep without affecting the player's movement? If not, what other methods can I use to make the monster move slower but not the player?
I'm not really sure how to fix this problem, any advise would help a lot! Thank you :)
This is a event-driven Swing GUI, and so the answer to the question:
How to use Thread.sleep(...)
is that you don't.
You're coding this as if it were a linear console program, and this design won't work for event-driven non-linear programs. Calling Thread.sleep on the Swing event thread and especially in any painting method is a guarantee to put your entire GUI to sleep so that it becomes totally unresponsive. The proper solution is to create a non-GUI Model class to go with your GUI (your "View"), and to change how the view responds based on the state of the Model, i.e., the state of its key fields.
Also, if your game is being run in "real time", then you will need a game loop to drive your game. This can be done easily via a Swing Timer, although it does not provide absolute precision. If greater precision is required, then use the Swing Timer, but don't assume that each time slice is accurate, and instead measure each delta time and base your physics on the measured slice, not the assumed slice. Other ways to create a game loop include use of a background thread with a while loop and Thread.sleep within this thread.
All you are slowing down is the rendering (drawing) of your monster. As all drawing is done on the UI thread, this will, as you have rightly noticed, have a huge performance impact on your entire application. As a general rule, you should never call a blocking operation such as sleep in the UI thread.
Given you only have two characters, what I would consider trying is creating two threads, one for your character and one for your monster. You will do what you need to do in each of these threads, and then call repaint() when you need to redraw. If you want to slow down the monster, for example, you can call sleep in the monster thread.
I have done a program that numerically solves a set of differential equations which describes how an "arbitrary" illness move in an isolated and constant population, it was a programming assignment from a class I took a while ago. What I've done to extend it is to add some graphical components that can pause, reset and "play" the simulation, as well as some components that allows me to change some constants in the equations.
All this was an exercise in programming as I find it to be fun and exciting and want to become better.
However, at the moment I'm stuck, what I want to do now is to make a very simple form of animation of it. I want to visualize the data I get for the number of infected, susceptibles and resistants in a grid as points. I managed to create the grid and have an idea of how to place the dots.
The problem I have is how to draw the dots as the program is working, I can draw one dot in the grid but only as the grid is created, that's it. I need to be able to create a dot at a specific place in the grid, this goes on until the number of dots reaches a finite number, say 30. At that points I want to have the first dot, the one the left, removed, all the dots shifted to the left and place the new dot at the furthest right of the grid, the whole thing is then repeated.
I think I will be able to figure it out with some help/hints about the paintComponent() method and whether I need to use repaint() method at all, I can't get my head around these for some reason. I've read through my course literature on Java, but despite the extensive sections where he explains most of the different graphical components he does not say that much about those methods, only that you don't call for the paintComponent() method, it is done automatically.
If there is something unclear let me know and I'll try to clarify it.
Thanks in advance.
//
Fox Mulder
I think I will be able to figure it out with some help/hints about the paintComponent() method and whether I need to use repaint() method at all, I can't get my head around these for some reason.
Basically, say you create a custom component by extending JPanel. When you #Override the paintComponent() method, it get implicitly called for you, so you never have to call it. So what ever you paint inside the method, gets drawn on your surface. For example
public class DrawingPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x, y, 10, 10);
}
}
When you call repaint() you are basically causing the paintComponent method to be call implicitly. So to answer your question, Yes you will need to call it if you want to animate, as you will need to update some kind of variable (like the x and y) in the paintComponent() method, to see any change in the drawing.
You can see more at Performing Custom Painting
Not to handle the actual animation, you'll want to use a javax.swing.Timer. You can see more at How to use Swing Timers. Here's the basic construct
Timer ( int delayInMillis, ActionListener listener )
where delayInMillis is the time to delay between ticks(in this case animations) and the ActionListener listens for "ticks". Each tick, the actionPerformed of the ActionListener is called. There, you can put the code to update any variables you use for animation.
So for example you update the x and y, in the actionPerformed, then call repaint()
public class DrawingPanel extends JPanel {
int x = 50;
int y = 50;
public DrawingPanel() {
Timer timer = new Timer(40, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
x += 5;
y += 5;
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x, y, 10, 10);
}
}
Now this was just a simple example. But in your case you want to animate a scatter plot. So what you can do is have a list of Points and in the actionPerformed you can add pull points from that list and push them into another list that is to be drawn. So say you have this
List<Point> originalPoints;
List<Point> pointsToDraw;
...
#Override
protected void paintComponent(Grapchics g) {
super.paintComponent(g);
for (Point point : pointsToDraw) {
g.fillOval(point.x - 5, point.y - 5, 10, 10);
}
}
Basically all the points in pointsToDraw list will be drawn. Initially it will be empty. And in the timer, you can add to the list, until the originalPoints list is exhausted. For example.
List<Point> originalPoints;
List<point> pointsToDraw;
private int currentIndex = 0;
public DrawingPanel(List<Point> originalPoints) {
this.originalPoints = originalPoints;
pointsToDraw = new ArrayList<>();
Timer timer = new Timer(40, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
if (currentIndex == originalPoints.size() - 1) {
((Timer)e.getSource()).stop();
} else {
pointsToDraw.add(originalPoints.get(currentIndex));
currentIndex++;
}
repaint();
}
});
timer.start();
}
So basicall you just keep a current index. When the index reaches the size of the original list, you stop the timer. Otherwise you just pop from the originalPoints and push to the pointsToDraw. For every point you add the pointsToDraw, a repaint() is called, and there will be another point for the paintComponent to draw a circle with.
The END
UDPATE
I just reread your question, and I think I have have misunderstood it. If you want all the points drawn, then basically just have one list. And draw all the points initially. with each tick, just remove the first index, advance all the rest up an index, and add a new one to the end. Though this is the implementation of a LinkedList so you may just want to use that
I have a question regarding the callback speed of the mouseDragged message of the MouseMotionListener in Java Swing. This post is sort of related but it's not entirely the same so I started a question of my own.
I'm making a small in-house application with no eye on commercial distribution that is basically a digitalized TCG (Trading Card Game) emulator. For any of you familiar with MtG (Magic the Gathering), you might've heard from such a similar program. I'm trying to create something that looks sort of like this, but less fancy.
My GUI consists of a JFrame with menu and then some panels containing various buttons and labels, but I'll only go over the relevent parts to explain my problem.
In essence, I'm using a vertical split JSplitPane with a JPanel on the left, with in that a JScrollPane with a JList in it, which represents at any time the cards in your hand that you can play. On the right side of the split, I have a JLayeredPane with a background image in the DEFAULT_LAYER (subclass of JPanel that overrides the draw function to add an image) and, on various layers above the PALETTE_LAYER, I display the cards that are in play (gathered in an ArrayList) by means of custom CardPanels (another subclass of JPanel that illustrates a card). The entire JLayeredPane is thus a representation of the table in front of you with all the cards you've already played.
I first started by adding a MouseListener and a MouseMotionListener to the JLayeredPane to pick up events, allowing me to register a mouse press, check if this was above a card, then use the mouse dragged event to move the card around and finally mouse release to place it back . This all works perfectly fine and if I add logging information I can see the mouseDragged callback function is called often, allowing for a visually fast dragging motion without lag.
Today I decided to add functionality to allow the user to drag a card from his hand to the "table" (instead of double clicking on the card in the JList), so I added the appropriate listeners to the JList along with filling in some functions like MousePressed and MouseReleased. On a mouse press, I check what card from the list was clicked, I lock the list, create a custom CardPanel (but don't add it anywhere yet, I just allocate and initiate it!) and set a flag. In mouse dragged, I check if this flag is set. If it is, I check where the cursor is. If it is anywhere above the JLayeredPane, I add the CardPanel to the DRAG_LAYER and set another flag. If this second flag is set in successive calls to mouse dragged, I don't add the panel again but I just change the location. This functionality is practically the same as the one in my previous mouse dragged callback. On mouse release, I unlock the list and add the CardPanel on the correct layer in the JLayeredPane.
Everything is working as intended so I'm pretty sure my code is okay, but there is just one slight issue:
When dragging a card from the list to the layered pane (instead of from the layered pane to the layered pane), I notice the mouseDragged callback is called at a pretty low frequency by the JList (approx 10 times per second), introducing some visually disturbing lag (compared to approx 30 times per second in the first case of dragging).
I'm going to add some code snippets as to clarify my problem but I'm afraid adding all the code to allow you to run it yourself would be serious overkill.
The main question in this post is: does anybody know why the mouseDragged is called faster by one MouseMotionListener than by another MouseMotionListener? The listener to the JLayeredPane component makes fast successive calls, the listener to the JList calls significantly slower.
Note: I'm developing in Netbeans and I'm using the built-in graphical Swing Interface Builder. I'm using a JFrame form as my main class.
public class MyFrame extends JFrame{
...
protected JLayeredPane layeredPane;
protected JList cardsInHandList;
...
...
protected ArrayList<String> cardsInHand;
...
private void attachListeners(){
layeredPane.addMouseListener(new MouseAdapter(){
public void MousePressed(MouseEvent e){
// set a flag, start a drag
}
public void MouseReleased(MouseEvent e){
// unset a flag, stop a drag
}
});
layeredPane.addMouseMotionListener(new MouseMotionAdapter(){
public void MouseDragged(MouseEvent e){
// drag the card around
// gets called a lot!
// actual code:
if (e.getButton() == MouseEvent.BUTTON1) {
if (!dragging) return; // the flag
int x = e.getX() - 10;
int y = e.getY() - 10;
// snap to grid
x /= GRIDX;
x *= GRIDX;
y /= GRIDY;
y *= GRIDY;
// redraw the card at its new location
draggedCard.setLocation(x, y);
}
}
});
cardsInHandList.addMouseListener(new MouseAdapter(){
public void MousePressed(MouseEvent e){
// set a flag, start a drag
}
public void MouseReleased(MouseEvent e){
// unset a flag, stop a drag
}
});
cardsInHandList.addMouseMotionListener(new MouseMotionAdapter(){
public void MouseDragged(MouseEvent evt){
// check cursor location, drag if within bounds of layeredPane
// gets called a whole lot less!! _Why?_
// actual code:
if (!draggingFromHand) return; // the flag
// check location of cursor with own method (contains() didn't work for me)
if (isCursorAtPointAboveLayeredPane(evt.getLocationOnScreen())) {
// calculate where and snap to grid
int x = (int) (evt.getLocationOnScreen().getX() - layeredPane.getLocationOnScreen().getX())-10;
int y = (int) (evt.getLocationOnScreen().getY() - layeredPane.getLocationOnScreen().getY())-10;
// snap to grid
x /= GRIDX;
x *= GRIDX;
y /= GRIDY;
y *= GRIDY;
if(!draggingFromHandCardPanelAdded){
layeredPane.add(draggingFromHandCardPanel, JLayeredPane.DRAG_LAYER);
draggingFromHandCardPanelAdded = true;
} else {
draggingFromHandCardPanel.setLocation(x,y);
}
}
}
});
}
I'll try to build a short runnable example reproducing the problem and then attach the code somewhere but right now I got to skoot.
Thanks in advance
PS: I am aware that there is another way to drag in Java, involving TransferHandlers and all that but it just seems like too much hassle and it isn't an actual answer to my question of how come the one callback seems to be called more than the other, so please don't tell me to use that instead.
Once you drag outside the list, Java start generating synthetic mouse events for the list, which might be the cause. See the javadoc for JComponent#setAutoscrolls(boolean).
You might get better results using a global event listener, see
http://tips4java.wordpress.com/2009/08/30/global-event-listeners/
I am working on a first person game in Java, and I am trying to get the 3D movement working.
My problem is I would like to capture mouse movement, yet keep the mouse inside the window. After I capture the mouse movement, I figure the best way to keep the mouse in my window is to center the mouse in the window after moving, using Robot.moveMouse(x,y). This works fine, however the movement from the Robot triggers an event in my window which then gets interpreted as a normal event, and thus moves my character in the world.
I've tried various schemes of keeping state and ignoring movements until I am in the center, but they all seem finicky and don't quite detect which events are user vs Robot controlled.
Is there an easy way to detect that a mouse movement came from the Robot?
Is there perhaps a simpler way to solve my problem that I am overlooking?
I solved this by switching to NEWT with JOGL 2.0 RC4. In particular, I use GLWindow and warpPointer instead of an AWT Frame with the Robot.mouseMove. With the switch, I instantly got smooth movements. Some sample code similar to what I'm doing (mileage may vary):
public class MyClass implements MouseListener {
private GLWindow window;
private int centeredX = -1;
private int centeredY = -1;
// ...
public void mouseMoved(MouseEvent e) {
if (centeredX == -1 || centeredY == -1) {
center();
return;
}
int deltaX = e.getX() - centeredX;
int deltaY = e.getY() - centeredY;
// ... Do something with the deltas
centeredX = window.getWidth() / 2;
centeredY = window.getHeight() / 2;
window.warpPointer(centeredX, centeredY);
}
}
Well, I'm not 100% about this, but have you used the getsource() or getComponent() functions on your mouse event? They may return the robot as the source of it. Barring that, I would have a class variable like boolean robotControlling and anytime it takes control of the mouse, set that to true. Then, in you mouseListener, do a if(!robotControlling){...}. Hope this helps.
EDIT: if you have unused mouse buttons in your application (Java has Button 1, Button 2 and Button 3), you could make the robot press that, and in your mouse listener ignore any events with that code pressed. (use evt.getButton() for this) Of course, thats not exactly the cleanest solution :P
I'm writing a simple Game of Life program in Java and am having a bit of trouble getting it to animate. I've got a JComponent class called LifeDraw, which displays a grid of pixels, with the following paint method:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int y = 0; y < lGrid.getHeight(); y++) {
for (int x = 0; x < lGrid.getWidth(); x++) {
if (lGrid.getCell(x,y) == 1) {
g.setColor(Color.red);
g.fillRect(x * lGrid.getSqSize(), y * lGrid.getSqSize(), lGrid.getSqSize(), lGrid.getSqSize());
} else {
g.setColor(Color.white);
g.fillRect(x * lGrid.getSqSize(), y * lGrid.getSqSize(), lGrid.getSqSize(), lGrid.getSqSize());
}
}
}
}
And then another class LifeGrid that has a method run(), which when called will update the grid of pixels for one generation and then call LifeDraw.repaint(). However, if I try to call run() in a loop, the JComponent doesn't repaint until the loop is finished so all that is ever displayed is the first generation and the last one. I figured it was probably just updating too quickly to repaint, so I tried using Thread.sleep() between iterations but still had the same problem. Theoretically (or at least I was hoping it would), it should repaint the component between each iteration and display an animation of the pixels changing.
I'm not that well versed in Java GUI, so any help would be really appreciated. Hopefully I've explained it clearly enough, let me know otherwise!
I don't know if it would solve your problem, but you could try calling run() from a Timer instead of a simple for or while loop.
From the JavaDocs for repaint():
Adds the specified region to the dirty region list if the component is
showing. The component will be repainted after all of the currently
pending events have been dispatched.
All repaint does is signal that the area needs repainting.
Try paintImmediately or update.
Repaint has to be fired in the event loop, not in another thread.
Try replacing your call to repaint() with following code:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
repaint();
}
});