Friday, 25 May 2012

Wolfenstein 3D (part 2)

As promised I'll show how to add images.

First of all let's create an image. Rebol has many images inside, one of the is old icon image:
to see it:
view layout [icon]
The game script shows just column of 4 pixel (see last post), so we need to divide enlarge and divide the image:

;let's create the image using the an rebol internal image:
layout [temp: icon]  
my_image: temp/pane/image ;icon is ane image 48x48
layout [temp: image my_image 1024x1024 ]
my_image: to-image temp
; now we slices in columns the image
imgs: copy []
size: 4x1024   ; width and height of each subimage
reps: my_image/size / size ;number of repetions
repeat y reps/y   [
    repeat x reps/x   [
        xy: size * as-pair x - 1 y - 1
        append imgs copy/part at my_image xy size
    ]
]
thelay: copy []
a: 1
b: 4
foreach item imgs [append thelay reduce ['image item (as-pair a 1) (as-pair b 1024 )]
    a: a + 4
    b: b + 4
    ]
view layout [box 1024x1024 effect [ draw   thelay     ]]

This will be the result:
Ok, now just decide a color, for example 4 of the map, will be a block with this image and we'll substitute the color slice with the image slice. A simple calculus will indicate wich slice to use, and the xx position if it's an horizontal wall or a vertical wall:
 Here the commented source:
REBOL [
subject: "raycasting engine"
version: 0.8
]
;let's create the image using the an rebol internal image:
layout [temp: icon]  
my_image: temp/pane/image ;icon is ane image 48x48
layout [temp: image my_image 1024x1024 ]
my_image: to-image temp
; now we slices in columns the image
imgs: copy []
size: 4x1024   ; width and height of each subimage
reps: my_image/size / size ;number of repetions
repeat y reps/y   [
    repeat x reps/x   [
        xy: size * as-pair x - 1 y - 1
        append imgs copy/part at my_image xy size
    ]
]
;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 ;initial view (degree)
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 )]
get-angles: func [v ] [to-integer (((sine 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: 0x0
    xy2: 4x0
    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
    temp: copy []
    for a angle (angle + 89) 1 [
        ;temporary coordinates
        xx: px
        yy: py
        ;get the ray direction
        stepx: get-angles a
        stepy: get-angle a  
        ;distance or lenght of the ray fro eyes to the wall
        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      
        color: pick palette laby/:line/:column
        ; block with color 4 will be   our blocks with image
        either   laby/:line/:column = 4 [
            slice: to-integer (((xx // 1024 ) / 1024 ) * 256)
            if   any [((xx // 1024) < 55)   ((xx // 1024) > 945 )   ]   [slice:   (to-integer (((yy // 1024 ) / 1024 ) * 256) )]                
            append display/effect/draw reduce ['image imgs/:slice xy1 xy2 ]
            ][
                append display/effect/draw reduce [
                    'pen color
                    'fill-pen color
                    'box xy1 xy2
                    ]
                ]
        xy1/x: xy1/x + 4
        xy2/x: xy2/x + 4
        ]
   
    show display    
    ]
   
;this function   calculate the new position
player-move: function [/backwards ] [mul ] [
    either backwards [ mul: -1 ] [mul: 1 ]
    newpx: px - ((get-angles heading ) * 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


This is just a very simple example. This script can be optimized in many, many ways.
This post just show you can Rebol language can be applied or every task, it's so powerful and simple, that I consider it better than Java, C++, C#, etc.

No comments:

Post a Comment