Продолжаю писать игру "Змейка"
В функции, где происходит считывание символов с клавиатуры (input()), чтобы передвинуть объект (называемой "змея") в другое место по оси x или y, который обозначен в моем коде, как символ '0' -- необходимо все время прожимать клавишиу 'enter', то есть: ввел символ передвижения (w,s,a,d) --> ввел клавишу 'enter' чтобы функция потока ввода считала мои данные и далее, оператор ветвления switch бы начал свою работу. Мне же, как и всем другим пользователям, которые пишут свои игры, необходимо, чтобы можно было передвигать объект БЕЗ постоянного нажатия клавиши 'enter', ведь это, как минимум не нормально, не говоря уж об удобстве и т.п. Как осуществить это?
Я слышал про библиотеку <conio.h>, которая позволяет осуществлять подобные вещи. Почитав несколько часов ее документацию -- я понял, что можно считывать символ (переменная operation в функции input()) не используя функцию ввода scanf(), а используя _getch(). И да, стало быть все нормально, я действительно больше не нажимаю постоянно клавишу 'enter' при вводе символа передвижения, но увы, у меня перестали пропадать яблоки, когда бы мое тело ('0') доходило до них.
И да, еще хочу добавить, что тело, которое передвигается расположено сверху, а не снизу (то есть, при вводе символа (w,s,a,d) выводиться сразу два поля (как бы карта, где можно увидеть gameplay), в первом поле же ничего нет, а если взглянуть чуть повыше, то вы сможете увидеть это тело, которое передвигается при нажатии клавиши). Эту проблему я пытался решить несколько часов, но так и не понял, в чем дело (upd. до сих продолжаю).
В общем , проблем куча, потому я и обращаюсь за помощью.
Вот мой код:
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <windows.h>
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
#define STOP 100
#define HEIGHT 10
#define WIDTH 20
int picture[HEIGHT][WIDTH];
int input(int, int);
void printSnake(void);
void initialization(void){
extern int picture[HEIGHT][WIDTH];
for(int index1 = 0; index1 < HEIGHT; index1++)
for(int index2 = 0; index2 < WIDTH; index2++)
picture[index1][index2] = ' ';
}
void apples(void){
extern int picture[HEIGHT][WIDTH];
picture[HEIGHT-4][WIDTH-4] = '.';
picture[HEIGHT-8][WIDTH-10] = '.';
}
int draw(int x, int y){
extern int picture[HEIGHT][WIDTH];
initialization();
int i = 0 , j = 0;
for(int i = 0; i < HEIGHT; i++){
for(int j = 0; j < WIDTH; j++){
if(i == 0)picture[i][j] = '#';
if(i == HEIGHT - 1) picture[i][j] = '#';
if(j == WIDTH - 1) picture[i][j] = '|';
}
picture[i][j] = '\n';
}
for(int a = 0; a < HEIGHT; a++)
picture[a][1] = '|';
// for(int i = 0; i < HEIGHT; i++){
// for(int j = 0; j < WIDTH){
// if(picture[x] == '|' || picture[x][] == )
// }
// }
if(picture[x][y] == '|' || picture[x][y] == '#'){
return 0;
}
return 1;
}
// 1
void draw_apple_1(void){
extern int picture[HEIGHT][WIDTH];
picture[HEIGHT-4][WIDTH-4] = '.';
}
void apple_1(int not_apple){
if(not_apple == 0)
draw_apple_1();
}
// 2
void draw_apple_2(void){
extern int picture[HEIGHT][WIDTH];
picture[HEIGHT-6][WIDTH-10] = '.';
}
void apple_2(int not_apple){
if(not_apple == 0)
draw_apple_2();
}
// 3
void draw_apple_3(void){
extern int picture[HEIGHT][WIDTH];
picture[HEIGHT-8][WIDTH-10] = '.';
}
void apple_3(int not_apple){
if(not_apple == 0)
draw_apple_3();
}
int input(int x, int y){
extern int picture[HEIGHT][WIDTH];
char operation;
printf("%s\n","Enter (UP,DOWN,RIGHT,LEFT):");
scanf("%c",&operation);
switch (operation)
{
case 'w':
picture[--x][y] = '0';
return UP;
case 's':
picture[++x][y] = '0';
return DOWN;
case 'a':
picture[x][--y] = '0';
return LEFT;
case 'd':
picture[x][++y] = '0';
return RIGHT;
case 'f':
return STOP;
default:
return 0;
}
}
void printSnake(void){
extern int picture[HEIGHT][WIDTH];
for(int index1 = 0; index1 < HEIGHT; index1++)
for(int index2 = 0; index2 < WIDTH; index2++)
printf("%c",picture[index1][index2]);
}
int check(int x, int y){
extern int picture[HEIGHT][WIDTH];
for(int i = 0; i < HEIGHT; i++){
for(int j = 0; j < WIDTH; j++){
if(picture[i][j] == '.' && picture[x][y] == '.'){
return 1;
}
}
}
return 0;
}
int check_in_snake(int x, int y){
extern int picture[HEIGHT][WIDTH];
if(picture[x][y] == '.'){
return 1;
}
return 0;
}
void increase(int x, int y){
extern int picture[HEIGHT][WIDTH];
picture[x][--y] = '0';
}
int main(void){
int count_all_apples = 0;
int conflict = 0;
int is_all_apple = 0;
int x = 5, y = 9;
int not_apple_1 = 0;
int not_apple_2 = 0;
int not_apple_3 = 0;
while(1){
printf("\nx: %i y: %i\n", x,y);
if(!draw(x,y))
{
conflict = 1;
break;
}
apple_1(not_apple_1);
apple_2(not_apple_2);
apple_3(not_apple_3);
printf("\nThe number of apples you have: %i\n",count_all_apples);
int cnt = input(x, y);
if (cnt == UP) --x;
else if (cnt == DOWN) ++x;
else if (cnt == LEFT) --y;
else if (cnt == RIGHT) ++y;
else if (cnt == STOP) break;
int flag = check(x,y);
if(flag){
count_all_apples++;
printf("%s\n","Apple is FIND");
if(x == HEIGHT-4 && y == WIDTH-4) not_apple_1 = 1;
if(x == HEIGHT-6 && y == WIDTH-10) not_apple_2 = 1;
if(x == HEIGHT-8 && y == WIDTH-10) not_apple_3 = 1;
} else if(!flag) printf("%s\n","Apple is NOT FIND");
int sn = check_in_snake(x,y);
if(sn == 1){
printf("%s\n","It's time to lengthen the snake");
increase(x,y);
}
else {
printf("%s\n","Don't lengthen the snake yet.");
}
printSnake();
if(count_all_apples == 3){
is_all_apple = 1;
break;
}
}
system("cls");
printf("%s\n","-------------------------------------------------------");
if(is_all_apple){printf("%s\n","You have collected all the apples.");}
else if(conflict == 1){
printf("%s\n", "The snake collided with the wall.");
}
printf("\n%s","Game Over.");
printf("\n%s\n","-------------------------------------------------------");
return 0;
}
p.s. Не нужно осуждать, что "игра" написана отвратительно. Я это сам прекрасно понимаю, но и вы поймите, что змея -- это мой самый первый пет-проект, да и в целом я в программировании всего пару месяцев.
Ответы (1 шт):
Попробую ещё раз указать на недочёты.
int picture[HEIGHT][WIDTH];
если массив предназначен для хранения символов, он не должен быть int, он должен быть char. Это не только сэкономит место, но и позволит использовать блочные операции, а не посимвольные. Например вывод на экран одной printf, а не циклом. Для этого будет удобно ширину увеличить на 1 и добавить в конец каждой строки '\n'.
extern int picture[HEIGHT][WIDTH]; - бесполезная инструкция, предназначенная для подключения ресурсов, расположенных в других модулях.
initialization(); - бесполезная функция, зря затирающая весь массив.
int draw(int x, int y){
//extern int picture[HEIGHT][WIDTH];
//initialization();
int i = 0 , j = 0;
for(int i = 0; i < HEIGHT; i++){
for(int j = 0; j < WIDTH; j++){
if(i == 0) picture[i][j] = '#';
else if(i == HEIGHT - 1) picture[i][j] = '#';
else if(j == WIDTH - 1) picture[i][j] = '|';
// Эта строка заменяет функцию initialization();
else picture[i][j] = ' ';
}
picture[i][j] = '\n';
//после выхода из цикла j содержит WIDTH, что за пределами массива
// либо picture[i][WIDTH-1] = '\n';
// либо увеличить массив выше до picture[HEIGHT][WIDTH+1]
}
//По какой причине этот цикл сделан отдельно?
//Достаточно было в предыдущем сделать аналогичную проверку
//else if(j == 1) picture[a][1] = '|';
for(int a = 0; a < HEIGHT; a++)
picture[a][1] = '|';
// Это единственное, что по большому счёту надо оставить в этой функции,
// а постоянную перерисовку рамок и пустоты вынести в initialization()
// и вызвать один раз при старте программы
if(picture[x][y] == '|' || picture[x][y] == '#'){
return 0;
}
return 1;
}
Далее идут мелкие функции, отрисовывающие яблоки. Если функция вызывается только из одного места и представляет из себя одну команду, напрашивается вопрос, а нужна ли она вообще? В частности, отрисовку яблок можно также переложить на инициализацию и сделать один раз. Кроме того, сами яблоки можно сложить в массив с координатами и в цикле отрисовать всё сразу.
Определение наезда на яблоко тоже не должно требовать очередного указания его координат. Во-первых оно отрисовано на поле в соответствующих координатах, во-вторых, как я и советовал, яблоки могут быть в отдельном массиве. Имея под рукой x и y их легко найти там.
Теперь input(); Тут тоже проделываются одинаковые действия по изменению координат один раз тут, другой раз там, где вызывается.
Можно x и y передавать по ссылке и изменять сами координаты а в качестве возврата оставить только признак выхода:
//объявление
int input(int* x, int* y){
...
//вызов
if(input(&x, &y))
break;
Функция check() тоже какая-то бессмысленная. Зачем обходить двойным циклом всё поле, искать на нем яблоки и проверять наличие их же по имеющимся координатам? Не достаточно разве одной единственной проверки if(picture[x][y] == '.') и всё?
Функция printSnake тоже сводится к единственному printf("%s",(char*)picture);, если массив будет оформлен, как я указывал ранее.
А как только придёт понимание, что саму змейку придётся хранить в массиве (возможно циклическом, чтоб не переписывать постоянно) с координатами каждого её элемента, то станет понятно, что перерисовывание всего поля станет бессмысленным вовсе. Достаточно научиться переставлять курсор в терминале в нужные координаты и отрисовывать только змею по месту.
И последнее. Текущие координаты x и y прям напрашиваются стать глобальными и доступными во всех функциях сразу.
Ну, пока хватит. Спасибо за внимание.