Check if two objects are facing eachother - javascript

I've searched all over and couldn't find an answer to this seemingly common question, surprisingly. The problem I'm currently facing is checking if the player is facing an enemy, then if so within what range of the players' view (adjustable) and if it's within that range then move away in the nearest safe direction.
Here's a picture :D
So, how would I accomplish this? I have the x, y, and direction, of every ship object. This is my last failed attempt, attempting to consider that the player's direction will be exactly 180 degrees away from the enemy's direction relative to the player.
var direction=Math.direction(this.x,this.y,player.x,player.y,1),
playerview=Math.abs(direction)-Math.abs(player.direction-180)
if(Math.abs(playerview)<10) {
console.log('in view')
this.xVelocity+=AI.speed*Math.sin(playerview*Math.PI/180)
this.xVelocity+=AI.speed*Math.cos(playerview*Math.PI/180)
}
In this example, 10 would be the range. Of course, I've only managed to make ships rotate to the right, so aside from the detection only working on half a circle I can't make the enemy go to the right side either. Any ideas?

In Your code, You are modifying this.xVelocity twice instead of modifying this.yVelocity.
I guess, Math.direction is not in the JavaScript/ECMA standard. Are You sure, You are using it correctly? Consider Math.atan2.
Moreover, You should provide a definition of "facing each other".
If it's "seeing each other", then Your comment "in view" is misleading.
But the main issue is:
Math.abs(angleInDegrees) modifies the angle!
The correct way to build an absolute value of an angle is:
while (angleInDegrees < 0)
{
angleInDegrees += 360;
}
// If necessary, add this too:
while (angleInDegrees >= 360)
{
angleInDegrees -= 360;
}
-10 and 350 are identical, -10 and 10 are 20 degrees apart.
For further explanation, let's call the code above normalizeAngle
(Note: For huge values, the loop may run very long or even forever. If such values may occur, this should be checked.)
The following should do the trick:
playerview = normalizeAngle(direction - player.direction - 180)
if (playerview < range || playerview > 360-range) {
By the way: "playerview" should be the mininum from the player's field of view and the enemy's field of view.

Related

struggling to create a smooth-moving snake in p5 js - tail catching up to head

I'm putting together a p5 sketch with little wiggling snakes that move randomly across the screen.
Unfortunately, the tail keeps catching up to the head every time it does a sharpish turn.
Here is the function I'm using to calculate the move, I've tried with a few different ways of calculating the speed, fixed numbers, relative to the snake's length.
It's supposed to work by moving the snakes head (points[3]) in a semi-random direction and then having each body point move towards the one before it by the same amount. This isn't working, and I feel there's something wrong with my algorithm itself. I'm not familiar with these kinds of intermediate random-walks, so I've just been going by guesswork for the most part.
this["moveCurve"] = function() {
let newDir = this["oldDir"] + (random() - 1/2)*PI/6;
let velocity = createVector(1,0);
velocity.setMag(5);
velocity.setHeading(newDir);
this["points"][3].add(velocity);
for (let i = 2; i >= 0; i--) {
this["points"][i].add(p5.Vector.sub(this["points"][i + 1],this["points"][i]).setMag(5));
}
this["oldDir"] = newDir;
}
If you have any idea what I could do to make this work properly, I'd love to hear your advice. Thanks!
This does look like an algorithmic issue / not a bug with how you implemented it.
Here's my go at explaining why the gap between two points must decrease in this algorithm:
Let's consider just a two point snake, with two points Hi (head) and Ti (tail) at an initial locations Hi: (20, 0), and Ti: (0, 0). So, the heading here is 0 radians.
What happens when moveCurve is called? A new heading is chosen (let's use PI/2, a right angle to make it easy to imagine) and using a fixed velocity of 5 we calculate a new position for the head of (20, 5), let's call it Hf. T also moves, but it also moves toward Hf at the same 5 unit velocity, ending up at about (4.85, 1.21). The distance between these two final positions is now 15.62657, which is smaller than the initial distance.
To visualize this, think of the triangle formed between Ti, Hi, and Hf. Ti, and Hi, form the base of this triangle. Ti will move along the hypotenuse to get to Tf, while Hi will move along the other side. The directions they are moving in form an angle which is smaller than PI radians and both points are moving at the same speed so intuitively the points must be getting closer together.
So how to solve this? Well if we consider our tiny snake's movement, the tail moved in a decent direction but too far. One solution might be to scale the velocity vector in order to maintain a fixed distance between points instead of using a fixed velocity. For example instead of stepping 5 units along the hypotenuse from Ti toward Hf in the example, you could step 20 units along the hypotenuse from Hf toward Ti. I'm not sure how this would work out for your snake, just an idea!
Keep slithering!
Fortunately, it turns out p5's documentation itself had the answer for me. By adapting the code from here to use p5 Vectors, I was able to get it all working.
The segLengths property is defined when the object is made, just takes the distances between all the points.
this["moveCurve"] = function() {
let newDir = this["oldDir"] + (random() - 1/2)*PI/6;
let velocity = p5.Vector.fromAngle(newDir).setMag(5);
this["points"][3].add(velocity);
for (let i = 2; i >= 0; i--) {
this["points"][i].set(p5.Vector.sub(this["points"][i+1], p5.Vector.fromAngle(p5.Vector.sub(this["points"][i+1],this["points"][i]).heading()).setMag(this["segLengths"][i])));
}
this["oldDir"] = newDir;
}
I might spend a little time trying to clean up the code a bit, it's a jot messy for my tastes at the moment. But it works.

How to check if an object is hitting another object during animation.

Hello and thanks for your time and help. I am building a mini golf game using HTML 5 Canvas. I have a golf ball that is able to be hit around with a specific power and direction and stoping and slowing down accordingly.
However, i am stuck on finding a optimal way of detecting wether the ball is hitting or over an obstacle on the course. I have each of the obstacle objects stored in an array. So it is easy to access the x,y,height, and width of the object. I was thinking it would be best to have a for loop that goes through each of the objects checking if the ball is hitting any of them during animation. Also the x and y of the golf ball is easily accessible, its values are stored in a dictionary.
Any suggestions of testing to see if any of the obstacles are being hit?
Here is the code on how i am testing for the boundaries and having the ball bounce back correctly
function animateGolfBall(timestamp){
if(powerBar.widthChange != 0){
context.clearRect(0, 0, canvasTag.width, canvasTag.height);
if (golfBall.x > canvasTag.width - 5 || golfBall.x < 5 ) {
golfBall.angle = 180 - golfBall.angle;
updateGolfBall();
drawEverything();
}
else if (golfBall.y > canvasTag.height - 5 || golfBall.y < 5) {
golfBall.angle = 360 - golfBall.angle;
updateGolfBall();
drawEverything();
}
else{
updateGolfBall();
drawEverything();
}
window.requestAnimationFrame(animateGolfBall);
powerBar.widthChange -= 1;
}
else{
golfBall.isMoving = false;
drawEverything();
}
}
Here is the code where the obstacles are being redrawn, and where i believe the check should be placed to see if the golf ball is hitting them
function drawObstacle(){
for(var i = 0; i < obsticles.length; i++){
obsticles[i].createSquare(obsticles[i].squareX/canvasTag.width,
obsticles[i].squareY/canvasTag.height,
obsticles[i].squareWidth/canvasTag.width,
0,
obsticles[i].squareHeight/canvasTag.height,
0,"pink",3,"yellow");
// if { need help with logic here
//And what to put here, and how ever many logical statements will be needed
// }
}
Any help or tips would be much appreciated. If you need more code or i wasn't clear on something please let me know and ill update my question.
This is called collision detection. There are many ways to deal with collisions. Depending on the shape of your objects (are they circles or squares or car-shaped) and what you want to happen when a collision occurs. Does the ball bounce back? Does it stop? Is it of the most importance that the collision is detected at the edge of the object?
You can read about simple collision detection working with either square or circle shaped objects here.

Draggable.create - Want show/hide toggle on certain array/endvalues

I've hit a snag during a project I've been working on for quite some time. The client wants a spinnable dial on his frontpage, like a safe combination dial or a volume knob.
The knob has 4 endvalues, to which the object snaps (to the nearest value). The problem I've hit is that I have no idea how to make JS toggle a hide/show on a certain element once its corresponding value has been reached.
For instance, at 90 degrees rotation, I want element X to show. At 180 degrees rotation, I want element X to hide and Y to take its place/to show. Repeat in 90 degree increments for Z and A.
My current code attempt (amongst many) looks like this
var dialarray = [0,90,180,270];
var rotationSnap = 90;
Draggable.create("#Stage_Group", {
type:"rotation", //instead of "x,y" or "top,left", we can simply do "rotation" to make the object spinnable!
throwProps:true, //enables kinetic-based flicking (continuation of movement, decelerating after releasing the mouse/finger)
snap:function(endValue) {
//this function gets called when the mouse/finger is released and it plots where rotation should normally end and we can alter that value and return a new one instead. This gives us an easy way to apply custom snapping behavior with any logic we want. In this case, just make sure the end value snaps to 90-degree increments but only when the "snap" checkbox is selected.
return Math.round(endValue / rotationSnap) * rotationSnap;
if (rotation("#Stage_Group") = 90) {
sym.$("brand_awareness").show();
} else {
sym.$("brand_awareness").hide();
}
}
})
I'm using Greensocks, where support has been lacking to get me the right answer, so with some help from a more code-savvy friend (who didn't figure it out either) I turn to StackOverflow.
Can somebody please guide me to the solution here? It's driving me bonkers, even if it means ditching the current code for a working code will do, maybe I overlooked another, more logical solution towards making a rotatable object with triggers at certain values?
Thanks in advance!
This would be the code inside your snap. Updated seeing your comment
var imgRotation = Math.round(endValue / rotationSnap) * rotationSnap;
if (imgRotation == 90) {
sym.$("id1").show();
} else if (imgRotation == 180) {
sym.$("id2").show();
} else if (imgRotation == 270 ) {
sym.$("id3").show();
}
else if ((imgRotation >= 360) || (imgRotation == 0)){
sym.$("id4").show();
}
return imgRotation;

multiple DIV collision detection in Javascript/JQuery

Working on a little "zombies" or "tag you're it" or "ew! you got cooties"-styled game where each AI object (a person, basically) runs around randomly. There is an initial object that is "it" or "infected" and as it moves about the screen and touches/overlaps/collides with another object it should change the touched object to the same color as the object that touched it. Newly infected objects can continue to infect other objects they randomly collide with, until - in principle - the whole population is the same color as the first infected object. (I'll worry about fancier AI where infected actively hunt nearby objects or healthy objects can avoid infected objects, later).
But after looking at various similar questions in StackOverflow that generally deal with 2 DIVs colliding, or use some sort of jQuery draggable detection trick, I'm still at a bit of a loss as to how to build upon those ideas to scale up a simple "if I am touching/overlapping/colliding with another object it should get infected too" that can be applied to a large number of elements on the page, say... less than 100 so as not to drag the browser down.
I basically get as far as determining position and widths/heights of the objects so that I know how much space they take, but then the brain goes 'bzzzzt' when trying to develop a function that checks over all the population for collisions.
Got the population moving around randomly without trouble - see JSFiddle https://jsfiddle.net/digitalmouse/5tvyjhjL/1/ for the related code. Affected function should be in the 'animateDiv()', seen below to make the stackoverflow question asking editor happy that I included some code in my question. :)
function animateDiv($target) {
var newq = makeNewPosition($target.parent());
var oldq = $target.offset();
var speed = calcSpeed([oldq.top, oldq.left], newq);
// I believe collision should be dealt with here,
// just before moving an object
$target.animate({
top: newq[0],
left: newq[1]
}, speed, function () {
animateDiv($target);
});
}
Any hints, tricks, adaptations, or code snippets that push me in the right direction are appreciated.
a quick, down and dirty solution (there are more complex algorithms) would be to use:
document.elementFromPoint(x, y);
It gets the element at the position specified. The full spec can be found here.
Assuming your 'zombies' are rectangular, you could call this for each corner, and if you get a hit, that isn't the background or the element you're checking, you've got a collision...
EDIT:
An alternate method, even 'downer and dirtier' than above, but stupidly quick, would be to get the centre points of the two objects to check, then find their absolute displacements in X and Y. If the differences are less than the sum of half their widths and heights then they are overlapping. It's by no means pix perfect, but it should be able to handle a large number objects really quickly.
EDIT 2:
First off, we need to get the centres of each object (to check)
// Values for main object
// pop these in vars as we'll need them again in a sec...
hw = object.style.width >> 1; // half width of object
hh = object.style.height >> 1; // (bit shift is faster than / 2)
cx = object.style.left + hw; // centre point in x
cy = object.style.top + hh; // and in y
// repeat for secondary object
If you don't know / store the width and height you can use:
object.getBoundingClientRect();
which returns a 'rect' object with the fields left, top, right and bottom.
Now we check proximity...
xDif = Math.abs(cx - cx1); // where cx1 is centre of object to check against
if(xDif > hw + hw1) return false; // there is no possibility of a collision!
// if we get here, there's a possible collision, so...
yDif = Math.abs(cy - cy1);
if(yDif > hh + hh1) return false; // no collision - bug out.
else {
// handle collision here...
}
Danny

Click detection in a 2D isometric grid?

I've been doing web development for years now and I'm slowly getting myself involved with game development and for my current project I've got this isometric map, where I need to use an algorithm to detect which field is being clicked on. This is all in the browser with Javascript by the way.
The map
It looks like this and I've added some numbers to show you the structure of the fields (tiles) and their IDs. All the fields have a center point (array of x,y) which the four corners are based on when drawn.
As you can see it's not a diamond shape, but a zig-zag map and there's no angle (top-down view) which is why I can't find an answer myself considering that all articles and calculations are usually based on a diamond shape with an angle.
The numbers
It's a dynamic map and all sizes and numbers can be changed to generate a new map.
I know it isn't a lot of data, but the map is generated based on the map and field sizes.
- Map Size: x:800 y:400
- Field Size: 80x80 (between corners)
- Center position of all the fields (x,y)
The goal
To come up with an algorithm which tells the client (game) which field the mouse is located in at any given event (click, movement etc).
Disclaimer
I do want to mention that I've already come up with a working solution myself, however I'm 100% certain it could be written in a better way (my solution involves a lot of nested if-statements and loops), and that's why I'm asking here.
Here's an example of my solution where I basically find a square with corners in the nearest 4 known positions and then I get my result based on the smallest square between the 2 nearest fields. Does that make any sense?
Ask if I missed something.
Here's what I came up with,
function posInGrid(x, y, length) {
xFromColCenter = x % length - length / 2;
yFromRowCenter = y % length - length / 2;
col = (x - xFromColCenter) / length;
row = (y - yFromRowCenter) / length;
if (yFromRowCenter < xFromColCenter) {
if (yFromRowCenter < (-xFromColCenter))--row;
else++col;
} else if (yFromRowCenter > xFromColCenter) {
if (yFromRowCenter < (-xFromColCenter))--col;
else++row;
}
return "Col:"+col+", Row:"+row+", xFC:"+xFromColCenter+", yFC:"+yFromRowCenter;
}
X and Y are the coords in the image, and length is the spacing of the grid.
Right now it returns a string, just for testing.. result should be row and col, and those are the coordinates I chose: your tile 1 has coords (1,0) tile 2 is(3,0), tile 10 is (0,1), tile 11 is (2,1). You could convert my coordinates to your numbered tiles in a line or two.
And a JSFiddle for testing http://jsfiddle.net/NHV3y/
Cheers.
EDIT: changed the return statement, had some variables I used for debugging left in.
A pixel perfect way of hit detection I've used in the past (in OpenGL, but the concept stands here too) is an off screen rendering of the scene where the different objects are identified with different colors.
This approach requires double the memory and double the rendering but the hit detection of arbitrarily complex scenes is done with a simple color lookup.
Since you want to detect a cell in a grid there are probably more efficient solutions but I wanted to mention this one for it's simplicity and flexibility.
This has been solved before, let me consult my notes...
Here's a couple of good resources:
From Laserbrain Studios, The basics of isometric programming
Useful article in the thread posted here, in Java
Let me know if this helps, and good luck with your game!
This code calculates the position in the grid given the uneven spacing. Should be pretty fast; almost all operations are done mathematically, using just one loop. I'll ponder the other part of the problem later.
def cspot(x,y,length):
l=length
lp=length+1
vlist = [ (l*(k%2))+(lp*((k+1)%2)) for k in range(1,y+1) ]
vlist.append(1)
return x + sum(vlist)

Categories

Resources