Παρασκευή 24 Δεκεμβρίου 2010

ΧΝΑ - Το πρώτο μας τρίγωνο



Μάθημα 4ο

  • Όπως είπαμε όλα αποτελούνται από τρίγωνα. Ένα τρίγωνο καθορίζεται από τρία σημεία. Τα σημεία με την σειρά τους ορίζονται από ένα διάνυσμα το οποίο κρατάει τις συντεταγμένες τους στον τρισδιάστατο χώρο. Όμως εκτός από την θέση, ίσως χρειαζόμαστε και το χρώμα των σημείων. Ένα vertex κρατάει όλες αυτές τις σημαντικές πληροφορίες ενός σημείου.(πληθυντικός, vertices).

    Ευτυχώς το XNA έχει μια δομή η οποία μπορεί να κρατήσει της πληροφορίες ενός vertex. Η δομή αυτή είναι η VertexPositionColor.
  • Για να δημιουργήσουμε ένα τρίγωνο, χρειαζόμαστε τρία vertices τα οποία και θα βάλουμε σε έναν πίνακα. Οπότε, χωρίς να καθυστερούμε, γράφουμε τον παρακάτω κώδικα στην αρχή της κλάσης:
    VertexPositionColor[] vertices;

    Και τώρα φτιάχνουμε μια συνάρτηση η οποία θα γεμίζει αυτόν τον πίνακα:

    private void SetUpVertices()

    {
    vertices = new VertexPositionColor[3]; 
vertices[0].Position = new Vector3(-0.5f, -0.5f, 0f);
vertices[0].Color = Color.Red;
vertices[1].Position = new Vector3(0f, 0.5f, 0f);
vertices[1].Color = Color.Green;
vertices[2].Position = new Vector3(0.5f, -0.5f, 0f);
vertices[2].Color = Color.Yellow;

Το ήξερες? : private σημαίνει ότι η μέθοδος δεν μπορεί να χρησιμοποιηθεί από άλλες κλάσεις. void σημαίνει ότι δεν επιστρέφει τιμή. Υπάρχει τρόπος μια μέθοδος να επιστρέψει πάνω από μια τιμές, εφόσον δηλώσει μια παράμετρο της ως out!

Αυτές οι συντεταγμένες αφορούν την οθόνη μας. Για παράδειγμα το (-1,-1,0) είναι το κάτω αριστερό άκρο ενώ το (1,1,0) είναι το πάνω δεξιά. Αφού αυτές οι συντεταγμένες είναι ήδη σωστές, δεν χρειάζονται κάποιον μετασχηματισμό για να γίνουν από τρισδιάστατες δισδιάστατες. Για αυτόν τον λόγο, είχαμε χρησιμοποιήσει το Prestransformed για τεχνική του effect.

Το ήξερες? : το f δίπλα στους αριθμούς σημαίνει ότι είναι float, και χρειάζεται διότι ένας αριθμός όπως ο 0.5 θεωρείται double από μόνος του. Το XNA χρησιμοποιεί κυρίως floats.

  • Όταν πούμε στο XNA να σχεδιάσει τα τρίγωνα, αυτό πρέπει να στείλει τα vertices σε ένα byte stream το οποίο θα δεχτεί η κάρτα γραφικών. Η κάρτα γραφικών βλέπει το byte stream αλλά δεν ξέρει τι είναι εκεί μέσα. Θα χρειαστούμε λοιπόν ένα αντικείμενο VertexDeclaration για να πούμε στην κάρτα γραφικών τι είδους vertices να περιμένει.

    Αυτό το αντικείμενο είναι πάρα πολύ σημαντικό για όλο το project μας (και άρα θα το βάλουμε στην αρχή της κλάσης μας), μιας και χωρίς αυτό δεν θα μπορούμε να σχεδιάσουμε vertices με την κάρτα γραφικών. Επιπλέον, πρέπει να το κάνουμε initialize μόνο μια φορά. (Αν προσπαθήσουμε να το ξανακάνουμε, εφόσον τρέχουμε το παιχνίδι σε Xbox360 το .NET framework θα κρασάρει!). Άρα γράφουμε στην αρχή της κλάσης μας:

    VertexDeclaration myVertexDeclaration;
  • Τώρα αρχικοποιούμε αυτήν την μεταβλητή, κάπου μέσα στον κώδικα της συνάρτησης που γράψαμε:
    myVertexDeclaration = new VertexDeclaration(Gdevice,VertexPositionColor.VertexElements);

    Αυτό το αντικείμενο πρέπει να το στείλουμε στην κάρτα γραφικών ώστε να ξέρει ότι έρχονται αντικείμενα τύπου VertexPositionColor προς αυτήν.

  • Πρέπει φυσικά να καλέσουμε την συνάρτηση, και αφού η συνάρτηση χρησιμοποιεί το Gdevice, καλύτερα αυτό να γίνει στο τέλος της LoadContent μεθόδου:

    SetUpVertices(); 
  • Τέλος θα χρειαστεί φυσικά να σχεδιάσουμε το τρίγωνο. Ανάμεσα στο pass.Begin() και pass.End στην μέθοδο Draw γράφουμε:

    Gdevice.VertexDeclaration = myVertexDeclaration; Gdevice.DrawUserPrimitives(PrimitiveType.TriangleList,vertices,0,1);

    Η τελευταία παράμετρος είναι ο αριθμός των τριγώνων, ενώ η προτελευταία ο αριθμός του vertex από το οποίο ξεκινάμε. Φυσικά πρέπει να δώσουμε τον πίνακα των vertices και να πούμε στην κάρτα γραφικών ότι της στέλνουμε μια λίστα από τρίγωνα. Υπάρχει για παράδειγμα και η TriangleStrip η οποία σχεδιάζει πιο γρήγορα αλλά αφορά τρίγωνα που έχουν κάποιες ακμές ενωμένες.


  • Ιδιαίτερη προσοχή πρέπει να δοθεί στην σειρά με την οποία δηλώνονται τα σημεία, διότι αν τα σημεία του τριγώνου δηλωθούν αριστερόστροφα, ο προσανατολισμός του αντικειμένου είναι προς την αντίθετη μεριά από αυτήν που κοιτάμε. Με λίγα λόγια, το αντικείμενο θα εξαφανιστεί. Πρέπει να δηλώνουμε τα σημεία με την φορά του ρολογιού.
Το 4ο μάθημα έφτασε στο τέλος του.



Το αποτέλεσμα της δουλειάς μας μέχρι τώρα

Ασχολίες για εξάσκηση:
  1. Προσπαθήστε να αλλάξετε το μέγεθος του αρχικού τριγώνου
  2. Δημιουργήστε δύο τρίγωνα τα οποία να καλύπτουν την οθόνη (και να μην είναι έξω από αυτήν)
  3. Δημιουργήστε ένα τρίγωνο με αριστερόστροφο προσανατολισμό


Πηγές: Riemer's Tutorial

_________________________________________

Απλοποιημένος κώδικας (χωρίς περιττά σχόλια)



using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace SandboxGame
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        GraphicsDevice Gdevice;

        Effect myEffect;

        VertexPositionColor[] vertices;

        VertexDeclaration myVertexDeclaration;

        private void SetUpVertices()
        {
            myVertexDeclaration = new VertexDeclaration(Gdevice, VertexPositionColor.VertexElements);
            vertices = new VertexPositionColor[3];
            vertices[0].Position = new Vector3(-0.5f,-0.5f,0f);
            vertices[1].Position = new Vector3(0f,0.5f,0f);
            vertices[2].Position = new Vector3(0.5f,-0.5f,0f);

            vertices[0].Color = Color.Red;
            vertices[1].Color = Color.Green;
            vertices[2].Color = Color.Yellow;
        }

        // Constructor
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        // Init
        protected override void Initialize()
        {
            graphics.PreferredBackBufferHeight = 600;
            graphics.PreferredBackBufferWidth = 800;
            graphics.IsFullScreen = false;
            graphics.ApplyChanges();
            Window.Title = "Το αλμυρό φυστίκι";

            base.Initialize();
        }

        // Load Content
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            Gdevice = graphics.GraphicsDevice;
            myEffect = Content.Load<Effect>("effects");

            SetUpVertices();
        }

        protected override void UnloadContent()
        {

        }

        // Update (60 times per second)
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        // Draw
        protected override void Draw(GameTime gameTime)
        {
            Gdevice.Clear(Color.CornflowerBlue);

            myEffect.CurrentTechnique = myEffect.Techniques["Pretransformed"];

            myEffect.Begin();

            foreach (EffectPass pass in myEffect.CurrentTechnique.Passes)
            {
                pass.Begin();

                Gdevice.VertexDeclaration = myVertexDeclaration;
                Gdevice.DrawUserPrimitives(PrimitiveType.TriangleList,vertices,0,1);

                pass.End();
            }
            myEffect.End();

            base.Draw(gameTime);
        }
    }
}

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου