Jump to content
  • Advertisement

C# 101. Snake. WinForms, OpenGL 3.1

8Observer8

858 views

Step-by-step instruction of Snake 2D using C#, WinForms, GDI+

We will place OpenTK.GLControl on the Form to draw graphics with modern shader OpenGL 3.1.

This is a gif animation of the final result of our work:

Snake_WinFormsOpenGL31CSharp_MovingSnake.gif.07c0907cb82e4261867eb64089042459.gif

Note. I take ideas from this tutorial: Python Snake Game

Please, download this empty project: Snake_WinFormsOpenGL31CSharp.zip. It includes OpenTK.dll and OpenTK.GLControl.dll

Or if you know how to add libraries from References and how to add Control to Toolbox you can download these two DLL's: OpenTK.zip and OpenTK.GLControl.zip You can search in the Internet how to do it.

Current Form1.css file:

using System;
using System.Windows.Forms;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics;

namespace Snake
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            // Centers the form on the current screen
            CenterToScreen();
        }

        private void glControl_Load(object sender, EventArgs e)
        {
            glControl.MakeCurrent();

            // Set a color for clear the glControl
            GL.ClearColor(Color4.Black);
        }

        private void glControl_Paint(object sender, PaintEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit);

            Draw();

            glControl.SwapBuffers();
        }

        private void Draw()
        {
            // Draw game entities here
        }
    }
}

These commands just clear a render canvas (glControl) with a black color. RGB values (0f, 0f, 0f) mean the black color. If you write (1f, 0f, 0f) - it will be a red color or if you write (1f, 1f, 1f) - it will be a white color. You can choose your own value of normalized color using this color calculator.

Snake_WinFormsOpenGL31CSharp_glControl.png.650dacbbf4efdce8906e15f4919de9c9.png

I set "FormBorderStyle" to "FixedDialog". You cannot change size of the window with the mouse. And I set "MaximizeBox" to "False". You cannot maximize the window by click on "Maximize" button on the window.

Let's draw a square. We need to write a pair of shaders: a vertex shader and a fragment shader. This pair will be associated with a shader program object that we will create too. The pair of shaders and the shader program will placed in a video card memory. We will create an array of vertices of our square and vertex buffer object (VBO) on the video card memory. We will move the array of coordinates of vertices to VBO. The vertex shader will be executed for every vertex when we will call the function: drawArrays(). The vertex shader just set coordinates for vertices. The fragment shader will be executed for every pixel of the square and set a color for every pixel.

This book is one of the best for start: WebGL Programming Guide

Snake_WinFormsOpenGL31CSharp_Square.png.638850a88980235b8624330fab02f6ab.png

using System;
using System.Windows.Forms;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics;

namespace Snake
{
    public partial class Form1 : Form
    {
        private int _shaderProgram;
        private int _uColorLocation;

        public Form1()
        {
            InitializeComponent();

            // Centers the form on the current screen
            CenterToScreen();
        }

        private void glControl_Load(object sender, EventArgs e)
        {
            glControl.MakeCurrent();

            // Set a color for clear the glControl
            GL.ClearColor(Color4.Black);

            // Initialize shaders and get a shader program
            _shaderProgram = InitShadersAndGetProgram();
            if (_shaderProgram < 0) return;

            // Initialize vertex buffers
            InitVertexBuffers();

            _uColorLocation = GL.GetUniformLocation(_shaderProgram, "uColor");
            if (_uColorLocation < 0)
            {
                MessageBox.Show("Failed to get uColorLocation variable");
                return;
            }

            // Set a triangle color
            GL.Uniform3(_uColorLocation, 0.1f, 0.8f, 0.3f);
        }

        private void glControl_Paint(object sender, PaintEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit);

            Draw();

            glControl.SwapBuffers();
        }

        private void Draw()
        {
            // Draw game entities here
            DrawSquare(0, 0, Color4.LightCoral, 10);
        }

        private void DrawSquare(int x, int y, Color4 color, int size)
        {
            // Set color to fragment shader
            GL.Uniform3(_uColorLocation, color.R, color.G, color.B);
            // Draw Rectangle
            GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
        }

        private int InitShadersAndGetProgram()
        {
            string vertexShaderSource =
                "#version 140\n" +
                "in vec2 aPosition;" +
                "void main()" +
                "{" +
                "    gl_Position = vec4(aPosition, 1.0, 1.0);" +
                "}";

            string fragmentShaderSource =
                "#version 140\n" +
                "out vec4 fragColor;" +
                "uniform vec3 uColor;" +
                "void main()" +
                "{" +
                "    fragColor = vec4(uColor, 1.0);" +
                "}";

            // Vertex Shader
            int vShader = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vShader, vertexShaderSource);
            GL.CompileShader(vShader);
            // Check compilation
            string vShaderInfo = GL.GetShaderInfoLog(vShader);
            if (!vShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(vShaderInfo);
                return -1;
            }

            // Fragment Shader
            int fShader = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fShader, fragmentShaderSource);
            GL.CompileShader(fShader);
            string fShaderInfo = GL.GetShaderInfoLog(fShader);
            if (!fShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(fShaderInfo);
                return -1;
            }

            int program = GL.CreateProgram();
            GL.AttachShader(program, vShader);
            GL.AttachShader(program, fShader);
            GL.LinkProgram(program);
            GL.UseProgram(program);

            return program;
        }

        private void InitVertexBuffers()
        {
            float[] vertices = new float[]
            {
                -0.5f, -0.5f,
                0.5f, -0.5f,
                -0.5f, 0.5f,
                0.5f, 0.5f
            };

            int vbo;
            GL.GenBuffers(1, out vbo);

            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            // Get an array size in bytes
            int sizeInBytes = vertices.Length * sizeof(float);
            // Send the vertex array to a video card memory
            GL.BufferData(BufferTarget.ArrayBuffer, sizeInBytes, vertices, BufferUsageHint.StaticDraw);
            // Config
            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, 0);
            GL.EnableVertexAttribArray(0);
        }
    }
}

We need to set a coordinate system: [0, 20]. I want to have (0, 0) in top left corner. Y axis will have a direction from top to bottom. And I add ability to set a size and a position of square:

Snake_WinFormsOpenGL31CSharp_SetNewCoordSystem.png.902946a87d728e3011814ab7e4d500eb.png

using System;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics;

namespace Snake
{
    public partial class Form1 : Form
    {
        private int _shaderProgram;
        private int _uColorLocation;

        private int _gameFieldWidth = 20;
        private int _gameFieldHeight = 20;

        private int _uScaleMatrixLocation;
        private int _uTranslateMatrixLocation;
        private Matrix4 _scaleMatrix;
        private Matrix4 _translateMatrix;

        public Form1()
        {
            InitializeComponent();

            // Centers the form on the current screen
            CenterToScreen();
        }

        private void glControl_Load(object sender, EventArgs e)
        {
            glControl.MakeCurrent();

            // Set a color for clear the glControl
            GL.ClearColor(Color4.Black);

            // Initialize shaders and get a shader program
            _shaderProgram = InitShadersAndGetProgram();
            if (_shaderProgram < 0) return;

            // Initialize vertex buffers
            InitVertexBuffers();

            _uColorLocation = GL.GetUniformLocation(_shaderProgram, "uColor");
            if (_uColorLocation < 0)
            {
                MessageBox.Show("Failed to get uColorLocation variable");
                return;
            }

            // Set a coordinate cell
            int uProjMatrixLocation = GL.GetUniformLocation(_shaderProgram, "uProjMatrix");
            if (uProjMatrixLocation < 0)
            {
                MessageBox.Show("Failed to get a location uProjMatrix variable");
                return;
            }
            Matrix4 projMatrix = Matrix4.CreateOrthographicOffCenter(0f, _gameFieldWidth, _gameFieldHeight, 0f, -100f, 100f);
            GL.UniformMatrix4(uProjMatrixLocation, false, ref projMatrix);

            // Get uScaleMatrix Location
            _uScaleMatrixLocation = GL.GetUniformLocation(_shaderProgram, "uScaleMatrix");
            if (_uScaleMatrixLocation < 0)
            {
                MessageBox.Show("Failed to get a location uScaleMatrix variable");
                return;
            }
            _scaleMatrix = new Matrix4();

            // Get uTranslateMatrix Location
            _uTranslateMatrixLocation = GL.GetUniformLocation(_shaderProgram, "uTranslateMatrix");
            if (_uTranslateMatrixLocation < 0)
            {
                MessageBox.Show("Failed to get a location uTranslateMatrix variable");
                return;
            }
            _translateMatrix = new Matrix4();

            GL.Viewport(0, 0, glControl.Width, glControl.Height);
        }

        private void glControl_Paint(object sender, PaintEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit);

            Draw();

            glControl.SwapBuffers();
        }

        private void Draw()
        {
            // Draw game entities here
            DrawSquare(1, 1, Color4.LightCoral, 1);
        }

        private void DrawSquare(int x, int y, Color4 color, int size = 1)
        {
            // Set color to fragment shader
            GL.Uniform3(_uColorLocation, color.R, color.G, color.B);
            // Set a size of the square
            _scaleMatrix = Matrix4.CreateScale(size);
            GL.UniformMatrix4(_uScaleMatrixLocation, false, ref _scaleMatrix);
            // Set a position of the square
            _translateMatrix = Matrix4.CreateTranslation(new Vector3(x, y, 1f));
            GL.UniformMatrix4(_uTranslateMatrixLocation, false, ref _translateMatrix);
            // Draw Rectangle
            GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
        }

        private int InitShadersAndGetProgram()
        {
            string vertexShaderSource =
                "#version 140\n" +
                "in vec2 aPosition;" +
                "uniform mat4 uProjMatrix;" +
                "uniform mat4 uScaleMatrix;" +
                "uniform mat4 uTranslateMatrix;" +
                "void main()" +
                "{" +
                "    gl_Position = uProjMatrix * uTranslateMatrix * uScaleMatrix * vec4(aPosition, 1.0, 1.0);" +
                "}";

            string fragmentShaderSource =
                "#version 140\n" +
                "out vec4 fragColor;" +
                "uniform vec3 uColor;" +
                "void main()" +
                "{" +
                "    fragColor = vec4(uColor, 1.0);" +
                "}";

            // Vertex Shader
            int vShader = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vShader, vertexShaderSource);
            GL.CompileShader(vShader);
            // Check compilation
            string vShaderInfo = GL.GetShaderInfoLog(vShader);
            if (!vShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(vShaderInfo);
                return -1;
            }

            // Fragment Shader
            int fShader = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fShader, fragmentShaderSource);
            GL.CompileShader(fShader);
            string fShaderInfo = GL.GetShaderInfoLog(fShader);
            if (!fShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(fShaderInfo);
                return -1;
            }

            int program = GL.CreateProgram();
            GL.AttachShader(program, vShader);
            GL.AttachShader(program, fShader);
            GL.LinkProgram(program);
            GL.UseProgram(program);

            return program;
        }

        private void InitVertexBuffers()
        {
            float[] vertices = new float[]
            {
                0f, 0f,
                0f, 1f,
                1f, 0f,
                1f, 1f
            };

            int vbo;
            GL.GenBuffers(1, out vbo);

            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            // Get an array size in bytes
            int sizeInBytes = vertices.Length * sizeof(float);
            // Send the vertex array to a video card memory
            GL.BufferData(BufferTarget.ArrayBuffer, sizeInBytes, vertices, BufferUsageHint.StaticDraw);
            // Config
            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, 0);
            GL.EnableVertexAttribArray(0);
        }
    }
}

Each game must have a game loop that will be called by timer. I created the GameLoop method that just prints "Hello, World!" to the debug console every 500 ms:

public Form1()
{
    InitializeComponent();

    // Centers the form on the current screen
    CenterToScreen();

    // Create a timer for the GameLoop method
    var timer = new Timer();
    timer.Tick += GameLoop;
    timer.Interval = 500;
    timer.Start();
}

private void GameLoop(object sender, System.EventArgs e)
{
    Console.WriteLine("Hello, World!");
}

Update() method will have updates for snake cell coordinates and etc. The Draw() method will have only draw methods for game entities. Method glControl.Invalidate() will provoke a call of Draw() method.

private void GameLoop(object sender, System.EventArgs e)
{
    // Update coordinates of game entities
    // or check collisions
    Update();

    // This method calls glControl_Paint
    glControl.Invalidate();
}

private void Update()
{
    Console.WriteLine("Game entities coords was updated");
}

private void glControl_Paint(object sender, PaintEventArgs e)
{
    GL.Clear(ClearBufferMask.ColorBufferBit);

    Draw();

    glControl.SwapBuffers();
}

private void Draw()
{
    DrawFood();
    DrawSnake();
}

private void DrawSnake()
{
    Console.WriteLine("Snake was drawn");
    DrawSquare(2, 1, Color4.LightGreen);
}

private void DrawFood()
{
    Console.WriteLine("Food was drawn");
}

List data structure is ideal for keeping snake cells coordinates:

// Snake list of (x, y) positions
private List<Point> _snake = new List<Point>()
    {
        new Point(1, 1)
    };

Point(1, 1) - it is position of the head.

Method for drawing the snake:

private void DrawSnake()
{
    foreach (var cell in _snake)
    {
        DrawSquare(cell.X, cell.Y, Color4.LightGreen);
    }
}

For moving the snake we need to create the "snakeDir" variable:

// Snake movement direction
private Point _snakeDir = new Point(1, 0);

The snake moving is very simple. Please, read comments:

private void Update()
{
    // Calc a new position of the head
    Point newHeadPosition = new Point(
        _snake[0].X + _snakeDir.X,
        _snake[0].Y + _snakeDir.Y
    );

    // Insert new position in the beginning of the snake list
    _snake.Insert(0, newHeadPosition);

    // Remove the last element
    _snake.RemoveAt(_snake.Count - 1);
}

Snake_WinFormsOpenGL31CSharp_MovingHead.gif.9f06765590efca7cf20c4d8e5e077f3c.gif

using System;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics;
using System.Collections.Generic;
using System.Drawing;

namespace Snake
{
    public partial class Form1 : Form
    {
        private int _shaderProgram;
        private int _uColorLocation;

        private int _gameFieldWidth = 20;
        private int _gameFieldHeight = 20;

        private int _uScaleMatrixLocation;
        private int _uTranslateMatrixLocation;
        private Matrix4 _scaleMatrix;
        private Matrix4 _translateMatrix;

        // Snake list of (x, y) positions
        private List<Point> _snake = new List<Point>()
            {
                new Point(1, 1)
            };

        // Snake movement direction
        private Point _snakeDir = new Point(1, 0);

        public Form1()
        {
            InitializeComponent();

            // Centers the form on the current screen
            CenterToScreen();

            // Create a timer for the GameLoop method
            var timer = new Timer();
            timer.Tick += GameLoop;
            timer.Interval = 500;
            timer.Start();
        }

        private void GameLoop(object sender, System.EventArgs e)
        {
            // Update coordinates of game entities
            // or check collisions
            Update();

            // This method calls glControl_Paint
            glControl.Invalidate();
        }

        private void Update()
        {
            // Calc a new position of the head
            Point newHeadPosition = new Point(
                _snake[0].X + _snakeDir.X,
                _snake[0].Y + _snakeDir.Y
            );

            // Insert new position in the beginning of the snake list
            _snake.Insert(0, newHeadPosition);

            // Remove the last element
            _snake.RemoveAt(_snake.Count - 1);
        }

        private void glControl_Paint(object sender, PaintEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit);

            Draw();

            glControl.SwapBuffers();
        }

        private void Draw()
        {
            DrawFood();
            DrawSnake();
        }

        private void DrawSnake()
        {
            foreach (var cell in _snake)
            {
                DrawSquare(cell.X, cell.Y, Color4.LightGreen);
            }
        }

        private void DrawFood()
        {
            Console.WriteLine("Food was drawn");
        }

        private void glControl_Load(object sender, EventArgs e)
        {
            glControl.MakeCurrent();

            // Set a color for clear the glControl
            GL.ClearColor(Color4.Black);

            // Initialize shaders and get a shader program
            _shaderProgram = InitShadersAndGetProgram();
            if (_shaderProgram < 0) return;

            // Initialize vertex buffers
            InitVertexBuffers();

            _uColorLocation = GL.GetUniformLocation(_shaderProgram, "uColor");
            if (_uColorLocation < 0)
            {
                MessageBox.Show("Failed to get uColorLocation variable");
                return;
            }

            // Set a coordinate cell
            int uProjMatrixLocation = GL.GetUniformLocation(_shaderProgram, "uProjMatrix");
            if (uProjMatrixLocation < 0)
            {
                MessageBox.Show("Failed to get a location uProjMatrix variable");
                return;
            }
            Matrix4 projMatrix = Matrix4.CreateOrthographicOffCenter(0f, _gameFieldWidth, _gameFieldHeight, 0f, -100f, 100f);
            GL.UniformMatrix4(uProjMatrixLocation, false, ref projMatrix);

            // Get uScaleMatrix Location
            _uScaleMatrixLocation = GL.GetUniformLocation(_shaderProgram, "uScaleMatrix");
            if (_uScaleMatrixLocation < 0)
            {
                MessageBox.Show("Failed to get a location uScaleMatrix variable");
                return;
            }
            _scaleMatrix = new Matrix4();

            // Get uTranslateMatrix Location
            _uTranslateMatrixLocation = GL.GetUniformLocation(_shaderProgram, "uTranslateMatrix");
            if (_uTranslateMatrixLocation < 0)
            {
                MessageBox.Show("Failed to get a location uTranslateMatrix variable");
                return;
            }
            _translateMatrix = new Matrix4();

            GL.Viewport(0, 0, glControl.Width, glControl.Height);
        }

        private void DrawSquare(int x, int y, Color4 color, int size = 1)
        {
            // Set color to fragment shader
            GL.Uniform3(_uColorLocation, color.R, color.G, color.B);
            // Set a size of the square
            _scaleMatrix = Matrix4.CreateScale(size);
            GL.UniformMatrix4(_uScaleMatrixLocation, false, ref _scaleMatrix);
            // Set a position of the square
            _translateMatrix = Matrix4.CreateTranslation(new Vector3(x, y, 1f));
            GL.UniformMatrix4(_uTranslateMatrixLocation, false, ref _translateMatrix);
            // Draw Rectangle
            GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
        }

        private int InitShadersAndGetProgram()
        {
            string vertexShaderSource =
                "#version 140\n" +
                "in vec2 aPosition;" +
                "uniform mat4 uProjMatrix;" +
                "uniform mat4 uScaleMatrix;" +
                "uniform mat4 uTranslateMatrix;" +
                "void main()" +
                "{" +
                "    gl_Position = uProjMatrix * uTranslateMatrix * uScaleMatrix * vec4(aPosition, 1.0, 1.0);" +
                "}";

            string fragmentShaderSource =
                "#version 140\n" +
                "out vec4 fragColor;" +
                "uniform vec3 uColor;" +
                "void main()" +
                "{" +
                "    fragColor = vec4(uColor, 1.0);" +
                "}";

            // Vertex Shader
            int vShader = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vShader, vertexShaderSource);
            GL.CompileShader(vShader);
            // Check compilation
            string vShaderInfo = GL.GetShaderInfoLog(vShader);
            if (!vShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(vShaderInfo);
                return -1;
            }

            // Fragment Shader
            int fShader = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fShader, fragmentShaderSource);
            GL.CompileShader(fShader);
            string fShaderInfo = GL.GetShaderInfoLog(fShader);
            if (!fShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(fShaderInfo);
                return -1;
            }

            int program = GL.CreateProgram();
            GL.AttachShader(program, vShader);
            GL.AttachShader(program, fShader);
            GL.LinkProgram(program);
            GL.UseProgram(program);

            return program;
        }

        private void InitVertexBuffers()
        {
            float[] vertices = new float[]
            {
                0f, 0f,
                0f, 1f,
                1f, 0f,
                1f, 1f
            };

            int vbo;
            GL.GenBuffers(1, out vbo);

            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            // Get an array size in bytes
            int sizeInBytes = vertices.Length * sizeof(float);
            // Send the vertex array to a video card memory
            GL.BufferData(BufferTarget.ArrayBuffer, sizeInBytes, vertices, BufferUsageHint.StaticDraw);
            // Config
            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, 0);
            GL.EnableVertexAttribArray(0);
        }
    }
}

I will explain eating food later. But you can read comments in the code.

This is the result:

Snake_WinFormsOpenGL31CSharp_MovingSnake.gif.07c0907cb82e4261867eb64089042459.gif

using System;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics;
using System.Collections.Generic;
using System.Drawing;

namespace Snake
{
    public partial class Form1 : Form
    {
        private int _shaderProgram;
        private int _uColorLocation;

        private int _gameFieldWidth = 20;
        private int _gameFieldHeight = 20;

        private int _uScaleMatrixLocation;
        private int _uTranslateMatrixLocation;
        private Matrix4 _scaleMatrix;
        private Matrix4 _translateMatrix;

        // Snake list of (x, y) positions
        private List<Point> _snake = new List<Point>()
            {
                new Point(1, 1)
            };

        // Snake movement direction
        private Point _snakeDir = new Point(1, 0);

        // Food
        private Point _food = new Point();

        // Random generator
        private Random _rnd = new Random();

        public Form1()
        {
            InitializeComponent();

            // Centers the form on the current screen
            CenterToScreen();

            // Generate an initial random position for the food
            GenerateFood();

            // Create a timer for the GameLoop method
            var timer = new Timer();
            timer.Tick += GameLoop;
            timer.Interval = 200;
            timer.Start();
        }

        private void GameLoop(object sender, System.EventArgs e)
        {
            // Update coordinates of game entities
            // or check collisions
            Update();

            // This method calls glControl_Paint
            glControl.Invalidate();
        }

        private void Update()
        {
            // Calc a new position of the head
            Point newHeadPosition = new Point(
                _snake[0].X + _snakeDir.X,
                _snake[0].Y + _snakeDir.Y
            );

            // Insert new position in the beginning of the snake list
            _snake.Insert(0, newHeadPosition);

            // Remove the last element
            _snake.RemoveAt(_snake.Count - 1);

            // Check collision with the food
            if (_snake[0].X == _food.X &&
                _snake[0].Y == _food.Y)
            {
                // Add new element in the snake
                _snake.Add(new Point(_food.X, _food.Y));

                // Generate a new food position
                GenerateFood();
            }
        }

        private void glControl_Paint(object sender, PaintEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit);

            Draw();

            glControl.SwapBuffers();
        }

        private void Draw()
        {
            DrawFood();
            DrawSnake();
        }

        private void DrawSnake()
        {
            foreach (var cell in _snake)
            {
                DrawSquare(cell.X, cell.Y, Color4.LightGreen);
            }
        }

        private void DrawFood()
        {
            DrawSquare(_food.X, _food.Y, Color.OrangeRed);
        }

        private void GenerateFood()
        {
            _food.X = _rnd.Next(0, _gameFieldWidth);
            _food.Y = _rnd.Next(0, _gameFieldHeight);
        }

        private void glControl_Load(object sender, EventArgs e)
        {
            glControl.MakeCurrent();

            // Set a color for clear the glControl
            GL.ClearColor(Color4.Black);

            // Initialize shaders and get a shader program
            _shaderProgram = InitShadersAndGetProgram();
            if (_shaderProgram < 0) return;

            // Initialize vertex buffers
            InitVertexBuffers();

            _uColorLocation = GL.GetUniformLocation(_shaderProgram, "uColor");
            if (_uColorLocation < 0)
            {
                MessageBox.Show("Failed to get uColorLocation variable");
                return;
            }

            // Set a coordinate cell
            int uProjMatrixLocation = GL.GetUniformLocation(_shaderProgram, "uProjMatrix");
            if (uProjMatrixLocation < 0)
            {
                MessageBox.Show("Failed to get a location uProjMatrix variable");
                return;
            }
            Matrix4 projMatrix = Matrix4.CreateOrthographicOffCenter(0f, _gameFieldWidth, _gameFieldHeight, 0f, -100f, 100f);
            GL.UniformMatrix4(uProjMatrixLocation, false, ref projMatrix);

            // Get uScaleMatrix Location
            _uScaleMatrixLocation = GL.GetUniformLocation(_shaderProgram, "uScaleMatrix");
            if (_uScaleMatrixLocation < 0)
            {
                MessageBox.Show("Failed to get a location uScaleMatrix variable");
                return;
            }
            _scaleMatrix = new Matrix4();

            // Get uTranslateMatrix Location
            _uTranslateMatrixLocation = GL.GetUniformLocation(_shaderProgram, "uTranslateMatrix");
            if (_uTranslateMatrixLocation < 0)
            {
                MessageBox.Show("Failed to get a location uTranslateMatrix variable");
                return;
            }
            _translateMatrix = new Matrix4();

            GL.Viewport(0, 0, glControl.Width, glControl.Height);
        }

        private void DrawSquare(int x, int y, Color4 color, int size = 1)
        {
            // Set color to fragment shader
            GL.Uniform3(_uColorLocation, color.R, color.G, color.B);
            // Set a size of the square
            _scaleMatrix = Matrix4.CreateScale(size);
            GL.UniformMatrix4(_uScaleMatrixLocation, false, ref _scaleMatrix);
            // Set a position of the square
            _translateMatrix = Matrix4.CreateTranslation(new Vector3(x, y, 1f));
            GL.UniformMatrix4(_uTranslateMatrixLocation, false, ref _translateMatrix);
            // Draw Rectangle
            GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
        }

        private int InitShadersAndGetProgram()
        {
            string vertexShaderSource =
                "#version 140\n" +
                "in vec2 aPosition;" +
                "uniform mat4 uProjMatrix;" +
                "uniform mat4 uScaleMatrix;" +
                "uniform mat4 uTranslateMatrix;" +
                "void main()" +
                "{" +
                "    gl_Position = uProjMatrix * uTranslateMatrix * uScaleMatrix * vec4(aPosition, 1.0, 1.0);" +
                "}";

            string fragmentShaderSource =
                "#version 140\n" +
                "out vec4 fragColor;" +
                "uniform vec3 uColor;" +
                "void main()" +
                "{" +
                "    fragColor = vec4(uColor, 1.0);" +
                "}";

            // Vertex Shader
            int vShader = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vShader, vertexShaderSource);
            GL.CompileShader(vShader);
            // Check compilation
            string vShaderInfo = GL.GetShaderInfoLog(vShader);
            if (!vShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(vShaderInfo);
                return -1;
            }

            // Fragment Shader
            int fShader = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fShader, fragmentShaderSource);
            GL.CompileShader(fShader);
            string fShaderInfo = GL.GetShaderInfoLog(fShader);
            if (!fShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(fShaderInfo);
                return -1;
            }

            int program = GL.CreateProgram();
            GL.AttachShader(program, vShader);
            GL.AttachShader(program, fShader);
            GL.LinkProgram(program);
            GL.UseProgram(program);

            return program;
        }

        private void InitVertexBuffers()
        {
            float[] vertices = new float[]
            {
                0f, 0f,
                0f, 1f,
                1f, 0f,
                1f, 1f
            };

            int vbo;
            GL.GenBuffers(1, out vbo);

            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            // Get an array size in bytes
            int sizeInBytes = vertices.Length * sizeof(float);
            // Send the vertex array to a video card memory
            GL.BufferData(BufferTarget.ArrayBuffer, sizeInBytes, vertices, BufferUsageHint.StaticDraw);
            // Config
            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, 0);
            GL.EnableVertexAttribArray(0);
        }

        private void glControl_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            switch (e.KeyChar)
            {
                case 'w':
                    _snakeDir.X = 0;
                    _snakeDir.Y = -1;
                    break;
                case 'a':
                    _snakeDir.X = -1;
                    _snakeDir.Y = 0;
                    break;
                case 's':
                    _snakeDir.X = 0;
                    _snakeDir.Y = 1;
                    break;
                case 'd':
                    _snakeDir.X = 1;
                    _snakeDir.Y = 0;
                    break;
            }
            //glControl.Invalidate();
        }
    }
}

 



0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • What is your GameDev Story?

    In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

    (You must login to your GameDev.net account.)

  • Blog Entries

  • Similar Content

    • By SuperVGA
      Hi Everyone,
      It's been a while - I've been keeping myself busy with some less gratifying aspects to prevent me from playing too much with the unfinished product, then not knowing where to go next. I suppose we all learn as we go...
      So a few years back, I was so sure I nailed (tesselation) shaders. Obviously, I had just gotten lucky.
      I did things different back then - somehow I managed to get a 410 core tesselation shader working.
      I'm already suspicious of my Shader Loader class, because I can't for the life of me find anything wrong with my shaders. I'll proceed to refactor my Shader Loader now, but perhaps I'll upload that if you agree that my shaders are OK, and that I'm not going crazy...
      Vert
      #version 410 core in vec4 position; in vec4 color; uniform mat4 model; uniform mat4 view; uniform mat4 projection; out vec4 position_tc_in; out vec4 color_tc_in; void main() { position_tc_in = projection * view * model * position; color_tc_in = color; } TesC
      #version 410 core in vec4 position_tc_in[]; out vec4 position_te_in[]; in vec4 color_tc_in[]; out vec4 color_te_in[]; layout (vertices = 4) out; void main() { position_te_in[gl_InvocationID] = position_tc_in[gl_InvocationID]; color_te_in[gl_InvocationID] = color_tc_in[gl_InvocationID]; if(gl_InvocationID == 0) { gl_TessLevelOuter[0] = 4.0; gl_TessLevelOuter[1] = 4.0; gl_TessLevelOuter[2] = 4.0; gl_TessLevelOuter[3] = 4.0; gl_TessLevelInner[0] = 8.0; gl_TessLevelInner[1] = 8.0; } } TesE
      #version 410 core in vec4 position_te_in[]; in vec4 color_te_in[]; out vec4 color_f_in; layout (quads) in; void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; vec4 a = mix(position_te_in[1], position_te_in[0], u); vec4 b = mix(position_te_in[2], position_te_in[3], u); gl_Position = mix(a, b, v); vec4 ca = mix(color_te_in[1], color_te_in[0], u); vec4 cb = mix(color_te_in[2], color_te_in[3], u); color_f_in = mix(ca, cb, v); } Frag
      #version 410 core in vec4 position_te_in[]; in vec4 color_te_in[]; out vec4 color_f_in; layout (quads) in; void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; vec4 a = mix(position_te_in[1], position_te_in[0], u); vec4 b = mix(position_te_in[2], position_te_in[3], u); gl_Position = mix(a, b, v); vec4 ca = mix(color_te_in[1], color_te_in[0], u); vec4 cb = mix(color_te_in[2], color_te_in[3], u); color_f_in = mix(ca, cb, v); } The error
      assets/shaders/terrain failed to link: Tessellation control info ------------------------- 0(23) : error C0000: syntax error, unexpected '[', expecting "::" at token "[" (0) : error C2003: incompatible options for link This doesn't lead me to think the shader loading code is wrong, since it actually references a line in my tessellation shader. It's just that I don't think it's making a lot of sense. This is why I think my loader might be wrong, I just think it's really strange that everything here works if I remove the color part. I've spent a lot of time thinking it was the wrong way to pass a vertex attribute through the tessellation stages, but it doesn't really seem incorrect.
      Many thanks in advance!
      Best
      - Svga
       
    • By TexasJack
      Firstly, a disclaimer: my query covers both graphics AND bits of artificial intelligence, if it's in the wrong forum - shift it by all means.

      I am a graphic designer and I have a real layman's interest in the recent developments in AI and how it is being used to enhance images. It sounds sad, but as a sort of hobby, I constantly muse over different methods of calculating how to upscale images based on samples of other pixels etc... I have heaps of jottings/notes of formulas that I have devised that I would love to put into practise by writing little experimental programs to implement them.

      By extension, I am also fascinated by deep learning and neural networks and how they can be trained to develop image enhancement algorithms.

      Here's the problem.

      I have no idea about programming/coding (but I am trying to learn). I've been struggling along with C# as part of also learning to make games in Unity - but I'm barely at the 'Hello World' stage and would have no idea how to write a program that would handle images (for example, how do C# programs break down and handle the individual pixels in a bitmap - and how are these individual pixels edited etc...).

      I would really appreciate pointers on what the best way to start learning something like this in my spare time would be?

      Sorry if this is really vague, I can clarify anything if need be.
    • By Turbo14
      I'm having some problems with path-tracing code. Not sure if it's from the random number generator or what. but my projected rays don't "stick" to surfaces.

      Here's what I'm doing:

      what I want is the hit points to appear to "stick" to surfaces but instead they're flying all around. The only time they stick is when I strafe. No idea what I'm doing wrong.

      void Update () {
      m_Clear_Buff.CopyTo(m_Color_Buff, 0);

      m_main_Cam.enabled = false;
      m_Trace_Cam.enabled = true;
      System.Random r = new System.Random(98);

      for (int i = 0; i < 300; i++)
      {
      Vector3 dir = new Vector3((float)r.Next(360) /360f-.5f, (float)r.Next(360) / 360f - .5f, (float)r.Next(360) / 360f - .5f);

      Ray ray = new Ray(new Vector3(0,0,0), dir);
      RaycastHit rch;
      if (Physics.Raycast(ray, out rch, 50f))
      {

      Vector3 sp = m_Trace_Cam.WorldToScreenPoint(rch.point);

      if (sp.x >=0 && sp.x < m_Trace_Cam.pixelRect.width-1 && sp.y >=0 && sp.y < m_Trace_Cam.pixelRect.height-1)
      {
      float lv = Mathf.Max(Vector3.Dot(rch.point.normalized, new Vector3(0, 0, 0)), 0);
      lv *= 10;

      Material m = rch.collider.gameObject.GetComponent().material;
      m_Color_Buff[(int)(sp.x + sp.y * m_Trace_Cam.pixelRect.width)] = new Color(m.color.r * lv, m.color.g * lv, m.color.b * lv,1);
      }
      }
      }
      m_Trace_Cam.enabled = false;
      m_main_Cam.enabled = true;
      m_Trace_Tex.SetPixels(m_Color_Buff);
      m_Trace_Tex.Apply();
      }

    • By Okkama Games
      Hi to all!

      I've created minimalistic puzzle game for Android, and need some feedback about gameplay!
      It's simple 2D game with unique game mechanic (I did not see exactly the same mechanic in other games, if you know similar games, please tell me)

      Also, i need feedback about levels difficulty. (Some levels may seem impassable at first glance)
      Download and strain your brains!
      https://play.google.com/store/apps/details?id=org.godotengine.projectionfree
      Some screenshots:
       



    • By sidbhati32
      https://www.youtube.com/watch?v=xuKz-komT5I&t=12s     hey     How to achieve the horizontal swipe somewhat close to the video?      
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!