Game development (i)--Console snake

Source: Internet
Author: User
Tags abs

Greedy snake game design mainly need to pay attention to several points:

1: The definition of the coordinates: the upper left corner is (0,0), the right is x positive direction, the downward is the Y positive direction


2: The design of snakes,

Snake body: m_body, here with the list (is written before the double linked list), a node is a snake body section

The properties of each snake body include the x,y coordinates: COLUMN_X,ROW_Y,X represents the first few columns on the map, and Y indicates the first few lines on the map.

Snakes have an attribute called orientation, which is currently moving up, down, left, and right: M_forward

Snake's action has: Turn, turn. Turn to have a judgment, is not in the opposite direction, such as the original upward movement, press the down arrow, is not valid.

MoveTo, move to a position (usually in the m_forward direction, the next position of the snake head). MoveTo logically should be the coordinates of each snake body in turn forward one, here you can use a tricky way, that is, the last of the tail of the snake unloaded, received before the snake head, into a new snake head. Makes it look like the whole snake is moving one step further. This will only need to change the coordinates of the point, the efficiency is high.

Addbody, eat a food, the snake body to add a section. Isbody, determine if a position has been taken by a snake. The snake body cannot cross forward, so it needs to be judged.

Getdstxy, get the snake in the direction of the M_forward, the next step to reach the position.

GETHEADXY, get the position of the current snake head

Gettailxy, get the current position of the snake tail


3: Design of the food:

The properties of the food include the column_x,row_y,x representation on the map, the first few lines of y on the map, and a valid to indicate whether the food has been eaten by a snake.

Food usually comes out 3 at a time, and not a few foods appear in the same place. It is therefore necessary to note that the generated random number cannot be duplicated.

Here we use the previously written random number pool to guarantee the generation of n no repeat random numbers.

In addition, the location of food can not be found on the map border, can not be taken up by the snake body.


4: Map scene design, the map is generally a two-dimensional array (you can also use a one-dimensional representation), under the console, each element can be expressed in one character: "" means "space, '-' indicating the snake body, '";

This two-dimensional array uses the previous array template

Here two two-dimensional array m_map1, and M_MAP2 as double buffering, can be compared to the game before and after the changes, each game screen updates, only redraw the changed parts, so as to prevent full screen redraw caused by the splash screen.

In addition, the map also includes two members: the food M_food mentioned before (food has multiple, so is the number of groups), a random number of food coordinates for the pool M_food_xy_pool

And there's the snake m_snake.

The main features of the scene are: Randfood, randomly generated location without duplication, and not be occupied by the snake body food

Processlogic, processing the user presses the top, bottom, on the left and right, the snake moves one step in a direction, including whether it can move (will not move out of the map boundary judgment, will the snake head bump into the body of the judge), will not eat the judgment of food (eat the food snake body length increased logical processing), The judgment of whether or not the food is eaten (the logical process of continuing to generate food after eating)

Issnakedeath, to determine whether the snake is dead, that is, the snake moved out of the map boundary, or the snake head touched the snake body


5: Because it is the program of the console, map, snake, food are all characters, it involves a question: how to draw the character to the specified position

Here's the fillconsoleoutputcharacter.

How to detect top, bottom, left, and right keys, and eject (ESC), where two functions are used

_kbhit, and _getch.

The function of specific functions is not introduced, you can Google


6: The snake's own regular movement, that is, people do not control the time, still moving in the original direction, where the timing, for example, 300 milliseconds to move one step. This is the timer you wrote before.


7: The game main cycle logic is roughly as follows:

Initialize while
(true)
{
	if (Player has keyboard action)
	{
		if (Esc key)
		{
			//jump Out of loop
		}
		else if (arrow key)
		{
			if (the game is in progress without end)
			{
				//snake moves in the direction of player
			action
		}
		else//other key
		{
			//non-
		processing
	}

	//assumes 300 milliseconds to process a game logic
	//The Snake body moves one step
	if (300 milliseconds interval to)
	{
		if (the game is not over) {if (no end in progress)
		{
			//Snake moves in the default direction, regardless of person's control
		}
	}
	
	if (game over)
	{
		//Show Game End
	}

	sleep
}


7.15 Modification: Increased the display of the game border, redefined some constants in the Snakescene, modified the main logic flow


Game screenshot:


Here's the code first.

Snake.h:

#ifndef _snake_
#define _snake_

#include <windows.h>

#include "TBDLinkList.h"

const int Snake_ up = 1;
const int Snake_down =-1;
const int snake_left = 2;
const int snake_right =-2;

typedef struct SNAKEBODY
{
	int column_x;
	int row_y;
} Snakebody;

Class Snake
{public
:
	Snake ();

	~snake ();

	BOOL Turn (int forward);

	int Getforward ();

	void Gettailxy (int& column_x, int& row_y);

	void Getheadxy (int& column_x, int& row_y);

	void Getdstxy (int& column_x, int& row_y);

	void MoveTo (int column_x, int row_y);
		
	void Addbody (int column_x, int row_y);

	BOOL Isbody (int column_x, int row_y);

Private:
	int m_forward;
	
	Tbdlinklist<snakebody> m_body;
};

#endif


Snake.cpp:

#include "Snake.h" Snake::snake () {m_forward = Snake_right;} Snake::~snake () {tbdlinker<snakebody> *plinker = m_body.
	Pophead ();
		while (NULL!= plinker) {delete plinker; Plinker = M_body.
	Pophead ();
	} bool Snake::turn (int forward) {if (M_forward = = forward) {return true;
		else {if (abs (M_forward) = ABS (forward)) {return false;
			else {m_forward = forward;
		return true;

()} int Snake::getforward () {return m_forward;} void Snake::gettailxy (int& column_x, int& row_y) {snakebody *ptail = m_body.
	GetTail ();
	if (NULL = = Ptail) {return;
	} column_x = ptail->column_x;
Row_y = ptail->row_y; } void Snake::getheadxy (int& column_x, int& row_y) {snakebody *phead = m_body.
	GetHead ();
	if (NULL = = Phead) {return;
	} column_x = phead->column_x;
Row_y = phead->row_y; } void Snake::getdstxy (int& column_x, int& row_y) {snakebody *phead = m_body.
	GetHead (); if (NULL = = Phead) {return ;
	int OLDX = phead->column_x;
	int oldy = phead->row_y;
		Switch (m_forward) {case snake_up:oldy--;
	Break
		Case snake_down:oldy++;
	Break
		Case snake_left:oldx--;
	Break
		Case snake_right:oldx++;
	Break
	Default:break;
	} column_x = OLDX;
row_y = Oldy; } void Snake::moveto (int column_x, int row_y) {tbdlinker<snakebody> *ptail = m_body.
	Poptail ();
		if (NULL!= ptail) {ptail->m_value.column_x = column_x;
		ptail->m_value.row_y = row_y;

		Ptail->m_plinklist = NULL; M_body.
	Pushhead (Ptail); 
	} void Snake::addbody (int column_x, int row_y) {tbdlinker<snakebody> *plinker = new tbdlinker<snakebody>;
		if (NULL!= plinker) {plinker->m_value.column_x = column_x;
		plinker->m_value.row_y = row_y;
		
		Plinker->m_plinklist = NULL; M_body.
	Pushhead (Plinker);
	} bool Snake::isbody (int column_x, int row_y) {titerator<snakebody> iter; Iter.

	Register (M_body); Iter.
	Beginlist (); Snakebody *pbody = iteR.getnext ();
		while (NULL!= pbody) {if (pbody->column_x = = column_x && pbody->row_y = = row_y) {return true; } pbody = iter.
	GetNext ();
return false; }


SnakeScene.h:

#ifndef _snake_scene_ #define _snake_scene_ #include "TArray.h" #include "TRandPool.h" #include "Snake.h" Const int SNA

Ke_max_food = 3;
	typedef struct FOOD {int column_x;
	int row_y;
BOOL valid;

}food;
The position of the game area in the map, number of rows, number of columns const int snake_game_row_y = 1;
const int snake_game_column_x = 1;
const int snake_game_max_row = 20;

const int snake_game_max_column = 30;
The total number of rows in the map, the number of columns//More than 2 lines is the top and bottom border//More than 2 columns is the left and right border const int Snake_scene_max_row = 1 + snake_game_max_row + 1;

const int Snake_scene_max_column = 1 + snake_game_max_column + 1;
Blank scene Lattice const WCHAR Ws_snake_scene_grid = L ';
Snake Body const WCHAR ws_snake_body = L '-';
Food const WCHAR Ws_snake_food = L ';


Scene border Lattice const WCHAR ws_snake_scene_frame = L ';

	Class Snakescene {public:snakescene ();

	void Initmap ();

	void Initsnake ();

	void Randfood ();

	BOOL Issnakedeath ();
	Tarray1<tarray1<wchar, Snake_scene_max_column&gt, snake_scene_max_row>& GetMap1 (); Tarray1<tarray1<wchar, Snake_scene_max_column&gt, SNAKE_SCene_max_row>& GetMap2 ();
	BOOL Processlogic ();

BOOL Processlogic (int forward);
	Private:tarray1<tarray1<wchar, Snake_scene_max_column>, snake_scene_max_row> M_map1;
	
	Tarray1<tarray1<wchar, Snake_scene_max_column&gt, snake_scene_max_row> m_map2;//double buffering, for contrasting the changed position, preventing the splash screen
	Tarray1<food, snake_max_food> M_food;

	Trandpool<snake_scene_max_column * snake_scene_max_row> M_food_xy_pool;
Snake M_snake;

}; #endif


SnakeScene.cpp:

#include "SnakeScene.h" Snakescene::snakescene () {initmap ();
Initsnake ();//initsnake before Randfood, otherwise the position of food may be taken up by the Snake Body Randfood (); void Snakescene::initmap () {for (int y = 0; y < Snake_scene_max_row; y++) {for (int x = 0; x< snake_scene_m Ax_column;
		X + +) {m_map1[y][x] = Ws_snake_scene_frame; (int y = snake_game_row_y y < snake_game_row_y + Snake_game_max_row; y++) {for (int x = Snake_game_colum n_x; x< snake_game_column_x + snake_game_max_column;
		X + +) {m_map1[y][x] = Ws_snake_scene_grid; }} void Snakescene::initsnake () {m_snake.
	Addbody (1, 7); M_snake.
	Addbody (2, 7); M_snake.
	Addbody (3, 7);//addbody is the head insert, the last one is the snake head m_map1[7][3] = ws_snake_body;
	M_MAP1[7][2] = ws_snake_body;
M_MAP1[7][1] = ws_snake_body; } void Snakescene::randfood () {M_food_xy_pool.

	Rand ();
	int foodcount = 0; for (unsigned int i = 0; i < M_food_xy_pool. Capacity ();
		i++) {int num = m_food_xy_pool[i];
		int column_x = num% Snake_scene_max_column; int row_y = Num/snake_scene_max_column; if (m_map1[row_y][column_x]!= ws_snake_scene_frame &&!m_snake.
			Isbody (column_x, row_y)) {m_food[foodcount].column_x = column_x;
			m_food[foodcount].row_y = row_y;

			M_food[foodcount].valid = true;
			M_map1[row_y][column_x] = Ws_snake_food;
			foodcount++;
			if (Snake_max_food = = Foodcount) {break; 
	}}} Tarray1<tarray1<wchar, Snake_scene_max_column&gt, snake_scene_max_row>& SnakeScene::GetMap1 () {
return M_MAP1;
	Tarray1<tarray1<wchar, Snake_scene_max_column>, snake_scene_max_row>& SnakeScene::GetMap2 () {
return M_MAP2;
	BOOL Snakescene::issnakedeath () {int newx = 0;
	int newy = 0; M_snake.
	
	Getdstxy (newx, newy); 
		if (Newx < snake_game_column_x | |
		Newx >= snake_game_column_x + snake_game_max_column | | 
		Newy < Snake_game_row_y | |
		Newy >= snake_game_row_y + snake_game_max_row| | M_snake.
	Isbody (Newx, Newy)) {return true;
return false; } bool SnakEscene::P rocesslogic () {return processlogic (m_snake.
Getforward ());

}; BOOL Snakescene::P rocesslogic (int forward) {if (!m_snake.
	Turn (Forward)) {return true;
	} if (Issnakedeath ()) {return false;
	int oldx = 0;
	int oldy = 0; M_snake.

	Gettailxy (OLDX, Oldy);
	int newx = 0;
	int newy = 0; M_snake.

	Getdstxy (newx, newy);
	BOOL Iseat = false; for (int i = 0; i < Snake_max_food i++) {if (m_food[i].valid) {if (newx = = m_food[i].column_x && New Y = = m_food[i].row_y) {m_snake.
				Addbody (newx, newy);

				M_MAP1[NEWY][NEWX] = ws_snake_body;

				M_food[i].valid = false;

				Iseat = true;
			Break }} if (!iseat) {M_snake.

		MoveTo (newx, newy);
		M_MAP1[OLDY][OLDX] = Ws_snake_scene_grid;
	M_MAP1[NEWY][NEWX] = ws_snake_body;
	bool Needrandfood = true;
			for (int i = 0; i < Snake_max_food i++) {if (m_food[i].valid) {Needrandfood = false;
		Break
	} if (Needrandfood) {Randfood ();
return true; }


Main.cpp:

#include <stdio.h> #include <windows.h> #include <conio.h> #include "TArray.h" #include "TRandPool.h"
#include "Snake.h" #include "SnakeScene.h" #include "CPerformance.h" Const unsigned char key_forward = 224;
Const unsigned char key_up = 72;
Const unsigned char key_down = 80;
Const unsigned char key_left = 75;
Const unsigned char key_right = 77;

Const unsigned char key_esc = 27;

Snakescene G_snakescene;
const int scene_row_y = 0;
const int scene_column_x = 5;
const int scene_max_row = Snake_scene_max_row;

const int scene_max_column = Snake_scene_max_column;
const int LOGIC_UP = SNAKE_UP;
const int logic_down = Snake_down;
const int logic_left = Snake_left;

const int logic_right = Snake_right;
	void Drawmap (HANDLE hOut) {COORD pos = {0, 0}; for (int y = 0; y < Scene_max_row; y++) {for (int x = 0; x< scene_max_column x + +) {if (G_snakescene.getma P1 () [y][x]!= g_snakescene.getmap2 () [y][x]) {pos.
				x = x * 2 + scene_column_x; Pos. Y= y + scene_row_y;

				Fillconsoleoutputcharacter (HOut, G_snakescene.getmap1 () [Y][x], 2, POS, NULL);
			G_SNAKESCENE.GETMAP2 () [Y][x] = G_snakescene.getmap1 () [y][x];

	int main () {HANDLE hOut = GetStdHandle (Std_output_handle);
	Console_screen_buffer_info Binfo;

	Getconsolescreenbufferinfo (HOut, &binfo);

	Drawmap (HOut); Cperformance perf;//Timer perf.
	Start ();

	float times = 0.0f;
			BOOL BRes = flag that true;//the end of the game, true to in progress, false to end while (true) {if (_kbhit ()) {int ch = _getch ();
			if (Key_esc = = ch) {break;
					else if (Key_forward = = ch) {if (bRes) {ch = _getch ();
						Switch (CH) {Case key_up:bres = g_snakescene.processlogic (LOGIC_UP);
					Break
						Case key_down:bres = g_snakescene.processlogic (Logic_down);
					Break
						Case key_left:bres = g_snakescene.processlogic (logic_left);
					Break
						Case key_right:bres = g_snakescene.processlogic (logic_right);
					Break
			}		Drawmap (HOut); The Times = perf.
		End (); if (Times >) {perf.

			Start ();
				if (bRes) {bRes = G_snakescene.processlogic ();
			Drawmap (HOut);
			} if (!bres) {WCHAR info[100] = {0};
			wcscpy_s (info,, L "Game over, press ESC key to exit.");
				for (size_t i = 0; i < wcslen (info); i++) {COORD pos = {i, Scene_max_row + scene_row_y + 2};
			Fillconsoleoutputcharacter (HOut, Info[i], 1, POS, NULL);
	} sleep (1);

	} closehandle (HOut);
return 0; }


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.