1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
| /*Модель 3D Raycasting на C(89). Написана нейронной сетью. Отредактирована by dcc0.
Стены с помощью символов
Компиляция и запуск gcc -o ray ray.c -lm; ./ray
* Поворот исправлен на матрицу. Формула поиска угла луча упрощена.
* или gcc -std=c89 -o ray ray.c -lm; ./ray
Убран эффект рыбьего глаза*/
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
/*Размеры карты и экрана*/
#define MAP_WIDTH 16
#define MAP_HEIGHT 16
#define SCREEN_WIDTH 60
#define SCREEN_HEIGHT 24
#define M_PI 3.14159265358979323846
#define FOV (M_PI / 3.0)
#define MAX_DEPTH 16.0
/*Карта*/
int worldMap[MAP_HEIGHT][MAP_WIDTH] = {
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
{1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,1},
{1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
//Матрица для поворота наблюдателя вокруг себя
double matrix_angle[33] = {-8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2,
-1.5, -1, -0.5, 0.0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8};
double playerX = 3.0; /*Начальные координаты игрока*/
double playerY = 3.0;
//double playerAngle = 0.0;
void draw(double playerAngle) {
int y, x, RayX, RayY, hit, ceiling, floor, rrandd;
double angleDifference;
double eyeX, eyeY, rayAngle, distanceToWall;
printf("\033[2J"); //Очистка экрана
printf("\033[H"); //И сброс курсора в начало
fflush(stdout);
for (y = 0; y < SCREEN_HEIGHT; y++) {
for (x = 0; x < SCREEN_WIDTH; x++) {
/*Вычисляем угол для каждого луча*/
//rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;
//Угол игрока минус FOV/2 (30 градусов влево от точки взгляда игрока)
// + x умн. на 1 градус в радианах = 0.0174532925
//Из FOV (= 60 градусов) вычитаем 30 градусов в радианах для левой части от POV (Point of view - точка обзора)
//Если не вычитать 0.52359878, то получим только половину обзора
//Но если убрать, все равно будет работать, будто закрыли один глаз
//Нам надо найти 60 углов для луча, но не от точки взгдяла, а от точки взгляд - 30градусов (в радианх)
//а это и есть минус 52359878
//Так тоже можно, но тогда углы будут от точки взгляда, левая часть из обзора пропадет
//rayAngle = (playerAngle) + ((double)x * 0.0174532925);
rayAngle = (playerAngle - 0.52359878) + ((double)x * 0.0174532925);
distanceToWall = 0.0;
hit = 0;
/*Можно удалить */
//eyeX = cos(rayAngle);
//eyeY = sin(rayAngle);
//Тут луч начианет двигаться
while (!hit && distanceToWall < MAX_DEPTH) {
distanceToWall += 0.1; /* шаг по лучу*/
// testX = (int)(playerX + eyeX * distanceToWall); /*переменная луча по X*/
//testY = (int)(playerY + eyeY * distanceToWall); /*по Y*/
//Округлим до целого. Это и есть луч.
RayX = (int)(playerX + cos(rayAngle) * distanceToWall); /*переменная луча по X*/
RayY = (int)(playerY + sin(rayAngle) * distanceToWall); /*по Y*/
//Можно закомментировать. При такой карте не сработает.
//if (RayX < 0 || RayX >= MAP_WIDTH || RayY < 0 || RayY >= MAP_HEIGHT) {
// hit = 1; /*луч вышел за границы карты, считаем, что столкнулся с границей*/
// distanceToWall = MAX_DEPTH;
//} else {
//Луч всегда упрется в стену
if (worldMap[RayY][RayX] == 1) {
hit = 1; /*столкновение со стеной*/
}
//}
}
// new: Корректировка расстояния для устранения эффекта рыбьего глаза (new)
angleDifference = rayAngle - playerAngle; // new: расчет разницы углов (new)
if (angleDifference > M_PI) angleDifference -= 2 * M_PI; // new: приведение к диапазону (-π, π) (new)
if (angleDifference < -M_PI) angleDifference += 2 * M_PI; // new
distanceToWall = distanceToWall * cos(angleDifference); // new: исправление расстояния (new)
//Делим на двое высоту находим точку до которой рисовать потолок пробелом
ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / distanceToWall);
//откуда начнется пол, тоже пробелами
floor = SCREEN_HEIGHT - ceiling;
if (y < ceiling) {
printf(" "); /*рисуем пробел - небо*/
} else if (y > ceiling && y <= floor) {
if (distanceToWall <= MAX_DEPTH / 4.0) {
printf("#"); /*рисуем стены разной жирности*/
} else if (distanceToWall < MAX_DEPTH / 2.0) {
printf("+");
} else {
printf(".");
}
} else {
printf(" "); /*рисуем пол*/
}
}
printf("\n");
}
printf("\nУгол поворта: %g\n", playerAngle);
}
int main() {
char command[10];
double newX, newY;
char c;
int ma = 16;
//Начальный угол игрока = 0.0
double playerAngle = matrix_angle[16];
//Управление
while (1) {
//На каждое движение перерисовываем видимую область
draw(playerAngle);
printf("Введите (W/A/S/D + enter, 'exit' - выход): ");
if (!fgets(command, sizeof(command), stdin)) break;
if (strncmp(command, "exit", 4) == 0) {
break; /*Выход из программы*/
}
c = command[0];
if (c == 'w' || c == 'W') {
/*Вперед*/
newX = playerX + cos(playerAngle) * 0.2;
newY = playerY + sin(playerAngle) * 0.2;
if (worldMap[(int)newY][(int)newX] == 0) {
playerX = newX;
playerY = newY;
}
} else if (c == 's' || c == 'S') {
/*Назад*/
newX = playerX - cos(playerAngle) * 0.2;
newY = playerY - sin(playerAngle) * 0.2;
if (worldMap[(int)newY][(int)newX] == 0) {
playerX = newX;
playerY = newY;
}
} else if (c == 'a' || c == 'A') {
/*Поворот налево*/
ma--;
if (ma < 0) ma = 32;
if (ma > 32) ma = 0;
playerAngle = matrix_angle[ma];
//playerAngle -= 0.1;
} else if (c == 'd' || c == 'D') {
/*Поворот направо*/
ma++;
if (ma < 0) ma = 32;
if (ma > 32) ma = 0;
playerAngle = matrix_angle[ma];
//playerAngle += 0.1;
}
}
return 0;
} |