I'm in the process of creating my own two-dimensional game. Every object in the game world has a Sprite object, and when the screen is drawn, the object's sprite is drawn in the object's location.
The sprite class can be either a single image or a series of images used to make an animates sprite.
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
public class Sprite implements Runnable{
private int currentImage;
private BufferedImage[] sprites;
private int delayMS;
private Thread animation;
public Sprite(BufferedImage sprite){
sprites = new BufferedImage[1];
sprites[0] = sprite;
}
public Sprite(BufferedImage[] spriteAnimation,int delay){
this.sprites = spriteAnimation;
currentImage = 0;
delayMS = delay;
//start a thread to time animation
animation = new Thread(this);
animation.start();
}
private void next(){
if(currentImage < sprites.length - 1)
currentImage++;
else
currentImage = 0;
}
public void run() {
while (Thread.currentThread() == animation) {
//delay the animation for delayMS
try {
Thread.sleep(delayMS);
} catch (InterruptedException e) {
break;
}
//next image
next();
}
}
public void draw(Graphics2D g,int x,int y){
g.drawImage(sprites[currentImage],null,x,y);
}
}
I couldn't find any reliable information on the subject of using threads to run many animations, and I was wondering whether this was the best approach.
Everything works great until I throw 200ish identical objects into the world. FPS begins to lower and some animated sprites begin to change frames at different times. This would make sense since the threads would start to delay when instantiated.
My questions is whether there is a more efficient way to handle this, allowing me to use more objects without significant FPS loss, and synchronize the thread/threads so that the animations switch frames together.
The usual way of doing this is to have a game loop that knows all the sprites and calls a draw method on them (or preferably only those that needs be redrawn) for each frame. This way, whenever a sprite decides it wants to animate, it will not do so until the next frame. Just do the animating the the draw method, e.i. the next() part.
And of course, in the game loop you put your delay, which will determine the fps.
To get frame rate independent animation you do the same but add an argument to the draw method which says how much time has passed since the last draw, then calculate the amount of animation based on this.
Instead of running every sprite on its own thread, you could put the sprites on a container, and on a rendering thread iterate through all visible sprites and draw them (you could even implement sprite priorities, render background layers before sprites, ...).
if you use more threads for sprites (i.e, one thread for one sprite) then it will eat your cpu and it will hang the best way to implement animation in games is for every run loop of thread do change the animation sequences ( like next image or previous upon key Presses) and paint the sprites at the end.
Here if it is more sprites and paint will take more time then FPS will drop at the time two or three threads max with synchronization
Related
GitHub
I’m making a gear simulator and I’ve set it up where you can place the gears, and I have a plan on how you can update each gears rotation speed. I’m going to make a Initialize (method?) where I make the gear that will spin check every point next to it(it’s beta size hasn’t been implemented yet) and then I will add the original gear to a dontSpin list, then make every gear the first one detected spin everything around it, except for gears in dontSpin.
My issue I’m having is how do I rotate the gear Image a bit every tick? I’ve done a bunch of research and tried implementing some things I found but they all rotate it once, and I can’t find a way/I’m not smart enough to know how to make each gear object in the gearList rotate at the rotateSpeed every tick.
If you need more information please message me as I’ve been working on this for like a week and this has been a roadblock for at least 3 days making me lose motivation.
I tried researching multiple different sites and different methods of rotating images and it seemed none were what I needed, they seemed to all be a single rotation. I tried just staring at my code for 15 minutes waiting for it to just pop in my head to no avail. I tried asking on a discord help server, where I was told “just make a method to rotate it, then use it” and I’m not even kidding lmao. I even tried asking a fellow java coder about it, but they had no idea.
Help me stack overflow, you’re my only hope.
Edit: taken down for focusing on 2 problems , so I’ll elaborate on the one problem.
Gear is a class with a Point(x,y), I have a Board class with the bulk of my code, where I have a 10 by 10 ish size grid of squares, the gear and player automatically moves around on these squares.
You can hit a button ‘E’ to add a gear, and ‘Q’ to remove a gear. Every time you add a gear, a new gear is added to the gearList ArrayList. My issue is how to update the gear Images every single tick in the board class.
Here is where the gears are drawn
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// when calling g.drawImage() we can use "this" for the ImageObserver
// because Component implements the ImageObserver interface, and JPanel
// extends from Component. So "this" Board instance, as a Component, can
// react to imageUpdate() events triggered by g.drawImage()
// draw our graphics.
drawBackground(g);
drawScore(g);
for (Gear gear : gearList) {
gear.draw(g, this);
}
player.draw(g, this);
// this smooths out animations on some systems
Toolkit.getDefaultToolkit().sync();
}
This is what that calls
public class Gear {
// image that represents the gear's position on the board
private BufferedImage image;
private BufferedImage newSizeImage;
// current position of the gear on the board grid
private Point pos;
private int rot = 45;
private int rotSpeed = 5;
public Gear(Point gpos) {
// load the assets
loadImage();
// initialize the state
pos = gpos;
}
private void loadImage() {
try {
// you can use just the filename if the image file is in your
// project folder, otherwise you need to provide the file path.
image = ImageIO.read(new File("src/images/gear.png"));
finalImage = rotate(image.getScaledInstance(Board.TILE_SIZE, Board.TILE_SIZE, Image.SCALE_DEFAULT));
} catch (IOException exc) {
System.out.println("Error opening image file: " + exc.getMessage());
}
}
public void draw(Graphics g, ImageObserver observer) {
// with the Point class, note that pos.getX() returns a double, but
// pos.x reliably returns an int. https://stackoverflow.com/a/30220114/4655368
// this is also where we translate board grid position into a canvas pixel
// position by multiplying by the tile size.
g.drawImage(
finalImage,
pos.x * Board.TILE_SIZE,
pos.y * Board.TILE_SIZE,
observer);
}
I have a jpanel that I repaint in a step event. It takes a cars position x and draws it. This works with integers but I'd like to use a double. The car objects x and y are needed to be doubles because I want to rotate and accelerate in a direction. Any ideas? Here's my current code:
public class Map extends JPanel implements ActionListener {
private void step() {
for(int i = 0; i <cars.length; i++) {
Car car = cars[i];
car.move();
repaint(car.getX()-1, car.getY()-1, car.getWidth()+2, car.getHeight()+2);
}
}
}
One possibility is for you to use doubles in your program's model, the classes that describe the state of your system, and use ints (by casting or rounding) in the view -- your GUI graphics code -- when displaying the state of the model.
Another is for you to consider using classes that implement the Shape interface, such as Rectangle2D, Ellipse2D, and Path2D as these use double or float specifiers for positioning
Your current code is broken in that the code is called on the Swing event thread, but appears to attempt to animate within a for loop. That's not going to work, and will only show the starting and finishing state of the animation due to it blocking the Swing event thread. Instead use a Swing Timer to drive the animation.
I've searched for similar threads like this but couldn't find any.
I am wondering if there is a more efficient way of rendering a tile, which only uses 1 image and draws the same image in a grid to cover an area.
I'm using the code below:
public void render(Graphics g) {
for(int r = 0; r <= tilePieces.length; r++) {
for(int c = 0; c <= tilePieces[0].length; c++) {
try {
tilePieces[r][c].render(g);
}
catch(Exception e) {
}
}
}
}
When I create a tile, I also create as many tile pieces (squares) it needs and I store them in an array.
Every time the render() method of the object is called, it loops through the array and calls their render() methods, which draw the image.
There is no lag in the game, but I find this way of doing this inefficient, since I have to create a bunch of objects for one tile and call their render methods.
That's why, I tried drawing the same one image in the method above, instead of calling the render() method of each tile piece, and that caused the game to run like 1 frame per 5 seconds. Which is weird, what is the difference between calling a method to draw your image and drawing your image directly in the loop?
I am working on my first side-scroller game and am trying to achieve a neverending background effect and it's nearly working as i would expect but there are little glitches.
I have 2 instances of the background (each fills the screen width), with one placed at the bottom left corner of the screen and one off the screen to the right. I am then moving my camera each frame to the right and when the first background instance is completely offscreen to the left i reset its X to the right of the camera so that it is now (relative to the camera) off the right of the screen. this is working a lot of the time, but every now and again it seems the method to reset its x position is getting called a few frames late and results in a gap in the background.
the update code i am using in the main game is -
private void update(float delta){
//update camera and world
camera.position.set(camera.position.x+scrollSpeed, camera.position.y, 0);
camera.update();
world.step(step, velocityIterations, positionIterations);
if(gameInProgress){
//backgrounds (array holds the 2 instances of the background)
for(int i=0; i< bgFillArray.size; i++){
bgFillArray.get(i).update();
}
}
stage.act(delta);
tweenManager.update(delta);
}
the update method in the BgFill class -
public void update(){
float currentXPos = getX();
float leftBoundary = camera.position.x-1200;
float rightOfScreen = camera.position.x+400;
if(active){
//check to see if the camera has gone past this instance. if so then move to right
if(currentXPos <= leftBoundary){
setX(rightOfScreen);
}
}
}
Firstly, is this the usual way (or even close) to do a continuous scrolling background?
if it is, what am I doing wrong?
I'm writing a bomberman game in Java and I have already wrote the code for the map of the game (which contains tiles), the players (and their movement in the map) and now I am stuck in the code for the bomb explosion.
I have a Map class which contains a 2d array of Tiles, which can contain Players, Blocks and Bombs.
The Player object have a method dropBomb who calls the method receiveBomb from the Map object (every Player has the reference of the Map object) with the position of the bomb and the bomb. When the Map method receiveBomb is called, the map put the bomb in the correct Tile.
My problem is in the explosion of the bomb. Who should care about it? The bomb itself? If it is, should the bomb have the reference for the Tile that contains it? Until now my tile haven't need the Map reference.
One possibility that I thought is to have the Tile reference inside the Bomb object, so, when the bomb explodes (and the bomb knows when it should explode) it calls a method in the tile object for the explosion and the tile calls a method in the map. By the way, I don't know this is a good idea. What should I do?
public class Tile {
private boolean available; //if the tile is not occupied by a indestructible block or bomb
private List<Entity> entities; //you can have more than one player at a tile
public boolean receiveEntity(Entity entity) {
boolean received = false;
if (available) {
this.entities.add(entity);
received = true;
if (entity instanceof Block || entity instanceof Bomb) {
available = false;
}
}
return received;
}
public boolean removePlayer(Player player) {
return entities.remove(player);
}
}
Player class:
public class Player implements Entity {
private Map gameMap;
private int posX;
private int posY;
private int explosionRange; //the explosion range for bombs
public Player(int posX, int posY, Map gameMap) {
this.gameMap = gameMap;
this.posX = posX;
this.posY = posY;
this.explosionRange = 1;
}
public void dropBomb() {
gameMap.receiveBomb(new Bomb(explosionRange), posX, posY);
}
}
Map class:
public class Map {
private Grid<Tile> tileGrid;
private int width;
private int height;
public Map(int width, int height, BuildingStrategy buildingStrategy) {
this.width = width;
this.height = height;
this.tileGrid = new Grid<Tile>(width, height);
buildingStrategy.buildMap(this);
}
public void receiveBomb(Bomb bomb, int posX, int posY) {
tileGrid.get(posX, posY).receiveEntity(bomb);
}
}
I have omitted the movement methods, because the movement is already fine.
I have always learned, and live by the rule "the table paints itself". The painter might choose the color and call the method, the floor might decide how the leaks and splatter is shown, but the table paints itself.
Back to your issue: the bomb explodes itself. This way you can have different effects of different bombs. The bomb has an effect on the tile, and the tile reacts to that.
Example: A bomb has a force and a type of explosion. The bomb, (occupying one and one tile only I think?) will 'give' it's effect to a tile.
Now it's the tile that deals with distributing this force. Lets say you have several kinds of bombs, one power (lets say a number between 1 and 10), and two type (lets say normal, incendiary, freeze).
Now your bomb explodes, and because your avatar is a level 5 fire-mage, your bombs are of power 4 and type incendiary. So you say to your tile: I explode with power 4 and I am setting you on fire!
Now the tile comes in to play. Any tile that gets 'touched' by the force of an explosion needs to call it's "Exploded" function to do stuff. If it is also on fire, there is more to do in the "onFire" function
What tiles are 'exploded' comes from force. Normal tiles with force 4 will give the expotion off to all squares within a range of 4, but if it is a special tile (it knows that from itself), like a mountain tile, it might not be able to advance with that force.
Tile 1 explodes with 4 and gives it to adjacent tiles with force 3. One of those tiles might be a wall, so doens't do anything further. Another is a normal tile, and explodes, and continues giving it forward with force 2, etc. If it is a 'water' tile, the explosion is pushed ofrward, but the fire isn't, etc
so:
bomb explodes itself and gives calls the tiles explosion function
tile is exploded and pushes explosion forward according to tile-type.
subsequent tiles explode because of this.
In the end it might look like most of the work is done by the tiles, and this is probably even the case. but the first steps: the calculation of the force, type, and the first calls are from the bomb. The bomb explodes. And then the explosion has an effect on the tile. The tile handles that, and if needed propagates it.
Your Map should be responsible for the explosion, as it is for every other tile on the map. After all, what is an explosion if not for another tile-type that disappears after a few seconds?
When your game loop calls the update method on the Map object your map should figure out:
What tile is the bomb on
Ask the bomb how far the reach is
Figure out what's in the adjacent tiles that the bomb can reach
Think of your design as a series of events, taken care of one by one in the game loop before eventually being drawn. When your bomb is dropped, it raises an event to the Map in the form of recieveBomb() with the Map being the event controller.
I believe this question fits better in a discussion format and not a Q&A format. It's hard to tell you what is the "correct design" without understanding the overall architecture.
The Map should be the responsible one for handling a bomb explosion.
I would suggest having a queue in the Map, that contains all the bombs present. Also, your bombs should have a timer (i.e., CreationTime) so that as bombs get pushed into the queue, you check each bomb in the queue for how long they have been in there and if applicable "explode" them.
Add a ExplodeBomb function in the Map that checks all 4 directions and handle the tiles accordingly.