对于我的最终项目,我正在尝试设计类似吉他英雄的东西,基于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>
答案 0 :(得分:1)
我重新编写了一些代码并对其进行了优化,如果您有任何问题请告诉我。吉他适应画布的大小,我可以在方形尺寸上做同样的事情,但我没有做到(不难)。如果您有任何问题,请告诉我。
//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;