Jump to content
  • Advertisement
  • entries
    8
  • comments
    3
  • views
    389

About this blog

I want to explain my progress of game development in the form of step-by-step instructions for beginners. I hope someone will corrent my progress and message me about big mistakes in English grammar.

Entries in this blog

 

Javascript 101. Socket.io Connection, JS/ES5

I will try to explain a process in step-by-step how to prepare server but it will be good if you will watch this video tutorial in parallel: Setup & Sending Files. Node.js Tutorial Guide At first test let's create a very simple server script locally that will send "Hello, World" to a client and the client will show it on the screen. You need to download and install Node.js: https://nodejs.org/en/download/ Create a folder for your project and go to the folder using CMD. Write this command in the console terminale to create package.json: npm init -y Install "socket.io" and "express" to a local "node_modules" folder. Enter the command for this: npm install --save socket.io express Now we are ready to write a simple server script and a client script. I use VSCode editor: https://code.visualstudio.com/ Open your prefered code editor. If you use VSCode you can run in the console terminal this command from your project folder: code . (you need note that it is "code" word and dot ".") Create two folders in your project: "server" and "client". Create the "server.js" file in the "server" folder. Create the "client.js" file in the "client" folder. Create the "index.html" file in the "client" folder. index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Multiplayer Snake</title> </head> <body> <h1>Hello, World</h1> </body> </html> Create the file: "app.js" in the root of your project: app.js var express = require("express"); var app = express(); var server = require("http").Server(app); app.get("/", function(req, res) { res.sendFile(__dirname + "/client/index.html"); }); app.use("/client", express.static(__dirname + "/client")); var port = 8080; server.listen(port); console.log("Server started. Port = " + port); You can run this script from the console terminal by the command: node app.js You will see a message: "Server started. Port = 8080" Write the address in the browser: localhost:8080 You will see "Hello, World" on the web page. Let's find "cnd socket.io" url in the Internet. Add this code to the index.html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script> <title>Multiplayer Snake</title> </head> <body> <h1>Hello, World</h1> <script> var socket = io(); </script> </body> </html> Create a socket callback in app.js: app.js var express = require("express"); var app = express(); var server = require("http").Server(app); app.get("/", function(req, res) { res.sendFile(__dirname + "/client/index.html"); }); app.use("/client", express.static(__dirname + "/client")); var port = 8080; server.listen(port); console.log("Server started. Port = " + port); var io = require("socket.io")(server, {}); io.sockets.on("connection", function(socket) { console.log("socket connection"); }); Run the server: node app.js Write the address in the browser: localhost:8080 You will see the message "socket connection" in the console.

8Observer8

8Observer8

Algorithm Use UML Instead Of Flowcharts

Updated: 2/15/2019
- added "if...else" Double Selection Statement Visual Studio Enterprise 2015 has build in tool for creating UML Activity Diagrams. We can use this tool for creating flowcharts for describing algorithms. Creating UML Activity Diagram: Select: "File" -> "New" -> "Project..." -> "Modeling Projects" Write a name, for example: UseUMLInsteadOfFlowcharts_ModelingProject Press "OK" button Right Click on a name of the project Select: "Add" -> "New Item..." -> "UML Activity Diagram" Write a name, for example: UseUMLInsteadOfFlowcharts.activitydiagram Press "Add" button To add new items, drag them from the "Toolbox" I get these examples from the book: 2016 - 08 - C# 6 for Programmers - 6th Edition - Paul Deitel, Harvey Deitel. Code Examples: link 1. "if" Single-Selection Statement int studentGrade = 70; if (studentGrade >= 60) { Console.WriteLine("Passed"); } 2. "if...else" Double-Selection Statement int studentGrade = 70; if (studentGrade >= 60) { Console.WriteLine("Passed"); } else { Console.WriteLine("Failed"); }

8Observer8

8Observer8

Javascript 101. Snake. JS/ES5, Canvas API

Step-by-step instruction of Snake 2D using JavaScript/ES5, Canvas API Let's make a very simple classic snake game. For example, if we have a snake head with 10x10 pixels then we will move our snake by a step with 10 pixels using timer. This is the result of our work: Sandbox Note. I take ideas from this tutorial: Python Snake Game We need to get a canvas context from the canvas element. The canvas context is an object that have methods for working with graphics. Lets' create a script element inside of "index.html" file, get the canvas element, get the canvas context and set a clear color to black: Sandbox !DOCTYPE html> <head> <title>Snake</title> </head> <body> <canvas width="200" height="200" id="gameCanvas"> Your browser does not support HTML5 Canvas. Please shift to a newer browser. </canvas> <script> var canvas, ctx; canvas = document.getElementById("gameCanvas"); ctx = canvas.getContext("2d"); // Clear the canvas element ctx.clearRect(0, 0, canvas.width, canvas.height); // Set a clear color ctx.fillStyle = "black"; // Fill the canvas element with the clear color ctx.beginPath(); ctx.rect(0, 0, canvas.width, canvas.height); ctx.fill(); </script> </body> </html> Write a function for drawing a rectangle and call this function for test: Sandbox <!DOCTYPE html> <head> <title>Snake</title> </head> <body> <canvas width="200" height="200" id="gameCanvas"> Your browser does not support HTML5 Canvas. Please shift to a newer browser. </canvas> <script> var canvas, ctx; canvas = document.getElementById("gameCanvas"); ctx = canvas.getContext("2d"); // Clear the canvas element ctx.clearRect(0, 0, canvas.width, canvas.height); // Set a clear color ctx.fillStyle = "black"; // Fill the canvas element with the clear color ctx.beginPath(); ctx.rect(0, 0, canvas.width, canvas.height); ctx.fill(); // Call a drawRectangle() function drawRectangle(0, 20, "green", 20); function drawRectangle(x, y, color, size) { ctx.beginPath(); ctx.rect(x, y, size, size); ctx.fillStyle = color; ctx.fill(); } </script> </body> </html> 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: setInterval(gameLoop, 500) function gameLoop() { console.log("Hello, World!"); } For a while our GameLoop will have only two called methods Update() and Draw(). The Update() method will have updates for snake cell coordinates and etc. The Draw() method will have only draw methods for game entities. function gameLoop() { update(); draw(); } function update() { console.log("update"); } function draw() { drawSnake(); drawFood(); } function drawSnake() { console.log("draw snake"); } function drawFood() { console.log("draw food"); } List data structure is ideal for keeping snake cells coordinates: // Snake list of (x, y) positions var snake = [{ x: 10, y: 10 }]; Create a function for clearing the canvas: function draw() { clearCanvas("black") drawSnake(); drawFood(); } function clearCanvas(color) { // Clear the canvas element ctx.clearRect(0, 0, canvas.width, canvas.height); // Set a clear color ctx.fillStyle = color; // Fill the canvas element with the clear color ctx.beginPath(); ctx.rect(0, 0, canvas.width, canvas.height); ctx.fill(); } Draw the snake: function drawSnake() { snake.forEach(cell => { drawRectangle(cell.x, cell.y, "green"); }); } Sandbox For moving the snake we need to create the "snakeDir" variable: // Snake movement direction var snakeDir = { x: 10, y: 0 }; The snake moving is very simple. Please, read comments: function update() { // Calc a new position of the head var newHeadPosition = { x: snake[0].x + snakeDir.x, y: snake[0].y + snakeDir.y } // Insert new position in the beginning of the snake list snake.unshift(newHeadPosition); // Remove the last element snake.pop(); } Sandbox I will explain eating food later. But you can read comments in the code. The result code: Sandbox <!DOCTYPE html> <head> <title>Snake</title> </head> <body> <canvas width="200" height="200" id="gameCanvas"> Your browser does not support HTML5 Canvas. Please shift to a newer browser. </canvas> <p>Click on this window to activate keyboard handlers to control the snake by WASD and arrow keys.</p> <script> // Canvas element and context var canvas, ctx; // Snake list of (x, y) positions var snake = [{ x: 10, y: 10 }]; // Snake movement direction var snakeDir = { x: 10, y: 0 }; // Snake step var cellSize = 10; // Food var food = { x: 0, y: 0 }; canvas = document.getElementById("gameCanvas"); ctx = canvas.getContext("2d"); // Field size var fieldWidth = canvas.width; var fieldHeight = canvas.height; // Generate an initial random position for the food generateFood(); // Set a key handler document.onkeydown = (event) => { switch (event.keyCode) { case 38: // Up case 87: // W snakeDir.x = 0; snakeDir.y = -cellSize; break; case 37: // Left case 65: // A snakeDir.x = -cellSize; snakeDir.y = 0; break; case 39: // Right case 68: // D snakeDir.x = cellSize; snakeDir.y = 0; break; case 40: // Down case 83: // S snakeDir.x = 0; snakeDir.y = cellSize; break; } }; // Run Game Loop setInterval(gameLoop, 200) function gameLoop() { update(); draw(); } function update() { // Calc a new position of the head var newHeadPosition = { x: snake[0].x + snakeDir.x, y: snake[0].y + snakeDir.y } // Insert new position in the beginning of the snake list snake.unshift(newHeadPosition); // Remove the last element snake.pop(); // Check a collision with the snake and the food if (snake[0].x === food.x && snake[0].y === food.y) { snake.push({ x: food.x, y: food.y }); // Generate a new food position generateFood(); } } function draw() { clearCanvas("black") drawSnake(); drawFood(); } function clearCanvas(color) { // Clear the canvas element ctx.clearRect(0, 0, canvas.width, canvas.height); // Set a clear color ctx.fillStyle = color; // Fill the canvas element with the clear color ctx.beginPath(); ctx.rect(0, 0, canvas.width, canvas.height); ctx.fill(); } function drawSnake() { snake.forEach(cell => { drawRectangle(cell.x, cell.y, "green", cellSize); }); } function drawFood() { drawRectangle(food.x, food.y, "orange", cellSize); } function drawRectangle(x, y, color, size) { ctx.beginPath(); ctx.rect(x, y, size, size); ctx.fillStyle = color; ctx.fill(); } function generateFood() { food.x = 10 * getRandomInt(0, fieldWidth / 10 - 1); food.y = 10 * getRandomInt(0, fieldHeight / 10 - 1); } // Returns a random integer between min (inclusive) and max (inclusive) function getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; } </script> </body> </html>  

8Observer8

8Observer8

C# 101. Snake. WinForms, OpenGL 3.1

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: 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. 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 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: 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); } 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: 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(); } } }  

8Observer8

8Observer8

 

C++ SortFunctions, TDD, GoogleTest, C++

I made a project for Visual Studio 2015: SortFunctions.zip  This project will show you how set up GoogleTest and run simple unit test. Note. If you have another version of VS than before you will run unit tests you need to select VS 2017, like in this screenshot: This solution include two projects: SortFunctions SortFunctions_UnitTests SortFunctions project has two files: SortFunctions.h #pragma once extern void bubbleSort(int *array, unsigned int amount); extern void countingSort(int *array, unsigned int amount); SortFunctions.cpp #include "SortFunctions.h" void bubbleSort(int *array, unsigned int amount) { } void countingSort(int *array, unsigned int amount) { } SortFunctions_UnitTests project has tests. For example, this is the "bubbleSortTests.cpp" with two tests. The first test is for positive numbers and the second test is for negative numbers: bubbleSortTests.cpp #include <gtest/gtest.h> #include "SortFunctions.h" TEST(bubbleSortTest, AllPositiveElements) { // Arrange const unsigned int amount = 5; int actualArray[amount] = { 5, 3, 10, 2, 7 }; int expectedArray[amount] = { 2, 3, 5, 7, 10 }; // Act bubbleSort(actualArray, amount); // Assert for (size_t i = 0; i < amount; i++) { ASSERT_EQ(expectedArray[i], actualArray[i]); } } TEST(bubbleSortTest, AllNegativeElements) { // Arrange const unsigned int amount = 5; int actualArray[amount] = { -5, -3, -10, -2, -7 }; int expectedArray[amount] = { -10, -7, -5, -3, -2 }; // Act bubbleSort(actualArray, amount); // Assert for (size_t i = 0; i < amount; i++) { ASSERT_EQ(expectedArray[i], actualArray[i]); } }  

8Observer8

8Observer8

 

GameDev books that I study

Updated 2/12/2019 Writing Games: 2010 - 06 - C# Game Programming: For Serious Game Creation. Source Code: 9781435455566.zip (121 MB) 2015 - 09 - Build your own 2D Game Engine and Create Great Web Games Using HTML5, JavaScript, and WebGL by Kelvin Sung, Jebediah Pavleas, Fernando Arnez, and Jason Pace. Source Code: https://github.com/apress/build-your-own-2d-game-engine 2017 - 10 - Pro HTML5 Games - 2nd Edition - A.R. Shankar. Source Code: https://github.com/apress/pro-html5-games-17 2018 - 04 - Unity in Action - 2nd Edition - J. Hocking. Source Code: https://www.manning.com/books/unity-in-action-second-edition Computer graphics: 2013 - 07 - WebGL Programming Guide - K. Matsuda, R. Lea. Source Code: https://sites.google.com/site/webglbook/ 2013 - 06 - Computer Graphics Principles and Practice - 3rd Edition - John F. Hughes, Andries van Dam, Morgan McGuire, David F. Sklar, James D. Foley, Steven K. Feiner, Kurt Akeley. Source Code: http://cgpp.net/about.xml Multiplayer: 2015 - 05 - Multiplayer Game Development with HTML5 - Rodrigo Silveira. Source Code: https://www.packtpub.com/code_download/21527 2015 - 10 - Multiplayer Game Programming - &nbsp;Josh Glazer, Sanjay Madhav. Source Code: https://github.com/MultiplayerBook/MultiplayerBook

8Observer8

8Observer8

C# GUI WinForms + OpenGL 3.1

This my example how to use GUI WinForms and OpenTK.GLControl Source for Visual Studio (including DLLs): EditedTriangle_WinFormsOpenGL31CSharp.zip EXE: EditedTrianlge_WinFormsOpenGL31CSharp_EXE.zip Separated DLL's for references: OpenTK.zip, OpenTK.GLControl.zip Standard WinForms Color Dialog:    

8Observer8

8Observer8

C# 101. Snake. WinForms, GDI+

Step-by-step instruction of Snake 2D using C#, WinForms, GDI+ Let's make a very simple classic snake game. For example, if we have a snake head with 10x10 pixels then we will move our snake by a step with 10 pixels using timer. This is the result of our work: Note. I take ideas from this tutorial: Python Snake Game We can set a game field size like this: // Set a game field size ClientSize = new Size(200, 200); Let's create a method for drawing of a rectangle: private void DrawRect(int x, int y, Color color, int size = 10) { Graphics g = CreateGraphics(); SolidBrush brush = new SolidBrush(color); g.FillRectangle(brush, new Rectangle(x, y, size, size)); brush.Dispose(); g.Dispose(); } 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(); // Set a game field size ClientSize = new Size(200, 200); // 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) { System.Console.WriteLine("Hello, World!"); } For a while our GameLoop will have only two called methods Update() and Draw(). The Update() method will have updates for snake cell coordinates and etc. The Draw() method will have only draw methods for game entities.

For example: private void GameLoop(object sender, System.EventArgs e) { Update(); Draw(); } private void Update() { Console.WriteLine("Game entities coords was updated"); } private void Draw() { DrawFood(); DrawSnake(); } private void DrawSnake() { Console.WriteLine("Snake was drawn"); } 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(10, 10) }; Point (10, 10) - it is position of the head.

It is better to move a general Draw() method call to Form1_Paint() and add Invalidate() call to GameLoop: private void Form1_Paint(object sender, PaintEventArgs e) { Draw(); } private void GameLoop(object sender, System.EventArgs e) { Update(); Invalidate(); } Method for drawing the snake: private void DrawSnake() { foreach (var cell in _snake) { DrawRect(cell.X, cell.Y, Color.Green); } } For moving the snake we need to create the "snakeDir" variable: / Snake movement direction private Point _snakeDir = new Point(10, 0); The snake moving is very simple: you need to add two vectors: the snake head vector position and the snake direction. You add a new vector positions intead of head and remove last element. 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); } I will explain eating food later. But you can read comments in the code.

This is a result code: using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace Snake { public partial class Form1 : Form { // Snake list of (x, y) positions private List<Point> _snake = new List<Point>() { new Point(10, 10) }; // Snake movement direction private Point _snakeDir = new Point(10, 0); // Food private Point _food = new Point(); // Random generator private Random _rnd = new Random(); // Game field size private int _fieldWidth = 200; private int _fieldHeight = 200; // Snake step private int _snakeStep = 10; public Form1() { InitializeComponent(); // Centers the form on the current screen CenterToScreen(); // Set a game field size ClientSize = new Size(_fieldWidth, _fieldHeight); // 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 Form1_Paint(object sender, PaintEventArgs e) { Draw(); } private void GameLoop(object sender, System.EventArgs e) { Update(); 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 Draw() { DrawFood(); DrawSnake(); } private void DrawSnake() { foreach (var cell in _snake) { DrawRect(cell.X, cell.Y, Color.Green); } } private void DrawFood() { DrawRect(_food.X, _food.Y, Color.OrangeRed); } private void DrawRect(int x, int y, Color color, int size = 10) { Graphics g = CreateGraphics(); SolidBrush brush = new SolidBrush(color); g.FillRectangle(brush, new Rectangle(x, y, size, size)); brush.Dispose(); g.Dispose(); } private void GenerateFood() { _food.X = 10 * _rnd.Next(0, _fieldHeight / 10 - 1); _food.Y = 10 * _rnd.Next(0, _fieldHeight / 10 - 1); } private void Form1_KeyPress(object sender, KeyPressEventArgs e) { switch (e.KeyChar) { case 'w': _snakeDir.X = 0; _snakeDir.Y = -_snakeStep; break; case 'a': _snakeDir.X = -_snakeStep; _snakeDir.Y = 0; break; case 's': _snakeDir.X = 0; _snakeDir.Y = _snakeStep; break; case 'd': _snakeDir.X = _snakeStep; _snakeDir.Y = 0; break; } } } }  

8Observer8

8Observer8

  • Advertisement
×

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!