javascript编写奇迹mu原版(含服务端)(4)来到勇者大陆

上次做了准备工作。包括创建,登录等等,今天就来到出生点勇者大陆。

websocket会让用户加入到这个频道,并被动接受一些公共数据和即时数据:比方怪移动坐标每100毫秒更新一次。其他角色的移动坐标即时更新。这些数据包括:

1、npc,怪,玩家,掉落地上的道具。所有能移动的物品的位置,动作,方向 信息。由客户端进行渲染。 所有不能移动的物品确定是增加还是消失。

2、提交信息包括,动作,坐标,(服务端判断是否攻击有效。捡东西是否成功) 捡东西需要存数据库。要分清楚哪些存数据库哪些不存数据库,存redis.

3、背包物品的拖拽。商店购买。

4、 流程分析1。    

  • 客户端:玩家操作的界面,可修改人物坐标,血量信息。
  • 服务端:接受玩家传来的信息。并进行处理。即修改相关数据。
  • 服务端-GM:初始化并持续通过websocket传信息给所有当前(服务器-线)玩家。

5、流程分析2

  • 服务端启动,可接受注册登录。
  • 服务端启动,可初始化各个区域的NPC,怪的出生地,即刷怪。并计算移动者的位置方向动作。
  • 客户端登录后,持续接受服务端传递的信息。信息包需要检查完整性。

这里的hello是上次定义的频道。我们需要多几个频道。并且测试这个流程。

 

服务端会有下面的任务:

服务器1-1初始化(目前只初始化勇者大陆地图,按区域刷怪,npc完成。并下发怪物和npc坐标。)

npc驱动加载。(每隔100毫秒移动怪物。否则怪物不动。并检测附近一定范围是否有玩家,并主动靠近,到达攻击距离的时候就攻击。)

用户登录,发消息其他信息。

好了,分析到这里有点乱。先看下我们本节的一个目标:

  • 加载地图数据并保持更新。即刷怪。
  • 实现登录,加入勇者大陆。接受怪和其他玩家的信息。
  • 尝试攻击怪。杀怪。并更新怪。
  • 购买npc的商品。下线。

先看下代码。解析,然后我们再看下效果。效果视频请移步比占-记得点赞下,录制不易。:https://www.bilibili.com/video/BV1UU4y1a7zb/

第一步,加载地图。我将地图信息放到配置文件里。当服务器重启的时候,会读取配置文件,后期可以热加载,即不重启原来的服务器,开启新的服务器。

 

#serverList.properties
servername=server1-1,server1-2
mapname=yzdl,bfg
pkline=server1-2
pkmap=yzdl

这里需要做检测如果redis里没有服务器信息。就读取配置文件。有了这个信息后,我需要刷怪了。怪的信息被存储到数据库中,毕竟这个不常修改。

程序集成了springboot-mybatis-plus,这里贴下myBatis-plus的导包。第一个是可以不要的。所以注释掉了。最后一个是模版。可以不要。但自动生成文件的时候需要。

		<!--		<dependency>-->
		<!--			<groupId>com.baomidou</groupId>-->
		<!--			<artifactId>mybatis-plus-generator</artifactId>-->
		<!--			<version>3.3.1.tmp</version>-->
		<!--		</dependency>-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.4</version>
		</dependency>

		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatisplus-spring-boot-starter</artifactId>
			<version>1.0.5</version>
		</dependency>

		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus</artifactId>
			<version>2.3</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
			<version>2.3.31</version>
		</dependency>

这里配置好数据库

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;

-- 导出 muonline 的数据库结构
CREATE DATABASE IF NOT EXISTS `muonline` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `muonline`;


-- 导出  表 muonline.yf_monster 结构
CREATE TABLE IF NOT EXISTS `yf_monster` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '角色名称',
  `degree` int DEFAULT NULL COMMENT '等级',
  `blood` int unsigned DEFAULT '0' COMMENT '血量',
  `attackmin` int unsigned DEFAULT '0' COMMENT '最小攻击力',
  `attackmax` int unsigned DEFAULT '0' COMMENT '最大攻击力',
  `defance` int unsigned DEFAULT '0' COMMENT '防御力',
  `defancepercent` int unsigned DEFAULT '0' COMMENT '防御成功率',
  `descrtion` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '0' COMMENT '介绍',
  `mapname` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '0' COMMENT '初始地图',
  `posxy` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '0' COMMENT '初始位置',
  `create_time` int unsigned DEFAULT '0' COMMENT '创建时间',
  `update_time` int unsigned DEFAULT '0' COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='怪';

-- 正在导出表  muonline.yf_monster 的数据:~8 rows (大约)
DELETE FROM `yf_monster`;
/*!40000 ALTER TABLE `yf_monster` DISABLE KEYS */;
INSERT INTO `yf_monster` (`id`, `name`, `degree`, `blood`, `attackmin`, `attackmax`, `defance`, `defancepercent`, `descrtion`, `mapname`, `posxy`, `create_time`, `update_time`) VALUES
	(1, '蜘蛛', 2, 30, 4, 7, 1, 1, '随着魔王的复活,吸取邪恶的气而突变成庞大的蜘蛛,是勇者大陆最弱的怪', 'yzdl', '189.55,176.86,185.101,168.110,178.122', 0, 0),
	(2, '幼龙', 4, 60, 10, 13, 3, 3, '随着魔王复活后,从深山倾巢来到奇迹大陆的怪,属龙种族的一种。', 'yzdl', '139.188,149.121,166.111,178.109,110.129', 0, 0),
	(3, '牛怪', 6, 100, 16, 20, 6, 6, '该怪是用死亡战士的怨灵和死亡的牛隻溷合而产生的物种,半人半兽的外型,手持大镰刀,个性暴躁凶恶,是最具代表性群居类的怪物。', 'yzdl', '48.120,44.127', 0, 0),
	(4, '猎犬怪', 9, 140, 22, 27, 9, 9, '邪恶又狂暴,巨大的头有着永远慾求不满的个性。', 'yzdl', '24.99', 0, 0),
	(5, '蛮牛怪', 12, 190, 31, 36, 12, 12, '牛怪的领袖。由死亡战士的愤怒灵魂溷合而产生的巨大物种,虽具有强大的破坏力,但动作缓慢。', 'yzdl', '38.42', 0, 0),
	(6, '黑巫师', 14, 255, 41, 46, 14, 14, '信奉恶魔的黑巫师,是勇者大陆地下城入口的守卫魔法师,因为拥有陨石魔法的远距离施展能力,对魔法师和弓箭手们具有相当大的威胁。', 'yzdl', '152.38', 0, 0),
	(7, '巨人', 17, 400, 57, 62, 18, 18, '拥有巨大的身躯和双手持强大的巨斧,具有相当强大的杀伤力。', 'yzdl', '164.74', 0, 0),
	(8, '骷髅兵', 19, 525, 68, 74, 22, 22, '经常守备在通往地下城的入口处,是目前勇者大陆最强大的怪物。', 'yzdl', '166.40', 0, 0);
/*!40000 ALTER TABLE `yf_monster` ENABLE KEYS */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

配置好后,这里提醒下,由于是第一个表,需要先在control中测试下是否可以正常访问数据库,还是贴下aplication.prop的配置表吧。这个文件是springboot默认自带的.别起错名字了。

spring.datasource.username=root
spring.datasource.password=xxxx
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.0.248:3306/muonline?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai 
spring.datasource.hikari.minimum-idle=3
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.max-lifetime=30000
spring.datasource.hikari.connection-test-query=SELECT 1



spring.redis.database=1 
spring.redis.host=192.168.0.248
spring.redis.port=6379
spring.redis.password=
spring.redis.pool.max-active=200
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=10
spring.redis.pool.min-idle=0
spring.redis.timeout=1000

##日志级别
logging.level.com.dalaoyang.dao.UserMapper=debug
##mybatis-plus mapper xml 文件地址
mybatis-plus.mapper-locations=classpath*:mapper/*Mapper.xml
##mybatis-plus type-aliases 文件地址
mybatis-plus.type-aliases-package=com.pagemu.top.web.entity 


mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value= 0 
mybatis-plus.configuration.log-impl= org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.configuration.call-setters-on-nulls= true
#mybatis-plus.mapper-locations= classpath:mapper/*.xml

然后在心跳包里加入下面的代码。主要是检测如果没有serverlist。就获取。然后,检测没有怪就刷怪。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.pagemu.top.web.entity.YfMonster;
import com.pagemu.top.web.mapper.YfMonsterMapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

  @Autowired
  RedisConfig rc;

  @Resource
  YfMonsterMapper yfMonsterMapper;

。。。。
。。。。



//           心跳包。目的是往当前界面发送同步数据。如无数据需初始化。
          Object  serverList = rc.getString("serverlist"); //服务器1-1线的id=0的地图(勇者大陆)。 这个数据会存在redis里,

          //需要持久化保存的数据
          //        设置配置文件
          if (serverList==null    ) {
            //初始化
            synchronized (Thread.currentThread().getName().toString()) {
              serverList = rc.getString("serverlist");
              if (serverList == null) {
                System.out.println("读取数据------------------------");
                HeartBreak hb = new HeartBreak(yfMonsterMapper);
                rc.setString("serverlist", hb.startinit());
              }
            }
            System.out.println("地图加载中。。。------------------------");
          }else{
            //必定会有serverList
            List<MapInfo>  mapInfos = JSONObject.parseArray(serverList.toString(), MapInfo.class);//把字符串转换成集合
            for (MapInfo o: mapInfos){
//              根据serverList获取redis数据。没有就建立并刷怪。
              Object  currentMapInfo  = rc.getString(o.getServerName() + "-" + o.getMapName() ); //服务器1-1-勇者大陆)。 这个数据会存在redis里,
//              JSONObject.toJSONString(student)

              //需要持久化保存的数据
              //        设置配置文件
              if (currentMapInfo==null    ) {
                //初始化
                synchronized (Thread.currentThread().getName().toString()) {
                  currentMapInfo = rc.getString(o.getServerName() + "-" + o.getMapName() );
                  if (currentMapInfo == null) {
                    System.out.println("刷怪------------------------" + o.getServerName() + "-" + o.getMapName() + "-----------------" );
 
//                    这里基本上只需要将MonsterList清空下,然后按照posxy刷怪。
                    List<YfMonster> monsterList = new ArrayList<YfMonster>();
                    for (YfMonster monster: o.getMonsterList()){
                        //这里的数据是数据库里取出的。需要修改下插入redis里变成活物。
                      System.out.println(monster.getPosxy().toString());
                      for (String poss: monster.getPosxy().toString().split(",")){
//                        YfMonster yf1 = monster.;
                        System.out.println(poss);
                      }

                    }


                    rc.setString(o.getServerName() + "-" + o.getMapName() , JSONObject.toJSONString(o));
                  }
                }
              }else{
                System.out.println("redis 不空");
                System.out.println(currentMapInfo.toString());
//                currentMapInfo必定不为空
                if( o.getOnline()>0 ){
//                如果当前地图在线人数》0,有怪就移动怪并检测敌人攻击。怪不够就刷怪
                  System.out.println(   o.getMapName() );
                  System.out.println(   o.getMonsterList() );
                  List<YfMonster> monsterList = o.getMonsterList();
                  for (YfMonster m: monsterList){
                    System.out.println(m.getName());
                  }
                }

              } 
            } 
          } 
            //需要持久化保存的数据

细心的你看完代码会发现,刷怪部分还未完成。是的。刷怪部分的 代码需要修改。这里也没有判定边界。就是一种怪应该有一定的边界,随机数大于多少就要折返。

怪名字下面数字的含义是 血 横坐标,纵坐标。

到这里,应该可以看到刷怪的效果,视频请移步B站-记得点赞下,录制不易。:https://www.bilibili.com/video/BV1UU4y1a7zb/

是的,东边我安排了一个玩家刷怪点,嗯,怪物移动有点问题。很乱。这里需要增加移动逻辑。后面再想吧。

接下来,加入玩家的信息。玩家会出生在地图中间安全区内。然后可以走动。其他玩家也会看见这个走动的过程。当你走到怪旁边的时候,怪会朝你走来,到攻击范围后会开始按照攻击速度读秒,读秒结束开始发起一次攻击。

攻击如果判定有效,就开始扣血。血没了,就掉东西。这样完成一次杀怪过程。

手机版效果。其实是浏览器,全屏后的。没有声音,原版音效卡卡的声音,谁有,能用下不。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

东宇科技

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值