Attempting to learn: World Camera Ray Collision.

Started by SpellSword, July 06, 2008, 08:45:02 PM

Previous topic - Next topic

SpellSword

I'm trying to learn how to use Camera, World & Sprite commands. Before now, I've mostly been working with text commands and drawing static images directly to the screen without running any form of collision off them.

I've written a small program to attempt to load to objects, an image which I draw to the World and a Sprite which is rendered to Scene (I think...). I'm trying to detect if the Sprite lands on the World Image by using the RayIntersectWorld() command. In this case if it does, it should reset to 50 Pixels above the World Image and begin decending again.

However, I'm having trouble. Nothing I expected to happen occured:
| The sprite falls through the World Image block without being affected.
| Also, I tried drawing a line so I could chart the Ray's path, but it rendered the line behind the Camera.

; ---------------------------------------------------------------------
; Compiler V1.63w
; IDE V1.1.7b
; ---------------------------------------------------------------------

REM <START-UP>-----<LOAD IMAGES>
LoadImage "Block1.bmp",1
LoadImage "Block2.bmp",3


; Set frame rate to 30.
SETFPS 30

; Create World #1
CreateWorld 1
 
; Tell PB to Capture the following graphics
; commands to this world buffer
CaptureToWorld 1

; Create Camera
CreateCamera 1

; Give Camera 1 viewport dimensions.
CameraViewPort 1,50,50,GetScreenWidth()-50,GetScreenHeight()-50

; Draw the block
DrawImage 1,150,150,0




; Create Sprite
CreateSprite 1


; Assign image to Sprite 1
SpriteImage 1,3

; Position Sprite
MoveSpriteX 1,150

; Sprite Position =
SpriteXpos = GetSpriteX(1)
SpriteYpos = GetSpriteY(1)

; Start of DO/LOOP
  Do

; Sprite Position =
SpriteXpos = GetSpriteX(1)
SpriteYpos = GetSpriteY(1)

; Draw line to visually track Ray's path.
Line SpriteXpos,SpriteYpos,SpriteXpos,SpriteYpos

; Check for Ray hit on block, (From sprite coordinates to bellow height.)
; If Hit, move Sprite backup by 50 (Y = SpriteCurrentPosition - 50)
IF RayIntersectWorld(1,SpriteXpos,SpriteYpos,SpriteXpos,SpriteYpos + 48) = 1
MoveSpriteY 1,-50
ELSE
MoveSpriteY 1,1
ENDIF
     
; Enable Capture to Scene Buffer
CaptureToScene
     
; Clear the scene buffer
ClsScene
     
; Transfer the data from the World buffer
; to the scene buffer
CameraGrabWorld 1,1
     

; Draw Sprites
  DrawAllSprites

; Draw Camera #1
 DrawCamera 1


     
; Display the screen
Sync
     
; Loop back to the DO
Loop


I'm not sure what I'm doing wrong.  ???

The images are 48,48 square blocks. I've included them as attachments for easy use of the code snippet.

Edit: Fixed a typo
When I dream,
I carry a sword in one hand,
a gun in the other...

kevin

#1
 That's not how ray intersection works.  It's a VECTOR operation not a pixel operation.   I suggest reading up the 'CreateWorld'  in the help.  There's a whole section devoted to this.

SpellSword

I've re-read 'CameraBasics' and 'CreateWorld' tutorials.

Unfortunately I'm still having trouble achieving the result I want.  :'(

My goal is to use this collision so that I can have the player's sprite stand on the world image if he/she falls onto it.

I tried again with the RayIntersectWorld command using a Box instead of an image.
(Are commands such as 'Box, Line, Dot, etc' VECTOR?)

Example 1:
REM <Start-Up: Setup World>
; Create World: 1
CreateWorld 1

; Capture To World: 1
CaptureToWorld 1

; Draw Box to World
BOX 250,250,300,300,1

; Create Camera
CreateCamera 1

; Moving Box Y1 & Y2. (This box is for visual tracking of the ray command.
; I am not attempting to use it for any actual collision.)
Y1 = 150
Y2 = 155


; Set FPS to 45
SetFPS 45

; Start Do/Loop
Do
     
; Switch to Capture To Scene mode
CaptureToScene
     
; Blank scene buffer
ClsScene
     
; Draw Moving Box. (This is to help track the otherwise invisible ray command,
; the box does not serve any other function.)
BOX 255,Y1,260,Y2,1

; Grab stored image data in World 1 from Camera 1
CameraGrabWorld 1,1
     
; Draw Camera 1
DrawCamera 1
     
; Check for Ray Collision, run collision check slightly right of the Moving Box.
; Collision check ray has same length as box, so they should strike the larger box at the same time.
IF RayIntersectWorld(1,262,Y1,262,Y2)
PRINT "COLLISION DETECTED"
SYNC
WaitNoKey
WaitKey
ENDIF

; Sync screen
Sync


; Move Y1 & Y2 Lower
Y1 = Y1 + 1
Y2 = Y2 + 1
     
; End of Do/Loop
Loop



I also tried to use Sliding Collision Mode using a sprite and an image captured to world, however the sprite appeared in the upper left corner of the screen instead of where I thought I had placed it and did not move.  ???

Example 2:
REM <Start-UP: Load Images>
LoadImage "Block1.bmp",1
LoadImage "Block2.bmp",2

; Create Sprite 1
CreateSprite 1

; Assign image to Sprite 1
SpriteImage 1,2

; Postition Sprite
MoveSpriteX 1,250

REM <Start-Up: Setup World>
; Create World: 1
CreateWorld 1

; Capture To World: 1
CaptureToWorld 1

; Draw 'Block1' Image to World
DrawImage 1,250,250,0

; Create Camera
CreateCamera 1

; Set FPS to 45
SetFPS 45


REM <Start-Up: Setup Sliding Collision>

; Set Sprite 1 to use World 1 for sliding collision
SpriteCollisionWorld 1,1

; Set Sprite 1 to Mode 4 (Sliding Collision)
SpriteCollisionMode 1,4



; Start Do/Loop
Do
     
; Switch to Capture To Scene mode
CaptureToScene
     
; Blank scene buffer
ClsScene

; Grab stored image data in World 1 from Camera 1
CameraGrabWorld 1,1
     
; Draw Camera 1
DrawCamera 1

; Draw Sprites
DrawAllSprites

; Sync screen
Sync

; Move Sprite 1 toward block for collision test.
MoveSpriteY 1,1
     
; End of Do/Loop
Loop


I am still unsure as to what I'm doing wrong.  :-\
Could you please elaborate on how this is accomplished?
When I dream,
I carry a sword in one hand,
a gun in the other...

SpellSword

I tried to use SpriteHitWorld instead and still produced no result.  ???

REM <Start-UP: Load Images>
LoadImage "Block1.bmp",1
LoadImage "Block2.bmp",2

; Create Sprite 1
CreateSprite 1

; Assign image to Sprite 1
SpriteImage 1,2

; Postition Sprite
MoveSpriteX 1,250

REM <Start-Up: Setup World>
; Create World: 1
CreateWorld 1

; Capture To World: 1
CaptureToWorld 1

; Draw 'Block1' Image to World
DrawImage 1,250,250,0

; Create Camera
CreateCamera 1

; Set FPS to 45
SetFPS 45


REM <Start-Up: Setup Sliding Collision>

; Set Sprite 1 to use World 1 for sliding collision
SpriteCollisionWorld 1,1

; Set Sprite 1 to Mode 0 (Rectangle Collision)
SpriteCollisionMode 1,0



; Start Do/Loop
Do
     
; Switch to Capture To Scene mode
CaptureToScene
     
; Blank scene buffer
ClsScene

; Grab stored image data in World 1 from Camera 1
CameraGrabWorld 1,1
     
; Draw Camera 1
DrawCamera 1

; Draw Sprites
DrawAllSprites

; Sync screen
Sync

; Move Sprite 1 toward block for collision test.
MoveSpriteY 1,1
     
; Check For Sprite Hitting World Image
IF SpriteHitWorld(1) = 1
Print "HIT!!!!!"
SYNC
WaitNoKey
WaitKey
ENDIF

; End of Do/Loop
Loop


I also found that in my previous posts 2nd example that it was setting to SpriteCollisionMode 1,4 (Sliding collision) that was causing the sprite to appear in the upper left corner of the screen and not move. I haven't been able to figure out why that occurs though.
When I dream,
I carry a sword in one hand,
a gun in the other...

kevin



  You'll need to spatially partition then world space for when using world collision by using PartitionWorld
 
  Ray intersection / sliding collision etc are all LINE based.  Moreover the direction of the line matters.

  See the RayIntersectWorld example in the help.


Rembrandt Q Einstein

I remember running into this problem.  It turned out where you put the partitionworld command mattered.  I put it right after i was done drawing the lines onto the world and right before i wrote capturetoscene.  I used ray intersection and getnormals in leviathan, last summer's entry.  If you want a second example, i can send the code.

SpellSword

#6
[Ray To World Collision: RayIntersectWorld]

Thanks!
I'm now using RayIntersectWorld correctly. (I think)

I quickly drew these images to illustrate my thought process on line surface direction for Ray collision.


Example 1: 'Working' RayIntersectWorld impacting Line.
REM <Start-Up: Setup World>
; Create World: 1
CreateWorld 1

; Capture To World: 1
CaptureToWorld 1

; Draw Line to World (From X1 = 350 to X2 = 250 so Collision side is towards ray.)
LINE 350,250,250,250

; Create Camera
CreateCamera 1

; Moving Box Y1 & Y2. (This box is for visual tracking of the ray command.
; I am not attempting to use it for any actual collision.)
Y1 = 150
Y2 = 155

; Partition World
PartitionWorld 1,50

; Set FPS to 45
SetFPS 45

; Start Do/Loop
Do
     
; Switch to Capture To Scene mode
CaptureToScene
     
; Blank scene buffer
ClsScene
     
; Draw Moving Box. (This is to help track the otherwise invisible ray command,
; the box does not serve any other function.)
BOX 255,Y1,260,Y2,1

; Grab stored image data in World 1 from Camera 1
CameraGrabWorld 1,1
     
; Draw Camera 1
DrawCamera 1
     
; Check for Ray Collision, run collision check slightly right of the Moving Box.
; Collision check ray has same length as box, so they should strike the larger box at the same time.
IF RayIntersectWorld(1,262,Y1,262,Y2)
PRINT "COLLISION DETECTED"
SYNC
WaitNoKey
WaitKey
ENDIF

; Sync screen
Sync


; Move Y1 & Y2 Lower
Y1 = Y1 + 1
Y2 = Y2 + 1
     
; End of Do/Loop
Loop



If I understand correctly, lines are drawn to create collision areas on the world then images are drawn over them, so the player only sees the images and not the concealed collision zones.

Example 2: 'Working' RayIntersectWorld impacts Line, with image drawn over Line.
REM <Start-UP: Load Images>
LoadImage "Block1.bmp",1
LoadImage "Block2.bmp",2

REM <Start-Up: Setup World>
; Create World: 1
CreateWorld 1

; Capture To World: 1
CaptureToWorld 1

; Draw Line to World (From X1 = 350 to X2 = 250 so Collision side is towards ray.)
LINE 350,250,250,250

; Draw 'Block1' Image to World
DrawImage 1,250,250,0

; Create Camera
CreateCamera 1

; Moving Box Y1 & Y2. (This box is for visual tracking of the ray command.
; I am not attempting to use it for any actual collision.)
Y1 = 150
Y2 = 155

; Partition World
PartitionWorld 1,50

; Set FPS to 45
SetFPS 45

; Start Do/Loop
Do
     
; Switch to Capture To Scene mode
CaptureToScene
     
; Blank scene buffer
ClsScene
     
; Draw Moving Box. (This is to help track the otherwise invisible ray command,
; the box does not serve any other function.)
BOX 255,Y1,260,Y2,1

; Grab stored image data in World 1 from Camera 1
CameraGrabWorld 1,1
     
; Draw Camera 1
DrawCamera 1
     
; Check for Ray Collision, run collision check slightly right of the Moving Box.
; Collision check ray has same length as box, so they should strike the larger box at the same time.
IF RayIntersectWorld(1,262,Y1,262,Y2)
PRINT "COLLISION DETECTED"
SYNC
WaitNoKey
WaitKey
ENDIF

; Sync screen
Sync


; Move Y1 & Y2 Lower
Y1 = Y1 + 1
Y2 = Y2 + 1
     
; End of Do/Loop
Loop


Additionally, are Shapes a sequence of lines that you can run RayIntersectWorld against? Or are shapes something for a completely different use?

For example:
If I wanted to make a box instead of four line commands, I would create a shape of that box then use the stored shape repeatedly to create several boxes?



[Sprite To World: Sliding Collision]

Now that I'm using the PartitionWorld command, the test sprite is no longer stuck in the upper left corner of the screen. However, I haven't been able to produce the Sliding Collision effect yet.

Example 3: 'Failed' Sprite Sliding Collision.
REM <Start-UP: Load Images>
LoadImage "Block1.bmp",1
LoadImage "Block2.bmp",2

; Create Sprite 1
CreateSprite 1

; Assign image to Sprite 1
SpriteImage 1,2

; Postition Sprite
MoveSpriteX 1,250

REM <Start-Up: Setup World>
; Create World: 1
CreateWorld 1

; Capture To World: 1
CaptureToWorld 1

; Draw Line to World (From X1 = 350 to X2 = 250 so Collision side is towards sprite.)
LINE 350,250,250,250

; Draw 'Block1' Image to World
DrawImage 1,250,250,0

; Create Camera
CreateCamera 1

; Set FPS to 45
SetFPS 45

; Partition World
PartitionWorld 1,50

REM <Start-Up: Setup Sliding Collision>

; Set Sprite 1 to use World 1 for sliding collision
SpriteCollisionWorld 1,1

; Set Sprite 1 to Mode 4 (Sliding Collision)
SpriteCollisionMode 1,4



; Start Do/Loop
Do
     
; Switch to Capture To Scene mode
CaptureToScene
     
; Blank scene buffer
ClsScene

; Grab stored image data in World 1 from Camera 1
CameraGrabWorld 1,1
     
; Draw Camera 1
DrawCamera 1

; Draw Sprites
DrawAllSprites

; Sync screen
Sync

; Move Sprite 1 toward block for collision test.
MoveSpriteY 1,1
     
; End of Do/Loop
Loop





Rembrandt Q Einstein I would appreciate it if you would send me that code to look over as an example.
When I dream,
I carry a sword in one hand,
a gun in the other...

kevin

Quote from: Rembrandt Q Einstein on July 15, 2008, 08:49:10 AM
I remember running into this problem.  It turned out where you put the partitionworld command mattered.  I put it right after i was done drawing the lines onto the world and right before i wrote capturetoscene.

  Building the partition must be performed after the materials are captured, not before.

kevin


QuoteIf I understand correctly, lines are drawn to create collision areas on the world then images are drawn over them, so the player only sees the images and not the concealed collision zones.

      That's pretty much it, but rather than storing the collision boundaries and the game scenery in same world, it's better to store them in separate worlds.  It's a lot more efficient.  If they're in the same world then you're drawing the collision bounds for no reason.


QuoteAdditionally, are Shapes a sequence of lines that you can run RayIntersectWorld against? Or are shapes something for a completely different use?

   Shapes are for vector collision with sprites and the odd effect .    It's not possible to partition a shape but it is on the to-do list  The main reason is, that the world doesn't hold an impression of the shape (the actual edge data converted to lines), but rather it just stores a reference to the shape.


QuoteFor example:
If I wanted to make a box instead of four line commands, I would create a shape of that box then use the stored shape repeatedly to create several boxes?


Function CollisionBox(x1,y1,x2,y2)
line x2,y1,x1,y1
line x2,y2,x2,y1
line x1,y2,x2,y2
line x1,y1,x1,y2
EndFunction



SpellSword

Quote from: kevin on July 18, 2008, 08:49:25 PM
rather than storing the collision boundaries and the game scenery in same world, it's better to store them in separate worlds.
How do I assign which world a camera captures from?

Nonworking Mockup Example:
REM <Start-Up: Setup Worlds>
; Create World: 1
CreateWorld 1

; Capture To World: 1
CaptureToWorld 1

; Draw Line to World 1
LINE 350,250,250,250


; Create World: 2
CreateWorld 2

; Capture To World: 2
CaptureToWorld 2

; Draw Line to World 2
LINE 250,150,150,150


REM <Start-Up: Setup Cameras>
; Create Cameras 1 & 2
CreateCamera 1
CreateCamera 2

REM Command to set or change Camera's World - Example: 'SetCameraWorld CameraNumber,WorldNumber' <----------
; Set Camera 1 to World 2
SetCameraWorld 1,2
; Set Camera 2 to World 1
SetCameraWorld 2,1


Thank you for providing the CollisionBox() Function example.
When I dream,
I carry a sword in one hand,
a gun in the other...

kevin

See Docs->Worlds->   CameraGrabWorld or CameraGrabWorldAt