Post by eyfenna on Aug 18, 2019 2:05:55 GMT -6
Heya,
I did suggest a concept of collision detection based on checking the colour of a pixel in the feature request forum, as was pointed out this would be too cpu heavy.
So I sat down and coded a collision detection. In the spritesheet tutorial I introduced collision detection depending on the tile placed on the ground, while that is neat for water, lava or other tiles this type of collision detection is less usefull for what I call decorations, being single large objects being a house, a monument, a tree or a stone cliff that the character can collide with and serve two goals:
- making the map more realistic
- keeping the player from reaching specific points on the map
At first collsion with large objects seemed to me as a juggernaut to juggle with as in my mind every single tree would need his block zone to be set at some point in code. However with some thinking I found a way to break it down into three topics:
- the type of block: one for circular objects, one for rectangle objects and one for do not block objects
- a pool of objecttypes, this can be a tree a house, a fence, a large stone, a grave, a roof and other things
- the actual decoration, which "points" to an object in above list item
Additional I wanted to provide the ability to possibly enter a blocking object through an "entrance" and have a heigth for decorations with the intent that some are large ground tiles several are at a height where they collide with a character and some are to high. For a platfomer those would be distance values where a specific distance value might be a house in the background another a stone pillar which is directly in the way of the character and another being a tree besides the path of the character only blocking the sight.
Therefor the in the decorations has z-value and in the block subroutine the z-values for which blocks are checked can be set.
Last but not least the draw subroutine of decorations takes a height value only starting to calculate the position of the decoration for a second test which checks if the decoration is wihtin a specific "visibility field" set by a drawlimit parameter. If the picture moves and decorations might be up to 300 px as pictures the drawlimit should be set to 300 as if smaller then 300 let's say 100 then a 300 px x-axis long object would disappear if scrolling out of the window to the left when 200 px of the decoration picture are still visible in the window.
I finally introduce 8 sensor points which do collision check and use their value in the movement subroutine.
Keep in mind that the below demo is one way to do it.
You might notice a specific style of arranging code in the source, this is intentional a way for me to stay sane with larger programs.
In the demo you can move the blue rectangle with WASD keys the white points around it mark the sensor place, if they turn red a collision happened, if you move the blue rectangle to the middle of the bottom of the violet circle or rectangle the blue rectangle can enter them.
If you have a question feel free to ask it in this thread of as pm.
Overall have fun playing around with it:
'
' subpictures or tile loading, storing, modifiying and displaying
'
dim picx[5000]
dim picy[5000]
dim picslot[5000]
dim picsx[5000]
dim picsy[5000]
dim picsw[5000]
dim picsh[5000]
dim pice[5000]
dim picb[5000]
dim pict[5000]
sub PicAdd(picnum, imageslot, src_x, src_y, src_w, src_h, blocklevel,tile)
if pice[picnum] <> 0 then
fprint("Already a picture at picnum ")
print(picnum)
else
picslot[picnum] = imageslot
picsx[picnum] = src_x
picsy[picnum] = src_y
picsw[picnum] = src_w
picsh[picnum] = src_h
pice[picnum] = 1
picb[picnum] = blocklevel
pict[picnum] = tile
end if
end sub
sub PicModifySourceXY(picnum, src_x, src_y)
picsx[picnum] = src_x
picsy[picnum] = src_y
end sub
sub PicDrawBlit(picnum, x,y)
if pice[picnum] =0 then
fprint("No picture at ")
print(picnum)
else
DrawImage_Blit ( picslot[picnum], x, y, picsx[picnum], picsy[picnum], picsw[picnum], picsh[picnum])
end if
end sub
sub PicDrawBlitEx(picnum, x,y,w,h)
if pice[picnum] = 0 then
fprint("No picture at ")
print(picnum)
else
DrawImage_Blit_Ex(picslot[picnum],x,y,w,h,picsx[picnum],picsy[picnum], picsw[picnum],picsh[picnum])
end if
end sub
sub PicFree(picnum)
pice[picnum] = 0
end sub
'
'the type of block an object has,
'circular block at range
'rectangle block in rectangle form
'
dim objectcircularblock[3]
dim objectrectangleblock[3]
objectcircularblock[0] = true
objectrectangleblock[0] = false
objectcircularblock[1] = false
objectrectangleblock[1] = true
objectcircularblock[2] = false
objectrectangleblock[2] = false
'
'object types store the info about the picture
'also the position of the center, the radius of the block
'the size of the blockrectangle
'
dim otypeexists[1000]
dim otypepic[1000]
dim otypeblocktype[1000]
dim otypecenterx[1000]
dim otypecentery[1000]
dim otypeblockradius[1000]
dim otypeblockx[1000]
dim otypeblocky[1000]
dim otypeblockw[1000]
dim otypeblockh[1000]
dim otypefreex[1000]
dim otypefreey[1000]
dim otypefreew[1000]
dim otypefreeh[1000]
sub OtypeNewObjectType(number, picnum,blocktype, centerx,centery,blockradius,blockx,blocky, blockw,blockh,freex,freey,freew,freeh)
if otypeexists[number] <> 0 then
fprint("Already a object type at number: ")
print(number)
else
otypeexists[number] = 1
otypepic[number] = picnum
otypecenterx[number] = centerx
otypecentery[number] = centery
otypeblocktype[number] = blocktype
otypeblockradius[number] = blockradius
otypeblockx[number] = blockx
otypeblocky[number] = blocky
otypeblockw[number] = blockw
otypeblockh[number] = blockh
otypefreex[number] = freex
otypefreey[number] = freey
otypefreew[number] = freew
otypefreeh[number] = freeh
end if
end sub
sub OtypeTestBlockFreeZone(x,y,px,py,number,byref inside)
inside = true
if x > px + otypefreex[number] and x < px + otypefreex[number] + otypefreew[number] and y > py + otypefreey[number] and y < py + otypefreey[number] + otypefreeh[number] then
inside = false
end if
end sub
sub OtypeGetInCircle(x,y,px,py,number,byref inside)
inside = false
dim xlength
dim ylength
if x < px + otypecenterx[number] then
xlength = (px + otypecenterx[number]) - x
else
xlength = x - (px + otypecenterx[number])
end if
if y < py + otypecentery[number] then
ylength = (py + otypecentery[number]) - y
else
ylength = y - (py + otypecentery[number])
end if
hypotenusis = Sqrt(xlength^2 + ylength^2)
if hypotenusis < otypeblockradius[number] then
inside = true
end if
end sub
sub OtypeGetInRectangle(x,y,px,py,otypenumber, byref inside)
inside = false
if x > (px + otypeblockx[otypenumber]) and x < (px + otypeblockx[otypenumber] +otypeblockw[otypenumber]) and y > (py + otypeblocky[otypenumber]) and y < (py + otypeblocky[otypenumber] + otypeblockh[otypenumber]) then
inside = true
end if
end sub
sub OtypeGetBlock(x,y,px,py, number, byref inside)
inside = false
if objectcircularblock[otypeblocktype[number]] then
OtypeGetInCircle(x,y,px,py,number, inside)
if inside then
OtypeTestBlockFreeZone(x,y,px,py,number,byref inside)
end if
elseif objectrectangleblock[otypeblocktype[number]] then
OtypeGetInRectangle(x,y,px,py,number,inside)
if inside then
OtypeTestBlockFreeZone(x,y,px,py,number,byref inside)
end if
end if
end sub
sub OtypeDraw(otypenumber,x,y)
PicDrawBlit(otypepic[otypenumber],x,y)
end sub
sub OtypeFree(otypenumber)
otypeexists[otypenumber] = 0
end sub
sub OtypeGetPicDimension(number,byref w, byref h)
w = picsw[otypepic[number]]
h = picsh[otypepic[number]]
end sub
'
'decorations are objects which are although massive in size compared to tiles
'give a landscape a realistical feeling
'z is heigth above landscape
'z = 0 is ground
'z = 1 is low ground
'z = 2 is person hight
'z = 3 is higher then person rooted in the ground
'z = 4 is above person
'
dim decorationexists[500,5000]
dim decorationotype[500,5000]
dim decorationx[500,5000]
dim decorationy[500,5000]
dim decorationz[500,5000]
sub DecorationRegister(mapnumber, number, otype, x,y,z)
if decorationexists[mapnumber,number] <> 0 then
fprint("Already a decoration at: " + Str$(mapnumber) + "," + Str$(number) + "\n")
else
decorationexists[mapnumber,number] = 1
decorationotype[mapnumber,number] = otype
decorationx[mapnumber,number] = x
decorationy[mapnumber,number] = y
decorationz[mapnumber,number] = z
end if
end sub
sub DecorationDraw(mapnumber,windowsw, windowsh, offsetx, offsety, height, drawlimit)
for i = 0 to 4999
if decorationz[mapnumber,i] = height then
dim w
dim h
OtypeGetPicDimension(decorationotype[mapnumber,i],w,h)
x = decorationx[mapnumber,i]
y = decorationy[mapnumber,i]
ex = decorationx[mapnumber,i] + w
ey = decorationy[mapnumber,i] + h
if x > (offsetx - drawlimit) and y > (offsety - drawlimit) and ex < (windowsw+ offsetx+drawlimit) and ey < (windowsh + offsety + drawlimit) then
OtypeDraw(decorationotype[mapnumber,i],decorationx[mapnumber,i] + offsetx, decorationy[mapnumber,i] + offsety)
end if
end if
next
end sub
sub DecorationBlock(Testx, Testy,offsetx,offsety, mapnumber, number, byref inside)
inside = false
OtypeGetBlock(Testx, Testy, decorationx[mapnumber,number] + offsetx, decorationy[mapnumber,number] + offsety, decorationotype[mapnumber, number],inside)
end sub
sub DecorationBlockTest(Testx, Testy, offsetx, offsety, windowsw, windowsh, mapnumber, byref inside)
inside = false
for i = 0 to 4999
if decorationz[mapnumber,i] > 0 and decorationz[mapnumber,i] < 4 then
if decorationx[mapnumber,i] > offsetx - 300 and decorationy[mapnumber,i] > offsety - 300 and decorationx[mapnumber,i] < windowsw + offsetx + 300 and decorationy[mapnumber,i] < windowsh + offsety + 300 then
if (decorationx[mapnumber,i] + offsetx > Testx - 400 or decorationx[mapnumber,i] + offsetx < Testx + 100) and (decorationy[mapnumber,i] + offsety > Testy - 400 or decorationy[mapnumber,i] + offsety < Testy + 100) then
DecorationBlock(Testx, Testy, offsetx, offsety, mapnumber, i, inside)
if inside then
exit for
end if
end if
end if
end if
next
end sub
sub DecorationFree(mapnumber, number)
decorationexists[mapnumber,number] = 0
end sub
sub KeyMovementPressed(byref forright, byref forleft, byref forup, byref fordown)
forright = false
forleft = false
forup = false
fordown = false
if key(K_A) then
forleft = true
end if
if key(K_D) then
forright = true
end if
if key(K_W) then
forup = true
end if
if key(K_S) then
fordown = true
end if
end sub
dim sensorBlockTruth[8]
dim sensorBlockX[8]
dim sensorBlockY[8]
dim sensorBlockDX[8]
dim sensorBlockDY[8]
sub SensorRegister(number, dx,dy)
sensorBlockDX[number]= dx
sensorBlockDY[number]= dy
end sub
sub SensorUpdate(number,x,y)
sensorBlockX[number] = x
sensorBlockY[number] = y
end sub
function SensorGiveX(number)
Valuex = sensorBlockX[number] + sensorBlockDX[number]
return Valuex
end function
function SensorGiveY(number)
ValueY = sensorBlockY[number] + sensorBlockDY[number]
return ValueY
end function
dim dirright
dim dirleft
dim dirup
dim dirdown
sub CharacterMove(byref charx, byref chary)
if dirright and (not dirup or not dirdown) and not sensorBlockTruth[2] and not sensorBlockTruth[3] and not sensorBlockTruth[4] then
charx = charx +1
elseif dirleft and (not dirup or not dirdown) and not sensorBlockTruth[0] and not sensorBlockTruth[7] and not sensorBlockTruth[6] then
charx = charx -1
end if
if dirup and (not dirright or not dirleft) and not sensorBlockTruth[0] and not sensorBlockTruth[1] and not sensorBlockTruth[2] then
chary = chary -1
elseif dirdown and (not dirright or not dirleft) and not sensorBlockTruth[4] and not sensorBlockTruth[5] and not sensorBlockTruth[6] then
chary = chary + 1
end if
if dirup and dirright and not dirleft and not dirdown and not sensorBlockTruth[2] then
charx = charx + 0.5
chary = chary - 0.5
elseif dirup and dirleft and not dirdown and not dirright and not sensorBlockTruth[0] then
charx = charx -0.5
chary = chary -0.5
elseif dirdown and dirright and not dirup and not dirleft and not sensorBlockTruth[4] then
charx = charx + 0.5
chary = chary + 0.5
elseif dirdown and dirleft and not dirup and not dirright and not sensorBlockTruth[6] then
charx = charx -0.5
chary = chary +0.5
end if
end sub
WindowOpen ( 0, "TEST WINDOW", 100, 100, 640, 480, 0 )
CanvasOpen ( 0, 640, 480, 0, 0, 640, 480, 0 )
LoadImage(0,"circle.png")
LoadImage(1,"square.png")
PicAdd(0,0,0,0,100,100,0,0)
OtypeNewObjectType(0,0,0,50,50,50,0,0,0,0,30,70,40,40)
DecorationRegister(0,0,0,100,90,1)
PicAdd(1,1,0,0,100,100,0,0)
OtypeNewObjectType(1,1,1,0,0,0,0,0,100,100,30,70,40,40)
DecorationRegister(0,1,1,300,200,1)
SensorRegister(0,-1,-1)
SensorRegister(1,15,-4)
SensorRegister(2,31,-1)
SensorRegister(3,34,15)
SensorRegister(4,31,31)
SensorRegister(5,15,34)
SensorRegister(6,-1,31)
SensorRegister(7,-4,15)
dim mx
dim my
dim mb1
dim mb2
dim mb3
dim offsetx
dim offsety
dim cx
dim cy
dim truth
while true
clearcanvas
KeyMovementPressed(dirright, dirleft, dirup, dirdown)
CharacterMove(cx,cy)
for i = 0 to 7
SensorUpdate(i,cx,cy)
next
for i = 0 to 7
DecorationBlockTest(SensorGiveX(i), SensorGiveY(i),offsetx,offsety,640,480,0, truth)
sensorBlockTruth[i] = truth
next
DecorationDraw(0,640,480,offsetx,offsety,1,300)
setColor(RGB(100,100,255))
RectFill(cx,cy,30,30)
for i = 0 to 7
if sensorBlockTruth[i] then
setColor(RGB(255,0,0))
else
setColor(RGB(255,255,255))
end if
PSet(SensorGiveX(i), SensorGiveY(i))
next
update()
wend