Javascript-форум (https://javascript.ru/forum/)
-   Opera, Safari и др. (https://javascript.ru/forum/css-html-browser/)
-   -   Помогите! Не могу сделать поворот башни на танке! (https://javascript.ru/forum/css-html-browser/68771-pomogite-ne-mogu-sdelat-povorot-bashni-na-tanke.html)

Sasha05082002 07.05.2017 18:10

Помогите! Не могу сделать поворот башни на танке!
 
Вложений: 1
Хочу сделать игру на js, дошло дело до башни и тут у меня появилась проблема, мне надо сделать так чтобы башня поварачивалась за курсором с заданой скорость, но у меня не получается. :(
Весь инет перешарил, но ничего не нашёл. :(
Пожалуйста помогите:help: , буду очень благодарен.:thanks:
Вот мои коды(Полный код есть в архиве):
js/gameprocces.js
var Timer = {
    Work: null, Implement: 0,
    Update: 10, Time: 0,
    MilliSeconds: 0, Seconds: 0,
    Minutes: 0, Hours: 0, Day: 0}
var Key = [
    [87, "false", 0, "W"],
    [65, "false", 0, "A"],
    [83, "false", 0, "S"],
    [68, "false", 0, "D"]],
    Zoom = 1,
    CanvasGame = document.querySelector("#CanvasGame"),
    GC = CanvasGame.getContext("2d"),
    Display = {
        Center: {
            X: document.documentElement.clientWidth / 2,
            Y: document.documentElement.clientHeight / 2
        }
    },
    Angle = {
        Body: {Rotate: Math.PI, X: null, Y: null, route: null},
        Tower: {Rotate: Math.PI, X: null, Y: null, route: null, two: null},
        Mouse: {Rotate: Math.PI, X: null, Y: null, two: null}
    },
    Position = {
        Mouse: {X: null, Y: null,
            Click: {X: null, Y: null},
            From: {
                Display: {
                    Center: {
                        X: null,
                        Y: null
                    }
                }
            }
        }
    }
CanvasGame.width = document.documentElement.clientWidth;
CanvasGame.height = document.documentElement.clientHeight;
var GameProcess = {
    Work: null, Implement: 0,
    Update: 1
}
var Draw = {
    Work: null, Implement: 0,
    Update: 10
}

Timer.Work = setInterval(function() {
    Timer.Implement++;
    Timer.Time++;
    Timer.MilliSeconds++;
    if (Timer.MilliSeconds == 100) {Timer.Seconds++; Timer.Milliseconds = 0}
    if (Timer.Seconds == 60) {Timer.Minutes++; Timer.Seconds = 0}
    if (Timer.Minutes == 60) {Timer.Hours++; Timer.Minutes = 0}
    if (Timer.Hours == 60) {Timer.Days++; Timer.Hours = 0}
}, Timer.Update)

document.addEventListener('mousemove', function (e) {
    Position.Mouse.X = e.pageX;
    Position.Mouse.Y = e.pageY
}, false)
document.addEventListener('click', function (e) {
    Position.Mouse.Click.X = e.pageX;
    Position.Mouse.Click.Y = e.pageY
}, false)
document.addEventListener('keydown', function (e) {
    for (var I = 0; I < Key.length; I++) {
    if (e.keyCode == Key[I][0]) Key[I][1] = "true"}
}, false)
document.addEventListener('keyup', function (e) {
    for (var I = 0; I < Key.length; I++) {
    if (e.keyCode == Key[I][0]) Key[I][1] = "false"}
}, false)

GameProcess.Work = setInterval(function() {
    Position.Mouse.From.Display.Center.X = Display.Center.X - Position.Mouse.X;
    Position.Mouse.From.Display.Center.Y = Display.Center.Y - Position.Mouse.Y;
	if (Position.Mouse.From.Display.Center.X == 0) (Position.Mouse.From.Display.Center.Y > 0) ? (3 * Math.PI) / 2 : Math.PI / 2;
	Angle.Mouse.Rotate = Math.atan(Position.Mouse.From.Display.Center.Y / Position.Mouse.From.Display.Center.X);
	Angle.Mouse.Rotate += Math.PI/2;
	if (Position.Mouse.From.Display.Center.X < 0) Angle.Mouse.Rotate = Angle.Mouse.Rotate - Math.PI;
	Angle.Mouse.X = Math.sin(Angle.Mouse.Rotate);
	Angle.Mouse.Y = Math.cos(Angle.Mouse.Rotate);
    
	if (Key[1][1] == "true") Angle.Body.Rotate -= Math.PI/1024;
	if (Key[3][1] == "true") Angle.Body.Rotate += Math.PI/1014;
	if (Angle.Body.Rotate > Math.PI) Angle.Body.Rotate = -Angle.Body.Rotate + Math.PI/1024;
	if (Angle.Body.Rotate < -Math.PI) Angle.Body.Rotate = -Angle.Body.Rotate - Math.PI/1024;

	/*if (Angle.Tower.Rotate > Math.PI) Angle.Tower.Rotate = -Angle.Tower.Rotate + 0.03;
	if (Angle.Tower.Rotate < -Math.PI) Angle.Tower.Rotate = -Angle.Tower.Rotate - 0.03;*/
	/*if (Angle.Tower.Rotate != Angle.Mouse.Rotate) {
        if (Angle.Mouse.Rotate < Angle.Tower.Rotate && Angle.Mouse.Rotate > 0 && Angle.Tower.Rotate > 0) {Angle.Tower.Rotate -= 0.01}
        if (Angle.Mouse.Rotate > Angle.Tower.Rotate && Angle.Mouse.Rotate > 0 && Angle.Tower.Rotate > 0) {Angle.Tower.Rotate += 0.01}
        if (Angle.Mouse.Rotate < Angle.Tower.Rotate && Angle.Mouse.Rotate < 0 && Angle.Tower.Rotate < 0) {Angle.Tower.Rotate -= 0.01}
        if (Angle.Mouse.Rotate > Angle.Tower.Rotate && Angle.Mouse.Rotate < 0 && Angle.Tower.Rotate < 0) {Angle.Tower.Rotate += 0.01}

        if (Angle.Mouse.Rotate < Angle.Tower.Rotate && Angle.Mouse.Rotate < 0 && Angle.Tower.Rotate > 0) {Angle.Tower.Rotate += 0.01}
        if (Angle.Mouse.Rotate > Angle.Tower.Rotate && Angle.Mouse.Rotate > 0 && Angle.Tower.Rotate < 0) {Angle.Tower.Rotate -= 0.01}
        if (Angle.Mouse.Rotate < Angle.Tower.Rotate && Angle.Mouse.Rotate > 0 && Angle.Tower.Rotate < 0) {Angle.Tower.Rotate += 0.01}
        if (Angle.Mouse.Rotate > Angle.Tower.Rotate && Angle.Mouse.Rotate < 0 && Angle.Tower.Rotate > 0) {Angle.Tower.Rotate -= 0.01}
    }*/
    /*if (Angle.Tower.Rotate > 0 && Angle.Mouse.Rotate > 0) {Angle.Tower.two = Angle.Tower.Rotate - Angle.Mouse.Rotate}
    //if (Angle.Tower.Rotate < 0 && Angle.Mouse.Rotate < 0) {Angle.Tower.two = Angle.Tower.Rotate - Angle.Mouse.Rotate}
    //if (Angle.Tower.Rotate < 0 && Angle.Mouse.Rotate > 0) {Angle.Tower.two = Math.PI - Angle.Tower.Rotate + Angle.Mouse.Rotate}
    //if (Angle.Tower.Rotate > 0 && Angle.Mouse.Rotate < 0) {Angle.Tower.two = Math.PI - Angle.Tower.Rotate + Angle.Mouse.Rotate}*/
    //Angle.Tower.two = Angle.Tower.Rotate - ((Angle.Mouse.Rotate < 0) ? -Angle.Mouse.Rotate : Angle.Mouse.Rotate);
    //Angle.Tower.two = Angle.Tower.Rotate - Angle.Mouse.Rotate;
    if(Angle.Mouse.Rotate <= Math.PI && Angle.Mouse.Rotate >= 0) {Angle.Mouse.two = Math.abs(Angle.Tower.Rotate) + Math.abs(Angle.Mouse.Rotate)};
    if(Angle.Mouse.Rotate > -Math.PI && Angle.Mouse.Rotate < 0) {Angle.Mouse.two = -Math.abs(Angle.Tower.Rotate) + Math.abs(Angle.Mouse.Rotate)};
    //if(Angle.Mouse.Rotate > 0) {Angle.Mouse.two = Math.abs(Angle.Tower.Rotate) - Math.abs(Angle.Mouse.Rotate)};
    if(Angle.Mouse.two > 0) Angle.Tower.Rotate -= 0.01
    if(Angle.Mouse.two < 0) Angle.Tower.Rotate += 0.01
    //Angle.Mouse.two = Angle.Mouse.Rotate - Angle.Tower.Rotate;
	Angle.Mouse.X = Math.sin(Angle.Mouse.Rotate);
	Angle.Mouse.Y = Math.cos(Angle.Mouse.Rotate);
	Angle.Tower.X = Math.sin(Angle.Tower.Rotate);
	Angle.Tower.Y = Math.cos(Angle.Tower.Rotate);
	Angle.Body.X = Math.sin(Angle.Body.Rotate);
	Angle.Body.Y = Math.cos(Angle.Body.Rotate);
    GameProcess.Implement++;
}, GameProcess.Update)

Draw.Work = setInterval(function() {
    Draw.Implement++;
    GC.clearRect(0, 0, document.documentElement.clientWidth, document.documentElement.clientHeight);

    GC.beginPath();
    GC.arc(Display.Center.X, Display.Center.Y, 1, 0, Math.PI * 2, false);
    GC.stroke();

    GC.beginPath();
    GC.lineTo(Display.Center.X, Display.Center.Y);
    GC.lineTo(Display.Center.X - 100 * Angle.X, Display.Center.Y + 100 * Angle.Y);
    GC.stroke();

    GC.beginPath();
    GC.lineTo(Display.Center.X, Display.Center.Y);
    GC.lineTo(Display.Center.X - 100 * Angle.Body.X, Display.Center.Y + 100 * Angle.Body.Y);
    GC.stroke();

    GC.beginPath();
    GC.lineTo(Display.Center.X, Display.Center.Y);
    GC.lineTo(Display.Center.X - 100 * Angle.Tower.X, Display.Center.Y + 100 * Angle.Tower.Y);
    GC.stroke();

    GC.beginPath();
    GC.lineTo(Display.Center.X, Display.Center.Y);
    GC.lineTo(Display.Center.X - 100 * Angle.Mouse.X, Display.Center.Y + 100 * Angle.Mouse.Y);
    GC.stroke();
    
    GC.beginPath();
    GC.arc(Display.Center.X + 100 * Angle.Tower.X, Display.Center.Y - 100 * Angle.Tower.Y, 1, 0, Math.PI * 2, false);
    GC.stroke();
}, Draw.Update)

var Information = setInterval( function () {
    document.getElementById("Key.Down.WASD[0]").innerHTML = Key[0][1];
    document.getElementById("Key.Down.WASD[1]").innerHTML = Key[1][1];
    document.getElementById("Key.Down.WASD[2]").innerHTML = Key[2][1];
    document.getElementById("Key.Down.WASD[3]").innerHTML = Key[3][1];
    document.getElementById("Angle.Body.Rotate").innerHTML = Angle.Body.Rotate;
    document.getElementById("Angle.Tower.Rotate").innerHTML = Angle.Tower.Rotate;
    document.getElementById("Angle.Mouse.Rotate").innerHTML = Angle.Mouse.Rotate;
    document.getElementById("Angle.Tower.two").innerHTML = Angle.Tower.two;
    document.getElementById("Angle.Mouse.two").innerHTML = Angle.Mouse.two;
}, 30)


index.html
<!DOCTYPE html>
<html>
    <head>
        <title>Title</title>
        <link rel="stylesheet" href="css/main.css">
    </head>
    <body id="Body">
        <canvas id="CanvasGame"></canvas>
        <div id="Information">
            <p>W: <span id="Key.Down.WASD[0]"></span></p>
            <p>A: <span id="Key.Down.WASD[1]"></span></p>
            <p>S: <span id="Key.Down.WASD[2]"></span></p>
            <p>D: <span id="Key.Down.WASD[3]"></span></p>
            <p>Angle body rotate: <span id="Angle.Body.Rotate"></span></p>
            <p>Angle tower rotate: <span id="Angle.Tower.Rotate"></span></p>
            <p>Angle mouse rotate: <span id="Angle.Mouse.Rotate"></span></p>
            <p>Angle tower two: <span id="Angle.Tower.two"></span></p>
            <p>Angle mouse two: <span id="Angle.Mouse.two"></span></p>
        </div>
        <canvas id="Protection"></canvas>
        <script src="js/moduls.js"></script>
        <script src="js/militaryequipment.js"></script>
        <script src="js/gameprocess.js"></script>
    </body>
</html>

Rise 07.05.2017 20:17

Sasha05082002, что-то интервалов много, игровой цикл должен быть один.

Sasha05082002 08.05.2017 14:31

Ответ на Rise:
 
Цитата:

Сообщение от Rise (Сообщение 451871)
Sasha05082002, что-то интервалов много, игровой цикл должен быть один.

Каждый интервал выполняет свою функцию:
Time.Work - считает время, обновляется за 10 миллисекунд, чтобы точно рассчитать время);
GameProcess.Work - игровой процесс (Физика, подсчёты и т.д), обновляется за 1 миллисекунд, чтобы всё игровые процессы выполнялись быстро;
Draw.Work - рисование моделей, но пока-что поворот башни в разработке Я использую тока контуры, обновляется за 30 миллисекунд, чтобы не повышать нагрузку;
Information - Выводить информацию (Я потом её уберу).
Если можно сделать лучше, говорите (как :)). Спасибо за внимание!

Rise 08.05.2017 22:56

Цитата:

Сообщение от Sasha05082002 (Сообщение 451864)
чтобы башня поварачивалась за курсором с заданой скорость, но у меня не получается

Не понимаю, в примере и поворот и скорость есть.

Sasha05082002 09.05.2017 07:14

Ответ на Rise:
 
Цитата:

Сообщение от Rise (Сообщение 451933)
Не понимаю, в примере и поворот и скорость есть.

Есть, но они неправильно работает. Есть линия которая поворачивается за курсором, есть линия которая показывает направление танка и есть линия которая должна следовать за линией которая следует за курсором, но она не так работает. :(
Я просто не знаю как её сделать.
Вот это часть отвечает за поворот башни (Часть закомментирована так как они неправильно работают, да и впрочем всё остальное, а труды стирать неохото :)):

092
if (Angle.Tower.Rotate < -Math.PI) Angle.Tower.Rotate = -Angle.Tower.Rotate - 0.03;*/
093
/*if (Angle.Tower.Rotate != Angle.Mouse.Rotate) {
094
if (Angle.Mouse.Rotate < Angle.Tower.Rotate && Angle.Mouse.Rotate > 0 && Angle.Tower.Rotate > 0) {Angle.Tower.Rotate -= 0.01}
095
if (Angle.Mouse.Rotate > Angle.Tower.Rotate && Angle.Mouse.Rotate > 0 && Angle.Tower.Rotate > 0) {Angle.Tower.Rotate += 0.01}
096
if (Angle.Mouse.Rotate < Angle.Tower.Rotate && Angle.Mouse.Rotate < 0 && Angle.Tower.Rotate < 0) {Angle.Tower.Rotate -= 0.01}
097
if (Angle.Mouse.Rotate > Angle.Tower.Rotate && Angle.Mouse.Rotate < 0 && Angle.Tower.Rotate < 0) {Angle.Tower.Rotate += 0.01}
098

099
if (Angle.Mouse.Rotate < Angle.Tower.Rotate && Angle.Mouse.Rotate < 0 && Angle.Tower.Rotate > 0) {Angle.Tower.Rotate += 0.01}
100
if (Angle.Mouse.Rotate > Angle.Tower.Rotate && Angle.Mouse.Rotate > 0 && Angle.Tower.Rotate < 0) {Angle.Tower.Rotate -= 0.01}
101
if (Angle.Mouse.Rotate < Angle.Tower.Rotate && Angle.Mouse.Rotate > 0 && Angle.Tower.Rotate < 0) {Angle.Tower.Rotate += 0.01}
102
if (Angle.Mouse.Rotate > Angle.Tower.Rotate && Angle.Mouse.Rotate < 0 && Angle.Tower.Rotate > 0) {Angle.Tower.Rotate -= 0.01}
103
}*/
104
/*if (Angle.Tower.Rotate > 0 && Angle.Mouse.Rotate > 0) {Angle.Tower.two = Angle.Tower.Rotate - Angle.Mouse.Rotate}
105
//if (Angle.Tower.Rotate < 0 && Angle.Mouse.Rotate < 0) {Angle.Tower.two = Angle.Tower.Rotate - Angle.Mouse.Rotate}
106
//if (Angle.Tower.Rotate < 0 && Angle.Mouse.Rotate > 0) {Angle.Tower.two = Math.PI - Angle.Tower.Rotate + Angle.Mouse.Rotate}
107
//if (Angle.Tower.Rotate > 0 && Angle.Mouse.Rotate < 0) {Angle.Tower.two = Math.PI - Angle.Tower.Rotate + Angle.Mouse.Rotate}*/
108
//Angle.Tower.two = Angle.Tower.Rotate - ((Angle.Mouse.Rotate < 0) ? -Angle.Mouse.Rotate : Angle.Mouse.Rotate);
109
//Angle.Tower.two = Angle.Tower.Rotate - Angle.Mouse.Rotate;
110
if(Angle.Mouse.Rotate <= Math.PI && Angle.Mouse.Rotate >= 0) {Angle.Mouse.two = Math.abs(Angle.Tower.Rotate) + Math.abs(Angle.Mouse.Rotate)};
111
if(Angle.Mouse.Rotate > -Math.PI && Angle.Mouse.Rotate < 0) {Angle.Mouse.two = -Math.abs(Angle.Tower.Rotate) + Math.abs(Angle.Mouse.Rotate)};
112
//if(Angle.Mouse.Rotate > 0) {Angle.Mouse.two = Math.abs(Angle.Tower.Rotate) - Math.abs(Angle.Mouse.Rotate)};
113
if(Angle.Mouse.two > 0) Angle.Tower.Rotate -= 0.01
114
if(Angle.Mouse.two < 0) Angle.Tower.Rotate += 0.01
115
//Angle.Mouse.two = Angle.Mouse.Rotate - Angle.Tower.Rotate;
Спасибо за внимание, Rise! ^-^

Rise 09.05.2017 12:13

Цитата:

Сообщение от Sasha05082002 (Сообщение 451948)
есть линия которая должна следовать за линией которая следует за курсором, но она не так работает

Так она и так следует, ты расскажи как тебе надо чтобы было.

Sasha05082002 10.05.2017 03:33

Ответ на Rise:
 
Вложений: 2
Цитата:

Сообщение от Rise (Сообщение 451964)
Так она и так следует, ты расскажи как тебе надо чтобы было.

На вложенном снимке показано как примерно это должно выглядит. Линя 1 (Корпус) - Поворачивается при помощи клавиш A (Влево) и D (Вправо). Линия 2 - следует за курсором. Линия 3 (Башня) -она должна следовать за линией 2 с заданой скоростью, на примере как в World of tanks (Но она не так следует - скачайте архив с игрой, запустите игру (index.html) и вы увидите, что не так).
Спасибо за внимание, Rise! ^-^

Rise 16.05.2017 08:48

Sasha05082002, так?
<style>
canvas { outline: 1px solid gray; }
canvas:hover { outline-color: red; }
</style>
<canvas width="400" height="200" tabindex="1"></canvas>
<script>
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
context.fillText('Touch Me', canvas.width / 2, canvas.height / 2);
var requestID;
var Mouse = {
	x: 0,
	y: 0,
	angle: 0,
	follow: 0,
	info: function() {
		context.fillText('Mouse: angle ' + this.angle + ' x ' + this.x + ' y ' + this.y + ' follow ' + this.follow, 5, 10);
	}
};
var Tower = {
	x: canvas.width / 2,
	y: canvas.height / 2,
	angle: 0,
	width: 60,
	height: 40,
	degree: Math.PI / 180,
	left: function() {
		var angle = this.angle - this.degree;
		this.angle = (angle < 0) ? angle + 2 * Math.PI : angle;
	},
	right: function() {
		var angle = this.angle + this.degree;
		this.angle = (angle < 2 * Math.PI) ? angle : angle - 2 * Math.PI;
	},
	step: function() {
		if (Mouse.follow) {
			var angle = Math.atan2(Mouse.y - this.y, Mouse.x - this.x);
			Mouse.angle = (angle < 0) ? angle + 2 * Math.PI : angle;
			if (Mouse.angle < this.angle) {
				angle = this.angle - Mouse.angle;
				(angle < Math.PI) ? this.left() : this.right();
			} else {
				angle = Mouse.angle - this.angle;
				(angle < Math.PI) ? this.right() : this.left();
			}
			if (angle - this.degree < 0) Mouse.follow = 0;
		}
	},
	draw: function() {
		var x1, y1, x2, y2;
		context.save();
		context.translate(this.x, this.y);
		context.rotate(this.angle);
		context.translate(-this.x, -this.y);
		x1 = this.x - this.width / 2;
		y1 = this.y - this.height / 2;
		x2 = x1 + this.width;
		y2 = y1 + this.height;
		context.beginPath();
		context.moveTo(x1, y1);
		context.lineTo(x2, y1);
		context.lineTo(x2 + 10, this.y);
		context.lineTo(x2, y2);
		context.lineTo(x1, y2);
		context.fill();
		context.restore();
	},
	info: function() {
		context.fillText('Tower: angle ' + this.angle, 5, 20);
	}
};
canvas.addEventListener('mouseenter', function(e) {
	this.focus();
	requestID = requestAnimationFrame(function tick() {
		context.clearRect(0, 0, canvas.width, canvas.height);
		Tower.step();
		Tower.draw();
		Tower.info();
		Mouse.info();
		requestID = requestAnimationFrame(tick);
	});
});
canvas.addEventListener('mouseleave', function(e) {
	cancelAnimationFrame(requestID);
});
canvas.addEventListener('mousemove', function(e) {
	Mouse.follow = 1;
	Mouse.x = e.offsetX;
	Mouse.y = e.offsetY;
});
</script>

Sasha05082002 20.05.2017 15:05

Ответ на Rise:
 
Да, но есть 1 недочёт (Должен быт какой нибудь процесс который поможет выбрать куда поворачиваться башне, то есть если мышка будет больше повёрнута право то башня начнёт поворачиваться вправо, если лева то влево. Я думаю вы его заметили. Если есть возможность, то исправьте его пожалуйста. Спасибо! ^-^

Rise 20.05.2017 16:02

Sasha05082002, не понимаю, у меня танк смотрит вправо по оси X, поэтому влево будет сверху, а вправо будет снизу.

Sasha05082002 21.05.2017 11:17

Ответ на Rise:
 
Вложений: 1
Я извиняюсь, Я неправильно написал. :) Я думаю по этой картинке всё должно быть понятно.

Rise 22.05.2017 23:01

Вложений: 1
Sasha05082002, исправил, и система координат теперь более адекватная, нарисовал, так удобнее размышлять об углах :)

Sasha05082002 23.05.2017 17:36

Благодарности Risе'у
 
Сппппппппппааааасссиииббо оо огромное Rise!!!!:thanks: Я думаю что тема закрыта, осталось тока подробнее разобрать ваш код, и вопрос как тему закрыть то? :)

Rise 28.05.2017 04:31

Sasha05082002, никак, вот решил еще что-то написать но вовремя остановился :D получилось весьма интересно, особая гордость 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>

рони 28.05.2017 08:53

Rise,
:victory:

Sasha05082002 28.05.2017 12:56

Очень круто! :) Тока бот врезался в стенку и остановился.

Rise 28.05.2017 13:55

рони, :thanks:
Sasha05082002, он не врезался, у него закончился маршрут и он остановился :)

Rise 28.05.2017 15:44

Еще может возникнуть вопрос, почему круг (round) установлено 1, а проехал вроде как 2, на самом деле круг там один:

Первый проезд: произвольная точка -> начальная точка -> промежуточные точки -> конечная точка
Второй проезд: конечная точка -> начальная точка -> промежуточные точки -> конечная точка (round)

И еще, бесконечному числу кругов соответствует отрицательное значение.


Часовой пояс GMT +3, время: 05:47.