Coliziune de bază pentru jocuri 2D (Dezvoltarea jocurilor, Java, Detectarea Coliziunii, Libgdx)

TheGag96 a intrebat.
a intrebat.

Am încercat să programez un joc în ultimul timp și nu reușesc să îmi dau seama de un singur lucru: detectarea coliziunii. Pot să fac să funcționeze sus+jos și stânga+dreapta, dar când le pun împreună, ele interferează între ele.

Serios, nu prea știu cum să o fac și nu vreau să fiu nevoit să folosesc Box2D sau orice alt sistem de detectare a coliziunilor premeditat. Tot ce am nevoie este ceva simplu și care să funcționeze în 4 direcții.

Iată codul pe care îl am acum din clasa Player:

for (int i = 0; i < blocks.size(); i++) {
            Block b = blocks.get(i);

            if (isCollidingDown(b)) {
                b.doTopCollision(this);
            }

            else if (isCollidingUp(b)) {
                b.doBottomCollision(this);
            }

            else if (isCollidingLeft(b)) {
                b.doRightCollision(this);
            }

            else if (isCollidingRight(b)) {
                b.doLeftCollision(this);
            }

        }

Aici este codul de detecție propriu-zis. Știu că este lung, dar pur și simplu, dați-vă cu el:

public boolean isCollidingDown(Block b) {
        if ((Float.compare(this.yPos,b.yPos+b.height) <= 0) && (Float.compare(this.yPos,b.yPos)>=0) && (Float.compare(this.yPos+this.height,b.yPos+b.height)>=0)) {

            if (((Float.compare(this.xPos, b.xPos)>=0) && (Float.compare(this.xPos, b.xPos+b.width)<= 0)) || 
                    ((Float.compare(this.xPos+this.width,b.xPos)>=0) && (Float.compare(this.xPos+this.width,b.xPos+b.width)<=0))) {


                    return true;

            }
        }
        return false;
    }

    public boolean isCollidingUp(Block b) {
        if ((Float.compare(this.yPos+this.height,b.yPos) >=0) && (Float.compare(this.yPos+this.height,b.yPos+b.height)<=0) && (Float.compare(this.yPos,b.yPos)<=0)) {

            if (((Float.compare(this.xPos, b.xPos)>=0) && (Float.compare(this.xPos, b.xPos+b.width)<= 0)) || 
                    ((Float.compare(this.xPos+this.width,b.xPos)>=0) && (Float.compare(this.xPos+this.width,b.xPos+b.width)<=0))) {

                return true;
            }
        }
        return false;
    }

    public boolean isCollidingLeft(Block b) {
        if ((Float.compare(this.xPos,b.xPos+b.width) <=0) && (Float.compare(this.xPos,b.xPos)>=0)) {

            if (((Float.compare(this.yPos, b.yPos)>=0) && (Float.compare(this.yPos, b.yPos+b.height)<=0)) || 
                    ((Float.compare(this.yPos+this.height,b.yPos)>=0) && (Float.compare(this.yPos+this.height,b.yPos+b.height)<=0))) {

                return true;
            }
        }
        return false;
    }

    public boolean isCollidingRight(Block b) {
        if ((Float.compare(this.xPos+this.width,b.xPos) >=0) && (Float.compare(this.xPos+this.width,b.xPos+b.width)<=0)) {

            if (((Float.compare(this.yPos, b.yPos)>=0) && (Float.compare(this.yPos, b.yPos+b.height)<= 0)) ||
                    ((Float.compare(this.yPos+this.height,b.yPos)>=0) && (Float.compare(this.yPos+this.height,b.yPos+b.height)<=0))) {

                return true;
            }
        }
        return false;
    }

Rețineți că a trebuit să fac acest lucru Float.compare() deoarece float-urile nu sunt precise atunci când se compară. De asemenea, nu folosesc Java2D, ci un framework numit libgdx. Totuși, asta nu ar trebui să conteze.

Ce pot să fac? Mulțumesc!

EDIT: Băieți, nu asta e ideea. Știu că codul meu este foarte neprofesionist, dar ideea este că coliziunile nu funcționează așa cum ar trebui. Am nevoie doar de un sistem de coliziuni cu 4 căi cu cutii. Asta e tot.

Comentarii

  • De ce folosești matematica cu virgulă mobilă? – MeBigFatGuy
  • Pentru a fi mai precis? De ce aș folosi ints?- TheGag96
  • Ar trebui să folosești double, care este și mai precisă și probabil la fel de rapidă ca float, cel puțin dacă ești pe un sistem pe 64 de biți…-  > Por Per Alexandersson.
  • @Paxinum Chiar nu este nevoie să folosiți double, float-urile sunt suficient de precise atâta timp cât numărul rămâne relativ mic. V-aș recomanda, de asemenea, să vă redenumiți funcțiile, deoarece nu au sens : „if (isCollidingDown(b)) b.doTopCollision(this);” este pur și simplu confuz.  > Por Jonathan Connell.
4 răspunsuri
SimonW

Nu îmi este foarte clar ce încerci să faci aici. Se pare că doriți ca doar una dintre funcțiile doXCollision să fie apelată, dar o mulțime de coliziuni vor intersecta două părți ale obiectului dvs. deodată.

Presupunând că clasa Player moștenește din clasa Block, aș adăuga patru funcții pentru claritate:

public float Left()   { return xPos;}
public float Right()  { return xPos + width;}
public float Bottom() { return yPos;}
public float Top()    { return yPos + height;}

apoi puteți rescrie funcția de coliziune:

for (int blockIndex = 0; blockIndex < blocks.size(); ++blockIndex) 
{
    Block b = blocks.get(blockIndex);

    if (b.Left() > this.Right()) break;
    if (b.Right() < this.Left()) break;
    if (b.Bottom() > this.Top()) break;
    if (b.Top() < this.Bottom()) break;

    //Collision

    if (b.Bottom() < this.Top() && b.Top() > this.Top()) isCollidingUp();
    if (b.Top() > this.Bottom() && b.Bottom() < this.Bottom()) isCollidingDown();
    if (b.Left() < this.Right() && b.Right() > this.Right()) isCollidingRight();
    if (b.Right() > this.Left() && b.Left() < this.Left()) isCollidingLeft();

    //One object is inside the other - add your solution here
}

Îmi cer scuze pentru orice probleme de sintaxă în Java, nu este un limbaj cu care sunt familiarizat. Cred că am ratat și cazul în care acest este în totalitate în interiorul box, dar ar trebui să puteți să vă descurcați.

Comentarii

  • De ce nu ați scăzut lățimea și înălțimea din stânga și din partea de jos? Și de ce ar trebui să verificați dacă un obiect este în interiorul altuia, pentru că acest lucru ar fi imposibil dacă verificați toate laturile?  > Por SD1990.
  • Din codul original, puteți vedea că pozițiile sunt pentru un colț al obiectului (am presupus că în stânga jos, dar nu prea contează). Dacă doriți să stocați poziția ca fiind centrul obiectului, ar trebui să folosiți jumătate din lățime și jumătate din înălțime pentru a vă ușura viața. Un obiect poate fi în interiorul altuia dacă se mișcă suficient de repede. Detectarea coliziunii se face o dată pe timestep, iar un obiect mic și rapid ar putea intra cu ușurință în interiorul unuia mai mare fără a fi într-o poziție în care să intersecteze marginea. De fapt, obiectele mici și rapide sunt o adevărată pacoste în acest tip de detectare a coliziunilor.  > Por SimonW.
Paul Reed

Ce fel de detectare a coliziunii urmărești? Doar o simplă detectare a coliziunii cutie contra cutie (AABB)? Dacă da, este vorba doar de o simplă verificare a faptului dacă extremitățile unei cutii se află în interiorul extremităților alteia și viceversa.

Este doar un caz de a vedea dacă valorile sunt mai mici sau mai mari decât?

În acest caz, se calculează distanța dintre centrele fiecăruia dintre cercuri, ceea ce reprezintă un test simplu (calcularea distanței dintre două puncte), iar dacă această distanță este mai mică decât ambele raze ale cercurilor însumate, înseamnă că acestea se află în coliziune.

O altă metodă bună de utilizat, care este puțin mai complexă, este de a efectua un test la stânga; Google ar trebui să aibă câteva sugestii bune în acest sens; acest test este bun deoarece poate funcționa cu forme complexe.

BlueMonkMN

O privire foarte rapidă asupra codului tău îmi sugerează o cauză foarte simplă a erorii tale. Aveți „else” între toate condițiile. Asta înseamnă că dacă vă ciocniți în sus sau în jos, condiția din stânga sau din dreapta nu va fi niciodată verificată. Cred că ar trebui să eliminați „else” înainte de testul isCollidingLeft.

SD1990

Cheia este să nu complici lucrurile și se pare că tu ai făcut-o.

 for (int i = 0; i< blocks.length; i++)
    {
        Block b = blocks.get(i);


            return false;

        //bottom y
        if((this.ypos + this.height/2) > (b.ypos - b.height/2))
            b.doBottomCollision(this);


        //top y
        if((this.ypos - this.height/2) < (b.ypos + b.height/2))
            b.doTopCollision(this);


        //right x
        if((this.xpos + this.width/2) > (b.xpos - b.width/2))
            b.doRightCollision(this);

        //left x
        if((this.xpos - this.width/2) < (b.xpos + b.width/2))
            b.doLeftCollision(this);

    }

Mă bazez pe sistemul normal de coordonate. (stânga sus 0,0)

O altă observație ar fi să încercați să folosiți pointeri.

Un mod mai bun sau initailizarea blocului ar fi ceva de genul:

În afara buclei.

Block *block = new Block();

Block**blocks = block*[];

blocks[0] = new Block();
blocks[1] = new Block();
etc...

puteți trece prin buclă dacă doriți.

apoi, în bucla ta, poți face …

blocks[i];

în loc de :

blocks.get(i);

Deși nu sunt sigur că puteți face acest lucru în Java… În încheiere, cu codul de mai sus, nu ar trebui să fie nevoie să scrieți un cod pentru „a fi în interiorul unui alt obiect”, având în vedere că este imposibil dacă sunt pătrate, deși, din nou, nu ați specificat ce ați lovit de fapt, sau poate ați făcut-o, dar eu nu mă ocup de Java pentru jocuri.

Edit conform stackoverflow „.length” este mai bun decât „.size()”, în special în array-uri