IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> javaSE小游戏:古堡游戏 -> 正文阅读

[游戏开发]javaSE小游戏:古堡游戏

目标

? 开发一款基于javaSE的文字小游戏,通过控制台的输入与输出交互。比如说输入go south就能向南走,输入talk 就能和npc聊天,输入help可以获取帮助...。整体的游戏设计并不复杂,比较重要的是对象的设计,如果你是一名java的初学者,不妨可以自己先尝试写一写。

思路分析

? 首先是控制台交互的设计,如果按照面向过程来设计思路是很简单的,我只需要读取输入,然后写一些if esle语句来实现对应的要求即可。可是,仔细想一想,这样会很复杂,假如当前在bedroom里,控制台输入go south。为了实现命令,首先要解析命令,if(go)做什么,if(help)做什么。知道是go命令后还要判断当前在哪里,if(bedroom )怎么样 if( lobby)怎么样,这就非常的繁琐。而且,如果有一天,我想加入一个命令或者房间,整个程序都将会变动,扩展性极差。用专业术语来说这叫做耦合性太强,面向对象设计的目的是解耦。巧妙的运用继承和多态,程序将变得非常漂亮。按照这个思路,之后的任何东西都要封装成对象,比如房间,npc,装备...。

设计的实现

命令(command)

思路

? 为了解耦合,我们可以设计一个command父类,里面有doSomething()方法,具体的命令对象继承它,比如go、help。在Game类(也就是main所在的类)将这些命令new出来存放在map里,key为命令的名字,value为命令对象,当用户输入后我就查找map里有没有用户输入的命令,如果有,我就把对应的对象拿出来,然后直接调用他的doSomething()方法。

分析

? 这样的设计是不是有种焕然一新的感觉,我们不再关心输入的具体命令是什么,不用再写长串的? ?if else 只要找一个合适的对象来完成相关操作就好,这就是面向对象编程的好处。这样写扩展性就非常好了,想加一个命令,加一个对象就好。

代码

public class Game {
    public void run() {
        Scanner in = new Scanner(System.in);
        while (true) {
            String line = in.nextLine();
            //默认用户按照两位输入,比如go north 或者 talk flower
            String[] words = line.split(" ");
            command handle = commands.get(words[0]);
            if (handle == null) {
                System.out.println("错误的命令");
                continue;
            }
            if (handle.isBye()) {
                break;
            }
            String value = "";
            if (words.length > 1) {
                value = words[1];
            }
            if (handle != null) {
                handle.doSomthing(value);
            }
        }
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        Game game = new Game();
        game.printWelcome();
        game.run();
        System.out.println("感谢您的光临。再见!");
        in.close();
    }
}

/**
 * 命令的父类
 */
public class command {
  
    public void doSomthing(String word) {
    }

}

/**
 * 前往房间的命令,继承自command
 */
public class goRoom extends command{

    public void doSomthing(String word) {

         //这个方法具体干了什么先不用管,你只需知道,它做了进入房间的相关操作
         //记住这个hero,下面会讲解
        if(hero.getCurrentRoom().getNearRoom().containsKey(word)){
            hero.getCurrentRoom().sayBye();
            hero.setCurrentRoom(hero.getCurrentRoom().getNearRoom().get(word));
            hero.getCurrentRoom().sayHello();
            System.out.print("能走的路有");
            for(String s:hero.getCurrentRoom().getNearRoom().keySet()){
                System.out.print("  "+s+"  ");
            }
        }else {
            System.out.println("没有路");
        }
    }
}

房间(Room)

思路

? 有了上面的例子,房间是不是也可以顺理成章的想到继承,写一个room的父类,里面有map成员变量 nearRoom 和npcs 。nearRoom用于存储四周的房间,Npcs 用于存储房间里的npc。还有一个String 类型的description用于介绍房间。

分析

? 可能到现在你还不知道怎样把房间和命令组合起来,输入命令后就能进入对应的房间。别着急慢慢往下看,你也可以自己尝试想一下,如果是你会怎么实现。

代码


public class room{
    /**
     * 描述在new时就设置好,临近房间和npc得在游戏时设置
     */
    Map<String,room> nearRoom;
    Map<String, npc> npcs;
    //在这里description被我设置成map,这是后期优化的,你知道description是用来干什么的就好
    Map<String,String> descriptions;

    public room(Map<String, String> descriptions) {
        this.descriptions = descriptions;
    }

    public room() {
    }

    public void setDescriptions(Map<String, String> descriptions) {
        this.descriptions = descriptions;
    }

    public void setNearRoom(Map<String, room> nearRoom) {
        this.nearRoom = nearRoom;
    }

    public void setNpcs(Map<String, npc> npcs) {
        this.npcs = npcs;
    }

    public Map<String, room> getNearRoom() {
        return nearRoom;
    }

    public Map<String, npc> getNpcs() {
        return npcs;
    }

}
/**
 * 大堂
 */
public class lobby extends room {

    public lobby() {
         descriptions = new HashMap<String,String>();
        descriptions.put("hello","这是一间普通的大堂");
        descriptions.put("bye","欢迎下次再来大堂");
    }

    public lobby(Map<String, String> descriptions) {
        super(descriptions);
    }
}

勇者(Hero)

思路

? 勇者类的设计是为了将命令和房间组合起来,在Game类里,设置一个Hero类型的全局变量,勇者是游戏主体,任何命令的执行都需要这个勇者。勇者类里存储着当前的状态,比如当前在哪个房间,现在有什么装备,还剩多少血量...。还记得在命令实现里的那个hero吗,那个就是勇者,当用户输入go south 后,就根据勇者当前所在的房间做出一些操作,因为room里有nearroom这个变量,可以很容易的得到哪个方向可以走,根据这个做一些操作即可。

分析

? 勇者类加入后,整个游戏的框架就搭建好了,换句话说,就能玩了。而且,我说了框架,意味着这个程序很容易扩展,比如加入npc,加入剧情系统,加入装备,血量...。老样子,你可以自己先想一下,看看自己可以加入些什么东西。

代码

public class Hero {
    String name;
    Map<String, goods> bag = new HashMap<String,goods>();
    int blood = 100;
    room currentRoom;

    public room getCurrentRoom() {
        return currentRoom;
    }

    public Hero(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Hero(String name, room currentRoom) {
        this.name = name;
        this.currentRoom = currentRoom;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Map<String, goods> getBag() {
        return bag;
    }

    public void setCurrentRoom(room currentRoom) {
        this.currentRoom = currentRoom;
    }

    public int getBlood() {
        return blood;
    }
}

Interactive

思路

? 这个接口的意思是可交互的,没错我百度翻译的,英语不太好。这个接口是用来干什么的呢,还记得之前我明明说descriptions是String类型的但在代码中却改为了map类型不,这个接口就是为了填坑。我们很容易发现,无论是房间还是npc,还是之后会扩展的装备,它们都会自我介绍,也都可以交互,比如npc能交流,装备能被使用,也可以拿起房间内的装备。他们都具有相同的属性,就可以抽象成接口。

分析

? 这样的设计是为了让代码更加清晰

代码

package Game;

public interface Interactive {
    public void sayHello();
    public void sayBye();
    //与勇者进行交互
    public void talkHero(Hero hero);
}

public class room implements Interactive {
    /**
     * 描述在new时就设置好,临近房间和npc得在游戏时设置
     * 实现了Interactive 接口
     */
    Map<String,room> nearRoom;
    Map<String, npc> npcs;
    Map<String,String> descriptions;

    public room(Map<String, String> descriptions) {
        this.descriptions = descriptions;
    }

    public room() {
    }

    public void setDescriptions(Map<String, String> descriptions) {
        this.descriptions = descriptions;
    }

    public void setNearRoom(Map<String, room> nearRoom) {
        this.nearRoom = nearRoom;
    }

    public void setNpcs(Map<String, npc> npcs) {
        this.npcs = npcs;
    }

    public Map<String, room> getNearRoom() {
        return nearRoom;
    }

    public Map<String, npc> getNpcs() {
        return npcs;
    }

    @Override
    public void sayHello() {
        System.out.println(descriptions.get("hello"));
    }

    @Override
    public void sayBye() {
        System.out.println(descriptions.get("bye"));
    }

    @Override
    public void talkHero(Hero hero) {

    }
}

到这里第一阶段就完成了,可以玩了。

这一版的代码

?npc

思路

? 有了之前的经验,npc的设计就很简单了,在此之前,我提一个问题,npc对于勇者是否是可见的。答案是否定的,你可能会很疑惑,怎么可能是不可见的,如果不可见勇者怎么和npc交互呢?

? 其实呀,在这里我说的不可见指的是:站在勇者的角度上,勇者不能直接接触到npc,他只能通过currentRoom这个变量来找到当前所在的房间,然后在房间里找到npc,如果你仔细观察Hero也会发现,里面根本没有与npc有关的变量。同样的,npc也需要实现Interactive接口。

?分析

? 这样的好处很明显,勇者只能在特定的房间找到npc。想一下,如果不这样设计,当我在控制台输入talk npc时,是不是还需要判断一下当前房间能不能看到那个npc,这样耦合性就变高了。

代码

package Npc;

import Game.Hero;
import Game.Interactive;

import java.util.HashMap;
import java.util.Map;

/**
 * npc 的父类
 */
public class npc implements Interactive {
    Map<String,String> descriptions = new HashMap<String,String>();

    @Override
    public void sayHello() {
        System.out.println(descriptions.get("hello"));
    }

    @Override
    public void sayBye() {

        System.out.println(descriptions.get("bye"));
    }

    @Override
    public void talkHero(Hero hero) {

    }
}

装备(goods)

思路

? 有了之前的经验设计一个goods就很简单了,在这里有个问题,一定要思考一下,goods是设计成普通父类还是接口还是抽象类。为什么我要这样问呢,我们来思考一个具体的场景,假设可以在npc flower获得bloodBottom这个道具,你需要在勇者类里面写一个getGoods的方法(就像下面那样)。这样写后,如果goods是一个普通的父类,就是和上面的npc思路一致,那么放入背包的是goods类型的对象,那个真正的bloodBottom向上转型了,而这样的话,当我调用good的方法时调用的是父类的方法。所以goods设计成普通父类是行不通的(如果你不能理解,就自己尝试一下,你就会发现问题)。现在需要思考的问题是,父类能调用子类的方法吗?我找到一篇文章写的很棒,看完你就会发现,goods设计成抽象类会更好一些。这里还要留一个思考题,goods设计成接口会怎么样。设计没有标准答案,主要看你认为怎样合适,你可以尝试一下,如果设计成接口,每次都要重复写一下sayHello等方法,很麻烦。

父类能调用子类方法吗?

代码

    public void getGoods(String s,goods good){
        bag.put(s,good);
    }

/**
 * 装备的父类
 */
public  abstract class goods implements Interactive {
    Map<String,String> descriptions = new HashMap<String,String>();

    public void sayHello() {
        System.out.println(descriptions.get("hello"));
    }

    public void sayBye() {
        System.out.println(descriptions.get("bye"));
    }



/**
 * 血瓶
 */
public class bloodBottle extends goods{
    @Override
    public void talkHero(Hero hero) {
        hero.setBlood(hero.getBlood()+100);
        System.out.println("勇者加了100点血");
    }
}
/**
 * 使用道具命令
 */
public class use extends command{
    public use(Hero hero) {
        super(hero);
    }
    public void doSomthing(String word) {
        if(hero.getBag().containsKey(word)){
            hero.getBag().get(word).talkHero(hero);
        }else {
            System.out.println("没有这个道具");
        }
    }
}

总结

? 到这里本项目就写完了,具体的代码见下面的网址。这个游戏的可扩展性很好,可以再加一点剧情,加一点战斗,加一点解密。这个项目算是我自学java做的第一个项目,距今大概有一年多了,现在开始写博客,也就想把以前做的东西写出来。这个游戏整体难度不大设计的知识点也不多,但是通过这个项目可以更深入了解面向对象,个人认为对java初学者来说帮助很大,如果感觉写的不错,希望点个赞,接下来会跟新基于JavaSE的天天酷跑,也是个适合初学者的小游戏。最后,感谢浙江大学的翁恺老师,这个项目的思路来自他的慕课。

源码下载

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-08 11:53:00  更:2021-08-08 11:53:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 13:28:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码