Jump to content
Unity Insider Forum
Singular

Bei Kollision, Krafteinwirkung ignorieren

Recommended Posts

Hallo zusammen,

ich melde mich zurück aus einer leider etwas unfreiwilligen mehrmonatigen Pause. Ich bin leider nicht dazu gekommen weiter zu lernen hab es aber gleich wieder mit neuer Motivation aufgenommen. Jetzt aber zu meinem Problem:

In meinem Spiel hat der Spieler ein Raumschiff und darf damit auf Asteroiden Ballern. Soweit so gut. Der Laser soll nun also, wenn er mit dem Asteroiden Kollidiert, Schaden zufügen und selber zerstört werden, was auch funktioniert. Jedoch wird der Asteroid bei der Kollision in die Richtung katapultiert in die der Laser fliegt, weil er schon etwas an Geschwindigkeit hat. 

Ich könnte zwar den Laser.Collider zum Trigger machen, habe dann aber das Problem, dass das Script für den Schaden bei Health Script des Asteroiden liegen muss, "OnTriggerEnter()". Das möchte ich eigentlich nicht. Denn dann brauchen die anderen vom Raumschiff abgefeuert Raketen und Minen ebenfalls diese Methode um vom Laser abgeschossen werden zu können.

Hier gibt es mit Sicherheit mehrere Lösungsmöglichkeiten. Die einfachste wäre jedoch bei der Kollision die Krafteinwirkungen zu ignorieren.

Achso, den Asteroiden auf Kinematisch zu stellen ist auch keine Option, weil Explosionen von Rakete oder zusammenstoß mit anderen Asteroiden soll natürlich ganz normal weiter funktionieren.

Share this post


Link to post
Share on other sites

Also... ein Laser ist ja ein Strahl. Ich schließe aber aus deinem Text, dass du von einem Projektil redest? Wenn das Projektil sehr schnell ist, dann würde ich dir (unabhängig von deinem Problem) empfehlen, das nicht mit einem Rigidbody zu bauen, sondern mit einem Raycast. Du ermittelst einmalig beim Schuss, wo das Projektil auftreffen wird, und dann animierst du ein Objekt ohne Collider oder Rigidbody entlang des imaginären Strahls.

Sollte dein Objekt nicht so schnell sein, dann würde ich trotzdem auf den (kinematischen) Rigidbody verzichten, wenn dein Objekt keine Kräfte kennen soll. Ein kinematischer Rigidbody ist halt dafür da, dass Kräfte auf ihn einwirken können und er selber Kräfte weitergeben kann. Wenn das nicht gewünscht ist, einfach weg mit dem Kram. Ohne Rigidbody kannst du immer noch hervorragend Kollisionen checken. Z.B. kannst du ein Script schreiben, das in FixedUpdate einen Kollisions-Check mit eine der vielen Funktionen der Physics-Klasse macht. Und wenn das Ding überlappt, löst du eben Schaden aus. Bei komplexen Formen wäre ein Rigidbody nett, aber so ein Projektil sollte mit einer OverlapSphere schon gut zurecht kommen.

Share this post


Link to post
Share on other sites

Das klingt nach einem Weg (oder eher 2 😀 )

Ja, du hast recht. Sogesehen habe ich den Laser als Projektiel erstellt. Wenn ich hier nun den RB entfernen würde müsste ich das Objekt aber über den Transform bewegen oder? Ist das den eine saubere Lösung?

In der Physikklasse und auch mit OverlapSphere hab ich schon gearbeitet. Das nutzen die Explosionen meiner Raketen. Das würde ich aber erst mal nicht in Erwägung ziehen. Mit Raycast kenne ich mich leider noch nicht aus. Aber dann schau ich mir die Raycasts mal an. Bei Problemen melde ich mich hier dann noch einmal.

Danke für die Schnelle Antwort.

Share this post


Link to post
Share on other sites
vor 1 Stunde schrieb Singular:

über den Transform bewegen oder? Ist das den eine saubere Lösung?

Warum denn nicht? Alles, was Rigidbodys machen, ist:

  1. In jedem FixedUpdate die aktuelle Geschwindigkeit auf die Transform-Position draufaddieren. Etwas ähnliches dann auch noch für die Rotation.
  2. Im Anschluss checken, ob einer der eigenen Collider in irgendeinem anderen Collider drinsitzt und wenn ja, eine Kollision auslösen. Heißt:
    • Rausbewegen,
    • eigene Geschwindigkeit abändern,
    • ggf. dem anderen Rigidbody einen Impuls aufdrücken,
    • OnCollisionEnter usw. auslösen.

Das war's auch schon. Dinge über direkt über die Tranform-Komponente zu bewegen heißt nichts weiter, als dass du selber die Verantwortung für die Objektposition übernimmst, also z.B. selber Kollisionsabfragen machst, falls das Objekt kollidieren können soll.

vor 2 Stunden schrieb Singular:

Das würde ich aber erst mal nicht in Erwägung ziehen.

Wie gesagt, für ein langsames Objekt, das keine Impulse kennen soll, eine hervorragende Lösung.

vor 2 Stunden schrieb Singular:

Mit Raycast kenne ich mich leider noch nicht aus. Aber dann schau ich mir die Raycasts mal an. Bei Problemen melde ich mich hier dann noch einmal.

Das ist so oder so eine gute Idee, Raycasts sind wichtig :)

Share this post


Link to post
Share on other sites

So, jetzt hab ich mich schlau gemacht und meine Raycasts können sich doch schon mal sehen lassen... bzw. genau da liegt gerade mein Problem^^ Sie lassen sich halt nicht sehen. Schaden wird sauber übergeben. Die Asteroiden tun das was sie sollen nur leider bleibt der Raycast für den Spieler unsichtbar. Was irgendwie logisch ist, weil ich dazu noch nicht passendes gebaut habe.

Also, Spaß beiseite: Wie gesagt Raycast tut was er soll nun suche ich nach einer sauberen Möglichkeit den Ray auch sichtbar zu machen. Hierzu wollte ich mein ehemaliges Laser "Projetil" nehmen, was einfach auf die Position (egal ob der Raycast == true || Raycast == false ist) nach vorne geschossen werden. Dazu wollte ich die Projektile so schnell machen, dass sie direkt beim Ziel ankommen um dann zerstört zu werden. So schnell wie die dann aber sein müssten, sehe ich sie dann aber auch nicht mehr. Macht also keinen Sinn.

Gibt es dafür Partikeleffekte oder andere Möglichkeiten, die ich einbauen kann? Was bietet sich dafür am besten an?

Share this post


Link to post
Share on other sites

Der Ansatz mit dem Partikel ist schon gut. Wenn das Ding eine konstante Geschwindigkeit hat, kannst du ja ausrechnen, wie lange es fliegt, und dann vielleicht den Schaden und/oder die Animation zeitverzögert laufen lassen. Dass die Berechnung und der sichtbare Effekt eine kleine Zeitliche Differenz haben, fällt auch eher Entwicklern auf. Spieler merken das eher weniger, da musst du dir keine Sorgen machen.

Du kannst ansonsten auch einfach eine Linie mit dem LineRenderer zeichnen.

Share this post


Link to post
Share on other sites

Ausrechnen ist schwierig. Wenn ich auf stehende Objekte schieße ist das korrekt, dann kann man das leicht berechnen. Problem ist, wenn ich auf ein sich bewegendes Objekt schieße. Evtl habe ich dann mit dem Raycast nicht getroffen (knapp daneben ist auch vorbei) dann treffe ich aber doch, da das Objekt sich bewegt hat und mein Projektil auf das Objekt auftrifft.

vor 38 Minuten schrieb Sascha:

und dann vielleicht den Schaden und/oder die Animation zeitverzögert laufen lassen.

Das habe ich mir auch schon überlegt. Ist ja mit der Invoke() Methode auch kein Problem. Allerdings weiß ich nicht, wie ich der Methode, die ich in Invoke() aufrufe eine Variable übergebe.

Meine TakeDamage Methode erwartet nämlich die Menge des Schadens.

Zum Line Renderer werde ich morgen ein paar Fragen stellen... da komme ich gerade nicht wirklich klar.

Share this post


Link to post
Share on other sites
vor 8 Stunden schrieb Singular:

Problem ist, wenn ich auf ein sich bewegendes Objekt schieße. Evtl habe ich dann mit dem Raycast nicht getroffen (knapp daneben ist auch vorbei) dann treffe ich aber doch, da das Objekt sich bewegt hat und mein Projektil auf das Objekt auftrifft.

Das sind genau die Sachen, die ich meinte: Als Entwickler denkst du "muss so sein, ergibt Sinn", aber Spieler merken so etwas selten. Glaub mir, das kann man super verstecken und hat damit auch keinen Mist gebaut. Spiele sind zu 80% "Smoke and Mirrors" und gute Designer verstecken einfach nur sehr clever, dass das Gameplay tatsächlich viel, viel simpler ist als es den Anschein hat. Glaub mir, das ist nur eine Frage der genauen Werte und dann passt das, und oft fühlt sich die simple Mechanik für den Spieler auch besser an, weil sie für ihn einfacher zu handhaben ist.

vor 8 Stunden schrieb Singular:

Das habe ich mir auch schon überlegt. Ist ja mit der Invoke() Methode auch kein Problem. Allerdings weiß ich nicht, wie ich der Methode, die ich in Invoke() aufrufe eine Variable übergebe.

Lass bloß die Finger von Invoke. Methoden über ihre Namen als String aufzurufen ist ganz großer Käse.

Hier, nimm lieber das hier. Und damit einfach

StartCoroutine(Invoker.Invoke(() => asteroid.ApplyDamage(amountOfDamage), 2));

 

Share this post


Link to post
Share on other sites

 

Am 29.9.2020 um 11:09 schrieb Singular:

Ich könnte zwar den Laser.Collider zum Trigger machen, habe dann aber das Problem, dass das Script für den Schaden bei Health Script des Asteroiden liegen muss, "OnTriggerEnter()". Das möchte ich eigentlich nicht. Denn dann brauchen die anderen vom Raumschiff abgefeuert Raketen und Minen ebenfalls diese Methode um vom Laser abgeschossen werden zu können.

Hab den teil nicht genau verstanden, aber ich stelle mir eigentlich dein Setup so vor.

Beispiel:

Laser
-LaserScript
-LaserBoxCollider (GameObject mit Boxcollider als Trigger)
--TriggerScript

Asteroid
-HealthScript
-Rigidbody
-Collider (ohne Trigger)

1. Wenn das so aufgebaut ist, dann kannst im TriggerScript ein Reference zu LaserScript haben, falls du dort die all das mit Kollision berechnest. Man kann dafür extra ein TriggerScript schreiben um alles zu dem Main Script zu senden (z.B. mit UnityEvents). Dann müsste das schon gehen.

2. Methode was ich bei schnelle Projektilen gemacht habe war einfach Raycast bei jeden Tick benutzen allerdings hab ich immer vor der Bewegung berechnet, ob ich kollidieren könnte (damit es nicht durchfliegt). Wenn es um Laser geht kann man gleich die Strecke abchecken, ob es eine Kollision gibt 

3. Das was Sascha meint, soweit ich verstanden habe, einfach gucken, ob es ein Treffer ergeben könnte, wenn ja bis dahin animieren. Soweit ich weiß, machen das viele Spiele wie Battlefield, aber keine Ahnung wie das machen, wenn es um bewegliche Ziele geht. Bei Spielen wie Counter-Strike wird direkt geguckt, ob es Treffer ist und wird direkt auch schaden abgezogen.

Hier sieht man sogar, dass die erste und 2 Methode hier anwende: https://i.imgur.com/dIvr9De.gifv

Share this post


Link to post
Share on other sites
vor 4 Stunden schrieb MaZy:

Beispiel:


Laser
-LaserScript
-LaserBoxCollider (GameObject mit Boxcollider als Trigger)
--TriggerScript

Asteroid
-HealthScript
-Rigidbody
-Collider (ohne Trigger)

 

Fast. Das Trigger Script lag im HealthScript des Asteroiden. Geht es denn, dass der Trigger selbst das OnTriggerEnter script hält?

 

vor 4 Stunden schrieb MaZy:

1. Wenn das so aufgebaut ist, dann kannst im TriggerScript ein Reference zu LaserScript haben, falls du dort die all das mit Kollision berechnest. Man kann dafür extra ein TriggerScript schreiben um alles zu dem Main Script zu senden (z.B. mit UnityEvents). Dann müsste das schon gehen.

Das habe ich wiederum nicht verstanden.

 

vor 4 Stunden schrieb MaZy:

2. Methode was ich bei schnelle Projektilen gemacht habe war einfach Raycast bei jeden Tick benutzen allerdings hab ich immer vor der Bewegung berechnet, ob ich kollidieren könnte (damit es nicht durchfliegt). Wenn es um Laser geht kann man gleich die Strecke abchecken, ob es eine Kollision gibt 

Das wäre eine Möglichkeit. Ich hatte zunächst versucht, das Projektil einfach zu zerstören, wenn es an dem Punkt, den der Raycast ergeben hat ankommt oder zumindest sehr nah dran ist.

 

vor 4 Stunden schrieb MaZy:

3. Das was Sascha meint, soweit ich verstanden habe, einfach gucken, ob es ein Treffer ergeben könnte, wenn ja bis dahin animieren. Soweit ich weiß, machen das viele Spiele wie Battlefield, aber keine Ahnung wie das machen, wenn es um bewegliche Ziele geht. Bei Spielen wie Counter-Strike wird direkt geguckt, ob es Treffer ist und wird direkt auch schaden abgezogen.

Hier sieht man sogar, dass die erste und 2 Methode hier anwende: https://i.imgur.com/dIvr9De.gifv

Das Spiel sieht schon sehr nach dem aus, was ich mache. Nur, dass ich mich in einer 3D Umgebung befinde aber alles auf einer Ebene Spielt... also auch wieder 2D.^^

Ich versuche gerade das gute alte "Asteroids" neu aufzulegen ;)

Share this post


Link to post
Share on other sites

Ich habe aber dann doch nochmal eine Frage zu Raycats 😹 und dem was sie Treffen und dann noch zum LineRenderer.

Mein Raumschiff soll zwei Laser gleichzeitig, also einen links und einen rechts abfeuern. Also wird das script für den Raycast von beiden Ursprungsposiotionen aus abgefeuert. Es wird dann einzeln geschaut ob es einen Treffer von dieser Position aus gab oder nicht. Gab es einen Treffer soll der Punkt gespeichert werden um anschließend den Raycast nach dort zu ziehen. Das selbe passiert auch für die andere Seite. Gab es keinen Treffer, wird der Strahl einfach 50 units vor das Raumschiff geschmissen.

So jetzt zu meiner Frage. Was ist denn genau der "hit.point"? Weil, sollte es mal vorkommen, dass beide Seiten treffen, wird die Linie an die selbe stelle gezogen. 

Es ist nicht dramatisch aber etwas irritierend finde ich es schon, dass der getroffene "Punkt" der selbe sein soll.

tjRoxvB.png

 

Viel wichtiger ist die Frage: Wie kann ich von Position 2 zu Position 3 die Linie verschwinden lassen?

QNq6FKA.png

Derzeit mache ich es über einen Line Renderer. einen zweiten auf das selbe Objekt zu packen geht nicht würde also nur noch die Option bestehen, dass ganze auszulagern. Das geht doch bestimmt einfacher oder?

Share this post


Link to post
Share on other sites

Ohne deinen Code zu sehen, kann man da nicht viel sagen. Deine Beschreibung klingt schonmal gut.

Share this post


Link to post
Share on other sites
public class ShotLaser : MonoBehaviour
{
    private LineRenderer lineRenderer;

    public Transform spawnPlaceLeft;    public Transform spawnPlaceRight;    public Transform noTargetLeft;    public Transform noTargetRight;
    private Vector3 destinationLeft;    private Vector3 destinationRight;
    private Transform tr;

    private float lasertimerMax = 1.5f;
    private float laserEC = 5f;
    private float lasertimer = 3f;
    private float laserSchaden = 1f;

    private bool leftHit;
    private bool rightHit;


    // Start is called before the first frame update
    void Start()
    {
        tr = GetComponent<Transform>();
        lineRenderer = GetComponent<LineRenderer>();
        lineRenderer.startWidth = 0.3f;
        lineRenderer.endWidth = 0.3f;
    }

    // Update is called once per frame
    void Update()
    {
        lineRenderer.SetPosition(0, spawnPlaceLeft.position);
        lineRenderer.SetPosition(3, spawnPlaceRight.position);

        if (lasertimer < lasertimerMax) //Dafür da um zwischen den Schüssen etwas Zeit zu haben um nicht dauerhaft schießen zu können.
        {
            lasertimer += Time.deltaTime;
        }

        if (leftHit)
        {
            Invoke("ClearTargets", 0.15f);  //@Sascha: Ja ich weiß du und Invoke werden keine Freunde aber ein Problem nach dem anderen ;)
        }
        else
        {
            lineRenderer.SetPosition(1, spawnPlaceLeft.position);  // Punkt 1 ist der Zielpunkt für die Linke Waffe
            lineRenderer.SetPosition(0, spawnPlaceLeft.position);  // Punkt 0 ist Spawn der Linie für die Linke Waffe
        }

        if (rightHit)
        {
            Invoke("ClearTargets", 0.15f); // Same
        }
        else
        {
            lineRenderer.SetPosition(2, spawnPlaceRight.position);  // Punkt 2 ist der Zielpunkt für die Linke Waffe
            lineRenderer.SetPosition(3, spawnPlaceRight.position);  // Punkt 3 ist Spawn der Linie für die Linke Waffe
        }

        if (rightHit == false && leftHit == false)
        {
            lineRenderer.enabled = false;
        }
        else
        {
            lineRenderer.enabled = true;
        }

        LaserShot();
    }
private void LaserShot()
    {
        if (Input.GetKey(KeyCode.LeftControl) && lasertimer >= lasertimerMax)
        {
            if (GetComponent<PlayerMovement>().EC(laserEC))
            {
                lasertimer = 0;

                RaycastHit hit1;

                if (Physics.Raycast(spawnPlaceLeft.position, tr.forward, out hit1, 50))
                {
                    try { hit1.collider.GetComponent<Health>().TakeDamage(laserSchaden); }
                    catch { }

                    leftHit = true;

                    destinationLeft = hit1.point;

                    lineRenderer.SetPosition(1, destinationLeft);
                }
                else
                {
                    leftHit = true;
                    lineRenderer.SetPosition(1, noTargetLeft.position);
                }

                RaycastHit hit2;

                if (Physics.Raycast(spawnPlaceRight.position, tr.forward, out hit2, 50))
                {
                    try { hit2.collider.GetComponent<Health>().TakeDamage(laserSchaden); }
                    catch { }

                    rightHit = true;

                    destinationRight = hit2.point;

                    lineRenderer.SetPosition(3, destinationRight);
                }
                else
                {
                    rightHit = true;
                    lineRenderer.SetPosition(2, noTargetRight.position);
                }
            }
        }
    }

// Hier soll nur aufgeräumt werden für den nächsten Schuss
    private void ClearTargets() 
    {
        leftHit = false;
        lineRenderer.SetPosition(1, spawnPlaceLeft.position);
        rightHit = false;
        lineRenderer.SetPosition(2, spawnPlaceLeft.position); // hier nehme ich wieder "Left" um zu verhindern, dass eine Linie zwischen beiden Punkten Sichtbar ist (Wenn auch nur für kurze Zeit)
    }
}

 

Share this post


Link to post
Share on other sites
Am 30.9.2020 um 23:20 schrieb Singular:

POLhf2s.png

Das sieht übrigens auch nicht richtig aus... Warum wird die Linie denn wieder zurück zum ersten Punkt gezogen??

Oh du benutzt die selben Assets wie ich :D https://i.imgur.com/KcqYrEV.gifv

Aber ich habe leider wenig Zeit für die Entwicklung derzeit.

  • Haha 1

Share this post


Link to post
Share on other sites
vor 21 Stunden schrieb Singular:

 

Das habe ich wiederum nicht verstanden.

 

Ich hab dort gemeint, wenn man jetzt ein GameObject mit Laserscript hat und als Child noch ein GameObject mit Collider mit aktivierten IsTrigger hat, dann wird bei Laser nie OnTriggerEnter ausgeführt. Deswegen könnte man ein Trigger Script schreiben, der quasi zu Laserscript dann weiter schickt, wenn ein Trigger entstanden ist.

So als Pseude:

LaserScript.OnHit(Collider targetCollider)

TriggerScript.OnTriggerEnter(Collider targetCollider)
{
	LaserScript.OnHit(targetCollider);
}

 

Share this post


Link to post
Share on other sites

Habe meinen Code Mal Komplett eingefügt. Hat jemand eine Ahnung, wo mein Fehler liegt, bzw wie ich die Line zwischen Punkt 2 und 3 nicht rendern lasse?

Und wie gesagt das Ziel ist immer der gleiche Punkt. da geht es aber eher um mein Verständnis dieser gewählten Punkte.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...