背景
我正在写一个基本的目标和移动人工智能。有两种类型的“生物”:活动fauna
和非活动目标flora
。
问题
我的人工智能(附在fauna
上)首先针对的是flora
,但它只“看到”了一些flora
。当AI看不到flora
时,AI会绕圈旋转,似乎是随机的弹跳,即使还剩下flora
。
是什么导致只看到一些flora
呢?为什么fauna
在停止寻找flora
之后似乎漫无目的地反弹呢?为什么在代码运行一段时间后,fauna
就会聚集起来呢?是什么导致flora
没有被看到?
如果您需要任何额外的信息,请询问。
我试图修复它
我第一次尝试解决这个问题取得了一些成功,但没有完全解决这个问题。这是我用对象而不是数组重写代码的时候。一旦我这样做,目标的工作,但一些fauna
将无休止地旋转。
然后我意识到,生物旋转很可能无法与getAngle函数的返回相媲美。生物的旋转可能相当于回击角,但并不相等(例如。~= 720 but,但360 but=720 but)。在我修复这个问题之后,它似乎工作了一段时间,但是当我用更多的检查和更长的时间运行测试时,我发现了这些问题。
我真的不知道什么会导致这样的问题,但我很想知道。谢谢您的帮助:)
代码解释
该代码可在线访问:http://codepen.io/CKH4/pen/wgZqgL/。
在我的代码开始时,我有一些Object
原型扩展,允许我使用像数组这样的对象。它们大致相当于它们的Array
对应项。我不认为这些是问题的根源,但它们是程序运行所必需的。
Object.prototype.filter = function(fn) {
let ob = this, keep = {},
k = Object.keys(ob);
for (let i = 0; i < k.length; i++) {
if (fn(k[i], ob[k[i]]))
keep[k[i]] = ob[k[i]];
}
return keep;
}
Object.prototype.forEach = function(fn) {
let ob = this, k = Object.keys(ob);
for (let i = 0; i < k.length; i++)
fn(k[i], ob[k[i]]);
}
Object.prototype.reduce = function(test, initialValue = null) {
let ob = this, k = Object.keys(ob),
accumulator = initialValue || ob[k[0]],
i = (initialValue === null) ? 1 : 0;
for (; i < k.length; i++)
accumulator = test(accumulator, k[i], ob[k[i]], ob);
return accumulator;
}
接下来,我有一些帮助函数来操作“生物”。
// calculates the distance between two creatures by taking their [pos] as inputs
function getDist(p1, p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}
// calculates the angle from point1 to point2
function getAngle(p1, p2) {
return (Math.atan2(p2.y - p1.y, p2.x - p1.x) / Math.PI * 180 + 360) % 360;
}
// moves the creature in the direction they are facing
function move() {
this.pos.x += this.speed * Math.cos(this.direction * Math.PI / 180);
this.pos.y += this.speed * Math.sin(this.direction * Math.PI / 180);
}
// rotates the creature towards the angle input by the turn speed of the creature
function rotateTowards(angle) {
this.direction += Math.sign(angle - this.direction) * this.turnSpeed;
this.direction = this.direction % 360;
}
// rotates the creature by the angle provided
function rotateBy(angle) {
this.direction += angle;
this.direction = this.direction % 360;
}
现在我有了目标定位功能。它从运行ai的生物开始,然后使用生物的一个对象来查看,接下来是当前只能找到最近的一个模式,最后是一个过滤函数,让目标查找者只查看菌群。
代码首先过滤掉那些不在AI视线中的代码。这就是我期望的问题所在。接下来,它应用输入过滤器(因此在我的例子中只有flora
保留)。最后,只有在生物的对象中还剩下任何东西的情况下,代码才会将对象简化为最近的生物。如果在生物的对象中没有任何剩余的东西,它会返回一个未定义的数组。
function getTarget(c, of, mode = `nearest`, filter) {
let first;
// filter so its only the ones in view
of = of.filter((k, t) => {
return Math.abs(getAngle(c.pos, t.pos) - c.direction) < c.viewAngle / 2;
});
// filter for target type; eg. only return flora
if (filter)
of = of.filter(filter);
if (Object.keys(of).length) {
first = of[Object.keys(of)[0]];
if (mode == `nearest`) {
return of.reduce((acc, k, cur) => {
let dist = getDist(c.pos, cur.pos);
if (dist < acc[0])
return [dist, k];
else
return acc;
}, [getDist(c.pos, first.pos), first]);
}
}
else
return [undefined, undefined];
}
最后,我有一个通用的AI,它将目标系统与运动代码联系在一起。如果有目标,生物就会转向并向目标移动。如果目标距离生物在5个像素以内,该生物就会摧毁目标。否则,该生物转向一个积极的方向,“寻找”另一个目标。
function findfood() {
let target = getTarget(this, ob, `nearest`, (k, c) => c.type == `flora`);
this.target = target[1];
if (ob[this.target]) {
rotateTowards.call(this, getAngle(this.pos, ob[this.target].pos));
if (getDist(this.pos, ob[this.target].pos) > 5)
move.call(this);
else {
delete ob[this.target];
}
}
else
rotateBy.call(this, this.turnSpeed);
}
在这里,我生成一个具有随机放置的flora
和fauna
的对象。我使用的是ID系统,使用的是Object
而不是Array
。所有的生物都存储在一个动态的全局对象中。
ob = {};
for (let i = 20; i > 0; i--) {
let id = Math.floor(Math.random() * 1000000000),
type = (Math.random() > .2 ? `flora` : `fauna`);
ob[id] = {
type: type,
pos: { x: Math.floor(Math.random() * canvas.width), y: Math.floor(Math.random() * canvas.height) },
direction: Math.random() * 360
}
if (type == `fauna`) {
ob[id].ai = findfood;
ob[id].viewAngle = 90;
ob[id].speed = .8;
ob[id].turnSpeed = 1.6;
}
}
然后,我在一个setInterval中运行模拟,如果该生物有人工智能功能,它会调用它。问题也不在这里。
let fixedUpdate = setInterval(function() {
Object.keys(ob).forEach((ck) => {
let c = ob[ck];
if (c && c.ai)
c.ai.apply(c);
});
}, 1000 / 60);
这是我用来显示它的代码。这只是基本的画布材料,所以问题肯定不在这里。
let draw = () => {
// clear canvas
ctx.putImageData(emptyCanvas, 0, 0);
Object.keys(ob).forEach((ck) => {
let c = ob[ck];
if (c.type == 'flora')
ctx.fillStyle = '#22cc33';
else if (c.type == 'fauna') {
ctx.fillStyle = '#0066ee';
ctx.beginPath();
ctx.moveTo(c.pos.x, c.pos.y);
// ctx.lineTo(c.pos.x + 100, c.pos.y - 50);
// ctx.lineTo(c.pos.x + 100, c.pos.y + 50);
ctx.lineTo(c.pos.x, c.pos.y);
ctx.fill();
ctx.beginPath();
ctx.arc(c.pos.x, c.pos.y, 100, (c.direction - c.viewAngle / 2) * Math.PI / 180, (c.direction + c.viewAngle / 2) * Math.PI / 180);
ctx.fill();
}
else
ctx.fillStyle = '#424242';
ctx.beginPath();
ctx.arc(c.pos.x, c.pos.y, 10, 0, 2 * Math.PI);
ctx.fill();
});
requestAnimationFrame(draw);
}
draw();
下面是嵌入的代码:
console.clear();
Object.prototype.filter = function(fn) {
let ob = this, keep = {},
k = Object.keys(ob);
for (let i = 0; i < k.length; i++) {
if (fn(k[i], ob[k[i]]))
keep[k[i]] = ob[k[i]];
}
return keep;
}
Object.prototype.forEach = function(fn) {
let ob = this, k = Object.keys(ob);
for (let i = 0; i < k.length; i++)
fn(k[i], ob[k[i]]);
}
Object.prototype.reduce = function(test, initialValue = null) {
let ob = this, k = Object.keys(ob),
accumulator = initialValue || ob[k[0]],
i = (initialValue === null) ? 1 : 0;
for (; i < k.length; i++)
accumulator = test(accumulator, k[i], ob[k[i]], ob);
return accumulator;
}
function getDist(p1, p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}
function getAngle(p1, p2) {
return (Math.atan2(p2.y - p1.y, p2.x - p1.x) / Math.PI * 180 + 360) % 360;
}
function move() {
this.pos.x += this.speed * Math.cos(this.direction * Math.PI / 180);
this.pos.y += this.speed * Math.sin(this.direction * Math.PI / 180);
}
function rotateTowards(angle) {
this.direction += Math.sign(angle - this.direction) * this.turnSpeed;
this.direction = this.direction % 360;
}
function rotateBy(angle) {
this.direction += angle;
this.direction = this.direction % 360;
}
function getTarget(c, of, mode = `nearest`, filter) {
let first;
// filter so its only the ones in view
of = of.filter((k, t) => {
return Math.abs(getAngle(c.pos, t.pos) - c.direction) < c.viewAngle / 2;
});
// filter for target type; eg. only return flora
if (filter)
of = of.filter(filter);
if (Object.keys(of).length) {
first = of[Object.keys(of)[0]];
if (mode == `nearest`) {
return of.reduce((acc, k, cur) => {
let dist = getDist(c.pos, cur.pos);
if (dist < acc[0])
return [dist, k];
else
return acc;
}, [getDist(c.pos, first.pos), first]);
}
}
else
return [undefined, undefined];
}
function findfood() {
let target = getTarget(this, ob, `nearest`, (k, c) => c.type == `flora`);
this.target = target[1];
if (ob[this.target]) {
rotateTowards.call(this, getAngle(this.pos, ob[this.target].pos));
if (getDist(this.pos, ob[this.target].pos) > 5)
move.call(this);
else {
delete ob[this.target];
}
}
else
rotateBy.call(this, this.turnSpeed);
}
ob = {};
for (let i = 20; i > 0; i--) {
let id = Math.floor(Math.random() * 1000000000),
type = (Math.random() > .2 ? `flora` : `fauna`);
ob[id] = {
type: type,
pos: { x: Math.floor(Math.random() * canvas.width), y: Math.floor(Math.random() * canvas.height) },
direction: Math.random() * 360
}
if (type == `fauna`) {
ob[id].ai = findfood;
ob[id].viewAngle = 90;
ob[id].speed = .8;
ob[id].turnSpeed = 1.6;
}
}
console.log(ob);
let ctx = canvas.getContext(`2d`);
let emptyCanvas = ctx.getImageData(0,0,canvas.width,canvas.height);
let draw = () => {
// clear canvas
ctx.putImageData(emptyCanvas, 0, 0);
Object.keys(ob).forEach((ck) => {
let c = ob[ck];
if (c.type == 'flora')
ctx.fillStyle = '#22cc33';
else if (c.type == 'fauna') {
ctx.fillStyle = '#0066ee';
ctx.beginPath();
ctx.moveTo(c.pos.x, c.pos.y);
// ctx.lineTo(c.pos.x + 100, c.pos.y - 50);
// ctx.lineTo(c.pos.x + 100, c.pos.y + 50);
ctx.lineTo(c.pos.x, c.pos.y);
ctx.fill();
ctx.beginPath();
ctx.arc(c.pos.x, c.pos.y, 100, (c.direction - c.viewAngle / 2) * Math.PI / 180, (c.direction + c.viewAngle / 2) * Math.PI / 180);
ctx.fill();
}
else
ctx.fillStyle = '#424242';
ctx.beginPath();
ctx.arc(c.pos.x, c.pos.y, 10, 0, 2 * Math.PI);
ctx.fill();
});
requestAnimationFrame(draw);
}
draw();
let fixedUpdate = setInterval(function() {
Object.keys(ob).forEach((ck) => {
let c = ob[ck];
if (c && c.ai)
c.ai.apply(c);
})
}, 1000 / 60);
body {
margin: 0;
}
<canvas height="1000" id="canvas" width="1000"></canvas>
发布于 2017-02-18 23:48:13
我在代码中发现了错误。在getTarget()
中,当我获得第一个可能的目标(of[0]
)时,我首先存储的是对该生物的引用,而不是该生物的ID。
要修复它,我必须通过删除对对象的引用来存储ID。我改变了:
first = of[Object.keys(of)[0]];
至:
first = Object.keys(of)[0];
这在getTarget()
的其余代码中造成了问题,因为我试图将属性附加到该生物的对象,而不是生物ID。
}, [getDist(c.pos, first.pos), first]);
至:
}, [getDist(c.pos, of[first].pos), first]);
这给了我完成的getTarget()
:
function getTarget(c, of, mode = `nearest`, filter) {
let first;
// filter so its only the ones in view
of = of.filter((k, t) => {
return Math.abs(getAngle(c.pos, t.pos) - c.direction) < c.viewAngle / 2;
});
// filter for target type; eg. only return flora
if (filter)
of = of.filter(filter);
if (Object.keys(of).length) {
first = Object.keys(of)[0];
if (mode == `nearest`) {
return of.reduce((acc, k, cur) => {
let dist = getDist(c.pos, cur.pos);
if (dist < acc[0])
return [dist, k];
else
return acc;
}, [getDist(c.pos, of[first].pos), first]);
}
}
else
return [undefined, undefined];
}
https://stackoverflow.com/questions/42287146
复制相似问题