项目——超级马里奥——Day(4)

目录

一.实现马里奥的移动:

实现思路分析:

代码实现:

二.实现马里奥的跳跃和下落:

实现思路分析:

代码实现:

三.马里奥掉落地面的优化:

四.实现游戏结束:

五.支撑知识点:

六、当前代码展示:

马里奥类:

当前的窗口类:


一.实现马里奥的移动:

实现思路分析:

1.//水平方向移动就是移动速度,垂直方向移动就是跳跃速度

//马里奥的移动速度
private int xSpeed;
//马里奥的跳跃速度
private int ySpeed;  

 2.判断马里奥是否处于空中

由于在空中时,马里奥不能移动,所以要判断一下马里奥此时的状态是否在跳跃中

如果马里奥正在跳跃,就把马里奥的状态改为jump_left,如果马里奥不在空中,就把马里奥的状态修改为move_left,正在向左移动

 //马里奥向左移动
    public void leftMove(){
        xSpeed =-5;
        //判断马里奥是否处于空中
        if(status.indexOf("jump") != -1){
            status="jump_left";
        }else{           //马里奥向左移动时
            status="move_left";
        }
    }
   

以此类推,马里奥是否在向右移动,是否向左停止,是否向右停止

 //马里奥向右移动
    public void rightMove(){
        xSpeed =5;
        if(status.indexOf("jump") != -1){
            status="jump_right";
        }else{
            status="move_right";
        }
    }
    //马里奥向左停止
    public void leftStop(){
        xSpeed =0;
        if(status.indexOf("jump") != -1){   //判断是否在空中
            status="jump_left";
        }else{
            status="stop_left";
        }
    }
    //马里奥向右停止
    public void rightStop(){
        xSpeed =0;
        if(status.indexOf("jump") != -1){   //判断是否在空中
            status="jump_right";
        }else{
            status="stop_right";
        }
    }

3.定义一个index索引,来帮助我们获取马里奥的当前状态的图像 

其实是为了让马里奥能够在奔跑的两张图片中来回切换,马里奥只有跑的动作和停下来的两个动作

 @Override
    public void run() {
        while(true){
            if(xSpeed <0 ||xSpeed>0){
                x+=xSpeed;
                //判断马里奥是否运动到了最左边
                x=Math.max(0,x);
            }
            //判断当前是否是移动状态
            if(status.contains("move")){
                index=index==0?1:0;
            }
            //判断是否是向左跑
            if("move_left".equals(status)){
                show =StaticValue.run_L.get(index);
            }
            //判断是否向右移动
            if("move_right".equals(status)){
                show =StaticValue.run_R.get(index);
            }
            //判断是否向左停止移动
            if("stop_left".equals(status)){
                show =StaticValue.stand_L;
            }
            //判断是否向右停止移动
            if("stop_right".equals(status)){
                show =StaticValue.stand_R;
            }
        }
    }

4.由于是键盘来控制马里奥的跳跃,移动,所以我们写完这几个方法之后,需要用键盘监听函数进行监听

按下时向右移动,松开时停止

//当键盘按下按键时调用
    @Override
    public void keyPressed(KeyEvent e) {
        //向右移动
        if(e.getKeyCode()==KeyEvent.VK_D){
            mario.rightMove();
        }
        //向左移动
        if(e.getKeyCode()==KeyEvent.VK_A){
            mario.leftMove();
        }
    }

    //当键盘松开按键时调用
    @Override
    public void keyReleased(KeyEvent e) {
        //向右停止
        if(e.getKeyCode()==KeyEvent.VK_D){
            mario.rightStop();
        }
        //向左停止
        if(e.getKeyCode()==KeyEvent.VK_A){
            mario.leftStop();
        }
    }

二.实现马里奥的跳跃和下落:

实现思路分析:

1.在马里奥创建一个int变量表示马里奥的上升时间

2.马里奥跳跃函数和马里奥下落函数

   //马里奥跳跃
    public void jump(){
        if(status.indexOf("jump") == -1){  //表示不是跳跃状态
            if(status.indexOf("left")!=-1){ //判断方向
                status="jump_left";    //改变为向左跳跃
            }else {
                status="jump_right";
            }
            ySpeed = -10; //为什么是负数,因为我们在垂直方向上时,越往上坐标大小越小
            upTime =7;  //  说明跳跃高度为70  但是其实这里应该要变成重力的那个公式
        }
    }
    //马里奥下落
    public void fail(){
        if(status.indexOf("left")!=-1){
            status="jump_left";
        }else{
            status="jump_right";
        }
        
        ySpeed =10;
    }

但是有一点困惑的是如果马里奥从障碍物落下的话,这里还能表示么,好像可以,应为fail只判断了方向,也就是说,但是按键监听的时候估计还是得控制一下,

还有一个困惑的点是马里奥为啥自己要开一个线程,然后窗口也要控制一个线程,而且我咋感觉这个线程有点像嵌入式里边的那个loop循环

3.在run方法里边写这个代码:

            //判断是否是向左跳跃
            if("jump_left".equals(status)){
                show=StaticValue.jump_L;
            }
            //判断是否是向右跳跃
            if("jump_right".equals(status)){
                show=StaticValue.jump_R;
            }

4.判断马里奥是否在障碍物上

 5.实现障碍物的阻挡

马里奥向上跳被阻挡

障碍物的阻挡为什么是这样的???什么意思 

马里奥往右走被阻挡

马里奥往左走被阻挡 

砖块高度是420,马里奥人物高度是25,这个坐标是以哪里为原点的啊?

马里奥是以左上角为坐标原点

障碍物是以左上角,都是以左上角作为每一个图片的坐标原点

三.马里奥掉落地面的优化:

由于最开始马里奥是否在障碍物上的bool值是false,而在下一步才开始进行循环判断马里奥是否真正在每一个障碍物上,所以这其中有一个时间空缺,我们可以将马里奥出生位置往上挪一点点,这样使马里奥掉落到地面之前有一个时间段可以让for循环判断,不至于一开始马里奥掉到了地面的下一层

引起掉落到地面的下一层的原因位置代码:

            //还要时刻判断马里奥是否处于障碍物上
            boolean onObstacle=false;
            //遍历当前场景里所有的障碍物  所以这个时候background派上用场了,需要用background对象来获取当前的障碍物个数和位置
            for(int i=0;i<background.getObstacleList().size();i++){
                Obstacle ob = background.getObstacleList().get(i);
                //判断马里奥是否在障碍物上方
                if(ob.getY() ==this.y +25 && (ob.getX() >this.x -30 && ob.getX() <this.x + 25)){
                    //水平位置左右两边保证了,垂直方向上也要保证
                    onObstacle=true;
                }
            }

可以在MyFrame窗口的这两个地方进行修改代码:-----主要是修改马里奥初始化的高度

 @Override
    public void run() {
        while(true){
            repaint(); //用于重新绘制我们的图像
            try {
                Thread.sleep(50);
                //判断马里奥是否移动到了场景最右边,移动到了最右边就应该切换场景

                if(mario.getX()>=775){
                    nowBg =allBg.get(nowBg.getSort());
                    //将当前的场景对象传递给我们马里奥类
                    mario.setBackground(nowBg);
                    //重置马里奥的坐标
                    mario.setX(10);
                    mario.setY(355);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
        //初始化马里奥
        mario =new Mario(10,355);
        //创建全部的场景
        for(int i=1;i<=3;i++){
            allBg.add(new BackGround(i, i==3? true:false));
        }

四.实现游戏结束:

这里需要先做后几关的场景界面,还要实现碰到旗杆才会游戏结束。。

五、当前代码展示:

马里奥类:

package com.game;

import com.Constants.StaticValue;
import com.View.BackGround;

import java.awt.image.BufferedImage;

public class Mario implements Runnable{
    //用于表示横纵坐标
    private int x;
    private int y;
    //用于表示当前的状态
    private String status;
    //用于显示当前状态对应的图像
    private BufferedImage show=null;

    //定义一个BackGround对象,用来获取障碍物的信息!!!
    private BackGround background=new BackGround();
    //用于实现马里奥的动作
    private Thread thread=null;

    //马里奥的移动速度 xspeed不等于0说明在移动,负的为往左走,正的为往右走
    private int xSpeed;
    //马里奥的跳跃速度
    private int ySpeed;   //水平方向移动就是移动速度,垂直方向移动就是跳跃速度

    //定义一个索引  用于取得马里奥的运动图像
    private int index;

    //表示马里奥的上升时间
    private int upTime = 0;

    public Mario() {
    }

    public Mario(int x, int y) {
        this.x = x;
        this.y = y;
        show = StaticValue.stand_R;   //初始动作是向右站立
        this.status="stand-right";
        thread=new Thread(this);   //创建线程
        thread.start();//启动线程
    }

    //马里奥向左移动
    public void leftMove(){
        xSpeed =-5;
        //判断马里奥是否处于空中
        if(status.indexOf("jump") != -1){
            status="jump_left";
        }else{           //马里奥向左移动时
            status="move_left";
        }
    }
    //马里奥向右移动
    public void rightMove(){
        xSpeed =5;
        if(status.indexOf("jump") != -1){
            status="jump_right";
        }else{
            status="move_right";
        }
    }
    //马里奥向左停止
    public void leftStop(){
        xSpeed =0;
        if(status.indexOf("jump") != -1){   //判断是否在空中
            status="jump_left";
        }else{
            status="stop_left";
        }
    }
    //马里奥向右停止
    public void rightStop(){
        xSpeed =0;
        if(status.indexOf("jump") != -1){   //判断是否在空中
            status="jump_right";
        }else{
            status="stop_right";
        }
    }

    //马里奥跳跃
    public void jump(){
        if(status.indexOf("jump") == -1){  //表示不是跳跃状态
            if(status.indexOf("left")!=-1){ //判断方向
                status="jump_left";    //改变为向左跳跃
            }else {
                status="jump_right";
            }
            ySpeed = -10; //为什么是负数,因为我们在垂直方向上时,越往上坐标大小越小
            upTime =7;  //  TODO说明跳跃高度为70  但是其实这里应该要变成重力的那个公式
        }
    }
    //马里奥下落
    public void fail(){
        if(status.indexOf("left")!=-1){
            status="jump_left";
        }else{
            status="jump_right";
        }
        ySpeed =10;
    }

    @Override
    public void run() {
        while(true){

            //还要时刻判断马里奥是否处于障碍物上
            boolean onObstacle=false;
            //判断是否可以往右走
            boolean canRight =true;
            //判断是否可以往左走
            boolean canLeft =true;

            //遍历当前场景里所有的障碍物  所以这个时候background派上用场了,需要用background对象来获取当前的障碍物个数和位置
            for(int i=0;i<background.getObstacleList().size();i++){
                Obstacle ob = background.getObstacleList().get(i);
                //判断马里奥是否在障碍物上方
                if(ob.getY() ==this.y +25 && (ob.getX() >this.x -30 && ob.getX() <this.x + 25)){
                    //水平位置左右两边保证了,垂直方向上也要保证
                    onObstacle=true;
                }

                //判断是否跳起来顶到砖块
                if(ob.getY()>=this.y -30 &&ob.getY()<=this.y -20 &&(ob.getX() >this.x-30 && ob.getX() <this.x+25)) {
                    //砖块高度是420,马里奥人物高度是25,这个坐标是以哪里为原点的啊
                    //还需要判断是普通砖块还是可破坏砖块
                    if(ob.getType()==0){
                        //用background来调用obstaclelist
                        background.getObstacleList().remove(ob);
                    }
                    upTime=0; //顶到砖块后立刻下落
                }

                //判断是否可以往右走
                if(this.x+25==ob.getX()&&(this.y-30<ob.getY() && this.y+25>ob.getY())){
                    canRight=false;
                    //xSpeed=0;   //如果在这里把速度变成0,惹,,这个判断就一直成立,xspeed就一直等于0,阿哲,所以你就一动不了了
                }
                //判断是否可以往左走
                if(this.x==ob.getX()+30&&(this.y-30<ob.getY() && this.y+25>ob.getY())){
                    canLeft=false;
                    //xSpeed=0;
                }

            }
            //进行马里奥跳跃的操作   表示在障碍物上而且在障碍物上没有跳??因该是这个意思
            if(onObstacle && upTime == 0){
                if(status.indexOf("left")!=-1){
                    if(xSpeed!=0){
                        //表示向左移动
                        status="move_left";
                    }else{
                        status="stop_left";
                    }
                }else{
                    if(xSpeed!=0){
                        //表示向左移动
                        status="move_right";
                    }else{
                        status="stop_right";
                    }
                }
            }else{
                //处于上升状态
                if(upTime !=0){
                    upTime--;
                }else{
                    fail();
                }
                y+=ySpeed;
            }


            //这是移动函数    所以只有速度不等于0而且没有遇到障碍物才能移动
            if(canLeft && canRight &&(xSpeed <0 ||xSpeed>0)){
                x+=xSpeed;
                //判断马里奥是否运动到了最左边
                x=Math.max(0,x);
            }

            //判断当前是否是移动状态
            if(status.contains("move")){
                index=index==0?1:0;
            }

            //判断是否是向左跑
            if("move_left".equals(status)){
                show =StaticValue.run_L.get(index);
            }
            //判断是否向右移动
            if("move_right".equals(status)){
                show =StaticValue.run_R.get(index);
            }
            //判断是否向左停止移动
            if("stop_left".equals(status)){
                show =StaticValue.stand_L;
            }
            //判断是否向右停止移动
            if("stop_right".equals(status)){
                show =StaticValue.stand_R;
            }

            //判断是否是向左跳跃
            if("jump_left".equals(status)){
                show=StaticValue.jump_L;
            }
            //判断是否是向右跳跃
            if("jump_right".equals(status)){
                show=StaticValue.jump_R;
            }


            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public BufferedImage getShow() {
        return show;
    }

    public void setShow(BufferedImage show) {
        this.show = show;
    }


    public void setBackground(BackGround background) {
        this.background = background;
    }

}

当前的窗口类:

package com.View;

import com.Constants.StaticValue;
import com.game.Mario;
import com.game.Obstacle;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;

public class MyFrame extends JFrame implements KeyListener,Runnable{
    //为什么要存储场景   TODO惹??
    //BackGround类的list,里边全部是background类,不是buffered图片,要区别开来
    //用于存储  所有的背景
    private List<BackGround> allBg =new ArrayList<>();
    //用于存储  当前的背景
    private BackGround nowBg =new BackGround();
    //用于双缓存
    //定义一个变量用于双缓存       TODO????????
    private Image offScreenImage =null;

    //马里奥对象
    private Mario mario = new Mario();

    //定义一个线程对象,用于实现马里奥的运动
    private Thread thread =new Thread(this);

    //无参构造
    public MyFrame() {
        //设置窗口的大小为800*600
        this.setSize(800,600);
        //设置窗口的居中显示
        this.setLocationRelativeTo(null);
        //设置窗口的可见性
        this.setVisible(true);
        //设置点击窗口上的关闭键,结束程序
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口大小不可变
        this.setResizable(false);
        //向窗口对象添加键盘监听器
        this.addKeyListener(this);
        //设置窗口名称
        this.setTitle("超级玛丽");
        //初始化图片
        StaticValue.init();

        //初始化马里奥
        mario =new Mario(10,355);
        //创建全部的场景
        for(int i=1;i<=3;i++){
            allBg.add(new BackGround(i, i==3? true:false));
        }

        //将第一个场景设置为当前的场景
        nowBg =allBg.get(0);
        mario.setBackground(nowBg);   //当前场景的背景对象传递给马里奥
        //绘制图像   进行图像的绘制
        repaint();
        thread.start();   //启动线程
    }

    @Override
    public void paint(Graphics g) {
        if(offScreenImage ==null){
            offScreenImage =createImage(800,600);
        }

        Graphics graphics = offScreenImage.getGraphics();
        graphics.fillRect(0,0,800,600);
        //fillRect 用于在指定的矩形内绘制一个填充的矩形

        //绘制背景   将图像绘制到了缓冲区上
        graphics.drawImage(nowBg.getBgImage(), 0,0,this);

        //绘制障碍物
        for(Obstacle ob : nowBg.getObstacleList()){
            graphics.drawImage(ob.getShow(),ob.getX(),ob.getY(),this);
        }
        //绘制马里奥
        graphics.drawImage(mario.getShow(),mario.getX(),mario.getY(),this);       //注意马里奥绘制要在g.drawImage之前

        //将缓冲区的图片绘制到窗口中
        g.drawImage(offScreenImage,0,0,this);

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //当键盘按下按键时调用
    @Override
    public void keyPressed(KeyEvent e) {
        //向右移动
        if(e.getKeyCode()==KeyEvent.VK_D){
            mario.rightMove();
        }
        //向左移动
        if(e.getKeyCode()==KeyEvent.VK_A){
            mario.leftMove();
        }

        if (e.getKeyCode()==KeyEvent.VK_W){
            mario.jump();
        }
    }

    //当键盘松开按键时调用
    @Override
    public void keyReleased(KeyEvent e) {
        //向右停止
        if(e.getKeyCode()==KeyEvent.VK_D){
            mario.rightStop();
        }
        //向左停止
        if(e.getKeyCode()==KeyEvent.VK_A){
            mario.leftStop();
        }

    }


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

    @Override
    public void run() {
        while(true){
            repaint(); //用于重新绘制我们的图像
            try {
                Thread.sleep(50);
                //判断马里奥是否移动到了场景最右边,移动到了最右边就应该切换场景

                if(mario.getX()>=775){
                    nowBg =allBg.get(nowBg.getSort());
                    //将当前的场景对象传递给我们马里奥类
                    mario.setBackground(nowBg);
                    //重置马里奥的坐标
                    mario.setX(10);
                    mario.setY(355);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值