I am kinda new to Java and LibGdx but for my school project I need to Animate a Player-Sprite.
For only one Animation per Player everything went fine with the Methode I made for this but now I also need a Walking, Idling and Fighting Animation. I managed to get the Animations to switch but the Problem is now, that only one Frame of each Animation gets Displayed. After some research I know that I need to stop updating my Textures every frame but I cant get to know how to do so.
So far Ive tried a simple timer before updating but this just looks weird.
Here is my Code:
public Player(){
img3 = new Texture("platform_metroidvania asset pack v1.01/herochar sprites(new)/herochar_idle_anim_strip_4.png");
img = new Texture("platform_metroidvania asset pack v1.01/herochar sprites(new)/herochar_idle_anim_strip_4.png");
img1 = new Texture("platform_metroidvania asset pack v1.01/herochar sprites(new)/herochar_run_anim_strip_6.png");
img2 = new Texture("platform_metroidvania asset pack v1.01/herochar sprites(new)/herochar_sword_attack_anim_strip_4.png");
animation();
changeCharacter();
}
public static void animation(){
if(Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
img3 = img2;
amount = 3;
tileWidth = 32;
}else if(Gdx.input.isKeyPressed(Input.Keys.D) || Gdx.input.isKeyPressed(Input.Keys.A)){
img3 = img1;
tileWidth = 16;
amount = 3;
}else{
img3 = img;
amount = 3;
tileWidth = 16;
}
}
public static void changeCharacter(){
batch = new SpriteBatch();
regions = TextureRegion.split(img3, tileWidth, 16);
sprite = new Sprite(regions[0][0]);
sprite.setPosition(x,y);
Timer.schedule(new Timer.Task() {
#Override
public void run() {
frame++;
if (frame > amount) {
frame = 0;
}
sprite.setRegion(regions[0][frame]);
}
}, 0, 1 / 12f);
}
Both Methodes "animation" and "changeCharacter" are called in render().
I know there are simpler solutions for this in LibGdx but the rest of my code is based on this and I basically would have to rewrite the entire Project...
If you need any other classes just ask. I'm probably asking a very easy thing but I dont know what to do with this.
You need to rename first.
img3 looks like it serves the purpose of being the pointer to the currentTextureSet. so when you initialise in Player() you might want to rename img3 to currentTextureSet and then, instead of the duplicate load of herochar_idle_anim_strip_4.png, set
currentTextureSet = img;
after
(you may also want to rename img, img1, img2 to idleTextureSet, runTextureSet and swordAttackTextureSet).
That aside,
definitely get rid of Timer.schedule which you are calling from render will run in a background thread you don't want that.
Looking at the asset pack here https://o-lobster.itch.io/platformmetroidvania-pixel-art-asset-pack?download if this is the same one, the images should be selected left to right i.e. x should be incremented not y. However, it looks like you increment y...
sprite.setRegion(regions[0][frame]); and the incorrect array access is swallowed because in a background timer task and the only textureRegion you can access is [0][0] i.e. you just see a single frame.
So dump this
Timer.schedule(new Timer.Task() {
#Override
public void run() {
frame++;
if (frame > amount) {
frame = 0;
}
sprite.setRegion(regions[0][frame]);
}
}, 0, 1 / 12f);
and replace with
frame++;
if (frame > amount) {
frame = 0;
}
sprite.setRegion(regions[frame][0]);
*EDIT
So this means that your frame updates with every render, which is too fast. So the -easy fragile way- (not the best which would be deltaTime please read this excellent article https://gafferongames.com/post/fix_your_timestep/ ). The easy way, not the best in the case there is no time left, would be to declare
long renderCallCount =0;
put the above where frame is declared, then in your render method
renderCallCount++; //One more render call
if (renderCallCount % 60 == 0) { //Should I update frame?
frame++;
if (frame > amount) {
frame = 0;
}
sprite.setRegion(regions[0][frame]);
}
So whenever renderCallCount is divisible by 60 using the mod operator https://www.edureka.co/blog/mod-method-in-java/ , which would be once a second if your refresh rate is 60hz, then your frame is incremented. To switch frames twice a second change 60 to 30.
This assumes your monitor is ONLY going to run at 60hz. I think your application probably depends on the timing on the refresh rate for position updates so you may want to update that in the future based on some thinking from the article. On a monitor with a faster frame rate everything will speed up (consistently).
I just put some stuff in the right order and it now works! I dont really get why the number after the mod operator needs to be so small but otherwise it just doesnt look right. Also I know that you shouldnt create the Spritebatch in render() but when I do it in the constructor the Program just crashesbecause of the missing Spritebatch??? But thanks for the help :) Here is the working code for anyone whos interested in this:
public static void animation(){
if(Gdx.input.isButtonPressed(Input.Buttons.LEFT) && InfoGame.cl.isGrounded() && current > Enemy.lastDmg + Enemy.dmgCool){
currentTextureSet = attackTexture;
amount = 3;
tileWidth = 32;
}else if(Gdx.input.isKeyPressed(Input.Keys.D) || Gdx.input.isKeyPressed(Input.Keys.A)){
currentTextureSet = runTexture;
tileWidth = 16;
amount = 3;
}else{
currentTextureSet = idleTexture;
amount = 3;
tileWidth = 16;
}
}
public static void changeCharacter(){
batch = new SpriteBatch();
regions = TextureRegion.split(currentTextureSet, tileWidth, 16);
sprite = new Sprite(regions[0][0]);
sprite.setPosition(x,y);
sprite.setOrigin(8,8);
if (renderCallCount % 6 == 0) {
frame++;
if (frame > amount) {
frame = 0;
}
}
sprite.setRegion(regions[0][frame]);
}
Related
I'm attempting to slow down a sprite animation that I found from this website here: https://processing.org/examples/animatedsprite.html
Here is the class for the animation:
class Animation {
PImage[] images;
int imageCount;
int frame;
Animation(String imagePrefix, int count) {
imageCount = count;
images = new PImage[imageCount];
for (int i = 0; i < imageCount; i++) {
// Use nf() to number format 'i' into four digits
String filename = "images/" + imagePrefix + nf(i, 2) + ".png";
images[i] = loadImage(filename);
}
}
void display(float xpos, float ypos) {
frame = (frame+1) % imageCount;
image(images[frame], xpos, ypos);
}
int getWidth() {
return images[0].width;
}
}
From the website showing the animation, it says:
It would be easy to write a program to display animated GIFs, but would not allow as much control over the display sequence and rate of display.
So far, the animation plays at the current frame rate (around 60 FPS), but what if I want to play it at 5 FPS? Obviously, I can't just slow down the entire framerate, because I want just the animation to slow down, not my entire project/canvas.
I've tried slowing the animation down by doing something like this:
if (frameCount % 10 == 1) {
//Show image
}
But the above approach flashes the image, only showing it every ten frames. How would I successfully slow down the rate of display of the animation, so that the frames don't appear so quickly?
Here's what the animation looks like right now:
And here's what I want it to look like:
Thanks for any help.
You're sooo close ! :)
You've got the solution pretty much using frameCount, just need to separate the frame update from the drawing. You want to update based on the frameCount (every once in a while), but display continuously:
void display(float xpos, float ypos) {
if(frameCount % 12 == 0){
frame = (frame+1) % imageCount;
}
image(images[frame], xpos, ypos);
}
(feel to ajust 12 (60/5) to what suits your project best)
Shameless plug, you can also use the image-sequence-player library I wrote which allows you to call setDelay(1000/5) to have the image sequence play at 5fps.
I'm working on a game in java, based on the Atari game adventure. I got the basic KeyListener part working fine, but then I added another if statement, using another class, to test if if the player was going to hit a wall, and stopping movement if that was the case. The method I used also used if statements, and when I ran the code, it had MAJOR lag. I tried a while loop first, but that made it lag even worse. Anyway to make this not lag so much? It doesn't seem that complex a program to run, and I still have to add yet another if statement to make be able to move into another room, so I have to do something to massively cut down on the lag.
Here is the class:
class Player extends JPanel implements KeyListener{
private char c = 'e';
int x = 400;
int y = 400;
int mapX = 0;
int mapY = 0;
public Player() {
this.setPreferredSize(new Dimension(800, 500));
addKeyListener(this);
}
public void addNotify() {
super.addNotify();
requestFocus();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Environment Layout = new Environment();
Layout.drawRoom(mapX,mapY,g);
g.fillRect(x , y , 20, 20);
}
public void keyPressed(KeyEvent e) { }
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) {
c = e.getKeyChar();
repaint();
Environment Layout = new Environment();
if(Layout.isWall(x,y,c)){}
else{
if (c == 'a'){
x = x - 3;
}
else if (c == 'w'){
y = y - 3;
}
else if (c == 's'){
y = y + 3;
}
else if (c == 'd'){
x = x + 3;
}
}
}
public static void main(String[] s) throws IOException{
JFrame f = new JFrame();
f.getContentPane().add(new Player());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
The draw room method I used in this was just to put the background of the room into place.
Here is the isWall method from the Environment class:
public boolean isWall(int moveX, int moveY, char let){
BufferedImage room = null;
try {
room = ImageIO.read(new File(xNum + "," + yNum + ".png"));
}
catch (IOException e) {
}
int[][] walls = convertImage(room);
boolean blocked = false;
if(let == 'w') {
if(walls[moveY-8][moveX] == -3584){blocked = true;}
}
else if(let == 's') {
if(walls[moveY+20][moveX] == -3584){blocked = true;}
}
else if(let == 'a') {
if(walls[moveY][moveX-5] == -3584){blocked = true;}
}
else if(let == 'd') {
if(walls[moveY][moveX+20] == -3584){blocked = true;}
}
return blocked;
}
the convertImage method just converts the image of the room into an int array, for the value of the colors. -3584 is the color of the walls. It's possible this is what's lagging it, but this seemed like the best way for each room to have the walls done automatically.
I also tried a timer, but either I did that wrong, or it just didn't help.
I can give more of my code if that's needed, but help with this would be much appreciated. I'm relatively new to this kind of stuff, so it's likely I'm missing something big. Thanks.
The lag here is almost certainly not from the if statements. Those are really fast. I think the bigger issue is in isWall. Notice that any time you want to check for whether a wall is present, you
Open a file,
read the file contents,
convert the file contents from an image to a grid of pixels, and
read exactly one pixel.
Reading files from disk is extremely slow compared to looking at values in memory. For example, a regular magnetic hard drive works at around 7200 RPM, so the seek time is measured in milliseconds. On the other hand, your processor can do about a billion operations per second, so other operations take nanoseconds. That means that a disk read is roughly a million times slower than other operations, which is almost certainly where you're getting the lag from!
To fix this, consider rewriting your isWall code so that you only read the file and do the conversion once and, having done that, then just look up the part of the image you need. This converts doing tons of (glacially slow) file reads to one single (slow but inevitable) file read followed by tons of fast memory reads.
You appear to be moving your walls further than you are moving your player.
Is it possible that your player object is getting stuck in a wall there by producing "blocked = true" continuously?
Your character gets +- 3 in every direction, however your walls seem inconsistent and range from 8 up to 20 down to 5 left to 20 right.
This is an extension to #templatetypedef's answer.
Instead of loading the image files upon calling the isWall method, you might want to consider caching all of the walls on game start.
So I am thinking;
have a HashMap data structure keyed by <String, Integer>. Where String is your coordinates. E.g. coordinate string = "100,238"
parse all the .png image files in the directories and store the coordinates as key and the value can just be any dummy value like 1 or 2.
Then when isWall() is invoked. Given the X and Y coordinate, build the coordinate string as mentioned in point 1 and check if the key exists. If it does then we know it is a piece of wall else not.
This should drastically reduce the I/O disk contention.
In future, if you would like to extend the solution to incorporate APIs like isTreasureChest() or isMonster(). It can be extended by building a immutable class call "Room" or "Tile" to represent the object. Then modify the HashMap to take in <String, Room>.
For a university course I'm making a game with a friend. The general idea is that we have some platforms moving from right to left and each time one goes offscreen it is generated at a random x and y position on the right (within some limits). There will be a little sprite that jumps from platform to platform.
We have reached a problem we're not sure how to solve. We have all the right code and everything but the platforms just won't move. They should move to the left at a constant speed of -4 pixels per frame (rectVelocity).
We cannot get them to move, though; they are static on the screen at the position each one is initially called in at.
This is the code as condensed as I can make it:
Platforms [] mainPlats;
void setup() {
size(750, 400);
mainPlats = new Platforms[3];
}
void draw() {
level();
}
void level() {
//This is the code for the first platform
mainPlats[0] = new Platforms(200, 200, 100, 15); //These values need to be set inside the class so that
//they aren't constantly overwriting the movement variables in the class
mainPlats[0].displayPlat();
mainPlats[0].platTransition();
//This is the code for the second platform
mainPlats[1] = new Platforms(420, 300, 100, 15);
mainPlats[1].displayPlat();
mainPlats[1].platTransition();
//This is the code for the third platform
mainPlats[2] = new Platforms(570, 350, 100, 15);
mainPlats[2].displayPlat();
mainPlats[2].platTransition();
}
class Platforms {
PImage platform;
int rectX, rectY, rectWidth, rectHeight;
int rectVelocity = 4;
Platforms(int x, int y, int w, int h) {
rectX = x;
rectY = y;
// rectX = (int(random(600, 800))); //Tried to randomise start position, failed hilariously
//rectY = (int(random(150, 350)));
rectWidth = w;
rectHeight = h;
}
void displayPlat() {
platform = loadImage ("images/tiles.png");
//imageMode(CENTER);
image(platform, rectX, rectY, 100, 15); //rectangle platforms replaced with images
}
void platMove() {
rectX -= rectVelocity;
}
void platTransition() {
if (rectX < -200) {
rectX = (int(random(700, 1000)));
rectY = (int(random(150, 350)));
}
}
}
From the draw() function, you call your level() function, which initializes your Platform array every single frame.
This means that you create new Platforms at their starting positions every frame. You never see the platforms move, because as soon as you do move them, you replace them with new platforms at the starting positions again.
So step one is to move their initialization out of the level() function and only call them once, at the beginning of your sketch- the setup() function would be one place you could put them.
Your other problem is that you never actually call the platMove() function. So step two is to make sure you call that function.
A solution might look something like this:
Platforms [] mainPlats;
void setup() {
size(750, 400);
mainPlats = new Platforms[3];
mainPlats[0] = new Platforms(200, 200, 100, 15);
mainPlats[1] = new Platforms(420, 300, 100, 15);
mainPlats[2] = new Platforms(570, 350, 100, 15);
}
void draw() {
level();
}
void level() {
mainPlats[0].displayPlat();
mainPlats[0].platMove();
mainPlats[0].platTransition();
mainPlats[1].displayPlat();
mainPlats[1].platMove();
mainPlats[1].platTransition();
mainPlats[2].displayPlat();
mainPlats[2].platMove();
mainPlats[2].platTransition();
}
Also note that you shouldn't load the image every single frame, either. You should only load it once, at startup. You also might want to use a for loop to iterate over your Platforms instead of referring to every single index. But these don't really affect your problem.
You've got rectX as positive values (>0) when you construct the platforms, but you are checking for rectX < -200 when you call platTransition, which is why it never does anything.
This is a continuation from my last post Java: Animated Sprites on GridLayout. Thanks to a reply, it gave me an idea in where I just had to insert a loop in the trigger condition and call pi[i].repaint() in it. So far it works. Though I tried to integrate it to my game which composed of multiple sprites, it had no improvement in it. Without the animation, the sprites show on the grid with no problems. I inserted the animation loop in the GridFile class and it didn't show. I also tried to insert the animation loop in the MainFile, it showed irregular animations, kinda like a glitch. Can someone tell me where did I went wrong? Ideas are welcome.
MainFile class
public class MainFile {
JFrame mainWindow = new JFrame();
public JPanel gridPanel;
public MainFile() {
gridPanel= new GridFile();
mainWindow.add(gridPanel,BorderLayout.CENTER);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setSize(700,700);
mainWindow.setResizable(false);
mainWindow.setVisible(true);
}
public static void main(String[]args){
new MainFile();
}
}
GridFile class
public class GridFile extends JPanel{
ImageIcon gameBackground = new ImageIcon(getClass().getResource("Assets\\GridBackground.png"));
Image gameImage;
int[] pkmArray = new int[12];
int random = 0;
Pokemon[] pkm = new Pokemon[36];
JPanel[] pokeball = new JPanel[36];
int j = 0;
public GridFile(){
setLayout(new GridLayout(6,6,6,6));
setBorder(BorderFactory.createEmptyBorder(12,12,12,12));
gameImage = gameBackground.getImage();
for(int i = 0;i < 36;i++){
do{
random = (int)(Math.random() * 12 + 0);
if(pkmArray[random] <= 3){
pokeball[i] = new Pokemon(random);
pokeball[i].setOpaque(false);
pokeball[i].setLayout(new BorderLayout());
pkmArray[random]++;
}
}while(pkmArray[random] >= 4);
add(pokeball[i],BorderLayout.CENTER);
}
while(true){
for(int i = 0; i < 36; i++){
pokeball[i].repaint();
}
}
}
public void paintComponent(Graphics g){
if(gameImage != null){
g.drawImage(gameImage,0,0,getWidth(),getHeight(),this);
}
}
}
Use a swing Timer for the repainting, and give a bit time between the frames for swing to do the painting work. There's no point trying to draw faster than what could be displayed anyway. If you have the animation loop in main(), the repaint manager will try to drop some of the repaint requests that appear close to each other, which can be the cause of the irregular animation you see.
You should create and access swing components only in the event dispatch thread. You current approach is breaking the threading rules.
Addition: When the animation loop is where you have it now, the GridFile constructor never returns, which explains that you'll see nothing because the code never gets far enough to show the window.
The code is for making a text color change evenly every 200 ms. Why is the first version change uneven/flickering, and the second one evenly changes?
//first version
long lt = System.currentTimeMillis();
TextView tv = ...
for (int i = 1; i < 120; i++) {
final int cl = i % 2 == 0 ? 0xFFFF0000 : 0x00000000;
Message w = Message.obtain(handler, new Runnable() {
public void run() {
tv.setTextColor(cl);
tv.requestLayout();
}
});
handler.sendMessageDelayed(w,i * 200L - (System.currentTimeMillis()-lt));
}
//second version
Animation anim = new AlphaAnimation(0.0f, 1.0f);
anim.setDuration(200); //You can manage the time of the blink with this parameter
anim.setStartOffset(20);
anim.setRepeatMode(Animation.RESTART);
anim.setRepeatCount(Animation.INFINITE);
tv.startAnimation(anim);
I have had this problem before when creating/using animations. After you are done with your animation, simply call clearAnimation().
This will insure that it has fully stopped and should be nice and smooth, giving the user the experience you desire.
How to stop an animation (cancel() does not work)
Read more:
http://developer.android.com/reference/android/view/View.html
Regards,