Canvas中汽车与轨道线条碰撞检测及Alert触发技术问询
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
trackLinesarray withstartX,startY,endX,endYvalues.
内容的提问来源于stack exchange,提问作者fghuj

