0

I'm creating a program for a school project. It is a fan based program of Pokémon and I am having a little trouble understanding how to change images based on the key strokes.

Here is the code so far for the Character Class

import java.awt.Image;
import java.awt.event.KeyEvent;
import java.io.*;                        //the File class
import java.util.*;                     //the Scanner class
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import java.awt.image.*;


import javax.swing.ImageIcon;

public class MainCharacter {

  private Image up, up1, up2, down, down1, down2, left, left1, left2, right, right1, right2;


  private void loadImages() {


     up = new ImageIcon("Up.png").getImage();
     up1 = new ImageIcon("Up1.png").getImage();
     up2 = new ImageIcon("Up2.png").getImage();
     down = new ImageIcon("Down.png").getImage();
     down1 = new ImageIcon("Down1.png").getImage();
     down2= new ImageIcon("Down2.png").getImage();
     left = new ImageIcon("Left.png").getImage();
     left1 = new ImageIcon("Left1.png").getImage();
     left2 = new ImageIcon("Left2.png").getImage();
     right = new ImageIcon("Right.png").getImage();
     right1 = new ImageIcon("Right1.png").getImage();
     right2 = new ImageIcon("Right2.png").getImage();

  }










/*public Image getImage() 
{
     //dont know what to return here
}
*/


  public void keyPressed(KeyEvent e) {

     int key = e.getKeyCode();

     if (key == KeyEvent.VK_LEFT) {

     //dont know what to do

     }

     if (key == KeyEvent.VK_RIGHT) {
        //dont know what to do

     }


     if (key == KeyEvent.VK_UP) {

        //dont know what to do
     }

     if (key == KeyEvent.VK_DOWN) {
        //dont know what to do
     }
  }

  public void keyReleased(KeyEvent e) {
     int key = e.getKeyCode();

     if (key == KeyEvent.VK_LEFT) {
        //dont know what to do
     }

     if (key == KeyEvent.VK_RIGHT) {
        //dont know what to do
     }

     if (key == KeyEvent.VK_UP) {
        //dont know what to do
     }

     if (key == KeyEvent.VK_DOWN) {
        //dont know what to do
     }
  }
}

Here is the class where it moves along with the map:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JPanel;
import javax.swing.Timer;


public class MapMovement extends JPanel implements ActionListener {

private Timer timer;
private Map map;
 private MainCharacter mainCharacter;

public MapMovement() {

    addKeyListener(new TAdapter());
    setFocusable(true);
    setBackground(Color.BLACK);
    setDoubleBuffered(true);

    map = new Map();

    timer = new Timer(5, this);
    timer.start();
}


public void paint(Graphics g) {
    super.paint(g);

    Graphics2D g2d = (Graphics2D)g;
    g2d.drawImage(map.getImage(), map.getX(), map.getY(), 1000, 1500, this);

    Toolkit.getDefaultToolkit().sync();
    g.dispose();
}


public void actionPerformed(ActionEvent e) {


      map.move();
    repaint();  
}


private class TAdapter extends KeyAdapter {

    public void keyReleased(KeyEvent e) {
        map.keyReleased(e);
            //mainCharacter.keyReleased(e);
    }

    public void keyPressed(KeyEvent e) {
        map.keyPressed(e);
            //mainCharacter.keyPressed(e);

    }
}

}

UPDATE:

here is the Map class

import java.awt.Image;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;

public class Map {

private String map = "Map-1stCity.png";

private int dx;
private int dy;
private int x;
private int y;
private Image image;

public Map() {
    ImageIcon ii = new ImageIcon(this.getClass().getResource(map));
    image = ii.getImage();
    x = -100;
    y = -100;
}


public void move() {
    x += dx;
    y += dy;
}

public int getX() {
    return x;
}

public int getY() {
    return y;
}

public Image getImage() {
    return image;
}

public void keyPressed(KeyEvent e) {

    int key = e.getKeyCode();

    if (key == KeyEvent.VK_LEFT) {
        dx = 1;
    }

    if (key == KeyEvent.VK_RIGHT) {
        dx = -1;
    }

    if (key == KeyEvent.VK_UP) {
        dy = 1;
    }

    if (key == KeyEvent.VK_DOWN) {
        dy = -1;
    }
}

public void keyReleased(KeyEvent e) {
    int key = e.getKeyCode();

    if (key == KeyEvent.VK_LEFT) {
        dx = 0;
    }

    if (key == KeyEvent.VK_RIGHT) {
        dx = 0;
    }

    if (key == KeyEvent.VK_UP) {
        dy = 0;
    }

    if (key == KeyEvent.VK_DOWN) {
        dy = 0;
    }
}
}

Thank you for all your help in advance!

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Where else have you looked? – Lee Fogg May 25 '14 at 23:04
  • I have searched the internet and stackoverflow alot but I still dont understand how I can achieve it. I am not sure if I am supposed to use a Thread. If so, I do not know how to use it in this case. – Shiraz Chokshi May 25 '14 at 23:06
  • There are many ways to do this. You could have your `getImage()` method require a parameter (maybe a `String`, `int` or `enum`), then you return an image depending on what they pass through. You could also throw the images into an array, create a tracking int (field var like `int i = 0;`), and each time `getImage()` is called, you could increase the tracker int, then return the image in the array slot `images[i]`. When a key is pressed, call the `getImage()` method and `repaint()` – Vince May 25 '14 at 23:09
  • What part are you having trouble with? Also, don't call `g.dispose();` on a `Graphics` context you did not create, this will create endless problems – MadProgrammer May 26 '14 at 00:27
  • @VinceEmigh I understand the array stuff. However, could you explain how to make the `getImage()` method require a parameter and pass the array in it. – Shiraz Chokshi May 26 '14 at 00:31
  • @MadProgrammer I have having trouble with the actionlistener and the `getImage()` method part in the `MainCharacter` class – Shiraz Chokshi May 26 '14 at 00:33
  • Its hard to follow along with your design. You don't even show where you initialize `MainCharacter mainCharacter`. The only thing i can think of is to add a `updateMap(Image image)` method in your MapMovement class, then pass your MapMovement to MaimCharacter so you can call `mapMovement.updateMap(getImage("up"));` or something similar (and repaint after). You wanna update the map from the MainCharacter class, yet the MainCharacter class has no access to Map, so theres no way to change it. – Vince May 26 '14 at 00:54
  • @VinceEmigh I am creating the Main Character class just for the movements. I would then call that in the MapMovement class along with the Map class. I can also post the Map class if you want. My main question is how would you create the actionlistener for the change in pictures to create the illusion that the character is moving in the MainCharacter class. This has stumped me alot. I know how to call it in the MapMovement class but I do not understand how for the MainCharacter. – Shiraz Chokshi May 26 '14 at 00:59
  • @VinceEmigh I have added the map class so you can see it – Shiraz Chokshi May 26 '14 at 01:03
  • I'm much more interested in seeing where you do `new MainCharacter()`. When you create your character, pass an instance of your MapMovement to where you are handling the events (so you can change the map variable that is being painted). If you initialize mainCharacter inside of the MapMovment class, pass an instance like this: `new MainCharacter(this);` (or you can just use a method). That way your map character can change the image inside of your map (the image thats being painted). let me know if you need am example – Vince May 26 '14 at 01:11
  • @VinceEmigh I understand a little bit. However, it would be great if you could give me an example just so I can have a visual verification. Thanks! – Shiraz Chokshi May 26 '14 at 01:23

1 Answers1

2

Overview

Basically, you're going to need to know the current key state and the current frame (based on the fact that you have two images per position).

With this information, you can then determine which character image you should be displaying.

Example

This example is very simple and it demonstrates the key bindings API over using KeyListener as it resolves issues dealing with focus. It also demonstrates how easy it is to modify the current state through a single base Action

The example's MainCharacter has a single method (of interest) which is used to determine which image should be displayed based on the current KeyState (an enum in this example) and the current frame.

The getCharacter method switches the image every 10 frames in order to provide the animation between the current position...

Walk

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Character {

    public static void main(String[] args) {
        new Character();
    }

    public Character() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new GamePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GamePane extends JPanel {

        private MainCharacter mc;
        private KeyState keyState;
        private int frame;

        public GamePane() {

            mc = new MainCharacter();

            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    frame++;
                    if (frame > 100) {
                        frame = 0;
                    }
                    repaint();
                }
            });
            timer.start();


            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "Up");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "Down");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "Left");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "Right");

            ActionMap am = getActionMap();
            am.put("Up", new KeyStateAction(KeyState.UP));
            am.put("Down", new KeyStateAction(KeyState.DOWN));
            am.put("Left", new KeyStateAction(KeyState.LEFT));
            am.put("Right", new KeyStateAction(KeyState.RIGHT));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            Image img = mc.getCharacter(keyState, frame);
            int x = (getWidth() - img.getWidth(this)) / 2;
            int y = (getHeight() - img.getHeight(this)) / 2;
            g2d.drawImage(img, x, y, this);
            g2d.dispose();
        }

        public class KeyStateAction extends AbstractAction {

            private KeyState state;

            public KeyStateAction(KeyState state) {
                this.state = state;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                keyState = state;
            }

        }

    }

    public enum KeyState {

        UP, DOWN, LEFT, RIGHT;

    }

    public class MainCharacter {

        private Map<KeyState, List<Image>> mapImage;

        public MainCharacter() {
            mapImage = new HashMap<>(25);
            try {
                mapImage.put(KeyState.UP, loadImages("Up"));
                mapImage.put(KeyState.DOWN, loadImages("Down"));
                mapImage.put(KeyState.LEFT, loadImages("Left"));
                mapImage.put(KeyState.RIGHT, loadImages("Right"));
            } catch (IOException exp) {
                exp.printStackTrace();
            }
        }

        protected List<Image> loadImages(String name) throws IOException {

            List<Image> images = new ArrayList<>(25);
            images.add(ImageIO.read(new File(name + "1.png")));
            images.add(ImageIO.read(new File(name + "2.png")));

            return images;

        }

        public Image getCharacter(KeyState keyState, int frame) {

            Image img = null;
            List<Image> images = mapImage.get(keyState);
            if (images == null) {
                images = mapImage.get(KeyState.DOWN);
            }
            int index = (frame / 10) % 2 == 0 ? 0 : 1;
            img = images.get(index);

            return img;

        }

    }

}

As I said in my comments, don't call dispose on a Graphics context you did not create as this can affect not only what you are painting but what might be painted after you.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366