英文:
Car movement in the direction of rotation
问题
我目前正在使用画布来构建一个程序,让一辆汽车在圆形路径上行驶。我能够让汽车正确地在圆形路径上行驶,但是汽车始终朝着同一个方向,不随着曲线转向。我在某种程度上理解我需要获取汽车在圆上的切线,这将以某种方式告诉我汽车需要朝向的方向(Y轴的旋转),但我不明白如何在画布中做到这一点,也不明白如何使用这个切线来改变方向。
为什么它不朝着旋转的方向移动?
我希望它像图片中的样子。
(代码部分不予翻译)
英文:
you can fix bad english topic
Im currently using canvas to build a program where a car travels around in a circle. I am able to get the car to travel in a circle correctly, however the car is always facing the same direction and doesn't turn with the curve. I somewhat understand that I need to get the tangent of the car at its location to the circle and that will somehow tell me the direction the car needs to look in (rotation of Y axis), but I don't understand how to do this in canvas or in general how to use this tangent to change the direction.
moves with the arrow keys
Why doesn't it move in the direction of rotation?
I want it like in the picture
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-html -->
<style type="text/css">
canvas{
background: #f1f1f1;
border:1px solid #eeeeee;
}
</style>
<canvas height="900" width="900" id="canvas" style="float:left;margin-right:10px;"></canvas>
<script type="text/javascript">
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
var xpos = 300;
var ypos = 300;
var steering_angle = 0;
var motor = 0.0;
var heading = 0;
var track = 70;
var wheelbase = 110;
var turn_radius = 0;
var targetX = 0;
var targetY = 0;
var xOffset = 0;
var yOffset = 0;
var ack = 0;
function lineToAngle(ctx, x1, y1, length, angle) {
angle *= Math.PI / 180;
var x2 = x1 + length * Math.cos(angle),
y2 = y1 + length * Math.sin(angle);
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
return {x: x2, y: y2};
}
var test = 0;
function Car(){
this.render = function(){
steering_angle = wheelbase;
// Turn Radius
if(turn_radius < 0){
turn_radius = wheelbase / Math.sin(steering_angle * Math.PI/180) - (track / 2); // Turning radius
}else{
turn_radius = wheelbase / Math.sin(steering_angle * Math.PI/180) + (track / 2); // Turning radius
}
xOffset = Math.sin(heading) * wheelbase/2;
yOffset = Math.cos(heading) * wheelbase/2;
ack = {
x: xpos + turn_radius * Math.cos(-heading) + wheelbase/2 * Math.sin(-heading),
y: ypos + turn_radius * Math.sin(-heading) - wheelbase/2 * Math.cos(-heading)
};
var dCenterAck = Math.sqrt(wheelbase/2 * wheelbase/2 + turn_radius * turn_radius);
if(Math.abs(turn_radius) > 0 && Math.abs(turn_radius) < 1000){
var angularSpeed = 0;
if(turn_radius < 0){
angularSpeed = -(motor / (Math.PI * 2.0 * dCenterAck)) * Math.PI * 2;
}else{
angularSpeed = (motor / (Math.PI * 2.0 * dCenterAck)) * Math.PI * 2;
}
targetX = ack.x + dCenterAck * Math.sin(Math.atan2((xpos - ack.x), (ypos - ack.y)) + angularSpeed);
targetY = ack.y + dCenterAck * Math.cos(Math.atan2((xpos - ack.x), (ypos - ack.y)) + angularSpeed);
heading += angularSpeed;
xpos = targetX;
ypos = targetY;
}else{
xpos += motor * Math.sin(heading);
ypos += motor * Math.cos(heading);
}
// Arac Ön Orta ve Arka Pozasiyon
var CarCenterCoord = {x:xpos, y:ypos};
var CarFrontCoord = {x:xpos - xOffset, y:ypos - yOffset};
var CarBackCoord = {x:xOffset + xpos, y:yOffset + ypos};
//var a = getPosition(-100, -turn_radius, heading);
// Arac Tekerlek Pozisyonları
var BackRightWheel = {
x: xpos + -track/2 * Math.cos(-heading) + wheelbase/2 * Math.sin(-heading),
y: ypos + -track/2 * Math.sin(-heading) - wheelbase/2 * Math.cos(-heading)
};
var BackLeftWheel = {
x: xpos + track/2 * Math.cos(-heading) + wheelbase/2 * Math.sin(-heading),
y: ypos + track/2 * Math.sin(-heading) - wheelbase/2 * Math.cos(-heading)
};
var FrontRightWheel = {
x: xpos + -track/2 * Math.cos(-heading) - wheelbase/2 * Math.sin(-heading),
y: ypos + -track/2 * Math.sin(-heading) + wheelbase/2 * Math.cos(-heading)
};
var FrontLeftWheel = {
x: xpos + track/2 * Math.cos(-heading) - wheelbase/2 * Math.sin(-heading),
y: ypos + track/2 * Math.sin(-heading) + wheelbase/2 * Math.cos(-heading)
};
// Slip Angle
//var FrontAckermannAngleLeft = 0;
//var FrontAckermannAngleRight = 0;
var BackAckermannAngleLeft = 0;
var BackAckermannAngleRight = 0;
if(turn_radius > 0){
//BackAckermannAngleLeft = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius + (track / 2))) + Math.sign(wheelbase);
//BackAckermannAngleRight = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius - (track / 2))) - Math.sign(wheelbase);
FrontAckermannAngleLeft = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius + (track / 2)));
FrontAckermannAngleRight = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius - (track / 2)));
}else if(turn_radius < 0){
//BackAckermannAngleLeft = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius + (track / 2))) - Math.sign(wheelbase);
//BackAckermannAngleRight = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius - (track / 2))) + Math.sign(wheelbase);
FrontAckermannAngleLeft = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius - (track / 2)));
FrontAckermannAngleRight = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius + (track / 2)));
}else{
FrontAckermannAngleLeft = 0;
FrontAckermannAngleRight = 0;
}
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.fillRect(BackRightWheel.x, BackRightWheel.y, 5, 5);
ctx.fillRect(BackLeftWheel.x, BackLeftWheel.y, 5, 5);
ctx.fillRect(FrontRightWheel.x, FrontRightWheel.y, 5, 5);
ctx.fillRect(FrontLeftWheel.x, FrontLeftWheel.y, 5, 5);
ctx.stroke();
var aa = {x:xpos+FrontAckermannAngleLeft, y:ypos+30};
ctx.lineWidth = 0.5
ctx.strokeStyle = "blue";
ctx.beginPath();
//lineToAngle(ctx, FrontLeftWheel.x, FrontLeftWheel.y, 50, 90-steering_angle);
ctx.moveTo(xpos, ypos);
ctx.lineTo(aa.x, aa.y);
ctx.stroke();
// debug
ctx.fillStyle = "red";
ctx.strokeStyle = "blue";
ctx.lineWidth = 0.5
ctx.beginPath();
ctx.fillRect(ack.x, ack.y, 5, 5);
//ctx.arc(ack.x, ack.y, Math.abs(turn_radius), 0, 2 * Math.PI, false);
ctx.arc(ack.x, ack.y, dCenterAck, 0, 2 * Math.PI, false);
//ctx.arc(ack.x, ack.y, 115, 0, 2 * Math.PI, false);
//ctx.arc(ack.x, ack.y, 150, 0, 2 * Math.PI, false);
//ctx.arc(ack.x, ack.y, 185, 0, 2 * Math.PI, false);
//ctx.arc(ack.x, ack.y, 215, 0, 2 * Math.PI, false);
ctx.stroke();
// arac arka nokta
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.fillRect(CarFrontCoord.x, CarFrontCoord.y, 5, 5);
ctx.stroke();
ctx.lineWidth = 0.5
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(ack.x, ack.y);
ctx.lineTo(CarFrontCoord.x, CarFrontCoord.y);
ctx.stroke();
// arac orta nokta
ctx.fillStyle = "red";
ctx.beginPath();
ctx.fillRect(CarCenterCoord.x, CarCenterCoord.y, 5, 5);
ctx.stroke();
// arac on nokta
ctx.fillStyle = "purple";
ctx.beginPath();
ctx.fillRect(CarBackCoord.x, CarBackCoord.y, 5, 5);
ctx.stroke();
ctx.lineWidth = 0.5
ctx.strokeStyle = "purple";
ctx.beginPath();
ctx.moveTo(ack.x, ack.y);
ctx.lineTo(FrontRightWheel.x, FrontRightWheel.y);
ctx.stroke();
ctx.lineWidth = 0.5
ctx.strokeStyle = "purple";
ctx.beginPath();
ctx.moveTo(ack.x, ack.y);
ctx.lineTo(FrontLeftWheel.x, FrontLeftWheel.y);
ctx.stroke();
var img = new Image();
img.src = 'https://i.ibb.co/2y2wz7b/car.png';
ctx.save();
ctx.translate(xpos+100/2, ypos);
//ctx.rotate(motor * -steering_angle * Math.PI / 360);
ctx.translate(-xpos-100/2, -ypos-100/2);
ctx.drawImage(img, xpos-100/2, ypos-100/2);
ctx.restore();
test += 1;
}
}
var car = new Car();
function renderx(tickTime){
// Time in milisec per frame, to set FPS to 60 = 1000/60 => 16.6, 24 fps => 41
var targetFrameTime = 40;
var date = new Date();
var frameStart = date.getMilliseconds();
var endDate = new Date();
var frameStop = endDate.getMilliseconds();
var frameTime = frameStop-frameStart;
var timeout = targetFrameTime - frameTime - tickTime;
setTimeout(function(){ tick(); }, timeout);
ctx.clearRect(-10000, -10000, 10000*canvas.width, 10000*canvas.height);
car.render();
}
// Key Control
var pressedKeys = {};
document.onkeydown = function(e){
e = e || window.event;
pressedKeys[e.keyCode] = true;
}
window.document.onkeyup = function(e){
e = e || window.event;
delete pressedKeys[e.keyCode];
}
setInterval( function(){
if(pressedKeys[39]){
steering_angle -= 2;
}
if(pressedKeys[37]){
steering_angle += 2;
}
if(pressedKeys[38]){
motor += 0.1;
}
if(pressedKeys[40]){
motor -= 0.1;
}
}, 30 );
// Delta Time
function tick(){
var date = new Date();
var tickStart = date.getMilliseconds();
// Delta.Time
var date = new Date();
var tickEnd = date.getMilliseconds();
renderx( tickEnd - tickStart );
}
// FPS
var times = [];
var fps;
function refreshLoop(){
window.requestAnimationFrame(function() {
const now = performance.now();
while (times.length > 0 && times[0] <= now - 1000) {
times.shift();
}
times.push(now);
fps = times.length;
refreshLoop();
});
}
refreshLoop();
tick();
</script>
<!-- end snippet -->
答案1
得分: 0
以下是您要翻译的代码部分的翻译:
// 一些与旋转、平移和绘图位置相关的微调,它在一个方向上工作,但需要处理航向方向的变化 :)
<!-- 开始片段:JS 隐藏:假 控制台:真 Babel:假 -->
<!-- 语言:HTML -->
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
var xpos = 300;
var ypos = 300;
var steering_angle = 0;
var motor = 0.0;
var heading = 0;
var track = 70;
var wheelbase = 110;
var turn_radius = 0;
var targetX = 0;
var targetY = 0;
var xOffset = 0;
var yOffset = 0;
var ack = 0;
function lineToAngle(ctx, x1, y1, length, angle) {
angle *= Math.PI / 180;
var x2 = x1 + length * Math.cos(angle),
y2 = y1 + length * Math.sin(angle);
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
return { x: x2, y: y2 };
}
var test = 0;
function Car() {
// ...
}
var car = new Car();
function renderx(tickTime) {
// ...
}
// Key Control
var pressedKeys = {};
document.onkeydown = function (e) {
e = e || window.event;
pressedKeys[e.keyCode] = true;
};
window.document.onkeyup = function (e) {
e = e || window.event;
delete pressedKeys[e.keyCode];
};
setInterval(function () {
// ...
}, 30);
// Delta Time
function tick() {
var date = new Date();
var tickStart = date.getMilliseconds();
// Delta.Time
var date = new Date();
var tickEnd = date.getMilliseconds();
renderx(tickEnd - tickStart);
}
// FPS
var times = [];
var fps;
function refreshLoop() {
window.requestAnimationFrame(function () {
const now = performance.now();
while (times.length > 0 && times[0] <= now - 1000) {
times.shift();
}
times.push(now);
fps = times.length;
refreshLoop();
});
}
refreshLoop();
tick();
<!-- 结束片段 -->
希望这可以帮助您!如果您需要更多帮助,请随时告诉我。
英文:
A few tweaks relating to rotation/ translation and drawing position and it works in one direction- it remains to handle the heading direction change though
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-html -->
<style type="text/css">
canvas{
background: #f1f1f1;
border:1px solid #eeeeee;
}
</style>
<canvas height="900" width="900" id="canvas" style="float:left;margin-right:10px;"></canvas>
<script type="text/javascript">
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
var xpos = 300;
var ypos = 300;
var steering_angle = 0;
var motor = 0.0;
var heading = 0;
var track = 70;
var wheelbase = 110;
var turn_radius = 0;
var targetX = 0;
var targetY = 0;
var xOffset = 0;
var yOffset = 0;
var ack = 0;
function lineToAngle(ctx, x1, y1, length, angle) {
angle *= Math.PI / 180;
var x2 = x1 + length * Math.cos(angle),
y2 = y1 + length * Math.sin(angle);
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
return {x: x2, y: y2};
}
var test = 0;
function Car(){
this.render = function(){
steering_angle = wheelbase;
// Turn Radius
if(turn_radius < 0){
turn_radius = wheelbase / Math.sin(steering_angle * Math.PI/180) - (track / 2); // Turning radius
}else{
turn_radius = wheelbase / Math.sin(steering_angle * Math.PI/180) + (track / 2); // Turning radius
}
xOffset = Math.sin(heading) * wheelbase/2;
yOffset = Math.cos(heading) * wheelbase/2;
ack = {
x: xpos + turn_radius * Math.cos(-heading) + wheelbase/2 * Math.sin(-heading),
y: ypos + turn_radius * Math.sin(-heading) - wheelbase/2 * Math.cos(-heading)
};
var dCenterAck = Math.sqrt(wheelbase/2 * wheelbase/2 + turn_radius * turn_radius);
if(Math.abs(turn_radius) > 0 && Math.abs(turn_radius) < 1000){
var angularSpeed = 0;
if(turn_radius < 0){
angularSpeed = -(motor / (Math.PI * 2.0 * dCenterAck)) * Math.PI * 2;
}else{
angularSpeed = (motor / (Math.PI * 2.0 * dCenterAck)) * Math.PI * 2;
}
targetX = ack.x + dCenterAck * Math.sin(Math.atan2((xpos - ack.x), (ypos - ack.y)) + angularSpeed);
targetY = ack.y + dCenterAck * Math.cos(Math.atan2((xpos - ack.x), (ypos - ack.y)) + angularSpeed);
heading += angularSpeed;
xpos = targetX;
ypos = targetY;
}else{
xpos += motor * Math.sin(heading);
ypos += motor * Math.cos(heading);
}
// Arac Ön Orta ve Arka Pozasiyon
var CarCenterCoord = {x:xpos, y:ypos};
var CarFrontCoord = {x:xpos - xOffset, y:ypos - yOffset};
var CarBackCoord = {x:xOffset + xpos, y:yOffset + ypos};
//var a = getPosition(-100, -turn_radius, heading);
// Arac Tekerlek Pozisyonları
var BackRightWheel = {
x: xpos + -track/2 * Math.cos(-heading) + wheelbase/2 * Math.sin(-heading),
y: ypos + -track/2 * Math.sin(-heading) - wheelbase/2 * Math.cos(-heading)
};
var BackLeftWheel = {
x: xpos + track/2 * Math.cos(-heading) + wheelbase/2 * Math.sin(-heading),
y: ypos + track/2 * Math.sin(-heading) - wheelbase/2 * Math.cos(-heading)
};
var FrontRightWheel = {
x: xpos + -track/2 * Math.cos(-heading) - wheelbase/2 * Math.sin(-heading),
y: ypos + -track/2 * Math.sin(-heading) + wheelbase/2 * Math.cos(-heading)
};
var FrontLeftWheel = {
x: xpos + track/2 * Math.cos(-heading) - wheelbase/2 * Math.sin(-heading),
y: ypos + track/2 * Math.sin(-heading) + wheelbase/2 * Math.cos(-heading)
};
// Slip Angle
//var FrontAckermannAngleLeft = 0;
//var FrontAckermannAngleRight = 0;
var BackAckermannAngleLeft = 0;
var BackAckermannAngleRight = 0;
if(turn_radius > 0){
//BackAckermannAngleLeft = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius + (track / 2))) + Math.sign(wheelbase);
//BackAckermannAngleRight = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius - (track / 2))) - Math.sign(wheelbase);
FrontAckermannAngleLeft = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius + (track / 2)));
FrontAckermannAngleRight = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius - (track / 2)));
}else if(turn_radius < 0){
//BackAckermannAngleLeft = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius + (track / 2))) - Math.sign(wheelbase);
//BackAckermannAngleRight = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius - (track / 2))) + Math.sign(wheelbase);
FrontAckermannAngleLeft = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius - (track / 2)));
FrontAckermannAngleRight = (360 / (Math.PI * 2)) * Math.atan(wheelbase / (turn_radius + (track / 2)));
}else{
FrontAckermannAngleLeft = 0;
FrontAckermannAngleRight = 0;
}
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.fillRect(BackRightWheel.x, BackRightWheel.y, 5, 5);
ctx.fillRect(BackLeftWheel.x, BackLeftWheel.y, 5, 5);
ctx.fillRect(FrontRightWheel.x, FrontRightWheel.y, 5, 5);
ctx.fillRect(FrontLeftWheel.x, FrontLeftWheel.y, 5, 5);
ctx.stroke();
var aa = {x:xpos+FrontAckermannAngleLeft, y:ypos+30};
ctx.lineWidth = 0.5
ctx.strokeStyle = "blue";
ctx.beginPath();
//lineToAngle(ctx, FrontLeftWheel.x, FrontLeftWheel.y, 50, 90-steering_angle);
ctx.moveTo(xpos, ypos);
ctx.lineTo(aa.x, aa.y);
ctx.stroke();
// debug
ctx.fillStyle = "red";
ctx.strokeStyle = "blue";
ctx.lineWidth = 0.5
ctx.beginPath();
ctx.fillRect(ack.x, ack.y, 5, 5);
//ctx.arc(ack.x, ack.y, Math.abs(turn_radius), 0, 2 * Math.PI, false);
ctx.arc(ack.x, ack.y, dCenterAck, 0, 2 * Math.PI, false);
//ctx.arc(ack.x, ack.y, 115, 0, 2 * Math.PI, false);
//ctx.arc(ack.x, ack.y, 150, 0, 2 * Math.PI, false);
//ctx.arc(ack.x, ack.y, 185, 0, 2 * Math.PI, false);
//ctx.arc(ack.x, ack.y, 215, 0, 2 * Math.PI, false);
ctx.stroke();
// arac arka nokta
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.fillRect(CarFrontCoord.x, CarFrontCoord.y, 5, 5);
ctx.stroke();
ctx.lineWidth = 0.5
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(ack.x, ack.y);
ctx.lineTo(CarFrontCoord.x, CarFrontCoord.y);
ctx.stroke();
// arac orta nokta
ctx.fillStyle = "red";
ctx.beginPath();
ctx.fillRect(CarCenterCoord.x, CarCenterCoord.y, 5, 5);
ctx.stroke();
// arac on nokta
ctx.fillStyle = "purple";
ctx.beginPath();
ctx.fillRect(CarBackCoord.x, CarBackCoord.y, 5, 5);
ctx.stroke();
ctx.lineWidth = 0.5
ctx.strokeStyle = "purple";
ctx.beginPath();
ctx.moveTo(ack.x, ack.y);
ctx.lineTo(FrontRightWheel.x, FrontRightWheel.y);
ctx.stroke();
ctx.lineWidth = 0.5
ctx.strokeStyle = "purple";
ctx.beginPath();
ctx.moveTo(ack.x, ack.y);
ctx.lineTo(FrontLeftWheel.x, FrontLeftWheel.y);
ctx.stroke();
var img = new Image();
img.src = 'https://i.ibb.co/2y2wz7b/car.png';
ctx.save();
ctx.translate(CarCenterCoord.x, CarCenterCoord.y);
ctx.rotate(-heading-Math.PI);
ctx.drawImage(img, -45,-100);
ctx.restore();
test += 1;
}
}
var car = new Car();
function renderx(tickTime){
// Time in milisec per frame, to set FPS to 60 = 1000/60 => 16.6, 24 fps => 41
var targetFrameTime = 40;
var date = new Date();
var frameStart = date.getMilliseconds();
var endDate = new Date();
var frameStop = endDate.getMilliseconds();
var frameTime = frameStop-frameStart;
var timeout = targetFrameTime - frameTime - tickTime;
setTimeout(function(){ tick(); }, timeout);
ctx.clearRect(-10000, -10000, 10000*canvas.width, 10000*canvas.height);
car.render();
}
// Key Control
var pressedKeys = {};
document.onkeydown = function(e){
e = e || window.event;
pressedKeys[e.keyCode] = true;
}
window.document.onkeyup = function(e){
e = e || window.event;
delete pressedKeys[e.keyCode];
}
setInterval( function(){
if(pressedKeys[39]){
steering_angle -= 2;
}
if(pressedKeys[37]){
steering_angle += 2;
}
if(pressedKeys[38]){
motor += 0.1;
}
if(pressedKeys[40]){
motor -= 0.1;
}
}, 30 );
// Delta Time
function tick(){
var date = new Date();
var tickStart = date.getMilliseconds();
// Delta.Time
var date = new Date();
var tickEnd = date.getMilliseconds();
renderx( tickEnd - tickStart );
}
// FPS
var times = [];
var fps;
function refreshLoop(){
window.requestAnimationFrame(function() {
const now = performance.now();
while (times.length > 0 && times[0] <= now - 1000) {
times.shift();
}
times.push(now);
fps = times.length;
refreshLoop();
});
}
refreshLoop();
tick();
</script>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论