超级玛丽键盘监听改进
在上一篇文章中的项目存在一个细节问题:
- 长按→,然后按下↑后松开,此时→没有松开但是→检测不到pressed了。
- 键盘监听事件是单线程的,无法识别两个按键同时按下的监听事件。
- 当第一次按下→的时候,pressed方法里→被检测到,再按下↑的时候,pressed方法里↑被检测到,↑松开后虽然→没有松开,但是pressed方法只能检测到↑或其他新按下的键,没有松开的→不能被检测到。
- 当→松开时released可以检测到。
为了解决这个问题,可以利用还可以检测到的released方法。这里经宇哥提点后准备将移动的方法进行修改:
- 向左、向右等方法里面只需要改变对应的x方向的速度
- 在线程里面不断根据x方向的速度移动物体(move方法)
- 针对上面的问题,当→按下的时候x方向的速度为一个正整数dx,当↑按下的时候调用jump方法,将进行y方向的向上和向下的运动(在这个过程中x方向的运动在线程中完成,不会受到影响),当↑松开,角色落地后x方向的速度没有改变,会继续运动,当→松开后在released方法里面设置x方向的速度为0,角色停止移动
相应的改动:
/*
* 在线程里面移动角色
* */
public void run(){
while(true){
posx += dx;
if(posx<0)
posx=0;
if(posx>screenWidth-50)
posx = screenWidth-50;
switchN--;
if(switchN==0){
index = (index+1)%4;
switchN = 5;
}
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/*
* 水平左行走
* */
public void moveL( ){
if(overHead==0&&posx>0){
images = StaticValue.marioImgsLeft;
dx = -step;
isMoving = 2;
}
}
/*
* 水平右行走
* */
public void moveR( ){
if(overHead==0&&posx<screenWidth-50){
images = StaticValue.marioImgsRight;
dx = step;
isMoving = 1;
}
}
/*
* 跳跃
* */
public void jump(){
if(overHead==0&&alowJump){//是否在移动过程中跳跃,是否是再次按下jump键
alowJump = false;
if (isMoving == 1 ) {overHead = 1;}
else if(isMoving == 2 ){overHead = 2;}
//向上
for(int i=0 ;i<jumpN;i++){
try {
posy -=2*step;
dy = -2*step;
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//向下
for(int i=0 ;i<jumpN;i++){
try {
posy +=2*step;
dy = 2*step;
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
dy = 0;
overHead = 0;
}
}
(这里对跳跃还做了一个小小的处理,使得一次↑对应一个Jump动作,也就是用一个状态变量记录是否上一次的↑已经release)
处理后的全部代码:
package Mario_7_12;
import java.awt.image.BufferedImage;
import java.util.List;
/**
* FileName: Mario.java
* 游戏角色马里奥
*
* @author XuLiushen
* @Date 2020.7.19
* @version 1.00
*/
public class Mario implements Runnable {
private int screenWidth,screenHeight;//窗口的宽度和高度
public volatile int posx,posy,dx,dy;//马里奥的位置,x,y方向的速度
public boolean alowJump;
public int step,switchN;//速度
public int jumpN;//跳跃切换次数
public int index;//第index张图片
public List<BufferedImage> images;//马里奥动画图片的集合
public int isMoving;//是否在移动0没有移动,1向右,2向左
public int overHead;//是否在空中
/*
* 在线程里面移动角色
* */
public void run(){
while(true){
posx += dx;
if(posx<0)
posx=0;
if(posx>screenWidth-50)
posx = screenWidth-50;
switchN--;
if(switchN==0){
index = (index+1)%4;
switchN = 5;
}
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/*
* 初始化马里奥
* */
public void init(int screenWidth,int screenHeight){
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
images = StaticValue.marioImgsRight;
step = 5;
jumpN = 20;
isMoving = 0;
overHead = 0;
posx = 0;
posy = 400;
index = 0;
switchN = 5;
dx = dy = 0;
alowJump = true;
Thread tr = new Thread(this);
tr.start();
}
/*
* 水平左行走
* */
public void moveL( ){
if(overHead==0&&posx>0){
images = StaticValue.marioImgsLeft;
dx = -step;
isMoving = 2;
}
}
/*
* 水平右行走
* */
public void moveR( ){
if(overHead==0&&posx<screenWidth-50){
images = StaticValue.marioImgsRight;
dx = step;
isMoving = 1;
}
}
/*
* 跳跃
* */
public void jump(){
if(overHead==0&&alowJump){//是否在移动过程中跳跃,是否是再次按下jump键
alowJump = false;
if (isMoving == 1 ) {overHead = 1;}
else if(isMoving == 2 ){overHead = 2;}
//向上
for(int i=0 ;i<jumpN;i++){
try {
posy -=2*step;
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//向下
for(int i=0 ;i<jumpN;i++){
try {
posy +=2*step;
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
overHead = 0;
}
}
/*
* 得到图片
* */
public BufferedImage getImage(){
if(overHead !=0){
if(overHead == 1)
return StaticValue.marioImgsJump.get(0);
else if(overHead == 2)
return StaticValue.marioImgsJump.get(1);
}
if(isMoving == 0){
return images.get(0);
}
return images.get(index);
}
}
package Mario_7_12;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
/**
* FileName: MFrame.java
* 界面类
*
* @author XuLiushen
* @Date 2020.7.17
* @version 1.00
*/
public class MFrame extends JFrame implements Runnable,MouseListener,KeyListener{
private static final long serialVersionUID = 1L;
private Thread marioThread ;//线程
private Graphics g;
// 创建双缓冲画布
private BufferedImage buffimg;
// 取到缓冲画笔 画笔画的内容就先画在这张图片上buffimg
private Graphics buffg;
private Mario mario;
public static void main(String[]args){
MFrame mf = new MFrame();
mf.startGame();//开始游戏
}
public MFrame(){
this.setTitle("My super Mario");//标题
this.setSize(900,600);//窗口大小
this.setLocationRelativeTo(null);//窗口位置:正中间
this.setResizable(false);//窗口大小不可变
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击右上角X后结束程序
this.setVisible(true);//窗体可见
StaticValue.init();//初始化常量类
mario = new Mario();
mario.init(this.getWidth(), this.getHeight());//初始化马里奥类
g = this.getGraphics();//得到画布
buffimg = new BufferedImage(getWidth(), getHeight(),BufferedImage.TYPE_INT_ARGB);//缓冲图片
buffg = buffimg.getGraphics();//缓冲图片的画笔
marioThread = new Thread(this);
}
/*
* 重写paint方法
* */
public void paint(){
super.repaint();
g.drawImage(buffimg,0,0, null);
}
/*
* 在线程里面绘制图片
* */
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Image bgimg = new ImageIcon(StaticValue.bgImg1).getImage();//背景图片
buffg.drawImage(bgimg, 0,0, null);
buffg.drawImage(mario.getImage(), mario.posx, mario.posy, 50, 100, null);
g.drawImage(buffimg, 0, 0, null);
}
}
public void startGame(){
//显示游戏进入界面背景图片
Image bgimg = new ImageIcon(StaticValue.startImg).getImage();
buffg.fillRect(0, 0, this.getWidth(), this.getHeight());
buffg.drawImage(bgimg,0,0,null,null);
g.drawImage(buffimg, 0, 0, null, null);
//游戏开始按钮
this.setLayout(new BorderLayout(500,500));
JButton start_btn = new JButton();
start_btn.setText("START");
start_btn.setPreferredSize(new Dimension(100,50));
start_btn.setBackground(new Color(241,77,13));
start_btn.setBorderPainted(true);
start_btn.addMouseListener(this);//添加按钮监听器
this.addKeyListener(this);//添加键盘监听器
this.getContentPane().add("South",start_btn);
this.setVisible(true);
}
public void inGame(){
this.getContentPane().removeAll();//清除原来的组件
marioThread.start();
}
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
inGame();//按钮按下进入游戏界面
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
/*
* 按钮按下控制马里奥的移动
* */
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
int code = e.getKeyCode();
System.out.println("pressed"+code);
switch(code){
case 37://向左
mario.moveL();break;
case 38://跳跃
mario.jump();break;
case 39: //向右
mario.moveR();break;
// case 32://攻击
}
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
int code = e.getKeyCode();
if(code == 37||code == 39){
mario.dx = 0;
mario.isMoving = 0;//放开鼠标停了下来
mario.index = 0;
}
else if(code == 38){
mario.alowJump = true;
}
}
}
package Mario_7_12;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
/**
* FileName: StaticValue.java
* 游戏素材类
*
* @author XuLiushen
* @Date 2020.7.18
* @version 1.00
*/
public class StaticValue {
public static String imgPath = System.getProperty("user.dir")+"/image/";
public static BufferedImage startImg = null;//开始图片
public static BufferedImage endImg = null;//结束图片
public static BufferedImage bgImg1 = null;//背景图片1
public static BufferedImage bgImg2 = null;//背景图片2
public static List<BufferedImage> marioImgsRight = new ArrayList<BufferedImage> ();//马里奥向右
public static List<BufferedImage> marioImgsLeft = new ArrayList<BufferedImage> ();//马里奥向左
public static List<BufferedImage> marioImgsJump = new ArrayList<BufferedImage> ();//马里奥跳跃
public static BufferedImage marioImgsDead = null;//马里奥死亡
public static List<BufferedImage> flowerImgs = new ArrayList<BufferedImage> ();//食人花
public static List<BufferedImage> mushroomImgs = new ArrayList<BufferedImage> ();//蘑菇怪
public static List<BufferedImage> turtleImgs = new ArrayList<BufferedImage> ();//乌龟怪
public static List<BufferedImage> constructionsImgs = new ArrayList<BufferedImage> ();//障碍物
public static void init(){
try {
startImg = ImageIO.read(new File(imgPath+"bg/start.PNG"));
endImg = ImageIO.read(new File(imgPath+"bg/background.PNG"));
bgImg1 = ImageIO.read(new File(imgPath+"bg/firststage.gif"));
bgImg2 = ImageIO.read(new File(imgPath+"bg/firststageend.gif"));
for(int i=1;i<=4;i++){
BufferedImage img = ImageIO.read(new File(imgPath+"mario/"+i+".PNG"));
marioImgsRight.add(img);
}
for(int i=6;i<=9;i++){
BufferedImage img = ImageIO.read(new File(imgPath+"mario/"+i+".PNG"));
marioImgsLeft.add(img);
}
marioImgsJump.add(ImageIO.read(new File(imgPath+"mario/5.PNG")));
marioImgsJump.add(ImageIO.read(new File(imgPath+"mario/10.PNG")));
marioImgsDead = ImageIO.read(new File(imgPath+"mario/11.PNG"));
for(int i=0;i<4;i++){
BufferedImage img = ImageIO.read(new File(imgPath+"mushroom/en1_"+i+".PNG"));
mushroomImgs.add(img);
}
for(int i=1;i<=2;i++){
BufferedImage img = ImageIO.read(new File(imgPath+"flower/flower"+i+".gif"));
turtleImgs.add(img);
}
for(int i=0;i<4;i++){
BufferedImage img = ImageIO.read(new File(imgPath+"turtle/en0_"+i+".PNG"));
flowerImgs.add(img);
}
for(int i=1;i<=12;i++){
BufferedImage img = ImageIO.read(new File(imgPath+"construction/ob"+i+".PNG"));
constructionsImgs.add(img);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}