I'm making a game and although I'm mainly working on the logic first, I have a simple, fast-made draw method:
public class Draw extends JPanel {
private static final long serialVersionUID = 1L;
// BufferedImage heroitext;
Hero hero;
Vector<Enemy> enemies;
Vector<Bullet> ammo;
Vector<Terrain> terrains;
public Draw() {
// (int)try {
// heroitext = ImageIO.read(getClass().getResource("/images/circulo.png"));
// } catch (IOException e) {
// e.printStackTrace();
// }
}
public void render(Level lvl) {
hero = lvl.getHero();
ammo = lvl.getAmmo();
enemies = lvl.getEnemies();
terrains = lvl.getTerrains();
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("drawing");
//draw Hero
g.drawRect((int)hero.getX(), (int)hero.getY(), hero.getWidth(), hero.getHeight());
//draw Enemies
for(int i=0; i<enemies.size(); i++)
{
if(!enemies.get(i).isToKill()) g.drawRect((int)enemies.get(i).getX(), (int)enemies.get(i).getY(), enemies.get(i).getWidth(), enemies.get(i).getHeight());
for(int j=0; j<enemies.get(i).getAmmo().size(); j++)
{
g.drawRect((int) enemies.get(i).getAmmo().get(j).getX(), (int)enemies.get(i).getAmmo().get(j).getY(), enemies.get(i).getAmmo().get(j).getWidth(), enemies.get(i).getAmmo().get(j).getHeight());
}
}
//draw Ammo
for(int i=0; i<ammo.size(); i++)
{
g.drawRect((int)ammo.get(i).getX(), (int)ammo.get(i).getY(), ammo.get(i).getWidth(), ammo.get(i).getHeight());
}
//draw Terrains
for(int i=0; i<terrains.size(); i++)
{
g.drawRect((int)terrains.get(i).getX(), (int)terrains.get(i).getY(), terrains.get(i).getWidth(), terrains.get(i).getHeight());
}
}
}
And it usually works great, but sometimes, even when I don't change code, some Runs give me a white screen, and with a few println's I found out that although the code reaches repaint() the paintComponent method isn't called at all.
As I said, this only happens sometimes and IT SEEMS unpredictable.
Another thing that happens is when I add around 50000 enemies to the enemy vector, the paintComponent is never called, no matter how many runs I try. From what I researched, some people say it's because java won't bother drawing a panel if another panel is already waiting to be drawn (or something like that) but that doesn't make sense on the first case since I do not change any code.
Can someone explain me what is actually happening and how to properly fix it?
Related
So I've been trying to figure out what's wrong with my code, but I've had basically no luck when it comes to finding what's wrong. I've seen plenty of folks who had similar issues but none of the fixes that were suggested helped with my problem.
I've made a video showcasing my problem, but I'll give an explanation here too: whenever I move on the screen, the tiles in my game engine don't sync up with each other in terms of positioning, and it seems to be only on the rendering end. When I do any sort of checks to see if the distance between two tiles changes, I don't get any sort of errors. I get these gaps between the tiles specifically when I'm moving up or left, and they instead merge slightly when I move down or right, as if some of the tiles are updating faster than the others.
I don't want to just start dumping my couple thousand lines of code here since that won't really help anyone, but I'm also not well versed in the game development world, so I'm not entirely sure what pieces of code are super relevant here. If there's anything that y'all would like to see, let me know and I'll happily provide it.
The main method and postInit looks like this:
public static void main (String[] args)
{
EventQueue.invokeLater(() ->{
ex = new ShootyGame();
ex.dispose();
ex.setLayout(null);
ex.setUndecorated(Settings.isFullscreen);
ex.setVisible(true);
ex.setCursor(ex.getToolkit().createCustomCursor(
new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB), new Point(), null)); //hides the system cursor
ex.createBufferStrategy(2);
postInit();
});
}
private static void postInit()
{
//add a window listener to the main JFrame
ex.addWindowListener(new WindowAdapter()
{
/**
* Autosaves when the window is closed, then kills the game.
*/
#Override
public void windowClosing(WindowEvent e)
{
//TODO autosaving
kill();
}
});
render.start(); //these are just the two threads being started
run.start();
}
My two threads, which basically just run the step and render methods in my game class:
private static class Running extends Thread
{
boolean stop = false;
int fps = TickManager.fromFPS(Settings.FPS);
public void run()
{
while (!stop)
{
try
{
Running.sleep(fps);
}
catch(InterruptedException e)
{
System.err.println("B");
}
game.step();
}
}
}
private static class Render extends Thread
{
boolean stop = false;
int fps = TickManager.fromFPS(Settings.FPS);
public void run()
{
while (!stop)
{
try
{
Running.sleep(fps);
}
catch(InterruptedException e)
{
System.err.println("A");
}
game.render();
}
}
}
The run method in my Game class just calls the current character's step method (it'll call more later), which is what's directly used to move the Camera class' x and y position. The character step looks like this:
public void step()
{
this.calculateMove(); //gets info on which keys are being pressed, adds or subtracts an integer from dx and dy (which are also ints) based on which are being pressed
Game.cam.update(-this.dx, -this.dy); //sends the opposite movement to the camera
//change the position of the player character by the appropriate x and y amounts
this.changeX(this.dx);
this.changeY(this.dy);
this.reset(); //just sets dx and dy back to 0 after moving
}
And now to the rendering stuff here's the entire camera class since it's probably the most relevant class:
public class Camera
{
private int x;
private int y;
public Camera(int x, int y)
{
this.x = x;
this.y = y;
}
public void update(int dx, int dy)
{
moveX(dx);
moveY(dy);
}
public void moveX(int dx)
{
this.x += dx;
}
public void moveY(int dy)
{
this.y += dy;
}
/**
* Gets the top left corner of the camera's x position.
* #return The x position of the top left corner of the camera
*/
public int getCamX()
{
return this.x;
}
/**
* Gets the top left corner of the camera's y position.
* #return The y position of the top left corner of the camera
*/
public int getCamY()
{
return this.y;
}
}
And finally, the relevant code for the actual painting of the tiles:
public void render() //this is the part that the render thread calls initially
{
Toolkit.getDefaultToolkit().sync();
repaint();
}
public void paint(Graphics g) //the paint method, mostly handled in doDrawing
{
super.paint(g);
doDrawing(g);
}
private void doDrawing(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
//stuff that happens if the game isn't paused
if(!GameStates.isPaused)
{
currentMap.drawMap(g2d, this);
currentChar.draw(g2d, this);
}
}
public void drawMap(Graphics2D g2d, ImageObserver observer)
{
//for each tile in the map, call its draw method
for(int i = 0; i < this.tileArray.length; i++)
{
for(int j = 0; j < this.tileArray[0].length; j++)
{
this.tileArray[i][j].draw(g2d, observer);
}
}
}
public void draw(Graphics2D g2d, ImageObserver observer, BufferedImage image)
{
g2d.drawImage(image, this.getX() + Game.cam.getCamX(), this.getY() + Game.cam.getCamY() observer);
}
Sorry for the long winded post, I've just tried everything I can think of (and that my Googling abilities can help me think of) and I'm throwing this out as a last-ditch effort before I just move to an actual game engine. I wanted to make my own engine from scratch for the sake of getting more experience in Java, but if I can't figure this out I'd rather just move to a proper engine and actually make a game. Any help is massively appreciated :)
EDIT: capitalization
I have a problem with a Java 2d game I'm developing.
Some of the game screen elements are drawn in a Jpanel overriding the paint() method and some other elements are Jlabels added to that panel.
The problem is that the elements drawn in the paint() method are always up to date (I recall a Jpanel.repaint() every 30ms from a thread) but the Jlabels often are not shown or are blinking in a random way.
I've searched a lot but couldn't find a way to update all the elements at the same time.
This is the core structure of the code:
Main Class
final class ATC_sim {
// ...
public CustomLabel[] label;
// CustomLabel is just a class in which are organized differents JLabels in
// a certain relative position.
// ...
private ATC_sim(){
initGui();
initGame();
}
private void initGui(){
frame = new JFrame();
pGame = new GamePanel(this);
for(int i=0; i< label.length; i++){
pGame.add(label[i]);
}
frame.add(pGame);
// ...
}
private void initGame(){
loop = new Loop(this, 30);
thread = new Thread(loop);
thread.start();
pGame.repaint();
}
// method recalled every 30ms from thread(loop)
public void updateGame(){
// do the math of the game (in which I also move the position of labels)
// ...
for(int i=0; i< label.length; i++){
label[i].update(); // this method update text in Jlabels and position of label.
}
pGame.repaint();
}
}
GamePanel class
public class GamePanel extends JPanel {
// ...
public GamePanel(ATC_sim a) {
this.atc = a;
// ...
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
// do some drawings according to some variable calculated in updateGame()
}
}
At first, if you want better performance and a lower chance of lagging/stuttering, use paintImmediately() instead of repaint().
It could be, that this solves your problem already.
The second part is: Do you use double buffering? If you use this, this problem will definitely disappear (in my oppinion).
I'm working on a vertical scrolling game, and I'm using a thread to generate new enemies every 2 seconds. Each enemy is an image in a JPanel. For some reason, The generated enemies are not showing up in the JFrame, but they are present. When the player collides with one of the enemies, all the enemies show up.
Here's the code:
private void checkCollision() {
for(AlienShip as : enemies) {
if(player.getBounds().intersects(as.getBounds()))
player.setVisible(false);
}
}
private void setAlien() {
alien = new AlienShip();
add(alien);
enemies.add(alien);
System.out.println("Enemies: " + enemies.size());
}
public Thread alienGenerator() {
for(int i = 0; i < 3; i++) { // these are being drawn
setAlien();
}
return new Thread(new Runnable() {
#Override
public void run() {
int sleepTime = 2000;
while(true) {
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
System.out.println(e);
}
setAlien(); //these aren't
}
}
});
}
private void gameLoop() {
alienGenerator().start();
mainTimer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
checkCollision();
}
});
mainTimer.start();
}
It always seems that you're Darned If You Do And Darned If You Don't. As far as I'm concerned the code you had placed in your earlier post was adequate, as a matter of fact, it was still lacking (no PlayerShip Class). The code example in this post does even less justice. Never the less......
Before I get started I just want you to know that I personally would have tackled this task somewhat differently and the meager assistance provided here will be solely based on the code you have already provided in this and previous posts.
The reason you are not seeing your Alien Ships displaying onto the Game Board upon creation is because you don't revalidate the board panel. As you currently have your code now this can be done from within the Board.setAlien() method where the Alien Ships are added. Directly under the code lines:
alien = new AlienShip();
add(alien);
enemies.add(alien);
add the code line: revalidate();, so the code would look like this:
alien = new AlienShip();
add(alien);
enemies.add(alien);
revalidate();
Your Alien Ships should now display.
On A Side Note:
What is to happen when any Alien Ship actually makes it to the bottom of the Game Board? As a suggestion, have them re-spawn to the top of the game board (serves ya right fer missin em). This can be done from within the AlienShip.scrollShip() method by checking to see if the Alien Ship has reached the bottom of the board, for example:
public void scrollShip() {
if (getY() + 1 > this.getParent().getHeight()) {
setY(0 - PANEL_HEIGHT);
}
else {
setY(getY() + 1);
}
}
In my opinion, PANEL_HEIGHT is the wrong field name to use. I think it would be more appropriate to use something like ALIEN_SHIP_WIDTH and ALIEN_SHIP_HEIGHT. Same for the variables panelX and panelY, could be alienShipX and alienShipY. Food for thought.
As you can see in the code above the current Game Board height is acquired by polling the Game Board's getHeight() method with: this.getParent().getHeight(). This allows you to change the Game Board size at any time and the Alien Ships will know where that current boundary is when scrolling down. All this then means that the setResizable(false); property setting done in the Main Class for the Game's JFrame window can now be resizable: setResizable(true);.
You will also notice that when the Alien Ship is re-spawned at top of the Game Board it is actually out of site and it flows into view as it moves downward. I think this is a much smoother transition into the gaming area rather than just popping into view. This is accomplished with the setY(0 - PANEL_HEIGHT); code line above. As a matter of fact even when the game initially starts, your Alien Ships should flow into the the gaming area this way and that can be done from within the AlienShip.initAlienShip() method by initializing the panelY variable to panelY = -PANEL_HEIGHT;.
This now takes me to the initialization of the PANEL_WIDTH and PANEL_HEIGHT fields. The values seem enormous (224 and 250 respectively). Of course you may have set to these sizes for collision testing purposes, etc but I think an image size of 64 x 35 would most likely suffice:
This image should be a PNG image with a transparent background which then eliminates the need for the setBackground(Color.BLUE); code line located within the AlienShip.initAlienShip() method.
The AlienShip.getX() and AlienShip.getY() methods should be overridden:
#Override
public int getX() { ... }
#Override
public int getY() { ... }
I think extending the AlienShip Class to JLabel would be better than to JPanel. To JPanel seems like overkill:
public class AlienShip extends JLabel { ... }
Adding a background image to the Game Board can add pizazz to the game. This can be achieved by adding the following code to the Board.paintComponent() method:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon imgIcon = new ImageIcon("images/StarBackground.png");
Image img = imgIcon.getImage();
g.drawImage(img, 0, 0, this.getSize().width, this.getSize().height, this);
}
Images can be acquired here.
This should keep you going for a while. Before to long it'll be Alien mayhem.
I am making a classic space shooter type game for a GUI project, using JPanels and swing etc. I have a multi-layered main menu with a button for play and exit etc, however, the issue I am having is in my DrawPanel class which paints all the graphics for the game. This class has one giant paint component method that activates on a timer every 500 ms, and renders blinking stars on a background, renders the spaceship, and renders falling enemies. The stars are blinking, the enemies are falling but the spaceship is not moving at all and I have to use the "a" and "d' keys. The question: how can I move the ship? Below is an extremely summarized version of that rendering class to keep it simple, the paint component class is full of other things however including generating blinking stars.
public DrawPanel(int x, int y)
{
t = new Timer(500, new ActionListener(){#Override public void actionPerformed (ActionEvent event){repaint();}});
this.addKeyListener(new KeyAdapter()
{
#Override public void keyPressed(KeyEvent e) { ship.keyPressed(e);System.out.println("keypressed");}
#Override public void keyReleased(KeyEvent e) { ship.keyReleased(e);System.out.println("keyreleased");}
});
t.start();
}
public void paintComponent(Graphics Graphic)
{
super.paintComponent(Graphic);
drawShip(Graphic);
drawEnemy(Graphic,10);
}
private void drawShip(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(ship.getImage(), ship.getX(),
ship.getY(), this);
}
This is the spaceship class that is called by the drawship method, and that explains the calls by variable "ship" in the previous code.(Note: there are no compiler errors or runtime errors in anything)
public class Spaceship {
private int x = 780;
private int y = 850;
private Image image;
public Spaceship()
{
ImageIcon ii = new ImageIcon("spaceship.png");
image = ii.getImage();
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key ==KeyEvent.VK_A) {
x += -10;
}
if (key == KeyEvent.VK_D) {
x+= 10;
}
}
public void keyReleased(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_A) {
x += 0;
}
if (key == KeyEvent.VK_D) {
x += 0;
}
}
}
My thought process on this was that the keyboard would be active at all times and anytime it registered a press it would modify an x so that the next time the ship would be drawn it would be in a different position. However it would be bound by the same time that the enemies and background are bound by and wouldn't fluidly move side to side now that I think about it, but I have no clue how to make a separate paint component, timer, and repaint as that's the only way I know to paint. Also
I do realize I haven't done bound checking on this for the ship movement but that shouldn't be an issue right now as it's not even moving a centimeter yet, much less out of bounds, and it is not even printing the debug statements in the listener.
All of my classes are added to panels which have been bound to buttons and tabs in my main menu by adding it all to default constructor of the main method, so this is how the entire game executes:
public static void main(String[] args) throws IOException
{
GalagaRipOff main = new GalagaRipOff();
}
I'm working on the old multi-threaded bouncing balls problem in swing. I've got everything set up so far, but I'd like to add an explosion animation when two balls collide. I've got collision detection and I can display text where the collision took place, but I was wondering on the best modular approach to creating a small animation (for example, pixels exploding 360 degrees around the point, fading out over time)
Class structure:
Ball
public class Ball {
private double x,y,dx,dy;
private static final int XSIZE = 15;
private static final int YSIZE = 15;
public Ball(){
// make x,y,dx,dy random
}
public int getX(){//}
public int getY(){//}
public Point position(){
return new Point(x,y);
}
public void move(Rectangle2D bounds){
//do movement (change x,y,dx,dy)
}
public Ellipse2D getShape(){
return new Ellipse
}
public boolean collide(Ball other){
if (this.position().distance(other.position()) < XSIZE)
return true;
return false;
}
}
BallComponent
public class BallComponent extends JPanel {
public ArrayList<Ball> balls = new ArrayList<Ball>();
private ArrayList<Color> colors = new ArrayList<Color>();
private ArrayList<Point> explosions = new ArrayList<Point>();
Random rnd = new Random();
private boolean exploding = false;
public void add(Ball b) {
balls.add(b);
colors.add(new Color(rnd.nextFloat(),rnd.nextFloat(),rnd.nextFloat()));
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for(int i=0; i<balls.size(); i++){
for(int j=0; j<i; j++){
if (balls.get(i).collide(balls.get(j))){
exploding = true;
explosions.add(balls.get(i).position());
balls.remove(i);
colors.remove(i);
balls.remove(j);
colors.remove(j);
return;
}
}
g2.setColor(colors.get(i));
g2.fill(balls.get(i).getShape());
if (exploding){
for (Point p : explosions){
g2.drawString("boom", p.x, p.y);
}
}
}
}
public void reset(){
balls = new ArrayList<Ball>();
colors = new ArrayList<Color>();
explosions = new ArrayList<Point>();
}
}
Thanks in advance
One thing you must do is get program logic out of the painting method, here the paintComponent method. You have collision detection within this method, your removing logical items from collections from within this method, you're even returning out of the method before it has fully done its primary job -- which is drawing all the components.
Instead I suggest that you re-arrange your program more along M-V-C or Model-View-Controller lines where the state of the balls is held by the model classes, the game loop controlled by the controller class, where collision detection is done in the controller using the model's state, and where the GUI classes, the "view" holds no program logic whatsoever and simply displays the state of the model.