JavaScript 凸面形状上的墙壁碰撞,卡在角落

2023-12-07

这是另一个问题的后续:如何处理玩家与墙角的碰撞

在给出的代码的启发下它的答案,我尝试编写一些新代码。

基本上,在原版中,墙壁滑动在墙壁内部效果很好,但我想让它在外部也起作用,所以我根据他的技术制作了一个新的基本代码引擎:

var aD =[]
var r
function start() {
	r = new CanvasRenderer(can),
		my = new scene();
	window.my = my
	eventHandler();
	my.add(new mesh({
		verts: [
			0,   0,
			100, 15,
			115, 60,
			50, 100,
			20, 75,2,8
		],
		position: {
			x: 100,
			y:100
		},
		scale: {

			x:4,y:5
		},
		color:"orange",
		onupdate(me) {
		//	me.position.x++
		}
	}));
	var g = false
	my.add(new mesh({
		primitive:"rect",
		name: "player",
		scale: {
			x: 50,
			y:50
		},
		position: {
			x: 311,
			y:75
		},
		origin: {
			x:0.5,
			y:0.5
		},
		onupdate(me) {
			var upKey = keys[38],
				downKey = keys[40],
				rightKey = keys[39],
				leftKey = keys[37],
				drx  = 0,
				dx = 0,
				speed = 5,
				turningSpeed = 3
			
			drx = leftKey ? -1 : rightKey ? 1 : 0
			forward = upKey ? 1 : downKey ? -1 : 0

			me.rotation.x += (
				(drx * Math.PI / 180 * turningSpeed )
			)
			me.rotation.y = 1;

			var xDir = Math.cos(me.rotation.x)
			var yDir = Math.sin(me.rotation.x)
			
			me.position.x += xDir  * forward * speed
			me.position.y += yDir * forward * speed

			for(var i = 0; i < my.objects.length; i++) {
				let cur = my.objects[i];
				if(cur.name !== me.name) {
					cur.lineSegments.forEach(l => {
						var col = checkCollision(
							me.position.x,
							me.position.y,
							me.scale.x/2,
							l
						)
						
						if(col) {
		
							me.position.y=col.y
							me.position.x = col.x
						}
					 });
				}
			}


		
			
		}

	}));
	
	let i = setInterval(() => render(r, my), 16);
	r.on("resize", () => render(r, my));

}

function checkCollision(x1, y1, rad,l) {
		var dist = distance2(
							l.start[0],
							l.start[1],
							
							l.end[0],
							l.end[1]
						),
							vec1 = [
								x1 - l.start[0],
								y1 - l.start[1]
							],

							vec2 = [
								l.end[0] - l.start[0],
								l.end[1] - l.start[1]
							],

							percentOfWall = (
								Math.max(
									0,
									Math.min(
										1, 
										dot(
											vec1[0],
											vec1[1],

											vec2[0],
											vec2[1]
										) / dist
									)
								)
							),
							projection = [
								l.start[0] + percentOfWall * vec2[0],
								l.start[1] + percentOfWall * vec2[1],
							],
							acDist = Math.sqrt(distance2(
								x1, y1,
								projection[0], projection[1]
							))
aD.push( () => {
						r.ctx.beginPath()
						r.ctx.fillStyle="green"
						r.ctx.arc(projection[0], projection[1], 5, 0, Math.PI*2);
						r.ctx.fill()
						r.ctx.closePath();
						})

					
					if(acDist < rad) {
						var mag = Math.sqrt(dist),
							delt = [
							l.end[0] - l.start[0],
							l.end[1] - l.start[1]
						],
							normal = [
							delt[0] / mag,
							delt[1] / mag
						]
						
						return {
						
							x: projection[0] + 

							rad * (normal[1] ),
						
							 y:projection[1] + 
							rad* (-normal[0] ),
							projection,
							normal
						}
					}

					
}


function dot(x1, y1, x2, y2) {
	return (
		x1 * x2 + y1 * y2
	)
}

function distance2(x1, y1, x2, y2) {
	let dx = (x1 - x2), dy = (y1 - y2);
	return (
		dx * dx + dy * dy
	);
}

function render(r,s) {
//r.ctx.clearRect(0,0,r.ctx.canvas.width,r.ctx.canvas.height)
	s.update();
	r.render(s)
	aD.forEach(x=>x());
	aD = []
}

onload = start;

function eventHandler() {
	window.keys = {};
	addEventListener("keyup" , e=> {
		keys[e.keyCode] = false;
			
	});

	addEventListener("keydown" , e=> {
		keys[e.keyCode] = true;
	});
}

function CanvasRenderer(dom) {
	if(!dom) dom = document.createElement("canvas");
	
	var events = {}, self = this;
	function rsz() {
		dom.width = dom.clientWidth;
		dom.height = dom.clientHeight;
		self.dispatchEvent("resize");
	}
	
	window.addEventListener("resize", rsz);	

	let ctx = dom.getContext("2d");

	function render(scene) {
		ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
		for(let i = 0; i < scene.objects.length; i++) {
			let o = scene.objects[i],
				verts = o.realVerts;

			
			ctx.beginPath();
			ctx.moveTo(
				verts[0] , 

				verts[1]
			);
			verts.forEach((v, i, ar) => {
				let y = i;
				


				ctx.lineTo(
					v[0] , 

					v[1]
				);
				
			});
			ctx.lineTo(
				verts[0],
				verts[1] 
			);
			ctx.fillStyle = o.color || "blue";
			ctx.lineWidth = 1;
			ctx.fill()
			ctx.stroke();
			ctx.closePath();
		}
	}

	Object.defineProperties(this, {
		domElement: {
			get: () => dom
		},	
		ctx: {
			get: () => ctx
		},
		render: {
			get: () => render
		},
		on: {
			get: () => (nm, cb) => {
				if(!events[nm]) {
					events[nm] = [];
				}
				events[nm].push(data => {
					if(typeof cb == "function") {
						cb(data);
					}
				});
			}		
		},
		dispatchEvent: {
			get: () => (name, data) => {
				if(events[name]) {
					events[name].forEach(x => {
						x(data);
					});
				}
			}
		}
	});
	
	rsz();

}

function scene() {
	let objects = [];
	Object.defineProperties(this, {
		add: {
			get: () => obj => {
				objects.push(obj);
			}
		},
		objects: {
			get: () => objects
		},
		update: {
			get: () => () => {				
				objects.forEach(x => {
					if(typeof x.update == "function") {
						x.update();
					}
				});
				
			}
		}
	});
}

function mesh(data={}) {
	let verts = [],
		self = this,
		holder = {
			position:{},
			scale: {
				
			},
			rotation: {},
			origin:{}
		},
		actual = {
	
		},
		position = {},
		scale = {},
		rotation = {},
		origin = {},
		color,
		name,
		primitive,
		eventNames = "update",
		events = {},
		drawPrimitive = {
			circle(ctx) {
				ctx.beginPath();
				ctx.arc(
					self.position.x,
					self.position.y,
					5, 
					0,
					360 * Math.PI / 180
				);
				ctx.closePath();
			},
			rect(ctx) {
				ctx.strokeRect(
					self.position.x,
					self.position.y,
					30, 30
				);
			}
		},
		width = 1,
		height = 1,
		primitiveToVerts = {
			rect: () =>  [
					0, 0,
					width , 0,
					width, height,
					0, height
			]
		},
		realVerts = verts,
		lineSegments = [],
		o = this;
	
	function updateRealVerts() {
			
			let  actualVerts = [],
				originedVerts = [],
				adjustedVerts = [],
				rotatedVerts = [],
				stepSize = o.step || 2,
				curVerts = [];
			
			o.verts.forEach((v, i) => {
				curVerts.push(v);
				if(
					(i - 1) % stepSize === 0 &&
					i !== 0
				) {
					actualVerts.push(curVerts);
					curVerts = [];
				}
			});
			actualVerts = actualVerts.filter(x => x.length == stepSize);
			
			originedVerts = actualVerts.map(v => [
				v[0] - o.origin.x,
				v[1] - o.origin.y,
				v[2] - o.origin.z
			]);
	
			rotatedVerts = originedVerts.map(v => 
				[

					v[0] * Math.cos(o.rotation.x) - 
					v[1] * Math.sin(o.rotation.x),

					v[0] * Math.sin(o.rotation.x) + 
					v[1] *Math.cos(o.rotation.x),
v[2]
				]
			);

			adjustedVerts = rotatedVerts.map(v => 
				[
					v[0] * 
					o.scale.x + 
					o.position.x,
	
					v[1] * 
					o.scale.y + 
					o.position.y,

					v[2] * 
					o.scale.z + 
					o.position.z,
				]
			);

			realVerts = adjustedVerts;
			updateLineSegments();
	}	

	function updateLineSegments() {
				let lines = [];
				for(let i = 0, a = realVerts; i < a.length;i++) {
					let start = [], end = []
					if(i < a.length - 1) {
						start = a[i];
						end = a[i + 1];
					} else {
						start = a[i];
						end = a[0];
					}

					lines.push({
						start, end
					})
				}
				lineSegments = lines;
	}
	Object.defineProperties(position, {
		x: {
			get: () => holder.position.x || 0,
			set: v => holder.position.x = v
		},
		y: {
			get: () => holder.position.y || 0,
			set: v => holder.position.y = v
		},
		z: {
			get: () => holder.position.z || 0,
			set: v => holder.position.z = v
		}
	});

	Object.defineProperties(scale, {
		x: {
			get: () => holder.scale.x || 1,
			set: v => holder.scale.x = v
		},
		y: {
			get: () => holder.scale.y || 1,
			set: v => holder.scale.y = v
		},
		z: {
			get: () => holder.scale.z || 1,
			set: v => holder.scale.z = v
		}
	});

	Object.defineProperties(rotation, {
		x: {
			get: () => holder.rotation.x || 0,
			set: v => holder.rotation.x = v
		},
		y: {
			get: () => holder.rotation.y || 0,
			set: v => holder.rotation.y = v
		},
		z: {
			get: () => holder.rotation.z || 0,
			set: v => holder.rotation.z = v
		}
	});

	Object.defineProperties(origin, {
		x: {
			get: () => holder.origin.x || 0,
			set: v => holder.origin.x = v
		},
		y: {
			get: () => holder.origin.y || 0,
			set: v => holder.origin.y = v
		},
		z: {
			get: () => holder.origin.z || 0,
			set: v => holder.origin.z = v
		}
	});
	

	Object.defineProperties(this, {
		verts: {
			get: ()=>verts,
			set(v) {
				verts = v
			}
		},
		name: {
			get: ()=>name,
			set(v) {
				name = v
			}
		},
		primitive: {
			get: ()=>primitive,
			set(v) {
				primitive = v;
				let newVerts = primitiveToVerts[v];
				if(newVerts) {
					this.verts = newVerts();
				}
			}
		},
		width: {
			get: ()=>width,
			set(v) {
				width = v
			}
		},
		height: {
			get: ()=>height,
			set(v) {
				height = v
			}
		},
		position: {
			get: () => position,
			set: v => {
				position.x = v.x || 0;
				position.y = v.y || 0;
				position.z = v.z || 0;
			}
		},
		scale: {
			get: () => scale,
			set: v => {
				scale.x = v.x || v.x === 0 ? v.x : 1;
				scale.y = v.y  || v.y === 0 ? v.y : 1;
				scale.z = v.z  || v.z === 0 ? v.z : 1;
			}
		},
		rotation: {
			get: () => rotation,
			set: v => {
				rotation.x = v.x || 0;
				rotation.y = v.y || 0;
				rotation.z = v.z || 0;
			}
		},
		origin: {
			get: () => origin,
			set: v => {
				origin.x = v.x || 0;
				origin.y = v.y || 0;
				origin.z = v.z || 0;
			}
		},
		color: {
			get: () => color,
			set: v => {
				color = v;
			}
		},
		realVerts: {
			get: () => realVerts
		},
		lineSegments: {
			get: () => lineSegments
		},
		update: {
			get: () => () => {
				if(events["update"]) {
					events.update.forEach(x => {
						updateRealVerts();
						x(this);
					});
				}
			}
		},
		on: {
			get: () => (nm, fnc) => {
				if(!events[nm]) events[nm] = [];
				events[nm].push(stuff => {
					if(typeof fnc == "function") {
						fnc(stuff);
					}
				});
			}
		}
	});

	eventNames.split(" ").forEach(x => {
		var name = "on" + x;
		if(!this.hasOwnProperty(name)) {
			Object.defineProperty(this, name, {
				get: () => events[name],
				set(v) {
					events[x] = [
						data => {
							typeof v == "function" && v(data)
						}
					];
				}
			});
		}
	});

	for(let k in data) {
		this[k] = data[k]
	}

	updateRealVerts();

}
canvas{
	width:100%;
	height:100%;
	position:absolute;
	top:0;	
	left:0px
}

.wow{
	float:right;
	z-index:1298737198
}
<meta charset="utf-8">
<button onclick="start()" class=wow>ok</button>
<canvas id=can>

</canvas>

请参阅第 71 行了解碰撞检测实现调用(以及那里函数的返回值)。

问题是,正如您希望看到的那样(只需全屏显示它并使用箭头键移动,尝试与角落处的橙色网格碰撞)它滑动得很好,但是当它到达角落时,它会卡在角落处。

有什么想法可以解决这个问题 - 不使用任何类型的外部库等(仅在代码片段中可用)?


我想说的是,检查一下你是否在角落里——丢弃案件percentOfWall正好是0或者确切地说1

EDIT:为了解决评论中提到的问题,我必须解释为什么你的实现陷入困境。它计算了所有墙壁的穿透力,并通过该穿透量减少了位置变化。在角落里,物体同时与两侧边缘碰撞,同时受到两侧边缘的排斥,停止了移动。

正如您正确地注意到的那样,在角落里,您的方块进入障碍物一帧,然后在随后的帧上被推出。

然而,替代解决方案更复杂且更难调试,但这里是几个选项的草图:

  • 限制碰撞一次只能从一堵墙上被击退并尽早退出forEach环形。这是一个简单的解决方案,在这个特定的例子中可以工作,但在一般情况下不起作用,例如在角落里,当你需要与两堵墙碰撞以防止你朝两个方向移动时。
  • 在每个角上添加一个小圆圈并与其碰撞以避免进入内部。法线是沿着圆心和接触点之间的线。这可以平滑拐角处的法线不连续性,并且始终存在一个单一法线,沿着该法线物体会被排斥,从一个线段连续变化到另一线段。

将“推”引导到障碍物的每个部分的外部(这就是您所要求的)不会阻止在拐角处停止(这正是您所关心的点),两面墙都会与您的物体碰撞并且“外部”将朝相反的方向。因此,它会像以前一样以同样的方式卡住,并且出于完全相同的原因 - 法线将不连续。

我希望这有帮助

var aD =[]
var r
function start() {
	r = new CanvasRenderer(can),
		my = new scene();
	window.my = my
	eventHandler();
	my.add(new mesh({
		verts: [
			0,   0,
			100, 15,
			115, 60,
			50, 100,
			20, 75,2,8
		],
		position: {
			x: 100,
			y:100
		},
		scale: {

			x:4,y:5
		},
		color:"orange",
		onupdate(me) {
		//	me.position.x++
		}
	}));
	var g = false
	my.add(new mesh({
		primitive:"rect",
		name: "player",
		scale: {
			x: 50,
			y:50
		},
		position: {
			x: 311,
			y:75
		},
		origin: {
			x:0.5,
			y:0.5
		},
		onupdate(me) {
			var upKey = keys[38],
				downKey = keys[40],
				rightKey = keys[39],
				leftKey = keys[37],
				drx  = 0,
				dx = 0,
				speed = 5,
				turningSpeed = 3
			
			drx = leftKey ? -1 : rightKey ? 1 : 0
			forward = upKey ? 1 : downKey ? -1 : 0

			me.rotation.x += (
				(drx * Math.PI / 180 * turningSpeed )
			)
			me.rotation.y = 1;

			var xDir = Math.cos(me.rotation.x)
			var yDir = Math.sin(me.rotation.x)
			
			me.position.x += xDir  * forward * speed
			me.position.y += yDir * forward * speed

			for(var i = 0; i < my.objects.length; i++) {
				let cur = my.objects[i];
				if(cur.name !== me.name) {
					cur.lineSegments.forEach(l => {
						var col = checkCollision(
							me.position.x,
							me.position.y,
							me.scale.x/2,
							l
						)
						
						if(col) {
							me.position.y=col.y
							me.position.x = col.x
						}
					 });
				}
			}


		
			
		}

	}));
	
	let i = setInterval(() => render(r, my), 16);
	r.on("resize", () => render(r, my));

}

function checkCollision(x1, y1, rad,l) {
		var dist = distance2(
							l.start[0],
							l.start[1],
							
							l.end[0],
							l.end[1]
						),
							vec1 = [
								x1 - l.start[0],
								y1 - l.start[1]
							],

							vec2 = [
								l.end[0] - l.start[0],
								l.end[1] - l.start[1]
							],

							percentOfWall = (
								Math.max(
									0,
									Math.min(
										1, 
										dot(
											vec1[0],
											vec1[1],

											vec2[0],
											vec2[1]
										) / dist
									)
								)
							),
							projection = [
								l.start[0] + percentOfWall * vec2[0],
								l.start[1] + percentOfWall * vec2[1],
							],
							acDist = Math.sqrt(distance2(
								x1, y1,
								projection[0], projection[1]
							))
aD.push( () => {
						r.ctx.beginPath()
						r.ctx.fillStyle="green"
						r.ctx.arc(projection[0], projection[1], 5, 0, Math.PI*2);
						r.ctx.fill()
						r.ctx.closePath();
						})

					
					if(acDist < rad && percentOfWall > 0 && percentOfWall < 1) {
						var mag = Math.sqrt(dist),
							delt = [
							l.end[0] - l.start[0],
							l.end[1] - l.start[1]
						],
							normal = [
							delt[0] / mag,
							delt[1] / mag
						]
						
						return {
						
							x: projection[0] + 

							rad * (normal[1] ),
						
							 y:projection[1] + 
							rad* (-normal[0] ),
							projection,
							normal
						}
					}

					
}


function dot(x1, y1, x2, y2) {
	return (
		x1 * x2 + y1 * y2
	)
}

function distance2(x1, y1, x2, y2) {
	let dx = (x1 - x2), dy = (y1 - y2);
	return (
		dx * dx + dy * dy
	);
}

function render(r,s) {
//r.ctx.clearRect(0,0,r.ctx.canvas.width,r.ctx.canvas.height)
	s.update();
	r.render(s)
	aD.forEach(x=>x());
	aD = []
}

onload = start;

function eventHandler() {
	window.keys = {};
	addEventListener("keyup" , e=> {
		keys[e.keyCode] = false;
			
	});

	addEventListener("keydown" , e=> {
		keys[e.keyCode] = true;
	});
}

function CanvasRenderer(dom) {
	if(!dom) dom = document.createElement("canvas");
	
	var events = {}, self = this;
	function rsz() {
		dom.width = dom.clientWidth;
		dom.height = dom.clientHeight;
		self.dispatchEvent("resize");
	}
	
	window.addEventListener("resize", rsz);	

	let ctx = dom.getContext("2d");

	function render(scene) {
		ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
		for(let i = 0; i < scene.objects.length; i++) {
			let o = scene.objects[i],
				verts = o.realVerts;

			
			ctx.beginPath();
			ctx.moveTo(
				verts[0] , 

				verts[1]
			);
			verts.forEach((v, i, ar) => {
				let y = i;
				


				ctx.lineTo(
					v[0] , 

					v[1]
				);
				
			});
			ctx.lineTo(
				verts[0],
				verts[1] 
			);
			ctx.fillStyle = o.color || "blue";
			ctx.lineWidth = 1;
			ctx.fill()
			ctx.stroke();
			ctx.closePath();
		}
	}

	Object.defineProperties(this, {
		domElement: {
			get: () => dom
		},	
		ctx: {
			get: () => ctx
		},
		render: {
			get: () => render
		},
		on: {
			get: () => (nm, cb) => {
				if(!events[nm]) {
					events[nm] = [];
				}
				events[nm].push(data => {
					if(typeof cb == "function") {
						cb(data);
					}
				});
			}		
		},
		dispatchEvent: {
			get: () => (name, data) => {
				if(events[name]) {
					events[name].forEach(x => {
						x(data);
					});
				}
			}
		}
	});
	
	rsz();

}

function scene() {
	let objects = [];
	Object.defineProperties(this, {
		add: {
			get: () => obj => {
				objects.push(obj);
			}
		},
		objects: {
			get: () => objects
		},
		update: {
			get: () => () => {				
				objects.forEach(x => {
					if(typeof x.update == "function") {
						x.update();
					}
				});
				
			}
		}
	});
}

function mesh(data={}) {
	let verts = [],
		self = this,
		holder = {
			position:{},
			scale: {
				
			},
			rotation: {},
			origin:{}
		},
		actual = {
	
		},
		position = {},
		scale = {},
		rotation = {},
		origin = {},
		color,
		name,
		primitive,
		eventNames = "update",
		events = {},
		drawPrimitive = {
			circle(ctx) {
				ctx.beginPath();
				ctx.arc(
					self.position.x,
					self.position.y,
					5, 
					0,
					360 * Math.PI / 180
				);
				ctx.closePath();
			},
			rect(ctx) {
				ctx.strokeRect(
					self.position.x,
					self.position.y,
					30, 30
				);
			}
		},
		width = 1,
		height = 1,
		primitiveToVerts = {
			rect: () =>  [
					0, 0,
					width , 0,
					width, height,
					0, height
			]
		},
		realVerts = verts,
		lineSegments = [],
		o = this;
	
	function updateRealVerts() {
			
			let  actualVerts = [],
				originedVerts = [],
				adjustedVerts = [],
				rotatedVerts = [],
				stepSize = o.step || 2,
				curVerts = [];
			
			o.verts.forEach((v, i) => {
				curVerts.push(v);
				if(
					(i - 1) % stepSize === 0 &&
					i !== 0
				) {
					actualVerts.push(curVerts);
					curVerts = [];
				}
			});
			actualVerts = actualVerts.filter(x => x.length == stepSize);
			
			originedVerts = actualVerts.map(v => [
				v[0] - o.origin.x,
				v[1] - o.origin.y,
				v[2] - o.origin.z
			]);
	
			rotatedVerts = originedVerts.map(v => 
				[

					v[0] * Math.cos(o.rotation.x) - 
					v[1] * Math.sin(o.rotation.x),

					v[0] * Math.sin(o.rotation.x) + 
					v[1] *Math.cos(o.rotation.x),
v[2]
				]
			);

			adjustedVerts = rotatedVerts.map(v => 
				[
					v[0] * 
					o.scale.x + 
					o.position.x,
	
					v[1] * 
					o.scale.y + 
					o.position.y,

					v[2] * 
					o.scale.z + 
					o.position.z,
				]
			);

			realVerts = adjustedVerts;
			updateLineSegments();
	}	

	function updateLineSegments() {
				let lines = [];
				for(let i = 0, a = realVerts; i < a.length;i++) {
					let start = [], end = []
					if(i < a.length - 1) {
						start = a[i];
						end = a[i + 1];
					} else {
						start = a[i];
						end = a[0];
					}

					lines.push({
						start, end
					})
				}
				lineSegments = lines;
	}
	Object.defineProperties(position, {
		x: {
			get: () => holder.position.x || 0,
			set: v => holder.position.x = v
		},
		y: {
			get: () => holder.position.y || 0,
			set: v => holder.position.y = v
		},
		z: {
			get: () => holder.position.z || 0,
			set: v => holder.position.z = v
		}
	});

	Object.defineProperties(scale, {
		x: {
			get: () => holder.scale.x || 1,
			set: v => holder.scale.x = v
		},
		y: {
			get: () => holder.scale.y || 1,
			set: v => holder.scale.y = v
		},
		z: {
			get: () => holder.scale.z || 1,
			set: v => holder.scale.z = v
		}
	});

	Object.defineProperties(rotation, {
		x: {
			get: () => holder.rotation.x || 0,
			set: v => holder.rotation.x = v
		},
		y: {
			get: () => holder.rotation.y || 0,
			set: v => holder.rotation.y = v
		},
		z: {
			get: () => holder.rotation.z || 0,
			set: v => holder.rotation.z = v
		}
	});

	Object.defineProperties(origin, {
		x: {
			get: () => holder.origin.x || 0,
			set: v => holder.origin.x = v
		},
		y: {
			get: () => holder.origin.y || 0,
			set: v => holder.origin.y = v
		},
		z: {
			get: () => holder.origin.z || 0,
			set: v => holder.origin.z = v
		}
	});
	

	Object.defineProperties(this, {
		verts: {
			get: ()=>verts,
			set(v) {
				verts = v
			}
		},
		name: {
			get: ()=>name,
			set(v) {
				name = v
			}
		},
		primitive: {
			get: ()=>primitive,
			set(v) {
				primitive = v;
				let newVerts = primitiveToVerts[v];
				if(newVerts) {
					this.verts = newVerts();
				}
			}
		},
		width: {
			get: ()=>width,
			set(v) {
				width = v
			}
		},
		height: {
			get: ()=>height,
			set(v) {
				height = v
			}
		},
		position: {
			get: () => position,
			set: v => {
				position.x = v.x || 0;
				position.y = v.y || 0;
				position.z = v.z || 0;
			}
		},
		scale: {
			get: () => scale,
			set: v => {
				scale.x = v.x || v.x === 0 ? v.x : 1;
				scale.y = v.y  || v.y === 0 ? v.y : 1;
				scale.z = v.z  || v.z === 0 ? v.z : 1;
			}
		},
		rotation: {
			get: () => rotation,
			set: v => {
				rotation.x = v.x || 0;
				rotation.y = v.y || 0;
				rotation.z = v.z || 0;
			}
		},
		origin: {
			get: () => origin,
			set: v => {
				origin.x = v.x || 0;
				origin.y = v.y || 0;
				origin.z = v.z || 0;
			}
		},
		color: {
			get: () => color,
			set: v => {
				color = v;
			}
		},
		realVerts: {
			get: () => realVerts
		},
		lineSegments: {
			get: () => lineSegments
		},
		update: {
			get: () => () => {
				if(events["update"]) {
					events.update.forEach(x => {
						updateRealVerts();
						x(this);
					});
				}
			}
		},
		on: {
			get: () => (nm, fnc) => {
				if(!events[nm]) events[nm] = [];
				events[nm].push(stuff => {
					if(typeof fnc == "function") {
						fnc(stuff);
					}
				});
			}
		}
	});

	eventNames.split(" ").forEach(x => {
		var name = "on" + x;
		if(!this.hasOwnProperty(name)) {
			Object.defineProperty(this, name, {
				get: () => events[name],
				set(v) {
					events[x] = [
						data => {
							typeof v == "function" && v(data)
						}
					];
				}
			});
		}
	});

	for(let k in data) {
		this[k] = data[k]
	}

	updateRealVerts();

}
canvas{
	width:100%;
	height:100%;
	position:absolute;
	top:0;	
	left:0px
}

.wow{
	float:right;
	z-index:1298737198
}
<meta charset="utf-8">
<button onclick="start()" class=wow>ok</button>
<canvas id=can>

</canvas>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript 凸面形状上的墙壁碰撞,卡在角落 的相关文章

  • 禁用外部点击时关闭模式

    我正在制作一些使用模式的博客物质化 但我的模态 onclick 外部和错误数据有问题 这是我的代码 main js function changepassword var user userlog val var content conte
  • JavaScript CSV 验证

    如何检查文本框中的逗号分隔值并在未找到时发出警报 如果有的话 里面应该有字符 比如A B C D function validate validate text box
  • 无法将消息发布到服务工作人员,因为控制器值为空

    我正在尝试做一个website https secure depths 31934 herokuapp com 在 Service Worker 的帮助下可以离线使用 以缓存页面所需的文件 我试图让用户控制他希望缓存的图像 为此 我使用一个
  • Javascript“this”在 IE 中丢失上下文

    以下在 firefox safari chrome 中工作正常 在 IE 中 this 似乎在 handleEvent 函数中丢失上下文 警报的结果是 object Window 这不是我想要的 当从handleEvent 输出时 this
  • Mongoose 查询执行后回调函数从未被调用

    以下是我的代码 mongoose connect mongodb localhost mydatabase var db mongoose connection db on error console error bind console
  • 如何从 javascript 错误对象读取错误消息

    有人可以帮我解决以下问题吗 我正在通过 redux 操作进行后调用 如下所示 export const addEmployee firstName surname contactNumber email gt async dispatch
  • 使用非常大的背景位置偏移是否存在性能问题?

    我正在构建一个进度条控件 并且正在研究它实际上并不显示进度 而只是旋转 正在发生某事 的指示器的情况 我的设计基本上是交替的对角条纹 本质上是一个像这样的理发杆 但是 旋转 由于希望将尽可能多的负载转移给渲染引擎 我想为此使用 CSS 过渡
  • 有没有办法让 jslint 在 javascript 的下一行中使用大括号?

    我改变了我的编码风格 function getParams entity use strict var accountID store getItem AccountID switch entity case Topic to functi
  • 有没有办法在 React 中自动播放音频而不使用 onClick 事件?

    我在尝试在 componentDidMount 中播放音频时收到此错误 未捕获 承诺中 DOMException play 失败 因为用户没有先与文档交互 componentDidMount document getElementById
  • Twitter Bootstrap - 下拉菜单 - 箭头键不适用于 Firefox 中的输入标签

    要求 我想在带有用户名和密码字段的下拉菜单中放置一个登录表单 我可以做到这一点 除了以下问题之外 一切正常 Issue 打字时我无法使用箭头键 上 下 firefox 当输入位于下拉代码之外时 这很有效 这适用于其他浏览器 例如 googl
  • setTimeout范围问题

    我在控制玩家重生的函数内部定义了一个 setTimeout 我正在创建一个游戏 var player death function this alive false Console log death var timer3 setTimeo
  • 纯 JS 相当于 Jquery eq()

    jquery 的纯等价物是什么eq 例如 我怎样才能实现 class1 class2 eq 0 text 1254 在纯 JavaScript 中 要获取数组中的元素索引 可以使用 在 JavaScript 中 因此 要重现您的代码 您可以
  • Angular UI-Router:多个 URL 到单一状态

    我已经开始使用 Angular 的 ui router 并且我正在尝试弄清楚如何让多个 URL 引用单个状态 例如 orgs 12354 overview retyrns the same pages as org overview 我的
  • 只保留 A-Z 0-9 并使用 javascript 从字符串中删除其他字符

    我正在尝试验证字符串以使它们成为有效的网址 我只需要保留 A Z 0 9 并使用以下命令从字符串中删除其他字符javascript or jquery 例如 贝儿餐厅 我需要将其转换为 百丽餐厅 所以字符被删除 只保留 A Z a z 0
  • JQuery $.ajax() 在 java servlet 中发布数据

    我想将数据发送到 java servlet 进行处理 数据将具有可变长度并采用键 值对 A1984 1 A9873 5 A1674 2 A8724 1 A3574 3 A1165 5 数据不需要这样格式化 这就是我现在的方式 var sav
  • 在 Chrome 开发者工具中禁用调试器语句

    我正在尝试对恶意 JavaScript 进行逆向工程 当我最初加载侧面时 会注入 JS 代码 其中包括 debugger 语句并将断点注入我的 chrome 开发人员控制台 通过stackoverflow阅读 禁用所有断点does not帮
  • 此版本的 CLI 仅与 Angular 版本 5.0.0 或更高版本兼容错误

    我已经有 Angular 项目在 4 版本中运行 在安装新项目时 不幸的是我安装了 6 版本的 Angular cli 在以 4 版本运行的旧项目中运行 ngserve 命令时 这会引发错误 您的全局 Angular CLI 版本大于本地版
  • 从请求url获取hash参数

    我有这样的网址 http www coolsite com daily plan id 1 http www coolsite com daily plan id 1解析该字符串并读取哈希值 id 之后的值 的最简单方法是什么 谢谢 在客户
  • ‘state’未定义 no-undef

    我使用教程来学习 React 但我很快就陷入困境 在教程中 他们使用以下代码 import React Component from react class Counter extends Component state count 0 r
  • VS Code 扩展 - 获取完整路径

    我正在为 VS Code 编写一个插件 我需要知道调用扩展的文件的路径 无论是从编辑器上下文菜单或资源管理器上下文菜单调用还是用户只需键入扩展命令 function activate context get full path of the

随机推荐

  • NSURLSession didCompleteWithError: 被调用时 NSError 为 nil

    场景是在上传过程中多次将应用程序带到后台和前台 在将应用程序带到前台时调用 didCompleteWithError 方法 错误参数为 null 概率是1 3 由于错误参数没有提供任何信息 我如何找出出了什么问题 问题是 didComple
  • 使用“A Simple Delphi Wrapper for SQLite3”插入日期

    我正在使用 delphi 2010 和 Tim Anderson 的 SQLite3 包装器 http www itwriting com blog page id 659 但我在插入日期时遇到问题 这是我的数据库创建 DB ExecSql
  • 如何在 html 按钮上单击调用 php 脚本/函数

    在有人对我进行攻击或对此进行标记之前 我已经在互联网上查找了如何执行此操作 包括 stackoverflow 上的相同问题 我是新人 我发现学习新概念非常困难 所以请对我宽容一点 我想做的是在单击按钮时调用 php 脚本 函数 如果有帮助的
  • GCC -fobjc-direct-dispatch 选项到底有什么作用?

    海湾合作委员会手册说 fobjc direct dispatch 允许快速跳转到消息调度程序 在 达尔文这是通过通讯页面完成的 我可以假设这个标志消除了动态调度吗 它是如何工作的 我相信如果直接链接它应该和 C 函数调用一样快 不 动态调度
  • 如何将大型 F# 记录数组保存到文件中?

    我想将一个大型 f 记录数组 gt 10 000 000 个元素 保存到磁盘 以便以后可以轻松地将数组重新加载到内存中 我使用 Visual F 2010 中的以下简单函数进行技术计算 let save filename x use str
  • 如何使用 jquery mobile 制作可拖动的 li 元素?

    我是网络移动新手 当我想在我们中制作可拖动项目时 我只使用jquery ui 在移动Web应用程序中如何做到这一点 我正在使用jquerymobile 我们应该在网络移动中使用 jquery ui 还是存在其他好方法 Thanks jQue
  • 如何查看MySQL内部innodb临时表的大小

    我看到大量内部临时磁盘表正在被写入 我可以看到计数SHOW GLOBAL STATUS where Variable name like Created tmp disk tables 我知道我可以更新max heap table size
  • Android自定义键盘:空格键的触摸区域未完全覆盖

    我开发了一款 Android 定制键盘 它看起来几乎就像原来的键盘一样 它还具有用于在字母 数字和 Shift 之间切换的特殊键 除了 SPACE 键 底部的大条 之外 所有键似乎都可以正常工作 我可以按中间的空格键但左右区域 大约空格键宽
  • Soap xml 响应使用 xsd 文件进行验证

    using https www freeformatter com xml validator xsd html 如果我在响应和模式中完全将soapenf从混合中取出 它可以正常工作 但我想两者兼而有之 仅供参考 我想指出此 wsdl 和
  • C# Windows 应用程序访问数据库数据在关闭时不会保留

    我正在使用 C 创建一个 Windows 应用程序 通过该应用程序访问一个空的 Access 数据库 其中包含两个表 省份和位置 我正在处理仅处理 Provinces 表的表单 如下所示 这是一个子表单 当它打开时 我可以插入 更新记录等
  • 在 C# 中使用 file.move 重命名新文件

    我对编码非常陌生 我正在编写一个将重命名文件的应用程序 通过将毫秒附加到已从 MFD 扫描的文件的现有文件名中 该文件夹是共享文件夹 重命名的文件应保留在其中 而不是复制到其他地方 通过大量搜索 我知道 File Move 是我前进的方向
  • 如何在不同主机之间移动 Docker 容器?

    我找不到将运行 docker 的容器从一台主机移动到另一台主机的方法 有什么方法可以将我的容器推送到存储库 就像我们对图像所做的那样 目前 我没有使用数据卷来存储与容器内运行的应用程序关联的数据 因此 一些数据驻留在容器内 我想在重新设计设
  • 解释“if”函数中的“条件长度> 1”警告

    我有一个数组 a lt c 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0
  • Android:覆盖 Android 相机预览

    我正在使用相机 API 并调用相机 我想在相机预览的顶部显示标题 用于品牌 标题是 jpeg 图像 是否可以 任何帮助表示赞赏 提前致谢 我的代码如下 public class CameraActivity extends Activity
  • git format-patch < 自上次 svn rebase 以来的所有内容 >

    我正在尝试将自上次推送到 svn 服务器以来的所有更改合并为一个大补丁 我可以通过电子邮件发送给我的同事进行审查 我可以这样做吗git format patch 你可以使用git format patch origin master获取自当
  • 闭源程序集“无法验证的代码未通过策略检查”

    我试图在 MSI 安装例程期间从 C 程序中的资源流动态加载一些 购买的 程序集 但我收到 无法验证的代码未通过策略检查 我在线阅读了一些有关使用 clr safe 编译嵌入式程序集的提示 但我没有该选项 有没有办法绕过这个政策检查 Tha
  • Vue.js Router:组件准备就绪时运行代码

    我正在使用 Vue js 及其官方路由器开发一个单页应用程序 我使用路由器加载的每个部分都有一个菜单和一个组件 vue 文件 在每个组件中我都有一些与此类似的代码
  • React Native 和 iOS 模拟器的地理位置错误

    我正在使用react native maps 并使用react native api 进行地理定位 当我通过 API 使用位置时 模拟器上返回的结果显示我在旧金山 而不是我所在的科罗拉多州丹佛市 是否有原因导致我的位置无法显示我所在的位置
  • 使用纯js获取具有特定类的单击元素的下一个元素

    我试图通过以下方式获取具有特定类的第一个元素 该类遵循使用纯 JS 无 JQuery 单击的元素 但 get el nextSibling 不是函数错误 最初我使用的是 JQueryparents next 但想用纯 JS 来做到这一点 c
  • JavaScript 凸面形状上的墙壁碰撞,卡在角落

    这是另一个问题的后续 如何处理玩家与墙角的碰撞 在给出的代码的启发下它的答案 我尝试编写一些新代码 基本上 在原版中 墙壁滑动在墙壁内部效果很好 但我想让它在外部也起作用 所以我根据他的技术制作了一个新的基本代码引擎 var aD var