Pikselintarkka näkyvyys ei taida olla toteutettavissa. Tein version jossa tilekartasta etsitään aluksi kaikki nurkat, ja sitten raycastit tehdään vain kohti ruudulla näkyviä nurkkia. Ongelmia ilmeni kolme:
- Pelkät nurkat eivät riitä, sillä ruudun reunoille jää muuten tyhjiä alueita. Alla oleva kuva valottaa ongelmaa.
- Säteenheittokulmat pitää järjestää. Tämä ei ole suuri ongelma, mutta välttämätön paha joka syö laskenta-aikaa.
- Kolmioiden täyttö on lukittunakin hidasta. Tästä ei pääse mihinkään.
Ongelmalliset kohdat on ympyröity magentalla.
Lisäksi käytän pelissä ObjectPickiä pelihahmojen näkyvyystarkistuksiin, eli pickaus pitäisi kytkeä pois päältä näille objekteille näkökentän piirron ajaksi.
Tässä testikoodi joka tuottaa ylläolevan kuvan:
Code: Select all
// #define TIC __timerstart = timer()
// #define TOC (timer() - __timerstart)
dim mapCorners(0,0)
dim mapLocal(1,1) 'for temp use
const CORNER_NW = 0
const CORNER_NE = 1
const CORNER_SE = 2
const CORNER_SW = 3
function find_map_corners(map, tilesize)
mapw = mapWidth()
maph = mapHeight()
mapx = objectX(map)
mapy = objectY(map)
reDim mapCorners(mapw, maph)
for ty=1 to maph
for tx=1 to mapw
mapLocal(0,0) = getMap2(2, tx-1, ty-1)
mapLocal(1,0) = getMap2(2, tx , ty-1)
mapLocal(0,1) = getMap2(2, tx-1, ty)
mapLocal(1,1) = getMap2(2, tx , ty)
' 0,0 1,0
' 0,1 1,1
corner = true
' A A
' B B
if (mapLocal(0,0) = mapLocal(1,0)) and (mapLocal(0,1) = mapLocal(1,1)) then corner = false
' A B
' A B
if mapLocal(0,0) = mapLocal(0,1) and mapLocal(1,0) = mapLocal(1,1) then corner = false
mapCorners(tx-1, ty-1) = corner
next tx
next ty
endFunction
const MAX_CORNER_COUNT = 150
'global losCornerCount // done at runtime
dim losCornerAngles(MAX_CORNER_COUNT) as float
'! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
'Aja tämä ohjelma painamalla F5.
'Lopeta ohjelma painamalla ESC.
'! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
SCREEN 640,480
'frameLimit 40 'rajoita nopeutta
kartta = LoadMap("Media\cdm2.til","Media\tileset.bmp")
PlayObject kartta,0,0,1
ukko = LoadObject ("Media\guy.bmp",72)
'setupCollision ukko, kartta, 1, 4, 2
dummy = makeObject()
objectPickable kartta, ON
translateObject kartta, 16, 0
__timerstart = timer()
find_map_corners(kartta, 32)
setWindow " took " + (timer() - __timerstart) + " ms"
corner_max = 0
draw_triangles = true
repeat
if keyHit(cbKeySpace) then draw_triangles = not draw_triangles
'Ukon ohjaus
If LeftKey() Then TurnObject ukko,5
If RightKey() Then TurnObject ukko,-5
If UpKey() Then MoveObject ukko,2*2
If DownKey() Then MoveObject ukko,-2*2
updateGame
CloneCameraPosition ukko
drawGame
color 255,255,255
drawToWorld ON,ON,ON
mapw = mapWidth()
maph = mapHeight()
mapx = objectX(kartta)
mapy = objectY(kartta)
map_bound_x = max(0, ((cameraX()-320) - (-objectSizeX(kartta)/2.0 + objectX(kartta)))/32)
map_bound_y = max(0, ((-cameraY()-240) - (-objectSizeY(kartta)/2.0 - objectY(kartta)))/32)
' piirtoa varten ei flipata kameran y:tä, kiitos vaan zerppa
map_bound_y2 = max(0, ((cameraY()+240) - (-objectSizeY(kartta)/2.0 + objectY(kartta)))/32)
map_bound_x_end = min(mapw-1, map_bound_x + roundUp(640/32.0))
map_bound_y_end = min(maph-1, map_bound_y + roundUp(480/32.0))
setWindow map_bound_x + ", " + map_bound_y
box map_bound_x, map_bound_y ,10, 10, 1
' static corners
'const MAX_CORNER_COUNT = 150
'dim losCornerAngles(MAX_CORNER_COUNT)
color cbwhite
corner_count = 0
for ty=map_bound_y to map_bound_y_end
for tx=map_bound_x to map_bound_x_end
if mapCorners(tx, ty) then
sx = (tx)*32 - objectSizeX(kartta)/2.0 + objectX(kartta)
sy = (ty)*32 - objectSizeY(kartta)/2.0 + objectY(kartta)
'sy2 = (ty)*32 - objectSizeY(kartta)/2.0 - objectY(kartta)
ang# = getAngle(objectX(ukko), -objectY(ukko), sx, sy)
losCornerAngles(corner_count) = ang
corner_count = corner_count + 1
box sx,sy,4,4,1
if corner_count >= MAX_CORNER_COUNT then
exit
endif
endif
next tx
if corner_count >= MAX_CORNER_COUNT then
exit
endif
next ty
' dynamic corners
last_hit = -1 'always hit ON first
for x=0 to 20 '640/32
xx = map_bound_x + x
yy = map_bound_y + 1
hit = getMap2(2, xx, yy)
if (last_hit <> hit) or x=20 then
xx=xx-1 'the last tile is the corner
sx = (xx)*32 - objectSizeX(kartta)/2.0 + objectX(kartta)
sy = (map_bound_y2-1)*32 - objectSizeY(kartta)/2.0 + objectY(kartta)
color cbred
box sx, sy+8, 8, 8, 1
last_hit = hit
ang# = getAngle(objectX(ukko), -objectY(ukko), sx, sy)
losCornerAngles(corner_count) = ang
corner_count = corner_count + 1
endif
next x
iters = 0
repeat
sorted = true
for i = 0 to corner_count-1-1
if losCornerAngles(i) > losCornerAngles(i+1) then
temppi# = losCornerAngles(i)
losCornerAngles(i) = losCornerAngles(i+1)
losCornerAngles(i+1) = temppi
sorted = false
iters + 1
endif
next i
until sorted
corner_max = max(corner_count, corner_max)
steps = 30
a# = 360.0/float(steps)
cloneObjectPosition dummy, ukko
ox = objectX(dummy)
oy = objectY(dummy)
px = 0
py = 0
px2 = 0
py2 = 0
lock(SCREEN())
for i = 0 to corner_count
ang# = losCornerAngles(i mod corner_count)
dist# = 9999999.0
rotateObject dummy, ang
objectPick dummy
px = pickedX()
py = pickedY()
if i > 0 then
color 255,255,255
if draw_triangles then drawtri(ox, oy, px, py, px2, py2)
color 0,255,0
line ox, oy, px, py
color 255,255,0
'circle px-4,py-4,16,1
endif
px2 = px
py2 = py
next i
unlock(SCREEN())
drawToWorld OFF,OFF,OFF
color 255,0,0
text 0,0,"FPS: " + FPS()
text 0,12,"corner count: " + corner_count
text 0,12*2,"corner max: " + corner_max
text 0,12*3,"sort iters: " + iters
text 0,12*4,"space: toggle fill"
for i = 0 to corner_count-1
text 200,12*i,i+": " + losCornerAngles(i)
next i
drawScreen
forever
function drawtri(x1,y1,x2,y2,x3,y3) 'by atomimalli
'line x1,y1,x2,y2
'line x2,y2,x3,y3
'line x3,y3,x1,y1
If y2<y1 Then 'jos p2 on ylempänä kuin p1 vaihdetaan niiden paikkaa
tmp=y1
y1=y2
y2=tmp
tmp=x1
x1=x2
x2=tmp
EndIf
If y3<y1 Then 'jos p3 on ylempänä kuin p1 vaihdetaan niiden paikkaa
tmp=y1
y1=y3
y3=tmp
tmp=x1
x1=x3
x3=tmp
endif
if y3<y2 Then 'jos p3 on ylempänä kuin p2 vaihdetaan niiden paikkaa
tmp=y2
y2=y3
y3=tmp
tmp=x2
x2=x3
x3=tmp
endif
'pisteet ovat nyt järjestyksessä
'ylhäältä alas p1(x1,y1), p2(x2,y2), p3(x3,y3)
dy1=y2-y1'pystysuora matka p1:sta p2:seen
dx1=x2-x1'vaakasuora matka p1:sta p2:seen
dy2=y3-y1'pystysuora matka p1:sta p3:meen
dx2=x3-x1'vaakasuora matka p1:sta p3:meen
If dy1 Then 'jos kolmion yläosa on pidempi kuin 0
'käydään läpi kaikki vaakaviivat kolmion yläosassa(p1-p2)
for i = y1 To y2
'lasketaan seuraava x-koordinaatti p1:stä p2:seen
ax=x1+((i-y1)*dx1)/dy1
'lasketaan seuraava x-koordinaatti p1:stä p3:meen
bx=x1+((i-y1)*dx2)/dy2
Line ax,i,bx,i 'piirretään viiva kolmion reunojen välille
Next i
endif
dy1=y3-y2'pystysuora matka p2:sta p3:meen
dx1=x3-x2'vaakasuora matka p2:sta p3:meen
If dy1 Then 'jos kolmion alaosa on pidempi kuin 0
'käydään läpi kaikki vaakaviivat kolmion alaosassa(p2-p3)
For i = y2 To y3
'lasketaan seuraava x-koordinaatti p2:stä p3:meen
ax=x2+((i-y2)*dx1)/dy1
'lasketaan seuraava x-koordinaatti p1:stä p3:meen
bx=x1+((i-y1)*dx2)/dy2
Line ax,i,bx,i 'piirretään viiva kolmion reunojen välille
next i
endif
endFunction
Niin, ja CoolBasicin epäkonsistentit koordinaatistot (y-akseli käänteinen joissakin piirto-operaatioissa, copyboxin riippuvuus kameran paikasta) hankaloittavat kehitystä enemmän kuin uskoin.
Tilekartan päälle piirretään useita 64x64 kuvia.
Ajattelin myös piirtää veritahrat erillisiin dynaamisesti luotaviin kuviin. Tilekartan kokoista kuvaa ei varattaisi kerralla, vaan ruudukko pienempiä kuvia jotka luodaan vasta kun niihin piirretään jotain. Tein ihan toimivan prototyypinkin, jota voi testata seuraavalla koodilla:
Code: Select all
SCREEN 1280, 800
kartta = LoadMap("Media\cdm2.til","Media\tileset.bmp")
PlayObject kartta,0,0,1
ukko = LoadObject ("Media\guy.bmp",72)
img = loadImage("media\map.bmp")
'setupCollision ukko, kartta, 1, 4, 2
dim map_tile_images(0,0)
const SECTOR_SIZE = 64
global map_tile_images_tiles_x, map_tile_images_tiles_y
global map_tile_images_tilemap
function tileImages_init(map)
tilesize = 32
map_tile_images_tilemap = map
for y = 0 to map_tile_images_tiles_x - 1
for x = 0 to map_tile_images_tiles_y - 1
'if map_tile_images(x,y) <> 0 then
'deleteImage map_tile_images(x,y)
'endif
next x
next y
pix_w = objectSizeX(map)
pix_h = objectSizeY(map)
tiles_x = int(pix_w / SECTOR_SIZE) + 1
tiles_y = int(pix_h / SECTOR_SIZE) + 1
addText "" + tiles_x
addText "" + tiles_y
map_tile_images_tiles_x = tiles_x
map_tile_images_tiles_y = tiles_y
reDim map_tile_images(tiles_x - 1, tiles_y - 1)
for y = 0 to map_tile_images_tiles_y - 1
for x = 0 to map_tile_images_tiles_x - 1
'map_tile_images(x,y) = makeImage(SECTOR_SIZE, SECTOR_SIZE)
next x
next y
endFunction
function tileImages_debug_draw()
mx = objectSizeX(map_tile_images_tilemap)/2
my = objectSizeY(map_tile_images_tilemap)/2
drawToWorld ON, ON, ON
for y = 0 to map_tile_images_tiles_y - 1
for x = 0 to map_tile_images_tiles_x - 1
xx = x * SECTOR_SIZE - mx
yy = -y * SECTOR_SIZE + my
yy2 = y * SECTOR_SIZE - my
if map_tile_images(x,y) <> 0
color 0, 255, 0
drawImage map_tile_images(x,y), xx, yy2
else
color 255,0,0
endif
box xx, yy, SECTOR_SIZE, SECTOR_SIZE, 0
text xx,yy, x + "," + y
text xx,yy-12, "" + map_tile_images(x,y)
next x
next y
endFunction
function tileImages_draw_image(img,target_x,target_y)
mx = objectSizeX(map_tile_images_tilemap)/2
my = objectSizeY(map_tile_images_tilemap)/2
'color 0,255,255
'box rel_x, rel_y, 8 , 8, 1
'color 255,255,0
'text 50,0,txi + "," + tyi + " : " + rel_x + ", " + rel_y
img_w = roundUp(imagewidth(img)/float(SECTOR_SIZE)) + 1 'TODO calculate offset not just add one
img_h = roundUp(imageheight(img)/float(SECTOR_SIZE)) + 1
for iy = 0 to img_h-1
for ix = 0 to img_w-1
x_offset = ix*SECTOR_SIZE
y_offset = iy*SECTOR_SIZE
tx = target_x + x_offset + mx
ty = target_y + y_offset + my
txi = roundDown(tx/SECTOR_SIZE)
tyi = roundDown(ty/SECTOR_SIZE)
rel_x = tx mod SECTOR_SIZE
rel_y = ty mod SECTOR_SIZE
if txi >= 0 and txi < map_tile_images_tiles_x then
if tyi >= 0 and tyi < map_tile_images_tiles_y then
text 50, 20, "ok!"
tile = map_tile_images(txi,tyi)
if not tile then
tile = makeImage(SECTOR_SIZE, SECTOR_SIZE)
map_tile_images(txi,tyi) = tile
endif
drawToImage tile
drawImage img, rel_x - x_offset, rel_y - y_offset
'box rel_x, rel_y, 8 , 8, 1
drawToScreen
endif
endif
next ix
next iy
endFunction
tileImages_init(kartta)
addText "click with mouse to draw map"
repeat
'Ukon ohjaus
If LeftKey() Then translateCamera -10, 0
If RightKey() Then translateCamera 10, 0
If UpKey() then translateCamera 0, 10
If DownKey() then translateCamera 0, -10
updateGame
'cloneCameraPosition ukko
mous_x = mouseWX()
mous_y = -mouseWY()
drawGame
drawToWorld OFF,OFF,OFF
if mouseHit(1) then tileImages_draw_image(img,mous_x, mous_y)
drawToWorld ON,ON,ON
tileImages_debug_draw()
'drawImage img, mous_x, mous_y
drawScreen
forever
Tajusin kuitenkin toteutuksen jälkeen, että veritahrakuvat olisi piirrettävä kartan back-kerroksen päälle, mutta kuitenkin objektien alle. Valitettavasti haluamani piirtojärjestys ei siltään CB:llä onnistu, joten joutuisin luultavasti piirtelemään tilekartan back-kerroksen itse käsin. En ole tehnyt nopeusmittauksia, mutta luultavasti kannattaisi luoda iso kuva jossa olisi koko back kerros, ja tämä sitten piirrettäisiin kerralla. Siis ylläolevalle koodille ei ole tarvetta, koska veritahrat voitaisiin piirtää suoraan juuri siihen samaan kuvaan!
Pitäisi varmaan suunnitella kenttiäkin eikä vain kikkailla grafiikoiden kanssa