Gravity

BlitzPlus Forums/BlitzPlus Programming/Gravity

QuickSilva(Posted 2003) [#1]
Hi all,

I`m having a small problem with gravity that I hope someone can help me with. In my game, a 2D platformer
I have an item in the sky which slowly falls to the ground gradually increasing in speed (gravity). When
the item hits an obstacle, in this case the ground, it stops. This worked fine when I used a simple
item\yposition=item\yposition+1 method, the item fell and rested on the ground when and where it hit it but
now I have added gravity the effect is ruined. The item still stops when it meets the ground but lands
slightly out, either just to high or just to low.

Here is what I`m using at the moment,

; create an items type.
type items
field xposition#,yposition#,fallspeed#,landed
end type

; set a constant rate for gravity.
const gravity#=0.1

; test the item for a collision.
item\collided=ImagesCollide(item\image,item\xposition,item\yposition,0,ground,groundxposition,groundyposition,0)

; if the item has collided with the ground then stop it otherwise gently increase its fallspeed by the constant
; gravity variable and add this to the items yposition to simulate simple gravity.

if item\collided=True
item\landed=true
else
item\yposition=item\yposition+item\fallspeed
item\fallspeed=item\fallspeed+gravity
endif

Like I said what happens is the item does stop when it hits the ground but it is off by a couple of pixels
which looks kind of ugly. This is obviously caused by the fallrate variable (er... I think) so I thought
that just subtracting the fallrate (at the time of impact from the items yposition) would cure this but it
doesn`t :( Maybe using floor or ceil would help round this floating point value to a whole value and cure
the problem but I`m not really sure how to implement it.

Any suggestions, Or other approaches?

Thanks,
Jason.


Stevie G(Posted 2003) [#2]
Depending on how far the object has to fall your item\fallspeed could get quite high which is likely to be the reason for this. Just a suggestion but why don't you limit the speed with something like :-

item\fallspeed=item\fallspeed + gravity * (item\fallspeed < max_speed )

Another method could be adding a bounce when the object hits the floor. i.e. hit floor then item\fallspeed=- item\fallspeed *.25 so that, while it may not land perfectly due to it's speed, the bounce will reduce it speed so that say, by the 2nd bounce the object will settle nicely on the ground. You would only say it has 'landed' when it's fallspeed was, say <1 pixel.


QuietBloke(Posted 2003) [#3]
WHen the object hits the ground it may be moving at more than 1 pixel so at the time of collision you might end up inside the ground. Just subtracting the fallspeed at collision wont fix it becuase you might end up above the ground.

e.g.
The object is 2 pixels above the ground. It moves down 3 pixels. So now it has fallen 1 pixel into the ground which is not good. If you subtract the speed it will end up hovering 2 pixels above the ground which is still no good.

The quickest way I can think of fixing this would be to add code after the collision check. Set a loop up to subtract 1 from the yposisition until you are no longer colliding. This should make you end up sitting nicely onto of the ground.

Hope that makes sense.


JoeGr(Posted 2003) [#4]
No need for the loop described above. As soon as your item\collided=True just reposition the object at ground level. Or have I misunderstood?

Unless your ground is uneven. Then do what QuietBloke says.


QuietBloke(Posted 2003) [#5]
Good point Joe.


cyberseth(Posted 2003) [#6]
If you know what ground level is, then you can do that. But if you don't, or if you're bouncing on an irregularly shaped platform, you won't know what the ground level is, so you'll have to do the method described above where you move down 1 pixel (or 2 pixels if you want it quicker but less accurate) until the collision occurs.


JoeGr(Posted 2003) [#7]
But then there would be no accelleration, which is what the thread is about. Blitz can do pixel-perfect collision using ImagesCollide() so why would irregular platforms or not knowing the ground level ever be a problem? Unless there was a huge amount of stuff going on.


MadMax(Posted 2003) [#8]
There is a very easy solution to this, There is no need to only check for collision when you draw the image, you have to check all pixels in the trajectory, in a for next loop whith an exit in case you have collided. This way the object can travel as fast as you like, it will detect even a 1 pixel platform.

Hope this helps, if you DO need some sample code just ask.


QuickSilva(Posted 2003) [#9]
If you could provide some sample code it would be nice :)
Jason.


pemaden25(Posted 2003) [#10]
Am I wrog in thinking that gravity does not increase in speed as a object falls? It's the same all the way down correct? Or did my physics teacher tell us wrong =P


Pete H(Posted 2003) [#11]
The speed increases until it attains a fixed speed. All onjects fall at the same speed eventually.

Before you ask, I can't remember the speed. But I do know it changes depending on the amout of gravity there is.

This is all from memory, and the last physics book I read was 15 years ago!


pemaden25(Posted 2003) [#12]
Well, I'll bieleve you then =P After all, I did just start physics class about 3 days ago


MadMax(Posted 2003) [#13]
Well here is some code,

For n=1 To fall_speed
	If ReadPixel(x,y)<>ctrl_color
		y=y+1
	Else
		Exit
	EndIf
Next


Here is a little sample so you can try it.

;****************************************************************
;*                                                              *
;*                   PIXEL PERFECT COLLISION                    *
;*                                                              *
;*                          by MadMax                           *
;*                                                              *
;****************************************************************
x=400
y=100
fall_speed=5

Graphics 800,600,16

screen=CreateImage(800,600)
sprite=CreateImage(20,64)
cursor=CreateImage(32,32)

HandleImage sprite,10,64

MidHandle cursor 

Color 255,0,0

SetBuffer ImageBuffer(screen)

Rect 0,0,5,5,True

Line 0,500,799,500

ctrl_color=ReadPixel(2,2)

Color 0,0,0

Rect 0,0,5,5,True

Color 0,0,255

SetBuffer ImageBuffer(sprite)

Rect 0,0,20,64,True


Color 255,0,255

SetBuffer ImageBuffer(cursor)

Rect 15,0,2,32,True
Rect 0,15,32,2,True
Rect 8,0,16,2,True
Rect 8,30,16,2,True
Rect 0,8,2,16,True
Rect 30,8,2,16,True

Color 255,255,0

Rect 14,14,4,4,True

Color 255,255,255

SetBuffer BackBuffer()

While Not KeyDown(1)

If MouseHit(1)
x=MouseX()
y=MouseY()
EndIf

If KeyHit(74) Then fall_speed=fall_speed-1
If KeyHit(78) Then fall_speed=fall_speed+1

Cls

DrawImage screen,0,0

;*******************************************************
;           THE FOLLOWING IS THE RELEVANT BIT
For n=1 To fall_speed
	If ReadPixel(x,y)<>ctrl_color
		y=y+1
	Else
		Exit
	EndIf
Next
;*******************************************************

DrawImage sprite,x,y

Text 20,20,"Fall Speed :"+fall_speed

If MouseY()>484 Then MoveMouse(MouseX(),484)

DrawImage cursor,MouseX(),MouseY()

Color 255,255,0
Text 400,525,"USE '+' '-' KEYS ON KEYPAD TO CHANGE FALL SPEED.",True,True
Color 155,200,255
Text 400,550,"USE LEFT MOUSE BUTTON TO PLACE SPRITE",True,True

Color 255,255,255

Flip True

Wend


Hope it helps


Curtastic(Posted 2003) [#14]
That method is slower than what quietbloke described, and it should do the same thing as his method only it could be used instead if you need to make sure that the object doesnt pass through the entire wall.


Curtastic(Posted 2003) [#15]
this could be added, unless you wat something more flexible.

; test the item for a collision. 
item\collided=ImagesCollide(item\image,item\xposition,item\yposition,0,ground,groundxposition,groundyposition,0) 

; if the item has collided with the ground then stop it otherwise gently increase its fallspeed by the constant 
; gravity variable and add this to the items yposition to simulate simple gravity. 

if item\collided=True 
item\landed=true 
repeat
item\yposition=item\yposition-1
until ImagesCollide(item\image,item\xposition,item\yposition,0,ground,groundxposition,groundyposition,0)=false
else 
item\yposition=item\yposition+item\fallspeed 
item\fallspeed=item\fallspeed+gravity 
endif 



MadMax(Posted 2003) [#16]
@Corae,,, Nothing against the way QuietBloke suggested, only trouble is that in some cases it's possible to go through a wall. As for the speed overhead, I'd say this is insignificant, and I can hardly imagine a game that needs to have over 2000 sprites falling onto a platform (there is no way to display them all without them covering each other) also the FPS decrease is more due to drawing each Sprite than because of the collision system. I have also found out in my tests that there is a very slight performance hit of around with 2000 sprites falling at a speed of 1000 pixels per frame (still performs well over 60 FPS with 2000 sprites) and it still detects a one pixel platform.

Also with the method QuietBloke suggested, once the sprite hits the platform, the speed hit will be very similar.

So unless you need to have 20000 sprites falling at a rate of hundreds of pixels. And remember that in this case drawimage will dramaticaly slow down performance(maybe you'd need to write an ASM routine that's more optimized than Blitz' one). This method works very well.

Note: locking the buffer and using readpixelfast will of course increment performance.


QuickSilva(Posted 2003) [#17]
Thanks guys, thats helped a lot.

Regards,
Jason.