爱玩灬 发表于 2019-6-12 16:34

自制 JAVA 版 魔塔

本帖最后由 爱玩灬 于 2019-8-7 15:45 编辑

首先,我们要有图片

其次要有每层的地图数据

然后我们先创建一个东西的类 定义我们需要的属性可以自己自定义比如魔攻、魔防、幸运值、甚至子弹数等等public class Stuff {
      int hp;                //生命
      int atk;      //攻击
      int def;      //防御
      int money;      //价值
      int exp;      //经验
      }然后创建英雄类和怪物类继承Stuff代码如下怪物类:public class Monster extends Stuff{
      public String name;
      public Image mimage;
      /**
         * 这里我们初始化怪物对象时候需要给予他们需要的属性
         * @Param paramString 名称
         * @param paramInt1 生命
         * @param paramInt2 攻击
         * @param paramInt3 防御
         * @param paramInt4 价值
         * @param paramInt5 经验
         * @param miImage 图片
         */
      public Monster(String paramString,int paramInt1,int paramInt2,int paramInt3,int paramInt4,int paramInt5,Image miImage) {
                this.name=paramString;//名称
                this.hp=paramInt1;//生命
                this.atk=paramInt2;//攻击
                this.def=paramInt3;//防御
                this.money=paramInt4;//价值
                this.exp=paramInt5;//经验
                this.mimage=miImage;//图片
      }
      public String getName() {
                return name;
      }
      public void setName(String name) {
                this.name = name;
      }   
}英雄类:public class Hero extends Stuff {
      private int level;// 英雄等级
      private int hp;
      private int atk;
      private int def;
      private int money;
      private int exp;
      private int ykey;//钥匙
      private int bkey;
      private int rkey;
      private int maxstair;//目前高层用于以后使用飞机跳层
      public Image hImage;

/***set get 以及构造方法省略*/
//遇到怪物触发战斗的方法
      public int attack(Monster m) {
                // 有效输出=攻击-怪物护甲
                int h_harm = atk - m.def;
                // 承受伤害值=怪物攻击-护甲
                int m_harm = m.atk - def;
                //击败怪物需要的回合数
                int Rounds = 0;
                if (h_harm>0) {
                        Rounds = (int)Math.ceil((double)m.hp/h_harm);
                }else {
                        //无法击穿怪物护甲
                        return 0;
                }
                if (h_harm > 0 && m_harm <= 0) {
                        //碾压怪物方法
                        money = money + m.money;
                        exp = exp + m.exp;
                        return 1;
                }else if(Rounds*m_harm>=hp){
                        //生命值不足以支付消耗
                        return 0;
                } else {
                        //两败俱伤
                        m.hp = 0;
                        hp=hp-(Rounds*m_harm);
                        money = money + m.money;
                        exp = exp + m.exp;
                }
                return -1;
      }
}
现在有怪物有英雄了就缺地图了:public class ReadMap {
      private int[][][] maps = new int;
      /**
         * 初始化地图数据简单讲就是读取本地map文件将里面的数字存到三维数组中
         */
      public ReadMap() {
                //这里i<=1只是测试用正常有几层写几
                for (int i = 1; i <= 1; i++) {
                        try {
                              BufferedReader in = new BufferedReader(new FileReader("maps/" + i + ".map"));                           
                              String line;
                              int row = 0;
                              // 逐行读取,并将每个数组放入到数组中
                              while ((line = in.readLine()) != null) {
                                        String[] temp = line.split("\\s+");
                                        for (int j = 0; j < temp.length; j++) {
                                                maps = Integer.parseInt(temp);
                                        }
                                        row++;
                              }
                              in.close();
                        } catch (FileNotFoundException e) {
                              e.printStackTrace();
                        } catch (IOException e) {
                              e.printStackTrace();
                        }
                }
      }
      public int[][][] getMaps() {
                return maps;
      }
}
ok,现在地图数据,英雄,怪物都有了接下来就是创建视图展示了主体用JFrame,用JPanel放游戏主体窗口和一个展示面板大概是这样子:

class startGame extends JFrame{
      
      boolean canmove = true; //可否移动开关对话时候可以改成float
      private Image iBuffer;
      private Graphics gBuffer;
      Mypanel mypanel;//游戏界面
      Mypanel.MyState myState;//属性界面
      Hero hero;//英雄
      Toolkit kit = Toolkit.getDefaultToolkit();
      /**
         * 载入图片数据
         */
      Image mapimg[] = {kit.getImage("maparea/1.jpg"), kit.getImage("maparea/2.jpg"), kit.getImage("maparea/3.jpg"),
                        kit.getImage("maparea/4.jpg"), kit.getImage("maparea/5.jpg"), kit.getImage("maparea/6.jpg"),
                        kit.getImage("maparea/7.jpg"), kit.getImage("maparea/8.jpg"), kit.getImage("maparea/9.jpg"),
                        kit.getImage("maparea/10.jpg"), kit.getImage("npc/11.jpg"), kit.getImage("npc/12.jpg"),
                        kit.getImage("npc/13.jpg"), kit.getImage("npc/14.jpg"), kit.getImage("npc/15.jpg"),
                        kit.getImage("npc/16.jpg"), kit.getImage("npc/17.jpg"), kit.getImage("npc/18.jpg"),
                        kit.getImage("monsters/19.jpg"), kit.getImage("monsters/20.jpg"), kit.getImage("monsters/21.jpg"),
                        kit.getImage("monsters/22.jpg"), kit.getImage("monsters/23.jpg"), kit.getImage("monsters/24.jpg"),
                        kit.getImage("monsters/25.jpg"), kit.getImage("monsters/26.jpg"), kit.getImage("monsters/27.jpg"),
                        kit.getImage("monsters/28.jpg"), kit.getImage("monsters/29.jpg"), kit.getImage("monsters/30.jpg"),
                        kit.getImage("monsters/31.jpg"), kit.getImage("monsters/32.jpg"), kit.getImage("monsters/33.jpg"),
                        kit.getImage("monsters/34.jpg"), kit.getImage("monsters/35.jpg"), kit.getImage("monsters/36.jpg"),
                        kit.getImage("monsters/37.jpg"), kit.getImage("monsters/38.jpg"), kit.getImage("monsters/39.jpg"),
                        kit.getImage("monsters/40.jpg"), kit.getImage("monsters/41.jpg"), kit.getImage("monsters/42.jpg"),
                        kit.getImage("monsters/43.jpg"), kit.getImage("monsters/44.jpg"), kit.getImage("monsters/45.jpg"),
                        kit.getImage("monsters/46.jpg"), kit.getImage("monsters/47.jpg"), kit.getImage("monsters/48.jpg"),
                        kit.getImage("monsters/49.jpg"), kit.getImage("monsters/50.jpg"), kit.getImage("monsters/51.jpg"),
                        kit.getImage("tool/52.jpg"), kit.getImage("tool/53.jpg"), kit.getImage("tool/54.jpg"),
                        kit.getImage("tool/55.jpg"), kit.getImage("tool/56.jpg"), kit.getImage("tool/57.jpg"),
                        kit.getImage("tool/58.jpg"), kit.getImage("tool/59.jpg"), kit.getImage("tool/60.jpg"),
                        kit.getImage("tool/61.jpg"), kit.getImage("tool/62.jpg"), kit.getImage("tool/63.jpg"),
                        kit.getImage("tool/64.jpg"), kit.getImage("tool/65.jpg"), kit.getImage("tool/66.jpg"),
                        kit.getImage("tool/67.jpg"), kit.getImage("tool/68.jpg"), kit.getImage("tool/69.jpg"),
                        kit.getImage("tool/70.jpg"), kit.getImage("tool/71.jpg"), kit.getImage("tool/72.jpg"),
                        kit.getImage("tool/73.jpg"), kit.getImage("role/74.jpg"), kit.getImage("role/75.jpg"),
                        kit.getImage("role/76.jpg"), kit.getImage("role/77.jpg"), kit.getImage("extra/78.jpg"),
                        kit.getImage("extra/79.jpg"), kit.getImage("extra/80.jpg"), kit.getImage("extra/81.jpg"),                        kit.getImage("extra/82.jpg"), kit.getImage("extra/83.png")};
      /**
* 怪物
*/
Monster monster[] = new Monster;
      /**
         * 初始化英雄与怪物属性
         */
      public void init() {
                hero = new Hero(1, 10000, 10, 10, 0, 0, 10, 0, 0, 1, mapimg);
                monster = new Monster("小史莱姆", 50, 20, 1, 1, 1, mapimg);
      }
      public startGame() {
                super("魔塔demo");
                init();
                mypanel = new Mypanel();//游戏界面
                mypanel.setBounds(200, 32, 352, 352);
                myState = mypanel.new MyState();//属性面板
                myState.setBounds(30, 32, 128, 352);
                Container c = getContentPane();//装箱
                c.setLayout(null);
                c.setBackground(Color.BLACK);
                c.add(mypanel);
                c.add(myState);
                setSize(590, 450);
                setVisible(true);
                setLocationRelativeTo(null);
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                mypanel.requestFocus();
      }

//因为要监听键盘输入所以这里继承KeyListener接口
      class Mypanel extends JPanel implements KeyListener {
                private static final long serialVersionUID = 1L;
               
                private int level = 1; //所在楼层数
                private int mx, my; //xy坐标
                private int tempmap[][][];//地图数据
                public Mypanel() {
                        setSize(352, 352);
                        this.addKeyListener(this);
                        this.tempmap = new ReadMap().getMaps();//读取map文件
                        this.mx = 10;
                        this.my = 5;
                        requestFocus();
                }
   
                @Override
                public void paint(Graphics g) {
                        if (iBuffer == null) {
                              iBuffer = createImage(this.getSize().width, this.getSize().height);
                              gBuffer = iBuffer.getGraphics();
                        }
                        for (int i = 0; i < 11; i++) {
                              for (int j = 0; j < 11; j++) {
                                        //将地图图片添加到游戏界面 图片名从1开始数组从0开始所以要-1
                                       gBuffer.drawImage(mapimg - 1], j * 32, i * 32, 32, 32, this);                           
                              }
                        }
                     //临时在2,2坐标加一直史莱姆
                     tempmap=2;
                     gBuffer.drawImage(mapimg, 1* 32, 1 * 32, 32, 32, this);
                     //将英雄图片添加到 英雄的坐标只能执行一次所以后面会添加一个是否第一次进入的开关标识防止出现两个英雄
                        gBuffer.drawImage(mapimg, my * 32, mx * 32, 32, 32, this);
                        g.drawImage(iBuffer, 0, 0, this);
                        requestFocus();
                }               
                /**
               * 移动方法
               * 键盘监听keycode实现移动
               * 因为移动都差不多所以直接写了个move方法* 传入移动目的坐标以及英雄朝向的图片实现移动
               */
                public void keyPressed(KeyEvent e) {
                        if (canmove) {
                              if (e.getKeyCode() == 37) {
                                        // 向左移动                              
                                        if(my-1>-1) move(mx,my-1,74);
                              } else if (e.getKeyCode() == 38) {
                                        // 向上移动                        
                                        if(mx-1>-1) move(mx-1,my,77);
                              } else if (e.getKeyCode() == 39) {
                                        // 向右移动               
                                        if(my+1<11) move(mx,my+1,75);
                              } else if (e.getKeyCode() == 40) {
                                        // 向下移动                        
                                        if(mx+1<11) move(mx+1,my,76);
                              }
                        }
                        mypanel.repaint();
                }

                @Override
                public void keyReleased(KeyEvent e) {}

//这里移动方法用来判断移动目的地如果碰到各种方块的不同实现方法这里先不写了只接基本的移动
                public void move(int gox, int goy, int face) {
                        if (tempmap == 5) {
                              tempmap = face;
                              tempmap = 5;
                              mx = gox;
                              my = goy;
                        }
                        myState.repaint();
                        repaint();//刷新
                }


//这里是属性面板就不多赘述了 后面要添加鼠标点击监听来实现飞机跳层
                class MyState extends JPanel {         
                        Toolkit kit = Toolkit.getDefaultToolkit();
                        Image image[] = { kit.getImage("maparea/5.jpg"), kit.getImage("tool/54.jpg"), kit.getImage("tool/55.jpg"),
                                        kit.getImage("tool/56.jpg"), kit.getImage("tool/59.jpg"), kit.getImage("tool/66.jpg"),
                                        kit.getImage("tool/69.jpg"), kit.getImage("tool/71.jpg") };
                        public MyState() {
                              hero.getLevel();                        
                              setSize(128, 352);
                        }

                        @Override
                        public void paint(Graphics g) {
                              Font font = new Font("TimesRoman", Font.BOLD + Font.ITALIC, 18);
                              for (int i = 0; i < 4; i++) {
                                        for (int j = 0; j < 11; j++) {
                                                g.drawImage(image, i * 32, j * 32, 32, 32, this);
                                        }
                              }
                              g.setFont(font);
                              g.drawString(hero.getLevel() + "级", 0, 1 * 32);
                              g.drawString("生命" + hero.getHp(), 0, 2 * 32);
                              g.drawString("攻击" + hero.getAtk(), 0, 3 * 32);
                              g.drawString("防御" + hero.getDef(), 0, 4 * 32);
                              g.drawString("金币" + hero.getMoney(), 0, 5 * 32);
                              g.drawString("经验" + hero.getExp(), 0, 6 * 32);
                              g.drawImage(image, 0, 6 * 32 + 10, this);
                              g.drawString("" + hero.getYkey(), 2 * 32, 7 * 32);
                              g.drawImage(image, 0, 7 * 32 + 10, this);
                              g.drawString("" + hero.getBkey(), 2 * 32, 8 * 32);
                              g.drawImage(image, 0, 8 * 32 + 10, this);
                              g.drawString("" + hero.getRkey(), 2 * 32, 9 * 32);
                              g.drawString("第" + mypanel.level + "层", 0, 350);
                        }
                }
                @Override
                public void keyTyped(KeyEvent e) {
                        
                }
      }ok保存一下运行测试一下public class Game {
      public static void main(String[] args) {
                new startGame();
      }
}

ok基本完成界面,也可以移动,就是我只放如了一张英雄朝向的图片所以他不管怎么动都是朝向右边,因为没有写移动时候触发不同元素的事件所以不会触发战斗方法;
遭遇战斗方法:
if (tempmap >= 19 && tempmap <= 50) {
                              // 地图元素中19-50是怪物,战斗方法返回值为+-1及能打过可以写一个通过地图元素id获取怪物对象的方法来获取怪物对象                              
                                 if ((hero.attack(怪物对象) == -1) || (hero.attack(怪物对象) == 1)) {
                                        tempmap = face;
                                        tempmap = 5;
                                        mx = gox;
                                        my = goy;
                              }
至此基本完成大体的框架 剩下的只有各种遭遇方法的补充
===========================================================================================
6月13日更新:基于原有的demo添加了上下楼,道具拾取以及对话窗
先多创建几个map文件:5是地板 4是上楼楼梯,78是下楼楼梯
5   5   5   5   54   5   5   5   55   
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55         

5   5   5   5   45   5   5   5   55   
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   55   5   5   5   55
5   5   5   5   578   5   5   5   55   

思路:创建一个数组保存上下楼的坐标在接触楼梯时候触发上下楼方法 可以通过一个标识符判断上下楼
一下代码在原基础上新增修改

//在ReadMap类中新增自动判断文件数 主体未作改变方法主体省略
      File f = new File("maps");
                File fs[] = f.listFiles();               
                for (int i = 1; i <= fs.length; i++) {}
//在startGame内部类中完善上次的初始化英雄图片方法,只执行一次
                        if(isStart) {
                              tempmap=76;
                              gBuffer.drawImage(mapimg, my * 32, mx * 32, 32, 32, this);
                              isStart=false;
                        }
//创建两个开关和一个坐标数组用于控制上下楼
         /**
         * 上层开关
         */
      boolean up_stair = false;
      /**
         * 下层开关
         */
      boolean down_stair = false;
         /**
         * 每层的移动坐标 前为上楼后所在坐标,后为下楼后所在坐标: 坐标自定义
         * 下楼进入第一层时所在位置x,y
         * 上楼进入第一层时所在位置x,y
         */
      private int location[][][] = { { { 10, 5 }, { 5, 5 } }, { { 5, 5 }, { 5, 5 } }};
/**
* 在Mypanel内部类中新增获取上下楼坐标的方法
*/
public void readmaps(int level) {
                        // 获取上下层的 初始坐标
                        if (up_stair) {
                              mx = location;
                              my = location;
                        } else if (down_stair) {
                              mx = location;
                              my = location;
                        }
                }

//添加移动触发方法
else if (tempmap == 4) {
                              tempmap = 5;
                              level++;
                              up_stair = true;//上楼标识符
                              if (level > hero.getMaxstair()) {
                                        // 更新到达的最高层
                                        hero.setMaxstair(level);
                              }
                              readmaps(level);//获取下层坐标
                              tempmap = face;      
                              up_stair = false;
                        }else if (tempmap == 78) {
                              tempmap = 5;
                              level--;
                              down_stair = true;//下楼标识符                              
                              readmaps(level);//获取下层坐标
                              tempmap = face;      
                              down_stair = false;
                        }

效果图:成功

然后是钥匙的使用和拾取以及道具的拾取方法:

else if (tempmap == 3 && myState.yellowkey > 0) {
                              tempmap = face;
                              tempmap = 5;
                              mx = gox;
                              my = goy;
                              myState.yellowkey--;
                              hero.setYkey(myState.yellowkey);
                        } else if (tempmap == 55) {
                              tempmap = face;
                              tempmap = 5;
                              mx = gox;
                              my = goy;
                              myState.yellowkey++;
                              hero.setYkey(myState.yellowkey);
                        }else if (tempmap == 53) {
                              //吃道具+属性
                              tempmap = face;
                              tempmap = 5;
                              mx = gox;
                              my = goy;
                              myState.atk = myState.atk + 30000;
                              hero.setAtk(myState.atk);
                        }
对话框方法:public void showSpeaking(Graphics drawOffScreen, String string) {                        drawOffScreen.setColor(Color.BLACK);
                        drawOffScreen.fillRect(100, 132, 200, 120);
                        drawOffScreen.setColor(Color.RED);
                        drawOffScreen.drawRoundRect(100, 132, 200, 120, 10, 10);
                        drawOffScreen.setColor(Color.WHITE);
                        drawOffScreen.setFont(new Font("TimesRoman", Font.BOLD, 15));
                        string += "                        ";
                        for (int i = 0; !("            ".equals(string.substring(i * 12, (i + 1) * 12))); ++i) {
                              drawOffScreen.drawString(string.substring(i * 12, (i + 1) * 12), 100, 148 + i * 20);
                        }
                        drawOffScreen.drawString("---space---", 160, 247);
                        drawOffScreen.setColor(Color.BLACK);
                }
效果:
好了我就做到这里了,后面大家自己完善吧,商店没写和对话框方法差不多不过要添加上下键和回车键的监听
代码地址:链接:https://pan.baidu.com/s/1ryrKHsAX9M6ArD8bueG8jw 提取码:7l3x

大家仔细看看注释和逻辑就会发现这个根本不难,只要会java基础,了解一点面向对象的思想,知道JFrame这个框架都有什么方法你也完全可以自己做一个,一个画板上面用数据带出不同的展示图片,通过监听键盘输入来控制一个英雄对象移动,并用if判断来触发与不同对象接触后的方法(战斗,道具,商店),没有复杂的逻辑复杂的算法,仔细看看注释你基本就能自己做一个demo出来了。

吾爱丶黑色铅笔 发表于 2019-6-12 17:40

本帖最后由 吾爱丶黑色铅笔 于 2019-6-12 17:43 编辑

我之前学习java的时候也做了个,就目前看你的项目,应该没我那个优秀,哈哈https://www.z4a.net/image/UwEHTC
https://www.z4a.net/image/UwEVsn
https://www.z4a.net/image/UwEja6
https://www.z4a.net/image/UwE2Hv
https://www.z4a.net/image/UwETbThttps://www.z4a.net/images/2019/06/12/0.png    https://www.z4a.net/images/2019/06/12/1f7df856e36371a23.png
https://www.z4a.net/images/2019/06/12/267b1da1affdd33d1.png    https://www.z4a.net/images/2019/06/12/31b7ebaf4fd14c414.png
https://www.z4a.net/images/2019/06/12/41de26f9556e86282.png

最小白 发表于 2019-6-12 16:56

现在都玩的这么高端,难道只有我一个人还在玩泥巴吗?

羽橙雨 发表于 2019-6-12 16:46

有点骚啊

白陌陌 发表于 2019-6-12 16:56

支持作者 加油啊 我才开始学java

lingfei 发表于 2019-6-12 16:58

我凑,大佬啊。厉害

YuLoo 发表于 2019-6-12 17:13

支持,以前在步步高电子辞典上,玩疯了

醉酒人生 发表于 2019-6-12 17:31


现在都玩的这么高端,难道只有我一个人还在玩泥巴吗?

Light紫星 发表于 2019-6-12 17:48

大佬大佬,学习了

kingbackgo 发表于 2019-6-12 18:11

学习一下,都是大佬啊!
页: [1] 2 3 4 5 6 7
查看完整版本: 自制 JAVA 版 魔塔