[SOLVED]Move graphics object on the screen

General FreeBASIC programming questions.
exagonx
Posts: 315
Joined: Mar 20, 2009 17:03
Location: Italy
Contact:

[SOLVED]Move graphics object on the screen

Post by exagonx »

Hello friends.

These days I'm trying to write a code where it allows me to move a graphic figure without erasing the underlying background, currently I recreate the background from scratch but this leads to an unpleasant graphic effect especially on older PCs.

There is a way to do it, I was trying to use sprites but surely I am not able to apply them.

Code: Select all

screen 19,32
dim keypressed as string
dim as integer CoX, CoY
dim image as any ptr
image = imagecreate(101,101,rgb(255,255,255))
circle (50,50),50,rgb(255,0,0)

get (0,0)-(100,100),image


do 
	keypressed = inkey
	
	if keypressed = chr(255) & "H" then CoY = CoY - 1
	if keypressed = chr(255) & "P" then CoY = CoY + 1
	if keypressed = chr(255) & "M" then CoX = CoX + 1
	if keypressed = chr(255) & "K" then CoX = CoX - 1
	
	screenlock()
	put (CoX,CoY),image,pset
	screenunlock()
	sleep 1

loop until keypressed = chr(27)


Last edited by exagonx on Feb 21, 2022 17:20, edited 1 time in total.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Move graphics object on the screen

Post by fxm »

Since you are moving the figure at most a single pixel at each step of the loop, you can enlarge the image with a frame of one pixel around the circle:

Code: Select all

screen 19,32
dim keypressed as string
dim as integer CoX, CoY
dim image as any ptr
image = imagecreate(103,103,rgb(255,255,255))
circle (51,51),50,rgb(255,0,0)

get (0,0)-(102,102),image


do 
	keypressed = inkey
	
	if keypressed = chr(255) & "H" then CoY = CoY - 1
	if keypressed = chr(255) & "P" then CoY = CoY + 1
	if keypressed = chr(255) & "M" then CoX = CoX + 1
	if keypressed = chr(255) & "K" then CoX = CoX - 1
	
	screenlock()
	put (CoX,CoY),image,pset
	screenunlock()
	sleep 1

loop until keypressed = chr(27)

imagedestroy image
exagonx
Posts: 315
Joined: Mar 20, 2009 17:03
Location: Italy
Contact:

Re: Move graphics object on the screen

Post by exagonx »

fxm wrote: Feb 19, 2022 20:11 Since you are moving the figure at most a single pixel at each step of the loop, you can enlarge the image with a frame of one pixel around the circle:

Code: Select all

screen 19,32
dim keypressed as string
dim as integer CoX, CoY
dim imageA as any ptr
dim imageB as any ptr

imageA = imagecreate(103,103,rgb(255,255,255))
circle imageA,(51,51),50,rgb(255,0,0)

imageB = imagecreate(203,203,rgb(255,255,255))
circle imageB,(50,50),50,rgb(255,0,0)




	put (100,100),imageB,pset

do 
	keypressed = inkey
	
	if keypressed = chr(255) & "H" then CoY = CoY - 1
	if keypressed = chr(255) & "P" then CoY = CoY + 1
	if keypressed = chr(255) & "M" then CoX = CoX + 1
	if keypressed = chr(255) & "K" then CoX = CoX - 1
	
	screenlock()
	put (CoX,CoY),imageA,pset
	screenunlock()
	sleep 1

loop until keypressed = chr(27)
Thanks for the tip, which even if it doesn't solve the question does take away the strip it leaves when moving.

However the problem is that the background image is overwritten, is there a way not to overwrite the background image?

That is, when I used sprites in other platforms these choose the layer and you can move them leaving the bottom and top layer intact as if you had three images drawn on overlapping transparent sheets.

I would like to create this effect but I have no idea if it is possible or if a library is needed.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Move graphics object on the screen

Post by dodicat »

You could make the three layers images.

Code: Select all

screen 19,32
dim keypressed as string
dim as integer CoX, CoY
dim image as any ptr
image = imagecreate(101,101,rgb(255,0,255))
for r as single=1 to 5 
circle image,(50,50),50-r,rgb(255,0,0)'make thicker
r-=.9
next


dim as any ptr back=imagecreate(800,600,0)
locate 5,13
print "Hello,bottom layer"
get (0,0)-(799,599),back

dim as any ptr top=imagecreate(800,600,rgb(255,0,255))
circle top,(100,100),20,rgb(0,200,0),,,,f
draw string top,(100-12,100-8),"TOP"

do 
	keypressed = inkey
	
	if keypressed = chr(255) & "H" then CoY = CoY - 1
	if keypressed = chr(255) & "P" then CoY = CoY + 1
	if keypressed = chr(255) & "M" then CoX = CoX + 1
	if keypressed = chr(255) & "K" then CoX = CoX - 1
	
	screenlock()
      put(0,0),back,pset
	put (CoX,CoY),image,trans
      put(0,0),top,trans
	screenunlock()
	sleep 1

loop until keypressed = chr(27)
imagedestroy back
imagedestroy image
imagedestroy top

  
exagonx
Posts: 315
Joined: Mar 20, 2009 17:03
Location: Italy
Contact:

Re: Move graphics object on the screen

Post by exagonx »

dodicat wrote: Feb 19, 2022 22:03 You could make the three layers images.

Code: Select all


	screenlock()
      put(0,0),back,pset
	put (CoX,CoY),image,trans
      put(0,0),top,trans
	screenunlock()
  
Thanks, so I have to use trans instead of pset, all clear.
RockTheSchock
Posts: 252
Joined: Mar 12, 2006 16:25

Re: Move graphics object on the screen

Post by RockTheSchock »

Just a starting point for OOP. U would need to implement into SpriteType procedures for moving and putting sprites to screen. Than you can create an array of sprites and it's a small step to play with 4 players and 4 cirlcles. And in the main loop you could place some sort of collission detection.

Code: Select all

#include "fbgfx.bi"

Type SpriteType
	Declare Constructor (w As Integer=101,h As Integer=101,x As Integer=0,y As Integer=0)
	Declare Destructor () 
	x As Integer
	y As Integer
	w As Integer
	h As Integer
	image As Any Ptr
End Type

Constructor SpriteType(w As Integer,h As Integer,x As Integer=0,y As Integer=0)
	This.image = imagecreate(w,h,rgb(255,0,255))
	This.w = w
	this.h = h
	This.x = x
	this.y = y  
End Constructor

Destructor SpriteType
	ImageDestroy This.image
End Destructor


CONST As Integer RES_X=800,RES_Y=600
ScreenRes RES_X, RES_Y,32


'Generating background image
Dim As Any Ptr background = ImageCreate(RES_X,RES_Y)
For x As Integer = 0 To 799
	For y As Integer=0 To 599
		PSet background,(x,y),RGB(x Mod 256,y Mod 256, (x+y) Mod 256)
	Next
Next


'Generating circle image
Dim sprite As SpriteType = SpriteType(,,RES_X/2,RES_Y/2)
Circle sprite.image,(50,50),50,rgb(255,0,0)


do
	if MultiKey(FB.SC_LEFT ) And sprite.x>0 then sprite.x = sprite.x - 1
	if MultiKey(FB.SC_RIGHT) And sprite.x+sprite.w<RES_X Then sprite.x = sprite.x + 1
	if MultiKey(FB.SC_UP   ) And sprite.y>0 then sprite.y = sprite.y - 1
	if MultiKey(FB.SC_DOWN ) And sprite.y+sprite.h<RES_Y Then sprite.y = sprite.y + 1

	screenlock()
	put (0,0),background,PSet

	put (sprite.x,sprite.y),sprite.image,trans
	screenunlock()
	sleep 1
loop Until MultiKey(FB.SC_ESCAPE)
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Move graphics object on the screen

Post by Munair »

What I often do to avoid confusion between parameters and members:

Code: Select all

Type SpriteType
  Declare Constructor (_w As Integer=101,_h As Integer=101,_x As Integer=0,_y As Integer=0)
  Declare Destructor ()
  x As Integer
  y As Integer
  w As Integer
  h As Integer
  image As Any Ptr
End Type

Constructor SpriteType(_w As Integer,_h As Integer,_x As Integer=0,_y As Integer=0)
  image = imagecreate(_w, _h,rgb(255,0,255))
  w = _w
  h = _h
  x = _x
  y = _y
End Constructor
Also eliminates the use of This.
exagonx
Posts: 315
Joined: Mar 20, 2009 17:03
Location: Italy
Contact:

Re: Move graphics object on the screen

Post by exagonx »

RockTheSchock wrote: Feb 19, 2022 23:26 Just a starting point for OOP. U would need to implement into SpriteType procedures for moving and putting sprites to screen. Than you can create an array of sprites and it's a small step to play with 4 players and 4 cirlcles. And in the main loop you could place some sort of collission detection.

Code: Select all

#include "fbgfx.bi"

Your example is quite complex for those who have not dealt with classes and types also does not clarify a thing

What exactly is this header (#include "fbgfx.bi") for?
RockTheSchock
Posts: 252
Joined: Mar 12, 2006 16:25

Re: Move graphics object on the screen

Post by RockTheSchock »

Munair wrote: Feb 20, 2022 6:50 eliminates the use of This
that's a matter of taste. For me it's clearer to use "this" and not to use underscore.
exagonx wrote: Feb 20, 2022 7:40 Your example is quite complex for those who have not dealt with classes and types also does not clarify a thing
fbgfx.bi is just a header file shipped with freebasic including some graphics and other useful stuff. It enables the use of MultiKey function.

I wanted to give you a starting point. The German wiki has some nicer examples than the official wiki.

https://freebasic.net/wiki/KeyPgConstructor
https://freebasic.net/wiki/ProPgTypeObjects

https://www.freebasic-portal.de/befehls ... r-110.html
https://www.freebasic-portal.de/befehls ... g-638.html
exagonx
Posts: 315
Joined: Mar 20, 2009 17:03
Location: Italy
Contact:

Re: Move graphics object on the screen

Post by exagonx »

RockTheSchock wrote: Feb 20, 2022 8:25
Munair wrote: Feb 20, 2022 6:50 eliminates the use of This
that's a matter of taste. For me it's clearer to use "this" and not to use underscore.
exagonx wrote: Feb 20, 2022 7:40 Your example is quite complex for those who have not dealt with classes and types also does not clarify a thing
fbgfx.bi is just a header file shipped with freebasic including some graphics and other useful stuff. It enables the use of MultiKey function.

I wanted to give you a starting point. The German wiki has some nicer examples than the official wiki.

https://freebasic.net/wiki/KeyPgConstructor
https://freebasic.net/wiki/ProPgTypeObjects

https://www.freebasic-portal.de/befehls ... r-110.html
https://www.freebasic-portal.de/befehls ... g-638.html
Thank you for the tip.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Move graphics object on the screen

Post by dodicat »

Move discs by mouse example.

Code: Select all


Type disc
    As single x,y 'centre
    As Long radius
    As Ulong colour
    As Any Ptr im
    Declare Destructor
End Type

Destructor disc
Imagedestroy im
Print "image destroyed"
Sleep 100
End Destructor

'is x,y in disc c?
Function indisc(c As disc,x As Long,y As Long) As Long
    Return (c.x-x)*(c.x-x) +(c.y-y)*(c.y-y)<= c.radius*c.radius
End Function

Function DetectDiscsAreClose( B1 As disc,B2 As disc) As Single 
    Dim As single xdiff = B2.x-B1.x
    Dim As single ydiff = B2.y-B1.y
    If Abs(xdiff) > (B2.radius+B1.radius) Then Return 0
    If Abs(ydiff) > (B2.radius+B1.radius) Then Return 0
    Var L=Sqr(xdiff*xdiff+ydiff*ydiff)
    If L<=(B2.radius+B1.radius) Then Function=L Else Function=0
End Function

Sub TouchingDiscs(b() As disc)
    For n1 As Long=Lbound(b) To Ubound(b)-1
        For n2 As Long=n1+1 To Ubound(b)
            Dim As Single  L= DetectDiscsAreClose(b(n1),b(n2))
            If L Then
                Dim As Single  impulsex=(b(n1).x-b(n2).x)
                Dim As Single  impulsey=(b(n1).y-b(n2).y)
                Dim As Single lngth=Sqr(impulsex*impulsex+impulsey*impulsey)
                impulsex/=lngth
                impulsey/=lngth
                Dim As Single  impactx=-Sgn(b(n1).x-b(n2).x)
                Dim As Single  impacty=-Sgn(b(n1).y-b(n2).y)
                Dim As Single  dot=impulsex*impactx+impulsey*impacty
                b(n1).x-=dot*impulsex*2 
                b(n1).y-=dot*impulsey*2 
                b(n2).x+=dot*impulsex*2
                b(n2).y+=dot*impulsey*2 
            End If
        Next n2
    Next n1
End Sub

Function map(a As Double,b As Double,x As Double,c As Double,d As Double) As Double
    Return (d-c)*(x-a)/(b-a)+c
End Function

Sub drawdiscs(d() As disc)
    Static As Any Ptr back
    If back=0 Then
        back=Imagecreate(800,600,Rgb(255,255,255))
        For x As Long=0 To 799
            For y As Long=0 To 699
                Pset back,(x,y),Rgba(x,x xor y,y,100)
            Next
        Next
    End If
    TouchingDiscs(d())
    Screenlock
    Put(0,0),back,Pset
    For n As Long=Lbound(d) To Ubound(d)
        Put(d(n).x-d(n).radius,d(n).y-d(n).radius),d(n).im,trans
    Next n
    Screenunlock
    For n As Long=1 To 100000:Next 'wait a bit
    End Sub
    
    Sub creatediscs(d() As disc)
        For n As Long=Lbound(d) To Ubound(d)
            d(n).x=Rnd*800
            d(n).y=Rnd*600
            d(n).radius=10+Rnd*50
            d(n).colour=Rnd*Rgb(255,255,255)
            d(n).im=Imagecreate(2*d(n).radius+2,2*d(n).radius+2)
            Var c=d(n).colour
            Var f=.99
            Var rd=Cast(Ubyte Ptr,@c)[2],gr=Cast(Ubyte Ptr,@c)[1],bl=Cast(Ubyte Ptr,@c)[0]
            For r As Long= d(n).radius To 0 Step -1
                Var r1=map(0,d(n).radius,r,rd,rd\2)
                Var g1=map(0,d(n).radius,r,gr,gr\2)
                Var b1=map(0,d(n).radius,r,bl,bl\2)
                Circle d(n).im,(d(n).radius,d(n).radius),r,Rgb(r1,g1,b1),,,,f
            Next r
        Next n
    End Sub
    
    Sub MoveByMouse(d() As disc,i As Long,mx As Long,my As Long,button As Long)
        Dim As Long x=mx,y=my,dx,dy,b
        Dim As Long idx=d(i).x-mx,idy=d(i).y-my
        While button = 1
            drawdiscs(d())
            Getmouse mx,my,,button
            If mx>0 And my>0 Then
                If mx<>x Or my<>y Then
                    dx = mx - x
                    dy = my - y
                    x = mx
                    y = my
                    d(i).x=x+dx+idx
                    d(i).y=y+dy+idy
                    'don't let active disc off screen (pushed ones are ignored)
                    If d(i).x< -d(i).radius+5 Then d(i).x=-d(i).radius+5
                    If d(i).y< -d(i).radius+5 Then d(i).y=-d(i).radius+5
                    If d(i).x>800+d(i).radius-5 Then d(i).x=800+d(i).radius-5
                    If d(i).y>600+d(i).radius-5 Then d(i).y=600+d(i).radius-5
                End If
            End If
        Wend
    End Sub
    
    Sub mainsub
        Screen 19,32,,64
        Windowtitle "Push things around, press <ecsape> to end."
        Dim As disc d(1 To 20)
        creatediscs(d())
        
        Dim As Long mx,my,button,ox,oy
        Do
            Getmouse mx,my,,button
            For n As Long=Lbound(d) To Ubound(d)
                If indisc(d(n),mx,my) And button=1 Then 'if mouse is in a disc
                    movebymouse(d(),n,mx,my,button)        'smooth drag keeping positions intact
                    Exit For
                End If
            Next n
            drawdiscs(d())
        Loop Until Inkey=Chr(27)'escape key
    End Sub
    '==================
    mainsub
    
    End
    
    
    
     
Roland Chastain
Posts: 1002
Joined: Nov 24, 2011 19:49
Location: France
Contact:

Re: Move graphics object on the screen

Post by Roland Chastain »

Very nice dodicat.

@exagonx

Here is yet another example.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Move graphics object on the screen

Post by BasicCoder2 »

@dodicat
Showing what is possible with FreeBASIC, good piece of code.

@Roland
And for us English only speakers :(

Code: Select all

' *** CODE 4.3: PUT with background saving

#DEFINE PI 3.141592653589793
SCREENRES 300, 200, 32                ' Graphic screen with 32-bit color depth
DIM AS ANY PTR image, background
DIM AS ULONG light = RGB(255, 64, 64) ' lighter color value of the stone
DIM AS ULONG dark = RGB(192, 0, 0)    ' dark color value of the stone

' Write image to buffer
image = IMAGECREATE(40, 40)
background = IMAGECREATE(40, 40)
CIRCLE image, (20, 25), 15, dark, PI, 0, .6
LINE image, (5, 20)-STEP (0, 5), dark
LINE image, (35, 20)-STEP (0, 5), dark
CIRCLE image, (20, 20), 15, dark, , , .6
PAINT image, (20, 30), dark, dark
PAINT image, (20, 20), light, dark

' create background
LINE (50, 50)-(250, 150), RGB(0,255,0), BF ' green rectangle ...
LINE (80, 80)-(220, 120), RGB(0,0,255), BF ' ... and a blue one inside

DIM AS INTEGER mx = 0, my = 0, mb ' Mouse position and button status
DIM AS INTEGER oldX = 0, oldY = 0          ' last noted mouse position
SETMOUSE mx, my, 0, 1                      ' Limit mouse to window
GET (oldX, oldY)-STEP(39, 39), background  ' save background
PUT (mx, my), image, TRANS
DO
  GETMOUSE mx, my, , mb           ' determine new position ...
  IF mx > 260 THEN mx = 260          ' ... and adjust to the limits
  IF my > 160 THEN my = 160
  IF mx <> oldX OR my <> oldY THEN   ' mouse was moved
    SCREENLOCK
    PUT (oldX, oldY), background, PSET       ' restore old position
    GET (mx, my)-STEP(39, 39), background    ' save background
    PUT (mx, my), image, TRANS               ' draw new position
    SCREENUNLOCK
    oldX = mx                           ' remember new position
    oldY = my
  END IF
  SLEEP 1
LOOP UNTIL mb > 0 OR INKEY = CHR(27)    ' exit with mouse click or ESC
IMAGEDESTROY image                      ' Release image buffer
IMAGEDESTROY background

fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Move graphics object on the screen

Post by fxm »

You could have taken the opportunity to fix the bug (of the freebasic-portal.de example) when the mouse pointer leaves the window (a test on the values returned by GetMouse to add):
Aborting due to runtime error 1 (illegal function call) at line 35 of .....
Roland Chastain
Posts: 1002
Joined: Nov 24, 2011 19:49
Location: France
Contact:

Re: Move graphics object on the screen

Post by Roland Chastain »

@fxm

Here (on Linux), the mouse pointer cannot leave the window. Or maybe I don't really understand what you mean.

@BasicCoder2

Thanks for the translation.
Post Reply