在Javascript中的特定时间绘制正方形然后移动

时间:2018-05-31 17:26:39

标签: javascript html5 canvas

对于我的最终项目,我正在尝试设计类似吉他英雄的东西,基于JavaScript。

如你所知,在“吉他英雄”中,圆圈代表你必须在某个时间播放的音符。我正在努力做同样的事情,但我的笔记将用正方形表示。

在上面的代码中,方块向下移动,但我不能让它们在某个时间移动。我的意思是,他们都在同一时间移动,但我希望他们在我想要的时候移动每一个。

代码最重要的部分是vectorSquares数组,它包含我想要绘制的所有正方形。

班级广场。

函数draw()和update()。

我希望任何人都可以帮助我,我需要完成我的最终项目,我现在感到很失落。

			

			
			c = document.getElementById("canvas");
			ctx = c.getContext("2d");

			c.width = 1000;
			c.height = 600;
				

			
			//Clases utilizadas para dibujar los cuadrados
	//function Start(){		
		
		
			class CanLine {
				
				constructor(x1, y1, x2, y2){
					
					this.x1 = x1;
					this.y1 = y1;
					this.x2 = x2;
					this.y2 = y2;
					this.strokeStyle = "black";
					
				}
				DrawingLines() {  
					ctx.beginPath();
					ctx.lineWidth="2";
					ctx.strokeStyle="black";
					ctx.moveTo(this.x1,this.y1);
					ctx.lineTo(this.x2,this.y2);
					ctx.stroke();
					ctx.closePath();
				}
			}
			
			
			
			class FinalLine {
				constructor(x, y, color) {
				this.x = x;
				this.y = y;
				this.color = color;
				this.height = 20;
			 	this.width = 800;
				}
				draw() {
					ctx.fillStyle = this.color;
					ctx.fillRect(this.x, this.y, this.width, this.height);
					ctx.fill();
			  	}
			}
			
			
			class FixedSquare {
				constructor(x, y, color) {
				this.x = x;
				this.y = y;
				this.color = color;
				this.size = 80;
			 	}
				draw() {
					ctx.fillStyle = this.color;
					ctx.fillRect(this.x, this.y, this.size, this.size);
					ctx.fill();
			  	}
			}
			
			
			
			
		
			class Square {
				
				constructor(x, y, color, speed) {
				this.x = x;
				this.y = y;
				this.color = color;
				this.size = 60;
				this.speed = speed
			 	}
				
								
				draw() {
					
					
					ctx.fillStyle = this.color;
					ctx.fillRect(this.x, this.y, this.size, this.size);
					ctx.fill();
				}
			  	
			  	update() {
					
					this.y += this.speed;
                    if (this.y == 480) {
                    this.y = 0 - this.size;
	                }
				}
				
			}
		
		
						
			//Arrays en los que serán almacenados los objetos para ser dibujados
			
			
			let Lines = [
				
				new CanLine(200, 0, 200, c.height),
				new CanLine(360, 0, 360, c.height),
				new CanLine(500, 0, 500, c.height),
				new CanLine(650, 0, 650, c.height),
				new CanLine(800, 0, 800, c.height)
			];
			
			
			let Final_Line = [
			
				new FinalLine(100, 500, "purple")
			];
			
			
			let Fixed_squares = [
			
				new FixedSquare(160, 465, "green"),
				new FixedSquare(321, 465, "gray"),
				new FixedSquare(460, 465, "blue"),
				new FixedSquare(610, 465, "yellow"),
				new FixedSquare(760, 465, "red")
			];
			
		
			//Array Importante, donde van los cuadrados que se van a mover
			
			let squares = [
			

					new Square(170, 0, "green", 3),
					new Square(170, 0, "green", 3),
					new Square(331, 0, "gray", 3),
					new Square(470, 0, "blue", 3),
					new Square(620, 0, "yellow", 3),
					new Square(770, 0, "red", 3)	
				
			];
			
			
			
			//Funciónes 
			
			
			
			//Aquí se añadirán todos los objetos que sean necesarios dibujar
	
			function DrawingLines() {  
				
				ctx.beginPath();
				ctx.lineWidth="2";
				ctx.strokeStyle="black";
				ctx.moveTo(0,0);
				ctx.lineTo(0,0);
				ctx.stroke();
				ctx.closePath();
				Lines.forEach(CanLine => CanLine.DrawingLines());
			}
			

			//var counter = 0;
			var currentTime = 0;
			
			var startTime = Date.now();
			
			function countUp (milisegundos){ 	//Valor que hay en el vector
					
				var TiempoActual = Date.now() - startTime;
				
				for(i=TiempoActual;i>=milisegundos;){
					
					i = Date.now() - startTime;
					
				}
				
				return true;
			}
			
			
				let tiempos = [3000, 7000 , 10000, 12000, 15000];
				function draw() {
					
					ctx.fillStyle = "white";
					ctx.fillRect(0, 0, c.width, c.height);
					ctx.fill();
					
				    squares.forEach(square => square.draw());
					
					Final_Line.forEach(FinalLine => FinalLine.draw());	
					Fixed_squares.forEach(FixedSquare => FixedSquare.draw());	

				}
				
		
			//Función que actualiza cada frame
			function update() {
			  	
				
				squares.forEach(square => square.update());
				draw();
				DrawingLines();
				requestAnimationFrame(update);
			}
			requestAnimationFrame(update);
			
			
<style>
		
		
		
		canvas {
			border: 3px solid #000000;
			
			padding-left: 0;
			padding-right: 0;
			margin-left: auto;
			margin-right: auto;
			display: block;
			border-radius: 5px;
		}
		
		.button {
			background-color: #f4511e;
			border: none;
			color: white;
			padding: 16px 32px;
			text-align: center;
			font-size: 16px;
			margin: 4px 2px;
			opacity: 0.6;
			transition: 0.3s;
			display: inline-block;
			text-decoration: none;
			cursor: pointer;
		}
			
		.button:hover {opacity: 1}
		
    </style>
<!DOCTYPE HTML>
<html>

	<body>

		
		<center>
			
			
			
		</center>
		
		<canvas id="canvas"></canvas>
		
			
		</body>
</html>

1 个答案:

答案 0 :(得分:1)

我重新编写了一些代码并对其进行了优化,如果您有任何问题请告诉我。吉他适应画布的大小,我可以在方形尺寸上做同样的事情,但我没有做到(不难)。如果您有任何问题,请告诉我。

&#13;
&#13;
//TODO: Probably fetch song via XMLHttpRequest and parse as JSON
//TODO: End game when song ends (haven't done that part)
//This is an example
const song = {
  duration: 30000, //in miliseconds, corresponds to 30 seconds
  notes: [{
      string: 0, //index of string 0-4 - this will also define the color: index 0 corresponds to the color "green" in colors array
      at: 10000, //appears at the 10 second mark
    },
    {
      string: 1,
      at: 1000,
    },
    {
      string: -1,
      at: 6000,
    },
    {
      string: 1,
      at: 2000,
    },
    {
      string: 1,
      at: 2500,
    },
    {
      string: 2,
      at: 1500
    },
    {
      string: 2,
      at: 2000
    },
    {
      string: 2,
      at: 3000
    },
    {
      string: 2,
      at: 4000
    },
    {
      string: 3,
      at: 1500
    },
    {
      string: 4,
      at: 2000
    }
  ]
  .sort((note1, note2) => note1.at - note2.at)
  .map((note,index)=>{
      note.visible=false;
      note.difference=null;
      note.accessible=true;
      note.failed=false;
      note.id=index;
      return note;
  })
}

//Max 5 strings, so 5 colors
const colors = ["green", "blue", "yellow", "red", "pink"];


window.addEventListener("load", () => {
  const canvas = document.getElementById("canvas");
  const correct = document.getElementById("correct");
  const errors = document.getElementById("errors");
  const missed = document.getElementById("missed");
  const gs = new GameService(canvas, colors);
  gs.start(song);
  function multipleKeys(event) {
    const key = parseInt(event.key);
    //check to see if number
    if (key !== NaN && key > 0 && key < 6) {
      gs.map[key] = event.type == 'keydown';
      gs.keydownHandler();
    }
  };
  window.addEventListener("keydown", multipleKeys);
  window.addEventListener("keypress", multipleKeys);
  
  //Todo: on game quit, stop SI
  let si = setInterval(()=>{
      correct.innerText = `Correct: ${gs.correct}`;
      errors.innerText = `Errors: ${gs.errors}`;
      missed.innerText = `Missed: ${gs.missed}`;
  }, 100);

});



class GameService {

  constructor(canvas, colors) {
    this.ctx = canvas.getContext("2d");
    this.then = Date.now();
    this.canvas = canvas;
    this.colors = colors;
    this.size = 20;
    //entities are your square notes
    this.entities = [];
    this.interval = 1000 / 30; //30 = fps
    this.finalLineHeight = this.size;
    this.nbLines = 5;
    this.heightInMiliseconds = 2000; //Notes appear 2 seconds before they reach purple line
    //TODO: Find the sweet spot, for keypress accuracy
    this.latencyFailSafe = 70;
    this.songOffset = 5000; //Song shouldn't start right away, give the player 10 seconds to get ready before notes appear.
    this.requestId = undefined;
  }

  start(song) {
    this.entities = song.notes;
    this.duration = song.duration;


    this.errors = 0;
    this.correct = 0;
    this.missed = 0;
    this.visibleNotes = [];
    this.map = {
      "1": false,
      "2": false,
      "3": false,
      "4": false,
      "5": false
    };
    //record when song starts with 5 seconds delay, so notes don't appear RIGHT away.
    this.start = Date.now() + this.songOffset;
    this.loop();
  }

  //TODO: Not finished
  keydownHandler() {
    for (const prop in this.map) {
      if (this.map.hasOwnProperty(prop) && this.map[prop] !== false) {
        //key was pressed, 
        const note = this.entities.find(note => note.visible && note.string + 1 == prop && note.accessible);

        //if no visibile note found 1 error
        if (note === undefined){ this.errors++; return; }
        
        //check to see if note exists on purple line
        else if (note.difference < (this.heightInMiliseconds * this.finalLineHeight / this.canvas.height)+this.latencyFailSafe){
          this.correct++;
        }
        else{ 
          this.errors++;
        };

        note.accessible = false;
      }
    }

  }

  draw() {
    let now = Date.now();
    let delta = now - this.then;
    //determine if it's the right time to draw
    if (delta > this.interval) {
      this.then = now - (delta % this.interval);
      //Clear rect before drawing
      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

      const canvasWidth = this.canvas.width;
      const canvasHeight = this.canvas.height;
      const percentage = 100 / this.nbLines / 100;
      const offset = canvasWidth * .1;

      //DRAWING FIXED ENTITIES FIRST

      //not many fixed entities so no point in instancing them.
      //Also this way, they adapt to the canvas size easily even when the canvas is resized.

      //draw can lines
      this.ctx.save();
      this.ctx.lineWidth = "2";
      this.ctx.strokeStyle = "black";

      for (let i = 0; i < this.nbLines; i++) {
        const X = (canvasWidth * i * percentage) + offset;
        this.ctx.beginPath();
        this.ctx.moveTo(X, 0);
        this.ctx.lineTo(X, canvasHeight);
        this.ctx.stroke();
        this.ctx.closePath();
      }
      this.ctx.restore();
      //draw final line
      //drawn at the bottom of the canvas
      this.ctx.fillStyle = "purple";
      this.ctx.fillRect(0, this.canvas.height - this.finalLineHeight, this.canvas.width, this.finalLineHeight);
      this.ctx.fill();

      //TODO: DRAW FIXED SQUARES HERE
      //Was lazy ;)

      //DRAWING DYNAMIC ENTITIES SECOND

      const milisecondsSinceStart = now - this.start;
      this.entities = this.entities.filter(entity => {
        const difference = entity.at - milisecondsSinceStart;
        //Check to see if note should appear
        if (difference > 0 && difference < this.heightInMiliseconds) {

          entity.difference = difference;
          entity.visible = true;
          const width = entity.string !== -1 ? this.size : canvasWidth;
          const X = (entity.string !== -1) ? (canvasWidth * entity.string * percentage) + offset: 0;
          const H = canvasHeight - canvasHeight * (difference / this.heightInMiliseconds);
          this.ctx.fillStyle =(entity.string !== -1) ? this.colors[entity.string]: "orange";
          this.ctx.fillRect(X - (this.size / 2), H, width, this.size);
          this.ctx.fill();
          return true;
        } else if (difference <= -this.latencyFailSafe) {
          
          //Missed a note
          if(entity.failed === false && entity.accessible === true){
            this.missed++;
            this.errors++;
          }
          return false;
        } else {
          return true;
        }

      });
    }
  }

  loop() {
    if(this.entities.length === 0){ this.stop(); return; }
    this.requestId = requestAnimationFrame(this.loop.bind(this));
    this.draw();
  }
  
  stop(){
    cancelAnimationFrame(this.requestId);
    this.requestId = undefined;
    alert(`Game Completed: correct = ${this.correct}, errors = ${this.errors}, missed = ${this.missed}`);
  }

}
&#13;
<canvas id="canvas"></canvas>
<p id="correct"></p>
<p id="errors"></p>
<p id="missed"></p>
&#13;
&#13;
&#13;