-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathworldManager.py
216 lines (184 loc) · 10.1 KB
/
worldManager.py
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import pygame
from pygame.locals import *
import math
texWidth = 64
texHeight = 64
class WorldManager(object):
def __init__(self,worldMap,sprite_positions,x,y,dirx,diry,planex,planey):
self.sprites = [
load_image(pygame.image.load("resources/items/barrel.png").convert(), False, colorKey = (0,0,0)),
load_image(pygame.image.load("resources/items/pillar.png").convert(), False, colorKey = (0,0,0)),
load_image(pygame.image.load("resources/items/greenlight.png").convert(), False, colorKey = (0,0,0)),
]
self.background = None
self.images = [
load_image(pygame.image.load("resources/walls/eagle.png").convert(), False),
load_image(pygame.image.load("resources/walls/redbrick.png").convert(), False),
load_image(pygame.image.load("resources/walls/purplestone.png").convert(), False),
load_image(pygame.image.load("resources/walls/greystone.png").convert(), False),
load_image(pygame.image.load("resources/walls/bluestone.png").convert(), False),
load_image(pygame.image.load("resources/walls/mossy.png").convert(), False),
load_image(pygame.image.load("resources/walls/wood.png").convert(), False),
load_image(pygame.image.load("resources/walls/colorstone.png").convert(), False),
load_image(pygame.image.load("resources/walls/eagle.png").convert(), True),
load_image(pygame.image.load("resources/walls/redbrick.png").convert(), True),
load_image(pygame.image.load("resources/walls/purplestone.png").convert(), True),
load_image(pygame.image.load("resources/walls/greystone.png").convert(), True),
load_image(pygame.image.load("resources/walls/bluestone.png").convert(), True),
load_image(pygame.image.load("resources/walls/mossy.png").convert(), True),
load_image(pygame.image.load("resources/walls/wood.png").convert(), True),
load_image(pygame.image.load("resources/walls/colorstone.png").convert(), True),
]
self.camera = Camera(x,y,dirx,diry,planex,planey)
self.worldMap = worldMap
self.sprite_positions = sprite_positions
def draw(self, surface):
w = surface.get_width()
h = surface.get_height()
#draw background
if self.background is None:
self.background = pygame.transform.scale(pygame.image.load("resources/background.png").convert(), (w,h))
surface.blit(self.background, (0,0))
zBuffer = []
for x in range(w):
#calculate ray position and direction
cameraX = float(2 * x / float(w) - 1) #x-coordinate in camera space
rayPosX = self.camera.x
rayPosY = self.camera.y
rayDirX = self.camera.dirx + self.camera.planex * cameraX
rayDirY = self.camera.diry + self.camera.planey * cameraX
#which box of the map we're in
mapX = int(rayPosX)
mapY = int(rayPosY)
#length of ray from current position to next x or y-side
sideDistX = 0.
sideDistY = 0.
#length of ray from one x or y-side to next x or y-side
deltaDistX = math.sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
if rayDirY == 0: rayDirY = 0.00001
deltaDistY = math.sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
perpWallDist = 0.
#what direction to step in x or y-direction (either +1 or -1)
stepX = 0
stepY = 0
hit = 0 #was there a wall hit?
side = 0 # was a NS or a EW wall hit?
# calculate step and initial sideDist
if rayDirX < 0:
stepX = - 1
sideDistX = (rayPosX - mapX) * deltaDistX
else:
stepX = 1
sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX
if rayDirY < 0:
stepY = - 1
sideDistY = (rayPosY - mapY) * deltaDistY
else:
stepY = 1
sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY
# perform DDA
while hit == 0:
# jump to next map square, OR in x - direction, OR in y - direction
if sideDistX < sideDistY:
sideDistX += deltaDistX
mapX += stepX
side = 0
else:
sideDistY += deltaDistY
mapY += stepY
side = 1
# Check if ray has hit a wall
if (self.worldMap[mapX][mapY] > 0):
hit = 1
# Calculate distance projected on camera direction (oblique distance will give fisheye effect !)
if (side == 0):
#perpWallDist = fabs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX)
perpWallDist = (abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX))
else:
perpWallDist = (abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY))
# Calculate height of line to draw on surface
if perpWallDist == 0:perpWallDist = 0.000001
lineHeight = abs(int(h / perpWallDist))
# calculate lowest and highest pixel to fill in current stripe
drawStart = - lineHeight / 2 + h / 2
drawEnd = lineHeight / 2 + h / 2
#texturing calculations
texNum = self.worldMap[mapX][mapY] - 1; #1 subtracted from it so that texture 0 can be used!
#calculate value of wallX
wallX = 0 #where exactly the wall was hit
if (side == 1):
wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY) * rayDirX
else:
wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX) * rayDirY;
wallX -= math.floor((wallX));
#x coordinate on the texture
texX = int(wallX * float(texWidth))
if(side == 0 and rayDirX > 0):
texX = texWidth - texX - 1;
if(side == 1 and rayDirY < 0):
texX = texWidth - texX - 1;
if(side == 1):
texNum +=8
if lineHeight > 10000:
lineHeight=10000
drawStart = -10000 /2 + h/2
surface.blit(pygame.transform.scale(self.images[texNum][texX], (1, lineHeight)), (x, drawStart))
zBuffer.append(perpWallDist)
#function to sort sprites
def sprite_sort_key(s):
import math
return math.sqrt((s[0] -self.camera.x) ** 2 + (s[1] -self.camera.y) ** 2)
self.sprite_positions.sort(key=sprite_sort_key, reverse=True)
for sprite in self.sprite_positions:
#translate sprite position to relative to camera
spriteX = sprite[0] - self.camera.x;
spriteY = sprite[1] - self.camera.y;
#transform sprite with the inverse camera matrix
# [ self.camera.planex self.camera.dirx ] -1 [ self.camera.diry -self.camera.dirx ]
# [ ] = 1/(self.camera.planex*self.camera.diry-self.camera.dirx*self.camera.planey) * [ ]
# [ self.camera.planey self.camera.diry ] [ -self.camera.planey self.camera.planex ]
invDet = 1.0 / (self.camera.planex * self.camera.diry - self.camera.dirx * self.camera.planey) #required for correct matrix multiplication
transformX = invDet * (self.camera.diry * spriteX - self.camera.dirx * spriteY)
transformY = invDet * (-self.camera.planey * spriteX + self.camera.planex * spriteY) #this is actually the depth inside the surface, that what Z is in 3D
spritesurfaceX = int((w / 2) * (1 + transformX / transformY))
#calculate height of the sprite on surface
spriteHeight = abs(int(h / (transformY))) #using "transformY" instead of the real distance prevents fisheye
#calculate lowest and highest pixel to fill in current stripe
drawStartY = -spriteHeight / 2 + h / 2
drawEndY = spriteHeight / 2 + h / 2
#calculate width of the sprite
spriteWidth = abs( int (h / (transformY)))
drawStartX = -spriteWidth / 2 + spritesurfaceX
drawEndX = spriteWidth / 2 + spritesurfaceX
if spriteHeight < 1000:
for stripe in range(int(round(drawStartX)), int(round(drawEndX))):
texX = int((256 * (stripe - (-spriteWidth / 2 + spritesurfaceX)) * texWidth / spriteWidth) / 256)
#the conditions in the if are:
##1) it's in front of camera plane so you don't see things behind you
##2) it's on the surface (left)
##3) it's on the surface (right)
##4) ZBuffer, with perpendicular distance
if(transformY > 0 and stripe > 0 and stripe < w and transformY < zBuffer[stripe]):
surface.blit(pygame.transform.scale(self.sprites[sprite[2]][texX], (1, spriteHeight)), (stripe, drawStartY))
class Camera(object):
def __init__(self,x,y,dirx,diry,planex,planey):
self.x = float(x)
self.y = float(y)
self.dirx = float(dirx)
self.diry = float(diry)
self.planex = float(planex)
self.planey = float(planey)
def load_image(image, darken, colorKey = None):
ret = []
if colorKey is not None:
image.set_colorkey(colorKey)
if darken:
image.set_alpha(127)
for i in range(image.get_width()):
s = pygame.Surface((1, image.get_height())).convert()
#s.fill((0,0,0))
s.blit(image, (- i, 0))
if colorKey is not None:
s.set_colorkey(colorKey)
ret.append(s)
return ret