SVG & JS - 光线被障碍物挡住

SVG & JS - Light stopped by obstacle

我实际上正在开发一个小游戏,我正在尝试用障碍物来阻止地图显示。目前我有这个:

我当然想要这个:

我尝试了 SAT.js 库,它在发生碰撞时让我知道,但我真的不知道你用它做什么。我的第一个想法是在障碍物后面创建一个黑色(或白色只是为了隐藏)多边形,但我相信这是一个更好的解决方案。

对于这种“光”效果,我在 SVG 中使用了这样的技巧:

   <clippath id="clips" >
        <path class="view" tokenname="" d="M 1 1000 L 250 1 L 500 1000 z"/>
    </clippath>
 <image xlink:href="https://i.pinimg.com/originals/46/3a/12/463a1244e2e2627c53ff9806e2012c84.jpg" width="1920" height="1080" x="0" y="0" class="one"/>
    <image xlink:href="https://i.pinimg.com/originals/46/3a/12/463a1244e2e2627c53ff9806e2012c84.jpg" width="1920" height="1080" x="0" y="0" clip-path="url(#clips)"/>

感谢您的帮助。

下一个演示的灵感来自本教程:2D Raycasting;

主要思想是使用白色光线作为图像的蒙版。请更改 css 中的描边宽度以获得更暗/更清晰的图像。

请将鼠标移到 svg canvas 上以查看其变化。

同时阅读代码中的注释,不要忘记查看 Daniel Shiffman 的教程。

let SVG_NS = "http://www.w3.org/2000/svg";
let SVG_XLINK = "http://www.w3.org/1999/xlink";
let svg = document.querySelector("svg");

let m = { x: 0, y: 0 };// the initial mouse position
let record = 600;//the maxim length of a ray

let walls = [];//the array of the walls 

//list of points for the boundary (walls)
let p1 = {
  x: 300,
  y: 100
};
let p2 = {
  x: 200,
  y: 300
};

let p3 = {
  x: 10,
  y: 300
};
let p4 = {
  x: 300,
  y: 200
};

class Particle {
  constructor(pos) {
    this.pos = pos;
    this.rays = [];

    for (let a = 0; a < 2 * Math.PI; a += Math.PI / 360) {
      this.rays.push(new Ray(this.pos, a));
    }
    // Uncomment to visualize the particle
    //let o = { cx: this.pos.x, cy: this.pos.y, r: 2, fill: "red" };
    //this.element = drawSVGelmt(o, "circle", svg);
  }

  show(m) {
    //update the position of the mouse
    this.update(m.x, m.y);
    //empty the group of rays
    rys.innerHTML = "";
    // Uncomment to visualize the particle
    //let o = { cx: this.pos.x, cy: this.pos.y };
    //this.element = updateSVGElmt(o, this.element);

    //first cast the rays
    for (let i = 0; i < this.rays.length; i++) {
      for (let w = 0; w < walls.length; w++) {
        this.rays[i].cast(walls[w]);
      }
    }
    
    
    //next draw the rays and append them to the rys group
    for (let i = 0; i < this.rays.length; i++) {
      //if the ray is intersecting one of the walls
      if (this.rays[i].intersection) {
        //set the attributes of the ray line
        var l = {};
        l.x1 = this.pos.x;
        l.y1 = this.pos.y;
        l.x2 = this.rays[i].intersection.x;
        l.y2 = this.rays[i].intersection.y;
        //draw ray and append it to the rys group
        this.line = drawSVGelmt(l, "line", rys);
      }
    }
  }
  update(x, y) {
    //update all the rays inside the rys
    this.rays.map((r) => {
      r.update();
    });
    //reset the position of the particle
    this.pos.x = x;
    this.pos.y = y;
  }
}

class Ray {
  constructor(pos, a) {
    this.pos = pos;//the starting point
    this.angle = a;//the angle of the ray
    this.maxLength = record;
    this.dir = {//the direction of a ray with an initial length of 1 unit
      x: Math.cos(this.angle),
      y: Math.sin(this.angle)
    };
  }

  cast(wall) {//cast the ray against the wall
    let p4 = {};
    p4.x = this.pos.x + this.dir.x;
    p4.y = this.pos.y + this.dir.y;
    
    // see if the ray is intersecting the wall
    let Intersection = Intersect(wall.a, wall.b, this.pos, p4);

    if (Intersection) {
      let length = dist(this.pos, Intersection);
      if (length < this.maxLength) {
        this.maxLength = length;
        this.intersection = Intersection;
      }
    }
  }


  // update the ray when the mouse (m) is moving
  update() {
    this.pos = { x: m.x, y: m.y };
    this.intersection = false;
    this.maxLength = record;
  }
}


//the walls
class Boundary {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }

  show() {
    // the attributes for the line
    let o = {};
    o.x1 = this.a.x;
    o.y1 = this.a.y;
    o.x2 = this.b.x;
    o.y2 = this.b.y;
    o.class = "boundary";// a class to style the walls 
    //draw and append the wall line
    this.line = drawSVGelmt(o, "line", wls);
  }
}

walls.push(new Boundary(p1, p2));
walls.push(new Boundary(p3, p4));

walls.forEach((w) => {
  w.show();
});

let p = new Particle(m);
p.show(m);

//HELPERS


// a function to get the intersection point of 2 lines, Returns the point of intersection or false if there is no intersection point
function Intersect(p1, p2, p3, p4) {
  var denominator =
    (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y);
  var ua =
    ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) /
    denominator;
  var ub =
    ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) /
    denominator;
  var x = p1.x + ua * (p2.x - p1.x);
  var y = p1.y + ua * (p2.y - p1.y);
  if (ua > 0 && ua < 1 && ub > 0 /*&& ub < 1*/) {
    return { x: x, y: y };
  } else {
    return false;
  }
}


// a function to draw an svg element
function drawSVGelmt(o, tag, parent) {
  let elmt = document.createElementNS(SVG_NS, tag);
  for (let name in o) {
    if (o.hasOwnProperty(name)) {
      elmt.setAttributeNS(null, name, o[name]);
    }
  }
  parent.appendChild(elmt);
  return elmt;
}
// a function to update an svg element
function updateSVGElmt(o, element) {
  for (var name in o) {
    if (o.hasOwnProperty(name)) {
      element.setAttribute(name, o[name]);
    }
  }
  return element;
}


//a function to get the angle of a line from p1 to p2
function getAngle(p1, p2) {
  let dx = p2.x - p1.x;
  let dy = p2.y - p1.y;
  let angle = Math.atan2(dy, dx);
  return angle < 0 ? 2 * Math.PI + angle : angle;
}

//a function to get the distance between 2 points: p1 & p2
function dist(p1, p2) {
  let dx = p2.x - p1.x;
  let dy = p2.y - p1.y;
  return Math.sqrt(dx * dx + dy * dy);
}


//a function to get the mouse position inside an svg element
function oMousePosSVG(e) {
  let p = svg.createSVGPoint();
  p.x = e.clientX;
  p.y = e.clientY;
  let ctm = svg.getScreenCTM().inverse();
  p = p.matrixTransform(ctm);
  return p;
}

svg.addEventListener("mousemove", function (e) {
  m = oMousePosSVG(e);
  p.show(m);
});
*{margin:0;padding:0;}
body{background:black;}
svg{border:1px solid silver;width:min(100vw,100vh)}

line{stroke:white;stroke-width:1px}
.boundary{stroke:white;stroke-width:2px}
<svg viewBox="0 0 400 400">
  <g id="wls"></g>
  <mask id="m">
  <g id="rys"></g>
  </mask>
  
  <image xlink:href="https://assets.codepen.io/222579/darwin300.jpg" height="400" width="400" mask="url(#m)"></image>
</svg>