我试图在JavaScript中更新每一帧的弹幕,但它似乎不能正常工作。这是我放在JSFiddle上的代码。
https://jsfiddle.net/Reverseblade/co2mpLnv/5/
var ctx;
var hero;
var enemy;
var beams = [];
var beam_hitting = false;
var continuous_hit = false;
var count = 0;
var canvas_w = 500, canvas_y= 700;
var keycode = NaN;
var laser_degree = 200;
function init(){
createCanvas();
createMainHero();
createEnemy();
draw();
mainRoutine();
}
function mainRoutine(){
count++;
ctx.clearRect(0, 0, canvas_w, canvas_y);
// laserTest();
hero.move(keycode);
enemyUpdate();
// hero.setBullets();
// hero.moveBullets();
draw();
window.setTimeout(mainRoutine, 7);
}
function createCanvas(){
var canvas = document.createElement("canvas");
canvas.id = "canvas";
canvas.width = canvas_w;
canvas.height = canvas_y;
document.body.appendChild(canvas);
}
function createMainHero(){
hero = new Hero();
}
function createEnemy(){
enemy = new Enemy;
}
function Hero(){
this.w = this.h = 25,
this.x = canvas_w/2 - this.w/2,
this.y = canvas_y - this.h,
this.dx = this.dy = 2.5;
this.bullets = [];
this.move = function(key){
switch(key){
case 37: if (hero.x > 0) {hero.x -= this.dx;} break;
case 38: if (hero.y > 0) {hero.y -= this.dy;} break;
case 39: if (hero.x < canvas_w - hero.w) {hero.x += this.dx;} break;
case 40: if (hero.y < canvas_y - hero.h) {hero.y += this.dy;} break;
}
};
this.setBullets = function(){
if (count % 20 === 0) {
var w = h = 8;
var dx = dy = 5;
var x = hero.x + hero.w/2 - w/2;
var y = hero.y;
hero.bullets.push({x: x, y: y, w: w, h: h, dx: dx, dy: dy, moving:true});
}
};
this.moveBullets = function(){
for (var i = 0; i < this.bullets.length; i++) {
if (this.bullets[i].y < 0) {
this.bullets[i].moving = false;
continue;
}
if (this.bullets[i].moving === false) {
continue;
}
this.bullets[i].y -= this.bullets[i].dy;
if (this.bullets[i].y < -100) {this.bullets.splice(i, 1)}
}
}
}
function Enemy(){
this.w = this.h = 25;
this.x = canvas_w/2 - this.w/2;
this.y = 50;
this.bullets = [];
this.moving = false;
this.move_to = 0;
this.bullets_count = 0;
this.bullets_angle = 0;
this.current_skill = 1;
this.barrage_count = 0;
this.skill = function(){
enemySkill();
};
}
function enemyUpdate(){
function move(){
function changeDirection(){
var options = ["left", "right", "up", "down"];
var id;
if (enemy.x <= 50) {id = options.indexOf("left"); options.splice(id, 1);}
if (enemy.x >= 450) {id = options.indexOf("right");options.splice(id, 1);}
if (enemy.y <= 50) {id = options.indexOf("up");options.splice(id, 1);}
if (enemy.y >= 200) {id = options.indexOf("down");options.splice(id, 1);}
var rand = Math.floor(Math.random() * options.length);
enemy.moving = options[rand];
switch(enemy.moving){
case "left": enemy.move_to = enemy.x - 150 ; break;
case "right": enemy.move_to = enemy.x + 150 ; break;
case "up": enemy.move_to = enemy.y - 150 ; break;
case "down": enemy.move_to = enemy.y + 150 ; break;
}
} /* end changeDirection() */
if (count % 800 === 0) {changeDirection(); console.log("changing");}
switch(enemy.moving){
case "left": if (enemy.x > 50 && enemy.x > enemy.move_to) {enemy.x -= 0.5;} break;
case "right": if (enemy.x < 450 && enemy.x < enemy.move_to) {enemy.x += 0.5;} break;
case "up": if (enemy.y > 50 && enemy.y > enemy.move_to) {enemy.y -= 0.5; } break;
case "down": if (enemy.y < 200 && enemy.y < enemy.move_to) {enemy.y += 0.5; } break;
}
} /* end move()*/
move();
enemy.skill();
} /* end enemyUpdate() */
function enemySkill(){
// console.log("enemy skill");
function setBullets(){
var prev_status = enemy.bullets_count === 0 ? 500 : enemy.bullets[enemy.bullets.length - 1]["radius"];
if (prev_status >25) {
// console.log("bullets set");
var center_x = enemy.x + enemy.w/2;
var center_y = enemy.y + enemy.h/2;
var radius = 20;
var ceil = enemy.bullets.length === 0 ? 0 : enemy.bullets.length -1;
for (var angle = enemy.bullets_angle, i= ceil; angle < enemy.bullets_angle + 360; angle += 40, i++ ) {
// console.log("i: " + i);
var radian = angle * Math.PI / 180;
var set_x = center_x + radius * Math.cos(radian);
var set_y = center_y + radius * Math.sin(radian);
// console.log("angle: " + /angle + "set_x: " + set_x + "set_y: " + set_y);
enemy.bullets.push({"x": set_x, "y": set_y, "moving": true, "radius": radius, "center_x": center_x, "center_y": center_y, "w": 25, "h": 25, "radian": radian});
if (enemy.bullets_count === 0) {enemy.bullets_count++;}
// console.log(enemy.bullets[0][i]["x"]);
}
enemy.bullets_angle += 10;
enemy.barrage_count ++;
if (enemy.barrage_count % 100 === 0) {
enemy.current_skill = 0;
}
}
} /* end setBullets */
function moveBullets(){
if (count % 4 ===0) {
for (var i = 0; i < enemy.bullets.length; i++) {
if (enemy.bullets[i]["moving"] === true) {
var radian = enemy.bullets[i]["radian"];
var center_x = enemy.bullets[i]["center_x"];
var center_y = enemy.bullets[i]["center_y"];
enemy.bullets[i]["radius"] += 5;
var radius = enemy.bullets[i]["radius"];
var set_x = center_x + radius * Math.cos(radian);
var set_y = center_y + radius * Math.sin(radian);
// console.log(set_y);
enemy.bullets[i]["x"] = set_x;
enemy.bullets[i]["y"] = set_y;
if (enemy.bullets[i]["x"] < -100 || enemy.bullets[i]["x"] > canvas_w + 100 || enemy.bullets[i]["y"] < -100 || enemy.bullets[i]["y"] > canvas_y + 100 ) {
// enemy.bullets[i]["moving"] = false;
enemy.bullets.splice(i, 1);
}
}
}
}
}
if (enemy.current_skill === 1) {
setBullets();
}
moveBullets();
}
function draw(){
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
//hero
//ctx.fillStyle = "blue";
//ctx.fillRect(hero.x, hero.y ,hero.w, hero.h);
//enemy
//ctx.fillStyle = "red";
//ctx.fillRect(enemy.x, enemy.y ,enemy.w, enemy.h);
//heroの弾
ctx.fillStyle = "blue";
for (var i = 0; i < hero.bullets.length; i++) {
if (hero.bullets[i].moving === false) {
continue;
}
ctx.fillRect(hero.bullets[i].x, hero.bullets[i].y ,hero.bullets[i].w, hero.bullets[i].h);
}
//敵の弾
ctx.fillStyle = "red";
for (var i = 0; i < enemy.bullets.length; i++) {
ctx.fillStyle = "green";
if (enemy.bullets[i]["moving"] === false) {
continue;
}
ctx.beginPath();
ctx.arc(enemy.bullets[i]["x"], enemy.bullets[i]["y"], 15, 0, 2 * Math.PI, false);
ctx.closePath();
ctx.fill();
}
}
window.addEventListener("keydown", function(e){
switch(e.keyCode){
case 37: keycode = 37; break;
case 38: keycode = 38; break;
case 39: keycode = 39; break;
case 40: keycode = 40; break;
}
}, false);
window.addEventListener("keyup", function(e){
switch(e.keyCode){
case 37: keycode = NaN; break;
case 38: keycode = NaN; break;
case 39: keycode = NaN; break;
case 40: keycode = NaN; break;
}
}, false);
init();*{
margin:0;
padding:0;
}
#canvas{
background:url("../img/seamles_space.jpg");
animation: mainBack 5s linear infinite;
animation-play-state:paused;
background: black;
display: block;
position:relative;
margin:50px auto;
}<body>
<script src="js/main.js"></script>
</body>
var ctx;
var hero;
var enemy;
var beams = [];
var beam_hitting = false;
var continuous_hit = false;
var count = 0;
var canvas_w = 500, canvas_y= 700;
var keycode = NaN;
var laser_degree = 200;
function init(){
createCanvas();
createMainHero();
createEnemy();
draw();
mainRoutine();
}
function mainRoutine(){
count++;
ctx.clearRect(0, 0, canvas_w, canvas_y);
// laserTest();
hero.move(keycode);
enemyUpdate();
// hero.setBullets();
// hero.moveBullets();
draw();
window.setTimeout(mainRoutine, 7);
}
function createCanvas(){
var canvas = document.createElement("canvas");
canvas.id = "canvas";
canvas.width = canvas_w;
canvas.height = canvas_y;
document.body.appendChild(canvas);
}
function createMainHero(){
hero = new Hero();
}
function createEnemy(){
enemy = new Enemy;
}
function Hero(){
this.w = this.h = 25,
this.x = canvas_w/2 - this.w/2,
this.y = canvas_y - this.h,
this.dx = this.dy = 2.5;
this.bullets = [];
this.move = function(key){
switch(key){
case 37: if (hero.x > 0) {hero.x -= this.dx;} break;
case 38: if (hero.y > 0) {hero.y -= this.dy;} break;
case 39: if (hero.x < canvas_w - hero.w) {hero.x += this.dx;} break;
case 40: if (hero.y < canvas_y - hero.h) {hero.y += this.dy;} break;
}
};
this.setBullets = function(){
if (count % 20 === 0) {
var w = h = 8;
var dx = dy = 5;
var x = hero.x + hero.w/2 - w/2;
var y = hero.y;
hero.bullets.push({x: x, y: y, w: w, h: h, dx: dx, dy: dy, moving:true});
}
};
this.moveBullets = function(){
for (var i = 0; i < this.bullets.length; i++) {
if (this.bullets[i].y < 0) {
this.bullets[i].moving = false;
continue;
}
if (this.bullets[i].moving === false) {
continue;
}
this.bullets[i].y -= this.bullets[i].dy;
if (this.bullets[i].y < -100) {this.bullets.splice(i, 1)}
}
}
}
function Enemy(){
this.w = this.h = 25;
this.x = canvas_w/2 - this.w/2;
this.y = 50;
this.bullets = [];
this.moving = false;
this.move_to = 0;
this.bullets_count = 0;
this.bullets_angle = 0;
this.current_skill = 1;
this.barrage_count = 0;
this.skill = function(){
enemySkill();
};
}
function enemyUpdate(){
function move(){
function changeDirection(){
var options = ["left", "right", "up", "down"];
var id;
if (enemy.x <= 50) {id = options.indexOf("left"); options.splice(id, 1);}
if (enemy.x >= 450) {id = options.indexOf("right");options.splice(id, 1);}
if (enemy.y <= 50) {id = options.indexOf("up");options.splice(id, 1);}
if (enemy.y >= 200) {id = options.indexOf("down");options.splice(id, 1);}
var rand = Math.floor(Math.random() * options.length);
enemy.moving = options[rand];
switch(enemy.moving){
case "left": enemy.move_to = enemy.x - 150 ; break;
case "right": enemy.move_to = enemy.x + 150 ; break;
case "up": enemy.move_to = enemy.y - 150 ; break;
case "down": enemy.move_to = enemy.y + 150 ; break;
}
} /* end changeDirection() */
if (count % 800 === 0) {changeDirection(); console.log("changing");}
switch(enemy.moving){
case "left": if (enemy.x > 50 && enemy.x > enemy.move_to) {enemy.x -= 0.5;} break;
case "right": if (enemy.x < 450 && enemy.x < enemy.move_to) {enemy.x += 0.5;} break;
case "up": if (enemy.y > 50 && enemy.y > enemy.move_to) {enemy.y -= 0.5; } break;
case "down": if (enemy.y < 200 && enemy.y < enemy.move_to) {enemy.y += 0.5; } break;
}
} /* end move()*/
move();
enemy.skill();
} /* end enemyUpdate() */
function enemySkill(){
// console.log("enemy skill");
function setBullets(){
var prev_status = enemy.bullets_count === 0 ? 500 : enemy.bullets[enemy.bullets.length - 1]["radius"];
if (prev_status >25) {
// console.log("bullets set");
var center_x = enemy.x + enemy.w/2;
var center_y = enemy.y + enemy.h/2;
var radius = 20;
var ceil = enemy.bullets.length === 0 ? 0 : enemy.bullets.length -1;
for (var angle = enemy.bullets_angle, i= ceil; angle < enemy.bullets_angle + 360; angle += 40, i++ ) {
// console.log("i: " + i);
var radian = angle * Math.PI / 180;
var set_x = center_x + radius * Math.cos(radian);
var set_y = center_y + radius * Math.sin(radian);
// console.log("angle: " + /angle + "set_x: " + set_x + "set_y: " + set_y);
enemy.bullets.push({"x": set_x, "y": set_y, "moving": true, "radius": radius, "center_x": center_x, "center_y": center_y, "w": 25, "h": 25, "radian": radian});
if (enemy.bullets_count === 0) {enemy.bullets_count++;}
// console.log(enemy.bullets[0][i]["x"]);
}
enemy.bullets_angle += 10;
enemy.barrage_count ++;
if (enemy.barrage_count % 100 === 0) {
enemy.current_skill = 0;
}
}
} /* end setBullets */
function moveBullets(){
if (count % 4 ===0) {
for (var i = 0; i < enemy.bullets.length; i++) {
if (enemy.bullets[i]["moving"] === true) {
var radian = enemy.bullets[i]["radian"];
var center_x = enemy.bullets[i]["center_x"];
var center_y = enemy.bullets[i]["center_y"];
enemy.bullets[i]["radius"] += 5;
var radius = enemy.bullets[i]["radius"];
var set_x = center_x + radius * Math.cos(radian);
var set_y = center_y + radius * Math.sin(radian);
// console.log(set_y);
enemy.bullets[i]["x"] = set_x;
enemy.bullets[i]["y"] = set_y;
if (enemy.bullets[i]["x"] < -100 || enemy.bullets[i]["x"] > canvas_w + 100 || enemy.bullets[i]["y"] < -100 || enemy.bullets[i]["y"] > canvas_y + 100 ) {
// enemy.bullets[i]["moving"] = false;
enemy.bullets.splice(i, 1);
}
}
}
}
}
if (enemy.current_skill === 1) {
setBullets();
}
moveBullets();
}
function draw(){
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
//hero
//ctx.fillStyle = "blue";
//ctx.fillRect(hero.x, hero.y ,hero.w, hero.h);
//enemy
//ctx.fillStyle = "red";
//ctx.fillRect(enemy.x, enemy.y ,enemy.w, enemy.h);
//heroの弾
ctx.fillStyle = "blue";
for (var i = 0; i < hero.bullets.length; i++) {
if (hero.bullets[i].moving === false) {
continue;
}
ctx.fillRect(hero.bullets[i].x, hero.bullets[i].y ,hero.bullets[i].w, hero.bullets[i].h);
}
//敵の弾
ctx.fillStyle = "red";
for (var i = 0; i < enemy.bullets.length; i++) {
ctx.fillStyle = "green";
if (enemy.bullets[i]["moving"] === false) {
continue;
}
ctx.beginPath();
ctx.arc(enemy.bullets[i]["x"], enemy.bullets[i]["y"], 15, 0, 2 * Math.PI, false);
ctx.closePath();
ctx.fill();
}
}
window.addEventListener("keydown", function(e){
switch(e.keyCode){
case 37: keycode = 37; break;
case 38: keycode = 38; break;
case 39: keycode = 39; break;
case 40: keycode = 40; break;
}
}, false);
window.addEventListener("keyup", function(e){
switch(e.keyCode){
case 37: keycode = NaN; break;
case 38: keycode = NaN; break;
case 39: keycode = NaN; break;
case 40: keycode = NaN; break;
}
}, false);
init();一开始没有问题,但随后一些子弹就会开始表现得很奇怪,就好像在某个时刻之后一样。
发布于 2017-03-10 18:09:38
原因是在for循环中删除了一个项目,导致跳过一个项目符号,更准确地说,这一行:
enemy.bullets.splice(i, 1);我建议一种不同的方法-构建一个只包含活动项目符号(moving===true)的新数组,然后在循环之后用新的数组替换该数组。
例如:
function moveBullets(){
if (count % 4 ===0) {
// will hold active bullets in current pass
var newBullets = [];
for (var i = 0; i < enemy.bullets.length; i++) {
// cut code for clarity
if (!(enemy.bullets[i].x < -100 || enemy.bullets[i].x > canvas_w + 100 ||
enemy.bullets[i].y < -100 || enemy.bullets[i].y > canvas_y + 100 )) {
newBullets.push(enemy.bullets[i]);
}
}
// replace array with only active bullets
enemy.bullets = newBullets;
}
}新数组将简单地保存对现有活动项目符号的引用。
发布于 2017-03-11 19:29:06
粒子和对象池。
在处理粒子系统时,创建新数组不是一个好策略。这就是有效的子弹。
删除的每个项目需要由GC (垃圾收集)清理,添加的每个项目需要创建并需要内存分配。这些开销可能会对游戏产生负面影响。GC可能会导致动画在随机时间挂起。
为了一致,平滑的动画,你应该瞄准零分配和删除(这是可能的,因为asm.js和web程序集不分配或删除运行中的模块)。
对象池
在vanilla JS中,这是可能的,但是代码对于这个答案来说太复杂了。下一个最好的解决方案是使用对象池。
由于项目符号最初是按常规创建的,但当不再需要某个项目符号时,您可以将其移动到另一个数组(称为池)中,下次需要项目符号时,您首先检查池中是否有可用的项目符号,并使用该项目符号,而不是创建新对象。
这确保了GC只需要清理不断变化的数组大小,而不是每个项目符号使用的数据。
var bullets = [];
var bulletPool = [];
function createBullet(x,y,dir,whatNot){
var newBullet;
if(bulletPool.length > 0){
newBullet = bulletPool.pop(); // reuse old bullet memory
newBullet.x = x;
newBullet.y = y;
newBullet.dir = dir;
newBullet.whatNot = whatNot;
newBullet.active = true;
}else{
newBullet = {x,y,dir,whatNot,active:true}; // create only if needed
}
return newBullet;
}
function fire(){
bullets.push(createBullet(10,10,0,0)); /// add a bullet
}在您的代码中,当不再需要项目符号时,只需将active标志设置为false。在游戏循环结束时,移除所有未激活的子弹。
删除项目符号时,只需将其从项目符号阵列移动到池中即可
function cleanBulletsArray(){
for(var i = 0; i < bullets.length; i ++){
if(!bullets[i].active){
bulletPool.push(bullets.splice(i--,1)[0]);
}
}
}降水阵列
之所以这样叫,是因为活动项落在数组的底部
更好的方法是只使用一个数组。当项目符号处于非活动状态时,它将保留在数组中,但当您迭代数组时,活动项目符号将替换为非活动项目符号,活动项目符号将向下移动,而不活动项目符号将向上移动。每次迭代最多有一个交换。您还可以对活动子弹的数量进行计数。当您需要新的项目符号时,您只需在索引count +1处重置项目符号的属性,然后将count增加1。
这种方法(如果您在游戏开始时预先分配所有的子弹)没有GC和内存开销,并且比创建、销毁和数组替换方法要快得多。
https://stackoverflow.com/questions/42711667
复制相似问题