Autor Wątek: Nie zainicjowany obiekt ? Początki z XNA.  (Przeczytany 3463 razy)

Offline Yorb

  • Użytkownik

# Listopad 16, 2013, 00:21:20
Witam to dopiero mój pierwszy weekend z XNA. W ogolę z programowaniem gier.

Bazowałem na pewnym tutorialu i do czasu było ok. Teraz próbuję całkiem coś od nowa stworzyć i już na początku mam problem.

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

        Map map;
        Camera camera;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);

            graphics.IsFullScreen = false;
            graphics.PreferredBackBufferHeight = 800;
            graphics.PreferredBackBufferWidth = 1650;

            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            camera = new Camera(GraphicsDevice.Viewport);
            map = new Map(200, 200);
           
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            Tile.Content = Content;

            map.generate(Map.GenerateRandomMap(100,100,25, 30), 32);

            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here

            camera.Update(gameTime, this);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, camera.transform);
            map.Draw(spriteBatch);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }

    class Map
    {
        public int width, height;

        public Tile[,] tiles;

        public Map(int w, int h)
        {
            tiles = new Tile[w, h];
        }

        public void generate(int[,] map, int size)  // size of tile rectangle (32x32)
        {
            for (int x = 0; x < map.GetLength(1); x++)
            {
                for (int y = 0; y < map.GetLength(0); y++)
                {
                    int number = map[x, y];

                    if (number > 0)
                    {
                        tiles[x, y] = new Tile(number, new Rectangle(x * size, y * size, size, size));
                        tiles[x, y].isCollidable = true;

                        width = (x + 1) * size;
                        height = (y + 1) * size;
                    }
                }
            }
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            for (int x = 0; x < tiles.GetLength(1); x++)
            {
                for (int y = 0; y < tiles.GetLength(0); y++)
                {
                    tiles[x, y].Draw(spriteBatch);
                }
            }
        }

        // add max and min base in each X column
        public static int[,] GenerateRandomMap(int sizeX, int sizeY, int baseY, int treePercent)
        {
              // wypelnianie tablicy (losowo generowany swiat)
        }
    }
}

    class Tile
    {
        public int X, Y;
        public int type;

        public Texture2D texture;
        //public Texture2D background_texture;
        public Rectangle rectangle;

        public bool isCollidable;
        public bool isVisible;
        public bool isAvailable;

        public static ContentManager Content;

        public Tile(int i, Rectangle rec)
        {
            texture = Content.Load<Texture2D>(i.ToString());
            this.rectangle = rec;

            isVisible = true;
            isAvailable = false;

            type = 0;
            X = 0;  Y = 0;
            isCollidable = true;
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(texture, rectangle, Color.White);
        }
    }



Cytuj
błąd NullReferenceExeption :Object reference not set to an instance of an object.

dokładnie w :

        public void Draw(SpriteBatch spriteBatch)
        {
            for (int x = 0; x < tiles.GetLength(1); x++)
            {
                for (int y = 0; y < tiles.GetLength(0); y++)
                {
                    tiles[x, y].Draw(spriteBatch);  // tutaj
                }
            }
        }

Nie umiem znaleźć przyczyny tego...wcześniej miałem List<> teraz ze zwykłą tablicą jest problem.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline ShadowDancer

  • Redaktor

  • +1
# Listopad 16, 2013, 02:17:13
Moja czarodziejska kula mówi mi, że jeśli nieprawda, że
Cytuj
if (number > 0)

to w tablicy masz nulla (do składowej którego próbujesz się potem odwołać).

Generalnie debugger visualowy jest fajny pod tym względem, że od razu pokazuje co jest nullem.

Offline Yorb

  • Użytkownik

# Listopad 16, 2013, 03:20:29
Jednak to nie to. Dalej ten sam błąd.

Dodałem 0.png do projektu i usunąłem warunek.
« Ostatnia zmiana: Listopad 16, 2013, 03:24:39 wysłana przez Yorb »

Offline koirat

  • Użytkownik

# Listopad 16, 2013, 03:49:41
Pierwsze co sie rzuca w oczy to
for (int x = 0; x < tiles.GetLength(1); x++)
Bo wcześniej inicjalizujesz:
tiles = new Tile[w, h];
A później robisz
width = (x + 1) * size;


Najpierw to poprawiaj (nie w jednym miejscu masz źle iterowanie po tablicy {GetLength(1)}) i zobacz czy dalej masz null
« Ostatnia zmiana: Listopad 16, 2013, 03:53:06 wysłana przez koirat »

Offline Yorb

  • Użytkownik

# Listopad 16, 2013, 04:11:11
Wywaliłem parę rzeczy i poszło ale dziwnie rysuję bo wszystko odwrócone o 90 stopni...

A jak zrobić aby tekstura w Tile była pusta (chodzi mi o to standardowe niebieskie tło) aby niektóre obiekty nie posiadały w ogolę tła.

Nie znam się ale w XNA na prawdę ciężko się połapać w tych osiach XY...albo ja źle coś robię albo nie jest to standardowy układ znany ze szkoły :)

Jeszcze pytanie :

To ja określam co i ile czego się rysuję ? Tzn jak mam mapę 2000x2000 kafli to to wszystko na raz się będzie rysowało mimo że na ekranie widać 30x30 ? I sam muszę zadbać o to zoptymalizować rysowanie ?

Jak zaimplementować jakieś najprostsze oświetlenie ? Od czego zacząć.


« Ostatnia zmiana: Listopad 16, 2013, 04:32:36 wysłana przez Yorb »

Offline Kebab_u_Turka

  • Użytkownik

# Listopad 16, 2013, 10:11:24
To nie wina xna, tylko w ogóle w "grafice komputerowej" układ współrzędny jest trochę inny.

Tutaj współrzędne 0,0 masz w lewym górnym rogu ;)

Cytuj
To ja określam co i ile czego się rysuję ? Tzn jak mam mapę 2000x2000 kafli to to wszystko na raz się będzie rysowało mimo że na ekranie widać 30x30 ? I sam muszę zadbać o to zoptymalizować rysowanie ?

Tak, Ty musisz zadbać o taką optymalizację. Wystarczy zrobić taką jakby "kamerę" i jeśli kafle są poza "kamerą" to nie wyświetlasz ich.

Jeśli dopiero zaczynasz pracę w XNA i grafiką w programach w ogóle, to radzę na razie brać się za proste rzeczy, daj sobie chwilowo spokój z oświetleniem.

Co do tła to nie rozumiem pytania, możesz sprecyzować ?
« Ostatnia zmiana: Listopad 16, 2013, 10:14:35 wysłana przez Kebab_u_Turka »

Offline Pawelx156

  • Użytkownik

# Listopad 16, 2013, 10:44:07
No to tak :

Cytuj
public Map(int w, int h)
        {
            tiles = new Tile[w, h];
        }

Musisz wiedzieć że inicjalizujesz tutaj tablicę referenci null;
Czyli jeśli ręcznie nie wywołasz konstruktora dla Tile masz tablicę nuli.  Możesz też skorzystać z domyślnego inicjalizowania pustym konstruktorem.


Teraz dalej:

Cytuj
int number = map[x, y];

                    if (number > 0)
                    {
                        tiles[x, y] = new Tile(number, new Rectangle(x * size, y * size, size, size));
                        tiles[x, y].isCollidable = true;

                        width = (x + 1) * size;
                        height = (y + 1) * size;
                    }

Fajnie dla numeru większego od 0 inicjalizujesz Tile konstruktorem wiec jest Ok. Ale co jeśli ten numer jest równy 0?
Twój til w tablicy jest wtedy nulem.

Więc jeśli  tablica
Cytuj
map[x, y]

posiada choćby jedno pole z zerem 

Cytuj
for (int x = 0; x < tiles.GetLength(1); x++)
            {
                for (int y = 0; y < tiles.GetLength(0); y++)
                {
                    tiles[x, y].Draw(spriteBatch);  // tutaj
                }
            }

przynajmniej jeden element tablicy tiles będzie nulem i zwróci wyjątek.

Przy gigantycznych mapach jednak warto mieć nule w tablicy bo wtedy nic ona nie zajmuje ( tylko trzeba być wtedy  bardzo ostrożnym żeby sobie nic nie nakopać ) .

Ty jednak w nule się nie baw. Wydaje mi się ze tu jest twój problem.

Cytuj
A jak zrobić aby tekstura w Tile była pusta (chodzi mi o to standardowe niebieskie tło) aby niektóre obiekty nie posiadały w ogolę tła.

Nadaj jakiś identyfikator tilowi np: 0 i zwyczajnie pomijaj wszystkie tile z takim numerem przy rysowaniu.


Cytuj
To ja określam co i ile czego się rysuję ? Tzn jak mam mapę 2000x2000 kafli to to wszystko na raz się będzie rysowało mimo że na ekranie widać 30x30 ? I sam muszę zadbać o to zoptymalizować rysowanie ?

Po pierwsze 2kx2k kafli to 4 miliony. Tu by wypadało już stosować zabawę w nule, a jak one nie pomogą nadal zbyt wiele miejsca w pamięci) to zabawa na prostych klasach z podstawowymi polami jak int short byte jako identyfikatory.

Ty się jednak nie babraj w mapy tego rozmiaru odejmij sobie po jednym zerze.

Tile ograniczasz na podstawie obszaru obserwatora ( kamera) . I wybierasz dokładnie te z tablicy które są widoczne.

Cytuj
ak zaimplementować jakieś najprostsze oświetlenie ? Od czego zacząć.

Jak już się bawisz w czyste sprajty, chyba najprostszym sposobem uzyskania światła jest jest taki shader:

sampler TextureSampler : register(s0);
sampler DisplacementSampler : register(s1);


float4 main(float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
 
// Textura swiatla
float4 co1 = tex2D(DisplacementSampler, texCoord);
// textura obrazu
float4 co2 = tex2D(TextureSampler, texCoord);

return (co1  * co2)  * 2;

}


technique Refraction
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 main();
    }
}


Potem używasz sobie rendertagetów i na nich odpowiednio rysujesz.
mniej więcej tak to wygląda:


 if (IsLightEnabled) // jesli swiatla sa wlaczone tworzy Bufor Swiatel
                {
                    GameServices.CurrentDevice.GraphicsDevice.SetRenderTarget(LightBuffer); // ustawienie bufora swiatel
                    GameServices.Sprite.Begin(DeafultSort, CurrentBlend, null, null, null, BlurEffect); // rysowanie z efektem rozmycia
   
                   
             
                    GameServices.CurrentDevice.GraphicsDevice.Clear(AmbientLightColor);
                    for (int x = 0; x < 2; x++)
                    {
                        for (int y = 0; y < LightLayers[x].Count; y++)
                        {
                            LightLayers[x][y].Draw(time);
                        }
                    }
                   
                    GameServices.Sprite.End();
                    GameServices.CurrentDevice.GraphicsDevice.SetRenderTarget(null);
             
                }
                GameServices.CurrentDevice.GraphicsDevice.SetRenderTarget(ScreenShot); // ustawienie Normalnego bufora
                GameServices.CurrentDevice.GraphicsDevice.Clear(BackBufferColor);


 if (IsLightEnabled) // jesli swiatla sa wlaczone rysuje je z efektem polaczenia
                {
                   
                    GameServices.CurrentDevice.GraphicsDevice.Textures[1] = LightBuffer;

           
                    GameServices.Sprite.Begin(DeafultSort, CurrentBlend, null, null, null, LightEffect);             
                    GameServices.Sprite.Draw(BackBuffer, Vector2.Zero, Color.White);
                    GameServices.Sprite.End();
                   
                }

// w BackBuffer została narysowana cała scena która zostanie poddana efektowi światła


To chyba najprostsze co można dla 2d.
A efekt tych świateł możesz zobaczyć na tej stronce na screenach : http://enitvare.com/media

edit:
Zapomniałem dodać, że intensywnością światła sterujesz poprzez wartość alpha koloru.  Im wartość większa tym jaśniejsze światło.
« Ostatnia zmiana: Listopad 16, 2013, 10:49:29 wysłana przez Pawelx156 »

Offline ShadowDancer

  • Redaktor

# Listopad 16, 2013, 13:10:45
Cytuj
A jak zrobić aby tekstura w Tile była pusta (chodzi mi o to standardowe niebieskie tło) aby niektóre obiekty nie posiadały w ogolę tła.
No to możesz ustawić dla Tile pustą teksturę (przeźroczystą, nie nulla), albo lepiej go nie rysować. Standardowe niebieskie tło to kolor okna, który prześwituje z pod tego co narysujesz, nie ma on swojej "tekstury".

Cytuj
Jak zaimplementować jakieś najprostsze oświetlenie ? Od czego zacząć.
Generalnie to do googla

Offline Yorb

  • Użytkownik

# Listopad 16, 2013, 18:34:04
Dzięki za odpowiedzi, na prawdę były pomocne. Dałem radę już z osiami wszystko się rysuję dobrze. Z standardowym tłem również.

Z wyświetlaniem poradziłem sobie właśnie przez klamrę (wyświetlam 50x50 kafli (mniej więcej) widocznych na ekranie każdy po 32x32 pixele. I przy 1000x1000 nic się nie tnie.

Właśnie robię minecrafta w 2D i świat teoretycznie ma być nieskończony - na osi X przynajmniej. I nie wiem jak z tym oświetleniem, tzn wystarczy mi teoretyczne słońce u góry, jakaś widoczność dawana przez gracza i ewentualnie pochodnie.
I chodzi mi praktycznie tylko o efekt - Kafelek oświetlony jest widoczny normalnie, nieoświetlony - jakaś 'mgła wojny' (nie wiem jak się to nazywa) i ewentualnie jakiś półmrok. Żadnych cieni itp i innych bajerów.
« Ostatnia zmiana: Listopad 16, 2013, 19:12:02 wysłana przez Yorb »

Offline Karol

  • Użytkownik

# Listopad 16, 2013, 19:46:36
I chodzi mi praktycznie tylko o efekt - Kafelek oświetlony jest widoczny normalnie, nieoświetlony - jakaś 'mgła wojny' (nie wiem jak się to nazywa) i ewentualnie jakiś półmrok. Żadnych cieni itp i innych bajerów.
Niech każdy kafelek ma swoją informację o oświetleniu i rysuj z odpowiednią wartością. Np. jeżeli maksymalna jasność to 20, to kafelek z 20 rysujesz w 100% alpha, kafelek z 10 rysujesz w 50% alphy, etc. Potem stosując jakieś algorytmy "flood-fill" możesz zaktualizować okolicę mapy nowymi wartościami oświetlenia po np. postawieniu pochodni.