Showing posts with label Game Development. Show all posts
Showing posts with label Game Development. Show all posts

Making a Loading Screen for a game

I will handle how to make a loading screen for a game using C# and SFML.Net. I think it covers other frameworks as well. I mean, you can make it with other C# graphics library or other some Java frameworks most probably. 

What's loading screen? The loading screen shows some information of the process that are necessary data for the game. These data can be world map, and generating a world map can take a noticeable amount of time. For example, Terraria, etc. Therefore, we need to use loading screen for that moment.

Normally, when I started the game, there is a blockage because of the generating something for the game. At this moment, we can not do anything with the window. If I click the window, the program might be crushed. Also, it does not give a good impression. 

I will keep my example very simple. Let's get to work.

Firstly, I'm going to use a thread for generating operation. So the map is generated in the another thread. Also, I need a text object. This object will be drawn to show the percentage of the generating map on the screen. I need to get information from the thread I created to use in the main thread. I created a class called SharedData for it:

    class SharedData
    {
        public int Progress {get; set;} = 0;
    }
    
    class Game
    {
        ...
        
        Thread thread;
        Text Percentage { get; set; }        
        public static SharedData shared = new SharedData();
        
        public Game(...)
        {
            ...
            
            Percentage = new Text("0%", font, 100);
            Percentage.Position = new Vector2f(-25, -25);
            Percentage.FillColor = Color.Black;
            
            thread = new Thread(GenerateMap);
            thread.Start();
        }
I defined the SharedData object as static because we need to access it from another thread easily. The shared variable is actually a global variable. I created the text object and named it as Percentage. After that, I created a thread called thread, and add GenerateMap method to the this thread. I started this thread with Start method. Let's use this shared variable in the Generate method:
        public void GenerateMap()
        {
            for(int i = 0; i < 4116; i++)
            {
                
                // generate data here
                
            	Game.shared.Progress = i; 
            }
       	}
I just track how many times the loop has run like in above. We need to use this data in the main thread for the loading screen. If the thread is alive then we can show the loading screen with percentage. That's so simple:
        ...

        public void update(float dt)
        {
            if(thread.IsAlive)
            {
                float calc = ((float)shared.Progress / 4116f) * 100;
                Percentage.DisplayedString = ((int)calc).ToString() + "%";
            }
            else
            {
                // update generated data
            }
        }

        public void draw(RenderTarget target)
        {
            if(thread.IsAlive)
            {
                target.Draw(Percentage);
            }
            else
            {
                // draw generated data
            }
        }
Let's see how it works:
making the loading screen for game
Devamını Oku »

Creating a Level - Unity 3D #1

I've had some dealings with Unity before, but I'm still a stranger to it. But now I have to put a stop to it. I will make a 3d game with Unity. Of course, it would be more accurate to say prototype rather than game. In this process, I will write down the topics that I do not know on this blog. 

In this post, I will create a scene. This scene will contain some cubes and one sphere. This sphere is controlled by player. 

The default screne already came as default with new project. Firstly, I renamed the default scene as Level1. We can do this in Assets/Scenes folder. Also, if we want to add new levels, it will be enough to create a new scene and add it to this folder.

The first thing I need are cubes. I will use them as intermediaries. After all, there will be gravitational force in the game, but this force will only be valid for the sphere. So the cubes will not be affected. They will simply be a platform for the sphere.

But before that, let's create two folders in our project. These folders are Scripts and Prefabs.

  • Scripts will contain source code as C#
  • Prefabs will contain reusable objects like cubes.
Unity Project Directory

Right click on top of the hierarchy panel and click 3D Object >  Cube and 3D Object > Sphere. 

Cube and Sphere

Drag both objects into the prefabs folder. In this way, we will not need to make changes one by one every time we create an object. Of course, you have to make the necessary changes before you put them in the prefabs folder. 

But we need to create Materials folder also. I create two materials called Cube and Sphere in this folder. I gave blue color to the sphere, and the color of cube material is brown. Then drag these objects into the prefabs folders.

Let's start building the level.

Building Level on Unity

Now, I want to give gravity force for Sphere. There is a component to make it that is provided by Unity.

Click on the sphere and look at the inspector, and click Add Component and choose RigidBody component, after that run the project.

Our red ball collided with the block:
Collided
It's time to control the sphere, I mean "the player". So let's create a script called Player in Scripts folder. We will type C# code in this section. We need to manipulate transform values. Because transform component contains position settings, and we will change this values via our keyboard at the game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public float speed;
    public float jumpSpeed;

    void Start() 
    {
        speed = 5f;
        jumpSpeed = 10f;
    }

    // Update is called once per frame
    void Update()
    {  
        if (Input.GetKey(KeyCode.W))
            transform.position += new Vector3(-1, 0, 0) * speed * Time.deltaTime;
        if (Input.GetKey(KeyCode.S))
            transform.position += new Vector3(1, 0, 0) * speed * Time.deltaTime;
        if (Input.GetKey(KeyCode.D))
            transform.position += new Vector3(0, 0, 1) * speed * Time.deltaTime;
        if (Input.GetKey(KeyCode.A))
            transform.position += new Vector3(0, 0, -1) * speed * Time.deltaTime;

        if(Input.GetKey(KeyCode.Space))
            transform.position += new Vector3(0, 1, 0) * jumpSpeed * Time.deltaTime;
    }
}
It's not very good code, but it's not very important for a start. I also added jump movement.
Unity Object Movement
I want to follow the ball with the camera. Currently, we don't need to type a script for the camera to follow the player. We will just add this Main Camera to the player like the below:
Third Person Camera
In addition, I made 4.0f of the Y value from the position of the transform:
Camera Position in Unity
The result is like this:
Third Person Camera Movement

I want to add one key game object to the last block. When the player collides with this object, this level must end and move on to the next level. I created a game object, and also I added RigidBody component to it:
Game Object in Unity
It was created in the same way we created other game objects. 

We need to create the second level. This time we position the blocks on the stage diagonally:

Creating Another Level on Unity

We will save as Level2 this scene. We have two levels right now:
Different Levels Unity

Choose Level1, and open File > Build Settings, click Add Open Scenes. Do it same operation for Level2.
Build Settings

Let's create a new script for key. After creating the script, drag it to the key object in Level1.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Key : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    void OnTriggerEnter(Collider collided) 
    {
        if(collided.gameObject.tag == "Player")
        {
            SceneManager.LoadScene("Level2");
        }
    }

    
    void Update()
    {
        
    }
}
But this code will not work. Firstly we need give a tag to Player:
Tag Unity

Also we need to change the collider of key like this:
IsTrigger
We must uncheck Gravity and Kinematic in RigidBody of key:
Let's test it:
Result

That's cool, but there is a problem about the view of Level2, it's dark. I choose the Level2 and then go Window > Rendering > Lighting and then click New Lighting Settings, after that uncheck Auto Generate and click Generate Lighting. The problem should be solved, and I don't know why it happens, probably it's a bug of Unity.
Solved Dark Scene Issue Unity
That's enough for now. 
 
Devamını Oku »

Making Minesweeper Clone with C#

In this post, We will see how we can make a minesweeper game clone. Let's start it. First of all if you've played in a minesweeper before, You know more or less how minesweeper works. Frankly, I will try to make the game based on this. 

So what are the basic rules of the minesweeper game? First of all, if the person playing the game clicks on the box containing the mines, it is game over. If a number comes out of the box that the player pressed, which can be 1, 2, 3, 4, 5, 6, 7, 8, then it means that there are as many mines as that number around the opened box. It will also be in empty boxes. Clicking on these empty boxes will open other empty boxes around it. I think I will solve this situation with the Breadth First Search algorithm. If the player is sure that a box contains a mine, the player will mark it with a flag.

Adapting all these rules to the game may sound a bit complicated, but it is actually quite simple. I think we can understand this better by making the game. 

I created a tileset that I will use in the game. I quickly made this texture with Aseprite. You can download it if you want to use it:

tileset minesweeper

In order, there is the image representing an unopened box, opened boxes indicating the number of mines 1,2,3,4,5,6,7 and 8. representative mine image, an unopened and checked box, and and finally an empty opened box image. Instead of using them all individually, I combined them into a tileset. I will take advantage of using the TextureRect feature that SFML provides.

I added this image into the Assets folder I created in the project directory. I created a class called Game. I loaded the tileset as texture.

using System;
using SFML.Graphics;
using SFML.Window;

namespace minesweeperclone
{
    class Game
    {
        Texture texture;

        public Game()
        {
            texture = new Texture("Assets/tileset.png");
        }
    }
}
Now it's time to create the box class. There will be 400 boxes in total, and forty of them will be mines.
using System;
using System.Collections.Generic;
using SFML.Graphics;
using SFML.Window;
using SFML.System;

namespace minesweeperclone
{
    class Box
    {
        Vector2f position;
        public bool isOpened;
        public bool isMine;
        public int mineCount;
        public bool isMarked;

        // ui
        public RectangleShape uiBox;
        Texture texture;
        public int type;
        

        public Box(Vector2f position, bool isMine, Texture texture)
        {
            this.position = position;
            this.isMine = isMine;

            this.uiBox = new RectangleShape(new Vector2f(16, 16));
            this.uiBox.Texture = texture;
            this.uiBox.Position = this.position;
            this.uiBox.TextureRect = new IntRect(type,0,10,10);
        }
    }
...
The size of each box will be 16 px. I will create a hashset inside Game class. The key type of this hashtable will be Vector2f and the value type will be Box.
using System;
using SFML.Graphics;
using SFML.Window;

namespace minesweeperclone
{

    class Game
    {
    	Sprite board;
        
        Texture texture;

        Dictionary<Vector2f, Box> boxes;
        

        public Game()
        {
            texture = new Texture("Assets/tileset.png");

            boxes = new Dictionary<Vector2f, Box>();

            for (int y = 0; y < 20; y++)
            {
                for (int x = 0; x < 20; x++)
                {
                    boxes[new Vector2f(x, y)] = new Box(new Vector2f(x * 16, y * 16), false, texture);
                }
            }
        
        }
    }
}
Now we need to draw these boxes on the screen as 20x20. I'm creating a canvas in the Game class. This canvas is actually referred to as rendertexture in SFML. The size of this Rendertexture must be 320x320, because of 16*20=320:
using System;
using SFML.Graphics;
using SFML.Window;

namespace minesweeperclone
{

    class Game
    {
    	Sprite board;
        
        Texture texture;

        Dictionary<Vector2f, Box> boxes; 

        RenderTexture renderTexture = new RenderTexture(320, 320);
        
    	public Game()
        {
        	board = new Sprite(renderTexture.texture);
            ...
Again, I am creating a draw method within the Game class:
        public void Draw(RenderTarget window)
        {
            renderTexture.Clear();

            for (int y = 0; y < 20; y++)
            {
                for (int x = 0; x < 20; x++)
                {
                    renderTexture.Draw(boxes[new Vector2f(x, y)].uiBox);
                }
            }

            renderTexture.Display();

            window.Draw(board);
        }
Now it's time to test. I've created an object of the Game class and rendered it in the game loop:
using System;
using SFML.Graphics;
using SFML.Window;

namespace minesweeperclone
{
    class Program
    {
        static void Main(string[] args)
        {
            const int WIDTH = 640;
            const int HEIGHT = 480;
            const string TITLE = "Minesweeper";
            
            VideoMode mode = new VideoMode(WIDTH, HEIGHT);
            RenderWindow window = new RenderWindow(mode, TITLE);
            Game game = new Game();
            
            window.SetVerticalSyncEnabled(true);

            window.Closed += (sender, args) => window.Close();

            while (window.IsOpen)
            {
                window.DispatchEvents();
                window.Clear(Color.Blue);

                game.Draw(window);
                
                window.Display();
            }
        }
    }
}
The result I got:

I think I need to center the board. There is a very simple formula for this (parent.width / 2 - child.width/2, parent.height/2 - child.height/2). We can adapt this to the position of the board:
        public Game()
        {
            board = new Sprite(renderTexture.Texture);
            board.Position = new Vector2f(640/2 - 320/2, 480/2 - 320/2);

            ...
If we run again we will see the board is centered:
result
It's time to bring the click feature to these squares. For this, I create a field named IntRect in Box class. If the mouse coordinates are within the area defined as IntRect, it means that the square can be clicked. 
namespace minesweeperclone
{
    class Box
    {
        ...
        public IntRect rect;
        
        public Box(Vector2f position, bool isMine, Texture texture)
        {
            ...

            this.rect = new IntRect(160 + (int)this.position.X, 80 + (int)this.position.Y, 16, 16);
        }
    }
Now, we will be able to easily check if that square is clicked by this feature. I will create update method in Game class. This method will continuously check all tiles:
        public void Update(Vector2i mousePos)
        {
            foreach (var box in boxes)
            {
                if(box.Value.rect.Contains(mousePos.X, mousePos.Y))
                {
                    // change view of the clicked box
                    box.Value.type = 9;
                    box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10, 0, 10, 10);
                }
            }
        }
As you can see, it takes mousePos as a parameter. We will send these mouse coordinates in the game loop.
            while (window.IsOpen)
            {
                window.DispatchEvents();

                Vector2i mousePos = Mouse.GetPosition((Window)window);
                game.Update(mousePos);
                ...
If we run it and hover the mouse cursor over the boxes:
minesweeper over button


But we have to click on the boxes. That's why I will create two properties called LeftClick and RightClick:
    class Game
    {
        ...

        RenderTexture renderTexture = new RenderTexture(320, 320);

        public bool LeftClick {get; set;}
        public bool RightClick {get; set;}
I returned the file containing the game loop and wrote an eventhandler for them:
            ...
            window.SetVerticalSyncEnabled(true);

            window.Closed += (sender, args) => window.Close();

            window.MouseButtonPressed += (sender, args) => 
            {
                if(args.Button == Mouse.Button.Left)
                {
                    game.LeftClick = true;
                }
                if(args.Button == Mouse.Button.Right)
                {
                    game.RightClick = true;
                } 
            };

            while (window.IsOpen)
            {
            	...
We will use these values in the update method of Game class. For example, when we click on the left click, the box will open, and when we click on the right click, the clicked box will be marked with a flag:
        public void Update(Vector2i mouseCoords)
        {
            
            foreach (var box in boxes)
            {
                if(LeftClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y))
                {
                    LeftClick = false;

                    // change view of the clicked box
                    box.Value.type = 9;
                    box.Value.isOpened = true;
                    box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10,0,10,10);
                }

                // set flag
                if(RightClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y))
                {
                    RightClick = false;

                    if(!box.Value.isOpened)
                    {
                        box.Value.type = 11;
                        box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10,0,10,10);
                    }
                }
            }
        }
So, let's test it:
test of game

I think we've taken care of the interface part in general. Now it's time to settle the background of the game. I mentioned that we will place 40 mines randomly. But of course, we need to add them periodically. I mentioned that we will place 40 mines randomly. But of course, we need to add them periodically.

If I remember correctly, the probability of the first clicked box being a mine is zero, because the placing of mines works at this point. That's why I will do it according to this condition. We will place the mines at the very moment when the first click is made, that is, when the box is clicked with a left click for the first time:
        public void GenerateMines()
        {   
            int limit = 0;
            int rowLimit = 0;
            Random random = new Random();

            for (int y = 0; y < 20; y++)
            {
                rowLimit = 0;
                for (int x = 0; x < 20; x++)
                {
                    if(!boxes[new Vector2f(x, y)].isOpened && rowLimit < 4 && !boxes[new Vector2f(x, y)].isMine && limit < 80)
                    {
                        if(random.Next(0,8) >= 6)
                        {
                            rowLimit++;
                            limit++;
                            boxes[new Vector2f(x, y)].isMine = true;
                            boxes[new Vector2f(x, y)].type = 10;
                        }
                    }
                }
            }
        }
It may not be a very good generator, but I think it is enough. As I said this generator will only run once. That's why I created a property called NotFirst in Game class.
public bool NotFirst {get; set;}
And inside the update method I used it like this:
        public void Update(Vector2i mouseCoords)
        {
            
            foreach (var box in boxes)
            {
                if(LeftClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y))
                {
                    LeftClick = false;

                    // change view of the clicked box
                    box.Value.type = 9;
                    box.Value.isOpened = true;
                    box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10,0,10,10);

                    if(!NotFirst)
                    {
                        GenerateMines();
                        NotFirst = true;
                    }
                }

                // set flag
                if(RightClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y))
                {
                    RightClick = false;

                    if(!box.Value.isOpened)
                    {
                        box.Value.type = 11;
                        box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10,0,10,10);
                    }
                }
            }
        }
Now it's time to place the numbers according to the mines placed. We will do this first too.
        public void CalculateNumbers()
        {
            Vector2f[] offset = 
            {
                new Vector2f(-1, -1),
                new Vector2f(0, -1),
                new Vector2f(1, -1),
                new Vector2f(-1, 0),
                new Vector2f(1, 0),
                new Vector2f(-1, 1),
                new Vector2f(0, 1),
                new Vector2f(1, 1)
            };
            int mineCounter = 0;

            for (int y = 0; y < 20; y++)
            {
                for (int x = 0; x < 20; x++)
                {
                    mineCounter = 0;

                    if(!boxes[new Vector2f(x, y)].isMine)
                    {
                        int i = 0;
                        while(i < 8)
                        {
                            if(boxes.ContainsKey(new Vector2f(x + offset[i].X, y + offset[i].Y)))
                            {
                                if(boxes[new Vector2f(x + offset[i].X, y + offset[i].Y)].isMine)
                                {
                                    mineCounter++;
                                }
                            }
                            i++;
                        }
                        if(mineCounter == 0)
                        {
                            boxes[new Vector2f(x, y)].type = 9;
                        }
                        else
                        {
                            boxes[new Vector2f(x, y)].type = mineCounter;
                        }
                    }
                }
            }
        }
Neighbors of each block will be checked. Neighbors of each block will be checked. If it is a mine, it will be added to mineCounter. If the above code is examined carefully, it can be easily understood what we are doing. This time, we are making the following change in our update method:
        public void Update(Vector2i mouseCoords)
        {
            
            foreach (var box in boxes)
            {
                if(LeftClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y))
                {
                    LeftClick = false;

                    // change view of the clicked box
                    box.Value.type = 9;
                    box.Value.isOpened = true;
                    

                    if(!NotFirst)
                    {
                        GenerateMines();
                        CalculateNumbers();
                        NotFirst = true;
                    }
                    box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10,0,10,10);
                }

                // set flag
                if(RightClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y))
                {
                    RightClick = false;

                    if(!box.Value.isOpened)
                    {
                        box.Value.type = 11;
                        box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10,0,10,10);
                    }
                }
            }
        }
Our game is very close to the end. There are two important things that come to my mind. One of them is to be game over when clicking on a mine. First, I created a function called ShowAllMines. If the player clicks on the mine, all mines will be visible. I also defined a property named EnabledClick. This property is set to true in the constructor function. If the player clicks on the mine, the game will be over and the player will not be able to press the boxes again:
        ...
        public void ShowAllMines()
        {
            foreach (var box in boxes)
            {
                if(box.Value.isMine)
                {
                    box.Value.uiBox.TextureRect = new IntRect(10 * 10, 0, 10, 10);
                }
            }
        }

        public void Update(Vector2i mouseCoords)
        {
            if(EnabledClick)
            {
                foreach (var box in boxes)
                {
                    if(LeftClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y))
                    {
                        LeftClick = false;

                        // change view of the clicked box
                        box.Value.isOpened = true;

                        // if it is mine then show game over text on the screen.
                        if(box.Value.isMine)
                        {
                            // game over
                            ShowAllMines();
                            EnabledClick = false;
                        }
                        ...
Let's test it:
Mines Explode

Normally, when clicking on the empty parts, all the empty cells adjacent to it and the numbers adjacent to them should be visible. But I haven't added that feature here yet, it still works logically.
I will use the BFS algorithm so that I can do this:
        private void showOthers(Vector2i tilePos)
        {
            if(tilePos.X >= 0 && tilePos.X < 20 && tilePos.Y >= 0 && tilePos.Y < 20)
            {
                
                Queue<Vector2i> queue = new Queue<Vector2i>();
                
                Dictionary<Vector2i, bool> registry = new Dictionary<Vector2i, bool>();

                queue.Enqueue(tilePos);
                
                registry[new Vector2i(tilePos.X, tilePos.Y)] = true;

                List<int[]> dirs = new List<int[]>()
                {
                    new int[] {1, 0},
                    new int[] {-1, 0},
                    new int[] {0, 1},
                    new int[] {0, -1},
                    new int[] {1,1},
                    new int[] {1,-1},
                    new int[] {-1,1},
                    new int[] {-1,-1}
                };
                
                while(queue.Count != 0)
                {
                    Vector2i currentTile = queue.Dequeue();

                    foreach (var dir in dirs)
                    {
                        int xx = currentTile.X + dir[0];
                        int yy = currentTile.Y + dir[1];

                        if(registry.ContainsKey(new Vector2i(xx, yy)))
                        {
                            continue;
                        }
                        if(xx >= 0 && xx < 20 && yy >= 0 && yy < 20)
                        {
                            if(
                                !boxes[new Vector2f(xx, yy)].isOpened && boxes[new Vector2f(xx, yy)].type >= 1 && 
                                boxes[new Vector2f(xx, yy)].type < 10 && !boxes[new Vector2f(xx, yy)].isMine
                            )
                            {
                                
                                boxes[new Vector2f(xx, yy)].uiBox.TextureRect = new IntRect(boxes[new Vector2f(xx, yy)].type * 10, 0, 10, 10);
                                boxes[new Vector2f(xx, yy)].isOpened = true;
                                registry[new Vector2i(xx, yy)] = true;
                            }
                        }
                        if(xx < 0 || xx >= 20 || yy < 0 || yy >= 20)
                        {
                            continue;
                        }
                        
                        registry[new Vector2i(xx, yy)] = true;

                        if(boxes[new Vector2f(xx, yy)].type == 9)
                            queue.Enqueue(new Vector2i(xx, yy));
                    }
                }
            }
        }
It may seem a little scary, but this is how the BFS algorithm generally works. It controls all cells from the center outward. Here's how we used this function in the update function:
        public void Update(Vector2i mouseCoords)
        {
            if(EnabledClick)
            {
                foreach (var box in boxes)
                {
                    if(LeftClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y))
                    {
                        ...

                        if(!NotFirst)
                        {
                            GenerateMines();
                            CalculateNumbers();
                            NotFirst = true;
                        }
                        box.Value.isOpened = true;
                        box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10,0,10,10);
                        
                        if(box.Value.type == 9)
                        {
                            showOthers(new Vector2i((int)box.Value.position.X/16, (int)box.Value.position.Y/16));
                        }
                    }

                    ...
                }
            }
        }
Let's run it our game:
Minesweeper Clone
That's great. If we flag all the mines and open the remaining boxes, we will win this game. We have the right to use a total of 80 flags. If all mines are flagged then we have won the game. Each time a flag is used, the game will check that all flags match all mines.

        public bool CheckMines()
        {
            foreach (var box in boxes)
            {
                if(box.Value.isMine && !box.Value.isMarked)
                {
                    return false;
                }
            }
            return true;
        }
Let's use it in update method:
                    // set flag
                    if(RightClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y))
                    {
                        RightClick = false;

                        if(!box.Value.isOpened)
                        {
                            box.Value.type = 11;
                            box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10,0,10,10);
                        }

                        if(NotFirst && CheckMines())
                        {
                            Console.WriteLine("You won!");
                            EnabledClick = false;
                        }
                    }
                }
            }
        }
Well, it works well. But there are some problems:
  • The amount of flags must be limited, otherwise the game can be won by placing a flag in each box.
  • If we set the flag by mistake, we cannot remove it again. We need to fix this.
The flag must be equal to the number of mines in the game. There will be 80 flags in total.But there may not be 80 mines in the game, there will usually be less than 80 mines(max 80).

Since I was writing the program at the same time as I was writing the article, it was a little annoying to discover some problems.

        public void Update(Vector2i mouseCoords)
        {
            if(EnabledClick)
            {
                foreach (var box in boxes)
                {
                    if(LeftClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y) && !box.Value.isOpened)
                    {
                        LeftClick = false;

                        // change view of the clicked box
                        box.Value.isOpened = true;

                        // if it is mine then show game over text on the screen.
                        if(box.Value.isMine)
                        {
                            ShowAllMines();
                            Console.WriteLine("Game over!");
                            box.Value.type = 10;
                            EnabledClick = false;
                        }

                        if(!NotFirst)
                        {
                            GenerateMines();
                            CalculateNumbers();
                            NotFirst = true;
                        }
                        box.Value.uiBox.TextureRect = new IntRect(box.Value.type * 10,0,10,10);
                        
                        if(box.Value.type == 9)
                        {
                            showOthers(new Vector2i((int)box.Value.position.X/16, (int)box.Value.position.Y/16));
                        }
                    }

                    // set flag
                    if(RightClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y) && flagCounter < LIMIT_FLAGS)
                    {
                        RightClick = false;

                        if(!box.Value.isMarked && !box.Value.isOpened)
                        {
                            box.Value.isMarked = true;
                            box.Value.uiBox.TextureRect = new IntRect(11 * 10,0,10,10);
                            flagCounter++;
                        }
                        else if(box.Value.isMarked && !box.Value.isOpened)
                        {
                            box.Value.isMarked = false;
                            box.Value.uiBox.TextureRect = new IntRect(0 * 10,0,10,10);
                            flagCounter--;
                        }


                        if(NotFirst && CheckMines())
                        {
                            Console.WriteLine("You won!");
                            EnabledClick = false;
                        }
                    }
                }
            }
        }
I have defined two variables. These are the LIMIT_FLAGS and flagCounter variables. You can see how they are used in the code above. Now let's move on to the finish. So I will design two banners for "You Won!" and "Game Over!". I will use Aseprite again for this. You can download this images:
 
Images for game
Images for game

I added these images in Assets folder. Let's use them. I will display these images when we win or lose the game. I will create two sprite objects and send each image as texture to these objects. Then I will position it at the required point. If the game is won or lost, it will be displayed on the screen accordingly. 
    class Game
    {
        ...
        
        Sprite gameOver;
        Sprite youWon;

        bool isWon;
        bool isLose;
After that, I updated the update method like this:
        public void Update(Vector2i mouseCoords)
        {
            if(EnabledClick)
            {
                foreach (var box in boxes)
                {
                    if(LeftClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y) && !box.Value.isOpened)
                    {
                        ...

                        // if it is mine then show game over text on the screen.
                        if(box.Value.isMine)
                        {
                            ShowAllMines();
                            isLose = true;
                            box.Value.type = 10;
                            EnabledClick = false;
                        }

                        ...
                    }

                    // set flag
                    if(RightClick && box.Value.rect.Contains((int)mouseCoords.X, (int)mouseCoords.Y) && flagCounter < LIMIT_FLAGS)
                    {
                        ...


                        if(NotFirst && CheckMines())
                        {
                            isWon = true;
                            EnabledClick = false;
                        }
                    }
                }
            }
        }
And finally, in the draw method:
        public void Draw(RenderTarget window)
        {
            renderTexture.Clear();

            ...

            if(isLose)
            {
                renderTexture.Draw(gameOver);
            }
            if(isWon)
            {
                renderTexture.Draw(youWon);
            }

            renderTexture.Display();
            
            window.Draw(board);
        }
    }
Let's run it again:
Final Version of minesweeper

Finally 💨.

Devamını Oku »

Polishing UI of the Inventory for Game

There should be an image representing each item, and we will provide this again using a hash table structure. But unlike the others, it will be predefined. The key's data type is uint and the value is a texture. Let's do it:

    class Inventory
    {
        ...
        Dictionary<uint, Texture> itemImages;
        ...

        public Inventory()
        {
            ...

            itemImages = new Dictionary<uint, Texture>();
            
            itemImages[4] = TextureManager.getTexture("quad");
            itemImages[6] = TextureManager.getTexture("star");
            ...
Now what I need to do is to update the showItems method accordingly.
        public void showItem()
        {
            // clear the inventory panel
            PanelTexture.Clear(new Color(194, 138, 85));

            ...

            foreach (var item in items)
            {   
                Sprite icon = new Sprite(itemImages[item.Key]);
                icon.Position = new Vector2f(10, lineHeight * line + lineHeight);
                icon.Scale = new Vector2f(0.7f, 0.7f);
                
                ...

                PanelTexture.Draw(icon);
                PanelTexture.Draw(Text);
                line++;
            }
            
            PanelTexture.Display();
            Panel.Texture = PanelTexture.Texture;
        }
Of course, we could have provided a much more efficient use here. But I'm moving a little fast. Let's look at the result:
Items with the icon in the inventory

That's okay. But now we have to improve the view of the panel. It looks so basic and irregular. Of course, it is not possible for me to achieve a responsive design. That process will take a lot of work. In my opinion, GUI can be one of the most difficult stages in game development. But, we must like difficult things if we are dealing with game programming.

I increased the panel width a little bit. Instead of showing the item id, I want to show the item name linked to that item id. So I defined a new hash table called itemNames(Dictionary<uint, string> itemNames; ). Initially, I determined the name of the items based on the item id.  I updated the showItems method as follows:
        public void showItem()
        {
            // clear the inventory panel
            PanelTexture.Clear(new Color(194, 138, 85));

            ...

            foreach (var item in items)
            {   
                ...
                
                Text.DisplayedString = itemNames[item.Key]+"\t "+itemsQuantity[item.Key];
                Text.Position = new Vector2f(42, lineHeight * line + lineHeight);
                Text.FillColor = Color.Blue;

                ...
            }
            
            PanelTexture.Display();
            Panel.Texture = PanelTexture.Texture;
        }
After this process, item name will be written instead of item id. Now, I want to make some changes in the background of the inventory. I will not add this changes in below, because it's really easy. I just added an outline and I changed alpha value for transparent:
Polished UI Inventory
That's all for now from this post. I hope it helped.We could make a lot things. I could also use a texture as background of the panel. 

Devamını Oku »

Making User Interface for Inventory System

We made a basic version of inventory system in the previous post. In this post, I want to create an user interface for inventory. When the player pressed E key, it will be appear ui of the inventory. The ui of the inventory wont be grid version for now. I will do it in the next posts.

I will create a rectangle in the inventory class. This rectangle will represent the inventory panel:

    class Inventory
    {
        ...
        
        // ui properties
        public bool ShowPanel { get; set; }
        RectangleShape Panel { get; set; }
        RenderTexture PanelTexture { get; set; }

        public Inventory()
        {
            ...

            Panel = new RectangleShape(new Vector2f(200, 250));
            PanelTexture = new RenderTexture(200, 250);
            PanelTexture.Clear(new Color(194, 138, 85));
            PanelTexture.Display();
            // centered on the screen
            Panel.Texture = PanelTexture.Texture;
            Panel.Position = new Vector2f(320 - 200/2, 240 - 250/2);
        }
Also I create ShowPanel property to manage inventory visibility. If the player of the game pressed E key the boolean value will be changed as opposite. We need to draw this panel, therefore I added a new method called draw and if ShowPanel is true then the Panel will be drew on the screen.
        public void Draw(RenderTarget target)
        {
            if(ShowPanel)
            {
                target.Draw(Panel);
            }
        }
I want to see this panel on the screen. So, I will execute this draw method in the draw method of the player class:
        public override void draw(RenderTarget target)
        {
            target.Draw(entity);
            inventory.Draw(target);
        }
After that, I have to add an delegate for key event to handling key from the user in the Progam.cs:
            window.KeyPressed += (sender, args) => 
            {
                if (args.Code == Keyboard.Key.E)
                {
                    player.Inventory.ShowPanel = !player.Inventory.ShowPanel;
                }
            };
Let's look at the result:
UI Inventory for the game
Now it's time to list the items in this inventory on this panel. Firstly I need a font to type some text. You can use what you want font this is optional. I created a folder named fonts in the project folder, I put the font file in this folder.
        Font Font { get; set; }
        Text Text { get; set; }

        public Inventory()
        {
            ...

            Font = new Font("Fonts/FreeMono.ttf");
            Text = new Text();
        }
Normally, we did list items in the console. Now we will do it the samething on the panel rectangle:
        Font Font { get; set; }
        Text Text { get; set; }

        public Inventory()
        {
            ...

            Font = new Font("Fonts/FreeMono.ttf");
            Text = new Text();
        }
I updated the showItem method:
        public void showItem()
        {
            // clear the inventory panel
            PanelTexture.Clear(new Color(194, 138, 85));

            Text = new Text("Item ID\tQuantity", Font, 15);
            Text.Position = new Vector2f(10, 5);
            int lineHeight = 25;
            int line = 0;
            Text.FillColor = Color.Black;
            Text.Style = Text.Styles.Bold;
            PanelTexture.Draw(Text);

            foreach (var item in items)
            {   
                Text.DisplayedString = item.Key+"\t\t\t"+itemsQuantity[item.Key];
                Text.Position = new Vector2f(10, lineHeight * line + lineHeight);
                Text.FillColor = Color.Blue;
                PanelTexture.Draw(Text);
                line++;
            }
            
            PanelTexture.Display();
            Panel.Texture = PanelTexture.Texture;
        }
I aware that looks awful at first, We don't have choice if we want to use custom UI for the game. Maybe I can minimize this usage in the next posts. Now, let's look at the result again:
Making User Interface for Game
That's cool. We can polish this panel with more ways. I will try to give a better view of the inventory in the next post. 

Devamını Oku »

How to make an Inventory System for RPG Game?

I stucked with inventory system in my game. I decided to create a new prototype of inventory. I'm not using any game engines and I will make this with a game library called SFML. 

Let's think about inventory system and we have define all requirements about it. 
inventory system for game

In the above image, I have two types items and a player which I will controll. Let every object in the game have a certain id. For example, the purple star item's id is 8, and the id of orange quads is 6. Also there is a player, but it's not important at this point because it's not an item.

We need to create an inventory system. This inventory system will be specific to the player. We already saw this system in the most of the games. The inventory that I will create, will contain these limitations:
  • An inventory can contain up to 9 different items.
  • Items will have one of two states, quantity and non-quantity.
  • If the item is quantity, then a maximum of 8 identical items can be stored in a single slot.
  • If the item is non-quantity then there can be at most one of that item in a single slot.
According to these informations, The inventory should look like in the below:
inventory
After this information, I think we can represent the inventory with a class structure. Well, We don't have many options anyway. Let's create this class using C#:
    class Inventory
    {
        Dictionary<uint, Entity> items;
        Dictionary<uint, int> itemsQuantity;
        uint length = 0; // 9 slot and max 8 items for each one if it is quantity

        public Inventory()
        {
            items = new Dictionary<uint, Entity>();
            itemsQuantity = new Dictionary<uint, int>();
        }
        ...
I will hold the ID of items and their quantity if there is, in the dictionarys or we can call them as hash tables. The important things is here about data. We can just store the data how many we have items in the inventory. So, we can use the chosen entity as much as we have. But we haven't gotten there yet. Our priority is to collect items at first. In our game, there will be items around the player we will use. In this case, if the player collides with these items, they must be added to their inventory. We will do this operation in the method of player class. But first we need to create a method called addItem in the inventory class:
        public void addItem(Entity entity)
        {
            if(length < 9)
            {
                length++;

                // if the same item is already added then increase the quantity of item 
                if(items.ContainsKey(entity.ID) && entity.Quantity)
                {
                    itemsQuantity[entity.ID] += 1;
                }
                // if item does not exist in inventory, add this item to the dictionary of items.
                else if(!items.ContainsKey(entity.ID))
                {
                    items[entity.ID] = entity;
                    itemsQuantity[entity.ID] = 1;
                }                
            }
            showItem();
        }

        public void showItem()
        {
            Console.Clear();
            foreach (var item in items)
            {
                // show item id and the quantity of item
                Console.WriteLine(item.Key + " : " + itemsQuantity[item.Key]);
            }
        }
        ...
Also, I added a method called showItem to track the inventory. Now, we can create an inventory for the player. I will define a field in the player class:
    class Player: Entity
    {
        Inventory inventory;

        Vector2f position;
        const float PLAYER_SPEED = 4f;

        public Player(Vector2f pos, Texture texture, uint id, bool quantity = false)
            :base(pos, texture, id, quantity)
        {
            inventory = new Inventory();
            position = pos;
        }
Now, we came the important part of adding item to the inventory; colliding part:
        public override void update(List<Entity> entities)
        {
            move();
            entity.Position = position;

            foreach (var item in entities)
            {
                if(this.entity.GetGlobalBounds().Intersects(item.entity.GetGlobalBounds()))
                {
                    if(item != this){
                        inventory.addItem(item);
                        item.Destroyed = true;
                    }
                }
            } 
        }
if you noticed, Destroyed is assigned as true. The reason for doing this is to delete the object from the game environment. In fact, what we are doing here is, in a sense, converting the object into data. If the entities destroyed value is true, these entities will be removed from the list of entities. We will do it this operation in the game loop.
            while (window.IsOpen)
            {
                window.DispatchEvents();

                // update entities
                for (int i = 0; i < entities.Count; i++)
                {
                    entities[i].update(entities);
                }

                // delete entity if it is destroyed
                for (int i = 0; i < entities.Count; i++)
                {
                    if(entities[i].Destroyed)
                    {
                        entities.Remove(entities[i]);
                    }
                }

				// draw entities
                window.Clear(new Color(150, 150, 150));
                
                ...
                
                window.Display();
            }
Now, let's look at the result, and also we will see output in console:
Inventory System
We have a long way to go. I want to show the invontery panel in the window with the icon of items. I want the inventory panel on the screen when I press the E key. However, this task is really complex and it needs too progress, and this is the first part of the making an inventory system.
Devamını Oku »

How to use VS Code for Godot?

Normally, we can use Godot script panel but most game developers prefer using a seperate editor for development. The most used of this is Visual Studio Code. 

I used mono version of Godot but, probably it doesn't matter which you have. 
  • Click Editor from topbar.
  • Select Editor Settings...
  • Go inside Mono from left panel and Click Editor.
  • Select your external editor as VS Code like in the below and click the Close button.
vs code in godot


Devamını Oku »

Using Transform in SFML

I want to explain how to use transform in SFML.NET.  I think, this is a good way to manipulate the sprites invidually. Also, this way solved my problem in my prototype. Firstly, I created a new project. I should save a template, because no need to setup sfml project again and again. You can find tutorial about creating SFML tutorial from this link.

I just created a texture as an example like this:


I want to draw it on the screen at first:

using System;
using SFML.Graphics;
using SFML.Window;

namespace transformUsage
{
    class Program
    {
        ...

        static void Main(string[] args)
        {
            ...
            Sprite entity = new Sprite(new Texture("textures/texture.png"));
            entity.Position = new SFML.System.Vector2f(0,0);

            ...

            while (window.IsOpen)
            {
                ...
                entity.Draw(window, new RenderStates(Transform.Identity));
                window.Display();
            }
        }
        
    }
}
Now, I want to rotate this from specific points of the entity. For this, I will create a new object as RenderStates. I will pass entity.Transform as argument, and then called this Rotate method from transform of our renderstate. We can define angle at first argument. According to this angle the object will be rotated. The other two arguments defined origin of the rotation:

namespace transformUsage
{
    class Program
    {
        ...

        static void Main(string[] args)
        {
            ...
            Sprite entity = new Sprite(new Texture("textures/texture.png"));
            entity.Position = new SFML.System.Vector2f(0,0);

            RenderStates rs = new RenderStates(entity.Transform);
            rs.Transform.Rotate(45, 0, 0);
            ...

            while (window.IsOpen)
            {
                ...
                entity.Draw(window, rs);
                window.Display();
            }
        }
        
    }
}
Let's look at the result:
using transform in sfml

This transform is actuall a matrix. You probably studied this subject from math in high school. There are a lot of sources about usage of matrix for transformation, one of these sources is OpenGL - Transformations.

My texture of sprite's dimension is 128x128. I can use it for define origin of the rotation. But, firstly, I want to centered my sprite on the screen.After that, Let's define the origin of the sprite for rotation:
        static void Main(string[] args)
        {
            RenderWindow window = new RenderWindow(new VideoMode(WIDTH, HEIGHT), TITLE);
            Sprite entity = new Sprite(new Texture("textures/texture.png"));
            entity.Origin = new SFML.System.Vector2f(64,64);
            entity.Position = new SFML.System.Vector2f(WIDTH/2, HEIGHT/2);

            RenderStates rs = new RenderStates(Transform.Identity);
            int rotation = 0;
            window.SetFramerateLimit(60);

            window.Closed += (sender, args) => window.Close();


            while (window.IsOpen)
            {
                window.DispatchEvents();

                rotation = 1;
                rs.Transform.Rotate(rotation, entity.Position);

                window.Clear(Color.Black);
                entity.Draw(window, rs);
                window.Display();
            }
        }
I changed entity.Transform as Transform.Identity, because that causes false positioning on the screen. I think we should use Transform.Identity at first all the time. This Identity represents of unit matris. Let's look at the result:
Now, we can rotate this entity according to the origin of the entity. Also, we can use scale and translate methods for this sprite like we did with rotate method.
Devamını Oku »

Displaying The Score in SFML / C#

In the following post, we'll learn how to display text on the screen. 

I found a font to use in the game. This font's name is FreeMono. You can find it on Internet, also you can get it from the GitHub repository of this game. I added this font in bin/Assets/Fonts folder. 

Displaying text on the screen is a very simple operation in SFML. Primarily, we need a class to manage texts on the game. So, I created TextManager class. In this class, I declared the path of the font we will use. After that, I defined a font field to create an object in loadFont of TextManager. And finally, we defined the list called texts to store texts and this provides easy management in this way. 

Let's write TextManager class. 

using System;
using System.Collections.Generic;
using SFML.Graphics;
using SFML.Window;
using SFML.System;

namespace shmup
{
    class TextManager
    {
        private const string FONT_PATH = "bin/Assets/Fonts/";
        public static Font font;

        private static List<Text> texts = new List<Text>();

        public static void loadFont(string fontFamily) 
        {
            font = new Font(FONT_PATH + fontFamily + ".ttf"); 
        }

        public static void typeText(string text, int value, uint fontSize, Color fontColor, Vector2f position)
        {
            Text textContent = new Text(text + value.ToString(), font, fontSize);
            textContent.Position = position;
            textContent.FillColor = fontColor;
            texts.Add(textContent);
        }

        public static void draw(RenderTarget window) 
        {
            for (int i = 0; i < texts.Count; i++)
            {
                window.Draw(texts[i]);
                texts.Remove(texts[i]);
            }
        }

    }
}
Now, let's use this class in our game. But first I need to display the score of the player. Therefore, I declared a field called score as integer and also property to use it:
namespace shmup {
    class Player 
    {
        ...
        private int score = 0;

        ...
        public int Score { get { return score; } set { score = value; } }
Now, the score of the player will be increased by 10 when the bullets of the player collided the invader. I'm going to do in the EnemyManager class:
        public bool collisionOfBullets(Enemy enemy, Player player)
        {
            ...

            for (int i = 0; i <yer.bullets.Count; i++)
            {
                if (enemy.EnemySprite.GetGlobalBounds().Intersects(player.bullets[i].RectangleBullet.GetGlobalBounds())) 
                {
                    player.Score += 10;
                    player.bullets.Remove(player.bullets[i]);
                    return true;
                }
            } 
            return false;
        }
We should load the font of TextManager in the Game class:
        public Game()
        {
            ...

            TextureManager.LoadTexture();
            TextManager.loadFont("FreeMono");
I created a new method called updateScore for displaying text on the screen. If you notice we call the score of the player to put on the text object:
        private void updateScore()
        {
            TextManager.typeText("Score: ", player.Score, 25, Color.White, new Vector2f(10f, 10f));
        }
Let's use this method in the update method of the Game class:
        private void update() 
        {
            this.updateScore();
            this.player.update();
            this.enemies.update(this.player);
            AnimationManager.update();
        }
Finally, we can draw these texts with TextManager on the screen:
        private void draw() 
        {
            this.window.Clear(Color.Blue);
            
            this.window.Draw(this.background); 
            this.player.draw(this.window);
            this.enemies.draw(this.window);
            AnimationManager.draw(this.window);
            TextManager.draw(this.window);

            this.window.Display();
        }
Let's check out the game:
The score of the player in space invaders


Devamını Oku »

Explosion Animation in SFML / C#

In this post, we are going to implement of animation system to create an explosion effect after shooting invaders.

The first thing I do, I drew a sprite sheet for the explosion effect. That looks cool for an amateur. But the effect should be fast because I can't say they look well for a single frame. This is the sprite sheet that we will use:

Explosion Sprite Sheet
However, I can't use them like that. I first separate them into 8 frames. I don't want to build a system for the sprite sheet, the tutorials should be basic. I'm using Aseprite, therefore it didn't hart to separate them. I just click File > Save as and save it explosion-000.png and the program saved the rest of the parts as numbered for me, like this:
the view of vs code
Now, let's load them with TextureManager. Firstly, I define the path of explosions folder and then I created the list called explosionTeztures as a field. Now we have to reach this list to use them necessary situation. To do this, I declare property to get the list.
        private static string ASSETS_PATH = "bin/Assets/Textures/";
        private static string EXPLOSION_ASSETS_PATH = "bin/Assets/Textures/explosions/";
		
        ...
        static List<Texture> explosionTextures = new List<Texture>(); 
		
        ...
        public static List<Texture> ExplosionTextures { get { return explosionTextures; } }
Let's load the frames of the explosion to the list using for loop in LoadTexture method:
        public static void LoadTexture()
        {
            playerTexture = new Texture(ASSETS_PATH + "player.png");
            enemyTexture = new Texture(ASSETS_PATH + "enemy.png");
            backgroundTexture = new Texture(ASSETS_PATH + "background.png");
            
            for (int i = 0; i < 8 ; i++)
            {
                explosionTextures.Add(new Texture(EXPLOSION_ASSETS_PATH + "explosion-00" + i.ToString() +".png"));
            }
        }
That's it for loading texture operation. Let's create an animation for our game. The logic of the game is simple. If bullets collide enemy object. The animation should appear at the position of the enemy sprite. An animation object will be created at the time of the collision. So, let's write Animation class:
namespace shmup
{
    class Animation
    {
        private Sprite sprite;
        private int count = 0;

        private bool destroyIt = false;

        private List<Texture> textures = new List<Texture>();

        public bool DestroyIt { get { return destroyIt; }}

        public Animation(List<Texture> textures, Vector2f position)
        {
            this.sprite = new Sprite();
            this.sprite.Position = position;
            this.sprite.Texture = textures[0];
            this.textures = textures;
        }

        public void update()
        {
            if (this.count == this.textures.Count - 1) 
            {
                this.destroyIt = true;
            }
            else 
            {
                this.count += 1;
                this.sprite.Texture = this.textures[count];
            }
        }

        public void draw(RenderTarget window)
        {
            window.Draw(this.sprite);
        }
    }
}
Let's examine the code above. We have to count each frame of the animation. Because this animation should be ended at the last frame. The frames are stored by the list called textures. If the value of the count matches the size of the list then destroyIt field should be set as true, but if not match then it will change the texture of sprite in order and the animation effect will be provided this way. In the constructor method, the sprite will be created according to the position of the enemy that exploded and the first texture frame was assigned to the texture of the sprite. Also, the textures from the parameter are stored in the field of textures. I already said the update method what it does and the draw method draw the sprite on the screen.

Animation class means nothing by itself. We need to manage them with a class. Because we designed to Animation class according to usage. We need to update all of these animation objects at one point:
namespace shmup
{
    class AnimationManager
    {
        private static List<Animation> animations = new List<Animation>();

        public static List<Animation> Animations { get { return animations; } }

        public static void update()
        {
            for (int i = 0; i < animations.Count; i++)
            {
                if (animations[i].DestroyIt) 
                {
                    animations.Remove(animations[i]);
                }
                else
                {
                    animations[i].update();
                }
            }
        }

        public static void draw(RenderTarget window) 
        {
            for (int i = 0; i < animations.Count; i++)
            {
                animations[i].draw(window);
            }
        }
    }
}
This is a classic way for any manager class. It works as static so we don't create objects from this class. The important thing is in the update method. If animation should be ended then it will remove from the list.

Now let's use this manager in EnemyManager when the invader died:
        public void update(Player player) 
        {
            ...
            
            for (int i = 0; i < enemies.Count; i++)
            {
                enemies[i].update();
                if (enemies[i].Position.Y > 480 || this.collisionOfBullets(enemies[i], player)) 
                {
                    AnimationManager.Animations.Add(new Animation(TextureManager.ExplosionTextures, enemies[i].Position));
                    enemies.Remove(enemies[i]);
                }
            }
        }
In the code above, an animation will be added if the necessary conditions occur. Finally, we can use it this AnimationManager in the Game class:
        private void update() 
        {
            this.player.update();
            this.enemies.update(this.player);
            AnimationManager.update();
        }

        private void draw() 
        {
            this.window.Clear(Color.Blue);
            
            this.window.Draw(this.background); 
            this.player.draw(this.window);
            this.enemies.draw(this.window);
            AnimationManager.draw(this.window);

            this.window.Display();
        }
    }
}
Let's look at the result:
Explosion Effect with SFML / C#


Devamını Oku »