Hey guys I have been working on a little project of mine utilizing the awesome library three.js
Now I have been working with the example of https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/Collision-Detection.html to handle collision detection, more so on when an object over laps another object utilizing voxel's.
To reference my problem I am using the http://threejs.org/examples/#canvas_interactive_voxelpainter example.
Anyways to continue, when I render a voxel onto the screen, anything above the cube will allow me to render another voxel anything within a certain radius below the volex will not let me render:
Here is shown above cube:
Now here is my neat little function I put together using the example provided by stemkoski:
checkOverlapObject: function(voxel) // THIS IS USED TO SEE IF WE ARE OVER LAPPING ANY OBJECTS
{
var originPoint = voxel.position.clone();
var collidableObjs = this.rooms;
for (var vertexIndex = 0; vertexIndex < voxel.geometry.vertices.length; vertexIndex++)
{
var localVertex = voxel.geometry.vertices[vertexIndex].clone();
console.log(localVertex);
var globalVertex = localVertex.applyMatrix4( voxel.matrix );
console.log(globalVertex);
var directionVector = globalVertex.sub( voxel.position );
console.log(directionVector);
console.log(originPoint);
console.log(directionVector.clone().normalize());
if(collidableObjs.length > 0)
{
var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() );
var collisionResults = ray.intersectObjects( collidableObjs );
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )
{
console.log(collisionResults);
console.log(collisionResults[0].distance);
console.log( directionVector.length() );
return false
}
}
}
return true;
},
Now what happens here is, before actually adding a rendered volex the user gets a preview of if they have permission to add the volex So we pass a volex made by:
var voxel = new THREE.Mesh( this.room.cubeGeometry, this.room.cubeTmpHoverMaterial );
voxel.geometry.computeBoundingBox();
voxel.position.copy( intersect.point ).add( intersect.face.normal );
voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
voxel.material.color.setHex(this.colorTmpHover);
into our checkOverlapObject function to see if the object is overlapping a object that has been rendered onto the screen/grid
Now following my little neat function I made, I have placed console.log to output parameters and here is some of that output:
T…E.Vector3 {x: 25, y: 25, z: 25} <!-- our localVertex
T…E.Vector3 {x: 25, y: 25, z: 25} <!-- our globalVertex
T…E.Vector3 {x: 0, y: 0, z: -350} <!-- our directionVector
T…E.Vector3 {x: 25, y: 25, z: 375} <!-- our originPoint
T…E.Vector3 {x: 0, y: 0, z: -1} <!-- our directionVector.clone().normalize()
[Object, Object] <!-- our collisionResults
225 <!-- our collisionResults[0].distance
350 <!-- our directionVector.length()
This data is based off of the first picture.
Please understand that I have other volex that take up 2 blocks on the grid or more. So the reason for this is, I have a center location of the position but I need to take into account the rest of the object if it takes up 2 blocks on the grid to check if that overlaps the already rendered volex I don't care if they touch each other.
Any suggestions or thoughts as to what might be the problem?
As asked above I finally found a solution. I created a custom function for object collision/overlap.
Firstly, here is whats wrong with the above collision code as shown above, it process's the raycaster starting with the direction of our object - shooting a line straight across from its orgin to see if anything comes into contact with that ray. Now that is great and all but not very sufficient in telling when we have a overlap/collision if any part of the object is touching another object
As shwon with our rays:
So here is my solution.
When ever we have an object on our plane, we know everything about it, our coordinates of x,y,z - I am not dealing with y in this example so ignore that.
What we care about is our objects width = x and our objects depth/length = z. So in essence when we are checking to see if an object is overlapping another object we simply need to process through each min x/z and max x/z of our to be added object and our already rendered obj
How we do that is, loop through our object to be added starting min z position with a for loop. This starting min z position contains our position z + min z of object. This gives us the exact z position on our plane.
We than move on to our width or our object to be added starting min x position with a for loop. Don't forget our x gives us the exact position on our plane
Now the reason for this is simple. We want to start at position 0,0 of our object to be added. Increment by one up our length/z - increment all the way down our width/x and while doing this process each object on our plane that is already rendered and check to see if any exact point matches ours.
Here is the actual method:
checkOverlapObject: function(voxel) // THIS IS USED TO SEE IF WE ARE OVER LAPPING ANY OBJECTS
{
var collidableObjs = this.rooms;
//lets get our voxel min and max vars
var voxelMX = voxel.geometry.boundingBox.max.x;
var voxelMZ = voxel.geometry.boundingBox.max.z;
var voxelmX = voxel.geometry.boundingBox.min.x;
var voxelmZ = voxel.geometry.boundingBox.min.z;
// we need to get our voxel position ad to do some math to see if voxel min and max do not go beyound our boundries for our plane
var voxelPX = voxel.position.x;
var voxelPZ = voxel.position.z;
var totalPositionVoxelminZ = (voxelPZ + voxelmZ);
var totalPositionVoxelminX = (voxelPX + voxelmX);
var totalPositionVoxelMAXZ = (voxelPZ + voxelMZ);
var totalPositionVoxelMAXX = (voxelPX + voxelMX);
// start loop for object to add Z cordinate
for(var length = totalPositionVoxelminZ; length < totalPositionVoxelMAXZ; length++)
{
// start loop for object to add X cordinate
for(var width = totalPositionVoxelminX; width < totalPositionVoxelMAXX; width++)
{
for(var i = 0; i < collidableObjs.length;i++)
{
//lets get our voxel min and max vars
var thisvoxelMX = this.rooms[i].geometry.boundingBox.max.x;
var thisvoxelMZ = this.rooms[i].geometry.boundingBox.max.z;
var thisvoxelmX = this.rooms[i].geometry.boundingBox.min.x;
var thisvoxelmZ = this.rooms[i].geometry.boundingBox.min.z;
// we need to get our voxel position ad to do some math to see if voxel min and max do not go beyound our boundries for our plane
var thisvoxelPX = this.rooms[i].position.x;
var thisvoxelPZ = this.rooms[i].position.z;
var thistotalPositionVoxelminZ = (thisvoxelPZ + thisvoxelmZ);
var thistotalPositionVoxelminX = (thisvoxelPX + thisvoxelmX);
var thistotalPositionVoxelMAXZ = (thisvoxelPZ + thisvoxelMZ);
var thistotalPositionVoxelMAXX = (thisvoxelPX + thisvoxelMX);
for(var insideZ = thistotalPositionVoxelminZ; insideZ < thistotalPositionVoxelMAXZ; insideZ++)
{
for(var insideX = thistotalPositionVoxelminX; insideX < thistotalPositionVoxelMAXX; insideX++)
{
if(insideZ == length && insideX == width)
{
return false;
}
}
}
}
}
}
return true;
}
Here is our result:
This really does provide an exact answer on if your object is touching something it should not be touching because of the point by point it processes.
I hope this helps! Please feel free to use this at anytime.
Also note this really could effect overhead/memory usage if you are not careful on how you use this. I am working on a way to better optimize this in the case of having hundred of objects to process. So if you have any suggestions on modifying or adding to my existing code to accomplish this better and offer better performance in case of hundreds of objects passed, feel free provide details!
Related
I have a file with sparse elevations. It is based off of gps data. I have been using this data to populate an PlaneBuffer array with elevations.
var vertices = new Float32Array( (grid.NCOL*grid.NROW) * 4 );
for (var i = 0, q = vertices.length; i < q; i++){
vertices[ i*3 + 0 ] = parseInt(i % (grid.NCOL+1)*4);
vertices[ i*3 + 1 ] = parseInt(i / (grid.NCOL+1)*4);
// vertices[ i*3 + 2 ] = null; // makes no difference
}
for (var i = 0, l = grid.NODES.length; i < l; i++) {
var nodeNumber = grid.NODES[i][0];
var elevation= grid.NODES[i][1];
vertices[ nodeNumber*3 + 2 ] = elevation;
}
My problem is that there are nodes that the elevation values are unknown(Vertex array is sparse with elevations) and should be represented by holes/cutouts in the plane. What I end up with is the null elevations being interpreted as 0 not as holes. I have started down the path of using a rawshader, but still not sure that making null values transparent is the correct method.
The below picture shows my issues. The circled area is a high wall that should not be there, because it triangulating to the "null/0" floor. The red-lines area is where we should have a hole.
EDIT:
Maybe this picture will help to. It is from the bottom. The null elevations being set to zero block the view of the plane and cause the edge of the plane to be triangulated to 0 elevation:
Here is what our desktop application displays. Notice the edges of the plane are not triangulated down to zero but instead left sharp?
Plane buffer Geometries take a Float32Array. This array is default set to 0. Using undefined setter allowed me to set the sparse array out of the float32 type. Attempts to set any value to null and NanN did not work.
RTFM:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array
final result as expected:
Your use case seems more appropriate for a point cloud with THREE.Points. potree.org/demo/potree_1.3/showcase/ca13.html – WestLangley 14 mins ago
This example helped:
http://threejs.org/examples/#webgl_buffergeometry_points
I'm trying to create a chess board, and place it in the middle of the screen, so far i cannot get it to be directly in the center. i don't want to hard code the position to the screen because i'm going to be dealing with different screen sizes.
var winsize = cc.director.getWinSize();
var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
for (i=0; i<64; i++){
var tile = cc.Sprite.create(res.tile_png);
this.addChild(tile,0);
tile.setPosition(winsize.width+i%8*50/-10, winsize.height-Math.floor(i/8)*50);
}
But the tiles and positioning is completely off
#jumpman8947, if you're using Cocos2d js perhaps you have a similar line: cc.view.setDesignResolutionSize(480, 320, cc.ResolutionPolicy.SHOW_ALL);
In this particular case the game will scale to any sceeen, but still run in 480x320 resolution, so no matter what screen resoultion you use, the center in the cocos world would always be cc.p(240, 160) so no matter what's the window size or the screen resolution, the resolution of the game stays the same
You can read more about resolution policies here (and in official js-doc):
http://www.cocos2d-x.org/wiki/Multiple_Resolution_Policy_for_Cocos2d-JS
Also please be aware, that the Sprite position in Cocos is the position of the centre of the sprite, not bottom left corner
In your question it's not completely clear exactly what you want. However, I made some assumptions. The explanation for my solution is embedded in the comments in the code below.
// var winsize = cc.director.getWinSize();
// Here is some example hard-coded return values:
var winsize = {width: 600, height: 400};
// You can change these numbers to see how they influence
// the outcome.
// var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
// This line doesn't seem relevant for the question you asked.
// Or, rather, the following calculations will result in the tiles
// being centred on the screen anyway, so this calculation here
// is unnecessary.
// Being a chess board, I assume that you want the tiles to be square,
// i.e. to have the same width and height.
// If so, first find out which is the minimum dimension
// and calculate the tile size as being 1/8 of that.
var minDimn = Math.min(winsize.width, winsize.height);
var tileSize = minDimn / 8;
// Find out how far in from the left and how far down from the top
// you need the upper left corner of the upper left tile to start.
// This assumes that you don't need any "margin" around the board.
// (If you do need such a "margin", basically subtract it twice
// from each of winsize.width and winsize.height above.)
// Start with default values of 0 for each, but then add in the
// excess for the longer dimension, but divide it by two
// because that excess will be split between either
// the top and bottom or the left and right.
var xStart = 0, yStart = 0;
if (winsize.width > winsize.height) {
xStart = (winsize.width - winsize.height) / 2;
} else if (winsize.height > winsize.width) {
yStart = (winsize.height - winsize.width) / 2;
}
// Instead of looping through all 64 positions in one loop,
// loop through all the horizontal positions in an outer loop
// and all the vertical positions in an inner loop.
for (i = 0; i < 8; i++) {
// For the horizontal dimension, calculate x for each tile
// as the starting position of the left-most tile plus
// the width of the tile multiplied by the number of tiles (0-based)
var x = xStart + i * tileSize;
// Now the inner loop
for (j = 0; j < 8; j++) {
// Same type of calculation for the y value.
var y = yStart + j * tileSize;
// You can see the values in this demo here.
document.write("<pre>(" + x + ", " + y + ")</pre>");
// The following two lines don't seem to be relevant to the question.
// var tile = cc.Sprite.create(res.tile_png);
// this.addChild(tile,0);
// Use your calculated values in your function call.
// tile.setPosition(x, y);
}
}
I'm building a turn based HTML game based on a 2D square grid. Each grid square could take a variable number of movement points to cross (IE: 1 MP for roads, 1.5 MP for grasslands, 2 MP for forests, etc). When the user clicks on a unit I want to determine all possible movable spaces with said unit's allotted movement points so that I can highlight them and make them clickable.
Is there a free library available to do this? I've seen a few pathing algorithms but nothing about determining movable area. How do other game developers handle this problem? I'm open to both vanilla JS and JQuery solutions.
Well, I decided to try and attack this myself. I've never been great at these sorts of algorithms so I'm sure there's a more efficient way to handle it than what I've done. However, for my purposes it runs quickly enough and is very simple and easy to understand.
In case it's helpful to anyone else looking to do the same, I've included the code below. This is an updated version of my original answer, which I modified to also store the path taken so that you can show the units moving through the correct spaces. This answer uses JQuery in the lower examples, but only in a few places; you can easily enough replace them with vanilla JS. And the first block of code, containing the actual path/area finding functionality, is pure JS.
<script>
var possibleMovementAreaArray = new Array(); // This array will hold our allowable movement tiles. Your other functions can access this after running possibleMovementArea().
function possibleMovementArea(unitIndex) {
// I'm storing each unit in my game in an array. So I pass in the index of the unit I want to determine the movement area for.
var x = unitList[unitIndex][10]; // x coordinate on the playgrid
var y = unitList[unitIndex][11]; // y coordinate on the playgrid
var mp = unitList[unitIndex][15]; // number of movement points
possibleMovementAreaArray.length = 0; // Clear our array so previous runs don't interfere.
findPossibleMovement(x, y, mp);
}
function findPossibleMovement(x, y, mp, prevStepX, prevStepY) {
// This is a recursive function; something I'm not normally too good at.
for (var d=1; d<=4; d++) {
// We run through each of the four cardinal directions. Bump this to 8 and add 4 more cases to include corners.
if (d == 1) {
// Check Up
var newX = x;
var newY = y - 1;
} else if (d == 2) {
// Check Down
var newX = x;
var newY = y + 1;
} else if (d == 3) {
// Check Left
var newX = x - 1;
var newY = y;
} else if (d == 4) {
// Check Right
var newX = x + 1;
var newY = y;
}
// Check to see if this square is occupied by another unit. Two units cannot occupy the same space.
spaceOccupied = false;
for (var j=1; j<=numUnits; j++) {
if (unitList[j][10] == newX && unitList[j][11] == newY)
spaceOccupied = true;
}
if (!spaceOccupied) {
// Modify this for loop as needed for your usage. I have a 2D array called mainMap that holds the ID of a type of terrain for each tile.
// I then have an array called terList that holds all the details for each type of terrain, such as movement points needed to get past.
// This for loop is just looking up the ID of the terrain for use later. Sort of like a "SELECT * FROM terrainInfo WHERE ID=terrainOfCurrentTile".
for (var j=1; j<=numTerrains; j++) {
if (newX > 0 && newX <= mapWidth && newY > 0 && newY <= mapHeight && terList[j][1] == mainMap[newX][newY])
break; // After finding the index of terList break out of the loop so j represents the correct index.
}
if (j <= numTerrains) { // Run if an actual terrain is found. No terrain is found if the search runs off the sides of the map.
var newMp = mp - terList[j][7]; // Decrement the movement points for this particular path.
if (newMp >= 0) { // Only continue if there were enough movement points to move to this square.
// Check to see if this square is already logged. For both efficiency and simplicity we only want each square logged once.
var newIndex = possibleMovementAreaArray.length
var alreadyLogged = false
if (possibleMovementAreaArray.length > 0) {
for (var j=0; j<possibleMovementAreaArray.length; j++) {
if (possibleMovementAreaArray[j][1] == newX && possibleMovementAreaArray[j][2] == newY) {
alreadyLogged = true;
var alreadyLoggedIndex = j;
}
}
}
if (!alreadyLogged) {
// This adds a row to the array and records the x and y coordinates of this tile as movable
possibleMovementAreaArray[newIndex] = new Array(6);
possibleMovementAreaArray[newIndex][1] = newX;
possibleMovementAreaArray[newIndex][2] = newY;
possibleMovementAreaArray[newIndex][3] = prevStepX; // This tracks the x coords of the steps taken so far to get here.
possibleMovementAreaArray[newIndex][4] = prevStepY; // This tracks the y coords of the steps taken so far to get here.
possibleMovementAreaArray[newIndex][5] = newMp; // Records remaining MP after the previous steps have been taken.
}
if (alreadyLogged && newMp > possibleMovementAreaArray[alreadyLoggedIndex][5]) {
// If this tile was already logged, but there was less MP remaining on that attempt, then this one is more efficient. Update the old path with this one.
possibleMovementAreaArray[alreadyLoggedIndex][3] = prevStepX;
possibleMovementAreaArray[alreadyLoggedIndex][4] = prevStepY;
possibleMovementAreaArray[alreadyLoggedIndex][5] = newMp;
}
if (newMp > 0) {
// Now update the list of previous steps to include this tile. This list will be passed along to the next call of this function, thus building a path.
if (prevStepX == '') {
var newPrevStepX = [newX];
var newPrevStepY = [newY];
} else {
// This code is required to make a full copy of the array holding the existing list of steps. If you use a simple equals then you just create a reference and
// subsequent calls are all updating the same array which creates a chaotic mess. This way we store a separate array for each possible path.
var newPrevStepX = prevStepX.slice();
newPrevStepX.push(newX);
var newPrevStepY = prevStepY.slice();
newPrevStepY.push(newY);
}
// If there are still movement points remaining, check and see where we could move next.
findPossibleMovement(newX, newY, newMp, newPrevStepX, newPrevStepY);
}
}
}
}
}
}
</script>
After running the above, you can then loop through the array to find all usable tiles. Here is how I did it:
<script>
// Shows the movement area based on the currently selected unit.
function showMovement() {
var newHTML = "";
curAction = "move";
possibleMovementArea(curUnit); // See above code
for (x=0; x<possibleMovementAreaArray.length; x++) {
// Loop over the array and do something with each tile. In this case I'm creating an overlay that I'll fade in and out.
var tileLeft = (possibleMovementAreaArray[x][1] - 1) * mapTileSize; // Figure out where to absolutely position this tile.
var tileTop = (possibleMovementAreaArray[x][2] - 1) * mapTileSize; // Figure out where to absolutely position this tile.
newHTML = newHTML + "<img id='path_" + possibleMovementAreaArray[x][1] + "_" + possibleMovementAreaArray[x][2] + "' onClick='mapClk(" + possibleMovementAreaArray[x][1] + ", " + possibleMovementAreaArray[x][2] + ", 0);' src='imgs/path.png' class='mapTile' style='left:" + tileLeft + "px; top:" + tileTop + "px;'>";
}
$("#movementDiv").html(newHTML); // Add all those images into a preexisting div.
$("#movementDiv").css("opacity", "0.5"); // Fade the div to 50%
$("#movementDiv").show(); // Make the div visible.
startFading(); // Run a routine to fade the div in and out.
}
</script>
Since we determined the path, we can easily show movement as well by looping through the stored information:
<script>
for (j=0; j<possibleMovementAreaArray[areaIndex][3].length; j++) {
// This loop moves the unit img to each tile on its way to its destination. The final destination tile is not included.
var animSpeed = 150; // Time in ms that it takes to move each square.
var animEase = "linear"; // We want movement to remain a constant speed through each square in this case.
var targetLeft = (possibleMovementAreaArray[areaIndex][3][j]-1) * mapTileSize; // This looks at each step in the path array and multiplies it by tile size to determine the new horizonal position.
var targetTop = (possibleMovementAreaArray[areaIndex][4][j]-1) * mapTileSize; // This looks at each step in the path array and multiplies it by tile size to determine the new vertical position.
$("#char_"+curUnit).animate({"left":targetLeft, "top":targetTop}, animSpeed, animEase); // Do the animation. Subsequent animations get queued.
}
// Now we need to move to that last tile.
newLeft = (x-1) * mapTileSize;
newTop = (y-1) * mapTileSize;
$("#char_"+curUnit).animate({"left":newLeft, "top":newTop}, 400, "easeOutCubic"); // Slow unit at the end of journey for aesthetic purposes.
$("#char_"+curUnit).addClass("unitMoved", 250); // Turns the image grayscale so it can easily be seen that it has already moved.
</script>
Hopefully this is helpful to others.
I am creating a new "whack-a-mole" style game where the children have to hit the correct numbers in accordance to the question. So far it is going really well, I have a timer, count the right and wrong answers and when the game is started I have a number of divs called "characters" that appear in the container randomly at set times.
The problem I am having is that because it is completely random, sometimes the "characters" appear overlapped with one another. Is there a way to organize them so that they appear in set places in the container and don't overlap when they appear.
Here I have the code that maps the divs to the container..
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function scramble() {
var children = $('#container').children();
var randomId = randomFromTo(1, children.length);
moveRandom('char' + randomId);
}
function moveRandom(id) {
var cPos = $('#container').offset();
var cHeight = $('#container').height();
var cWidth = $('#container').width();
var pad = parseInt($('#container').css('padding-top').replace('px', ''));
var bHeight = $('#' + id).height();
var bWidth = $('#' + id).width();
maxY = cPos.top + cHeight - bHeight - pad;
maxX = cPos.left + cWidth - bWidth - pad;
minY = cPos.top + pad;
minX = cPos.left + pad;
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX);
$('#' + id).css({
top: newY,
left: newX
}).fadeIn(100, function () {
setTimeout(function () {
$('#' + id).fadeOut(100);
window.cont++;
}, 1000);
});
I have a fiddle if it helps.. http://jsfiddle.net/pUwKb/8/
As #aug suggests, you should know where you cannot place things at draw-time, and only place them at valid positions. The easiest way to do this is to keep currently-occupied positions handy to check them against proposed locations.
I suggest something like
// locations of current divs; elements like {x: 10, y: 40}
var boxes = [];
// p point; b box top-left corner; w and h width and height
function inside(p, w, h, b) {
return (p.x >= b.x) && (p.y >= b.y) && (p.x < b.x + w) && (p.y < b.y + h);
}
// a and b box top-left corners; w and h width and height; m is margin
function overlaps(a, b, w, h, m) {
var corners = [a, {x:a.x+w, y:a.y}, {x:a.x, y:a.y+h}, {x:a.x+w, y:a.y+h}];
var bWithMargins = {x:b.x-m, y:b.y-m};
for (var i=0; i<corners.length; i++) {
if (inside(corners[i], bWithMargins, w+2*m, h+2*m) return true;
}
return false;
}
// when placing a new piece
var box;
while (box === undefined) {
box = createRandomPosition(); // returns something like {x: 15, y: 92}
for (var i=0; i<boxes.length; i++) {
if (overlaps(box, boxes[i], boxwidth, boxheight, margin)) {
box = undefined;
break;
}
}
}
boxes.push(box);
Warning: untested code, beware the typos.
The basic idea you will have to implement is that when a random coordinate is chosen, theoretically you SHOULD know the boundaries of what is not permissible and your program should know not to choose those places (whether you find an algorithm or way of simply disregarding those ranges or your program constantly checks to make sure that the number chosen isn't within the boundary is up to you. the latter is easier to implement but is a bad way of going about it simply because you are entirely relying on chance).
Let's say for example coordinate 50, 70 is selected. If the picture is 50x50 in size, the range of what is allowed would exclude not only the dimensions of the picture, but also 50px in all directions of the picture so that no overlap may occur.
Hope this helps. If I have time, I might try to code an example but I hope this answers the conceptual aspect of the question if that is what you were having trouble with.
Oh and btw forgot to say really great job on this program. It looks awesome :)
You can approach this problem in at least two ways (these two are popped up in my head).
How about to create a 2 dimensional grid segmentation based on the number of questions, the sizes of the question panel and an array holding the position of each question coordinates and then on each time frame to position randomly these panels on one of the allowed coordinates.
Note: read this article for further information: http://eloquentjavascript.net/chapter8.html
The second approach follow the same principle, but this time to check if the panel overlap the existing panel before you place it on the canvas.
var _grids;
var GRID_SIZE = 20 //a constant holding the panel size;
function createGrids() {
_grids = new Array();
for (var i = 0; i< stage.stageWidth / GRID_SIZE; i++) {
_grids[i] = new Array();
for (var j = 0; j< stage.stageHeight / GRID_SIZE; j++) {
_grids[i][j] = new Array();
}
}
}
Then on a separate function to create the collision check. I've created a gist for collision check in Actionscript, but you can use the same principle in Javascript too. I've created this gist for inspirational purposes.
Just use a random number which is based on the width of your board and then modulo with the height...
You get a cell which is where you can put the mole.
For the positions the x and y should never change as you have 9 spots lets say where the mole could pop up.
x x x
x x x
x x x
Each cell would be sized based on % rather then pixels and would allow re sizing the screen
1%3 = 1 (x)
3%3 = 0 (y)
Then no overlap is possible.
Once the mole is positioned it can be show or hidden or moved etc based on some extended logic if required.
If want to keep things your way and you just need a quick re-position algorithm... just set the NE to the SW if the X + width >= x of the character you want to check by setting the x = y+height of the item which overlaps. You could also enforce that logic in the drawing routine by caching the last x and ensuring the random number was not < last + width of the item.
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX); if(newX > lastX + characterWidth){ /*needful*/}
There could still however be overlap...
If you wanted to totally eliminate it you would need to keep track of state such as where each x was and then iterate that list to find a new position or position them first and then all them to move about randomly without intersecting which would would be able to control with just padding from that point.
Overall I think it would be easier to just keep X starting at 0 and then and then increment until you are at a X + character width > greater then the width of the board. Then just increase Y by character height and Set X = 0 or character width or some other offset.
newX = 0; newX += characterWidth; if(newX + chracterWidth > boardWidth) newX=0; newY+= characterHeight;
That results in no overlap and having nothing to iterate or keep track of additional to what you do now, the only downside is the pattern of the displayed characters being 'checker board style' or right next to each other (with possible random spacing in between horizontal and vertical placement e.g. you could adjust the padding randomly if you wanted too)
It's the whole random thing in the first place that adds the complexity.
AND I updated your fiddle to prove I eliminated the random and stopped the overlap :)
http://jsfiddle.net/pUwKb/51/
I am using three.js.
I have two mesh geometries in my scene.
If these geometries are intersected (or would intersect if translated) I want to detect this as a collision.
How do I go about performing collision detection with three.js? If three.js does not have collision detection facilities, are there other libraries I might use in conjuction with three.js?
In Three.js, the utilities CollisionUtils.js and Collisions.js no longer seem to be supported, and mrdoob (creator of three.js) himself recommends updating to the most recent version of three.js and use the Ray class for this purpose instead. What follows is one way to go about it.
The idea is this: let's say that we want to check if a given mesh, called "Player", intersects any meshes contained in an array called "collidableMeshList". What we can do is create a set of rays which start at the coordinates of the Player mesh (Player.position), and extend towards each vertex in the geometry of the Player mesh. Each Ray has a method called "intersectObjects" which returns an array of objects that the Ray intersected with, and the distance to each of these objects (as measured from the origin of the Ray). If the distance to an intersection is less than the distance between the Player's position and the geometry's vertex, then the collision occurred on the interior of the player's mesh -- what we would probably call an "actual" collision.
I have posted a working example at:
http://stemkoski.github.io/Three.js/Collision-Detection.html
You can move the red wireframe cube with the arrow keys and rotate it with W/A/S/D. When it intersects one of the blue cubes, the word "Hit" will appear at the top of the screen once for every intersection as described above. The important part of the code is below.
for (var vertexIndex = 0; vertexIndex < Player.geometry.vertices.length; vertexIndex++)
{
var localVertex = Player.geometry.vertices[vertexIndex].clone();
var globalVertex = Player.matrix.multiplyVector3(localVertex);
var directionVector = globalVertex.subSelf( Player.position );
var ray = new THREE.Ray( Player.position, directionVector.clone().normalize() );
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )
{
// a collision occurred... do something...
}
}
There are two potential problems with this particular approach.
(1) When the origin of the ray is within a mesh M, no collision results between the ray and M will be returned.
(2) It is possible for an object that is small (in relation to the Player mesh) to "slip" between the various rays and thus no collision will be registered. Two possible approaches to reduce the chances of this problem are to write code so that the small objects create the rays and do the collision detection effort from their perspective, or include more vertices on the mesh (e.g. using CubeGeometry(100, 100, 100, 20, 20, 20) rather than CubeGeometry(100, 100, 100, 1, 1, 1).) The latter approach will probably cause a performance hit, so I recommend using it sparingly.
I hope that others will contribute to this question with their solutions to this question. I struggled with it for quite a while myself before developing the solution described here.
An updated version of Lee's answer that works with latest version of three.js
for (var vertexIndex = 0; vertexIndex < Player.geometry.attributes.position.array.length; vertexIndex++)
{
var localVertex = new THREE.Vector3().fromBufferAttribute(Player.geometry.attributes.position, vertexIndex).clone();
var globalVertex = localVertex.applyMatrix4(Player.matrix);
var directionVector = globalVertex.sub( Player.position );
var ray = new THREE.Raycaster( Player.position, directionVector.clone().normalize() );
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )
{
// a collision occurred... do something...
}
}
This really is far too broad of a topic to cover in a SO question, but for the sake of greasing the SEO of the site a bit, here's a couple of simple starting points:
If you want really simple collision detection and not a full-on physics engine then check out (link removed due to no more existing website)
If, on the other hand you DO want some collision response, not just "did A and B bump?", take a look at (link removed due to no more existing website), which is a super easy to use Ammo.js wrapper built around Three.js
only works on BoxGeometry and BoxBufferGeometry
create the following function:
function checkTouching(a, d) {
let b1 = a.position.y - a.geometry.parameters.height / 2;
let t1 = a.position.y + a.geometry.parameters.height / 2;
let r1 = a.position.x + a.geometry.parameters.width / 2;
let l1 = a.position.x - a.geometry.parameters.width / 2;
let f1 = a.position.z - a.geometry.parameters.depth / 2;
let B1 = a.position.z + a.geometry.parameters.depth / 2;
let b2 = d.position.y - d.geometry.parameters.height / 2;
let t2 = d.position.y + d.geometry.parameters.height / 2;
let r2 = d.position.x + d.geometry.parameters.width / 2;
let l2 = d.position.x - d.geometry.parameters.width / 2;
let f2 = d.position.z - d.geometry.parameters.depth / 2;
let B2 = d.position.z + d.geometry.parameters.depth / 2;
if (t1 < b2 || r1 < l2 || b1 > t2 || l1 > r2 || f1 > B2 || B1 < f2) {
return false;
}
return true;
}
use it in conditional statements like this:
if (checkTouching(cube1,cube2)) {
alert("collision!")
}
I have an example using this at https://3d-collion-test.glitch.me/
Note: if you rotate(or scale) one (or both) of the cubes/prisims, it will detect as though they haven't been turned(or scaled)
since my other answer is limited I made something else that is more accurate and only returns true when there is a collision and false when there isn't (but sometimes when There still is)
anyway, First make The Following Function:
function rt(a,b) {
let d = [b];
let e = a.position.clone();
let f = a.geometry.vertices.length;
let g = a.position;
let h = a.matrix;
let i = a.geometry.vertices;
for (var vertexIndex = f-1; vertexIndex >= 0; vertexIndex--) {
let localVertex = i[vertexIndex].clone();
let globalVertex = localVertex.applyMatrix4(h);
let directionVector = globalVertex.sub(g);
let ray = new THREE.Raycaster(e,directionVector.clone().normalize());
let collisionResults = ray.intersectObjects(d);
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ) {
return true;
}
}
return false;
}
that above Function is the same as an answer in this question by
Lee Stemkoski (who I am giving credit for by typing that) but I made changes so it runs faster and you don't need to create an array of meshes. Ok step 2: create this function:
function ft(a,b) {
return rt(a,b)||rt(b,a)||(a.position.z==b.position.z&&a.position.x==b.position.x&&a.position.y==b.position.y)
}
it returns true if the center of mesh A isn't in mesh B AND the center of mesh B isn't in A OR There positions are equal AND they are actually touching. This DOES still work if you scale one (or both) of the meshes.
I have an example at: https://3d-collsion-test-r.glitch.me/
It seems like this has already been solved but I have an easier solution if you are not to comfortable using ray casting and creating your own physics environment.
CANNON.js and AMMO.js are both physics libraries built on top of THREE.js. They create a secondary physics environment and you tie your object positions to that scene to emulate a physics environment. the documentation is simple enough to follow for CANNON and it is what I use but it hasnt been updated since it was released 4 years ago. The repo has since been forked and a community keeps it updated as cannon-es. I will leave a code snippet here so you can see how it works
/**
* Floor
*/
const floorShape = new CANNON.Plane()
const floorBody = new CANNON.Body()
floorBody.mass = 0
floorBody.addShape(floorShape)
floorBody.quaternion.setFromAxisAngle(
new CANNON.Vec3(-1,0,0),
Math.PI / 2
)
world.addBody(floorBody)
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10),
new THREE.MeshStandardMaterial({
color: '#777777',
metalness: 0.3,
roughness: 0.4,
envMap: environmentMapTexture
})
)
floor.receiveShadow = true
floor.rotation.x = - Math.PI * 0.5
scene.add(floor)
// THREE mesh
const mesh = new THREE.Mesh(
sphereGeometry,
sphereMaterial
)
mesh.scale.set(1,1,1)
mesh.castShadow = true
mesh.position.copy({x: 0, y: 3, z: 0})
scene.add(mesh)
// Cannon
const shape = new CANNON.Sphere(1)
const body = new CANNON.Body({
mass: 1,
shape,
material: concretePlasticMaterial
})
body.position.copy({x: 0, y: 3, z: 0})
world.addBody(body)
This makes a floor and a ball but also creates the same thing in the CANNON.js enironment.
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
const deltaTime = elapsedTime - oldElapsedTime
oldElapsedTime = elapsedTime
// Update Physics World
mesh.position.copy(body.position)
world.step(1/60,deltaTime,3)
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
After this you just update the position of your THREE.js scene in the animate function based on the position of your physics scene.
Please check out the documentation as it might seem more complicated than it really is. Using a physics library is going to be the easiest way to simulate collisions. Also check out Physi.js, I have never used it but it is supposed to be a more friendly library that doesn't require you to make a secondary environment
In my threejs version, I only have geometry.attributes.position.array and not geometry.vertices. To convert it to vertices, I use the following TS function:
export const getVerticesForObject = (obj: THREE.Mesh): THREE.Vector3[] => {
const bufferVertices = obj.geometry.attributes.position.array;
const vertices: THREE.Vector3[] = [];
for (let i = 0; i < bufferVertices.length; i += 3) {
vertices.push(
new THREE.Vector3(
bufferVertices[i] + obj.position.x,
bufferVertices[i + 1] + obj.position.y,
bufferVertices[i + 2] + obj.position.z
)
);
}
return vertices;
};
I pass in the object's position for each dimension because the bufferVertices by default are relative to the object's center, and for my purposes I wanted them to be global.
I also wrote up a little function to detect collisions based on vertices. It optionally samples vertices for very involved objects, or checks for proximity of all vertices to the vertices of the other object:
const COLLISION_DISTANCE = 0.025;
const SAMPLE_SIZE = 50;
export const detectCollision = ({
collider,
collidables,
method,
}: DetectCollisionParams): GameObject | undefined => {
const { geometry, position } = collider.obj;
if (!geometry.boundingSphere) return;
const colliderCenter = new THREE.Vector3(position.x, position.y, position.z);
const colliderSampleVertices =
method === "sample"
? _.sampleSize(getVerticesForObject(collider.obj), SAMPLE_SIZE)
: getVerticesForObject(collider.obj);
for (const collidable of collidables) {
// First, detect if it's within the bounding box
const { geometry: colGeometry, position: colPosition } = collidable.obj;
if (!colGeometry.boundingSphere) continue;
const colCenter = new THREE.Vector3(
colPosition.x,
colPosition.y,
colPosition.z
);
const bothRadiuses =
geometry.boundingSphere.radius + colGeometry.boundingSphere.radius;
const distance = colliderCenter.distanceTo(colCenter);
if (distance > bothRadiuses) continue;
// Then, detect if there are overlapping vectors
const colSampleVertices =
method === "sample"
? _.sampleSize(getVerticesForObject(collidable.obj), SAMPLE_SIZE)
: getVerticesForObject(collidable.obj);
for (const v1 of colliderSampleVertices) {
for (const v2 of colSampleVertices) {
if (v1.distanceTo(v2) < COLLISION_DISTANCE) {
return collidable;
}
}
}
}
};
You could try cannon.js.It makes it easy to do collision and its my favorite collision detection library. There is also ammo.js too.