游戏物品管理系统
github: https://github.com/okfanger/xiaofang-adv
@email: lovefyj616@foxmail.com
本管理系统可以存储同一类物品的数量,支持出售、使用、移除、购买等物品操作,此外还引入了个人的生命值、法力值、金币、攻击力的属性,用来展示该系统优秀的数据绑定操作。
例如,苹果的效果是 “法力值+10” ,点击使用后,该人物对应的生命值属性+10,同时与该属性绑定的红色进度条(血条)也会同步更新;点击购买炸弹时,系统会首先判断用户是否具有足够的金钱购买,并进行响应…
部署该系统时,需要导入项目依赖(在下文会提及),并将src目录里的sql文件导入到数据库中,修改数据库连接的配置文件,方可正常运行!
1. 项目依赖(包)及结构
1.1 项目依赖
javafx-sdk-11.0.2、mysql-connector-java-8.8.16、mybatis、log4j-1.2.17
1.2 项目结构
![image-20211224142811672](https://s2.loli.net/2022/01/11/T9o8dvBPz7IuJ41.png)
1.3 数据库结构
![image-20211224143843672](https://s2.loli.net/2022/01/11/sUgbkpjaMFW9vC4.png)
2. 类结构(UML图)
2.1 数据bean
用来数据的存储及fx组件之间的动态绑定
2.2 主界面
2.2.1 游戏主类 (Game.java)
2.2.2 商店类 (MarketStage.java)
2.2.3 登录(Login.java)、注册类(Register.java)
2.2.4 自定义面板类(TableViewPane.java、TopPane)
2.3 工具类 (连接数据库)
3. 数据的存储及读入、查询
所有数据库操作都以静态方法的形式在MyBatis中定义
3.2 引入MyBatis Factory 单例模式
3.2.1 配置数据库地址、用户名、密码
在项目 cn/akfang/advanture/mybatis/mybatis-config.xml 中,配置数据库信息
在property标签中,分别填写name为 url, username, password的标签
如下代码,表示 数据库地址为 本机(127.0.0.1),数据库名为 xiaofang ,用户名为 root,密码为 root
...
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1/xiaofang?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
...
3.2.2 单例模式
static {
System.out.println("MyBatis factory 初始化 ...");
// 静态代码块: 初始化 SqlSessionFactory 实例
try{
String resource = "./cn/akfang/advanture/mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
factory = new SqlSessionFactoryBuilder().build(inputStream);
}
catch(Exception e)
{
e.printStackTrace();
}
}
3.3 在mapper.xml 中定义所有操作
这里篇幅有限,增删改查各选一个展示,详细请自行查看
<select id="checkUserExists" parameterType="hashmap" resultType="hashmap">
select * from `player_profile` where nick_name=#{nick_name};
</select>
<insert id="insertUser" parameterType="hashmap" useGeneratedKeys="true" keyProperty="player_id" >
INSERT INTO `player_profile` (`player_id`,`nick_name`,`password`,`registered_date`) VALUES (#{player_id},#{nick_name},#{password},#{registered_date});
</insert>
<update id="updatePlayerBoxItem" parameterType="hashmap" >
update `player_box` set `amount`=#{amount} where object_id=#{object_id}
</update>
<delete id="removeNullItemInPlayBox" parameterType="hashmap">
delete from player_box where object_id=#{object_id} and owner_id=#{owner_id}
</delete>
…
3.4 封装Sqlsession
所有session同样以静态方法的形式,存于MyBatis 类中
public static HashMap<Integer,HashMap<String,Object>> getObjectPropertyFromDatabase(){
HashMap<Integer,HashMap<String,Object>> objectProperty = new HashMap<>();
try (SqlSession session = MyBatis.factory.openSession()){
List<Map> resultList = session.selectList("getObjectClass");
resultList.forEach(item -> {
int key = (int) item.get("class_id");
HashMap<String,Object> value = new HashMap<>();
value.put("name",item.get("name"));
value.put("cost",item.get("cost"));
String[] effect = ((String)item.get("effect")).split(",");
for (String effects :effect){
String[] effectMap = effects.split(":");
value.put(effectMap[0],Integer.parseInt(effectMap[1]));
}
objectProperty.put(key,value);
});
}
return objectProperty;
}
public static void updatePlayerConditionHp(int player_id,int hp){
try (SqlSession session = MyBatis.factory.openSession()){
session.update("updatePlayerConditionHp",new HashMap<>(){{
put("player_id",player_id);
put("hp",hp);
}});
session.commit();
}
}
public static void updatePlayerConditionMoney(int player_id,int money){
try (SqlSession session = MyBatis.factory.openSession()){
session.update("updatePlayerConditionMoney",new HashMap<>(){{
put("player_id",player_id);
put("money",money);
}});
session.commit();
}
}
…
4. 布局
4.1 Game.java
4.2 TopPane.java
4.3 TableViewPane.java
4.4 Market.java
5. 部分关键操作演示
5.1 登录及注册
5.1.1 注册
点击注册按钮后,会向数据库提交一个select
try (SqlSession session = MyBatis.factory.openSession()){
List<Map> result = session.selectList(
"reg.checkUserExists",
new HashMap<>(){{
put("nick_name",tf[0].getText());
}}
);
...
}
若数据库中没有这个昵称的账号,就提交一个insert,并提交事务
if(result.size()!=1){
new Alert(Alert.AlertType.WARNING,"未找到对应的用户名").show();
} else {
String truePassword = (String) result.get(0).get("password");
if(truePassword.equals(pf.getText())){
thisStage.close();
new Game().begin(this.thisStage,result.get(0));
} else {
new Alert(Alert.AlertType.WARNING,"密码错误!").show();
}
}
5.1.2 登录
点击注册按钮后,会向数据库提交一个select
try (SqlSession session = MyBatis.factory.openSession()){
List<Map> result = session.selectList(
"reg.checkUserExists",
new HashMap<>(){{
put("nick_name",tf[0].getText());
}}
);
...
}
若数据库中没有这个昵称的账号,就弹出提示框
反之,将存有玩家数据的Map,传入Game.java,运行Game类的begin()方法
if(result.size()!=1){
new Alert(Alert.AlertType.WARNING,"未找到对应的用户名").show();
} else {
String truePassword = (String) result.get(0).get("password");
if(truePassword.equals(pf.getText())){
thisStage.close();
new Game().begin(this.thisStage,result.get(0));
} else {
new Alert(Alert.AlertType.WARNING,"密码错误!").show();
}
}
5.2 物品操作
5.2.1 物品使用
bt[0].setOnAction(event->{
if(thisItem.getAmount()==0){
return;
}
HashMap<String, Object> usage = Game.objectProperty.get(thisItem.getClassId());
for (String key : usage.keySet()) {
if (key.equals("attack")) player.setAttack(player.getAttack() + (Integer) usage.get("attack"));
if (key.equals("hp")) {
int after = player.getHp() + (Integer) usage.get("hp");
if (after < 0) {
new Alert(Alert.AlertType.WARNING, "生命值不足!").show();
return;
}
player.setHp(after);
}
if (key.equals("mp")) {
int after = player.getMp() + (Integer) usage.get("mp");
if (after < 0) {
new Alert(Alert.AlertType.WARNING, "法力值不足!").show();
return;
}
player.setMp(player.getMp() + (Integer) usage.get("mp"));
}
}
thisItem.setAmount(thisItem.getAmount() - 1);
//System.out.println(player.mpProperty().get());
});
5.2.2 物品出售
bt[1].setOnAction(event->{
if(thisItem.getAmount()==0){
return;
}
thisItem.setAmount(thisItem.getAmount()-1);
player.setMoney(player.getMoney()+thisItem.getCost());
});
5.2.4 物品移除
Button bt33 = new Button("移除");
this.setGraphic(bt33);
bt33.setOnAction(event->{
MyBatis.deleteNullItemInPlayerBox(thisItem.getId(),player.getPlayer_id());
data.remove(getIndex());
});
5.2.5 物品购买
buyButton.setOnAction(event->{
int money = player.getMoney();
if(money<thisItem.getCost()) return;
player.setMoney(player.getMoney()-thisItem.getCost());
GameObjectProperty result = checkPlayerBoxExists(thisItem.getClass_id());
if(result!=null){
result.setAmount(result.getAmount()+1);
//update
} else {
int feeback_id = MyBatis.insertItemInPlayerBox(player.getPlayer_id(),thisItem.getClass_id(),1);
//System.out.println(feeback_id);
playerbox.add(new GameObjectProperty(feeback_id,thisItem.getClass_id(),thisItem.getName(),thisItem.getCost(),1));
}
});
5.3 属性增减
5.3.1 血条增减
public void setHp(int hp) {
if(hp<0) return;
MyBatis.updatePlayerConditionHp(getPlayer_id(),hp);
this.hp.set(hp);
}
5.3.2 蓝条增减
public void setMp(int mp) {
if(mp<0) return;
MyBatis.updatePlayerConditionMp(getPlayer_id(),mp);
this.mp.set(mp);
}
5.3.3 金钱增减
public void setMoney(int money) {
if(money<0) return;
MyBatis.updatePlayerConditionMoney(getPlayer_id(),money);
this.money.set(money);
}