The answer is: YES!
First of all you have to know that the technique used with Wolfenstein 3D is called ray casting, you can find a great guide to it here: http://www.permadi.com/tutorial/raycast/index.html
A short explanation is the following:
- map is only 2D, not 3D, the ray casting give you the effect of 3D
- map is made of blocks
- you can walk inside transparent block
- you can make many steps inside a transparent block (for example 64x64 steps or 1024 x 1024 steps)
- you can't walk inside visible blocks
- visible blocks make walls
- Your view is 90°
- Your view is divided in 90 rays
- You view is spread on screen each vertical line is a ray. For example in a screen 360x200, every column of 4 pixels is a ray.
Here the commented code:
REBOL [
subject: "raycasting engine"
version: 0.7
]
;note some values are scaled to 1024 to create smooth movements
px: 9 * 1024 ; initial position
py: 11 * 1024 ; initial position
stride: 5 ;step
heading: 0 ;view
turn: 10 ;rotating step
;labirinth map
laby: [
[ 8 7 8 7 8 7 8 7 8 7 8 7 ]
[7 0 0 0 0 0 0 0 13 0 0 8 ]
[8 0 0 0 12 0 0 0 14 0 9 7 ]
[7 0 0 0 12 0 4 0 13 0 0 8 ]
[8 0 4 11 11 0 3 0 0 0 0 7 ]
[7 0 3 0 12 3 4 3 4 3 0 8 ]
[8 0 4 0 0 0 3 0 3 0 0 7 ]
[7 0 3 0 0 0 4 0 4 0 9 8 ]
[8 0 4 0 0 0 0 0 0 0 0 7 ]
[7 0 5 6 5 6 0 0 0 0 0 8 ]
[8 0 0 0 0 0 0 0 0 0 0 7 ]
[8 7 8 7 8 7 8 7 8 7 8 7 ]
]
;function to scale the projection of the ray
get-angle: func [v ] [to-integer (((cosine v ) * 1024) / 10)]
;colors
palette: [
0.0.128
0.128.0
0.128.128
0.0.128
128.0.128
128.128.0
192.192.192
128.128.128
0.0.255
0.255.0
255.255.0
0.0.255
255.0.255
0.255.255
255.255.255
]
;this will be player view
screen: layout [
display: box 360x200 effect [
gradient 0x1 0.0.0 128.128.128
draw []
] edge [
size: 1x1
color: 255.255.255
]
]
;main function to raycasting
retrace: does [
clear display/effect/draw
xy1: xy2: 0x0
angle: remainder (heading - 44) 360 ;this formula put angle between 0 and 359, and since the angle of view (heading) is in the middle of the cone, put the scanning ray at the beggining of the view
if angle < 0 [ angle: angle + 360 ] ;this make alway positive the angle
;starting scanning
for a angle (angle + 89) 1 [
;temporary coordinates
xx: px
yy: py
;get the ray direction
stepx: get-angle (a + 90)
stepy: get-angle a
l: 0
until [
;walk in the current ray direction in order to find a wall
xx: xx - stepx
yy: yy - stepy
l: l + 1
column: to-integer (xx / 1024)
line: to-integer (yy / 1024)
laby/:line/:column <> 0
]
h: to-integer (900 / l)
xy1/y: 100 - h
xy2/y: 100 + h
xy2/x: xy1/x + 3
color: pick palette laby/:line/:column
append display/effect/draw reduce [
'pen color
'fill-pen color
'box xy1 xy2
]
xy1/x: xy2/x + 1
]
show display
]
;this function calculate the new position
player-move: function [/backwards ] [mul ] [
either backwards [ mul: -1 ] [mul: 1 ]
newpx: px - ((get-angle (heading + 90)) * stride * mul)
newpy: py - ((get-angle heading) * stride * mul)
c: to-integer (newpx / 1024)
l: to-integer (newpy / 1024)
if laby/:l/:c = 0 [
px: newpx
py: newpy
retrace
]
]
;this control when user press keyboard buttons
insert-event-func [
if (event/type = 'key) [
switch event/key [
up [ player-move ]
down [player-move/backwards ]
left [
heading: remainder (heading + (360 - turn)) 360
retrace
]
right [
heading: remainder (heading + turn) 360
retrace
]
]
]
event
]
retrace
view/title screen join "Raycaster " system/script/header/version
You can see that the labyrinth is a simple array of numbers, if number is zero, than is a transparent block, otherwise is a wall. The number represent the color of the wall.
The main function is the retace function.
I hope that the comments are sufficient clear to understand all the script.
In the next post we'll see how to adding images to the wall.
No comments:
Post a Comment