How can I structure my code ?

Started by
16 comments, last by alx119 7 years, 1 month ago

So I have my code:

#include <iostream>
 
//GLEW
#define GLEW_STATIC
#include<glew.h>
 
//GLFW
#include <glfw3.h>
#include <SOIL.h>
#include "Shader.h"
// GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <vector>>
 
 
// Functions prototype
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void checkMovement();
bool checkCollision(glm::vec3 &fruitPos, glm::vec3 &headPos);
void growSnake();
void update();
GLint bodyLength = 0;
 
// Window Dimensions
const GLuint screenWidth = 1000, screenHeight = 800;
 
bool keys[1024];
 
 
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
 
 
 
bool up = true;
bool left = false;
bool right = false;
bool down = false;
 
glm::vec3 cameraPos(0.0f, 0.0f, 6.0f);
 
GLfloat sensitivity = 0.1f;
glm::vec3 headPos(0.0f, 0.4f, 0.0f);
glm::vec3 fruitPos(1.0f, 1.0f, 0.0f);
glm::vec3 bodyPos[7];
 
//glm::vec3 pos(0.0f, 0.4f, 0.0f);
 
const GLfloat SNAKE_TIME_DURATION = 0.3f;
 
GLfloat next_snake_time = SNAKE_TIME_DURATION;
 
 
int main()
{
std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl;
 
// GLFW init
glfwInit();
// Set all the required options for GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
 
// Create a GLFWWindow object that we can use for GLFW's functions
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "Snake OpenGL", nullptr, nullptr);
if (window == nullptr)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
}
glfwMakeContextCurrent(window);
 
// Set the required callback functions
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
 
// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
glewExperimental = GL_TRUE;
glewInit();
 
// Initialize GLEW to setup the OpenGL Function pointers
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW" << std::endl;
return -1;
}
int width, height;
 
glfwGetFramebufferSize(window, &width, &height);
 
// Define the viewport dimensions
glViewport(0, 0, screenWidth, screenHeight);
 
Shader headShader("headShader.vs", "headShader.frag");
Shader groundShader("headShader.vs", "groundShader.frag");
Shader fruitShader("headShader.vs", "fruitShader.frag");
Shader bodyShader("headShader.vs", "fruitShader.frag");
 
GLfloat vertices[] = {
0.5f,  0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
-0.5f,  0.5f, 0.0f, 0.0f, 1.0f
};
 
GLuint indices[] = {
0, 1, 3,
1, 2, 3
 
};
 
 
GLuint headTexture, groundTexture;
GLuint VBO, HEAD, EBO, GROUND, FRUIT, BODY[7];
 
glGenVertexArrays(1, &HEAD);
glGenVertexArrays(1, &GROUND);
glGenVertexArrays(1, &FRUIT);
glGenVertexArrays(7, BODY);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
 
// HEAD VAO
glBindVertexArray(HEAD);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Set the vertex attrib pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// Set the texture attrib pointers
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
 
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
 
 
// Head texture========================================
glGenTextures(1, &headTexture);
glBindTexture(GL_TEXTURE_2D, headTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture wrapping to GL_REPEAT
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// Set texture filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int imgWidth, imgHeight;
unsigned char* image = SOIL_load_image("images/metal.png", &imgWidth, &imgHeight, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);
 
 
//GROUND VAO============================================================
glBindVertexArray(GROUND);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Set the vertex attrib pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// Set the texture attrib pointers
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
 
 
// Ground texture==================================================
glGenTextures(1, &groundTexture);
glBindTexture(GL_TEXTURE_2D, groundTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture wrapping to GL_REPEAT
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// Set texture filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
image = SOIL_load_image("images/ground.jpg", &imgWidth, &imgHeight, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);
 
 
// FRUIT VAO
glBindVertexArray(FRUIT);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Set the vertex attrib pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
 
for(int i = 0; i < 7; i++)
{
glBindVertexArray(BODY[i]);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Set the vertex attrib pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}
// Game Loop
while (!glfwWindowShouldClose(window))
{
 
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
 
//Check if any events have been activated(key pressed, mouse moved etc.) and call corresponding response functions
glfwPollEvents();
GLfloat now = glfwGetTime();
if (now >= next_snake_time)
{
checkMovement();
next_snake_time = now + SNAKE_TIME_DURATION;
}
 
if (checkCollision(fruitPos, headPos))
{
growSnake();
update();
}
// Render
// Clear the color buffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
 
 
// Ground SHADER PROGRAM
groundShader.Use();
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;
 
view = glm::lookAt(cameraPos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
proj = glm::perspective(45.0f, (float)screenWidth / (float)screenHeight, 0.1f, 100.0f);
model = glm::translate(model, glm::vec3(0.0f, 0.0f, 3.0f));
model = glm::scale(model, glm::vec3(4.0f, 4.0f, 4.0f));
GLint modelLoc = glGetUniformLocation(headShader.Program, "model");
GLint viewLoc = glGetUniformLocation(headShader.Program, "view");
GLint projLoc = glGetUniformLocation(headShader.Program, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
 
glBindTexture(GL_TEXTURE_2D, groundTexture);
glUniform1i(glGetUniformLocation(groundShader.Program, "groundTexture"), 0);
glBindVertexArray(GROUND);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
 
 
//FRUIT SHADER PROGRAM
fruitShader.Use();
 
model = glm::mat4();
model = glm::translate(model, glm::vec3(fruitPos.x, fruitPos.y, 0.0f));
model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));
modelLoc = glGetUniformLocation(headShader.Program, "model");
viewLoc = glGetUniformLocation(headShader.Program, "view");
projLoc = glGetUniformLocation(headShader.Program, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
 
glBindVertexArray(FRUIT);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
 
 
//HEAD SHADER PROGRAM
headShader.Use();
 
// MODEL MATRIX
model = glm::mat4();
model = glm::translate(model, glm::vec3(headPos.x, headPos.y, 0.0f));
model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));
modelLoc = glGetUniformLocation(headShader.Program, "model");
viewLoc = glGetUniformLocation(headShader.Program, "view");
projLoc = glGetUniformLocation(headShader.Program, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
 
glBindTexture(GL_TEXTURE_2D, headTexture);
glUniform1i(glGetUniformLocation(headShader.Program, "ourTexture"), 0);
glBindVertexArray(HEAD);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
 
 
// BODY SHADER PROGRAM
bodyShader.Use();
for(int i = 0; i < bodyLength; i++)
{
model = glm::mat4();
model = glm::translate(model, glm::vec3(bodyPos[i].x, bodyPos[i].y, 0.0f));
model = glm::scale(model, glm::vec3(0.25f, 0.25f, 0.25f));
modelLoc = glGetUniformLocation(bodyShader.Program, "model");
viewLoc = glGetUniformLocation(bodyShader.Program, "view");
projLoc = glGetUniformLocation(bodyShader.Program, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
 
glBindVertexArray(BODY[i]);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
 
}
 
 
glfwSwapBuffers(window);
}
glDeleteVertexArrays(1, &HEAD);
glDeleteVertexArrays(1, &GROUND);
glDeleteVertexArrays(1, &FRUIT);
glDeleteVertexArrays(7, BODY);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
 
glfwTerminate();
return 0;
}
 
 
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mode)
{
 
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
 
/* if (key >= 0 && key < 1024)
{
if (action == GLFW_PRESS)
keys[key] = true;
else if (action == GLFW_RELEASE)
keys[key] = false;
}
*/
if (key == GLFW_KEY_W && action == GLFW_PRESS)
{
up = true;
down = false;
left = false;
right = false;
}
if (key == GLFW_KEY_S && action == GLFW_PRESS)
{
up = false;
down = true;
left = false;
right = false;
}
if (key == GLFW_KEY_D && action == GLFW_PRESS)
{
up = false;
down = false;
left = false;
right = true;
}
if (key == GLFW_KEY_A && action == GLFW_PRESS)
{
up = false;
down = false;
left = true;
right = false;
}
 
 
}
 
void checkMovement()
{
 
if (up)
{
 
update();
headPos.y += sensitivity;
}
if (down)
{
update();
headPos.y -= sensitivity;
}
if (right)
{
update();
headPos.x += sensitivity;
 
}
if (left)
{
 
update();
headPos.x -= sensitivity;
}
 
std::cout << std::endl;
}
 
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
 
}
 
bool checkCollision(glm::vec3 &fruitPos, glm::vec3 &headPos)
{
if ((GLint)(headPos.x) == (GLint)(fruitPos.x) && (GLint)(headPos.y) == (GLint)(fruitPos.y))
{
// growSnake();
fruitPos.x++;
std::cout << "fruitPos.X: " << fruitPos.x << std::endl;
fruitPos.y++;
std::cout << "fruitPos.Y: " << fruitPos.y << std::endl;
std::cout << std::endl;
return true;
}
return false;
}
 
void growSnake()
{
bodyLength++;
}
 
void update()
{
for(int i = bodyLength - 1; i >= 0; i--)
{
if (i == 0)
{
bodyPos[i] = headPos;
}
else {
bodyPos[i] = bodyPos[i - 1];
}
}
}

How can I structure my code(the buffers, the rendering, the updating etc.) using classes ?Can somebody give me an advice ?
P.S; I'm beginner in OOP (I know the syntax, but I didn't practice working with classes, objects etc. so I need something easy).

Thank you!

Advertisement

OOP is about "smart" data, ie piece of data that can also do something or answer a query.

At first sight, it seems the stuff attached to eg HEAD can all be wrapped into some "create", "use" (or "render"), and a "delete"-like method.

Similar things can be done to the other VAOs.

Once you have that, it looks like all these classes are mostly if not completely the same, perhaps you can re-use some of the classes for more than a single purpose?

Hi again Alberth, :D

Well, with your post, you gave me an idea.I will come back with the code for another review. :D

Thank you!

So I tried something...I don't know how good is, so I need your opinion. :)
Buffer.cpp:


#include "Buffer.h"

Buffer::Buffer(GLuint nrOfBuffers)
{
m_nrOfBuffers = nrOfBuffers;
}
Buffer::~Buffer()
{
    glDeleteVertexArrays(m_nrOfBuffers, &this->m_VAO);
}
 
void Buffer::InitBuffer()
{
   GLfloat vertices[] = {
   0.5f,  0.5f,   1.0f, 1.0f,
   0.5f, -0.5f,   1.0f, 0.0f,
   -0.5f, -0.5f,  0.0f, 0.0f,
   -0.5f,  0.5f,  0.0f, 1.0f
};
 
GLuint indices[] = {
    0, 1, 3,
    1, 2, 3
 };
 
   GLuint VBO;
   GLuint EBO;
   glGenVertexArrays(m_nrOfBuffers, &this->m_VAO);
   glGenBuffers(m_nrOfBuffers, &VBO);
   glGenBuffers(m_nrOfBuffers, &EBO);
   glBindVertexArray(this->m_VAO);
   glBindBuffer(GL_ARRAY_BUFFER, VBO);
   glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
   glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
   // Set the vertex attrib pointers
   glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
   glEnableVertexAttribArray(0);
   // Set the texture attrib pointers
   glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
   glEnableVertexAttribArray(1);
   glBindBuffer(GL_ARRAY_BUFFER, 0);
   glBindVertexArray(0);
}

Texture.cpp:


#include "Texture.h"
Texture::Texture(GLuint nrOfTextures)
{
   m_nrOfTextures = nrOfTextures;
}
 
Texture::~Texture()
{
}
 
void Texture::InitTexture(char* imgName)
{
   glGenTextures(m_nrOfTextures, &this->m_texture);
   glBindTexture(GL_TEXTURE_2D, m_texture);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture wrapping to GL_REPEAT
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   // Set texture filtering
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   int imgWidth, imgHeight;
   unsigned char* image = SOIL_load_image(imgName, &imgWidth, &imgHeight, 0, SOIL_LOAD_RGB);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
   glGenerateMipmap(GL_TEXTURE_2D);
   SOIL_free_image_data(image);
   glBindTexture(GL_TEXTURE_2D, 0);
}

Game.cpp:


#include "Game.h"
Game::Game() 
{
 
}
 
Game::~Game()
{
}
 
void Game::Init()
{
   head->InitBuffer();
   ground->InitBuffer();
   fruit->InitBuffer();
   body->InitBuffer();
   groundTexture->InitTexture("images/ground2.jpg");
   headTexture->InitTexture("images/metal.png");
}

This is for the moment.Can't wait to see if it is ok for a beginner. :D
Next I will make the inputs, update and render.

Seems like a right direction, please try some more :)

When you say, "try some more" what do you mean ? :D to make this code better, or to continue with what I didn't do yet, that is: process input, render, update ? :)

EDIT: I changed in the Game.cpp: body->InitBuffer(); (because I had an error) with:
for(Buffer *element = body; element < body + 7; ++element )
{
element->InitBuffer();
}

But now when I run the application I have another error: SnakeOpenGL has stopped working. (The debugger shows me an error when I swap the buffers). I don't know...it could might be from the Texture.cpp when I send the texture's name ?:
void Texture::InitTexture(char* imgName)
{
  glGenTextures(m_nrOfTextures, &this->m_texture);
  glBindTexture(GL_TEXTURE_2D, m_texture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture wrapping to GL_REPEAT
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  // Set texture filtering
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  int imgWidth, imgHeight;
  unsigned char* image = SOIL_load_image(imgName, &imgWidth, &imgHeight, 0, SOIL_LOAD_RGB);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imgWidth, imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
  glGenerateMipmap(GL_TEXTURE_2D);
  SOIL_free_image_data(image);
  glBindTexture(GL_TEXTURE_2D, 0);
}

Some things are objects, other things are not objects. Rendering and updating feel like functions, they don't feel like objects to me.

Now, update and render could be functions on a Game object, if that makes sense. It can make it easier to test these functions, as some of your global variables could be made local to this object - global variables make testing difficult as they may need to be reset between test cases. In some cases global objects maybe unavoidable, but minimising them is generally considered to be a good idea.

In some cases, things which aren't naturally "objects" may be modelled as such for implementation reasons, such as to avoid globals. For example, there are several input related variables, these could be wrapped up into an object. There are other ways to handle your current global variables, the input and rendering variables could be wrapped up in a simple value type (generally modelled as a "struct", rather than a "class"). This is a stylistic choice, there isn't a "right" way to do it.

Another potential object I see in your code is the Snake itself.

I was suggesting to do what you planned doing next, ie "process input, render, update".

In general, it's your code, so it's your call. I merely suggest things, which hopefully make sense in some cases.

If you make a backup copy before doing major rework, you have a safety net to fallback to, in case you make a mess. I use a VCS (version control system) as safety net, but zipping all source code in an archive works too.

Programming is a lot about doing things for the first time, figuring out how things work. Making a mess and learning from it happens often, even for experienced programmers.

Have a safety in place, and feel free to experiment with refactoring. It's one of the best ways to learn what works and doesn't work, for you.

I'd definitely recommend using version control. Once you learn it, you won't want to go back. Git is very popular, though perhaps not particularly friendly for beginners. As a solo developer however, the basic commands you need aren't very difficult to learn.

Thank you guys for reply.
@rip-off: I didn't want to make the process input, update and rendering as classes :P .Anyway I have a few things in my mind about how can I structure the code.I could use a GameObject class for snake and fruit.

I will take your advices and use a version control system.I will come back when I will do more.
Thank you again!

This topic is closed to new replies.

Advertisement