locked
Animating a snake (aka Snafu) RRS feed

  • Question


  • I'm not sure how to go about this. What to use to draw the snake to the screen and how to get it to animate properly like grow in size when it collects an item or just moving around the screen. I was told not to use a Picture box but to use Panels and no timers.

    http://www.youtube.com/watch?v=s2tlk1LpWjI&feature=related

    Maybe start off more simple like in the vid the snake just grows and the entire snake body doesn't move.

    Thanks in advance.



    • Moved by Neddy Ren Tuesday, March 29, 2011 6:52 AM (From:Windows Forms General)
    Saturday, March 26, 2011 12:22 AM

Answers

  • Hi Cyclone_S,

    This is a very similar post as your other one.  Previously I recommended you two options for moving the snake:

    1.) Every time the snake moves rearrange the list of panels.

    2.) Keep track of the current front/last and just move one panel at a time.

    To make the snake grow I would definitely recommend rearranging the panels and then adding a new one.  You can still use the faster option #2 for moving the snake, but rearranging for growing the snake is definitely advisable.

    I hope this helps,

    David Cravey


    David Cravey
    • Marked as answer by Jesse Jiang Tuesday, April 12, 2011 1:44 AM
    Tuesday, March 29, 2011 8:50 AM
  •  

    Hi Cyclone_S,

     

    Thanks for your posting in the MSDN forum.

     

    First, I think you need to record the every snake part's position in the Snake_Part class. As the width and height of snake part is fix, you need to  set two value at least, like x and y coordinates of top-left point. 

     

    Second, the move_snake function is drawing and redrawing these panels in Parts. In each moving, you need to calculate every panels' position.

     

    I hope my suggestions can help you to solve this problem.

     

    Best regards,

    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Jesse Jiang Tuesday, April 12, 2011 1:44 AM
    Tuesday, March 29, 2011 9:09 AM
  • One last thing. I got the collision function working 99% but there's a small glitch where if
    the head collides with the tail the collision function doesn't catch it and the snake keeps moving.

    David you may notice I didn't use enumerations or switches, this is cuzz I had most of the code
    writtten and didn't want to re-write all the code and mess things up(including my thinking lol)
    but I did appreciate the time you took to help.

     

    The collision function.

    void check_for_collision()
    
    			{
    
    				for(int Tail = 0; Tail <= (size-1); Tail++)
    
    				{
    
    					for(int I = 0;I <= (size-1); I++)
    
    					{
    
    						if(I != head){segments[I]->panel->BackColor= Color::Blue;} else
    
    						{
    
    							segments[head]->panel->BackColor= Color::Yellow; // Color the head a different yellow.
    
    						}
    
    
    
    						if(head!= Tail && segments[head]->panel->Left == segments[Tail]->panel->Left && segments[head]->panel->Top == segments[Tail]->panel->Top) // Detect a collision is segments overlap.
    
    						{
    
    							segments[head]->panel->BackColor= Color::Red;
    
    							Timer1->Stop();
    
    						}
    
    					
    
    					}
    
    				}
    
    			}
    
    

    Entire code.

     

    #include "stdafx.h"
    
    using namespace System;
    
    using namespace System::Drawing;
    
    using namespace System::Windows::Forms;
    
    using namespace System::Collections::Generic; // Required to make lists.
    
    
    
    public ref class snake_segment
    
    {
    
    	public:
    
    			Panel^ panel; // A Panel is used to make the snake.
    
    
    
    			snake_segment( Form ^ form ) // Class Constructor.
    
    			{ 
    
    				
    
    				panel = gcnew Panel();
    
    				//panel->BackColor= Color::Blue;
    
    				panel->Width = 20; panel->Height = 20;
    
    				form->Controls->Add(panel);
    
    			}
    
    };
    
    
    
    public ref class Form1 : public Form 
    
    {
    
    	public:
    
    			List<snake_segment^>^ segments; // A list that contains the snake segments.
    
    			int l; // Length of the snake at begining of game.
    
    			int size; // The number of segments in snake/list.
    
    			int tail; // The tail segment of the snake.
    
    			int head; // The head segment of the snake.
    
    			int x, y; // The coordinates of the snake segment(head).
    
    			int direction; // Stores the direction the snake will move.
    
    			Timer^ Timer1; // The timer that will move the snake.
    
    			bool game_started;
    
    			
    
    
    
    			Form1() // Class constructor.
    
    			{	
    
    				segments = gcnew List<snake_segment^>();
    
    				Timer1 = gcnew Timer();
    
    				Timer1->Interval = 500;
    
    				Timer1->Start();
    
    				Timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick);
    
    				this->KeyDown += gcnew System::Windows::Forms::KeyEventHandler(this, &Form1::Form_KeyDown);
    
    				Make_Snake();
    
    			}
    
    			
    
    			void Make_Snake()
    
    			{
    
    				for(int I = 0; I <= 5; I++)
    
    				{
    
    					snake_segment^ s = gcnew snake_segment(this);
    
    					s->panel->BackColor = Color::FromArgb(0,0,255);
    
    					segments->Add(s);
    
    				}
    
    
    
    				for each (snake_segment^ s in segments) // Position the segments at start of game.
    
    				{
    
    					l += s->panel->Width + 2;
    
    					s->panel->Left = l;
    
    				}
    
    				get_size_of_snake();
    
    				segments[size-1]->panel->BackColor= Color::Yellow; // Color the head yellow.
    
    			}
    
    
    
    			void get_size_of_snake() // Get the size of the snake when a new segment is added by eating something etc.
    
    			{
    
    				size=0;
    
    				for each (snake_segment^ s in segments)
    
    				{
    
    					size++;	
    
    				}
    
    				head=size-1;
    
    			}
    
    
    
    			void head_position() // The head position at start of game.
    
    			{
    
    				x = segments[size-1]->panel->Left;
    
    				y = segments[size-1]->panel->Top;
    
    			}
    
    
    
    			void move_snake()
    
    			{
    
    				if(direction==1) // Move snake left.
    
    				{
    
    					segments[tail]->panel->Location = Drawing::Point(x - 20 - 2,y); // Note using "Location" instead of "Left/Top" solved a problem where the segment would flicker as it's being moved to the head position.
    
    					head = tail;
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					if(tail >= size){tail=0;}
    
    				}
    
    
    
    				if(direction==-1) // Move snake right.
    
    				{
    
    					segments[tail]->panel->Location = Drawing::Point(x + 20 + 2,y);
    
    					head = tail;
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					if(tail >= size){tail=0;}
    
    				}
    
    
    
    				if(direction==2) // Move snake up;
    
    				{
    
    					segments[tail]->panel->Location = Drawing::Point(x,y - 20 - 2);
    
    					head = tail;
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					if(tail >= size){tail=0;}
    
    				}
    
    				
    
    				if(direction==-2) // Move snake down;
    
    				{
    
    					segments[tail]->panel->Location = Drawing::Point(x,y + 20 + 2);
    
    					head = tail;
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					if(tail >= size){tail=0;}
    
    				}
    
    				
    
    			}
    
    
    
    			void check_for_collision()
    
    			{
    
    				for(int Tail = 0; Tail <= (size-1); Tail++)
    
    				{
    
    					for(int I = 0;I <= (size-1); I++)
    
    					{
    
    						if(I != head){segments[I]->panel->BackColor= Color::Blue;} else
    
    						{
    
    							segments[head]->panel->BackColor= Color::Yellow; // Color the head a different yellow.
    
    						}
    
    
    
    						if(head!= Tail && segments[head]->panel->Left == segments[Tail]->panel->Left && segments[head]->panel->Top == segments[Tail]->panel->Top) // Detect a collision is segments overlap.
    
    						{
    
    							segments[head]->panel->BackColor= Color::Red;
    
    							Timer1->Stop();
    
    						}
    
    					
    
    					}
    
    				}
    
    			}
    
    
    
    			System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) // A timer that will move the snake;
    
    			{
    
    				
    
    				if(!game_started){head_position();game_started=true;} // Find first head position at start of game.
    
    				move_snake();
    
    				check_for_collision();
    
    			}
    
    
    
    			System::Void Form_KeyDown(System::Object^ sender, System::Windows::Forms::KeyEventArgs^ e)
    
    			{
    
    				if(e->KeyCode == Keys::Left){direction=1;}
    
    				if(e->KeyCode == Keys::Right){direction=-1;}
    
    				if(e->KeyCode == Keys::Up){direction=2;}
    
    				if(e->KeyCode == Keys::Down){direction=-2;}
    
    
    
    				if(e->KeyCode == Keys::Space) // Add a new segment to the snake.
    
    				{
    
    					snake_segment^ s = gcnew snake_segment(this);
    
    					segments->Insert(tail,s);
    
    					head = tail;
    
    					segments[tail]->panel->Location = Drawing::Point(x + segments[head]->panel->Width + 2,y); 
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					get_size_of_snake();
    
    					if(tail >= size){tail=0;}
    
    				}	
    
    			}
    
    };
    
    
    
    [STAThread]
    
    int main()
    
    {
    
     Application::Run(gcnew Form1());
    
    }
    
    
    
    

    • Marked as answer by Jesse Jiang Tuesday, April 12, 2011 1:44 AM
    Wednesday, March 30, 2011 11:24 PM
  • Nevermind I got it figured out. With a bit of help :)

    void check_for_collision()
    			{
    				for (int ThisSegment = 0; ThisSegment < (size-1); ThisSegment ++ )
    				{
    					if ( ThisSegment == head ) continue;
    					if ( segments[ThisSegment]->panel->Left == segments[head]->panel->Left && segments[ThisSegment]->panel->Top == segments[head]->panel->Top ){game_over = true;}
    				}
    
    			}
    • Marked as answer by Jesse Jiang Tuesday, April 12, 2011 1:44 AM
    Thursday, March 31, 2011 6:58 PM

All replies

  • Hi,

    So far I created a Snake_Part class. When the Form1 class is first created is calls the Make_Snake Function which creates 4 Snake_Part objects which are added to the list.

    How do I move the last panel to the front. I'm using a list to store the Snake_Part instances. A timer will be used to move the snake. I need to some how find the last/first elements in the list and then move them acordingly.

    
    #include "stdafx.h"
    using namespace System;
    using namespace System::Drawing;
    using namespace System::Windows::Forms;
    using namespace System::Collections::Generic; // Required to make lists.
    
    public ref class Snake_Part
    {
    	public:
    			Panel^ panel;
    
    			Snake_Part( Form ^ form ) // Class Constructor.
    			{ 
    				panel = gcnew Panel();
    				panel->BackColor = Color::Blue;
    				panel->Width = 20; panel->Height = 20;
    				form->Controls->Add(panel);
    			}
    };
    
    public ref class Form1 : public Form 
    {
    	public:
    			List<Snake_Part^>^ Parts; // A list that contains the snake parts.
    			int length; // The length of the snake.
    			Timer^ Timer1; // The timer that will move the snake;
    			//Panel^ p;
    			Form1() // Class constructor.
    			{	
    				Parts = gcnew List<Snake_Part^>();
    				Timer1 = gcnew Timer();
    				Timer1->Interval = 2000;
    				Timer1->Start();
    				Timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick);
    				Make_Snake();
    			}
    			
    			void Make_Snake()
    			{
    				double clr = 255;
    				double dclr = 128 / 2;
    
    				for(int I = 0; I <= 3; I++)
    				{
    					Snake_Part^ p = gcnew Snake_Part(this);
    					p->panel->BackColor = Color::FromArgb(0,0,clr);
    					Parts->Add(p);
    					clr -= dclr;
    				}
    
    				for each (Snake_Part^ p in Parts)
    				{
    					length += p->panel->Width + 2;
    					p->panel->Left = length;
    				}	
    			}
    
    			void move_snake()
    			{
    					
    			}
    
    			System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) // A timer that will move the snake;
    			{
    				move_snake();
    				
    			}
    };
    
    [STAThread]
    int main()
    {
      Application::Run(gcnew Form1());
    }
    
    


    Thanks

    Saturday, March 26, 2011 11:15 PM
  • Hi Cyclone_S,

    Welcome to the MSDN Fourm.

    This is Windows Form forum. We are discussing client application development using Windows Forms controls and features. As you use C/C++ programming, I'll help you to move your thread to C++ Generic forum for more professional supports.

    Thank you for understanding.

    Best Regards


    Neddy Ren [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, March 29, 2011 6:51 AM
  • Hi Cyclone_S,

    This is a very similar post as your other one.  Previously I recommended you two options for moving the snake:

    1.) Every time the snake moves rearrange the list of panels.

    2.) Keep track of the current front/last and just move one panel at a time.

    To make the snake grow I would definitely recommend rearranging the panels and then adding a new one.  You can still use the faster option #2 for moving the snake, but rearranging for growing the snake is definitely advisable.

    I hope this helps,

    David Cravey


    David Cravey
    • Marked as answer by Jesse Jiang Tuesday, April 12, 2011 1:44 AM
    Tuesday, March 29, 2011 8:50 AM
  •  

    Hi Cyclone_S,

     

    Thanks for your posting in the MSDN forum.

     

    First, I think you need to record the every snake part's position in the Snake_Part class. As the width and height of snake part is fix, you need to  set two value at least, like x and y coordinates of top-left point. 

     

    Second, the move_snake function is drawing and redrawing these panels in Parts. In each moving, you need to calculate every panels' position.

     

    I hope my suggestions can help you to solve this problem.

     

    Best regards,

    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Jesse Jiang Tuesday, April 12, 2011 1:44 AM
    Tuesday, March 29, 2011 9:09 AM
  • Thanks David. I chose option 2. I now have the snake moving perfectly and it grows to. I used the list insert method to insert the new snake segment in the correct order.

    Thanks Jesse I figured that out. There was some flickering caused by the two operations needed to move the snake segments. Using Panal->Location(x,y) does it in one step eliminating the flickering.

    Next thing to work on is the colision detection.

    Thanks again for the suggestions.

     

     

    Tuesday, March 29, 2011 11:40 PM
  • One last thing. I got the collision function working 99% but there's a small glitch where if
    the head collides with the tail the collision function doesn't catch it and the snake keeps moving.

    David you may notice I didn't use enumerations or switches, this is cuzz I had most of the code
    writtten and didn't want to re-write all the code and mess things up(including my thinking lol)
    but I did appreciate the time you took to help.

     

    The collision function.

    void check_for_collision()
    
    			{
    
    				for(int Tail = 0; Tail <= (size-1); Tail++)
    
    				{
    
    					for(int I = 0;I <= (size-1); I++)
    
    					{
    
    						if(I != head){segments[I]->panel->BackColor= Color::Blue;} else
    
    						{
    
    							segments[head]->panel->BackColor= Color::Yellow; // Color the head a different yellow.
    
    						}
    
    
    
    						if(head!= Tail && segments[head]->panel->Left == segments[Tail]->panel->Left && segments[head]->panel->Top == segments[Tail]->panel->Top) // Detect a collision is segments overlap.
    
    						{
    
    							segments[head]->panel->BackColor= Color::Red;
    
    							Timer1->Stop();
    
    						}
    
    					
    
    					}
    
    				}
    
    			}
    
    

    Entire code.

     

    #include "stdafx.h"
    
    using namespace System;
    
    using namespace System::Drawing;
    
    using namespace System::Windows::Forms;
    
    using namespace System::Collections::Generic; // Required to make lists.
    
    
    
    public ref class snake_segment
    
    {
    
    	public:
    
    			Panel^ panel; // A Panel is used to make the snake.
    
    
    
    			snake_segment( Form ^ form ) // Class Constructor.
    
    			{ 
    
    				
    
    				panel = gcnew Panel();
    
    				//panel->BackColor= Color::Blue;
    
    				panel->Width = 20; panel->Height = 20;
    
    				form->Controls->Add(panel);
    
    			}
    
    };
    
    
    
    public ref class Form1 : public Form 
    
    {
    
    	public:
    
    			List<snake_segment^>^ segments; // A list that contains the snake segments.
    
    			int l; // Length of the snake at begining of game.
    
    			int size; // The number of segments in snake/list.
    
    			int tail; // The tail segment of the snake.
    
    			int head; // The head segment of the snake.
    
    			int x, y; // The coordinates of the snake segment(head).
    
    			int direction; // Stores the direction the snake will move.
    
    			Timer^ Timer1; // The timer that will move the snake.
    
    			bool game_started;
    
    			
    
    
    
    			Form1() // Class constructor.
    
    			{	
    
    				segments = gcnew List<snake_segment^>();
    
    				Timer1 = gcnew Timer();
    
    				Timer1->Interval = 500;
    
    				Timer1->Start();
    
    				Timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick);
    
    				this->KeyDown += gcnew System::Windows::Forms::KeyEventHandler(this, &Form1::Form_KeyDown);
    
    				Make_Snake();
    
    			}
    
    			
    
    			void Make_Snake()
    
    			{
    
    				for(int I = 0; I <= 5; I++)
    
    				{
    
    					snake_segment^ s = gcnew snake_segment(this);
    
    					s->panel->BackColor = Color::FromArgb(0,0,255);
    
    					segments->Add(s);
    
    				}
    
    
    
    				for each (snake_segment^ s in segments) // Position the segments at start of game.
    
    				{
    
    					l += s->panel->Width + 2;
    
    					s->panel->Left = l;
    
    				}
    
    				get_size_of_snake();
    
    				segments[size-1]->panel->BackColor= Color::Yellow; // Color the head yellow.
    
    			}
    
    
    
    			void get_size_of_snake() // Get the size of the snake when a new segment is added by eating something etc.
    
    			{
    
    				size=0;
    
    				for each (snake_segment^ s in segments)
    
    				{
    
    					size++;	
    
    				}
    
    				head=size-1;
    
    			}
    
    
    
    			void head_position() // The head position at start of game.
    
    			{
    
    				x = segments[size-1]->panel->Left;
    
    				y = segments[size-1]->panel->Top;
    
    			}
    
    
    
    			void move_snake()
    
    			{
    
    				if(direction==1) // Move snake left.
    
    				{
    
    					segments[tail]->panel->Location = Drawing::Point(x - 20 - 2,y); // Note using "Location" instead of "Left/Top" solved a problem where the segment would flicker as it's being moved to the head position.
    
    					head = tail;
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					if(tail >= size){tail=0;}
    
    				}
    
    
    
    				if(direction==-1) // Move snake right.
    
    				{
    
    					segments[tail]->panel->Location = Drawing::Point(x + 20 + 2,y);
    
    					head = tail;
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					if(tail >= size){tail=0;}
    
    				}
    
    
    
    				if(direction==2) // Move snake up;
    
    				{
    
    					segments[tail]->panel->Location = Drawing::Point(x,y - 20 - 2);
    
    					head = tail;
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					if(tail >= size){tail=0;}
    
    				}
    
    				
    
    				if(direction==-2) // Move snake down;
    
    				{
    
    					segments[tail]->panel->Location = Drawing::Point(x,y + 20 + 2);
    
    					head = tail;
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					if(tail >= size){tail=0;}
    
    				}
    
    				
    
    			}
    
    
    
    			void check_for_collision()
    
    			{
    
    				for(int Tail = 0; Tail <= (size-1); Tail++)
    
    				{
    
    					for(int I = 0;I <= (size-1); I++)
    
    					{
    
    						if(I != head){segments[I]->panel->BackColor= Color::Blue;} else
    
    						{
    
    							segments[head]->panel->BackColor= Color::Yellow; // Color the head a different yellow.
    
    						}
    
    
    
    						if(head!= Tail && segments[head]->panel->Left == segments[Tail]->panel->Left && segments[head]->panel->Top == segments[Tail]->panel->Top) // Detect a collision is segments overlap.
    
    						{
    
    							segments[head]->panel->BackColor= Color::Red;
    
    							Timer1->Stop();
    
    						}
    
    					
    
    					}
    
    				}
    
    			}
    
    
    
    			System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) // A timer that will move the snake;
    
    			{
    
    				
    
    				if(!game_started){head_position();game_started=true;} // Find first head position at start of game.
    
    				move_snake();
    
    				check_for_collision();
    
    			}
    
    
    
    			System::Void Form_KeyDown(System::Object^ sender, System::Windows::Forms::KeyEventArgs^ e)
    
    			{
    
    				if(e->KeyCode == Keys::Left){direction=1;}
    
    				if(e->KeyCode == Keys::Right){direction=-1;}
    
    				if(e->KeyCode == Keys::Up){direction=2;}
    
    				if(e->KeyCode == Keys::Down){direction=-2;}
    
    
    
    				if(e->KeyCode == Keys::Space) // Add a new segment to the snake.
    
    				{
    
    					snake_segment^ s = gcnew snake_segment(this);
    
    					segments->Insert(tail,s);
    
    					head = tail;
    
    					segments[tail]->panel->Location = Drawing::Point(x + segments[head]->panel->Width + 2,y); 
    
    					x = segments[head]->panel->Left;
    
    					y = segments[head]->panel->Top;
    
    					tail++;
    
    					get_size_of_snake();
    
    					if(tail >= size){tail=0;}
    
    				}	
    
    			}
    
    };
    
    
    
    [STAThread]
    
    int main()
    
    {
    
     Application::Run(gcnew Form1());
    
    }
    
    
    
    

    • Marked as answer by Jesse Jiang Tuesday, April 12, 2011 1:44 AM
    Wednesday, March 30, 2011 11:24 PM
  • Nevermind I got it figured out. With a bit of help :)

    void check_for_collision()
    			{
    				for (int ThisSegment = 0; ThisSegment < (size-1); ThisSegment ++ )
    				{
    					if ( ThisSegment == head ) continue;
    					if ( segments[ThisSegment]->panel->Left == segments[head]->panel->Left && segments[ThisSegment]->panel->Top == segments[head]->panel->Top ){game_over = true;}
    				}
    
    			}
    • Marked as answer by Jesse Jiang Tuesday, April 12, 2011 1:44 AM
    Thursday, March 31, 2011 6:58 PM