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 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, время: 00:02.