Please wait...

Top Bar -->

Bài 2 - Lập trình game đi cảnh Megaman trên Java

date_range 2017-03-06

Bài tiếp theo chúng ta sẽ học về animation và kỹ thuật đồ họa trong game, cụ thể đó là viết ba lớp cho package effect. Các lớp này dùng nhiều cho đồ họa trong game để tạo các hình ảnh tạo hiệu ứng (animation) chuyển động.

1.Viết lớp FrameImage

Đây là một lớp dùng để lưu dữ liệu của một hình ảnh phục vụ cho quá trình vẽ animation hoặc đơn giản là vẽ lên màn hình một ảnh đơn. Lớp này chứa 2 thuộc tính là tên của frame và một Image tương ứng với tên frame đó. Những frame này sẽ được load lên chương trình từ data mà cũng trong bài này chúng ta sẽ đề cập đến.

public class FrameImage{

    private String name;
    private BufferedImage image;

    public FrameImage(String name, BufferedImage image){
        this.name = name;
        this.image = image;
    }

    public FrameImage(FrameImage frameImage){
        image = new BufferedImage(frameImage.getWidthImage(),
                frameImage.getHeightImage(), frameImage.image.getType());
        Graphics g = image.getGraphics();
        g.drawImage(frameImage.image, 0, 0, null);
        name = frameImage.name;
    }

    public void draw(int x, int y, Graphics2D g2){

        g2.drawImage(image, x - image.getWidth()/2, y - image.getHeight()/2, null);

    }

    public FrameImage(){
        this.name = null;
        image = null;
    }

    public int getWidthImage(){
        return image.getWidth();
    }

    public int getHeightImage(){
        return image.getHeight();
    }

    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }

    public BufferedImage getImage(){
        return image;
    }
    public void setImage(BufferedImage image){
        this.image = image;
    }

}

2. Viết lớp Animation

Lớp Animation là một lớp để lưu dữ liệu về số lượng và loại frame được sắp xếp theo một trật tự nhất định nhằm tạo nên một animation. Trong video mình có nói rõ về kỹ thuật được sử dụng cho animation.

public class Animation {

    private String name;

    private boolean isRepeated;

    private ArrayList<FrameImage> frameImages;
    private int currentFrame;

    private ArrayList<Boolean> ignoreFrames;

    private ArrayList<Double> delayFrames;
    private long beginTime;

    private boolean drawRectFrame;

    public Animation(){
        delayFrames = new ArrayList<Double>();
        beginTime = 0;
        currentFrame = 0;

        ignoreFrames = new ArrayList<Boolean>();

        frameImages = new ArrayList<FrameImage>();

        drawRectFrame = false;

        isRepeated = true;
    }

    public Animation(Animation animation){

        beginTime = animation.beginTime;
        currentFrame = animation.currentFrame;
        drawRectFrame = animation.drawRectFrame;
        isRepeated = animation.isRepeated;

        delayFrames = new ArrayList<Double>();
        for(Double d : animation.delayFrames){
            delayFrames.add(d);
        }

        ignoreFrames = new ArrayList<Boolean>();
        for(boolean b : animation.ignoreFrames){
            ignoreFrames.add(b);
        }

        frameImages = new ArrayList<FrameImage>();
        for(FrameImage f : animation.frameImages){
            frameImages.add(new FrameImage(f));
        }
    }

    public void setIsRepeated(boolean isRepeated){
        this.isRepeated = isRepeated;
    }

    public boolean getIsRepeated(){
        return isRepeated;
    }

    public boolean isIgnoreFrame(int id){
        return ignoreFrames.get(id);
    }

    public void setIgnoreFrame(int id){
        if(id >= 0 && id < ignoreFrames.size()) ignoreFrames.set(id, true); } public void unIgnoreFrame(int id){ if(id >= 0 && id < ignoreFrames.size()) ignoreFrames.set(id, false); } public void setName(String name){ this.name = name; } public String getName(){ return name; } public void setCurrentFrame(int currentFrame){ if(currentFrame >= 0 && currentFrame < frameImages.size()) this.currentFrame = currentFrame; else this.currentFrame = 0; } public int getCurrentFrame(){ return this.currentFrame; } public void reset(){ currentFrame = 0; beginTime = 0; } public void add(FrameImage frameImage, double timeToNextFrame){ ignoreFrames.add(false); frameImages.add(frameImage); delayFrames.add(new Double(timeToNextFrame)); } public void setDrawRectFrame(boolean b){ drawRectFrame = b; } public BufferedImage getCurrentImage(){ return frameImages.get(currentFrame).getImage(); } public void Update(long deltaTime){ if(beginTime == 0) beginTime = deltaTime; else{ if(deltaTime - beginTime > delayFrames.get(currentFrame)){
                nextFrame();
                beginTime = deltaTime;
            }
        }

    }

    public boolean isLastFrame(){
        if(currentFrame == frameImages.size() - 1)
            return true;
        else return false;
    }

    private void nextFrame(){

        if(currentFrame >= frameImages.size() - 1){

            if(isRepeated) currentFrame = 0;
        }
        else currentFrame++;

        if(ignoreFrames.get(currentFrame)) nextFrame();

    }

    public void flipAllImage(){

        for(int i = 0;i < frameImages.size(); i++){

            BufferedImage image = frameImages.get(i).getImage();

            AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
            tx.translate(-image.getWidth(), 0);

            AffineTransformOp op = new AffineTransformOp(tx,
            AffineTransformOp.TYPE_BILINEAR);
            image = op.filter(image, null);

            frameImages.get(i).setImage(image);

        }

    }

    public void draw(int x, int y, Graphics2D g2){

        BufferedImage image = getCurrentImage();

        g2.drawImage(image, x - image.getWidth()/2, y - image.getHeight()/2, null);
        if(drawRectFrame)
            g2.drawRect(x - image.getWidth()/2, x - image.getWidth()/2, image.getWidth(), image.getHeight());

    }

}

3.Viết lớp CacheDataLoader

Lớp này lưu các Data animation, frame, sound (tạm thời chưa implement) cho game. Các data này sẽ được load lên từ các file dữ liệu, ở đây mình sử dụng các file *.txt, mình dùng dạng file này để dễ dàng cho các bạn hiểu về kỹ thuật, còn việc lưu data trong các game thực tế thì sẽ không lưu như vậy nhé.

public class CacheDataLoader {

    private static CacheDataLoader instance = null;

    private String framefile = "data/frame.txt";
    private String animationfile = "data/animation.txt";
    private String physmapfile = "data/phys_map.txt";
    private String backgroundmapfile = "data/background_map.txt";
    private String soundfile = "data/sounds.txt";

    private Hashtable<String, FrameImage> frameImages;
    private Hashtable<String, Animation> animations;
    //private Hashtable<String, AudioClip> sounds;

    private int[][] phys_map;
    private int[][] background_map;

    private CacheDataLoader() {}

    public static CacheDataLoader getInstance(){
        if(instance == null)
            instance  = new CacheDataLoader();
        return instance;
    }

/*
    public AudioClip getSound(String name){
        return instance.sounds.get(name);
    }*/

    public Animation getAnimation(String name){

        Animation animation = new Animation(instance.animations.get(name));
        return animation;

    }

    public FrameImage getFrameImage(String name){

        FrameImage frameImage = new FrameImage(instance.frameImages.get(name));
        return frameImage;

    }

    public int[][] getPhysicalMap(){
        return instance.phys_map;
    }

    public int[][] getBackgroundMap(){
        return instance.background_map;
    }

    public void LoadData()throws IOException{

        LoadFrame();
        LoadAnimation();
        LoadPhysMap();
        LoadBackgroundMap();
        LoadSounds();

    }

/*
    public void LoadSounds() throws IOException{
        sounds = new Hashtable<String, AudioClip>();

        FileReader fr = new FileReader(soundfile);
        BufferedReader br = new BufferedReader(fr);

        String line = null;

        if(br.readLine()==null) { // no line = "" or something like that
            System.out.println("No data");
            throw new IOException();
        }
        else {

            fr = new FileReader(soundfile);
            br = new BufferedReader(fr);

            while((line = br.readLine()).equals(""));

            int n = Integer.parseInt(line);

            for(int i = 0;i < n; i ++){

                AudioClip audioClip = null;
                while((line = br.readLine()).equals(""));

                String[] str = line.split(" ");
                String name = str[0];

                String path = str[1];

                try {
                   audioClip =  Applet.newAudioClip(new URL("file","",str[1]));

                } catch (MalformedURLException ex) {}

                instance.sounds.put(name, audioClip);
            }

        }

        br.close();

    }
*/

    public void LoadBackgroundMap() throws IOException{

        FileReader fr = new FileReader(backgroundmapfile);
        BufferedReader br = new BufferedReader(fr);

        String line = null;

        line = br.readLine();
        int numberOfRows = Integer.parseInt(line);
        line = br.readLine();
        int numberOfColumns = Integer.parseInt(line);

        instance.background_map = new int[numberOfRows][numberOfColumns];

        for(int i = 0;i < numberOfRows;i++){
            line = br.readLine();
            String [] str = line.split(" |  ");
            for(int j = 0;j<numberOfColumns;j++)
                instance.background_map[i][j] = Integer.parseInt(str[j]);
        }

        for(int i = 0;i < numberOfRows;i++){

            for(int j = 0;j<numberOfColumns;j++)
                System.out.print(" "+instance.background_map[i][j]);

            System.out.println();
        }

        br.close();

    }

    public void LoadPhysMap() throws IOException{

        FileReader fr = new FileReader(physmapfile);
        BufferedReader br = new BufferedReader(fr);

        String line = null;

        line = br.readLine();
        int numberOfRows = Integer.parseInt(line);
        line = br.readLine();
        int numberOfColumns = Integer.parseInt(line);

        instance.phys_map = new int[numberOfRows][numberOfColumns];

        for(int i = 0;i < numberOfRows;i++){
            line = br.readLine();
            String [] str = line.split(" ");
            for(int j = 0;j<numberOfColumns;j++)
                instance.phys_map[i][j] = Integer.parseInt(str[j]);
        }

        for(int i = 0;i < numberOfRows;i++){

            for(int j = 0;j<numberOfColumns;j++)
                System.out.print(" "+instance.phys_map[i][j]);

            System.out.println();
        }

        br.close();

    }
    public void LoadAnimation() throws IOException {

        animations = new Hashtable<String, Animation>();

        FileReader fr = new FileReader(animationfile);
        BufferedReader br = new BufferedReader(fr);

        String line = null;

        if(br.readLine()==null) {
            System.out.println("No data");
            throw new IOException();
        }
        else {

            fr = new FileReader(animationfile);
            br = new BufferedReader(fr);

            while((line = br.readLine()).equals(""));
            int n = Integer.parseInt(line);

            for(int i = 0;i < n; i ++){

                Animation animation = new Animation();

                while((line = br.readLine()).equals(""));
                animation.setName(line);

                while((line = br.readLine()).equals(""));
                String[] str = line.split(" ");

                for(int j = 0;j<str.length;j+=2)
                    animation.add(getFrameImage(str[j]), Double.parseDouble(str[j+1]));

                instance.animations.put(animation.getName(), animation);

            }

        }

        br.close();
    }

    public void LoadFrame() throws IOException{

        frameImages = new Hashtable<String, FrameImage>();

        FileReader fr = new FileReader(framefile);
        BufferedReader br = new BufferedReader(fr);

        String line = null;

        if(br.readLine()==null) {
            System.out.println("No data");
            throw new IOException();
        }
        else {

            fr = new FileReader(framefile);
            br = new BufferedReader(fr);

            while((line = br.readLine()).equals(""));

            int n = Integer.parseInt(line);

            for(int i = 0;i < n; i ++){

                FrameImage frame = new FrameImage();
                while((line = br.readLine()).equals(""));
                frame.setName(line);

                while((line = br.readLine()).equals(""));
                String[] str = line.split(" ");
                String path = str[1];

                while((line = br.readLine()).equals(""));
                str = line.split(" ");
                int x = Integer.parseInt(str[1]);

                while((line = br.readLine()).equals(""));
                str = line.split(" ");
                int y = Integer.parseInt(str[1]);

                while((line = br.readLine()).equals(""));
                str = line.split(" ");
                int w = Integer.parseInt(str[1]);

                while((line = br.readLine()).equals(""));
                str = line.split(" ");
                int h = Integer.parseInt(str[1]);

                BufferedImage imageData = ImageIO.read(new File(path));
                BufferedImage image = imageData.getSubimage(x, y, w, h);
                frame.setImage(image);

                instance.frameImages.put(frame.getName(), frame);
            }

        }

        br.close();

    }

}

Các bạn xem thêm video để dễ hiểu hơn nhé. Chú ý là bài này sẽ xem các video 2.1 -> 2.6