OurJS


OurJS-我们的JS, 我们的技术-IT文摘; 专注JS相关领域;
我们热爱编程, 我们热爱技术;我们是高大上, 有品味的码农;

欢迎您订阅我们的技术周刊


我们会向您分享我们精心收集整理的,最新的行业资讯,技术动态,外文翻译,热点文章;
我们使用第三方邮件列表向您推送,我们不保存您的任何个人资料,注重您的隐私,您可以随时退订,

欢迎分享您的观点,经验,技巧,心得

让我们一起找寻程序员的快乐,探索技术, 发现IT人生的乐趣;


本网站使用缓存技术每次加载仅需很小流量, 可在手机中流畅浏览;
如果您发现任何BUG,请即时告知我们: ourjs(at)ourjs.com

Bearcat pomelo game 实战 -- treasures


分享到


分类 JS开源   关键字 Node.JS   发布 fantasyni  1400655715496
注意 转载须保留原文链接,译文链接,作者译者等信息。  

概述

这是一篇通过一个简单的 treasure 捡宝的例子讲述如何使用 Bearcat 来快速, 高效的进行 pomelo game 开发

起步

添加 bearcat

npm install bearcat --save

添加context.json, 并指定 scan 扫描路径, 来自动扫描 POJOs 

context.json

{
    "name": "bearcat-treasures",
    "scan": "app",
    "beans": []
}

修改app.js, 添加 bearcat 启动代码 

app.js

var contextPath = require.resolve('./context.json');
bearcat.createApp([contextPath]);

bearcat.start(function() {
  Configure(); // pomelo configure in app.js
  app.set('bearcat', bearcat);
  // start app
  app.start();
});

就是这么简单, bearcat 开发环境就已经搭建完毕, 之后就可以利用 bearcat 所提供的 IoC, AOP, 一致性配置等特性来编写简单, 可维护的 pomelo 应用

途中

handler, remote 交由 bearcat 管理

handler, remote 都以 POJO 的形式编写 

由于之前handler, remote在pomelo里面是通过 pomelo-loader 来管理的, 因此需要做一下适配转化

module.exports = function(app) {
    return bearcat.getBean({
        id: "gateHandler",
        func: GateHandler,
        args: [{
            name: "app",
            value: app
        }],
        props: [{
            name: "dispatcher",
            ref: "dispatcher"
        }]
    });
};

通过适配, gateHandler 就交给了 bearcat 来进行管理, 之后 gateHandler 需要什么依赖, 仅仅在 getBean 的 metadata 配置中描述好依赖关系就行了 

上面的gateHandler例子中, 就向 bearcat 容器描述了, gateHandler 需要在构造函数中传入一个 app 对象, 在对象属性中需要一个 dispatcher 依赖

domain 对象编写

domain 代表着数据和模型, 包括玩家player, 宝物treasure, 移动move等等 

domain 里的数据要被序列化, 需要定义序列化方法, 比如toJSON

entity.js

var EventEmitter = require('events').EventEmitter;
var util = require('util');

var id = 1;

function Entity(opts) {
    EventEmitter.call(this);
    this.opts = opts || {};
    this.entityId = id++;
    this.kindId = opts.kindId;
    this.kindName = opts.kindName;
    this.areaId = opts.areaId || 1;
    this.x = 0;
    this.y = 0;
}

util.inherits(Entity, EventEmitter);

Entity.prototype._init = function() {
    var opts = this.opts;
    if (opts.x === undefined || opts.y === undefined) {
        this.randPos();
    } else {
        this.x = opts.x;
        this.y = opts.y;
    }
}

Entity.prototype._toJSON = function() {
    return {
        x: this.x,
        y: this.y,
        entityId: this.entityId,
        kindId: this.kindId,
        kindName: this.kindName,
        areaId: this.areaId
    }
}

// random position
Entity.prototype.randPos = function() {
};

module.exports = {
    id: "entity",
    func: Entity,
    abstract: true,
    props: [{
        name: "dataApiUtil",
        ref: "dataApiUtil"
    }, {
        name: "utils",
        ref: "utils"
    }]
}

entity 是一个抽象的bean, 意味着它只是作为子bean的模版, 并不会被实例化, 它通过对象属性依赖注入了 dataApiUtil 和 util

player.js

var logger = require('pomelo-logger').getLogger('bearcat-treasures', 'Player');
var bearcat = require('bearcat');
var util = require('util');

function Player(opts) {
  this.opts = opts;
  this.id = opts.id;
  this.type = null;
  this.name = opts.name;
  this.walkSpeed = 240;
  this.score = opts.score || 0;
  this.target = null;
}

Player.prototype.init = function() {
  this.type = this.consts.EntityType.PLAYER;
  var Entity = bearcat.getFunction('entity');
  Entity.call(this, this.opts);
  this._init();
}

Player.prototype.addScore = function(score) {
  this.score += score;
};

Player.prototype.toJSON = function() {
  var r = this._toJSON();

  r['id'] = this.id;
  r['type'] = this.type;
  r['name'] = this.name;
  r['walkSpeed'] = this.walkSpeed;
  r['score'] = this.score;

  return r;
};

module.exports = {
  id: "player",
  func: Player,
  scope: "prototype",
  parent: "entity",
  init: "init",
  args: [{
    name: "opts",
    type: "Object"
  }],
  props: [{
    name: "consts",
    ref: "consts"
  }]
}

player 是 entity 的一个子类, 它通过在metadata配置中的 parent 继承了 entity prototype 里的方法 

player 的scope是 prototype 的, 并且需要定义一个 init 方法, 来调用 entity 的构造函数以及 entity 的 init 方法 _init

var Entity = bearcat.getFunction('entity');
 Entity.call(this, this.opts);
 this._init();

这里通过 bearcat.getFunction 来拿到 entity 的构造函数来进行调用

使用 domain

在没有bearcat的情况下, 使用domain需要自己先require进来, 然后再 new domain(), 现在你可以直接通过 getBean 来得到相应 domain 的实例

playerHandler enterScene

PlayerHandler.prototype.enterScene = function(msg, session, next) {
  var role = this.dataApiUtil.role().random();
  var player = bearcat.getBean('player', {
    id: msg.playerId,
    name: msg.name,
    kindId: role.id
  });

  player.serverId = session.frontendId;
  if (!this.areaService.addEntity(player)) {
    logger.error("Add player to area faild! areaId : " + player.areaId);
    next(new Error('fail to add user into area'), {
      route: msg.route,
      code: this.consts.MESSAGE.ERR
    });
    return;
  }

  var r = {
    code: this.consts.MESSAGE.RES,
    data: {
      area: this.areaService.getAreaInfo(),
      playerId: player.id
    }
  };

  next(null, r);
};
var player = bearcat.getBean('player', {
    id: msg.playerId,
    name: msg.name,
    kindId: role.id
  });

player 通过 bearcat.getBean 拿到

domain 事件的处理

移动和捡宝是通过 event 事件来处理的, 在一个 tick 时间内, 当移动到宝物的捡宝范围之内, 就会出发 pickItem 事件

Move.prototype.update = function() {
  var time = Date.now() - this.time;
  var speed = this.entity.walkSpeed;
  var moveLength = speed * time / 1000;
  var dis = getDis(this.entity.getPos(), this.endPos);
  if (dis <= moveLength / 2) {
    this.finished = true;
    this.entity.setPos(this.endPos.x, this.endPos.y);
    return;
  } else if (dis < 55 && this.entity.target) {
    this.entity.emit('pickItem', {
      entityId: this.entity.entityId,
      target: this.entity.target
    });
  }
  var curPos = getPos(this.entity.getPos(), this.endPos, moveLength, dis);
  this.entity.setPos(curPos.x, curPos.y);

  this.time = Date.now();
};

触发时间后, 就向channel广播捡宝这个事件

player.on('pickItem', function(args) {
    var player = self.getEntity(args.entityId);
    var treasure = self.getEntity(args.target);
    player.target = null;
    if (treasure) {
      player.addScore(treasure.score);
      self.removeEntity(args.target);
      self.getChannel().pushMessage({
        route: 'onPickItem',
        entityId: args.entityId,
        target: args.target,
        score: treasure.score
      });
    }
  });

areaService 编写

areaService 里面维护着当前area里面的玩家, 排名, 宝物等数据 

它在tick时间内, 向channel广播更新着area里面的最新数据

AreaService.prototype.tick = function() {
  //run all the action
  this.actionManagerService.update();
  this.entityUpdate();
  this.rankUpdate();
}

entityUpdate 更新着area里面的entity情况

AreaService.prototype.entityUpdate = function() {
  if (this.reduced.length > 0) {
    this.getChannel().pushMessage({
      route: 'removeEntities',
      entities: this.reduced
    });
    this.reduced = [];
  }
  if (this.added.length > 0) {
    var added = this.added;
    var r = [];
    for (var i = 0; i < added.length; i++) {
      r.push(added[i].toJSON());
    }

    this.getChannel().pushMessage({
      route: 'addEntities',
      entities: r
    });
    this.added = [];
  }
};

总结

在bearcat的统一管理协调下, 去除了烦人的require直接依赖关系, 可以放心大胆的进行编码甚至重构, bearcat 里面的任一组件都被有序的管理维护着, 使用时不再是一个个单一的个体, 而是一个集体

项目代码在 bearcat-treasures

原文地址: 点此
社区评论 ( Beta版 )
  • #0 kris 1400664194940

    不错,看下来,感觉大概意思是使用Java的思想来写Node。

OnceDoc 您自己的企业内容管理系统——文档、流程、知识库、报表、网盘All In One

访问404页面,寻找丢失儿童
 热门文章 - 分享最多
  1. 编程是一个没有前途的工作
  2. 你已经毁了JavaScript
  3. 是什么让Node.js比Java更快?
  4. 现在,你为什么应该学Node.js
  5. 将JavaScript 作为第一编程语言
  6. 抛弃jQuery,深入原生的JavaScript
  7. 干嘛不在企业中使用Node.js呢?
  8. 用JavaScript的5个原因
  9. 趣图:在NodeJS程序中有未处理的异常
  10. 使用集群(recluster)扩展多线程Node.JS
  11. 用 OnceAir 搭建个人Git/Svn/照片备份服务器,每年电费7块钱

 相关阅读 - JS开源
  1. Ws.js:基于 Node.js的WS-*实现
  2. 开源项目:CSS 3D转换和动画学习示例教程
  3. 我为什么选择 D3.js
  4. Mozilla实验室发布的一款实时协作工具库TogetherJS
  5. 厌倦 Bootstrap 了没?来试试新玩具
  6. 不用HTML/CSS,JS就够了
  7. Tessel 开源硬件正式发布

 关键字 - Node.JS
  1. 提高NodeJS网站的安全性:Web服务器防黑客攻击技巧
  2. Node.JS用Socket实现FTP Server服务器和Client客户端
  3. [译]Node.js 框架比较: Express vs. Koa vs. Hapi
  4. Express入门教程:一个简单的博客
  5. 是什么让Node.js比Java更快?为什么NodeJS这么快?
  6. 使用Node.JS批量查找替换目录下文本文件中图片地址内容
  7. 判断Node.JS TCP Socket当前连接状态
  8. Node.JS进程间通讯的几种方法:Redis Publish/Subscribe 和 UDP Socket
  9. Node.JS命令行或批处理中更改Linux用户密码
  10. Node.JS用Path将相对路径转为绝对路径

 欢迎订阅 - 技术周刊

我们热爱编程, 我们热爱技术; 我们是高端, 大气, 上档次, 有品味, 时刻需要和国际接轨的码农; 欢迎您订阅我们的技术周刊; 您只需要在右上角输入您的邮箱即可; 我们注重您的隐私,您可以随时退订.
加入我们吧! 让我们一起找寻码农的快乐,探索技术, 发现IT人生的乐趣;


 关注我们

我们的微信公众号: ourjs-com
打开微信扫一扫即可关注我们:
IT文摘-程序员(码农)技术周刊

ourjs官方微信号