Related
I'm making a game with libGDX in Java. I'm trying to make a collision detection. As you can see in the image, I have a line which is a wall and a player with specified radius. The desired position is the next location which the player is trying to be in. But because there is a wall, he's placed in the Actual Position which is on the Velocity vector, but more closer to the prev location. I'm trying to figure out how can I detect that closer position?
My attempt:
private void move(float deltaTime) {
float step;
beginMovementAltitude();
if (playerComponent.isWalking())
step = handleAcceleration(playerComponent.getSpeed() + playerComponent.getAcceleration());
else step = handleDeacceleration(playerComponent.getSpeed(), playerComponent.getAcceleration());
playerComponent.setSpeed(step);
if (step == 0) return;
takeStep(deltaTime, step, 0);
}
private void takeStep(float deltaTime, float step, int rotate) {
Vector3 position = playerComponent.getCamera().position;
float x = position.x;
float y = position.y;
int radius = playerComponent.getRadius();
auxEnvelope.init(x, x + radius, y, y + radius);
List<Line> nearbyLines = lines.query(auxEnvelope);
float theta;
int numberOfIntersections = 0;
float angleToMove = 0;
Gdx.app.log(step + "", "");
for (Line line : nearbyLines) {
VertexElement src = line.getSrc();
VertexElement dst = line.getDst();
auxVector3.set(playerComponent.getCamera().direction);
auxVector3.rotate(Vector3.Z, rotate);
float nextX = x + (step * deltaTime) * (auxVector3.x);
float nextY = y + (step * deltaTime) * playerComponent.getCamera().direction.y;
float dis = Intersector.distanceLinePoint(src.getX(), src.getY(), dst.getX(), dst.getY(), nextX, nextY);
boolean bodyIntersection = dis <= 0.5f;
auxVector21.set(src.getX(), src.getY());
auxVector22.set(dst.getX(), dst.getY());
auxVector23.set(nextX, nextY);
if (bodyIntersection) {
numberOfIntersections++;
if (numberOfIntersections > 1) {
return;
}
theta = auxVector22.sub(auxVector21).nor().angle();
float angle = (float) (180.0 / MathUtils.PI * MathUtils.atan2(auxVector23.y - position.y, auxVector23.x - position.x));
if (angle < 0) angle += 360;
float diff = (theta > angle) ? theta - angle : angle - theta;
if (step < 0) step *=-1;
angleToMove = (diff > 90) ? theta + 180 : theta;
}
}
if (numberOfIntersections == 0) {
moveCameraByWalking(deltaTime, step, rotate);
} else {
moveCameraInDirection(deltaTime, step, angleToMove);
}
}
The idea is to find intersection of path of object center and the line moved by radius of the circle, see that picture.
At first, you need to find a normal to the line. How to do it, depends on how the line is defined, if it's defined by two points, the formula is
nx = ay - by
ny = bx - ax
If the line is defined by canonical equation, then coefficients at x and y define normal, if I remembered correctly.
When normal is found, we need to normalize it - set length to 1 by dividing coordinates by vector length. Let it be n.
Then, we will project starting point, desired point and randomly chosen point on line to n, treating them as radius vectors.
Projection of vector a to vector b is
project (a, b) = scalar_product (a, b) / length (b)**2 * b
but since b is n which length equals 1, we will not apply division, and also we want to only find length of the result, we do not multiply by b. So we only compute scalar product with n for each of three aforementioned points, getting three numbers, let s be the result for starting point, d for desired point, l for chosen point on the line.
Then we should modify l by radius of the circle:
if (s < d) l -= r;
else if (s > d) l += r;
If s = d, your object moves in parallel along the line, so line can't obstruct its movement. It's highly improbable case but should be dealt with.
Also, that's important, if l was initially between s and d but after modifying is no longer between then, it's a special case you may want to handle (restrict object movement for example)
Ather that, you should compute (d - s) / (l - s).
If the result is greater or equals 1, the object will not reach the line.
If the result is between 0 and 1, the line obstructs movement and the result indicates part of the path the object will complete. 0.5 means that object will stop halfway.
If the result is negative, it means the line is behind the object and will not obstruct movement.
Note that when using floating point numbers the result will not be perfectly precise, that's why we handle that special case. If you want to prevent this from happening at all, organize loop and try approximations until needed precision is reached.
I am very new to coding and my aim is to make and object (a lorry) change destination when it reaches the first one (site).
As for now, the lorry leaves its original place (concrete plant) and moves to the first site created. The user can add sites with mousePress (I used an array of sites). But then my lorry gets stuck on the first site and doesn't go to the next one. I also want the lorry to go to 2 sites and then come back to the concrete plant to then again leave for 2 sites etc...
Can somebody help me, I am desperate and my deadline is next Monday.
This is my code:
Lorry lorry;
int xCoord;
int yCoord;
ArrayList<Site> sites;
int siteSize = 30;
void setup() // What is called once at the beginning
{
size (500, 500);
xCoord = int(width/2);
yCoord = int(height/2);
//Creating empty Array List where store sites objects
sites = new ArrayList<Site>();
//Adding first site
sites.add(new Site(random(width), random(height), siteSize));
//storing lorries
lorry = new Lorry(xCoord, yCoord);
}
void draw() // Draw the background and concrete plant
{
background (235, 247, 255);
ellipse(xCoord, yCoord, 60, 60);
//Calling the sites
for (int i = sites.size () - 1; i>=0; i--) {
Site site = sites.get(i);
site.displaySites();
}
//calling the lorry functions
lorry.updateLorry();
}
void mousePressed() {
sites.add(new Site(mouseX, mouseY, siteSize));
}
class Site
{
float x,y;
float size;
Site (float xin, float yin, float sin)
{
x = xin;
y = yin;
size = sin;
}
void displaySites()
{
rectangle(x, y, 60, 60);
}
}
class Lorry
{
PVector location;
PVector concretePlant;
PVector velocity;
boolean changeDirection;
int siteNumber = 0;
Site destination;
Lorry(float xCoord, float yCoord)
{
concretePlant = new PVector(xCoord, yCoord); //Initial start point
location = new PVector(xCoord, yCoord); //Initial start point
velocity = new PVector(2, 2);
destination = sites.get(siteNumber);
changeDirection = false;
}
void displayLorry()
{
rectangle(location.x, location.y, 30, 30);
}
void Move()
{
float xdir = destination.x - location.x;
float ydir = destination.y - location.y;
PVector dir = new PVector (xdir, ydir);
dir.normalize();
location.add(dir);
}
void reachDestination()
{
if ((destination.x == location.x) && (destination.y == location.y)) {
siteNumber++; // siteNumber = siteNumber + 1;
destination = sites.get(siteNumber);
changeDirection = true;
}
}
void updateLorry()
{
displayLorry();
Move();
reachDestination();
}
}
You're super close Lily, literally.
If you print the values of destination and location you'll notice they're getting super close, however, due to the increments they never "meet". The values never match (aren't equal).
You could easily swap your equals conditions for a more practical threshold distance condition (e.g. if the distance between the destination and location are smaller than 1px):
void reachDestination()
{
println(destination,location);
//if ((destination.x == location.x) && (destination.y == location.y)) {
if(dist(destination.x,destination.y,location.x,location.y) < 1){
if(siteNumber < sites.size() -1){
siteNumber++; // siteNumber = siteNumber + 1;
destination = sites.get(siteNumber);
changeDirection = true;
}else{
println("reached final site");
}
println("reachDestination");
}
}
Bare in mind that dist() uses the square root which could become a slow calculation for a large number of sites (due to sqrt), however you could use the squared distance instead.
Additionally, PVector has a lerp() function that returns the interpolated position between two given positions (e.g. destination and site) and an interpolation amount (a value between 0.0 (start position) and 1.0 (end position)).
Here's a proof of concept sketch:
PVector[] destinations = {
new PVector(10,10),
new PVector(90,10),
new PVector(90,90),
new PVector(10,90)
};
float traversal = 0.0;
void setup(){
}
void draw(){
background(255);
//draw destinations
for(PVector d : destinations){
ellipse(d.x,d.y,9,9);
}
//calculate traversal
PVector traversed = traversePoints(destinations,0.01);
//draw traversal
ellipse(traversed.x,traversed.y,3,3);
}
PVector traversePoints(PVector[] destinations,float speed){
if(speed < 0){
speed = 0.05;
}
//increment full path traversal (the higher the increment, the faster the move)
traversal += speed;
//loop back to 0 when fully traversed
if(traversal > destinations.length) traversal = 0.0;
//compute the current point index
int pointIndex = (int)traversal;
//compute the local traversal (0.0 -> 1.0 = 0% to 100% in between two points: current and next)
float pointT = traversal - pointIndex;
//compute the next current point index
int pointIndexNext = (pointIndex + 1) % destinations.length;
//interpolate between current and next points using above local traversal, offsetting by the last mainHingeition)
return PVector.lerp(destinations[pointIndex],
destinations[pointIndexNext],
pointT);
}
You can actually run this as demo bellow:
var destinations;
var traversal = 0.0;
function setup(){
createCanvas(100,100);
destinations = [
createVector(10,10),
createVector(90,10),
createVector(90,90),
createVector(10,90)
];
}
function draw(){
background(255);
//draw destinations
for(var i = 0 ; i < destinations.length; i++){
var d = destinations[i];
ellipse(d.x,d.y,9,9);
}
//calculate traversal
var traversed = traversePoints(destinations,0.01);
//draw traversal
ellipse(traversed.x,traversed.y,3,3);
}
function traversePoints(destinations,speed){
if(speed < 0){
speed = 0.05;
}
//increment full path traversal (the higher the increment, the faster the move)
traversal += speed;
//loop back to 0 when fully traversed
if(traversal > destinations.length) traversal = 0.0;
//compute the current point index
var pointIndex = Math.floor(traversal);
//compute the local traversal (0.0 -> 1.0 = 0% to 100% in between two points: current and next)
var pointT = traversal - pointIndex;
//compute the next current point index
var pointIndexNext = (pointIndex + 1) % destinations.length;
//interpolate between current and next points using above local traversal, offsetting by the last mainHingeition)
return p5.Vector.lerp(destinations[pointIndex],
destinations[pointIndexNext],
pointT);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js"></script>
I am making a game which needs an "arrow" to be shot from a stationary location (a set coordinate). The arrow's trajectory is based on the location that the user Clicks in the GUI. This is essentially an Aiming feature. I cant get the arrow to follow a working path, any equations ie used have led to weird, glitchy, and buggy results.
public class ReShoot implements ActionListener
{
public void actionPerformed(ActionEvent e){
ArrowShoot shoot = new ArrowShoot();
shoot.ReShoot();
}
}
public class ArrowShoot implements ActionListener
{
public Timer T = new Timer(5, this);
Arrow A = new Arrow();
public void ReShoot(){
T.start();
arrow_x=0;
arrow_y=200;
A.setBounds(0,200,10,10);
}
// MAIN: y=-16t^2 + Vy * t + h
//Vy = v * sin(a)
//Vx = v * cos(a)
//a = arctan( (200-mouse_y)/v
//v = Mouse_x - Arrow_x
//t = x / Vx
public void actionPerformed(ActionEvent e)
{//arrow_y = 0.0025 * Math.pow((mouse_x-arrow_x), 2)+ mouse_y;
Container container_arrow = getContentPane();
container_arrow.setLayout(null);
container_arrow.add(A);
A.setBounds(0,200,10,10);
arrow_x++;
double v = mouse_x/2; //height change
double a = 50* Math.atan((200-mouse_y) / (v));
double Vy = v * Math.sin(a);
double Vx = v * Math.cos(a);
double t = arrow_x/Vx;
double h = 200;
arrow_y = (16) * Math.pow(t, 2) + (Vy * t) + h;
int x = (int)Math.round(arrow_x);
int y = (int)Math.round(arrow_y);
A.setBounds(x, y,10,10);
if (arrow_y>=500)
T.stop();
}
I am pretty sure im doing this all wrong, and there has to be a more effective method to accomplish this task.
It doesn't look like you are calculating the trajectory path correctly. In actionPerformed, you are incrementing the x coordinate of the arrow, and then calculating the corresponding y. This will not work at all, since even though you can calculate y as a function of x, x is itself a function of t (time). Hence you have to calculate x at time t instead of assuming that x will always increase by 1 at the next invocation.
Given that you can calculate the angle, you can calculate the position of x and y as a function of time and the angle using the following formulas:
So your algorithm will essentially be:
time++; //time variable that you maintain
arrow_x = ... //calculate using (1)
arrow_y = ... //calculate using (2)
So, I'm trying to make a game in LWJGL and it seems to work fine for me. Although, I ran into some issues in moving my entities around on the screen. I want to make it go from one point to another, at the same speed. Also, I'm animating my sprite according to the direction the entity is moving.
But! I have some issues:
1# It flickers because the movement is defined a modifier: delta (to make smooth movement defined by the FPS). Actually, it never really reaches it's point (because it recalculates and never hits the position). How can I solve this?
2# When 2 players join the same server, my character on the fastest computer runs faster. I think it's because of the FPS, how can that be solved?
private String name;
private float positionx,positiony; // Current
private int targetx,targety; // Target
private int dx, dy; // Direction
private int pointx, pointy; // Direction
private float speed;
private Sprite sprite;
public Entity(String name, int positionx, int positiony, Sprite sprite){
this.name = name;
this.speed = 0.1f;
this.positionx = 720;
this.positiony = 450;
this.targetx = 1000; // fix this
this.targety = 10; // this for testing.
this.sprite = sprite;
this.dx = 0;
this.dy = 0;
}
//double distance = Math.sqrt((vx * vx) + (vy * vy));
public void move(long delta){
if(positionx < targetx){
dx = 1;
pointx = 1;
}else if(positionx > targetx){
dx = -1;
pointx = -1;
}else{
dx = 0;
}
if(positiony < targety){
dy = 1;
pointy = 1;
}else if(positiony > targety){
dy = -1;
pointy = -1;
}else{
dy = 0;
}
//Set animations:
if(positionx==targetx && positiony==targety){
if(pointx<0){
sprite.setAnimation(5, 2, 100); // Standing left
}else if(pointx>0){
sprite.setAnimation(6, 2, 100); // Standing right
}else if(pointy<0){
sprite.setAnimation(7, 2, 100); // Standing up
}else if(pointy>0){
sprite.setAnimation(4, 2, 100); // Standing down
}
}else{
if(pointx<0){
sprite.setAnimation(1, 2, 100); // Walking left
}else if(pointx>0){
sprite.setAnimation(2, 2, 100); // Walking right
}else if(pointy<0){
sprite.setAnimation(3, 2, 100); // Walking up
}else if(pointy>0){
sprite.setAnimation(0, 2, 100); // Walking down
}
}
//movement here.
positionx += dx*delta*speed;
positiony += dy*delta*speed;
System.out.println(dx*delta*speed);
sprite.setPosition((int)positionx, (int)positiony);
}
1# It flickers because the movement is defined a modifier: delta (to make smooth movement defined by the FPS). Actually, it never really reaches it's point (because it recalculates and never hits the position). How can I solve this?
If you store point A and point B between which it moves, you can set a time interval. Each time interval a set distance will be travelled and if at one iteration the object goes too far you can set its coordinates for point B. This can be easily done with a Timer. That way, after a certain amount of time, it will be on your specified position.
2# When 2 players join the same server, my character on the fastest computer runs faster. I think it's because of the FPS, how can that be solved?
Same answer as question #1, if you use a Timer. Each player will move at the same speed (because the elapsed time is the same for each gamer).
Bottom line:
fps is variable, while elapsed time is the same for everyone.
I'm having issues detecting the closest element in an array of blocks to the player (using circles).
What I have so far is:
public static int closestBarrier(GameObject object, GameObject[] barriers) {
int closest = -1;
float minDistSq = Float.MAX_VALUE;// ridiculously large value to start
for (int i = 0; i < barriers.length; i++) {
float barrierRadius = barriers[i].getWidth() / 2;
float objectRadius = object.getWidth() / 2;
GameObject curr = barriers[i];// current
float dx = (object.getX()) - ((curr.getX()));
float dy = (object.getY()) - ((curr.getY()));
float distSq = (((dx * dx + dy * dy) - objectRadius) - barrierRadius) ;// use the squared distance
if (distSq < minDistSq) {// find the smallest and remember the id
minDistSq = distSq;
closest = i;
}
}
return closest;
}
It passes most of the tests but on the final one the returned index of the closest one is 2 instead of the expected 3. Here are the tests (it is failing 'closest to fourth' :
public final void testClosestBarrier() {
// Closest to first
GameObject player = new GameObject(0,1);
player.setWidth(2);
GameObject[] barriers = new GameObject[4];
barriers[0] = new GameObject(8,9);
barriers[0].setWidth(3);
barriers[1] = new GameObject(10,15);
barriers[1].setWidth(2);
barriers[2] = new GameObject(15,20);
barriers[2].setWidth(5);
barriers[3] = new GameObject(100,210);
barriers[3].setWidth(10);
assertEquals("Player closest to first barrier",0,
Submission.closestBarrier(player,barriers));
// Closest to second
player.setX(12);
player.setY(12);
assertEquals("Player closest to second barrier",1,
Submission.closestBarrier(player,barriers));
// Closest to third
player.setX(12);
player.setY(20);
assertEquals("Player closest to third barrier",2,
Submission.closestBarrier(player,barriers));
// Closest to fourth
player.setX(90);
player.setY(100);
assertEquals("Player closest to fourth barrier",3,
Submission.closestBarrier(player,barriers));
}
Your code is correct and the test you have written is wrong - barrier 2 is closer to 90,100 than barrier 3.
barrier 2:
(90-15)^2 + (100-20)^2
12025
barrier 3:
(100-90)^2 + (210-100)^2
12200
12025 < 12200 -> barrier 2 is closer
EDIT: In your edited answer, you forgot to square objectRadius and barrierRadius. e.g.
float distSq = (((dx * dx + dy * dy) - objectRadius) - barrierRadius)
In this line, you have dx^2, dy^2 but only non-squared objectRadius and barrierRadius.