我怎样才能达到“熔岩”留下痕迹的效果

时间:2019-03-12 13:58:55

标签: javascript html css game-physics

在JavaScript游戏“开发”一段时间后,我想到了一个好主意,所以听起来似乎很不错。

我当时正在考虑创建一个代表熔岩的实体。该熔岩将使用以下方式沿特定方向移动:

function Lava(pos, ch) {
  this.pos = pos;
  this.size = new Vector(1, 1);
  if(ch == '-') {
    this.speed = new Vector(3, 0)
  }
}

var acrotchar = {"-": Lava};在哪里。

可以在here或以下看到整个代码:

var LEVELS = [
  ["                                  x  x",
   "                                  xx x",
   "                      xxx         x  x",
   "                     xx!xx        x ox",
   "                     x!!!x        x xx",
   "                     xx!xx        x  x",
   "  x                   xvx         x  x",
   "  x                               xx x",
   "  x                               x  x",
   "  xx                              x  x",
   "  x                               x xx",
   "  x                                  x",
   "  x @                xxxxx     o     x",
   "  xxxxxx     xxxxxxxxx   xxxxxxxxxxxxx",
   "       x     x                        ",
   "       x!!!!!x                        ",
   "       x!!!!!x                        ",
   "       xxxxxxx                        ",
   "--------------------------------------"]
   ];

//set variables (HP and EXP)
var life = 3;
var expo = 0;
document.getElementById("life").innerHTML = ("Lives left: " + life);
document.getElementById("expo").innerHTML = ("Points: " + expo);

//set the playzone
function Vector(x, y) {
	this.x = x; this.y = y;
}

Vector.prototype.plus = function(other) {
	return new Vector(this.x + other.x, this.y + other.y);
};

Vector.prototype.times = function(scale) {
	return new Vector(this.x * scale, this.y * scale);
};

// Note: uppercase words are used that means constructor are values
var actorchars =  {
	"@": Player,
	"o": Coin,
	"=": Lava,
	"|": Lava,
	"v": Lava,
	"#": Lava,
	"-": Flood
};

function Player(pos) {
	this.pos = pos.plus(new Vector(0, -.5));
	this.size = new Vector(.5, 1);
	this.speed = new Vector(0, 0);
}
Player.prototype.type = "player";

function Lava(pos, ch) {
	this.pos = pos;
	this.size = new Vector(1, 1);
	if (ch === "=")
		this.speed = new Vector(2, 0);
	else if (ch === '|')
		this.speed = new Vector(0, 2);
	else if (ch === 'v'){
		this.speed = new Vector(0, 5);	   
		this.repeatPos = pos;
	} else if (ch === '#')
  	this.speed = new Vector(0, 10);
}
Lava.prototype.type = "lava"

function Flood(pos, ch) {
  this.pos = pos;
  this.size = new Vector(1, 1);
  if  (ch === '-') {
    this.speed = new Vector(0, 1);
    this.repeatPos = pos;  //will be removed in the future 
  }
}
Flood.prototype.type = "flood"

//Lava.prototype.type = "Lava";

// function Wall(pos, ch) {
	// this.pos = pos;
	// this.size = new Vector(1, 1);
	// if (ch === "z")
		// this.speed = new Vector(0, 1);
// }
// Wall.prototype.type = "wall"

function Coin(pos) {
	this.basePos = this.pos = pos;
	this.size = new Vector(.6, .6);
	// take a look back
	this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = "coin";

Level.prototype.isFinished = function() {
  return this.status !== null && this.finishDelay < 0;
};

function Level(plan) {
	this.width = plan[0].length;
	this.height = plan.length;
	this.grid = [];
	this.actors = [];
	
	for (var y = 0; y < this.height; y++) {
		var line = plan[y],  gridLine = [];
		for (var x = 0; x < this.width; x++) {
			var ch = line[x], fieldType = null;
			var Actor = actorchars[ch];
			if (Actor)
				this.actors.push(new Actor(new Vector(x, y), ch));
			else if (ch === "x")
				fieldType = "wall";
			else if (ch === "z")
				fieldType = "wall";
			else if (ch === "!")
				fieldType = "lava";
			else if (ch === "|")
				fieldType = "lava";
			else if (ch === "=")
				fieldType = "lava";
			else if (ch === "#")
				fieldType = "lava";
      else if (ch === "-")
        fieldType = "flood";
			else if (ch === "v"){
				fieldType = "lava";
				console.log(fieldType);
			}
			gridLine.push(fieldType);
		}
		this.grid.push(gridLine);
	}
	this.player = this.actors.filter(function(actor) {
		return actor.type === "player";
	})[0];	
	this.status = this.finishDelay = null;
}

function element(name, className) {
	var elem = document.createElement(name);
	if(className) elem.className = className;
	return elem;
}

function DOMDisplay(parent, level) {
	this.wrap = parent.appendChild(element("div", "game"));
	this.level = level;
	
	this.wrap.appendChild(this.drawBackground());
	this.actorLayer = null;
	this.drawFrame();
}


var scale = 15;


DOMDisplay.prototype.drawBackground = function() {
	var table = element("table", "background");
	table.style.width = this.level.width * scale + "px";
	table.style.height = this.level.height * scale + "px";
	this.level.grid.forEach(function(row) {
  var rowElement = table.appendChild(element("tr"));
		rowElement.style.height = scale + "px";
		row.forEach(function(type) {
			rowElement.appendChild(element("td", type));
		});
	});
	return table;
};

DOMDisplay.prototype.drawActors = function() {
	var wrap = element("div");
	this.level.actors.forEach(function(actor) {
		var rect = wrap.appendChild(element("div", "actor " + actor.type));
		rect.style.width = actor.size.x * scale + "px";
		rect.style.height = actor.size.y * scale + "px";
		rect.style.left = actor.pos.x * scale + "px";
		rect.style.top = actor.pos.y * scale + "px";
	});
	return wrap;
};

DOMDisplay.prototype.drawFrame = function() {
	if (this.actorLayer)
		this.wrap.removeChild(this.actorLayer);
	this.actorLayer = this.wrap.appendChild(this.drawActors());
	this.wrap.className = "game " + (this.level.status || "");
	this.scrollPlayerIntoView();
};


// clear it later
DOMDisplay.prototype.scrollPlayerIntoView = function() {
  var width = this.wrap.clientWidth;
  var height = this.wrap.clientHeight;
  var margin = width / 3;

  // The viewport
  var left = this.wrap.scrollLeft, right = left + width;
  var top = this.wrap.scrollTop, bottom = top + height;

  var player = this.level.player;
  var center = player.pos.plus(player.size.times(0.5))
                 .times(scale);

  if (center.x < left + margin)
    this.wrap.scrollLeft = center.x - margin;
  else if (center.x > right - margin)
    this.wrap.scrollLeft = center.x + margin - width;
  if (center.y < top + margin)
    this.wrap.scrollTop = center.y - margin;
  else if (center.y > bottom - margin)
    this.wrap.scrollTop = center.y + margin - height;
};

DOMDisplay.prototype.clear = function() {
	this.wrap.parentNode.removeChild(this.wrap);
};

Level.prototype.obstacleAt = function(pos, size) {
  var xStart = Math.floor(pos.x);
  var xEnd = Math.ceil(pos.x + size.x);
  var yStart = Math.floor(pos.y);
  var yEnd = Math.ceil(pos.y + size.y);

  if (xStart < 0 || xEnd > this.width || yStart < 0)
    return "wall";
  if (yEnd > this.height)
    return "lava", "flood";
  for (var y = yStart; y < yEnd; y++) {
    for (var x = xStart; x < xEnd; x++) {
      var fieldType = this.grid[y][x];
      if (fieldType) return fieldType;
    }
  }
};

Level.prototype.actorAt = function(actor) {
  for (var i = 0; i < this.actors.length; i++) {
    var other = this.actors[i];
    if (other != actor &&
        actor.pos.x + actor.size.x > other.pos.x &&
        actor.pos.x < other.pos.x + other.size.x &&
        actor.pos.y + actor.size.y > other.pos.y &&
        actor.pos.y < other.pos.y + other.size.y)
      return other;
  }
};

var maxStep = 0.05;

Level.prototype.animate = function(step, keys) {
  if (this.status !== null)
    this.finishDelay -= step;

  while (step > 0) {
    var thisStep = Math.min(step, maxStep);
    this.actors.forEach(function(actor) {
      actor.act(thisStep, this, keys);
    }, this);
    step -= thisStep;
  }
};


Lava.prototype.act = function(step, level) {
  var newPos = this.pos.plus(this.speed.times(step));
  if (!level.obstacleAt(newPos, this.size))
    this.pos = newPos;
  else if (this.repeatPos)
    this.pos = this.repeatPos;
  else
    this.speed = this.speed.times(-1);
};

Flood.prototype.act = function(step, level) {
  var newPos = this.pos.plus(this.speed.times(step));
  if (!level.obstacleAt(newPos, this.size))
    this.pos = newPos;
  else
    this.speed = this.speed.times(-1);
};

var wobbleSpeed = 8, wobbleDist = 0.07;

Coin.prototype.act = function(step) {
  this.wobble += step * wobbleSpeed;
  var wobblePos = Math.sin(this.wobble) * wobbleDist;
  this.pos = this.basePos.plus(new Vector(0, wobblePos));
};


var playerXSpeed = 10;

Player.prototype.moveX = function(step, level, keys) {
  this.speed.x = 0;
  if (keys.left) this.speed.x -= playerXSpeed;
  if (keys.right) this.speed.x += playerXSpeed;

  var motion = new Vector(this.speed.x * step, 0);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle)
    level.playerTouched(obstacle);
  else
    this.pos = newPos;
};

var gravity = 30;
var jumpSpeed = 17;

Player.prototype.moveY = function(step, level, keys) {
  this.speed.y += step * gravity;
  var motion = new Vector(0, this.speed.y * step);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle) {
    level.playerTouched(obstacle);
    if (keys.up && this.speed.y > 0)
      this.speed.y = -jumpSpeed;
    else
      this.speed.y = 0;
  } else {
    this.pos = newPos;
  }
};

Player.prototype.act = function(step, level, keys) {
  this.moveX(step, level, keys);
  this.moveY(step, level, keys);

  var otherActor = level.actorAt(this);
  if (otherActor)
    level.playerTouched(otherActor.type, otherActor);

  // Losing animation
  if (level.status == "lost") {
    this.pos.y += step;
    this.size.y -= step;
  }
};

Level.prototype.playerTouched = function(type, actor) {
  //if (type == "lava" || type == "Lava" && this.status === null) { //DOESN'T SEEM TO WORK, FIND OUT WHY MASS DAMAGE
  if (type == "lava" && this.status === null || type == "flood" && this.status === null) {
    this.status = "lost";
	life -= 1;
	console.log(life);
    expo = 0;
    document.getElementById("expo").innerHTML = ("Points: " + expo);
	document.getElementById("life").innerHTML = ("Lives left: " + life);
  if(life < 0) {	
	sessionStorage.setItem("reloading", "true");
	document.location.reload();
  }
    this.finishDelay = 1;
  } else if (type == "coin") {
	  expo += 1;
      document.getElementById("expo").innerHTML = ("Points: " + expo);
    this.actors = this.actors.filter(function(other) {
      return other != actor;
    });
    if (!this.actors.some(function(actor) {
      return actor.type == "coin";
    })) {
	  life += 1;
	  document.getElementById("life").innerHTML = ("Lives left: " + life);
      this.status = "won";
      this.finishDelay = 1;
    }
  }
};

var arrowCodes = {37: "left", 38: "up", 39: "right"};

function trackKeys(codes) {
  var pressed = Object.create(null);
  function handler(event) {
    if (codes.hasOwnProperty(event.keyCode)) {
      var down = event.type == "keydown";
      pressed[codes[event.keyCode]] = down;
      event.preventDefault();
    }
  }
  addEventListener("keydown", handler);
  addEventListener("keyup", handler);
  return pressed;
}

function runAnimation(frameFunc) {
  var lastTime = null;
  function frame(time) {
    var stop = false;
    if (lastTime !== null) {
      var timeStep = Math.min(time - lastTime, 100) / 1000;
      stop = frameFunc(timeStep) === false;
    }
    lastTime = time;
    if (!stop)
      requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
}

var arrows = trackKeys(arrowCodes);

function runLevel(level, Display, andThen) {
  var display = new Display(document.body, level);
  runAnimation(function(step) {
    level.animate(step, arrows);
    display.drawFrame(step);
    if (level.isFinished()) {
      display.clear();
      if (andThen)
        andThen(level.status);
      return false;
    }
  });
}


var lives = function() {
	ctx.font = "20px Courier";
	ctx.fontFamily = "monospace";
	ctx.fillStyle = "#666";
	ctx.textAlign = "left";
	ctx.textBaseline = "top";
	ctx.fillText("Lives left: " + life, 10, 10);
};


function runGame(plans, Display) {
  function startLevel(n) {
    runLevel(new Level(plans[n]), Display, function(status) {
      if (status == "lost") {
        startLevel(n);							
	  } else if (n < plans.length - 1)
        startLevel(n + 1);
      else
        alert("You win!");
    });
  }
  startLevel(0);
}

runGame(LEVELS, DOMDisplay);
body {
  background: #222;
}

h2 {
  color: #666;
  font-family: monospace;
  text-align: center;
}

.background {
  table-layout: fixed;
  border-spacing: 0;
}

.background td {
  padding: 0;
}

.lava, .actor {
  background: #e55;
}

.wall {
  background: #444;
  border: solid 3px #333;
  box-sizing: content-box;
}

.actor {
  position: absolute;
}

.coin {
  background: #e2e838;
  border-radius: 50%;
}

.player {
  background: #335699;
  box-shadow: none;
}

.lost .player {
  background: #a04040;
}

.won .player {
  background: green;
}

.game {
  position: relative;
  overflow: hidden;
}

#life, #expo {
  font-size: 16px;
  font-family: monospace;
  color: #666;
  text-align: left;
  baseline: top;
  margin-left: 30px;
  font-weight: bold;
}

input {
  margin-left: 30px;
  
}
<h2>Simple JavaScript Game</h2>
  
  <div id="life"></div>  
  <div id="expo"></div>

我要实现的目标如下:

Lava(红色块,在代码中显示为“-”,标记为“ flood”)将

  • 沿特定方向移动(已完成,或多或少),

  • 在运动的后面留下“痕迹”(从技术上讲,在这种情况下,熔岩的高度似乎在上升),

  • “泛洪”能够遍历对象(可选,因为我必须自己仔细检查整个代码以查看在何处赋予实体“碰撞”)

我将非常感谢您的帮助,尤其是在熔岩上升效果方面。

2 个答案:

答案 0 :(得分:1)

您的游戏制作精良,但我有一些建议:

  • 请,请不要使用原型向Objects添加函数。对于不熟悉该代码的人来说,这非常非常困难。更好的方法是将所有成员函数放在同一对象中,以便将它们组合在一起。它使代码(以我的观点)更易于阅读和理解。 例如:
function Flood(pos, ch){
    this.pos = pos;
    //other assignments

    this.act = function(){
        //code
    }

    //other functions
}

将所有函数保留在一个类中也使查找函数比遍历整个文件来查找它更容易。

  • 即使已经完成了大多数游戏,但值得研究新的ES6类语法,因为这是继承和object oriented programming的完美示例。您可以具有一个通用的GridElement类,该类实现所有标准行为,并且仅扩展和修改因类型而异的部分。这种方法功能非常强大,并且可以轻松添加新类型。我强烈建议您阅读更多有关面向对象编程的理论,因为它是非常有用的工具,尤其是在大型项目中。 Daniel Shiffman是一位了不起的YouTuber,他制作了有关编程的精彩视频。他在youtube上有关于面向对象编程的完整系列文章,我建议您观看视频here

  • 在主显示功能中不显示元素。相反,让每个类都有返回div元素的自己的显示函数。您可以在主显示循环中调用每个元素的显示函数。这样,将额外的行为添加到特定类变得容易得多。如果Flood对象具有自己的显示功能,则它可以返回不连续的跟踪而不是修改其大小,这不是实现此行为的理想方法。

  • 我不知道地球上任何地方都有熔岩泛滥的地方,因此我建议您在代码中使用更准确的名称。当我看到洪水时,我立即想到水,这让我有些头疼,以至于我发现洪水实际上是上升的熔岩。更好的类名可能是LavaFlood,所以我可以自由地在下面的代码中将类Flood更改为LavaFlood

对于熔岩上升效应,这很简单。我所做的就是在(Lava)Flood类中添加一个trueSize变量并更改act函数:

(我无法将此代码发布为常规代码,因为该网站一直在说代码格式不正确)

LavaFlood.prototype.act = function(step, level) {
  var newPos = this.pos.plus(this.speed.times(step));
  if (!level.obstacleAt(newPos, this.trueSize)) {
    this.size = this.size.plus(this.speed.times(-step));
    this.pos = newPos;
  } else
    this.speed = this.speed.times(-1);
};

完整代码-熔岩上升:

var LEVELS = [
  ["                                  x  x",
   "                                  xx x",
   "                      xxx         x  x",
   "                     xx!xx        x ox",
   "                     x!!!x        x xx",
   "                     xx!xx        x  x",
   "  x                   xvx         x  x",
   "  x                               xx x",
   "  x                               x  x",
   "  xx                              x  x",
   "  x                               x xx",
   "  x                                  x",
   "  x @                xxxxx     o     x",
   "  xxxxxx     xxxxxxxxx   xxxxxxxxxxxxx",
   "       x     x                        ",
   "       x!!!!!x                        ",
   "       x!!!!!x                        ",
   "       xxxxxxx                        ",
   "--------------------------------------"]
   ];

//set variables (HP and EXP)
var life = 3;
var expo = 0;
document.getElementById("life").innerHTML = ("Lives left: " + life);
document.getElementById("expo").innerHTML = ("Points: " + expo);

//set the playzone
function Vector(x, y) {
	this.x = x; this.y = y;
}

Vector.prototype.plus = function(other) {
	return new Vector(this.x + other.x, this.y + other.y);
};

Vector.prototype.times = function(scale) {
	return new Vector(this.x * scale, this.y * scale);
};

// Note: uppercase words are used that means constructor are values
var actorchars =  {
	"@": Player,
	"o": Coin,
	"=": Lava,
	"|": Lava,
	"v": Lava,
	"#": Lava,
	"-": LavaFlood
};

function Player(pos) {
	this.pos = pos.plus(new Vector(0, -.5));
	this.size = new Vector(.5, 1);
	this.speed = new Vector(0, 0);
}
Player.prototype.type = "player";

function Lava(pos, ch) {
	this.pos = pos;
	this.size = new Vector(1, 1);
	if (ch === "=")
		this.speed = new Vector(2, 0);
	else if (ch === '|')
		this.speed = new Vector(0, 2);
	else if (ch === 'v'){
		this.speed = new Vector(0, 5);	   
		this.repeatPos = pos;
	} else if (ch === '#')
  	this.speed = new Vector(0, 10);
}
Lava.prototype.type = "lava"

function LavaFlood(pos, ch) {
  this.pos = pos;
  this.size = new Vector(1, 1);
  this.trueSize = new Vector(1, 1);
  if  (ch === '-') {
    this.speed = new Vector(0, -1);
    this.repeatPos = pos;  //will be removed in the future 
  }
}
LavaFlood.prototype.type = "lava-flood"

//Lava.prototype.type = "Lava";

// function Wall(pos, ch) {
	// this.pos = pos;
	// this.size = new Vector(1, 1);
	// if (ch === "z")
		// this.speed = new Vector(0, 1);
// }
// Wall.prototype.type = "wall"

function Coin(pos) {
	this.basePos = this.pos = pos;
	this.size = new Vector(.6, .6);
	// take a look back
	this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = "coin";

Level.prototype.isFinished = function() {
  return this.status !== null && this.finishDelay < 0;
};

function Level(plan) {
	this.width = plan[0].length;
	this.height = plan.length;
	this.grid = [];
	this.actors = [];
	
	for (var y = 0; y < this.height; y++) {
		var line = plan[y],  gridLine = [];
		for (var x = 0; x < this.width; x++) {
			var ch = line[x], fieldType = null;
			var Actor = actorchars[ch];
			if (Actor)
				this.actors.push(new Actor(new Vector(x, y), ch));
			else if (ch === "x")
				fieldType = "wall";
			else if (ch === "z")
				fieldType = "wall";
			else if (ch === "!")
				fieldType = "lava";
			else if (ch === "|")
				fieldType = "lava";
			else if (ch === "=")
				fieldType = "lava";
			else if (ch === "#")
				fieldType = "lava";
      else if (ch === "-")
        fieldType = "lava-flood";
			else if (ch === "v"){
				fieldType = "lava";
				console.log(fieldType);
			}
			gridLine.push(fieldType);
		}
		this.grid.push(gridLine);
	}
	this.player = this.actors.filter(function(actor) {
		return actor.type === "player";
	})[0];	
	this.status = this.finishDelay = null;
}

function element(name, className) {
	var elem = document.createElement(name);
	if(className) elem.className = className;
	return elem;
}

function DOMDisplay(parent, level) {
	this.wrap = parent.appendChild(element("div", "game"));
	this.level = level;
	
	this.wrap.appendChild(this.drawBackground());
	this.actorLayer = null;
	this.drawFrame();
}


var scale = 15;


DOMDisplay.prototype.drawBackground = function() {
	var table = element("table", "background");
	table.style.width = this.level.width * scale + "px";
	table.style.height = this.level.height * scale + "px";
	this.level.grid.forEach(function(row) {
  var rowElement = table.appendChild(element("tr"));
		rowElement.style.height = scale + "px";
		row.forEach(function(type) {
			rowElement.appendChild(element("td", type));
		});
	});
	return table;
};

DOMDisplay.prototype.drawActors = function() {
	var wrap = element("div");
	this.level.actors.forEach(function(actor) {
		var rect = wrap.appendChild(element("div", "actor " + actor.type));
		rect.style.width = actor.size.x * scale + "px";
		rect.style.height = actor.size.y * scale + "px";
		rect.style.left = actor.pos.x * scale + "px";
		rect.style.top = actor.pos.y * scale + "px";
	});
	return wrap;
};

DOMDisplay.prototype.drawFrame = function() {
	if (this.actorLayer)
		this.wrap.removeChild(this.actorLayer);
	this.actorLayer = this.wrap.appendChild(this.drawActors());
	this.wrap.className = "game " + (this.level.status || "");
	this.scrollPlayerIntoView();
};


// clear it later
DOMDisplay.prototype.scrollPlayerIntoView = function() {
  var width = this.wrap.clientWidth;
  var height = this.wrap.clientHeight;
  var margin = width / 3;

  // The viewport
  var left = this.wrap.scrollLeft, right = left + width;
  var top = this.wrap.scrollTop, bottom = top + height;

  var player = this.level.player;
  var center = player.pos.plus(player.size.times(0.5))
                 .times(scale);

  if (center.x < left + margin)
    this.wrap.scrollLeft = center.x - margin;
  else if (center.x > right - margin)
    this.wrap.scrollLeft = center.x + margin - width;
  if (center.y < top + margin)
    this.wrap.scrollTop = center.y - margin;
  else if (center.y > bottom - margin)
    this.wrap.scrollTop = center.y + margin - height;
};

DOMDisplay.prototype.clear = function() {
	this.wrap.parentNode.removeChild(this.wrap);
};

Level.prototype.obstacleAt = function(pos, size) {
  var xStart = Math.floor(pos.x);
  var xEnd = Math.ceil(pos.x + size.x);
  var yStart = Math.floor(pos.y);
  var yEnd = Math.ceil(pos.y + size.y);

  if (xStart < 0 || xEnd > this.width || yStart < 0)
    return "wall";
  if (yEnd > this.height)
    return "lava", "lava-flood";
  for (var y = yStart; y < yEnd; y++) {
    for (var x = xStart; x < xEnd; x++) {
      var fieldType = this.grid[y][x];
      if (fieldType) return fieldType;
    }
  }
};

Level.prototype.actorAt = function(actor) {
  for (var i = 0; i < this.actors.length; i++) {
    var other = this.actors[i];
    if (other != actor &&
        actor.pos.x + actor.size.x > other.pos.x &&
        actor.pos.x < other.pos.x + other.size.x &&
        actor.pos.y + actor.size.y > other.pos.y &&
        actor.pos.y < other.pos.y + other.size.y)
      return other;
  }
};

var maxStep = 0.05;

Level.prototype.animate = function(step, keys) {
  if (this.status !== null)
    this.finishDelay -= step;

  while (step > 0) {
    var thisStep = Math.min(step, maxStep);
    this.actors.forEach(function(actor) {
      actor.act(thisStep, this, keys);
    }, this);
    step -= thisStep;
  }
};


Lava.prototype.act = function(step, level) {
  var newPos = this.pos.plus(this.speed.times(step));
  if (!level.obstacleAt(newPos, this.size))
    this.pos = newPos;
  else if (this.repeatPos)
    this.pos = this.repeatPos;
  else
    this.speed = this.speed.times(-1);
};

LavaFlood.prototype.act = function(step, level) {
  var newPos = this.pos.plus(this.speed.times(step));
  if (!level.obstacleAt(newPos, this.trueSize)){
	this.size = this.size.plus(this.speed.times(-step)); 
    this.pos = newPos;
  }
  else
    this.speed = this.speed.times(-1);
};

var wobbleSpeed = 8, wobbleDist = 0.07;

Coin.prototype.act = function(step) {
  this.wobble += step * wobbleSpeed;
  var wobblePos = Math.sin(this.wobble) * wobbleDist;
  this.pos = this.basePos.plus(new Vector(0, wobblePos));
};


var playerXSpeed = 10;

Player.prototype.moveX = function(step, level, keys) {
  this.speed.x = 0;
  if (keys.left) this.speed.x -= playerXSpeed;
  if (keys.right) this.speed.x += playerXSpeed;

  var motion = new Vector(this.speed.x * step, 0);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle)
    level.playerTouched(obstacle);
  else
    this.pos = newPos;
};

var gravity = 30;
var jumpSpeed = 17;

Player.prototype.moveY = function(step, level, keys) {
  this.speed.y += step * gravity;
  var motion = new Vector(0, this.speed.y * step);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle) {
    level.playerTouched(obstacle);
    if (keys.up && this.speed.y > 0)
      this.speed.y = -jumpSpeed;
    else
      this.speed.y = 0;
  } else {
    this.pos = newPos;
  }
};

Player.prototype.act = function(step, level, keys) {
  this.moveX(step, level, keys);
  this.moveY(step, level, keys);

  var otherActor = level.actorAt(this);
  if (otherActor)
    level.playerTouched(otherActor.type, otherActor);

  // Losing animation
  if (level.status == "lost") {
    this.pos.y += step;
    this.size.y -= step;
  }
};

Level.prototype.playerTouched = function(type, actor) {
  //if (type == "lava" || type == "Lava" && this.status === null) { //DOESN'T SEEM TO WORK, FIND OUT WHY MASS DAMAGE
  if (type == "lava" && this.status === null || type == "lava-flood" && this.status === null) {
    this.status = "lost";
	life -= 1;
	console.log(life);
    expo = 0;
    document.getElementById("expo").innerHTML = ("Points: " + expo);
	document.getElementById("life").innerHTML = ("Lives left: " + life);
  if(life < 0) {	
	sessionStorage.setItem("reloading", "true");
	document.location.reload();
  }
    this.finishDelay = 1;
  } else if (type == "coin") {
	  expo += 1;
      document.getElementById("expo").innerHTML = ("Points: " + expo);
    this.actors = this.actors.filter(function(other) {
      return other != actor;
    });
    if (!this.actors.some(function(actor) {
      return actor.type == "coin";
    })) {
	  life += 1;
	  document.getElementById("life").innerHTML = ("Lives left: " + life);
      this.status = "won";
      this.finishDelay = 1;
    }
  }
};

var arrowCodes = {37: "left", 38: "up", 39: "right"};

function trackKeys(codes) {
  var pressed = Object.create(null);
  function handler(event) {
    if (codes.hasOwnProperty(event.keyCode)) {
      var down = event.type == "keydown";
      pressed[codes[event.keyCode]] = down;
      event.preventDefault();
    }
  }
  addEventListener("keydown", handler);
  addEventListener("keyup", handler);
  return pressed;
}

function runAnimation(frameFunc) {
  var lastTime = null;
  function frame(time) {
    var stop = false;
    if (lastTime !== null) {
      var timeStep = Math.min(time - lastTime, 100) / 1000;
      stop = frameFunc(timeStep) === false;
    }
    lastTime = time;
    if (!stop)
      requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
}

var arrows = trackKeys(arrowCodes);

function runLevel(level, Display, andThen) {
  var display = new Display(document.body, level);
  runAnimation(function(step) {
    level.animate(step, arrows);
    display.drawFrame(step);
    if (level.isFinished()) {
      display.clear();
      if (andThen)
        andThen(level.status);
      return false;
    }
  });
}


var lives = function() {
	ctx.font = "20px Courier";
	ctx.fontFamily = "monospace";
	ctx.fillStyle = "#666";
	ctx.textAlign = "left";
	ctx.textBaseline = "top";
	ctx.fillText("Lives left: " + life, 10, 10);
};


function runGame(plans, Display) {
  function startLevel(n) {
    runLevel(new Level(plans[n]), Display, function(status) {
      if (status == "lost") {
        startLevel(n);							
	  } else if (n < plans.length - 1)
        startLevel(n + 1);
      else
        alert("You win!");
    });
  }
  startLevel(0);
}

runGame(LEVELS, DOMDisplay);
body {
  background: #222;
}

h2 {
  color: #666;
  font-family: monospace;
  text-align: center;
}

.background {
  table-layout: fixed;
  border-spacing: 0;
}

.background td {
  padding: 0;
}

.lava, .actor {
  background: #e55;	
}

.wall {
  background: #444;
  border: solid 3px #333;
  box-sizing: content-box;
}

.actor {
  position: absolute;
}

.coin {
  background: #e2e838;
  border-radius: 50%;
}

.player {
  background: #335699;
  box-shadow: none;
}

.lost .player {
  background: #a04040;
}

.won .player {
  background: green;
}

.game {
  position: relative;
  overflow: hidden;
}

#life, #expo {
  font-size: 16px;
  font-family: monospace;
  color: #666;
  text-align: left;
  baseline: top;
  margin-left: 30px;
  font-weight: bold;
}

input {
  margin-left: 30px; 
}
<h2>Simple JavaScript Game</h2>
  
  <div id="life"></div>  
  <div id="expo"></div>

如果您需要任何澄清或帮助,请随时询问。

答案 1 :(得分:1)

所有内容都不适合一篇文章,其余部分就在这里:

要使熔岩从物体中升起,只需反转速度并除去if语句中的障碍物即可。要使熔岩看起来像在对象后面,只需添加css类lava-flood并将z-index设置为-1。如果您希望熔岩在对象之前升起,只需从CSS的lava-flood类中删除z-index属性。

完整代码-熔岩在物体后面升起

var LEVELS = [
  ["                                  x  x",
   "                                  xx x",
   "                      xxx         x  x",
   "                     xx!xx        x ox",
   "                     x!!!x        x xx",
   "                     xx!xx        x  x",
   "  x                   xvx         x  x",
   "  x                               xx x",
   "  x                               x  x",
   "  xx                              x  x",
   "  x                               x xx",
   "  x                                  x",
   "  x @                xxxxx     o     x",
   "  xxxxxx     xxxxxxxxx   xxxxxxxxxxxxx",
   "       x     x                        ",
   "       x!!!!!x                        ",
   "       x!!!!!x                        ",
   "       xxxxxxx                        ",
   "--------------------------------------"]
   ];

//set variables (HP and EXP)
var life = 3;
var expo = 0;
document.getElementById("life").innerHTML = ("Lives left: " + life);
document.getElementById("expo").innerHTML = ("Points: " + expo);

//set the playzone
function Vector(x, y) {
	this.x = x; this.y = y;
}

Vector.prototype.plus = function(other) {
	return new Vector(this.x + other.x, this.y + other.y);
};

Vector.prototype.times = function(scale) {
	return new Vector(this.x * scale, this.y * scale);
};

// Note: uppercase words are used that means constructor are values
var actorchars =  {
	"@": Player,
	"o": Coin,
	"=": Lava,
	"|": Lava,
	"v": Lava,
	"#": Lava,
	"-": LavaFlood
};

function Player(pos) {
	this.pos = pos.plus(new Vector(0, -.5));
	this.size = new Vector(.5, 1);
	this.speed = new Vector(0, 0);
}
Player.prototype.type = "player";

function Lava(pos, ch) {
	this.pos = pos;
	this.size = new Vector(1, 1);
	if (ch === "=")
		this.speed = new Vector(2, 0);
	else if (ch === '|')
		this.speed = new Vector(0, 2);
	else if (ch === 'v'){
		this.speed = new Vector(0, 5);	   
		this.repeatPos = pos;
	} else if (ch === '#')
  	this.speed = new Vector(0, 10);
}
Lava.prototype.type = "lava"

function LavaFlood(pos, ch) {
  this.pos = pos;
  this.size = new Vector(1, 1);
  this.trueSize = new Vector(1, 1);
  if  (ch === '-') {
    this.speed = new Vector(0, -1);
    this.repeatPos = pos;  //will be removed in the future 
  }
}
LavaFlood.prototype.type = "lava-flood"

//Lava.prototype.type = "Lava";

// function Wall(pos, ch) {
	// this.pos = pos;
	// this.size = new Vector(1, 1);
	// if (ch === "z")
		// this.speed = new Vector(0, 1);
// }
// Wall.prototype.type = "wall"

function Coin(pos) {
	this.basePos = this.pos = pos;
	this.size = new Vector(.6, .6);
	// take a look back
	this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = "coin";

Level.prototype.isFinished = function() {
  return this.status !== null && this.finishDelay < 0;
};

function Level(plan) {
	this.width = plan[0].length;
	this.height = plan.length;
	this.grid = [];
	this.actors = [];
	
	for (var y = 0; y < this.height; y++) {
		var line = plan[y],  gridLine = [];
		for (var x = 0; x < this.width; x++) {
			var ch = line[x], fieldType = null;
			var Actor = actorchars[ch];
			if (Actor)
				this.actors.push(new Actor(new Vector(x, y), ch));
			else if (ch === "x")
				fieldType = "wall";
			else if (ch === "z")
				fieldType = "wall";
			else if (ch === "!")
				fieldType = "lava";
			else if (ch === "|")
				fieldType = "lava";
			else if (ch === "=")
				fieldType = "lava";
			else if (ch === "#")
				fieldType = "lava";
      else if (ch === "-")
        fieldType = "lava-flood";
			else if (ch === "v"){
				fieldType = "lava";
				console.log(fieldType);
			}
			gridLine.push(fieldType);
		}
		this.grid.push(gridLine);
	}
	this.player = this.actors.filter(function(actor) {
		return actor.type === "player";
	})[0];	
	this.status = this.finishDelay = null;
}

function element(name, className) {
	var elem = document.createElement(name);
	if(className) elem.className = className;
	return elem;
}

function DOMDisplay(parent, level) {
	this.wrap = parent.appendChild(element("div", "game"));
	this.level = level;
	
	this.wrap.appendChild(this.drawBackground());
	this.actorLayer = null;
	this.drawFrame();
}


var scale = 15;


DOMDisplay.prototype.drawBackground = function() {
	var table = element("table", "background");
	table.style.width = this.level.width * scale + "px";
	table.style.height = this.level.height * scale + "px";
	this.level.grid.forEach(function(row) {
  var rowElement = table.appendChild(element("tr"));
		rowElement.style.height = scale + "px";
		row.forEach(function(type) {
			rowElement.appendChild(element("td", type));
		});
	});
	return table;
};

DOMDisplay.prototype.drawActors = function() {
	var wrap = element("div");
	this.level.actors.forEach(function(actor) {
		var rect = wrap.appendChild(element("div", "actor " + actor.type));
		rect.style.width = actor.size.x * scale + "px";
		rect.style.height = actor.size.y * scale + "px";
		rect.style.left = actor.pos.x * scale + "px";
		rect.style.top = actor.pos.y * scale + "px";
	});
	return wrap;
};

DOMDisplay.prototype.drawFrame = function() {
	if (this.actorLayer)
		this.wrap.removeChild(this.actorLayer);
	this.actorLayer = this.wrap.appendChild(this.drawActors());
	this.wrap.className = "game " + (this.level.status || "");
	this.scrollPlayerIntoView();
};


// clear it later
DOMDisplay.prototype.scrollPlayerIntoView = function() {
  var width = this.wrap.clientWidth;
  var height = this.wrap.clientHeight;
  var margin = width / 3;

  // The viewport
  var left = this.wrap.scrollLeft, right = left + width;
  var top = this.wrap.scrollTop, bottom = top + height;

  var player = this.level.player;
  var center = player.pos.plus(player.size.times(0.5))
                 .times(scale);

  if (center.x < left + margin)
    this.wrap.scrollLeft = center.x - margin;
  else if (center.x > right - margin)
    this.wrap.scrollLeft = center.x + margin - width;
  if (center.y < top + margin)
    this.wrap.scrollTop = center.y - margin;
  else if (center.y > bottom - margin)
    this.wrap.scrollTop = center.y + margin - height;
};

DOMDisplay.prototype.clear = function() {
	this.wrap.parentNode.removeChild(this.wrap);
};

Level.prototype.obstacleAt = function(pos, size) {
  var xStart = Math.floor(pos.x);
  var xEnd = Math.ceil(pos.x + size.x);
  var yStart = Math.floor(pos.y);
  var yEnd = Math.ceil(pos.y + size.y);

  if (xStart < 0 || xEnd > this.width || yStart < 0)
    return "wall";
  if (yEnd > this.height)
    return "lava", "lava-flood";
  for (var y = yStart; y < yEnd; y++) {
    for (var x = xStart; x < xEnd; x++) {
      var fieldType = this.grid[y][x];
      if (fieldType) return fieldType;
    }
  }
};

Level.prototype.actorAt = function(actor) {
  for (var i = 0; i < this.actors.length; i++) {
    var other = this.actors[i];
    if (other != actor &&
        actor.pos.x + actor.size.x > other.pos.x &&
        actor.pos.x < other.pos.x + other.size.x &&
        actor.pos.y + actor.size.y > other.pos.y &&
        actor.pos.y < other.pos.y + other.size.y)
      return other;
  }
};

var maxStep = 0.05;

Level.prototype.animate = function(step, keys) {
  if (this.status !== null)
    this.finishDelay -= step;

  while (step > 0) {
    var thisStep = Math.min(step, maxStep);
    this.actors.forEach(function(actor) {
      actor.act(thisStep, this, keys);
    }, this);
    step -= thisStep;
  }
};


Lava.prototype.act = function(step, level) {
  var newPos = this.pos.plus(this.speed.times(step));
  if (!level.obstacleAt(newPos, this.size))
    this.pos = newPos;
  else if (this.repeatPos)
    this.pos = this.repeatPos;
  else
    this.speed = this.speed.times(-1);
};

LavaFlood.prototype.act = function(step, level) {
  this.pos = this.pos.plus(this.speed.times(step));
  this.size = this.size.plus(this.speed.times(-step));
};

var wobbleSpeed = 8, wobbleDist = 0.07;

Coin.prototype.act = function(step) {
  this.wobble += step * wobbleSpeed;
  var wobblePos = Math.sin(this.wobble) * wobbleDist;
  this.pos = this.basePos.plus(new Vector(0, wobblePos));
};


var playerXSpeed = 10;

Player.prototype.moveX = function(step, level, keys) {
  this.speed.x = 0;
  if (keys.left) this.speed.x -= playerXSpeed;
  if (keys.right) this.speed.x += playerXSpeed;

  var motion = new Vector(this.speed.x * step, 0);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle)
    level.playerTouched(obstacle);
  else
    this.pos = newPos;
};

var gravity = 30;
var jumpSpeed = 17;

Player.prototype.moveY = function(step, level, keys) {
  this.speed.y += step * gravity;
  var motion = new Vector(0, this.speed.y * step);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle) {
    level.playerTouched(obstacle);
    if (keys.up && this.speed.y > 0)
      this.speed.y = -jumpSpeed;
    else
      this.speed.y = 0;
  } else {
    this.pos = newPos;
  }
};

Player.prototype.act = function(step, level, keys) {
  this.moveX(step, level, keys);
  this.moveY(step, level, keys);

  var otherActor = level.actorAt(this);
  if (otherActor)
    level.playerTouched(otherActor.type, otherActor);

  // Losing animation
  if (level.status == "lost") {
    this.pos.y += step;
    this.size.y -= step;
  }
};

Level.prototype.playerTouched = function(type, actor) {
  //if (type == "lava" || type == "Lava" && this.status === null) { //DOESN'T SEEM TO WORK, FIND OUT WHY MASS DAMAGE
  if (type == "lava" && this.status === null || type == "lava-flood" && this.status === null) {
    this.status = "lost";
	life -= 1;
	console.log(life);
    expo = 0;
    document.getElementById("expo").innerHTML = ("Points: " + expo);
	document.getElementById("life").innerHTML = ("Lives left: " + life);
  if(life < 0) {	
	sessionStorage.setItem("reloading", "true");
	document.location.reload();
  }
    this.finishDelay = 1;
  } else if (type == "coin") {
	  expo += 1;
      document.getElementById("expo").innerHTML = ("Points: " + expo);
    this.actors = this.actors.filter(function(other) {
      return other != actor;
    });
    if (!this.actors.some(function(actor) {
      return actor.type == "coin";
    })) {
	  life += 1;
	  document.getElementById("life").innerHTML = ("Lives left: " + life);
      this.status = "won";
      this.finishDelay = 1;
    }
  }
};

var arrowCodes = {37: "left", 38: "up", 39: "right"};

function trackKeys(codes) {
  var pressed = Object.create(null);
  function handler(event) {
    if (codes.hasOwnProperty(event.keyCode)) {
      var down = event.type == "keydown";
      pressed[codes[event.keyCode]] = down;
      event.preventDefault();
    }
  }
  addEventListener("keydown", handler);
  addEventListener("keyup", handler);
  return pressed;
}

function runAnimation(frameFunc) {
  var lastTime = null;
  function frame(time) {
    var stop = false;
    if (lastTime !== null) {
      var timeStep = Math.min(time - lastTime, 100) / 1000;
      stop = frameFunc(timeStep) === false;
    }
    lastTime = time;
    if (!stop)
      requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
}

var arrows = trackKeys(arrowCodes);

function runLevel(level, Display, andThen) {
  var display = new Display(document.body, level);
  runAnimation(function(step) {
    level.animate(step, arrows);
    display.drawFrame(step);
    if (level.isFinished()) {
      display.clear();
      if (andThen)
        andThen(level.status);
      return false;
    }
  });
}


var lives = function() {
	ctx.font = "20px Courier";
	ctx.fontFamily = "monospace";
	ctx.fillStyle = "#666";
	ctx.textAlign = "left";
	ctx.textBaseline = "top";
	ctx.fillText("Lives left: " + life, 10, 10);
};


function runGame(plans, Display) {
  function startLevel(n) {
    runLevel(new Level(plans[n]), Display, function(status) {
      if (status == "lost") {
        startLevel(n);							
	  } else if (n < plans.length - 1)
        startLevel(n + 1);
      else
        alert("You win!");
    });
  }
  startLevel(0);
}

runGame(LEVELS, DOMDisplay);
body {
  background: #222;
}

h2 {
  color: #666;
  font-family: monospace;
  text-align: center;
}

.background {
  table-layout: fixed;
  border-spacing: 0;
}

.background td {
  padding: 0;
}

.lava, .actor {
  background: #e55;	
}

.lava-flood {
  z-index: -1;	
}

.wall {
  background: #444;
  border: solid 3px #333;
  box-sizing: content-box;
}

.actor {
  position: absolute;
}

.coin {
  background: #e2e838;
  border-radius: 50%;
}

.player {
  background: #335699;
  box-shadow: none;
}

.lost .player {
  background: #a04040;
}

.won .player {
  background: green;
}

.game {
  position: relative;
  overflow: hidden;
}

#life, #expo {
  font-size: 16px;
  font-family: monospace;
  color: #666;
  text-align: left;
  baseline: top;
  margin-left: 30px;
  font-weight: bold;
}

input {
  margin-left: 30px; 
}
<h2>Simple JavaScript Game</h2>
  
  <div id="life"></div>  
  <div id="expo"></div>

如果您需要任何澄清或帮助,请随时询问。