Advertisement

Snake using SDL

Started by February 20, 2017 03:55 PM
9 comments, last by Oberon_Command 7 years, 9 months ago

Hi there i am a beginner in SDL and just wanted to make a snake clone

i have made it to full extent(not totally polished but the game runs)

and i have encountered a problem

my code moves the snake in all direction except the right in the first instance

if you can point me on how to edit this(i know i should use linked list and this is not the optimum way to write code)



#include<stdio.h>
#include<SDL.h>
#include<sstream>
#include<ctime>
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
const int screenWidth = 640;
const int screenHeight = 480;

bool init();
void close();

class food
{
private:
	int x=100, y=100, a, b;
	SDL_Rect foodBlock;
public:
	void render();
	void input(SDL_Event e);
	void changeLocation();
	SDL_Rect giveLocation();
}food1;

SDL_Rect food::giveLocation()
{	
	return foodBlock;
}

void food::changeLocation()
{
	x = rand() % screenWidth;
	y = rand() % screenHeight;
}
void food::render()
{
	foodBlock = { x,y,10,10 };
	SDL_SetRenderDrawColor(renderer, 255, 100,200, 255);
	SDL_RenderFillRect(renderer, &foodBlock);
	SDL_RenderDrawRect(renderer, &foodBlock);
}

class player
{
public:
	player();
	void input(SDL_Event e);
	void move(int d);
	void render();
	int getD();
	int getCount();
	void collision(int a);
private:
	int velX,velY,x,y,d=0;
	SDL_Rect snake[300] = {0,0,10,10};
	int direct=5;
	int old=5;
	int shit;
	int count=20;
	void reset();
//snake[0]will always be the head
}player1;


void player::collision(int a)
{
	int aXMin, aXMax, bXMin, bXMax,aYMin,aYMax,bYMin,bYMax, f=0;
	aXMin = snake[0].x;
	aXMax = snake[0].x + snake[0].w;
	aYMin = snake[0].y;
	aYMax = snake[0].y + snake[0].h;
	
	if (a == 1)
	{
		SDL_Rect b = food1.giveLocation();
		bXMin = b.x;
		bXMax = b.x + b.w;
		bYMin = b.y;
		bYMax = b.y + b.h;
		if (aYMax <= bYMin)
		{
			f = 1;
		}
		if (aYMin >= bYMax)
		{
			f = 1;
		}
		if (aXMax <= bXMin)
		{
			f = 1;
		}
		if (aXMin >= bXMax)
		{
			f = 1;
		}
		if (f != 1)
		{
			food1.changeLocation();
			player1.count++;
		}
	}
	else if (a == 2)
	{
		for (int i = 1; i < count;i++)
		{
			f = 0;
			//printf("%d\n",i);
			bXMin = snake[i].x;
			bXMax = snake[i].x + snake[i].w;
			bYMin = snake[i].y;
			bYMax = snake[i].y + snake[i].h;
			if (aYMax <= bYMin)
			{
				f = 1;
			}
			if (aYMin >= bYMax)
			{
				f = 1;
			}
			if (aXMax <= bXMin)
			{
				f = 1;
			}
			if (aXMin >= bXMax)
			{
				f = 1;
			}
			if (f != 1)
			{
				SDL_Delay(200);
				player1.reset();
				printf("inside");
			}
		}
		
	}

}
void player::reset()
{
	srand(time(NULL));
	x = rand()%screenWidth;
	y = rand()%screenHeight;
	velX = 10;
	velY = 10;
	d = 0;
	direct = 5;
	old = 5;
	count = 20;
	
}

int player::getCount()
{
	return count;
}

int player::getD()
{
	return d;
}
player::player()
{
	x = 400;
	y = 100;
	velX = 10;
	velY = 10;
	d = 0;
}
void player::move(int d)
{
	if(d==1)
	x += velX;
	else if(d==2)
	y += velY;
}

void player::render()
{
	int i = 0;
	int count = getCount();
	SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
	if (direct == 5)
	{
		for (i; i < count; i++)
		{
			SDL_Rect dest = { x + i * 10,y,10,10 };
			snake[i] = dest;
			SDL_RenderDrawRect(renderer, &snake[i]);
			SDL_RenderFillRect(renderer, &snake[i]);
		}
	}
	else if (direct == 0)
	{
		//snake[count] = {NULL,NULL,NULL,NULL};
		SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
		SDL_RenderClear(renderer);
		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);

		for (i = count; i > 0; i--)
		{
			snake[i] = snake[i - 1];
		}
		snake[0].y = snake[0].y + velY;//-velY
		for (i; i < count; i++)
		{
			SDL_RenderDrawRect(renderer, &snake[i]);
			SDL_RenderFillRect(renderer, &snake[i]);
			//SDL_Delay(50);
		}
	}


	else if (direct == 1)
	{
		//snake[count] = {NULL,NULL,NULL,NULL};
		SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
		SDL_RenderClear(renderer);
		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
		
		for (i = count; i > 0; i--)
		{
			snake[i] = snake[i-1];
		}
		snake[0].y = snake[0].y + velY;
		for (i; i < count; i++)
		{
			SDL_RenderDrawRect(renderer, &snake[i]);
			SDL_RenderFillRect(renderer, &snake[i]);
			//SDL_Delay(50);
		}
	}

	else if (direct == 2)
	{
		SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
		SDL_RenderClear(renderer);
		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);

		for (i = count; i > 0; i--)
		{
			snake[i] = snake[i - 1];
		}
		snake[0].x = snake[0].x + velX;//-velX
		for (i; i< count; i++)
		{
			SDL_RenderDrawRect(renderer, &snake[i]);
			SDL_RenderFillRect(renderer, &snake[i]);
			//SDL_Delay(50);
		}
	}
	else if (direct == 3)
	{
		SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
		SDL_RenderClear(renderer);
		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);

		for (i = count; i > 0; i--)
		{
			snake[i] = snake[i - 1];
		}
		snake[0].x = snake[0].x + velX;
		for (i; i< count; i++)
		{
			SDL_RenderDrawRect(renderer, &snake[i]);
			SDL_RenderFillRect(renderer, &snake[i]);
			//SDL_Delay(50);
		}
	}
	if (snake[0].x<-5 || snake[0].x>screenWidth)
	{
		SDL_Delay(100);
		player1.reset();
		
	}
	else if (snake[0].y<-5 || snake[0].y>screenHeight)
	{
		SDL_Delay(100);
		player1.reset();
		
	}
	
	player1.collision(2);
}

void player::input(SDL_Event e)
{
	if (e.type == SDL_KEYDOWN && e.key.repeat == 0)
	{
		switch (e.key.keysym.sym)
		{
		case SDLK_DOWN:
			
			if (old != 0)
			{
				old = 1;
				direct = 1;
				velY = abs(velY);
			}
			break;
		case SDLK_UP:
		
			if (old != 1)
			{
				direct = 0;
				old = 0;
				velY = -abs(velY);
			}
			break;
		case SDLK_RIGHT:
			
			if (old != 2)
			{
				old = 3;
				direct = 3;
				velX = abs(velX);
			}
			break;
		case SDLK_LEFT:
			
			
			if (old != 3)
			{
				direct = 2;
				old = 2;
				velX = -abs(velX);
			}
			break;
		case SDLK_1:
			count++;
			break;
		case SDLK_2:
			count--;
			break;
		case SDLK_r:
			player1.reset();
			player1.render();
			break;
		
		}
	}
}

bool init()
{
	bool success = true;
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		printf("error");
		success = false;
	}
	else
	{
		window = SDL_CreateWindow("SNAKE", 100, 100, screenWidth, screenHeight, SDL_WINDOW_SHOWN);
		if (window == NULL)
		{
			printf("error");
			success = false;
		}
		else
		{
			renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
			if (renderer == NULL)
			{
				printf("Error");
				success = false;
			}
		}
	}
	return success;
}
void close()
{
	SDL_DestroyWindow(window);
	SDL_DestroyRenderer(renderer);
	window = NULL;
	renderer = NULL;
}

int main(int argc, char* args[])
{
	if (!init())
	{
		printf("error");
	}
	else
	{
		SDL_Event e;
		bool quit = false;
		while (!quit)
		{
			SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
			SDL_RenderClear(renderer);
			if (SDL_PollEvent(&e) != 0)
			{
				if (e.type == SDL_QUIT || e.key.keysym.sym==SDLK_ESCAPE)
					quit = true;
				else if (e.type == SDL_KEYDOWN)
					player1.input(e);
				else if (e.key.keysym.sym == SDLK_f)
					food1.changeLocation();
			}
			int d = player1.getD();
			player1.move(d);
			player1.render();
			player1.collision(2);
			player1.collision(1);
			//player1.collision(2);//for food int a =1 and for snake int a =2
			food1.render();
			SDL_Delay(25);
			SDL_RenderPresent(renderer);
		}
	}
	return 0;
	close();
}
I am sorry for writing a crappy code
just nudge me in the right direction and don't flame me please!
thanks in advance!

Please use the code tags when posting code here, it makes it a lot easier to read.

In the editor, it's the symbol that looks like this:

<>

Because of that I haven't read thoroughly through all your code, but some comments:

You seem to be using a lot of magic numbers. collision(int a), for example. I would suggest using better names for variables so it's easier to see at a glance what things are, and you might also want to consider using enums instead of just passing various numbers around.

Additionally, I would probably just use a std::vector instead of any form of linked list, but that's just me.

Hello to all my stalkers.

Advertisement

Refactor. Here's your nudge.

Refactor. Here's your nudge.

PERFECT NUDGE
THANKS A LOT!!!!!!!!!


Please use the code tags when posting code here, it makes it a lot easier to read.

In the editor, it's the symbol that looks like this:

<>

Because of that I haven't read thoroughly through all your code, but some comments:

You seem to be using a lot of magic numbers. collision(int a), for example. I would suggest using better names for variables so it's easier to see at a glance what things are, and you might also want to consider using enums instead of just passing various numbers around.

Additionally, I would probably just use a std::vector instead of any form of linked list, but that's just me.

I know my naming schemes are horrible i am trying to improve them

i sorta right them in the flow and dont think about that while writing its a seriously bad habit and will try to improve them !

thanks for the valuable and great advice

will write it in an improved way

might just post here after improving

and thanks a lot! :)


another thing

is making data members of a class public a bad habit(like snakes x and y coordinates or any other variables?)????

Generally, yes.

Advertisement

Generally, yes.

Just to add to this, it's best to make class variables private unless they need to be public for some reason.

Getters and setters are the way these variables are handled, usually.

For a private variable int X of class myClass, here is a sample getter and setter:


int myClass::getX()
{
    return X;
}

void myClass::setX(int newVal)
{
    X = newVal;
}

The functions getX() and setX() are public. This reduces the chance of accidentally changing values when they shouldn't be touched.

Generally, yes.


Just to add to this, it's best to make class variables private unless they need to be public for some reason.
Getters and setters are the way these variables are handled, usually.
For a private variable int X of class myClass, here is a sample getter and setter:
int myClass::getX()
{
    return X;
}

void myClass::setX(int newVal)
{
    X = newVal;
}
The functions getX() and setX() are public. This reduces the chance of accidentally changing values when they shouldn't be touched.


This being said, writing getters and setters like this is often a code smell. The purpose of encapsulation is to enforce design contracts. If you have a private member variable that's hidden behind get and set functions that do nothing but get and set that variable, you have effectively made that variable public in a complicated way - there isn't any kind of design contract being enforced by the get/set functions, so you should either rethink making the variable private or rethink the design that led to you needing both the get and the set. On classes that aren't just aggregations of data, preferably the latter first.

This being said, writing getters and setters like this is often a code smell. The purpose of encapsulation is to enforce design contracts. If you have a private member variable that's hidden behind get and set functions that do nothing but get and set that variable, you have effectively made that variable public in a complicated way - there isn't any kind of design contract being enforced by the get/set functions, so you should either rethink making the variable private or rethink the design that led to you needing both the get and the set. On classes that aren't just aggregations of data, preferably the latter first.


This is something that has always confused me and is the biggest stumbling block for my adoption of OOP. It's been said that, for example, the player class has no need to call the physics or rendering engines, and yet, both of those require player data, like position. So, player->render() is bad OOP, but so it renderer->render(player). In order for the latter to work, the data from the player class would need to be retrieved by the render member function via getters.

It's pretty simple really - the idea of encapsulation is to enforce a contract, where the interface between objects is only what is necessary for them to perform their task, and everything else is handled internally.

If rendering needs a player position, it can ask for it via a getter on the Player. That's fine. But there's no need to add a setter as well because the renderer shouldn't be changing a player's position. Reporting its own position is part of the Player's interface. Letting other people set it is not. Provide only what is needed, no more.

This topic is closed to new replies.

Advertisement