Игра «Змейка» на С++ и OpenGl

Думаю, все помнят. Старую игру змейка, в которую мы играли еще на монохромных дисплеях моторол. Давайте сегодня попробуем создать похожую игру, используя наши знания в C++ и OpenGl.

Создание и настройку проекта мы рассматривали ранее, по этому не будем на этом останавливаться, а сразу перейдем к программированию.

[code]
#include<time.h>
#include<glut.h>
#include<stdlib.h>

void art() {

glClear(GL_COLOR_BUFFER_BIT);

glFlush();

}

int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB );
glutInitWindowSize (Wight,Height);
glutCreateWindow ("Змейка");
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,Wight,0,Height);
glutDisplayFunc (art);
glutMainLoop();
}

[/code]

И так, мы создали каркас нашей игры. Теперь начнем «вылепливать» нашу игру.  Первым делом зададим цвет фона.

[code]glClearColor(0.2,0.8,0.3,1.0);[/code]

Для анимации нам потребуется функция таймера. Создадим

[code]void timer(int = 0)
{
art();

glutTimerFunc(100,timer,0);

}
[/code]

И вызовем её в main

[code]glutTimerFunc(100,timer,0);[/code]

Отлично. Теперь давайте построим наше поле наше поле состоит из квадратиков. То есть мы должны построить сетку. Для сетки нам понадобятся следующие переменные:

[code]int N=30,M=20;
int Size=20;

int Wight = Size*N;
int Height = Size*M;
[/code]

Первая строка указывает на количество квадратиков. Размер естественно, размер данных квадратиков. Ширина и высота думаю понятно. Кстати, мы уже использовали последние переменные для задания размеров окна, при настройке OpenGl.

Теперь у нас есть все необходимые переменные, для того что бы нарисовать нашу сетку.

[code]void Board()
{
glColor3f(0.0,0.7,0.0);
glBegin(GL_LINES);
for (int i=0; i<Wight; i+=Size) {

glVertex2f(i,0);

glVertex2f(i,Height);

}
for (int j=0; j<Height; j+=Size) {
glVertex2f(0,j);
glVertex2f(Wight,j);
}
glEnd();
}
[/code]

Мы создали функцию доска. Задали цвет. И в цикле от 0 до размера окна, с шагом Size(размер квадрата) нарисовали линии. Точно так же нарисовали перпендикулярные линии. Однако, что бы они нарисовались. Нам необходимо вызвать эту функцию в функции рисования. Вызовем её в функции Art:  Board();

Перейдем непосредственно к змейке. Для ней, на необходимо всего две переменных. Направление движения и размер.

[code]int area,num=2;[/code]

Змейку создадим в виде структуры массива, где каждый член имеет 2 координаты.

[code]struct
{ int x;
int y;} s[100];
[/code]

У нас все готово, для создания перемещения змейки. Как же мы будем рисовать змейку? Нам необходимо перемещать первый элемент массива в направление перемещения, последующие элементы будут занимать место по очереди. Напишем пока функцию перемещения головы нашей змейки.

[code]
void Tick()
{

if (area==0) s[0].y+=1;
if (area==1) s[0].x-=1;
if (area==2) s[0].x+=1;
if (area==3) s[0].y-=1;

}
[/code]

Мы просто меняем координаты нашей головы в соответствие с направлением перемещения.

Дополним наш скрипт, так что бы перемещалась вся змейка. Алгоритм описан выше.

[code]for (int i=num;i>0;—i){

s[i].x=s[i-1].x;

s[i].y=s[i-1].y;

}
[/code]

Как всегда вызываем данную функцию в таймере. Теперь наша змейка рассчитывается на компьютере, однако не отображается пользователю. Давайте создадим функцию отвечающую за рисование змейки.

[code]void Snake(){
glColor3f(0.2,0.5,0.1);
for (int i=0;i<num;i++){
glRectf((s[i].x*Size)+2, s[i].y*Size+2, (s[i].x+0.8)*Size, (s[i].y+0.8)*Size);
}[/code]

Цикл, который рисует змейку с головного элемента по последний квадрат. Мы рисуем квадрат с координатами перемноженными на масштаб.  Подключим данную функцию в Art.

Пришло время сделать управление для нашей игры. А то какая играй, без участия пользователя?

Создадим функцию Key

[code]
void Key(int key, int a, int b){

switch(key){

case 101 : area=0; break;
case 102 : area=2; break;
case 100 : area=1; break;
case 103 : area=3; break;

}
}
[/code]

Любая клавиша на клавиатуре имеет свой код. Этот код мы и будем считывать. Управление мы сделали через стрелки. Напомню что area это переменная направления. У нас всего 4 направления вверх, направо, налево, вниз.

Введем в игру еду. При съедании еды, должно увеличиваться количество ячеек змеи.

Создаем класс еды. У них будет 2 переменных. Это координаты.

[code]class eat{

public:
int x,y;

} ;
[/code]

Класс должен уметь создавать новые элементы для этого напишем функцию

[code]void New(){
x=rand() % N;
y=rand() % M;
}
[/code]

Ну, а теперь функция для простого отображения  их на нашем экране.

[code]void Apple(){
glColor3f(1.0,0.6,0.0);
glRectf(x*Size+2,y*Size+2,(x+1)*Size-2,(y+1)*Size-2);
}m[10];
[/code]

В конце мы указали количество наших объектов. На этом класс мы закончили. Теперь раскидаем его вызовы по программе. Начнем с вывода яблок. Выводить мы их будем цикле.

[code]for (int i=0;i<10;i++)
m[i].Apple();[/code]

Однако это не совсем то, что нам надо. При старте уже должны быть яблоки, поэтому отрисуем их в функции main

[code]for (int i=0;i<10;i++){
m[i].New();

s[i].x=10;
s[i].y=10;
}[/code]

Мы уже видим яблоки и можем через них проходить, однако ничего не происходит. Дополним нашу функцию Tick

Если координаты яблока и змеи совпадают, то мы увеличиваем змею на 1 и создаем новое яблоко.

[code]for (int i=0;i<10;i++) if ((s[0].x==m[i].x) && (s[0].y==m[i].y)) { num++; m[i].New(); }[/code]

На сегодня нам осталось добавить сброс размера при наезде на себя и запрет выхода за границы. Если мы выезжаем за границу, то наша змейка приобретает обратное направление.

[code]if (s[0].x>N) area=1;
if (s[0].x<0) area=2; if (s[0].y>M) area=3;
if (s[0].y<0) area=0;[/code]

Проверяем совпадение головного элемента, с другими элементами змейки, то мы сокращаем их количество.

[code]for (int i=1;i<num;i++)
if (s[0].x==s[i].x && s[0].y==s[i].y) num=i;[/code]

Наша игра закончена. Остались некоторые недоработки, так называемые баги, которые мы исправим в следующей статье. Так же мы поработаем над разнообразием игры. А пока суть всех игры думаю понятно. Есть функция таймера и функция рисования. В таймере меняется логика, а функция рисования перерисовывает все на экране.

Вот такой вот должен быть код.

[code]
#include<time.h>
#include<glut.h>
#include<stdlib.h>

int N=30,M=20;
int Size=20;

int Wight = Size*N;
int Height = Size*M;

int area,num=2;

struct{
int x;
int y;} s[100];

class eat{

public:
int x,y;

void New(){
x=rand() % N;
y=rand() % M;
}

void Apple(){
glColor3f(1.0,0.6,0.0);
glRectf(x*Size+2,y*Size+2,(x+1)*Size-2,(y+1)*Size-2);
}

} m[15];

void Snake(){
glColor3f(0.2,0.5,0.1);
for (int i=0;i<num;i++){ glRectf((s[i].x*Size)+2, s[i].y*Size+2, (s[i].x+0.8)*Size, (s[i].y+0.8)*Size); } } void Tick(){ for (int i=num;i>0;—i){
s[i].x=s[i-1].x;
s[i].y=s[i-1].y;
}

if (area==0) s[0].y+=1;
if (area==1) s[0].x-=1;
if (area==2) s[0].x+=1;
if (area==3) s[0].y-=1;

for (int i=0;i<15;i++) if ((s[0].x==m[i].x) && (s[0].y==m[i].y)) { num++; m[i].New(); } if (s[0].x>N) area=1;
if (s[0].x<0) area=2; if (s[0].y>M) area=3;
if (s[0].y<0) area=0;

for (int i=1;i<num;i++)
if (s[0].x==s[i].x && s[0].y==s[i].y) num=i;
}

void Board(){
glColor3f(0.0,0.7,0.0);
glBegin(GL_LINES);
for (int i=0; i<Wight; i+=Size){
glVertex2f(i,0);
glVertex2f(i,Height);
}
for (int j=0; j<Height; j+=Size){
glVertex2f(0,j);
glVertex2f(Wight,j);
}
glEnd();
}

void art() {

glClear(GL_COLOR_BUFFER_BIT);

Board();

Snake();

for (int i=0;i<15;i++)
m[i].Apple();

glFlush();
glutSwapBuffers();
}

void Key(int key, int a, int b){

switch(key){

case 101 : area=0; break;
case 102 : area=2; break;
case 100 : area=1; break;
case 103 : area=3; break;

}
}

void timer(int = 0){

art();

Tick();

glutTimerFunc(100,timer,0);
}

int main(int argc, char **argv) {

srand(time(0));

for (int i=0;i<15;i++){
m[i].New();

s[i].x=10;
s[i].y=10;
}

glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB );
glutInitWindowSize (Wight,Height);
glutCreateWindow ("Змейка");
glClearColor(0.2,0.8,0.3,1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,Wight,0,Height);
glutDisplayFunc (art);
glutSpecialFunc(Key);
glutTimerFunc(100,timer,0);
glutMainLoop();
}
[/code]

Мы уложились в 144 строки. Но можно было обойтись и 100.
Скачать

1 Star2 Stars3 Stars4 Stars5 Stars (5 голосов, средний:5,00 из 5)
Вы можете пропустить чтение записи и оставить комментарий. Размещение ссылок запрещено.
Оставить комментарий