最近,我删除并重新编写了我的游戏代码,使其面向对象(在它仅仅是函数和大量的全局变量之前)。
在实现更多功能之前,我想做最后一步,以确保在继续之前我已经有了尽可能好的基础。
注意:这个游戏的目的是更好地学习原始的JavaScript方法和设计,所以我不想要任何插件,或者有人告诉我jQuery如何使这更容易;)
如果这个脚本太长,我非常抱歉,如果这是一个不恰当的问题,我将删除这个问题。
index.html
<!DOCTYPE html>
<html>
<head>
<title>A Vampire's Hunt</title>
<link rel="stylesheet" href="vamp.css">
</head>
<body>
<div id="msg" class="msg"></div>
<div>You have been dead for <span id="counter">0</span> hours..</div>
<div id="divCycle" class="cycle">It is currently: <span id="cycle"></span></div>
<div>Blood: <span id="blood">0</span></div>
<div class="hp" id="hpDiv">HP: <span id="hp">20</span></div>
<div class="gold" id="goldDiv">Gold: <span id="gold">0</span></div>
<script src="object-vamp.js"></script>
</body>
</html>元素对象
function _elements() {
this.counter;
this.blood;
this.gold;
this.spanHP;
this.divHP;
this.bloodElement;
this.raidElement;
this.msg;
this.goldDiv;
this.day;
this.cycle;
this.goHunting;
this.elm = function(name,props,style) {
var el = document.createElement(name);
for(var prop in props) if(props.hasOwnProperty(prop)) el[prop] = props[prop];
for(var prop in style) if(style.hasOwnProperty(prop)) el.style[prop] = style[prop];
return el;
}
this.showElement = function(id) {
document.getElementById(id).className = "";
}
this.disableElement = function(id,txt) {
document.getElementById(id).disabled = true;
document.getElementById(id).innerHTML = txt;
}
this.alterHTML = function(id,txt) {
document.getElementById(id).innerHTML = txt;
}
this.enableButton = function(id,e,txt) {
var element = document.getElementById(id);
if (engine.player.isDead() && engine.dayStatus != "night")
engine.elements.eventMsg("You are too weak to "+e+" until pure darkness allows it!");
else {
element.disabled = false;
this.alterHTML(id,txt);
}
}
this.addBorder = function(id) {
document.getElementById(id).style.border = "1px solid black";
}
this.eventMsg = function(txt) {
this.addBorder("msg");
var temp = document.getElementById("msg");
txt = "-"+txt+"<br />"+temp.innerHTML;
temp.innerHTML = txt;
}
this.bloodButton = function() {
this.goHunting = this.elm("button",{innerHTML:"Hunt for Blood", id:"bloodButton"},{});
document.body.appendChild(this.goHunting);
this.goHunting.addEventListener("click",engine.player.hunt.bind());
}
this.raidButton = function() {
this.goRaiding = this.elm("button",{innerHTML:"Raid for Gold", id:"raidButton"},{});
document.body.appendChild(this.goRaiding);
this.goRaiding.addEventListener("click",engine.player.raid.bind());
}
}播放器对象
function _player() {
this.hp = 20;
this.hpMax = 20;
this.bloodcount = 0;
this.goldCount = 0;
this.isDead = function() {
if (this.hp <= 0) return true;
else return false;
}
this.revive = function() {
if (engine.dayStatus == "night") {
engine.player.healDamage(1);
}
}
this.healDamage = function(heal) {
if ((this.hp+heal) > this.hpMax) this.hp = this.hpMax;
else this.hp += heal;
}
this.triggerDeath = function(cause,bloodLoss) {
engine.elements.eventMsg("You have died from: "+cause);
engine.elements.eventMsg("Your death has cost you "+bloodLoss+" pints of your precious blood!");
if (this.bloodcount < 20) this.bloodcount = 0;
else this.bloodcount -= bloodLoss;
engine.elements.alterHTML("blood",this.bloodcount);
}
this.dealDamage = function(dmg,type,bloodLossOnDeath) {
if (!engine.firstHPLoss) {
engine.firstHPLoss = true;
engine.elements.showElement("hpDiv");
}
if ((this.hp-dmg) <= 0) {
this.hp = 0;
this.triggerDeath(type,bloodLossOnDeath);
} else {
this.hp -= dmg;
}
}
this.hunt = function() {
var bloodCollected = 0;
engine.elements.alterHTML("bloodButton","Wait to hunt...");
engine.elements.goHunting.disabled = true;
if (engine.dayStatus == engine.statusCycle[0]) {
this.dealDamage(10,"sunlight",20);
engine.elements.eventMsg("Hunting in the daylight has hurt you! -10 HP!");
} else {
bloodCollected = 1*engine.multiplier;
engine.player.bloodcount += bloodCollected;
engine.elements.alterHTML("blood",engine.player.bloodcount);
engine.elements.eventMsg("Your hunt yielded "+bloodCollected+" pint(s) of blood!");
engine.player.healDamage(1);
engine.elements.alterHTML("hp",engine.player.hp);
}
}
this.raid = function() {
engine.elements.showElement("goldDiv");
var goldCollected;
var hpLoss = 0;
engine.elements.alterHTML("raidButton","Wait to raid...");
engine.elements.goRaiding.disabled = true;
if (engine.dayStatus == engine.statusCycle[0]) {
engine.player.dealDamage(15,"sunlight");
engine.elements.eventMsg("Raiding in the daylight has hurt you! -15 HP!");
} else {
hpLoss = Math.floor(Math.random()*(5-1+1)+1);
goldCollected = Math.floor((Math.random()*100));
engine.player.goldCount += goldCollected;
engine.elements.alterHTML("gold",engine.player.goldCount);
engine.elements.eventMsg("Your raid yielded "+goldCollected+" gold coins at the cost of "+hpLoss+"HP from the townspeople!");
engine.player.dealDamage(hpLoss,"raiding",15);
engine.elements.alterHTML("hp",engine.player.hp);
}
}
}主机对象
function _engine() {
this.count = 0;
this.cycleFlag = false;
this.firstHPLoss = false;
this.raidFlag = false;
this.multiplier = 1;
this.dayStatus = "dusk";
this.statusCycle = [
"day",
"dusk",
"night",
"dawn"
];
this.huntStatus = {
"day":0,
"dusk":3,
"night":4,
"dawn":2
};
this.dayFlavor = [
"The sun is bright outside..",
"The sun is setting..",
"The moon shines brightly..",
"The sun is rising.."
];
this.player = (function(){ return new _player(); }());
this.elements = (function(){ return new _elements(); }());
this.triggers = function(c) {
if (c == 5) this.elements.bloodButton();
if (!(c%1) && c > 5) this.elements.enableButton("bloodButton","hunt","Hunt for Blood");
if (!(c%1) && engine.player.bloodcount > 5) this.elements.enableButton("raidButton","raid","Raid for Gold");
if (engine.player.bloodcount >= 5 && !this.raidFlag) {
this.elements.raidButton();
this.raidFlag = true;
}
if (engine.player.bloodcount >= 10 && !this.cycleFlag) {
this.initDayCycle();
this.cycleFlag = true;
}
if (!(c%10) && this.cycleFlag) this.nextDayCycle();
}
this.initDayCycle = function() {
engine.elements.showElement("divCycle");
engine.elements.alterHTML("cycle",this.dayStatus);
engine.multiplier = 3;
}
this.nextDayCycle = function() {
var index = this.statusCycle.indexOf(this.dayStatus);
var cycleNext = this.statusCycle[(index+1)];
if (cycleNext) {
this.dayStatus = cycleNext;
this.multiplier = this.huntStatus[cycleNext];
}
else {
this.dayStatus = this.statusCycle[0];
this.multiplier = this.huntStatus[this.dayStatus];
}
engine.elements.alterHTML("cycle",this.dayStatus);
var newIndex = this.statusCycle.indexOf(this.dayStatus);
engine.elements.eventMsg(this.dayFlavor[newIndex]);
}
}最后给出了初始化和间隔循环。
var engine = (function(){ return new _engine(); }());
setInterval(function() {
engine.count++;
engine.elements.alterHTML("counter",engine.count);
engine.triggers(engine.count);
if (engine.player.isDead()) {
engine.elements.disableElement("bloodButton","You are dead..");
engine.elements.disableElement("raidButton","You are dead..");
engine.player.revive();
}
}, 1000);发布于 2013-11-24 11:23:29
我注意到你这样做:
function _player() {
...
this.isDead = function() {
if (this.hp <= 0) return true;
else return false;
}这个问题的一个问题是,对于_player的每个实例,您都要为构造函数的每个实例创建方法。这会消耗掉记忆。
您可以做的是使用原型继承。这个继承模型通过在实例之间“共享”构造函数的原型来工作。这样,方法只声明一次,但在实例之间共享。
function _player(){...}
_player.prototype = {
isDead : function(){...},
revive : function(){...},
...
};
var p1 = new _player();
var p2 = new _player();
// Both _player instances use the same revive method
// but operate on different _player objects
p1.revive();
p2.revive();您的代码是紧密耦合的。这意味着如果我破坏了代码的一部分,其他部分也会中断。
例如,以下代码:
this.eventMsg = function(txt) {
this.addBorder("msg");
var temp = document.getElementById("msg");
txt = "-"+txt+"<br />"+temp.innerHTML;
temp.innerHTML = txt;
}这假设存在一个元素#msg。如果我取出了HTML的那一部分或者偶然地重命名了它呢?temp将是undefined,访问temp.innerHTML会抛出一个错误。
以及以下代码:
this.player = (function(){ return new _player(); }());
this.elements = (function(){ return new _elements(); }());您的代码假定存在_player和_elements。如果我将它们取出来或重命名,您也将查看这段代码并将其重命名。不太实际。而且,这假设_player和_elements只是一个整体。如果你需要更多的球员呢?还是元素?
的解耦
注册允许通过将对象注册到系统中来实现解耦,而不是让系统硬编码所需的对象。这样,如果没有注册对象(因为它们被删除或其他原因),那么代码就不会中断。下面是一个简单的玩家示例:
//Engine.js
function Engine(){
// We can have more than one player
this.players = [];
}
Engine.prototype = {
// A simple register
registerPlayer : function(player){
this.players.push(player)
},
doSomethingWithPlayers : function(){
for(var i = 0; i < this.players.length; i++){
// Do something to registered players
// No players registered means code won't run
}
}
}
//Registration.js
var player1 = new Player();
var player2 = new Player();
var engine = new Engine();
engine.register(player1);
engine.register(player2);
engine.doSomethingWithPlayers(); //Some mass-heal effect?因此,在上面的示例中,我们将player对象“注册”到引擎中,而不是硬编码到引擎中。如你所见,好处是显而易见的:
Player.js不会破坏引擎代码。我注意到您使用时钟机制来启动与时间相关的事件。但是,您是硬编码事件以及对象:
this.triggers = function(c) {
if (c == 5) this.elements.bloodButton();
if (!(c%1) && c > 5) this.elements.enableButton("bloodButton","hunt","Hunt for Blood");
if (!(c%1) && engine.player.bloodcount > 5) this.elements.enableButton("raidButton","raid","Raid for Gold");
if (engine.player.bloodcount >= 5 && !this.raidFlag) {
this.elements.raidButton();
this.raidFlag = true;
}
if (engine.player.bloodcount >= 10 && !this.cycleFlag) {
this.initDayCycle();
this.cycleFlag = true;
}
if (!(c%10) && this.cycleFlag) this.nextDayCycle();
}你能做的就是某种"pub-sub“模式或事件侦听器。您可以监听来自对象的事件并作出相应的反应。基本上,该机制将函数注册到数组中,并在运行它们的时候运行它们。实现起来非常简单,我自己建了一个小图书馆。
//Engine.js
function Engine(){
this.count = 0;
this.timer = null;
}
//Refer to my library for a simple Event Emitter implementation
Engine.prototype.run = function(){
//Save the context
var instance = this;
this.timer = setTimeout(function(){
// Run registered events
// If they return true, then run the corresponding handlers
},1000);
}
//Main script
var engine = new Engine();
var player = new Player();
// Register an event that determines when it happens
engine.registerEvent('nextDayCycle',function(cycle){
//nextDayCycle event is triggered on this tick when this returns true
return (!(cycle%10) && this.cycleFlag);
});
// Register a handler that runs when an event happens
engine.on('nextDayCycle',function(){
// Runs on every nextDayCycle trigger
players.hunt();
});
engine.run();正如您所看到的,您可以将事件与保存时间管理机制的引擎分离。
发布于 2013-11-24 11:26:42
除了约瑟夫以前说过的话外,以下是一些小小的建议:
总体而言:
元素对象:
_element而不是_elements。elm的名称描述性不够。也许,createElement会做的更公正的方法。ShowElement中显示它。你需要在这里质疑你的假设。是否存在要删除的特定类或显示元素的所有类?DisableElement也要改变元素的HTML?你似乎已经有了另一种方法。enableButton、bloodButton和raidButton突然提到了全局变量引擎。我以为我们要去掉全局变量。您可能希望使用bind()将这些函数绑定到全局引擎对象。https://codereview.stackexchange.com/questions/35989
复制相似问题