Hello all,
Im trying to develop a simple 2 player snake game. Everything so far so good. Im only having troubles with my collision system. Maybe somebody can help me with this?
Im using Allegro 4.9.9.2 because 5 doesnt seem to like my Dev-C++ very much.
This is my collision function:
bool Snake::collision() { for(int i=0; i<100;i++){ if((x+1)==xPrevious[i] && y==yPrevious[i-1]){ return false; } } }
and my files
http://www.2shared.com/file/fHg8Hli4/Try_2.html
The collision code is in snake.cpp.
with real kind regards, Elmar.
Sliding text?
When asking for help, always say what the problem actually is. Is it not compiling? Is it crashing? Is it producing undesired results? If so, what are the desired results? Help people help you by telling them what to look for.
When posting code, put it in <code></code> tags. Use the edit button to add them.
Ok sorry!
and yeah the code tags, i was trying to use them but i used [code][/code].
It is perfectly fine running. Not crashing nor problems with compiling.
However once ingame, i cant move any direction using this code:
void Snake::moveRight() { if(!collision()){ if(x>610) x=10; toPrevious(); x+=10; } else textout_ex( screen, font, "Me gusta Collision Y", 320, 240, makecol(0, 0, 255), makecol(0, 0, 0)); }
So it detects a collision even though there is none!
bool Snake::collision() { for(int i=0; i<100;i++){ if((x+1)==xPrevious[i] && y==yPrevious[i-1]){ return false; } } }
Think about what you are doing - you are checking if x plus one is the same as any of the last 100 x values and the current y is the same as the yPrevious value one before the current (i) yPrevious value. Does that make sense?
Try storing your snake values in a std::list :
Well to me it makes a lot of sense to be honest. if it wants to go up, it will check first if the above position is equal to one of the previous positions.
And with your code, Thankx. But i have no clue how to use this, i have added it to my snake, but the list doesnt have a type, and when making it int it doesnt work. (Im unknown with lists). Also where to call for update snake? where does head.x and head.y reffer to? and the body.empty and body.grow are listfunctions i assume?
And then the iterator, The iterator is unknown. And it is unable to process It.
thanks for your help until now . But im still a bit stuck with this problem.
Well to me it makes a lot of sense to be honest. if it wants to go up, it will check first if the above position is equal to one of the previous positions.
But you're still only checking if it moves up, and you don't know if it has. And, you're not checking the right index for the previous Y value (You should use i, not i - 1).
And with your code, Thankx. But i have no clue how to use this, i have added it to my snake, but the list doesnt have a type, and when making it int it doesnt work. (Im unknown with lists). Also where to call for update snake? where does head.x and head.y reffer to? and the body.empty and body.grow are listfunctions i assume?
And then the iterator, The iterator is unknown. And it is unable to process It.
That's okay, I'll walk you through it.
A list is a C++ container template defined in the system header list and contained in the std namespace. This means to use it you will need to :
#include <list> // include the header that defines list using std::list;// tell the compiler we will be using list to refer to std::list
To use a template, you need to tell it what type it will be using.
list<segment> body;// declares a list of segment objects named body
To iterate over a list, you need to use a list<TYPE>::iterator. iterators are just an efficient way to move back and forth over the objects contained in the list (or more generally, container).
For more information on the Standard Template Library containers (like list), take a look here :
http://www.sgi.com/tech/stl/index.html
And I actually made a typo. body.grow() doesn't exist - it is not a member of the list class. What I meant was that if the snake will grow on this update, then you should not erase the tail of the snake (which is stored at the beginning of the list). So, something more like this :
--growcounter; bool grow = (growcounter == 0); if (growcounter == 0) {growcounter = time_between_growth;} if (!body.empty() && !grow) { body.erase(body.begin()); }
If you have any more questions or can't get it to work, post your errors here, and your questions too.
Im tying to implent the code(and having a hard time with it) But doesnt the segment structure have to be declared in the snake class aswell? or can i just declare it wherever i want?
Edit:
Nvm i found it out.
However the iteration methods arent totally clear to me, Where do i have to use them? i see that one is the collision check?
The segment structure can be declared anywhere before a class or function that uses it. This means it can go in a separate header, or in the same header as the one for your snake class, before you declare it.
Be specific with your problems.
// list iteration method one list<segment>::iterator it = body.begin();// The list::begin() method returns an iterator pointing to the beginning of the list. It may return the same value as list::end(). while (it != body.end()) { segment& s = *it;// iterators point to the elements stored in the list printf("s.x = %i , s.y = %i\n" , s.x , s.y); ++it;// move the iterator to point to the next element } // list iteration method two for (list<segment>::iterator it = body.begin() ; it != body.end() ; ++it) { segment& s = *it;// using a reference allows us to modify the object stored in the list if we want to printf("s.x = %i , s.y = %i\n" , s.x , s.y); }
Im getting what this code is doing (only the printf function, u suppose this is like cout?) But have no clue where to use it. And your last piece of code (with the growcounter) i suppose this is my grow() function?
If you don't know what printf does, you need to get yourself a C/C++ reference guide to look at. This one might be okay :
http://www.cplusplus.com/
http://www.cplusplus.com/reference/clibrary/
You'll want to iterate over the list when you check each segment in the body list against the head segment to find out if the snake ran over itself and died.
I'd just like to say that 4.9.9.2 IS allegro 5, just a WIP/Alpha version.
And if you're having problems with the 5.0.x releases, don't hesitate to ask for help on the installation forum.
@Thomas,
Yeah, i could probably find the problem with google. but if it aint broken, dont fix it! And this allegro is working perfectly fine for me, and has all the functions i need
OT,
First of all, thanks for the help .
second off all:
I have no clue how to declare the grow member
But still it says that class std::list<........> 'has no member named 'grow';
Also ive added my project again, maybe somebody can see what im doing wrong there?
You don't need a separate grow function, and the growth variables should be in your snake class, not your segment class. To combine the examples I gave would give you :
void Snake::UpdateSnake() { --growcounter; bool grow = (growcounter == 0); if (growcounter == 0) {growcounter = time_between_growth;} if (!body.empty() && !grow) { body.erase(body.begin()); } body.push_back(head); head.x += xdir; head.y += ydir; dead = CheckCollision(); }
growcounter and time_between_growth are both members of the snake class.
Ah oh ok, thx.
And the xdir and ydir are my snake x and y values rght?
xdir and ydir are the x and y directions, or velocities, which I assume are 1, or however wide and tall a square or segment is. Only call Snake::Update when you want your snake to move (ie, not on every frame). Better yet, rename Update to Move, and make a new Update function :
void Snake::Update() { --update_counter; if (update_counter == 0) { Move(); update_counter = time_til_next_update; } }
I have no clue on how to do it anymore :s.
So what what you gave me:
An update function with variables update_counter and a set value time_til_next_update, I should declare this int's in my class i suppose.
This part of code i get, so my time till next update variable is basicly the speed if im correct?
Then the updateSnake, this one i dont get. It sets the tail values, right?
but i dont really get the grow (boolean) and the growCounter.
And how to use these functions. I think i should use the Update() function in my main, which will use the renamed Update_Snake (Move() ) function. But when do i have to use my move function then?
void Snake::movePlayer() { clear_keybuf(); if (key[KEY_UP]) moveUp(); else if (key[KEY_DOWN]) moveDown(); else if (key[KEY_RIGHT])moveRight(); else if (key[KEY_LEFT])moveLeft(); Draw(); }
Thanks for keeping so patient, and helping me so much! Much appreciated!
That way, the direction will be set until the next time someone presses a key. You don't have to move right away when they press a key. It's up to you though. If you want them to move whenever you press a key, just add in Move() to the if clauses there after setting the direction.
while (!quit) { while (ticks < 1) {rest(1);} snake.CheckInputs(); snake.Update(); snake.Display(); }
Owh btw, how can i do the drawing for the tail? just by reading out all the segments? (so in a for loop, like i did for the array tail)
Just iterate over them, like I showed you earlier.
Yeah, its the same idea .
Now i reworked my whole code, no errors. runs. But i cant seem to be able to move the snakes head.
Ive used your exact pieces, using this for the drawing:
textout_ex( buffer, font, "@", head.x, head.y, makecol( 255, 0, 0), makecol( 0, 0, 0) );
and in the constructor(int x, int y):
head.x=x; head.y=y;
So it draws the initial position of the head. But does not change when pressing a key! =/
my main (i know using rest is not very good, ill change that later on).
Then there is something wrong with CheckInputs or Update. Post both of them as they are now.
It doesnt draw anything anymore now =/
Here it is .
I cant seem to attach the file :s.
And its not drawing no
First problem - your Snake::Snake(int,int) constructor doesn't initialize all it's values. This means update_counter,xdir,ydir,growcounter may all be zero, but they aren't guaranteed to be. So when you hit these lines the counter variables subtract endlessly and are never equal to zero :
You still need to add in drawing the body to Snake::Draw as well.
AH that was my problem, totally forgot about that. Also i tried to make them all 0, but that wouldnt work either. But i forgot that it FIRST substracts from the value, and only after that checks. Hence my problem.
And about the tail, i know that i didnt have it implented yet, i wanted to make the movement work first .
So now the tail!
Ok the tail works great, my only problem is the collision checking,
i use
while(snake.Dead() == false){ the functions to run the game are here } return 0;
where
bool Snake::Dead(){ return dead }
However i die instantly!
EDIT:
Nvm, i figured it out. Played a bit with the code, came to an understanding that the snakes head is a structure as well. And made a startup timer.
So now the engine is done. Thank you really much! Youve helped me develop a simple snake game, and gave me a base understanding of how iterator work.
Still, even though this is done, why didnt my old code work? (other then the fact i used +1 on the wrong location)
Kind regards, ELmar
Well, for one thing, you didn't keep track of how many segments were currently in the body, you checked against all 100 positions in the array regardless of current size. I never saw any more of your older code, so I couldn't say why it didn't work.
The rest is in my first post.
I know, i only checked it for 100 segments, but i could've changed that (for example by making a bigger array and copying all the positions in that). Or i could've made it with a vector.
EDIT
Also i have a question about the structure declared in the class. So i want to make it multiplayer for example. Then ill have to check if the player 2 head x and y coordinates dont touch that of player 1. THat is no problem. But then ill need another for statement witht the iterator, and have no clue how to do this.
This is what i had in mind:
I know the variables are private, but wouldnt it help if i made them public? that way i can easier check for a collision between tail and the head of another snake.
bool CheckForSnakeCollision(const Snake& s1 , const Snake& s2) { // check head one against body two // check head two against body one }
You don't need to make anything public or use a friend declaration either, but you could :
class Snake { // ... friend CheckForSnakeCollision(const Snake& s1 , const Snake& s2); };
Or you can use getter variables that return const references :
class Snake { // ... const list<segment>& Body() {return body;} const segment& Head() {return head;} };
hmm, the friend one i dont get really. so it grants a class the same permissions as another class? But why would i want that in this case?
The getter,
Yeah that basicly, and then use the collision check i stated in the previous post?
and the first option, this one is not really clear to me. Ill try to play around with it.
Also, isnt this very inefficient? checking for 2 lists if there are any collisions?
hmm, the friend one i dont get really. so it grants a class the same permissions as another class? But why would i want that in this case?
You can either friend a function prototype which gives it access to private members of the class, or you can friend another class, doing the same thing.
The getter,
Yeah that basicly, and then use the collision check i stated in the previous post?
and the first option, this one is not really clear to me. Ill try to play around with it.
Use Snake::Body() and Snake::Head() on opposing snakes, and iterate over each body segment, checking it against the head of the other snake.
Also, isnt this very inefficient? checking for 2 lists if there are any collisions?
Imagine your snakes are 1000 segments long, and you only check each time a snake moves, that is only (60/ticks_per_move)*1000 checks every second. If you moved on every frame, that would be 60000*2 checks, or 120000. Computers are capable of millions of calculations per second, so don't worry about things like that yet. Only look into them if they are actually causing you problems. If you're really worried, then use a vector instead of a list. That gives you direct access instead of indirect access, but it also makes adding and removing segments take longer (which is still probably the least of your worries). In short, don't worry about it.
Haha, ok so the calculations should be just fine .
Also for the checking part, i dont really see how i can read the values of the structure. Ive added the following functions (as you suggested).
const list<segment>& Body() {return body;} const segment& Head() {return head;}
Now to retrieve the values of the following code:
bool CheckForSnakeCollision(const Snake& s1 , const Snake& s2) { for (s1.list<segment>::iterator it = s1.body.begin() ; it != s1.body.end() ; ++it) { if ((it->x == s2.get_x) && (it->y == s2.get_y)) { return true;} } return false; } //where get_y and get_x: int Snake::get_x() { return head.x } //same goes for get_y, but then returning head.y
But obviously this wont work. I cant seem to find a way to call the first part of the for loop (The iterator part) from another object. Should this be done by getters as well???
I forgot you need to make the getters 'const' :
And you'll still need to check each snake head vs its own body as well.
Nice!
I have only but 2 questions, its about the functions and the use of objects.
Say ive made the following function:
void showCollision(const Snake& p1, const Snake& p2, const Snake& p3, const Snake& p4, Setup S1) { if(SnakeVsSnake(p1,p2)==1){ S1.DrawText("Player 2 hit player 1"); p2.stopMoving(); } else if(SnakeVsSnake(p1,p2)==2){ S1.DrawText("Player 1 hit player 2"); p1.stopMoving(); } }
Now this works fine! for 1 thing, i cant use p2/p1.stopMoving. This is because we are using a pointer here were we dont need that(right?) and thats also why it IS working for the S1.DrawText(const *char).
However, i only know 1 way to fix this, namely:
void showCollision(const Snake& p1, Snake P1, const Snake& p2, Snake P2, const Snake& p3, Snake P3, const Snake& p4, Sake P4, Setup S1) { if(SnakeVsSnake(p1,p2)==1){ S1.DrawText("Player 2 hit player 1"); P2.stopMoving(); } else if(SnakeVsSnake(p1,p2)==2){ S1.DrawText("Player 1 hit player 2"); P1.stopMoving(); } }
However then we get a code with 9 arguments! where i think only 5 are needed(at max)is there a way to go from const& p1 to p1?
also the error it gives:
main.cpp passing `const Snake' as `this' argument of `bool Snake::stopMoving()' discards qualifiers
If you need to modify a snake object (by using Snake::StopMoving()) then you should not be using a 'const Snake&', but a 'Snake&' only. That way you can still modify it. The warning came up because you are trying to call a non-const member function on a const Snake object.
To simplify code, you can use an array (this example uses a modified SnakeVsSnake called SnakeHitSnake that only checks if snake 1 hit snake 2 but not the other way around) :
void ShowCollision(....) { Snake& snakes[4] = {p1 , p2 , p3 , p4};// Or use a Snake* [4]. const char* text[4] = {"p1" , "p2" , "p3" , "p4"}; char* buf[1024]; for (unsigned int i = 0 ; i < 4 ; ++i) { for (unsigned int j = i + 1 ; j < 4 ; ++j) { if (SnakeHitSnake(snakes[i] , snakes[j])) { snakes[i].StopMoving(); sprintf(buf , "%s hit %s!" , text[i] , text[j]); } } } }
Both of your methods arent working. Ive been looking around a bit, and cant find a proper way to do it.
I found a way that makes an array of new objects
Snake* myObject[4]; int i; Snake * objectPointer; for (i = 0; i < 50; i++) { objectPointer = new Snake; }
But then ill have to copy all the values of the reference object, for a dummy object. and i think this is wasting resources a bit much.
So my guess is that i need to get rid of the constant in order to make my reference object functions work. However i do not know how to easily do this.
Also a total different question, is there a function to draw a string in allegro? Ive been searching a lot lately, but all i can come up with are 2 functions which use const * char. And i cant seem to convert a string to a const char (Or i have to use functions which require runtime librarys, in which case i cant port it to for example linux anymore)
thx
Define "aren't working".
Why are you trying to make 50 new snakes?
So my guess is that i need to get rid of the constant in order to make my reference object functions work. However i do not know how to easily do this.
If you need to modify a snake object (by using Snake::StopMoving()) then you should not be using a 'const Snake&', but a 'Snake&' only. That way you can still modify it. The warning came up because you are trying to call a non-const member function on a const Snake object.
Just don't use the 'const' keyword - in the member function declaration, or in the function prototype.
Also a total different question, is there a function to draw a string in allegro? Ive been searching a lot lately, but all i can come up with are 2 functions which use const * char. And i cant seem to convert a string to a const char (Or i have to use functions which require runtime librarys, in which case i cant port it to for example linux anymore)
See the manual : http://www.allegro.cc/manual/5/
Specifically, the font addon : http://www.allegro.cc/manual/5/font.html
To get a const char* from a std::string, use the string::c_str() method :
string s = "Lalalala"; printf("%s\n" , s.c_str());
Yeah the 50 was a mistake :p. It was from an example i found.
Ill try and get of the const then .
Also the s.c_str() is not working. Ive tried it before and googled it. But i cannot come up with a solution for that problem. And yes i did include <String> and even <CString> yet no succes!
And i know how to draw a char to the screen, but i want to be able to do it with strings .
also not working = it cant convert const snake to snake*. Thats when using the following code:
Snake *snakes[4] = {p1,p2,p3,p4};
Also the s.c_str() is not working. Ive tried it before and googled it. But i cannot come up with a solution for that problem. And yes i did include <String> and even <CString> yet no succes!
#include <string> using std::string;// so you can refer to it as 'string', instead of 'std::string'.
also not working = it cant convert const snake to snake*. Thats when using the following code:
Snake *snakes[4] = {p1,p2,p3,p4};
I told you, if you need to modify a Snake object, don't use a const reference or pointer. Also, you would have to use &p1, etc... to take the address of the objects in your example code.