You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Canvas中汽车与轨道线条碰撞检测及Alert触发技术问询

Solution: Collision Detection for Canvas Racing Car with Track Lines

Got it, let's tackle this problem step by step. We need to add reusable line intersection detection, manage multiple track lines, and trigger an alert when the car's borders collide with any track line. Here's how to implement it:

1. Reusable Line Segment Intersection Function

First, we'll build a core function to check if two line segments intersect. This uses the cross product method (a standard 2D collision detection technique) and works for any line segments you add later.

// Reusable line segment intersection detection function
function doLinesIntersect(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
    // Calculate cross products to check segment orientation
    const cross1 = (line1EndX - line1StartX) * (line2StartY - line1StartY) - (line1EndY - line1StartY) * (line2StartX - line1StartX);
    const cross2 = (line1EndX - line1StartX) * (line2EndY - line1StartY) - (line1EndY - line1StartY) * (line2EndX - line1StartX);
    const cross3 = (line2EndX - line2StartX) * (line1StartY - line2StartY) - (line2EndY - line2StartY) * (line1StartX - line2StartX);
    const cross4 = (line2EndX - line2StartX) * (line1EndY - line2StartY) - (line2EndY - line2StartY) * (line1EndX - line2StartX);

    // Segments intersect if they straddle each other (cross products have opposite signs)
    return ((cross1 < 0 && cross2 > 0) || (cross1 > 0 && cross2 < 0)) &&
           ((cross3 < 0 && cross4 > 0) || (cross3 > 0 && cross4 < 0));
}

2. Manage Track Lines as an Array

Instead of hardcoding a single track line, we'll store all track lines in an array. This makes it easy to add more lines later without rewriting core logic.

// Track lines array - add new lines here to expand the track
const trackLines = [
    { startX: 300, startY: 150, endX: 600, endY: 800 },
    // Example of adding a second line:
    // { startX: 100, startY: 400, endX: 1000, endY: 400 }
];

3. Extract Car Border Segments

We need to calculate the car's four border segments after rotation (since the car can turn). We'll add a method to the move class to get these rotated segments.

// Inside the move class, add this method
this.getBorderSegments = function() {
    const halfWidth = this.width / 2;
    const halfHeight = this.height / 2;
    // Base corners (before rotation)
    const baseCorners = [
        { x: -halfWidth, y: -halfHeight },
        { x: -halfWidth, y: halfHeight },
        { x: halfWidth, y: halfHeight },
        { x: halfWidth, y: -halfHeight }
    ];

    // Rotate each corner around the car's center point
    const rotatedCorners = baseCorners.map(corner => {
        const rotatedX = corner.x * Math.cos(this.angle) - corner.y * Math.sin(this.angle);
        const rotatedY = corner.x * Math.sin(this.angle) + corner.y * Math.cos(this.angle);
        return {
            x: this.x + rotatedX,
            y: this.y + rotatedY
        };
    });

    // Return the four border line segments
    return [
        { start: rotatedCorners[0], end: rotatedCorners[1] },
        { start: rotatedCorners[1], end: rotatedCorners[2] },
        { start: rotatedCorners[2], end: rotatedCorners[3] },
        { start: rotatedCorners[3], end: rotatedCorners[0] }
    ];
};

4. Collision Check Logic

In each frame, we'll check if any of the car's border segments intersect with any track line. If a collision is detected, we trigger an alert (and optionally stop the game).

Full Updated Code

Here's the complete code with all modifications integrated:

JavaScript

var car; 
var img = new Image(); 
img.src = 'https://cdn4.iconfinder.com/data/icons/transportation-2-4/60/transportation-2-flat-036-racing-car-top-512.png'; 

// Reusable line segment intersection detection function
function doLinesIntersect(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
    const cross1 = (line1EndX - line1StartX) * (line2StartY - line1StartY) - (line1EndY - line1StartY) * (line2StartX - line1StartX);
    const cross2 = (line1EndX - line1StartX) * (line2EndY - line1StartY) - (line1EndY - line1StartY) * (line2EndX - line1StartX);
    const cross3 = (line2EndX - line2StartX) * (line1StartY - line2StartY) - (line2EndY - line2StartY) * (line1StartX - line2StartX);
    const cross4 = (line2EndX - line2StartX) * (line1EndY - line2StartY) - (line2EndY - line2StartY) * (line1EndX - line2StartX);

    return ((cross1 < 0 && cross2 > 0) || (cross1 > 0 && cross2 < 0)) &&
           ((cross3 < 0 && cross4 > 0) || (cross3 > 0 && cross4 < 0));
}

// Track lines array - add more lines here to expand the track
const trackLines = [
    { startX: 300, startY: 150, endX: 600, endY: 800 },
    // Uncomment below to add a second track line:
    // { startX: 100, startY: 400, endX: 1000, endY: 400 }
];

function startGame() { 
    car = new move(20, 40, "red", 600, 300); 
    pg.start(); 
} 

var pg = { 
    canvas: document.createElement("canvas"), 
    start: function() { 
        this.canvas.width = 1200; 
        this.canvas.height = 600; 
        this.context = this.canvas.getContext("2d"); 
        document.body.insertBefore(this.canvas, document.body.childNodes[0]); 
        this.frameNo = 0; 
        this.interval = setInterval(updateframe, 20); 
        window.addEventListener('keydown', function(e) { 
            e.preventDefault(); 
            pg.keys = (pg.keys || []); 
            pg.keys[e.keyCode] = (e.type == "keydown"); 
        }); 
        window.addEventListener('keyup', function(e) { 
            pg.keys[e.keyCode] = (e.type == "keydown"); 
        }); 
    }, 
    stop: function() { 
        clearInterval(this.interval); 
    }, 
    clear: function() { 
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); 
    } 
}; 

function move(width, height, color, x, y, type) { 
    this.type = type; 
    this.width = width; 
    this.height = height; 
    this.speed = 0; 
    this.angle = 0; 
    this.moveAngle = 0; 
    this.x = x; 
    this.y = y; 

    this.update = function() { 
        ctx = pg.context; 
        ctx.save(); 
        ctx.translate(this.x, this.y); 
        ctx.rotate(this.angle); 
        ctx.drawImage(img, this.width / -2, this.height / -2, this.width, this.height); 
        // Draw car border (for visibility)
        ctx.beginPath(); 
        ctx.moveTo(this.width / -2, this.height / -2); 
        ctx.lineTo(this.width / -2, this.height / 2); 
        ctx.lineTo(this.width / 2, this.height / 2); 
        ctx.lineTo(this.width / 2, this.height / -2); 
        ctx.closePath(); 
        ctx.stroke(); 
        ctx.restore(); 

        // Draw all track lines
        trackLines.forEach(line => {
            ctx.beginPath();
            ctx.moveTo(line.startX, line.startY);
            ctx.lineTo(line.endX, line.endY);
            ctx.stroke();
        });
    }; 

    this.getBorderSegments = function() {
        const halfWidth = this.width / 2;
        const halfHeight = this.height / 2;
        const baseCorners = [
            { x: -halfWidth, y: -halfHeight },
            { x: -halfWidth, y: halfHeight },
            { x: halfWidth, y: halfHeight },
            { x: halfWidth, y: -halfHeight }
        ];

        const rotatedCorners = baseCorners.map(corner => {
            const rotatedX = corner.x * Math.cos(this.angle) - corner.y * Math.sin(this.angle);
            const rotatedY = corner.x * Math.sin(this.angle) + corner.y * Math.cos(this.angle);
            return {
                x: this.x + rotatedX,
                y: this.y + rotatedY
            };
        });

        return [
            { start: rotatedCorners[0], end: rotatedCorners[1] },
            { start: rotatedCorners[1], end: rotatedCorners[2] },
            { start: rotatedCorners[2], end: rotatedCorners[3] },
            { start: rotatedCorners[3], end: rotatedCorners[0] }
        ];
    };

    this.newPos = function() { 
        this.angle += this.moveAngle * Math.PI / 180; 
        this.x += this.speed * Math.sin(this.angle); 
        this.y -= this.speed * Math.cos(this.angle); 
    }; 
} 

function updateframe() { 
    pg.clear(); 
    car.moveAngle = 0; 
    car.speed = 0; 

    // Car control logic
    if (pg.keys) {
        if (pg.keys[37]) { // Left arrow
            car.moveAngle = pg.keys[40] ? 5 : -5;
        }
        if (pg.keys[39]) { // Right arrow
            car.moveAngle = pg.keys[40] ? -5 : 5;
        }
        if (pg.keys[38]) { // Up arrow (accelerate)
            car.speed = 5;
        }
        if (pg.keys[40]) { // Down arrow (reverse)
            car.speed = -5;
        }
    }

    car.newPos(); 
    car.update(); 

    // Check for collisions
    const carSegments = car.getBorderSegments();
    let collisionDetected = false;

    trackLines.forEach(trackLine => {
        carSegments.forEach(carSegment => {
            if (doLinesIntersect(
                carSegment.start.x, carSegment.start.y,
                carSegment.end.x, carSegment.end.y,
                trackLine.startX, trackLine.startY,
                trackLine.endX, trackLine.endY
            )) {
                collisionDetected = true;
            }
        });
    });

    if (collisionDetected) {
        alert("Collision detected!");
        pg.stop(); // Optional: stop the game when collision happens
    }
} 

startGame();

CSS

canvas { 
    border: 1px solid #d3d3d3; 
    background-color: #f1f1f1; 
}

How to Use

  • Controls: Use the arrow keys to drive the car:
    • ↑: Accelerate forward
    • ↓: Reverse
    • ←/→: Turn left/right (combine with ↑/↓ to turn while moving)
  • Collision: When any part of the car's border touches a track line, an alert will pop up, and the game will stop (you can remove the pg.stop() line if you want the game to continue after the alert).
  • Add More Track Lines: Just add new objects to the trackLines array with startX, startY, endX, endY values.

内容的提问来源于stack exchange,提问作者fghuj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:35:08