Рамка: не допускать перекрытия двух объектов.
После демоверсии Minecraft. Как я могу сделать так, чтобы блоки не появлялись в одном месте? Это сценарий, который порождает коробки.
Есть два подхода, которые я могу придумать:
- Создайте список отслеживания координат, который я установил для поля, и не создавайте его, если точка пересечения совпадает (или близка к порогу).
- Проверьте, соответствует ли сетка ограничивающему прямоугольнику (используя Box3 Threejs, который я не знаю, как использовать).
Есть идеи о том, как лучше подойти к этому?
1 ответ
Это мой ответ на #2: я создал массив. Этот массив будет содержать точки evt.detail.intersection.point. Перед вставкой нового объекта я применяю Пифагор (точки x и z) и сравниваю его с порогом. Только если он выше порога, я позволяю ему продолжать и сохраняю новые точки в нем.
Весь код ниже, с комментариями:
Я использовал здесь TypeScript, я не буду его включать, чтобы он охватывал более широкую аудиторию.
Я обернул компонент intersection-spawn классом ES2015 только для разделения кода. Я не знаю родного способа создания A-Frame через ES2015.
Это основной класс "intersection-spawn.js"
export default class IntersectionSpawn {
constructor(lamp) {
//The array that will track the position.
this.positionHistory = new Array();
//The spacing which it will allow to span another light.
this.minSpacing = 2;
//Captures the class' *this* so it can be used in the
//Event Listener.
const _this = this;
//Dependency Injection. Injects the lamp class that manages
//the lamp creation.
this.lamp = lamp;
AFRAME.registerComponent('intersection-spawn', {
schema: {
default: '',
parse: AFRAME.utils.styleParser.parse
},
init: function () {
//This data comes from the HTML's <a-entity> attribute
const data = this.data;
//References the current element. This comes from A-Frame.
const el = this.el;
//Reducing the code a little bit.
//This will create an event listener and pass it to the
//intersection method.
el.addEventListener(data.event, evt => {
_this.intersection(evt, el, data)
});
}
});
}
//This takes care of create the element and insert it.
intersection(evt, el, data) {
//Just a safeguard. If the event data doesn't contain
//the intersection property, then I can't do anything.
if (evt.detail.hasOwnProperty("intersection") === false)
return;
//Define a position object to keep hold of everything.
//Note that in here I'm just selecting points x and z
//because in my app, those are the only ones which interests
//me. "y" is also available by using vt.detail.intersection.point.y
let pos = {
x: evt.detail.intersection.point.x,
z: evt.detail.intersection.point.z
};
//If true then it continues, and adds the element.
//Otherwise exit.
if (!this.canAddToGrid(pos))
return;
//Creates a new lamp to be inserted.
const elem = this.lamp.generate(data, pos);
el.sceneEl.appendChild(elem);
this.appendToHistory(pos);
}
//Adds to the current history to be tracked.
appendToHistory(pos) {
this.positionHistory.push(pos);
}
/**
* Checks whether it's posisble to add to the grid or not.
* This will check if the distance of the current insertion point
* is equal or smaller to the distance to any of the cylinders.
* If that's the case, it will return false. Otherwise it will return
* true.
* The position of the current object to be inserted.
* @param pos
*/
canAddToGrid(pos) {
for (let position of this.positionHistory) {
if (this.calcDistance(pos.x, pos.z, position.x, position.z) <= this.minSpacing) {
return false;
}
}
return true;
}
/**
* Calculates the distance from the center of the lamp to the center
* of the insertion points.
*
* @param x1 Position x of the object to be inserted
* @param z1 Position z of the object to be inserted
* @param x2 Position x of the object inside the array
* @param z2 Position z of the object inside the array
*/
calcDistance(x1, z1, x2, z2) {
return Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((z2 - z1), 2));
}
}
Это lamp.js (тот, который создает объект лампы) и добавляется в класс intersectionSpawn:
export default class Lamp {
constructor() {
}
/**
* Creates the Lamp. Right now it's a cylinder.
* @param pos The positions you want the lamp to be in.
*/
create(pos) {
let elem = (document.createElement("a-cylinder"));
elem.setAttribute('width', "1");
elem.setAttribute('height', "4");
elem.setAttribute('depth', "1");
elem.setAttribute('position', `${pos.x} 0 ${pos.z}`);
return elem;
}
/**
* This works like a decorator. this was originaly in the intersection-spawn.
* I do not know a lot what it does, but it's necessary for the element to work.
* @param elem The generated element from the create(pos) method.
* @param data Comes from A-Frame's data.
*/
AddAframeUtils(elem, data) {
Object.keys(data).forEach(name => {
if (name === 'event') { return; }
AFRAME.utils.entity.setComponentProperty(elem, name, data[name]);
});
return elem;
}
/**
* The public method which generates a fully functional element.
* @param data This comes from A-Frame's data.
* @param position The position in which I want to create the element.
*/
generate(data, position) {
return this.AddAframeUtils(this.create(position), data);
}
}
script.js, который включает оба класса:
import Lamp from './js/lamp/lamp';
import IntersectionSpawn from './js/components/intersection-spawn';
new IntersectionSpawn(new Lamp());
А теперь index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://rawgit.com/mayognaise/aframe-mouse-cursor-component/master/dist/aframe-mouse-cursor-component.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<a-scene>
<a-sky color="#ECECEC"></a-sky>
<a-camera>
<!-- We include the intersection-spawn in here:-->
<a-cursor intersection-spawn="event: click;"></a-cursor>
</a-camera>
</a-scene>
</body>
</html>