超级马里奥游戏开发一(键盘控制游戏角色)

实现功能:← → ↑ 键控制马里奥的向左向右和跳跃

效果

目录

(1)马里奥类

(2)MFrame类

(3)MFrame的重绘 


(1)马里奥类

要绘制马里奥角色,需要知道角色的位置、此时的图片以及一些有关运动的信息,马里奥角色还需要有向左向右移动和跳跃的方法,因此我们将角色封装成一个类

①首先我们需要角色的位置坐标,为了不让角色跑到窗口外面去还需要知道窗口的大小

②马里奥的不同的动作会对应不同的动作集合(向左、向右、原地跳跃、向左跳跃、向右跳跃、向左停止、向右停止),我们用一个List来存储,用index表示此时应该显示第几张图片

	public static int posx,posy,screenWidth,screenHeight;//马里奥的位置和窗口的宽度和高度
	public static List<BufferedImage> images;//马里奥动画图片的集合
        public static int index;//第index张图片
	

对应的,将StaticValue里面的马里奥角色图片分成不同的动作集合:
 

	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;//马里奥死亡

③在界面上画出马里奥的时候需要获得马里奥此时的图片:
考虑如果此时马里奥在空中,向右跳跃过程中得到向左跳跃过程中得到,其他时刻得到向右移动的动作集合 向左移动的动作集合  里面的index索引处的图片。

	/*
	 * 得到图片
	 * */
	public static BufferedImage getImage(){
		if(overHead !=0){//在空中
			if(overHead == 1)//向右飞行中
				return StaticValue.marioImgsJump.get(0);
			else if(overHead == 2)//向左飞行中
				return StaticValue.marioImgsJump.get(1);
		}
		return images.get(index);//其他动作集合中第index张图片
	}

④接下来实现马里奥的向右向左移动和跳跃的方法:

明确一点,我们使用的是双缓冲画图,也就是在界面类里面每隔一段时间会将当时的图片状态画出来,因此在其他改变图像某部分的方法里面,我们只需要考虑正确改变物体的状态,修改对应的Image

A.向左向右移动:

  • 注意的是当角色处于跳跃中,或者相应方向处于边界,向左向右行走的方法不执行
  • 需要为马里奥确定一个步长step=7
  • 会发现一个问题,就是动作图片切换的太快了,这里我的处理方式是每调用两次切换一次动作图片switchN=2
	/*
	 * 水平左行走
	 * */
	public static  void moveL( ){
		if(overHead==0&&posx>0){
			images = StaticValue.marioImgsLeft;//向左行走的动作集合
			posx -= step;
			switchN--;
			if(switchN == 0){
				index = (index+1)%4;//每调用两次切换对应的动作突破
				switchN = 2;
			}
			isMoving = 2;//向左移动标记
			if(posx<0)//不超过窗口边界
				posx=0;
		}
	}
	
	/*
	 * 水平右行走
	 * */
	public static void moveR( ){
		if(overHead==0&&posx<screenWidth-50){
			images = StaticValue.marioImgsRight;//向右行走的动作集合
			posx += step;
			switchN--;
			if(switchN == 0){
				index = (index+1)%4;//每调用两次切换对应的动作突破
				switchN = 2;
			}
			isMoving = 1;//向右移动标记
			if(posx>screenWidth-50)//不超过窗口边界
				posx = screenWidth-50;
		}
	}

B.跳跃

  • 跳跃分为两个阶段: 上升和下降,包装在两个方法内
  • 当不在空中的时候jump才执行,跳跃分为三种:原地、在向左移动过程中、在向右移动过程中,用一个属性isMoving来标记移动的状态:0静止、1向右移动、2向左移动,overHead来标记跳跃状态:0不处于跳跃中,1向右跳跃中、2向左跳跃中
  • 要处理的是,跳跃时的位置不能切换太快,我的处理是Thread.sleep(20),让线程休眠20ms
	/*
	 * 向上
         * @param int dx 表示水平的步长,负数向左,正数向右
	 * */
	public static void moveU(int dx){
			posx += dx;
			posy -= 2*step;
			if(posx<0) posx=0;
			if(posx>screenWidth-50)
				posx = screenWidth-50;
	}
	
	/*
	 * 向下
         * @param int dx 表示水平的步长,负数向左,正数向右
	 * */
	public static void moveD(int dx){
			posx += dx;
			posy += 2*step;
			if(posx<0) posx=0;
			if(posx>screenWidth-50)
				posx = screenWidth-50;
	}
	
	/*
	 * 跳跃
	 * */
	public static void jump(){
		if(overHead==0){
			//是否在移动过程中跳跃
			int dx = 0;
			if (isMoving == 1 ) {dx = step/2;overHead = 1;}
			else if(isMoving == 2 ){dx = -step/2;overHead = 2;}
			
			for(int i = 0 ; i< jumpN/2 ; i++){//上升
				try {
					Thread.sleep(20);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				moveU(dx);
			}
			
			for(int i = jumpN/2 ; i< jumpN ; i++){//下降
				try {
					Thread.sleep(20);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				moveD(dx);
			}
			overHead = 0;
		}
	}

上面的跳跃方法还会有一些问题,当长按↑键时,松开键后角色会持续跳跃很长一段时间。

我们应该希望监听器检测到↑键按下,如果在时间上距离上一次跳跃的时间间隔小于某个值,则不执行此次跳跃。

这里运用:System.currentTimeMillis();(System.currentTimeMillis()产生一个当前的毫秒,这个毫秒其实就是自1970年1月1日0时起的毫秒数)

	/*
	 * 按钮按下控制马里奥的移动
	 * */
	@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://跳跃
			t2 = System.currentTimeMillis();
			if(t2-t1>500){//当时间间隔小于500ms时不执行
				Mario.jump();
				t1 = t2;
			}
			break;
		case 39: //向右
			Mario.moveR();break;
//		else if(code == 32){//攻击
//			
		}
		
		
	}

C.最后不要忘了初始化方法:

	public static void init(int screenWidth,int screenHeight){
		Mario.screenWidth = screenWidth;
		Mario.screenHeight = screenHeight;
		images = StaticValue.marioImgsRight;
		step = 7;
		jumpN = 20;
		isMoving = 0;
		overHead = 0;
		posx = 0;
		posy = 400;
		index = 0;
		switchN = 2;
	}

 D.最终的Mario类的属性:

	public static int posx,posy,screenWidth,screenHeight;//马里奥的位置和窗口的宽度和高度
	public static int step,switchN;//速度
	public static int jumpN;//跳跃切换次数
	public static int index;//第index张图片
	public static List<BufferedImage> images;//马里奥动画图片的集合
	public static int isMoving;//是否在移动0没有移动,1向右,2向左
	public static int overHead;//是否在空中

(2)MFrame类

  • 要通过键盘控制角色,窗口必须实现键盘监听器keyListener接口
  • 双缓冲图片需要不停地绘画,需要运用线程,这里通过实现Runnable接口实现

①实现键盘监听器

	@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();
		if( code == 37){//向左
			Mario.moveL();
		}
		else if(code == 38){//跳跃
			Mario.jump();
		}
		else if(code == 39){//向右
			Mario.moveR();
		}
//		else if(code == 32){//攻击
//			
//		}
		
		
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
		int code = e.getKeyCode();
		if(code == 37||code == 39){
			Mario.isMoving = 0;//放开鼠标停了下来
			Mario.index = 0;//动作图片中恢复静止的图片索引
		}
		else if(code == 38){
		}
	}

②实现Runnable接口:

	/*
	 * 在线程里面绘制图片
	 * */
	@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);
		}
	}

③给MFrame类添加属性:

private Thread marioThread ;//线程

在构造方法里面初始化:

最后不要忘了添加键盘监听器和启动线程 

(3)MFrame的重绘

	/*
	 * 重写paint方法
	 * */
	public void paint(Graphics g){
		super.repaint();
		if(marioDead) 
			g.drawImage(buffimg,0,0, null);
	}
	
	

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hnu哈哈

请接受直女的么么哒????

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值