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>
我实际上正在开发一个小游戏,我正在尝试用障碍物来阻止地图显示。目前我有这个:
我当然想要这个:
我尝试了 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>