慧-子-果 2020-03-20 21:52 采纳率: 0%
浏览 328

Java小白求救啊!!!

package com.shuai.plane;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.lang.ref.PhantomReference;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;

//JAVA中游戏面板:JPanel
/*

  • 自定义游戏面板
  • 1.写一个类继承JPanel
  • 2.创建构造方法,初始化游戏面板的属性
  • 画图的步骤
  • 1.在类中定义图片,并取名
  • 2.在构造方法中调用工具类(Photo)初始化图片
  • 3.在画图方法paint中,画图片(paint是专门的画图方法)
  • */

public class GamePanel extends JPanel{

//创建一个集合,用来存放敌机(敌机的大本营)
List<EnemyPlane> arrays = new ArrayList<>();
//创建一个集合,用来放英雄机的弹药
List<HeroFire> HFire = new ArrayList<>();
//创建一个集合,用来放敌机的弹药
List<EnemyFire> EnFire = new ArrayList<>();

//创建英雄机的对象
HeroPlane heroPlane = new HeroPlane();

//创建敌机的对象
EnemyPlane enemyPlane = new EnemyPlane();

//写一个线程,让程序跑起来
//写一个方法,启动线程
public void action() {
    new Thread() {
        public void run() {
            //定义一个变量控制敌机出现的数量
            int a = 0;
            int b = 20;
            //写一个死循环,让程序一直运行
            while(true) {
                //让敌机出现在面板上
                if(a == b) {
                    MakeEP();
                    a = 0;
                }
                //让敌机移动
                epMove();
                //让英雄机子弹出现在面板上
                if(a == 0) {
                    MakeHeroFire();
                }
                //让英雄机的子弹动起来
                HeroFireMove();
                //让敌机的子弹出现在面板上
                //MakeEnemyFire(); 
                //让敌机的子弹动起来 
                //EnemyFireMove();
                //重绘布局(刷新页面)
                repaint();
                try {
                    this.sleep(30);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                a++;
            }   
        }
    }.start();
}

//写一个方法,让敌机移动
public void epMove() {
    for(int i = 0; i < arrays.size(); i++) {
        EnemyPlane enemyPlane = arrays.get(i);
        enemyPlane.y += 10;
        if(enemyPlane.y > 800) {
            arrays.remove(enemyPlane);
        }
        //EnemyFire enemyFire = EnFire.get(i);
        //enemyFire.y += 15;
    }
}

//写一个方法,用来生产敌机,并把它放进集合中
void MakeEP() {
    //创建敌机
    EnemyPlane enemyPlane = new EnemyPlane();
    //创建敌机的子弹
    //EnemyFire enemyFire = new EnemyFire(enemyPlane.x, enemyPlane.y);
    //把敌机加到大本营中(集合)
    arrays.add(enemyPlane);
    //EnFire.add(enemyFire);
}

//写一个方法,生产英雄机的弹药,并把它放进英雄机的弹药库
void MakeHeroFire() {
    for(int i = 0; i < arrays.size(); i++) {
    HeroFire heroFire = new HeroFire(heroPlane.x + 15, heroPlane.y, 0);
    HFire.add(heroFire);
    HeroFire heroFire2 = new HeroFire(heroPlane.x + 78, heroPlane.y, 2);
    HFire.add(heroFire2);
    HeroFire heroFire3 = new HeroFire(heroPlane.x + 48, heroPlane.y - 20, 1);
    HFire.add(heroFire3);
    }
}

//写一个方法让英雄机子弹移动
public void HeroFireMove() {
    for(int i = 0; i < HFire.size(); i++) {
        HeroFire heroFire = HFire.get(i);
        if(heroFire.dir == 0) {
            heroFire.x -= 1;
        }
        if(heroFire.dir == 2) {
            heroFire.x += 1;
        }
        heroFire.y -= 10;

// if(heroFire.y < -heroFire.h) {
// HFire.remove(heroFire);
// }
}
}

//写一个方法生产敌机的弹药,并放进敌机的弹药库
void MakeEnemyFire() {
    EnemyFire enemyFire = new EnemyFire(enemyPlane.x, enemyPlane.y);
    EnFire.add(enemyFire);
}

//让敌机的子弹动起来
void EnemyFireMove() {
    for(int i = 0; i < EnFire.size(); i++) {
        EnFire.get(i).y += 15;
    }
}

//定义背景图
BufferedImage bgp;
//创建构造方法,初始化面板
public GamePanel(GameFrame gameFrame) {
    //设置面板的背景
    //setBackground(Color.cyan);
    //通过工具类(Photo)获取图片
    bgp = Photo.getImage("/Img/bg1.jpg");

    /*
     * 使用鼠标监听器(固定格式)
     */
    //1.创建鼠标适配器
    //这是一个匿名内部类
    MouseAdapter adapter = new MouseAdapter() {
        //2.确定需要的鼠标监听事件
        /*
         * 鼠标监听事件:
         *      1.mouseMoved();监听鼠标移动事件
         *      2.mouseCliked();监听鼠标单击事件(鼠标按下后松开才会被监听,按下不会被监听)
         *      3.mousePressed();监听鼠标按下去的事件(只要鼠标按下就会被监听)
         *      4.mouseEntered();监听鼠标移入游戏界面的事件
         *      5.mouseExited();监听鼠标移出游戏界面的事件
         * 
         *      MouseEvevt:鼠标的事件,记录鼠标做的事情
         * */

        @Override
        //当鼠标在游戏界面移动时,会触发此方法
        public void mouseMoved(MouseEvent e) {
            //让英雄机跟着鼠标移动,就让英雄机的横纵坐标等于鼠标的横纵坐标
            //获取鼠标的坐标(getX();获取鼠标的横坐标, getY();获取鼠标的纵坐标)
            int mx = e.getX();
            int my = e.getY();
            //让英雄机移动到鼠标的位置(英雄机移动到鼠标的位置,是英雄机的行为,可以在英雄机中定义方法)
            //此时英雄机依然不会移动,其实英雄机的坐标已经改变,但是没有把英雄机绘制到新的坐标,需要刷新页面
            //heroPlane.move(mx, my);
            heroPlane.x = mx - heroPlane.w/2;
            heroPlane.y = my - heroPlane.h/2;
            //刷新页面,将英雄机绘制到新的页面
            //重新调用paint方法(re 表示重新调用)
            repaint();
        }

        @Override
        //当鼠标在游戏界面单击时,会触发此方法
        public void mouseClicked(MouseEvent e) {
            System.out.println("~~~~~~~鼠标单击了");
        }

        @Override
        //当鼠标在游戏界面按下时,会触发此方法
        public void mousePressed(MouseEvent e) {
            System.out.println("!!!!!!!!鼠标被按下了");
        }

        @Override
        //当鼠标移入游戏界面时,会触发此方法
        public void mouseEntered(MouseEvent e) {
            System.out.println("**************鼠标被移出");
        }

        @Override
        //当鼠标移出游戏界面时,会触发此方法
        public void mouseExited(MouseEvent e) {
            System.out.println("**************鼠标被移入");
        }
    };

    //3.将适配器加入到监听器中(固定格式)
    addMouseListener(adapter);
    addMouseMotionListener(adapter);


    /*
     * 使用键盘适配器(固定模式)
     */
    // 1.创建键盘适配器
    KeyAdapter key = new KeyAdapter() {
        // 2.确定需要监听的键盘事件
        @Override
        //当键盘按下时,会触发此方法
        public void keyPressed(KeyEvent e) {
            //监听键盘的按键(每一个按键都对应有一个数字)
            //获取按键的数字
            int a = e.getKeyCode();
            //KeyEvent.VK_?;是表示指定的按键对应的数字
            if(a == KeyEvent.VK_A || a == KeyEvent.VK_LEFT) {
                if((heroPlane.x -= 10) < -heroPlane.w/2) {
                    heroPlane.x = 0 - heroPlane.w/2;
                }
            }else if(a == KeyEvent.VK_D || a == KeyEvent.VK_RIGHT) {
                if((heroPlane.x += 10) > 600 - heroPlane.w/2) {
                    heroPlane.x = 600 - heroPlane.w/2;
                }
            }else if(a == KeyEvent.VK_W || a == KeyEvent.VK_UP) {
                if((heroPlane.y -= 10) < 0) {
                    heroPlane.y = 0;
                }
            }else if(a == KeyEvent.VK_S || a == KeyEvent.VK_DOWN) {
                if((heroPlane.y += 10) > 800 - heroPlane.h) {
                    heroPlane.y = 800 - heroPlane.h;
                }
            }
            //重绘,将英雄机画到新的位置上
            repaint();
            /*
            if(e.getKeyChar() == 'a') {
                if((heroPlane.x -= 10) < -heroPlane.w/2) {
                    heroPlane.x = 0 - heroPlane.w/2;
                }
            }
            if(e.getKeyChar() == 'd') {
                if((heroPlane.x += 10) > 600 - heroPlane.w/2) {
                    heroPlane.x = 600 - heroPlane.w/2;
                }
            }
            if(e.getKeyChar() == 'w') {
                if((heroPlane.y -= 10) < 0) {
                    heroPlane.y = 0;
                }
            }
            if(e.getKeyChar() == 's') {
                if((heroPlane.y += 10) > 800 - heroPlane.h) {
                    heroPlane.y = 800 - heroPlane.h;
                }
            }
            //重绘,将英雄机画到新的位置上
            repaint();
            */
        }
    };

    // 3.将适配器加入到窗体的监听器中(加到面板的键盘监听器中是没有用的)
    //可以在创建面板对象时,把窗体对象传过来,然后把键盘适配器添加到窗体的键盘监听器中
    //需要面板的构造器添加参数,并指定类型
    gameFrame.addKeyListener(key);


}


//专门画图的方法,重写于JComponent类(JPanel的父类)
//画图的方法:(画图时一定要注意图片的尺寸)
//  格式: g.drawImage(图片, 横坐标, 纵坐标, 宽度, 高度,  当转换了更多图片是要通知的对象)
//  画图时如果不设定图片的大小,图片显示就是原图的大小
@Override
public void paint(Graphics g) {
    //g.drawImage(img, x, y, observer);
    //横纵坐标是设置图片左上角的坐标,设置图片的大小
    g.drawImage(bgp, 0, 0, 600, 800, null);

    //在paint中画图是有顺序的,先画的在下面一层,先画的会被后画的覆盖

    //给敌机画图
    //已经把敌机放入集合中了,使用遍历绘制敌机
    for(int i = 0; i < arrays.size(); i++) {
        EnemyPlane enemyPlane = arrays.get(i);
        g.drawImage(enemyPlane.img, enemyPlane.x, enemyPlane.y, enemyPlane.w, enemyPlane.h, null);
    }
    //给英雄机画图
    g.drawImage(heroPlane.img, heroPlane.x, heroPlane.y, heroPlane.w, heroPlane.h, null);

    //把英雄机的弹药画进面板
    for(int i = 0; i < HFire.size(); i++) {
        HeroFire heroFire = HFire.get(i);
        g.drawImage(heroFire.img, heroFire.x, heroFire.y, heroFire.w, heroFire.h, null);
    }

    //把敌机的子弹画进面板中

// for(int i = 0; i < EnFire.size(); i++) {
// EnemyFire enemyFire = EnFire.get(i);
// g.drawImage(enemyFire.img, enemyFire.x, enemyFire.y, enemyFire.w, enemyFire.h, null);

// }

}

}
图片说明图片说明图片说明图片说明
图片说明

图一是运行时操作的画面,如果移动鼠标或者按着键盘不放(应该是飞机移动太快的时候),就会出现下面的画面,会一次出现多个子弹(我就的是鼠标移动时刷新的太快,但是线程里面是限制了时间的,难道是鼠标或者键盘和县线程里面重复刷新了???不会啊,求救啊!!!)。还有,截图代码中,如果把圈出来的判断代码放出来,游戏界面就会出现圈起来的样子(最左边的子弹向上走到一定高度就会变成两个,每次都是在那个高度,并且只有最左边的子弹会变化。)(我就的可能是集合删除后子弹的位置改变了,如果用LinkedList会不会好点?求救!!!)

  • 写回答

1条回答 默认 最新

  • UFO_SERIESOFSOFT 2020-03-21 00:36
    关注

    实现细节我没有深入分析,但你的设计方案有待优化。我开发游戏不会把刷新放到事件里面。刷新要独立处理,事件只改变参数。同时,快速刷新在要有双缓存设计,即更新后再提交到画面,防止“裂屏”等错位问题。
    从你描述现象来看,很大可能是刷新问题。可以将帧率改小,看会不会再现,不会就是刷新。这个问题处理就是我上面说的设计方案

    评论

报告相同问题?

悬赏问题

  • ¥15 oracle集群安装出bug
  • ¥15 关于#python#的问题:自动化测试
  • ¥20 问题请教!vue项目关于Nginx配置nonce安全策略的问题
  • ¥15 教务系统账号被盗号如何追溯设备
  • ¥20 delta降尺度方法,未来数据怎么降尺度
  • ¥15 c# 使用NPOI快速将datatable数据导入excel中指定sheet,要求快速高效
  • ¥15 再不同版本的系统上,TCP传输速度不一致
  • ¥15 高德地图点聚合中Marker的位置无法实时更新
  • ¥15 DIFY API Endpoint 问题。
  • ¥20 sub地址DHCP问题