Unity 3x3 Tic Tac Toe Feld erstellen

_Phoenix_

Cadet 3rd Year
Registriert
Juni 2020
Beiträge
38
Hallo zusammen,
Ich wollte mit Unity ein Tic Tac Toe Spiel erstelle,
ich habe versucht mit einem array vom Typ Rect die 3x3 Felder eines Tic Tac Toe Spiel zu erstellen und darzustellen.
Dabei bin ich aber auf Schwierigkeiten gestossen, egal wie ich es versucht habe, ich habe es nicht geschafft die Felder darzustellen,
ich denke aber das ich von der Lösung nicht mehr so weit entfernt bin.
Also was muss ich ändern damit ich ein 3x3 Feld erhalte??

Mein Code:

C#:
using System.Collections.Generic;
using UnityEngine;
using System;

public class WereIsClicked : MonoBehaviour
{
    public Rect[] rects = new Rect[9];
    void Start()
    {
        transform.position = new Vector3(Camera.main.aspect * Camera.main.orthographicSize, Camera.main.orthographicSize, 0);
        float camwidth = Camera.main.aspect * Camera.main.orthographicSize * 2;
        float camheight = Camera.main.orthographicSize * 2;
        float xpos = (camwidth / 2) - (camheight / 2);
        float ypos = 0;
        for (int i = 0; i < 9; i++)
        {
            rects[i] = new Rect(xpos, ypos, camheight / 3, camheight / 3);
            
            if (ypos >= camheight)
            {
                Debug.Log("Set y to zero");
                ypos = 0;
                xpos += (camheight / 3);
                Debug.Log("Before: Y  of Rec number: "+i+" is "+rects[i].y);
                rects[i] = new Rect(xpos, ypos, camheight / 3, camheight / 3);
                Debug.Log("After Y  of Rec number: " + i + " is " + rects[i].y);
            }
            else
            {
                ypos += (camheight / 3);
            }
            
        }
    }

    // Update is called once per frame
    void Update()
    {

        Vector3 mouseposition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 1.0f));

        for (int i = 0; i < 9; i++)
        {

            if (Input.GetMouseButtonDown(0) && rects[i].Contains(mouseposition))
            {
                UnityEngine.Debug.Log("Das Quadrat Nummer " + i + " wurde angeklickt");

                Debug.Log(rects[i].y);

            }
        }
    }


    void OnDrawGizmos()
    {
        float camwidth = Camera.main.aspect * Camera.main.orthographicSize * 2;
        float camheight = Camera.main.orthographicSize * 2;


        //Canvas
        Rect Canvas = new Rect(0, 0, camwidth, camheight);
        Gizmos.color = new Color(0.0f, 1.0f, 0.0f);
        DrawRect(Canvas);
        
        
        for (int i = 0; i < 9; i++)
        {


            DrawRect(rects[i]);
        }


        

        
    }


    void DrawRect( Rect rect )
    {
        Gizmos.DrawWireCube(new Vector3(rect.center.x, rect.center.y, 0.01f), new Vector3(rect.size.x, rect.size.y, 0.01f));
    }



}

Vielen Dank schon mal in Voraus für eure Hilfe

Lg
Phoenix
 
  • Gefällt mir
Reaktionen: Hayda Ministral
Hast du, @Flynn74 vielleicht eine Idee??
Ich war mal so frech und hab dich markiert, wenn dich stört werde ich das aber in Zukunft unterlassen.
Lg Phoenix
 
Mir ist nicht ganz klar was du erreichen möchtest. Zum Einen fängst du den Maus-Input in Update() im Playmode ab - versuchst aber deine Felder mit OnDrawGizmos() zu realisieren - welches AUSSCHLIESSLICH in der Scene-View klappt und auch nur dafür gedacht ist kleine GameObject-Helferlein beim Erstellen der Szene zu zeichnen (z.B. Hilfslinien). Aber wie gesagt NUR im Scene-Window.

Ich vermute jetzt einfachmal du möchtest schon alles im Playmode (also im Game-Window - wenn du in Unity auf "Play" klickst) realisieren? Dann musst du deine Rechtecke auch einmalig in Start() oder Awake() generieren (siehe nächster Absatz) und dann wie gehabt die Interaktion in Update() abfangen und visualisieren. Aktuell dürfest du also garnix sehen. Zumindest nicht im "Game" Window.

Und zu guter Letzt ist Rect nur eine Datenstruktur - nix Visuelles. Da du nicht den UI Modus von Unity nutzt und anscheinend alles im 3D Mode realisierst könntest du 3D Planes oder Cubes on-the-fly generieren und deren Abmessungen dann mit deinen Rect Daten füttern: GameObject.CreatePrimitive(PrimitiveType.Plane)
Ganz generell ist dein Projekt aber eigentlich viel besser im UI Mode mit Images und RectTransforms aufgehoben. Aber in 3D gehts natürlich auch.
 
Also erst mal Danke für deine schnelle Antwort @Flynn74.
Die Idee war eigentlich zuerst die 3x3 Felder zu erstellen, und nur zum testen habe ich Gizmos benutzt später wollte ich die Outlines mit OnGui anzeigen lassen aber irgendwie macht mir OnGui die Felder ganz klein und schiebt sie in die linke obere Ecke, und die Zeichen also das Kreuz und den Kreis hätte ich dann als Bild hinzugefügt.

Mein Code dazu:
C#:
void OnGUI()
    {
        float camwidth = Camera.main.aspect * Camera.main.orthographicSize * 2;
        float camheight = Camera.main.orthographicSize * 2;
        float xpos = (camwidth / 2) - (camheight / 2);
        float ypos = 0;
        for (int i = 0; i < 9; i++)
        {

            DrawQuad(rects[i], Color.red);
        }

        void DrawQuad( Rect position, Color color )
        {
            Texture2D texture = new Texture2D(1, 1);
            texture.SetPixel(0, 0, color);
            texture.Apply();
            GUI.skin.box.normal.background = texture;
            GUI.Box(position, GUIContent.none);
        }
    }
Aber noch mal zu meinem Ursprungs-Problem es werden nur 7 von 9 Feldern gezeichnet und ich habe keinen Plan wieso...

Lg
Phoniex
 
Nun, das liegt daran dass bei deiner Rechteck-Generierung jeweils zwei Rechtecke die gleichen Positionen besitzen. Beschäftige dich am besten mal mit dem Thema "Debugging" - dann findest du solche Problemchen ganz leicht:

Screenshot_1.png


Zu Problem Nummer 2 - die winzigen Rechtecke - das liegt dran dass du mit der Kamera-Breite/Höhe (in Unit Values), nicht mit der Bildschirm Breite/Höhe (in Pixeln) arbeitest. Anstatt Camera.main.orthographicSize liefern Screen.width und Screen.height die passenden Werte.

Und zu guter Letzt - da die Rechtecke nun mit Screen-Pixel Angaben generiert und visualisiert werden, brauchst du auch die Maus-Position nicht länger in Unit Values umzurechnen und kannst direkt mit Pixelwerten arbeiten um die Klicks abzufangen. Dabei aber bedenken das Y = 0 unten und nicht oben ist. X = 0 und Y = 0 ist also unten links.

Ich war mal so frei:

C#:
using System.Collections.Generic;
using UnityEngine;
using System;

public class WhereIsClicked : MonoBehaviour
{
    public Rect[] rects = new Rect[9];
    private float camwidth = Screen.width;
    private float camheight = Screen.height;

    void Start()
    {
        transform.position = new Vector3(0, 0);

        int offset = 0;
        float rectWidth = camwidth / 3f;
        float rectHeight = camheight / 3f;
        for (float ypos = 0; ypos < camheight; ypos+=rectHeight)
        {
            for (float xpos = 0; xpos < camwidth; xpos+=rectWidth)
            {
                rects[offset++] = new Rect(xpos, ypos, rectWidth, rectHeight);
            }
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            for (int i = 0; i < rects.Length; i++)
            {
                if (rects[i].Contains(new Vector2(Input.mousePosition.x, camheight - Input.mousePosition.y)))
                {
                    UnityEngine.Debug.Log("Das Quadrat Nummer " + i + " wurde angeklickt");
                }
            }
        }
    }

    void OnGUI()
    {
        float xpos = (camwidth / 2) - (camheight / 2);
        float ypos = 0;
        for (int i = 0; i < 9; i++)
        {
            DrawQuad(rects[i], Color.red);
        }       
    }
   
    void DrawQuad(Rect position, Color color)
        {
            Texture2D texture = new Texture2D(1, 1);
            texture.SetPixel(0, 0, color);
            texture.Apply();
            GUI.skin.box.normal.background = texture;
            GUI.Box(position, GUIContent.none);
        }
}

Wie gesagt, versuche mit Debugging zu arbeiten, setze Breakpoints und untersuche deine Variablen zur Laufzeit. Dann findest du solche Fehler recht schnell.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: _Phoenix_
Wieso Linux? Ich für meinen Teil nutze das aktuelle Visual Studio unter Windows. VSCode würde es aber auch tun.
Und ja...C#...geht ja um Unity3D.
 
Ich hätte noch mal eine Frage bezüglich meines Unity-Projekts.
Ich bin jetzt so weit dass jetzt nur noch die Outlines der Rechtecke angezeigt werden, dazu habe ich es sogar noch geschafft einen Funktionierenden Siegercode zu erstellen. Das einzige was jetzt noch fehlt, sind die Kreise und Kreuze, ich habe versucht diese mit DrawTexture einzubinden, hat aber zu nicht geführt,
Also zu meiner Frage, wie stelle ich Bilder in Unity dar.

Mein Code:
C#:
using System.Collections.Generic;
using UnityEngine;
using System;

public class WhereIsClicked : MonoBehaviour
{
    private float camwidth = Screen.width;
    private  float camheight = Screen.height;
    public Rect[] rects = new Rect[9];
    public List<int> player1 = new List<int>();
    public List<int> player2 = new List<int>();
    public int currentplayer = 2;

    public Tuple<int, int, int>[] win_set = {
            //top to bottem
            Tuple.Create(0,1,2),
            Tuple.Create(3,4,5),
            Tuple.Create(6,7,8),
            //left to right
            Tuple.Create(0,3,6),
            Tuple.Create(1,4,7),
            Tuple.Create(2,5,8),
            //Diagonal
            Tuple.Create(0,4,8),
            Tuple.Create(2,4,6)
    };

    
    
    void Start()
    {
        transform.position = new Vector3(Camera.main.aspect * Camera.main.orthographicSize, Camera.main.orthographicSize, 0);

        float xpos = (camwidth / 2) - (camheight / 2);
        float ypos = 0;

        for (int i = 0; i < 9; i++)
        {
            if (ypos == camheight)
            {
                ypos = 0;
                xpos += (camheight / 3);
                rects[i] = new Rect(xpos, ypos, camheight / 3, camheight / 3);
                ypos += (camheight / 3);
            }
            else
            {
                rects[i] = new Rect(xpos, ypos, camheight / 3, camheight / 3);
                ypos += (camheight / 3);
            }
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            if (currentplayer == 2)
            {
                currentplayer = 1;
            }
            else
            {
                currentplayer = 2;
            }

            for (int i = 0; i < rects.Length; i++)
            {
                if (rects[i].Contains(Input.mousePosition))
                {
                    Debug.Log(i);
                    if (currentplayer == 1)
                    {
                        
                        player1.Add(i);
                        if (Iswin(player1, win_set))
                        {
                            Debug.Log("Spieler " + currentplayer + " hat gewonnen");
                        }
                    }
                    else
                    {
                        player2.Add(i);
                        if(Iswin(player2, win_set))
                        {
                            Debug.Log("Spieler "+currentplayer+" hat gewonnen");
                        }
                    }
                }
            }
        }
    }

    
    public static bool Iswin( List<int> clicked, Tuple<int, int, int>[] win )
    {
        foreach (Tuple<int, int, int> i in win)
        {
            if (Contain(clicked, i.Item1))
            {
                if (Contain(clicked, i.Item2))
                {
                    if (Contain(clicked, i.Item3))
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    static bool Contain( List<int> clicked, int number )
    {
        foreach (int i in clicked)
        {
            if (i == number)
            {
                return true;
            }
        }
        return false;
    }
    void OnGUI()
    {
        Rect main = new Rect((camwidth / 2) - (camheight / 2), 0, camheight, camheight);
        
            for (int i = 0; i < 9; i++)
            {
                DrawRectangle(rects[i], 1, Color.black);
            }
            
            DrawRectangle(main, 2, Color.black);
            
    }

  
    public static void DrawRectangle( Rect area, int frameWidth, Color color )
    {
        //Create a one pixel texture with the right color
        var texture = new Texture2D(1, 1);
        texture.SetPixel(0, 0, color);
        texture.Apply();

        Rect lineArea = area;
        lineArea.height = frameWidth; //Top line
        GUI.DrawTexture(lineArea, texture);
        lineArea.y = area.yMax - frameWidth; //Bottom
        GUI.DrawTexture(lineArea, texture);
        lineArea = area;
        lineArea.width = frameWidth; //Left
        GUI.DrawTexture(lineArea, texture);
        lineArea.x = area.xMax - frameWidth;//Right
        GUI.DrawTexture(lineArea, texture);
    }
}

Lg Phoenix
 
Dein Ansatz mit GUI.DrawTexture war schon richtig - auch wenn (ich hatte es ja schon mal erwähnt) der OnGUI Kram in Unity hoffnungslos veraltet ist und das Projekt eigentlich mit Unity UI umgesetzt werden müsste. Aber sei's drum.

Ich habe deinen Code mal um zwei Funktionen zum Zeichnen der Bilder (Texturen) ergänzt und den Aufruf zweimal getriggert. Ich hab die Funktionsaufrufe nicht in deine Programmlogik eingebunden (du sollst ja auch noch was zu basteln haben ;)), es geht nur drum dir das "wie" zu zeigen:


C#:
using System.Collections.Generic;
using UnityEngine;
using System;

public class WhereIsClicked : MonoBehaviour
{
    private float camwidth = Screen.width;
    private float camheight = Screen.height;
    public Rect[] rects = new Rect[9];

    public Texture textureCross;
    public Texture textureCircle;

    public List<int> player1 = new List<int>();
    public List<int> player2 = new List<int>();
    public int currentplayer = 2;

    public Tuple<int, int, int>[] win_set = {
            //top to bottem
            Tuple.Create(0,1,2),
            Tuple.Create(3,4,5),
            Tuple.Create(6,7,8),
            //left to right
            Tuple.Create(0,3,6),
            Tuple.Create(1,4,7),
            Tuple.Create(2,5,8),
            //Diagonal
            Tuple.Create(0,4,8),
            Tuple.Create(2,4,6)
    };



    void Start()
    {
        transform.position = new Vector3(Camera.main.aspect * Camera.main.orthographicSize, Camera.main.orthographicSize, 0);

        float xpos = (camwidth / 2) - (camheight / 2);
        float ypos = 0;

        for (int i = 0; i < 9; i++)
        {
            if (ypos == camheight)
            {
                ypos = 0;
                xpos += (camheight / 3);
                rects[i] = new Rect(xpos, ypos, camheight / 3, camheight / 3);
                ypos += (camheight / 3);
            }
            else
            {
                rects[i] = new Rect(xpos, ypos, camheight / 3, camheight / 3);
                ypos += (camheight / 3);
            }
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            if (currentplayer == 2)
            {
                currentplayer = 1;
            }
            else
            {
                currentplayer = 2;
            }

            for (int i = 0; i < rects.Length; i++)
            {
                if (rects[i].Contains(Input.mousePosition))
                {
                    Debug.Log(i);
                    if (currentplayer == 1)
                    {

                        player1.Add(i);
                        if (Iswin(player1, win_set))
                        {
                            Debug.Log("Spieler " + currentplayer + " hat gewonnen");
                        }
                    }
                    else
                    {
                        player2.Add(i);
                        if (Iswin(player2, win_set))
                        {
                            Debug.Log("Spieler " + currentplayer + " hat gewonnen");
                        }
                    }
                }
            }
        }
    }


    public static bool Iswin(List<int> clicked, Tuple<int, int, int>[] win)
    {
        foreach (Tuple<int, int, int> i in win)
        {
            if (Contain(clicked, i.Item1))
            {
                if (Contain(clicked, i.Item2))
                {
                    if (Contain(clicked, i.Item3))
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    static bool Contain(List<int> clicked, int number)
    {
        foreach (int i in clicked)
        {
            if (i == number)
            {
                return true;
            }
        }
        return false;
    }
    void OnGUI()
    {
        Rect main = new Rect((camwidth / 2) - (camheight / 2), 0, camheight, camheight);

        for (int i = 0; i < 9; i++)
        {
            DrawRectangle(rects[i], 1, Color.black);
        }

        DrawRectangle(main, 2, Color.black);
        
        this.DrawCross(3);
        this.DrawCircle(5);
    }

    public void DrawCross(int rectId)
    {
        GUI.DrawTexture(rects[rectId], this.textureCross);
    }

    public void DrawCircle(int rectId)
    {
        GUI.DrawTexture(rects[rectId], this.textureCircle);
    }

    public static void DrawRectangle(Rect area, int frameWidth, Color color)
    {
        //Create a one pixel texture with the right color
        var texture = new Texture2D(1, 1);
        texture.SetPixel(0, 0, color);
        texture.Apply();

        Rect lineArea = area;
        lineArea.height = frameWidth; //Top line
        GUI.DrawTexture(lineArea, texture);
        lineArea.y = area.yMax - frameWidth; //Bottom
        GUI.DrawTexture(lineArea, texture);
        lineArea = area;
        lineArea.width = frameWidth; //Left
        GUI.DrawTexture(lineArea, texture);
        lineArea.x = area.xMax - frameWidth;//Right
        GUI.DrawTexture(lineArea, texture);
    }
}

Die verwendeten Texturen findest du hier im Anhang.

Du hast dein Skript ja sicher auf irgendeinem Gameobjekt in der Szene liegen? In diesem müsstest du die Texturen wie folgt per drag'n'drop verlinken:

Screenshot.png


Voila. :)

Es macht übrigens keinen Sinn deine Funktionen als "statics" zu deklarieren. Du hast von deiner Klasse ja sicher eine Instanz und keine weiteren Skripte - da würde ich das static einfach weglassen. Statische Funktionen machen (unter Anderem) da Sinn wo es keine Klasseninstanz von gibt. Das aber nur so als abschließender Tip.
 

Anhänge

  • circle.png
    circle.png
    14 KB · Aufrufe: 208
  • cross.png
    cross.png
    13,2 KB · Aufrufe: 221
  • Gefällt mir
Reaktionen: Hayda Ministral

Ähnliche Themen

Antworten
8
Aufrufe
5.294
Antworten
15
Aufrufe
1.265
Vasdada
V
  • Gesperrt
Antworten
17
Aufrufe
4.220
Zurück
Oben