Sasha05082002, никак, вот решил еще что-то написать но вовремя остановился
получилось весьма интересно, особая гордость XBOT patrol - поворот по кратчайшему пути, движение по определенному маршруту (не)определенное число раз
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>TANK</title></head>
<body>
<style>
canvas { outline: 1px solid gray; }
canvas:hover { outline-color: red; }
pre { width: 600px; font: 12px/12px monospace; outline: 1px solid gray; margin: 0; }
</style>
Control: Mouse + AWDS (click to focus)
<pre></pre>
<canvas width="600" height="300" tabindex="1"></canvas>
<script>
/* Class Tank */
// constructor
function Tank(config) {
for (var prop in config) {
this[prop] = config[prop];
}
}
// public members
Tank.prototype = {
constructor: Tank,
// default properties
name: 'TANK',
crew: 'empty',
status: 1,
pivotX: 100,
pivotY: 100,
headWidth: 50,
headHeight: 40,
headArrow: 10,
headColor: '#aaaaaa',
headAngle: 0,
bodyWidth: 100,
bodyHeight: 60,
bodyArrow: 10,
bodyColor: '#cccccc',
bodyAngle: 0,
stepAngle: Math.PI / 180,
stepFactor: 1,
// move methods
bodyForward: function () {
this.pivotX += Math.cos(this.bodyAngle) * this.stepFactor;
this.pivotY += Math.sin(this.bodyAngle) * this.stepFactor;
return this;
},
bodyBackward: function () {
this.pivotX -= Math.cos(this.bodyAngle) * this.stepFactor;
this.pivotY -= Math.sin(this.bodyAngle) * this.stepFactor;
return this;
},
bodyLeft: function () {
var angle = this.bodyAngle - this.stepAngle;
this.bodyAngle = (angle < 0) ? angle + 2 * Math.PI : angle;
return this;
},
bodyRight: function () {
var angle = this.bodyAngle + this.stepAngle;
this.bodyAngle = (angle < 2 * Math.PI) ? angle : angle - 2 * Math.PI;
return this;
},
headLeft: function () {
var angle = this.headAngle - this.stepAngle;
this.headAngle = (angle < 0) ? angle + 2 * Math.PI : angle;
return this;
},
headRight: function () {
var angle = this.headAngle + this.stepAngle;
this.headAngle = (angle < 2 * Math.PI) ? angle : angle - 2 * Math.PI;
return this;
},
// frame methods
update: function () {
Tank.crew[this.crew].call(this);
return this;
},
render: function () {
var x1, y1, x2, y2;
// body
Context.save();
Context.translate(this.pivotX, this.pivotY);
Context.rotate(this.bodyAngle);
Context.translate(-this.pivotX, -this.pivotY);
x1 = this.pivotX - this.bodyWidth / 2;
y1 = this.pivotY - this.bodyHeight / 2;
x2 = x1 + this.bodyWidth;
y2 = y1 + this.bodyHeight;
// body polygon
Context.beginPath();
Context.moveTo(x1, y1);
Context.lineTo(x2, y1);
Context.lineTo(x2 + this.bodyArrow, this.pivotY);
Context.lineTo(x2, y2);
Context.lineTo(x1, y2);
Context.fillStyle = this.bodyColor;
Context.fill();
Context.restore();
// head
Context.save();
Context.translate(this.pivotX, this.pivotY);
Context.rotate(this.headAngle);
Context.translate(-this.pivotX, -this.pivotY);
x1 = this.pivotX - this.headWidth / 2;
y1 = this.pivotY - this.headHeight / 2;
x2 = x1 + this.headWidth;
y2 = y1 + this.headHeight;
// head polygon
Context.beginPath();
Context.moveTo(x1, y1);
Context.lineTo(x2, y1);
Context.lineTo(x2 + this.headArrow, this.pivotY);
Context.lineTo(x2, y2);
Context.lineTo(x1, y2);
Context.fillStyle = this.headColor;
Context.fill();
Context.restore();
// status text
if (this.status) {
Context.fillText(this.name + '(' + this.crew + ')', this.pivotX, this.pivotY);
}
return this;
},
inform: function (x, y, Context) {
Context.fillText(this.name + ':' +
' X ' + this.pivotX.toFixed(2) +
' Y ' + this.pivotX.toFixed(2) +
' bA ' + this.bodyAngle.toFixed(5) +
' hA ' + this.headAngle.toFixed(5) +
' sA ' + this.stepAngle.toFixed(5) +
' sF ' + this.stepFactor.toFixed(5), x, y);
return this;
}
};
// static members
// array of instances
Tank.base = [];
// crew methods
Tank.crew = {
empty: function () {
//console.log(this.name, this.crew);
},
player: function () {
// input control
if (Input.keys.left) {
this.bodyLeft();
}
if (Input.keys.forward) {
this.bodyForward();
}
if (Input.keys.right) {
this.bodyRight();
}
if (Input.keys.backward) {
this.bodyBackward();
}
// mouse control
if (Mouse.follow) {
Mouse.angle = Tank.math.hypotAngle(Mouse.x - this.pivotX, Mouse.y - this.pivotY);
var angle = Tank.math.deltaAngle(Mouse.angle, this.headAngle);
if (angle < 0) {
this.headLeft();
} else {
this.headRight();
}
if (Math.abs(angle) - this.stepAngle < 0) {
Mouse.follow = 0;
}
}
},
patrol: function () {
// states: stop 0, init 1, turn 2, move 3
if (this.routeState == 0) {
//console.log('state', 0);
if (this.routeIndex < this.routeArray.length) {
this.routeState = 1;
} else if (this.routeRound) {
if (this.routeRound > 0) {
this.routeRound--;
}
this.routeIndex = 0;
}
} else if (this.routeState == 1) {
//console.log('state', 1);
this.routePoint = this.routeArray[this.routeIndex++];
var deltaX = this.routePoint[0] - this.pivotX;
var deltaY = this.routePoint[1] - this.pivotY;
this.routeHypot = Tank.math.hypot(deltaX, deltaY);
this.routeAngle = Tank.math.hypotAngle(deltaX, deltaY);
this.routeState = 2;
} else if (this.routeState == 2) {
//console.log('state', 2);
var angle = Tank.math.deltaAngle(this.routeAngle, this.bodyAngle);
if (angle < 0) {
this.bodyLeft().headLeft();
} else {
this.bodyRight().headRight();
}
if (Math.abs(angle) - this.stepAngle < 0) {
this.routeState = 3;
}
} else if (this.routeState == 3) {
//console.log('state', 3);
this.bodyForward();
this.routeHypot -= this.stepFactor;
if (this.routeHypot < 0) {
this.routeState = 0;
}
} else {
this.routeIndex = 0;
this.routeState = 0;
}
}
};
// math methods
Tank.math = {
hypot: function (deltaX, deltaY) {
return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
},
hypotAngle: function (deltaX, deltaY) {
// delta = point - pivot
var angle = Math.atan2(deltaY, deltaX);
if (angle < 0) {
angle += 2 * Math.PI;
}
return angle;
},
deltaAngle: function (hypotAngle, pivotAngle) {
var angle;
if (hypotAngle < pivotAngle) {
angle = pivotAngle - hypotAngle;
if (angle < Math.PI) {
angle = -angle;
}
} else {
angle = hypotAngle - pivotAngle;
if (angle > Math.PI) {
angle = -angle;
}
}
// -angle - left, +angle - right, |angle| - delta
return angle;
}
};
/* Input & Mouse & Output */
var Input = {
// awds
codes: { 65: 'left', 87: 'forward', 68: 'right', 83: 'backward' },
// arrows
//codes: { 37: 'left', 38: 'forward', 39: 'right', 40: 'backward' },
keys: { left: 0, forward: 0, right: 0, backward: 0 },
inform: function (x, y, Context) {
Context.fillText('INPUT:' +
' L ' + this.keys.left +
' F ' + this.keys.forward +
' R ' + this.keys.right +
' B ' + this.keys.backward, x, y);
}
};
var Mouse = {
x: 0,
y: 0,
angle: 0,
follow: 0,
inform: function (x, y, Context) {
Context.fillText('MOUSE:' +
' X ' + this.x.toFixed(2) +
' Y ' + this.y.toFixed(2) +
' A ' + this.angle.toFixed(5) +
' F ' + this.follow, x, y);
}
};
var Output = {
setContext: function (context, pre) {
if (pre) {
this.context = this;
this.nodes = pre.childNodes;
var count = 25;
while (count--) {
pre.appendChild(document.createTextNode(''));
}
} else {
this.context = context;
}
},
fillText: function (text, x, y) {
this.nodes[y / 12].nodeValue = ' ' + text + '\n';
}
};
/* Canvas & Context */
var Canvas = document.querySelector('canvas');
var Context = Canvas.getContext('2d');
Context.font = '12px monospace';
Context.fillText('Touch Me', Canvas.width / 2, Canvas.height / 2);
Output.setContext(Context, document.querySelector('pre'));
Canvas.addEventListener('mouseenter', onMouseenter);
Canvas.addEventListener('mouseleave', onMouseleave);
Canvas.addEventListener('mousemove', onMousemove);
Canvas.addEventListener('keydown', onKeydown);
Canvas.addEventListener('keyup', onKeyup);
/* Event Handlers */
function onMouseenter(e) {
Canvas.focus();
onAnimationFrame.id = requestAnimationFrame(onAnimationFrame);
}
function onMouseleave(e) {
cancelAnimationFrame(onAnimationFrame.id);
}
function onMousemove(e) {
Mouse.follow = 1;
Mouse.x = e.offsetX;
Mouse.y = e.offsetY;
}
function onKeydown(e) {
var code = Input.codes[e.keyCode];
if (code) {
Input.keys[code] = 1;
}
}
function onKeyup(e) {
var code = Input.codes[e.keyCode];
if (code) {
Input.keys[code] = 0;
}
}
function onAnimationFrame() {
Context.clearRect(0, 0, Canvas.width, Canvas.height);
var tank, tanks = Tank.base, index = tanks.length;
while (index--) {
tank = tanks[index];
tank.update().render();
tank.inform(5, 36 + 12 * index, Output.context);
}
Input.inform(5, 12, Output.context);
Mouse.inform(5, 24, Output.context);
onAnimationFrame.id = requestAnimationFrame(onAnimationFrame);
}
/* Tank Base Init */
Tank.base.push(
new Tank({
name: 'RISE',
crew: 'player',
pivotX: 250,
pivotY: 150,
headColor: '#666633',
headAngle: 5 / 3 * Math.PI,
bodyColor: '#999966',
bodyAngle: 5 / 3 * Math.PI
}),
new Tank({
name: 'XBOT',
crew: 'patrol',
pivotX: 400,
pivotY: 200,
headColor: '#663300',
headAngle: 5 / 6 * Math.PI,
bodyColor: '#996633',
bodyAngle: 5 / 6 * Math.PI,
// extra
routeArray: [[500, 70], [100, 200], [520, 230]],
routeRound: 1
}),
new Tank()
);
</script>
</body>
</html>