Jump to content
Unity Insider Forum

Benötige Hilfe für GnatAttack (MarioPaint Minispiel) angelehntes Spiel - BoxCollider2D nur bei Mausklick aktivieren


@kuma:0

Recommended Posts

Hallo zusammen,

bin erst kürzlich in Unity eingestiegen, nachdem ich bereits zuvor ein Buch zum entwickeln von Spielen in C++ durchgearbeitet habe, und einige Tutorials zu Unity. Habe lediglich Grundwissen in Programmiersprachen Java und C++. Im Zuge der Durcharbeitung von dem Buch und den Tutorials habe ich auch erste Spiele entwickelt. Nun möchte in Unity mein erstes, eigenes Spiel entwickeln.

Es soll ein Spiel werden, dass an das Minispiel innerhalb von Mario Paint (Gnat Attack), angelehnt ist.  

Was ich bisher habe:

  • Einen Hintergrund für das Spielgeschehen
  • EInen animierten Sprite des Spielers (Fliegenklatsche).
  • Die Fliegenklatsche wird mit der Maus oder auf Android Geräten mit Touch-Eingabe gesteuert.Bei Mausklick schlägt man mit der Fliegenklatsche zu (Animation)
  • Sprites von zwei unterschieldichen Gegnertypen mit Animation (Spinnen Gegnertyp, Fliegen Gegnertyp) 
  • Gegner werden in unterschiedlichen Wellen gespawnt. Eine Welle stellt ein Level dar. Je höher der Level, desto mehr Gegner werden gespawnt und die Gegner werden schneller

Was mir nun Schwierigkeiten bereitet ist die Kollisionabfrage zwischen Spieler (Fliegenklatsche) und Gegner.

Was ich hinbekommen habe ist, dass man die Gegner verschwinden lässt, durch einen Mausklick, oder Touch-Eingabe direkt auf die Gegner. Ich möchte nun aber, dass nur die Fliegenklatsche (nicht die Hand die Ihn hält) die Gegner verschwinden lassen kann; und das auch nur wenn die Rechte Maustaste geklickt wurde (also wenn die Fliegenklatsche heruntergeschwungen wird).

Meine Idee war nun, dass ich über ein MouseButtonDown Event den Trigger des Boxcolliders der Fliegenklatsche deaktiveren und aktivieren lasse. Das funktioniert auch, jedoch ist das Timing unpraktisch. 

Während ich das Spiel laufen lasse, kann ich in dem Inspector sehen, dass der Trigger des BoxColliders2D bei Mausklick für einen sehr kurzen Zeitraum deaktiviert wird, und anschließend wieder aktiviert. Jedoch ist das Zeitfenster zu kurz, um damit die Gegner verschwinden zu lassen.

Ich kann die Gegner nur verschwinden lassen, wenn ich die Maustaste gedrückt halte, und dann über die Gegnersprites drüber hinweg scrolle...

Hier ist der Code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Kill : MonoBehaviour
{
    BoxCollider2D boxCol;

    void Start()
    {
        boxCol = GetComponent<BoxCollider2D>();
    }

    void Update () {
        if (Input.GetMouseButtonDown(0)) {
            boxCol.isEnable = false;
        }
        if (Input.GetMouseButtonUp(0))
        {
            boxCol.isEnable = true;
        }
    }

    void OnCollisionEnter2D(Collision2D otherCol)
    {

            //Debug.Log(otherCol.gameObject.name);
            if (otherCol.gameObject.CompareTag("Enemy"))
            {
                otherCol.transform.gameObject.SendMessage("KillMe", 0, SendMessageOptions.DontRequireReceiver);
            }
        }
    
}

Das hier ist der Code für die generelle Maus/Touch Steuerung (dort habe ich ursprünglich die simplere Kollisionabfrage über die direkte Maus/Touch-Eingabe ohne BoxCollider der Fliegenklatsche drin):

using UnityEngine;
using System.Collections;

public class SwatterController : MonoBehaviour
{
    public Animator anim;
    bool doInputChecking = true;
    public float speed = 1.5f;
    private Vector3 target;

    void Start()
    {
        target = transform.position;

    }

    void Update()
    {


        if (Input.touchCount > 0)
        {
            if (Input.GetTouch(0).phase == TouchPhase.Began)
                CheckInput(Input.GetTouch(0).position);
            target = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
            target.z = transform.position.z;
        }
        else
        {
            if (Input.GetMouseButtonDown(0))
                CheckInput(Input.mousePosition);
            target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            target.z = transform.position.z;
        }
        transform.position = Vector3.MoveTowards(transform.position, target, speed * 5);
    }

    void CheckInput(Vector3 pos)
    {

        if (doInputChecking)
        {
            Vector3 wp = Camera.main.ScreenToWorldPoint(pos);

            Vector2 touchPos = new Vector2(wp.x, wp.y);

            anim.SetTrigger("Click");


            Collider2D otherCol = Physics2D.OverlapPoint(touchPos);

            if (otherCol)
            {
                //Debug.Log(otherCol.gameObject.name);
                if (otherCol.gameObject.CompareTag("Enemy"))
                {
                    otherCol.transform.gameObject.SendMessage("KillM", 0, SendMessageOptions.DontRequireReceiver);
                }
            }
        }
        anim.SetTrigger("Release");
    }


    void StopInputChecking()
    {
        doInputChecking = false;
    }


}

Ich hoffe es ist einigermaßen Verständlich geworden, und das mir hier jemand weiterhelfen kann. Leider fällt mir das ganze Scripting in Unity noch recht schwer.

 

Grüße @kuma:0

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi!

Bei einer Fliegenklatsche würde ich vermuten, dass nur im Moment des Klickens ein einziges Mal geschaut werden sollte, ob etwas darunter ist. Collider sind allerdings eher über gewisse Zeiträume hinweg sinnvoll, wie du schon etwas gemerkt hast.

Schau dir mal die Klasse Physics2D an. Dort findest du Funktionen, die du aufrufen kannst und die dir sagen, ob sich in diesem Moment etwas in einem bestimmten Bereich befindet, z.B. Physics2D.OverlapArea.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi Sascha,

danke für deine Antwort. Ich war ja schon an dem Punkt, dass ich direkt über Mausklick, oder Touch-Eingabe, auf die Gegnersprites, diese verschwinden lassen kann. Jedoch stehe ich gerade echt auf dem Schlauch wie ich in dem bestehenden Code Abschnitt, die aktuell verwendete Methode "Physics2D.OverlapPoint" mit "Physics2D.OverlapArea" austausche.

Hier nochmal der Code, für die Maus / Touchsteuerung inklusive die Methode Physics2D.OverLapPoint, die bei Mausklick, bzw Touch-Eingabe, direkt auf den Collider er Gegnersprites, diese verschwinden lässt:

 

// SwatterController.cs

using UnityEngine;
using System.Collections;

public class SwatterController : MonoBehaviour
{
    public Animator anim;
    bool doInputChecking = true;
    public float speed = 1.5f;
    private Vector3 target;

    void Start()
    {
        target = transform.position;

    }

    void Update()
    {


        if (Input.touchCount > 0)
        {
            if (Input.GetTouch(0).phase == TouchPhase.Began)
                CheckInput(Input.GetTouch(0).position);
            target = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
            target.z = transform.position.z;
        }
        else
        {
            if (Input.GetMouseButtonDown(0))
                CheckInput(Input.mousePosition);
            target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            target.z = transform.position.z;
        }
        transform.position = Vector3.MoveTowards(transform.position, target, speed * 5);
    }

    void CheckInput(Vector3 pos)
    {

        if (doInputChecking)
        {
            Vector3 wp = Camera.main.ScreenToWorldPoint(pos);

            Vector2 touchPos = new Vector2(wp.x, wp.y);

            anim.SetTrigger("Click");


            Collider2D otherCol = Physics2D.OverlapPoint(touchPos);

            if (otherCol)
            {
                //Debug.Log(otherCol.gameObject.name);
                if (otherCol.gameObject.CompareTag("Enemy"))
                {
                    otherCol.transform.gameObject.SendMessage("KillMe", 0, SendMessageOptions.DontRequireReceiver);
                }
            }
        }
        anim.SetTrigger("Release");
    }


    void StopInputChecking()
    {
        doInputChecking = false;
    }


}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ah, ich hatte mir den Code nicht so genau angesehen und mich eher darauf bezogen:

vor 23 Stunden schrieb @kuma:0:

Meine Idee war nun, dass ich über ein MouseButtonDown Event den Trigger des Boxcolliders der Fliegenklatsche deaktiveren und aktivieren lasse. Das funktioniert auch, jedoch ist das Timing unpraktisch. 

Wenn ich das jetzt richtig verstehe, ist dein Problem, dass du nicht den Punkt, an dem sich die Maus befindet, sondern den Bereich, wo sich relativ dazu die Fliegenklatsche befindet, von Zielen befreien willst.

Ich würde dafür zwei Vector2 definieren, die dieselben Werte darstellen wie beim BoxCollider2D:

public Vector2 offset;
public Vector2 size = Vector2.one;

Damit man die Box, die von den beiden dargestellt wird, sehen kann, kannst du MonoBehaviour.OnDrawGizmosSelected nehmen:

void OnDrawGizmosSeclected()
{
  Gizmos.color = Color.yellow;
  Gizmos.DrawWireCube(transform.position + offset, size);
}

offset und size kannst du dann für die Berechnung der Parameter von Physics2D.OverlapArea benutzen. So kannst du einen Bereich definieren wie bei einem BoxCollider2D, nur halt ohne tatsächlich einen Collider zu haben.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja - ganz genau das möchte ich umgesetzt kriegen :)

An welcher Stelle soll ich die Methode OnDrawGizmosSelected einfügen? Ich bekomme aktuell folgenden Error Code in der Konsole, sobald ich die Methode einfüge:

Assets/Scripts/SwatterController.cs(43,39): error CS0121: The call is ambiguous between the following methods or properties: `UnityEngine.Vector3.operator +(UnityEngine.Vector3, UnityEngine.Vector3)' and `UnityEngine.Vector2.operator +(UnityEngine.Vector2, UnityEngine.Vector2)'

Muss ich die Beiden Vektoren (Offset und Size) mit denselben Werten initialisieren, wie den ursprünglichen BoxCollider den ich für die Fliegenklatsche definiert hatte?

Die BoxCollider2D Komponente deaktiviere ich dann wieder - richtig?

Das ist der aktuelle Code:

using UnityEngine;
using System.Collections;

public class SwatterController : MonoBehaviour
{
    public Animator anim;
    bool doInputChecking = true;
    public float speed = 1.5f;
    private Vector3 target;
    public Vector2 offset;
    public Vector2 size = Vector2.one;

    void Start()
    {
        target = transform.position;

    }

    void Update()
    {


        if (Input.touchCount > 0)
        {
            if (Input.GetTouch(0).phase == TouchPhase.Began)
                CheckInput(Input.GetTouch(0).position);
            target = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
            target.z = transform.position.z;
        }
        else
        {
            if (Input.GetMouseButtonDown(0))
                CheckInput(Input.mousePosition);
            target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            target.z = transform.position.z;
        }
        transform.position = Vector3.MoveTowards(transform.position, target, speed * 5);
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireCube(transform.position + offset, size);
    }

    void CheckInput(Vector3 pos)
    {

        if (doInputChecking)
        {
            Vector3 wp = Camera.main.ScreenToWorldPoint(pos);

            Vector2 touchPos = new Vector2(wp.x, wp.y);

            anim.SetTrigger("Click");


            Collider2D otherCol = Physics2D.OverlapArea(offset, size);

            if (otherCol)
            {
                //Debug.Log(otherCol.gameObject.name);
                if (otherCol.gameObject.CompareTag("Enemy"))
                {
                    otherCol.transform.gameObject.SendMessage("KillMe", 0, SendMessageOptions.DontRequireReceiver);
                }
            }
        }
        anim.SetTrigger("Release");
    }

    void StopInputChecking()
    {
        doInputChecking = false;
    }

}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ach ja... hatte ich fast befürchtet. Ändere die eine Zeile mal zu

Gizmos.DrawWireCube(transform.position + (Vector3)offset, size);

 

vor 2 Stunden schrieb @kuma:0:

Muss ich die Beiden Vektoren (Offset und Size) mit denselben Werten initialisieren, wie den ursprünglichen BoxCollider den ich für die Fliegenklatsche definiert hatte?

Jau, das wäre sinnvoll.

 

vor 2 Stunden schrieb @kuma:0:

Die BoxCollider2D Komponente deaktiviere ich dann wieder - richtig?

Kannst du sogar ganz löschen dann ;)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich glaube ich übersehe noch irgendetwas grundlegendes...

Der Code läuft nun ohne zu meckern, macht aber noch nicht was er soll. Die Box welche wir mit der OverlapArea Methode erstellt haben, ist nun fix an einer bestimmten Stelle am Bildschirm und ändert sich nicht relativ zur Mausposition. 

Offset und Size habe ich einfach mit exakt denselben Werten initialisiert wie zuvor den Box Collider (den ich nun auch entfernt habe). Die Box scheint jetzt mitten auf dem Spielfeld positioniert zu sein. Wenn nun ein Gegner sich zufällig in die Mitte des Spielfelds bewegt, und ich die Maustaste drücke, dann verschwindet dieser. 

Das Skript ist dem GameObject Swatter, also der Fliegenklatsche zugeordnet. Ich bin davon ausgegangen, die durch die OverlapArea defnierte Box, würde sich auf der relativen Position des Gameobjects beziehen?

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 14 Minuten schrieb @kuma:0:

Ich bin davon ausgegangen, die durch die OverlapArea defnierte Box, würde sich auf der relativen Position des Gameobjects beziehen?

Nein, die ist in world space.

Die Lösung für das Problem verbirgt sich in der OnDrawGizmosSelected-Methode, die ich dir gegeben habe. Dir wird aufgefallen sein, dass die angezeigte Position stimmt. Das passiert durch Addieren von transform.position und offset.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich kann in der Scene View die Box mit dem gelben Rahmen sehen. Die Box umrandet nun genau die Fliegenklatsche (siehe Screenshot)

Wenn ich das Spiel starte und die Mausbewege, sehe ich in der Scene View immernoch wie sich die gelbe Box mit der Mausposition mitbewegt. Allerdings verschwinden die Gegner dennoch nicht bei Mausklick. 

Wenn aber ein Gegner in der Mitte des Feldes ist, und es wird mit der Maus geklickt, verschwindet er... Wieso bleibt die Position immer noch "Fix" in der Mitte des Spielfelds?

 

Swatter.png

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hier nochmal der aktuelle Code:

 

using UnityEngine;
using System.Collections;

public class SwatterController : MonoBehaviour
{
    public Animator anim;
    bool doInputChecking = true;
    public float speed = 1.5f;
    private Vector3 target;
    public Vector2 offset;
    public Vector2 size = Vector2.one;

    void Start()
    {
        target = transform.position;

    }

    void Update()
    {


        if (Input.touchCount > 0)
        {
            if (Input.GetTouch(0).phase == TouchPhase.Began)
                CheckInput(Input.GetTouch(0).position);
            target = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
            target.z = transform.position.z;
        }
        else
        {
            if (Input.GetMouseButtonDown(0))
                CheckInput(Input.mousePosition);
            target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            target.z = transform.position.z;
        }
        transform.position = Vector3.MoveTowards(transform.position, target, speed * 5);
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireCube(transform.position + (Vector3)offset, size);
    }

    void CheckInput(Vector3 pos)
    {

        if (doInputChecking)
        {
           // Vector3 wp = Camera.main.ScreenToWorldPoint(pos);

           // Vector2 touchPos = new Vector2(wp.x, wp.y);

            anim.SetTrigger("Click");


            Collider2D otherCol = Physics2D.OverlapArea(offset, size);

            if (otherCol)
            {
                //Debug.Log(otherCol.gameObject.name);
                if (otherCol.gameObject.CompareTag("Enemy"))
                {
                    otherCol.transform.gameObject.SendMessage("KillMe", 0, SendMessageOptions.DontRequireReceiver);
                }
            }
        }
        anim.SetTrigger("Release");
    }

    void StopInputChecking()
    {
        doInputChecking = false;
    }

}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hm... du hast auch einfach ignoriert, was ich dir geschrieben habe.

vor 4 Stunden schrieb Sascha:

Die Lösung für das Problem verbirgt sich in der OnDrawGizmosSelected-Methode, die ich dir gegeben habe. Dir wird aufgefallen sein, dass die angezeigte Position stimmt. Das passiert durch Addieren von transform.position und offset.

Also nochmal vergleichen - richtige Position:

Gizmos.DrawWireCube(transform.position + (Vector3)offset, size);

gegen falsche Position:

Physics2D.OverlapArea(offset, size);

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...