Общее·количество·просмотров·страницы

воскресенье, 28 октября 2012 г.

Создание псевдотрехмерного движка

Конечно движок это через чур загнуто сказано,ну оно похоже на движок:)

Побудило меня на это то,что я фанат серии игра дум и ее модов,портов и т.д.(короче дум-задрот).меня всегда влекла атмосферность этой игры и еще со школы я мечтал написать свое вульфенштейно или думо подобное двигло.
Вот и зародились у меня некоторые темы.

Язык: Си.
Среда разработки: борланд Си 3.1

Поехали.

Итак движок будет под ДОС как вы поняли и рисоваться он будет методом рейкастинга.

В чем суть методики.

Это преобразование супер просто формы данных (в нашем случае обыкновенная матрица) в псевдотрехмерную проекцию с помощью пускания лучей из точки обзора в ее объем.

Выглядит это вот так:
на первом куске- двумерная карта и точка обзора откуда испускаются лучи,на втором куске трехмерная(псевдо) проекция.
Особенности в том,что стенки будут всегда перпендикулярны полу.за счет этого этот метод выигрывает в скорости,так как можно пускать один луч для всего столба экрана.
А так же стенки будут представлять собой равносторонние квадраты.

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

начнем писать код и походу дела его толковать.

из хидеров нам понадобится

#include<graphics.h>
#include<math.h>
#include<dos.h>
#include<conio.h>
#include<stdlib.h>

начальные переменные

float rad;
int scan, exitt=0,pres=0, speed=2, anglspd=3, curangl=-30;

тут понятно я думаю,скэн коды клавиш,выход,нажата клавиша,скорость движения по карте,скорость поворота в сторону,текущая позиция взгляда.

int KARTE[16][16]={
    {1,9,9,9,9,9,9,9,9,9,9,9,9,9,1},
    {9,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
    {9,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
    {9,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
    {9,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
    {9,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
    {9,0,0,0,0,7,7,7,7,7,7,7,7,0,4},
    {9,0,0,0,0,7,0,0,0,0,0,0,0,0,4},
    {9,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
    {9,0,0,0,0,7,0,0,0,0,0,0,0,0,4},
    {9,0,0,0,0,7,7,7,7,7,7,7,7,0,4},
    {9,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
    {9,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, 
     {9,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
      {1,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    };
вот и сама карта,она размерами 16х16.
мы находимся в точке которая помечена зеленым и смотрим в сторону где помечено красным.



карта это матрица(двумерный массив,который будет проектироваться в псевдотрехмерный мир)
каждая цифра отличная от 0 будет стеной (пола и потолка нет,я это спиздил у вульфенштэйна :D) и каждая цифра же одновременно окрашивает стену в цвет в соответствии с цветами драйвера EGAVGA.Bgi (его присутствие,к сожалению,необходимо).

Теперь что касается управления

void interrupt (*oldint9)(); // указатель на первичное прерывание клавиатуры
void interrupt newint9()

перехватываем int9 (прерывание клавиатуры) и в новом обработчике ставим свои кнопки управления(стрелки)

выглядит это следующим образом:

static int kbrdvalue;
 scan = inportb(0x60); // будем читать скэн-коды клавиш
   if(scan == 1)
 exitt = 1; // эск- выход
   else
   {
     if(scan == 72) // стрелки будут клавишами перемещения
 pres = 1;
     else
     {
       if(scan == 80)
  pres = 2;
       else
{
if(scan == 75)
pres = 3;
else
  {
  if(scan == 77)
pres = 4;

      else
  {
  pres = -1;
пояснение в комментариях.
так же нужно сделать сброс контоллера прерываний клавиатуры,процедурку я нашел в тырнетах:

kbrdvalue = inportb(0x61); // сбросим контроллер прерываний клавиатуры, т.к. мы не одни в системе (хотя это и ДОС)
 outportb(0x61, kbrdvalue | 0x80);
 outportb(0x61, kbrdvalue);
 outportb(0x20, 0x20);

начинаем основную часть:

initgraph(&gdriver, &gmode, "C:\\bc\\BGI"); // инициализируем графич.режим

xmax = getmaxx();
ymax = getmaxy();
rad = 3.1415926535897932384626433832795/180.0;
vlx = xmax/fov;

получаем размеры экрана.затем нам понадобятся радианы,ну и в конце по алгоритму значение vlx будет переходом от столбца к столбцу.
FOV как вы поняли это field of vision равное 60 градусам (можно поиздеваться с градусами и получить прикольные наркоманские эффекты):)

теперь установим наши прерывания 
oldint9 = getvect(9); // для установки первоначального значения получим вектор прерывания клавиатуры
setvect(9,newint9);

ну и основной цикл:

    for(angle = curangl; angle <= curangl+fov;angle++) // получаем угол (в градусах)

    {
    if(angle<0)
            tmpangl = angle + 360;
    else
         {
          if(angle>360)
                tmpangl = angle - 360;
          else
                tmpangl = angle;
          }

                          // вычисляем расстояние от нас до стенки (это тоже взято из алгоритма)
    xb = sin(angle*rad);       
    yb = cos(angle*rad);         
    bx = px;                 
    by = py;
    l = 0;
    xbtmp = xb;
    ybtmp = yb;

     do {
     bx = bx + xb; // находим первое ближайшее пересечение (т.к луч идет в квадрат и сталкивается с точками)
     by = by + yb;
     l = l + 0.5; // отображение стенок (можно регулировать отдаленность)
     fy = (int)by/10; // примерное наше расположение в квадрате (можно где то от 8 до 12, что бы не застрять в стенке) (как вы поняли каждое значение-это еще один квадрат)
     fx = (int)bx/10;
     k = KARTE[fx][fy];
   }while(k == 0);

   dd = (2*ymax) / l; //высоты столбца

   hp1 = ymax/2-dd; // находим верхние и нижние точки откуда сможем отрисовывать столбцы
   hp2 = ymax/2+dd;

   setfillstyle(1,k);
   bar(x,hp1,x+vlx,hp2); // рисуем двухмерными прямоугольниками
   setfillstyle(1,ceiling);
   bar(x,1,x+vlx,hp1-1);       //потолок
   setfillstyle(1,ground);
   bar(x,hp2+1,x+vlx,ymax);       //пол
   setlinestyle(SOLID_LINE, 1, 1); // окантовка-сплошная черная линия
   setcolor(0);
   line (x,hp1,x+vlx,hp1);
   line (x,hp2,x+vlx,hp2);
   x = x+vlx;


   }
   // тут просто- если нажали- изменяем координаты и углы
    if(pres == 1)
        {
    
         px = px +speed*xbtmp;
         py = py +speed*ybtmp;
       
        }
 if(pres == 2)
{
px = px - speed*xbtmp;
py = py - speed*ybtmp;
}

  if(pres == 3)
 curangl -= anglspd;

  if(pres == 4) 
curangl += anglspd;

  if(exitt == 1) // если выход- выполняем восстановления векторов прерываний и перевод в первоначальный режим экрана
   {
   disable();
            setvect(9,oldint9);
            kbrdvalue = inportb(0x61);
            outportb(0x61, kbrdvalue | 0x80);
            outportb(0x61, kbrdvalue);
            outportb(0x20, 0x20);
            enable();
            closegraph();
            exit(0);
            }
}


}



вот и все!)
вот как выглядит это кривое отродие:

Я думаю его немного улучшить, но думаю максимум на чем я закончу это что натяну на него текстуры и усе)))
в прочем хочется еще попробовать на паскале поколдовать,есть алгоритм по сложнее,но и двигло получается более-менее не квадратное,в общем посмотрим как пойдет.
ну и под конец думаю сделать очень простой двиг на delphi с opengl или D3D рендером.А пока вот что имеем,и то уверяю что писать такое не совсем просто.

Спасибо за внимание).






2 комментария:

  1. Здравствуй, DooD.

    Не сохранились ли у тебя фулл исходники этой софтинки? Самому написать что-то руководствуясь статьей не удалось.

    ОтветитьУдалить
    Ответы
    1. Здрасте,вот как раз не так давно я опять задумывался об этом и ковыряя инет нашел немного полезной инфы,так попытаюсь реализовать сверхуебищную псевдотрехмерную игру, где уже будут бродить человечки или что то вроде,так что я думаю лучше немного подождем и посмотрим что выйдет.в целом сорцы могу закрекпить,хотя они и так полные :-)

      Удалить