我正在为一款多人游戏制作服务器,并且在弄清楚如何使逻辑模块化方面存在一些问题。当玩家移动时,我想要更新对象,更新数据库记录并向所有连接的客户端发出移动。现在我是这样做的:
var socket = require('./socket');
var db = require('./db');
function Player(id, x, y) {
this.id = id;
this.x = x;
this.y = y;
}
Player.prototype.move = function(x, y) {
this.x = x;
this.y = y;
db.update({id: this.id}, {x: x, y: y});
socket.emit('player moved', {x: x, y: y});
}
这将套接字和数据库与玩家对象紧密地结合在一起,这让人感觉是错误的。然而,我不想做db。更新和套接字。在每次更新播放器对象时,在游戏循环中发出。
正确的做法是什么?
如果Player
"类"的唯一目的是处理这些类型的更新,那么我看到的唯一真正的改变是参数化玩家代码,而不是让它直接包含db和socket。但这是一个非常小的变化,因为require调用已经在某种程度上解耦了。
我可以看到三种方法:
-
让它们成为
Player
的参数:function Player(id, x, y, db, socket) { this.id = id; this.x = x; this.y = y; this.db = db; this.socket = socket; } Player.prototype.move = function(x, y) { this.x = x; this.y = y; this.db.update({id: this.id}, {x: x, y: y}); this.socket.emit('player moved', {x: x, y: y}); };
但是你最终在每个对象中都有这些引用。
-
使它们成为创建
Player
构造函数的构造函数的参数exports.buildPlayerClass = function(db, socket) { function Player(id, x, y) { this.id = id; this.x = x; this.y = y; } Player.prototype.move = function(x, y) { this.x = x; this.y = y; db.update({id: this.id}, {x: x, y: y}); socket.emit('player moved', {x: x, y: y}); }; };
调用模块中的用法是:
var socket = require('./socket'); var db = require('./db'); var Player = require('./player').buildPlayerClass(db, socket); // ... var p = new Player("some-id", 0, 0);
-
使
Player
和EventEmitter
发出move
事件:var EventEmitter = require('events').EventEmitter; function Player(id, x, y) { EventEmitter.call(this); this.id = id; this.x = x; this.y = y; } Player.prototype = Object.create(EventEmitter.prototype); Player.prototype.constructor = Player; Player.prototype.move = function(x, y) { this.x = x; this.y = y; this.emit('move', {p: this, x: x, y: y}); };
用法如下:
var socket = require('./socket'); var db = require('./db'); // ... var p = new Player("some-id", 0, 0); p.on('move', function(e) { db.update({id: e.p.id}, {x: e.x, y: e.y}); socket.emit('player moved', {x: e.x, y: e.y}); });
-
使
Player
使用公共事件发射器:function Player(id, x, y, emitter) { this.id = id; this.x = x; this.y = y; this.emitter = emitter; } Player.prototype.move = function(x, y) { this.x = x; this.y = y; this.emitter.emit('move', {p: this, x: x, y: y}); };
用法如下:
var playerEvents = new EventEmitter(); playerEvents.on('move', function(e) { db.update({id: e.p.id}, {x: e.x, y: e.y}); socket.emit('player moved', {x: e.x, y: e.y}); }); // ... var p1 = new Player("p1", 0, 0, playerEvents); var p2 = new Player("p2", 0, 0, playerEvents);
我会做两件事:
1)让你的Player类发出事件。这可以让你在将来添加更多的触发器,而不必修改你的播放器类
2)使用pos对象来保存x和y值。这使得代码更简洁。
// In Player file
var EventEmitter = require('events').EventEmitter;
function Player(id, pos) {
this.id = id;
this.pos = pos;
}
Player.events = new EventEmitter();
Player.prototype.move = function (pos) {
Player.events.emit('move', this);
};
module.exports = Player;
// In database file...
var db = require('./db');
var Player = require('./Player');
Player.events.on('move', function (player) {
db.update({id: player.id}, player.pos);
});
// In socket file...
var socket = require('./socket');
var Player = require('./Player');
Player.events.on('move', function (player) {
socket.emit('player moved', player.pos);
});
我本人对看到在这种情况下解耦的建议非常感兴趣。我个人的建议
Player.prototype.observers = [];
Player.prototype.observers.push(
function(){
if(conditionToUpdateDb){
db.update({id: this.id}, {x: this.x, y: this.y});
}
});
Player.prototype.observers.push(
function(){
if(conditionToEmit){
socket.emit('player moved', {x: this.x, y: this.y});
}
});
和
Player.prototype.move = function(x, y) {
this.x = x;
this.y = y;
for(key in this.observers){
this.observers[key]();
}
}
如果您希望不同的Players
对应不同的observers
,您可以选择
function Player(id, x, y, observers) {
this.id = id;
this.x = x;
this.y = y;
this.observers = observers;
}
和
for(key in this.observers){
this.observers[key]();
}