JavaScript墙在凸形上碰撞,卡在角落 [英] JavaScript wall collision on convex shapes, getting stuck at corner

查看:119
本文介绍了JavaScript墙在凸形上碰撞,卡在角落的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

this is a follow up to this other question: How do I handle player collision with corners of a wall

答案,我尝试编写一些新代码。

In inspiration of the code given in its answer, I tried to write some new code.

基本上,在最初,墙壁的滑动在墙壁的内部效果很好,但是我想使其在外部也起作用,所以我做了基于他的技术的新的基本代码引擎:

Basically, in the original, the wall sliding works very well on the inside of the walls, but I wanted to make it work on the outside as well, so I made a new basic code engine, based on his technique:

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行。

See line 71 for the collision detection implementation call (and the return value of the function there).

问题是,您可以希望看到它(可以全屏显示并使用箭头键移动,尝试与角处的橙色网格碰撞),但是滑动得很好。

The problem is, as you can hopefully see (just fullscreen it and use arrow keys to move, try colliding with the orange mesh at the corners) that it slides fine, but when it gets to the corners, it gets stuck at them.

任何解决此问题的想法-无需使用任何类型的外部库等(仅摘录中提供的内容)?

Any ideas how to fix this -- without using any kind of external libraries etc. (only what's available in the snippet)?

推荐答案

我会说,只是检查一下您是否在角落里-丢弃 percentOfWall 的情况code>恰好是 0 或恰好是 1

I would say just check that you are not in the corner - discard cases when percentOfWall is either exactly 0 or exactly 1

编辑:要解决评论中提到的问题,我必须解释您的实施为何陷入困境。它计算了所有壁的穿透力,并通过该穿透量减少了位置变化。在拐角处,物体立即与两个边缘碰撞,并立即被排斥而停止移动。

EDIT: to address the corners mentioned in the comments I have to explain why your implementation got stuck. It calculated penetration with all the walls and decreased position change by this penetration amount. In the corner, the object collided with both edges at once, and being repulsed from both at once stopped moving.

如您所知,拐角处的正方形进入了拐角处

As you rightly noticed, in the corners your square gets inside the obstacle for one frame and then is pushed out on the consequent frame.

但是,替代解决方案更复杂且更难调试,但这是一个解决方案的草图。几种选择:

Alternative solutions, however, are more complicated and harder to debug, but here is a sketch for a couple of options:


  • 限制碰撞,一次只能从一堵墙排斥,并尽早退出 forEach 循环。这是一个简单的解决方案,将在该特定示例中起作用,但在一般情况下将不起作用,例如在拐角处,当您需要与2面墙碰撞而无法双向行驶时。

  • 在每个角上添加一个小圆圈,并与之碰撞以避免进入内部。法线沿着圆心和接触点之间的线。这样可以平滑角落中的法线不连续性,并且始终存在一个单一法线,物体会沿着该法线被排斥,并不断从一个线段变为另一个线段。

  • Limit the collisions to repulse from only one wall at a time and early exit your forEach loop. This is a simple solution which will work in this particular example but won't work in the general case, for example in the corner when you need to collide with 2 walls preventing you to go in both directions.
  • Add a tiny circle in each of the corners and collide with it to avoid going inside. The normal is along the line between the center of the circle and the point of contact. That smooths the normal discontinuity you have in the corners and there is always a single normal along which the object is repulsed, continuously changing from one segment to the other.

将推指向障碍物的每个部分之外(这就是您要问的)并不能阻止在拐角处(这正是您所关注的点)停下来,两堵墙都会与您的对象碰撞,外侧将朝相反的方向。因此,它将以与以前相同的方式被卡住,并且由于完全相同的原因-法线将不会连续。

Directing the "push" to outside each segment of the obstacle (which is what you are asking) wouldn't prevent stopping as on the corner (which is exactly the point you are concerned with), both walls will collide with your object and "outside" will be in the opposite directions. So it'll get stuck the same way as before and for exact same reason - normals will be not continuous.

我希望这会有所帮助

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>

这篇关于JavaScript墙在凸形上碰撞,卡在角落的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆