/ / Processing2 2D Fizyka Kolizja w narożnikach 90 stopni - 2d, przetwarzanie, fizyka

Processing2 2D Kolizja Fizyki w narożnikach 90 stopni - 2d, przetwarzanie, fizyka

więc jest to mój pierwszy post w sprawie zadawania pytań dotyczących programowania, więc proszę o cierpliwość :)

Za mały projekt w szkole zrobiłem trochęklasa fizyki, kolizja manipulacyjna. Chociaż udało się to dobrze, wciąż mam błąd, którego nie mogłem wymyślić po kilku godzinach poszukiwań i nadal nie wiem, gdzie leży problem. Do implementacji użyto języka Processing języka Java, który jest używany do wprowadzenia do programowania i prototypowania.

Za pomocą lewego przycisku myszki mogę odrodzić kilka piłekktóre kolidują pikselowo z określonym kolorem na ekranie. Kolidując z kątem 90 stopni po prostu wpadają przez przeszkodę. Niestety nie mogę opublikować zrzutu ekranu z powodu mojego braku reputacji.

Więc moje pytanie dotyczy problemu. Ktoś, kogo zapytałem, powiedział, że może to być problem z produktem z kropką, którego używam do obliczania nowej prędkości lustrzanej, ale nie mogłem znaleźć niczego w tym kierunku. Podejrzewam, że błąd leży gdzieś w części, w której obliczana jest nowa prędkość, w metoda aktualizacji klasy PhysicsEntity.

Dziękuję wszystkim, którzy odpowiadają, jestem wdzięczny za każdą przydatną wskazówkę :)

Oto mój kod, składa się z trzech klas. Zamierzam opublikować wszystko, abyś mógł uruchomić kod samodzielnie. Jeśli nie masz przetwarzania, musisz je pobrać http://processing.org/ w celu uruchomienia poniższego przykładowego kodu.

Main.pde UWAGA: Ta część jest tylko przykładem użycia mojej klasy fizyki.

ArrayList<PhysicsEntity> entities = new ArrayList<PhysicsEntity>();

boolean mouseClicked = false;
boolean paused = false;

void setup()
{
size(800, 600);
background(0);
frameRate(60);
}

void draw()
{
if (!paused)
{
clear();
float gameTime = 1 / frameRate;

loadPixels();

for (int x = 0; x < width; ++x)
{
for (int y = height - 100; y < height; ++y)
{
pixels[x + y * width] = color(0, 200, 0, 128);
}
}
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < 20; ++y)
{
pixels[x + y * width] = color(0, 200, 0, 128);
}
}
for (int x = 0; x < 100; ++x)
{
for (int y = 0; y < height; ++y)
{
pixels[x + y * width] = color(0, 200, 0, 128);
}
}
for (int x = width - 100; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
pixels[x + y * width] = color(0, 200, 0, 128);
}
}

updatePixels();

if (mousePressed)
{
entities.add(new PhysicsEntity(new Vector2(width / 2, height / 2), new Vector2(random(-100, 100), random(-100, 100)), new Vector2(0.0f, 250.0f)));
}

for (int i = 0; i < entities.size(); ++i)
{
entities.get(i).update(gameTime);
entities.get(i).show();
}
}
}

Vector2.pde UWAGA: Ta klasa jest potrzebna tylko do kalibrowania rzeczy w klasie fizyki.

class Vector2
{
float a;
float b;

Vector2()
{
a = 0.0f;
b = 0.0f;
}

Vector2(float _a, float _b)
{
a = _a;
b = _b;
}

/* Return exact copy of the vector */
Vector2 Copy()
{
return new Vector2(a, b);
}

Vector2 Add(Vector2 vecB)
{
return new Vector2(a + vecB.a, b + vecB.b);
}

Vector2 Substract(Vector2 vecB)
{
return new Vector2(a - vecB.a, b - vecB.b);
}

/* Scale the vector by a scalar x */
Vector2 Scale(float x)
{
return new Vector2(a * x, b * x);
}

Vector2 Divide(float x)
{
return new Vector2(a / x, b / x);
}

float Dot(Vector2 vecB)
{
return (a * vecB.a + b * vecB.b);
}

float SqrLength()
{
return (pow(a, 2) + pow(b, 2));
}

float Length()
{
return sqrt(SqrLength());
}

boolean Equals(Vector2 vecB)
{
return (a != vecB.a || b != vecB.b) ? false : true;
}
}

Vector2 ZeroVector()
{
return new Vector2(0.0f, 0.0f);
}

PhysicsEntity.pde UWAGA: To jest klasa, w której faktycznie nie powiodło się.

class PhysicsEntity
{
Vector2 m_Pos;
Vector2 m_PrevPos;

Vector2 m_Vel;
Vector2 m_Acc;

/* bouncyness in case of collision; gets multiplied with the velocity */
float m_fBouncyness = 1.0f;

color collisionKey = color(0, 200, 0, 128);

public PhysicsEntity(Vector2 _pos, Vector2 _vel, Vector2 _acc)
{
if (_vel == null)
_vel = new Vector2(0.0f, 0.0f);

m_Pos = new Vector2(_pos.a, _pos.b);
m_PrevPos = m_Pos;

m_Vel = _vel;
m_Acc = _acc;
}

public void update(float dt)
{
/* Euler Integration more accurate Version */
/* x = x + vt + 0.5*at^2 */
m_Pos = m_Pos.Add(m_Vel.Scale(dt)).Add(m_Acc.Scale(pow(dt, 2)).Scale(0.5));
/* v = v + at */
m_Vel = m_Vel.Add(m_Acc.Scale(dt));

/* Collision based on color key */
if (isCollidable(m_Pos.a, m_Pos.b, collisionKey))
{
float speed = m_Vel.Length();
if (speed > 0.0f)
{
/* normalized vector of velocity */
Vector2 velNorm = m_Vel.Divide(speed);
/* getting the floor normal */
Vector2 floorNorm = interp(m_Pos, m_PrevPos);

if (!floorNorm.Equals(ZeroVector()))
{
/* mirror velocity on floor normal vector */
/* C = A - (2 * B * (A dot B)) where A is original vector, B the mirror, C result. */
Vector2 mirVel = velNorm.Substract(floorNorm.Scale(2.0f).Scale(velNorm.Dot(floorNorm)));

/* caculate new velocity */
m_Vel = mirVel.Scale(speed).Scale(m_fBouncyness);
/* add to position to move out of collision */
m_Pos = m_Pos.Add(m_Vel.Scale(dt));
}
}
}

m_PrevPos = m_Pos;
}

public void show()
{
ellipse(m_Pos.a, m_Pos.b, 10, 10);
}

public Vector2 interp(Vector2 pos, Vector2 PrevPos)
{
/* Vector from previous position to current position */
Vector2 line = pos.Substract(PrevPos);

float iLength = line.Length();
Vector2 lineFraction = ZeroVector();
/* checks if there the is vectorlength greater zero that connects the current and the previous position */
if (iLength > 0.0f)
lineFraction = line.Divide(iLength);

/* loop from through positions between previous position and current position */
for (int i = 0; i <= iLength; ++i)
{
Vector2 normVec = getNormal(PrevPos.Add(lineFraction.Scale(i)), collisionKey);

if (!normVec.Equals(ZeroVector()))
return normVec;
}

return ZeroVector();
}
}

/* returns normal vector of a 2d landscape in a certain area */
public Vector2 getNormal(Vector2 pos, color col)
{
int area = 10;

/* prevent coordinates from being out of the window */
if (pos.a <= area || pos.a >= width - area || pos.b <= area || pos.b >= height - area)
return ZeroVector();

Vector2 avg = new Vector2();
float loops = 0;

/* loop through an area of pixels */
for (int x = -area; x <= area; ++x)
{
for (int y = -area; y <= area; ++y)
{
if (x*x + y*y <= area*area)
{
float sumX = pos.a + float(x);
float sumY = pos.b + float(y);
/* count collidable pixels in area */
if (isCollidable(sumX, sumY, col))
{
/* add up positions of these pixels */
avg.a += sumX;
avg.b += sumY;
++loops;
}
}
}
}

if (loops == 0)
return ZeroVector();

/* calculate average position */
avg = avg.Divide(loops);
/* calculate length of the vector from initial position to average position */
float avgLength = dist(avg.a, avg.b, pos.a, pos.b);

/* check if avgLenth is zero or in other words: if avg is equals to pos */
if (avgLength == 0.0f)
return ZeroVector();

/* calculate vector(connection vector) from initial position to average position */
Vector2 conVec = pos.Substract(avg);
/* return normalized connection vector */
return conVec.Divide(avgLength);
}

/* method to check if pixel on a certain position is collidable */
public boolean isCollidable(float pixelX, float pixelY, color col)
{
if (pixelX >= width || pixelX < 0 || pixelY >= height || pixelY < 0)
return false;

return pixels[int(pixelX) + int(pixelY) * width] == col;
}

Edytuj1: Więc dzięki przyjaznej pierwszej powtórce rozebrałem mój kod kilkoma linijkami :) Jeśli nadal jest problem z moim postem, daj mi znać!

Odpowiedzi:

0 dla odpowiedzi № 1

Nie mogę przeanalizować poprawności wszystkich obliczeń fizycznych, ale moim zdaniem problem polega na obliczeniu nowej prędkości i:

/* caculate new velocity */
m_Vel = mirVel.Scale(speed).Scale(m_fBouncyness);
/* add to position to move out of collision */
m_Pos = m_Pos.Add(m_Vel.Scale(dt));

Ponieważ jeśli się zmienisz m_fBouncyness do rzeczywistej wartości symulującej pewne grawitacje (0.8f lub mniej) twój problem nigdy się nie wydarzy, ale jeśli zmienisz go na jakąś nierealną wartość, jak 2.0f stracisz wszystkie piłki po kilku odskokach.

To wskazuje problem w algorytmie. Twoje podejście polega (w prosty sposób) na następującej pętli:

  • zaktualizuj pozycję piłki
    • obliczyć nową pozycję
    • właściwa pozycja w zależności od odbicia
  • narysuj piłkę

Tutaj może być problem, ponieważ obliczasz nowypozycja piłki - ta pozycja jest poza czarną skrzynką, więc obliczyć średnią pozycję, a następnie nową prędkość i poprawić nową pozycję. Następnie wyciągnij piłkę i powtórz, ale co, jeśli ta nowa pozycja również zniknie z czarnej skrzynki? Ta piłka będzie odbijała się od granicy ... dzieje się to w rogu ze względu na obliczenie średniej pozycji (w rogu masz daleko od czarnego pudełka, a następnie na klasycznej granicy (gdy ustawisz m_fBouncyness do jakiejś większej wartości stanie się to nawet na normalnej granicy nie tylko w rogu!))

Mam nadzieję, że to pomoże ci znaleźć twój problem.


0 dla odpowiedzi nr 2

W końcu mam rozwiązanie.

Wygląda na to, że odpowiedź Majlika była bardzo pomocna. Zgodnie z jego odpowiedzią zrobiłem kilka zmian, które wyjaśnię teraz.

Przede wszystkim umieszczam instrukcję if jeśli (prędkość> 0.0f) wyżej, na całym kodzie ruchu, więc nic się już nie dzieje, jeśli prędkość jest zbyt niska. Oczywiście możesz zdefiniować pewien próg, który działa dla ciebie.

Oprócz tego wprowadziłem inny przypadek,dla instrukcji if (colliding), w której przetwarzany jest kod ruchu, więc jeśli kula jest obecnie kolizyjna, to nie porusza się wcale poza kodem obsługi kolizji.

W końcu pomyślałem o nowym sposobie poruszania piłkąz kolizji. Sugestia Maljika okazała się słuszna. Moja poprzednia metoda w ogóle nie usunęła piłki z kolizji. W tym celu wykonałem pętlę z pętlą tak długo, jak długo piłka nadal znajduje się w kolizji. W każdym ruchu kulka zostaje poruszona przez znormalizowany wektor z tym samym kierunkiem, co mój lustrzany wektor prędkości. Ze względów bezpieczeństwa wciąż mam inkrementację za każdym razem zwiększającą, więc nie kończy się to nieskończoną pętlą.

Po tym wszystkim rozwiązanie było bardzo oczywiste. Ale dzięki tym, którzy odpowiedzieli.

Poniżej nowego zmienionego kodu:

  public void update(float dt)
{
float speed = m_Vel.Length();
if (speed > 0.0f)
{
/* Collision based on color key */
if (isCollidable(m_Pos.a, m_Pos.b, collisionKey))
{
/* normalized vector of velocity */
Vector2 velNorm = m_Vel.Divide(speed);
/* getting the floor normal */
Vector2 floorNorm = interp(m_Pos, m_PrevPos);

if (!floorNorm.Equals(ZeroVector()))
{
/* mirror velocity on floor normal vector */
/* C = A - (2 * B * (A dot B)) where A is original vector, B the mirror, C result. */
Vector2 mirVel = velNorm.Substract(floorNorm.Scale(2.0f).Scale(velNorm.Dot(floorNorm)));

/* caculate new velocity */
m_Vel = mirVel.Scale(speed).Scale(m_fBouncyness);

int it = 0;
Vector2 normMirVel = mirVel.Divide(mirVel.Length());
while (isCollidable(m_Pos.a, m_Pos.b, collisionKey) && it < 100)
{
/* add to position to move out of collision */
m_Pos = m_Pos.Add(normMirVel);
++it;
}
}
}
else
{
/* Euler Integration more accurate Version */
/* x = x + vt + 0.5*at^2 */
m_Pos = m_Pos.Add(m_Vel.Scale(dt)).Add(m_Acc.Scale(pow(dt, 2)).Scale(0.5));
/* v = v + at */
m_Vel = m_Vel.Add(m_Acc.Scale(dt));
}
}

m_PrevPos = m_Pos;
}

Edytować: Być może nie jest to idealne rozwiązanie, ponieważ piłka porusza się dalej niż powinna w tej ramie. Może powinieneś tylko obliczyć niezbędną odległość, aby wyjść z kolizji i dodać rzeczywistą prędkość krok po kroku. Można również porównać aktualny kierunek prędkości z kierunkiem, w którym powinien się udać. Jeśli już porusza się we właściwym kierunku, nie ma potrzeby ingerencji.