Posted: 4th Oct 2022 1:10
I'm creating an effect where the sprite has motion trail, that works, but the problem is that depending on the framerate the behavior changes

this script uses time to keep everything at the same speed regardless of framerate but it still doesn't work correctly, the problem here is not the speed
but how many times does it run

at 60fps


at infinite fps

Effect does not play or is reduced at higher framerates


I created here an example with my trail function to demonstrate

+ Code Snippet
SetErrorMode(2)
SetWindowTitle( "Trail" )
SetWindowSize( 1280, 720, 0 )
SetWindowAllowResize( 1 )
SetVirtualResolution( 320, 180 )
SetOrientationAllowed( 1, 1, 1, 1 )
SetSyncRate( 60, 1 )
SetScissor( 0,0,0,0 )
UseNewDefaultFonts( 1 )
SetClearColor(80, 80, 80)

spr as integer
spr = CreateSprite(0)
SetSpriteSize(spr, 25, 25)
SetSpritePosition(spr, 15, 15)

type _trailEffect
  sprMain as integer
  sprs as integer[]
  active as integer
  time as float
endtype

global List_TrailEffect as _trailEffect[]

type _shot
	spr as integer
	angle as float
endtype

global shots as _shot[]

do

	if GetPointerPressed() = 1
		Shot( ScreenToWorldX(getpointerX()), ScreenToWorldY(getpointerY()), spr)
	endif

	for i = 0 to shots.length
		SetSpritePosition(shots[i].spr, getspriteX(shots[i].spr) + (300 * sin(shots[i].angle) * GetFrameTime()), getspriteY(shots[i].spr) + (300 * cos(shots[i].angle) * GetFrameTime()))
	next i

	if GetRawKeyPressed(69) // E
		SetSyncRate( 0, 1 )
	endif

	if GetRawKeyPressed(87) // W	
		SetSyncRate( 60, 1 )
	endif

	print(ScreenFPS())
	update_Trail()
    Sync()
loop


function create_Trail(spr as integer)
  for i = 0 to List_TrailEffect.length // prevent repetition
    if List_TrailEffect[i].sprMain = spr then exitfunction
  next i

  t as _trailEffect
  t.time = 2.0
  t.sprMain = spr
  t.active = 1
  List_TrailEffect.insert(t)
endfunction

function update_Trail()
    for i = 0 to List_TrailEffect.length
      if List_TrailEffect[i].time > 0.001
        if not GetSpriteExists(List_TrailEffect[i].sprMain) //If the sprite where the effect is applied does not exist, disables
          List_TrailEffect[i].active = 0
        endif

        // create effect
        if List_TrailEffect[i].active = 1
          n = CreateSprite(GetSpriteImageID(List_TrailEffect[i].sprMain))
          SetSpriteSize(n, 3, 3)
          SetSpritePosition(n, GetSpriteX(List_TrailEffect[i].sprMain),  GetSpriteY(List_TrailEffect[i].sprMain))
          List_TrailEffect[i].sprs.insert(n)
        endif

        // Reduces the alpha of sprites queued backwards
        for k = List_TrailEffect[i].sprs.length to 0 step -1
          alfa = GetSpriteColorAlpha(List_TrailEffect[i].sprs[k])
          dec alfa, 50
          if alfa < 0 then alfa = 0
          SetSpriteColorAlpha(List_TrailEffect[i].sprs[k], alfa)
          if alfa = 0
            DeleteSprite(List_TrailEffect[i].sprs[k])
            List_TrailEffect[i].sprs.remove(k)
          endif
        next k

        if List_TrailEffect[i].sprs.length = -1
          List_TrailEffect.remove(i)
          continue
        endif

        List_TrailEffect[i].time = 0
      endif

      inc List_TrailEffect[i].time, GetFrameTime()
    next i
endfunction



function Shot(AimX as integer, AimY as integer, char as integer)
	xi as float
	yi as float
	dirx as float
	diry as float
	d as _shot

	xi = getspriteX(char) + (GetSpriteWidth(char) / 2)
	yi = (getspriteY(char) + (GetSpriteHeight(char) / 2))
	dirx = (AimX) - xi
	diry = yi - (AimY)
	angle = atanfull(dirx, diry)
	d.angle = angle

	sprShot = CreateSprite(0)
	d.spr = sprShot
	SetSpriteSize(sprShot, 3, 3)
	SetSpritePositionByOffset(sprShot, GetSpriteXByOffset(char), GetSpriteYByOffset(char))

	create_Trail(sprShot)
	shots.insert(d)
endfunction
Posted: 4th Oct 2022 3:02
Hi brunuu,

At a glance, it looks like you left a static gap potential in your timer. You're checking for >0.001, but then just reset to 0 instead of accounting for any differential that may be present whenever your limit is exceeded. This can produce a 'wobble' at different framerates. I would initially recommend changing these lines:

+ Code Snippet
        // List_TrailEffect[i].time = 0
        List_TrailEffect[i].time = List_TrailEffect[i].time - 0.001


This way, your timer deducts by a fixed 0.001 to only remove the step/tick amount you want to apply, accounting for any leftover value above 0.001. So if you're effect time is say, 0.001225, you subtract 0.001 to leave the next phase of your counter starting at 0.000225. This smooths out the movement regardless of framerate and accounts for potential variation since you will almost never have a scenario with a perfect 0 to 0.001 step.

If that doesn't solve what you're trying to do, then a closer look at your timing mechanism may be needed to determine what other possible issue there may be.
Posted: 4th Oct 2022 3:21
The active variable does not store time, Its only use is just to know if the main sprint exists so when it ceases to exist it no longer creates the effect
Posted: 4th Oct 2022 3:41
Woops, yup, I meant the time element. Corrected in the post above and here:

+ Code Snippet
        // List_TrailEffect[i].time = 0
        List_TrailEffect[i].time = List_TrailEffect[i].time - 0.001
Posted: 4th Oct 2022 10:01
yeh, that will fix the stutters
but not the problem of the effect being different at different frame rates, and I have no idea why
Posted: 4th Oct 2022 10:56
I think it is because you are reducing the alpha value of your trailing effect by fixed values. you should also reduce this based on time.
Or you calculate the alpha value according to the age of the 'trailing patikel'. (age / max_age)*255.0
Posted: 4th Oct 2022 12:13
I've tried something similar, but the problem still persists.


if I try it without making the sprite transparent, I can see that more sprites are created the higher the framerate


Posted: 4th Oct 2022 13:32
Replace line 88 with this one and you will get a similar effect.
+ Code Snippet
          dec alfa, 2000.0*GetFrameTime()

it doesn't look the same now. that's because multiple sprites are rendered over the same spot using additive blend mode. That's why you have a slower gradient.
Posted: 4th Oct 2022 14:52
This is the suggestion I posted on discord, with an additional note. I tried it and it works for me. I also tried with not decreasing the alpha and it works as expected.

+ Code Snippet
SetErrorMode(2)
SetWindowTitle( "Trail" )
SetWindowSize( 1280, 720, 0 )
SetWindowAllowResize( 1 )
SetVirtualResolution( 320, 180 )
SetOrientationAllowed( 1, 1, 1, 1 )
SetSyncRate( 60, 1 )
SetScissor( 0,0,0,0 )
UseNewDefaultFonts( 1 )
SetClearColor(80, 80, 80)
 
spr as integer
spr = CreateSprite(0)
SetSpriteSize(spr, 25, 25)
SetSpritePosition(spr, 15, 15)
 
type _trailEffect
  sprMain as integer
  sprs as integer[]
  active as integer
  time as float
endtype
 
global List_TrailEffect as _trailEffect[]
 
type _shot
    spr as integer
    angle as float
endtype
 
global shots as _shot[]
 
do
 
    if GetPointerPressed() = 1
        Shot( ScreenToWorldX(getpointerX()), ScreenToWorldY(getpointerY()), spr)
    endif
 
    for i = 0 to shots.length
        SetSpritePosition(shots[i].spr, getspriteX(shots[i].spr) + (300 * sin(shots[i].angle) * GetFrameTime()), getspriteY(shots[i].spr) + (300 * cos(shots[i].angle) * GetFrameTime()))
    next i
 
    if GetRawKeyPressed(69) // E
        SetSyncRate( 0, 1 )
    endif
 
    if GetRawKeyPressed(87) // W    
        SetSyncRate( 60, 1 )
    endif
 
    print(ScreenFPS())
    update_Trail()
    Sync()
loop
 
 
function create_Trail(spr as integer)
  for i = 0 to List_TrailEffect.length // prevent repetition
    if List_TrailEffect[i].sprMain = spr then exitfunction
  next i
 
  t as _trailEffect
  t.time = 2.0
  t.sprMain = spr
  t.active = 1
  List_TrailEffect.insert(t)
endfunction
 
function update_Trail()
    for i = 0 to List_TrailEffect.length
      if List_TrailEffect[i].time > 0.03
        if not GetSpriteExists(List_TrailEffect[i].sprMain) //If the sprite where the effect is applied does not exist, disables
          List_TrailEffect[i].active = 0
        endif
 
        // create effect
        if List_TrailEffect[i].active = 1
          n = CreateSprite(GetSpriteImageID(List_TrailEffect[i].sprMain))
          SetSpriteSize(n, 3, 3)
          SetSpritePosition(n, GetSpriteX(List_TrailEffect[i].sprMain),  GetSpriteY(List_TrailEffect[i].sprMain))
          List_TrailEffect[i].sprs.insert(n)
        endif
 
        // Reduces the alpha of sprites queued backwards
        for k = List_TrailEffect[i].sprs.length to 0 step -1
          alfa = GetSpriteColorAlpha(List_TrailEffect[i].sprs[k])
          dec alfa, 50
          if alfa < 0 then alfa = 0
          SetSpriteColorAlpha(List_TrailEffect[i].sprs[k], alfa)
          if alfa = 0
            DeleteSprite(List_TrailEffect[i].sprs[k])
            List_TrailEffect[i].sprs.remove(k)
          endif
        next k
 
        if List_TrailEffect[i].sprs.length = -1
          List_TrailEffect.remove(i)
          continue
        endif
 
        List_TrailEffect[i].time = 0
// you could change the above line to
//        dec List_TrailEffect[i].time 0.03
      endif
 
      inc List_TrailEffect[i].time, GetFrameTime()
    next i
endfunction
 
 
 
function Shot(AimX as integer, AimY as integer, char as integer)
    xi as float
    yi as float
    dirx as float
    diry as float
    d as _shot
 
    xi = getspriteX(char) + (GetSpriteWidth(char) / 2)
    yi = (getspriteY(char) + (GetSpriteHeight(char) / 2))
    dirx = (AimX) - xi
    diry = yi - (AimY)
    angle = atanfull(dirx, diry)
    d.angle = angle
 
    sprShot = CreateSprite(0)
    d.spr = sprShot
    SetSpriteSize(sprShot, 3, 3)
    SetSpritePositionByOffset(sprShot, GetSpriteXByOffset(char), GetSpriteYByOffset(char))
 
    create_Trail(sprShot)
    shots.insert(d)
endfunction
Posted: 4th Oct 2022 16:30
I tried some different values, and I like this effect the best but it depends on what you're going for. If you set the time threshold too low you won't get a consistent effect when you change the frame rate.

+ Code Snippet
SetErrorMode(2)
SetWindowTitle( "Trail" )
SetWindowSize( 1280, 720, 0 )
SetWindowAllowResize( 1 )
SetVirtualResolution( 320, 180 )
SetOrientationAllowed( 1, 1, 1, 1 )
SetSyncRate( 60, 1 )
SetScissor( 0,0,0,0 )
UseNewDefaultFonts( 1 )
SetClearColor(80, 80, 80)
 
spr as integer
spr = CreateSprite(0)
SetSpriteSize(spr, 25, 25)
SetSpritePosition(spr, 15, 15)
 
type _trailEffect
  sprMain as integer
  sprs as integer[]
  active as integer
  time as float
endtype
 
global List_TrailEffect as _trailEffect[]
 
type _shot
    spr as integer
    angle as float
endtype
 
global shots as _shot[]
 
do
 
    if GetPointerPressed() = 1
        Shot( ScreenToWorldX(getpointerX()), ScreenToWorldY(getpointerY()), spr)
    endif
 
    for i = 0 to shots.length
        SetSpritePosition(shots[i].spr, getspriteX(shots[i].spr) + (300 * sin(shots[i].angle) * GetFrameTime()), getspriteY(shots[i].spr) + (300 * cos(shots[i].angle) * GetFrameTime()))
    next i
 
    if GetRawKeyPressed(69) // E
        SetSyncRate( 0, 1 )
    endif
 
    if GetRawKeyPressed(87) // W    
        SetSyncRate( 60, 1 )
    endif
 
    print(ScreenFPS())
    update_Trail()
    Sync()
loop
 
 
function create_Trail(spr as integer)
  for i = 0 to List_TrailEffect.length // prevent repetition
    if List_TrailEffect[i].sprMain = spr then exitfunction
  next i
 
  t as _trailEffect
  t.time = 0.0
  t.sprMain = spr
  t.active = 1
  List_TrailEffect.insert(t)
endfunction
 
function update_Trail()
    for i = 0 to List_TrailEffect.length
      if List_TrailEffect[i].time > 0.06
        if not GetSpriteExists(List_TrailEffect[i].sprMain) //If the sprite where the effect is applied does not exist, disables
          List_TrailEffect[i].active = 0
        endif
 
        // create effect
        if List_TrailEffect[i].active = 1
          n = CreateSprite(GetSpriteImageID(List_TrailEffect[i].sprMain))
          SetSpriteSize(n, 3, 3)
          SetSpritePosition(n, GetSpriteX(List_TrailEffect[i].sprMain),  GetSpriteY(List_TrailEffect[i].sprMain))
          List_TrailEffect[i].sprs.insert(n)
        endif
 
        // Reduces the alpha of sprites queued backwards
        for k = List_TrailEffect[i].sprs.length to 0 step -1
          alfa = GetSpriteColorAlpha(List_TrailEffect[i].sprs[k])
          dec alfa, 50
          if alfa < 0 then alfa = 0
          SetSpriteColorAlpha(List_TrailEffect[i].sprs[k], alfa)
          if alfa = 0
            DeleteSprite(List_TrailEffect[i].sprs[k])
            List_TrailEffect[i].sprs.remove(k)
          endif
        next k
 
        if List_TrailEffect[i].sprs.length = -1
          List_TrailEffect.remove(i)
          continue
        endif
 
//        List_TrailEffect[i].time = 0
// you could change the above line to
        dec List_TrailEffect[i].time, 0.06
      endif
 
      inc List_TrailEffect[i].time, GetFrameTime()
    next i
endfunction
 
 
 
function Shot(AimX as integer, AimY as integer, char as integer)
    xi as float
    yi as float
    dirx as float
    diry as float
    d as _shot
 
    xi = getspriteX(char) + (GetSpriteWidth(char) / 2)
    yi = (getspriteY(char) + (GetSpriteHeight(char) / 2))
    dirx = (AimX) - xi
    diry = yi - (AimY)
    angle = atanfull(dirx, diry)
    d.angle = angle
 
    sprShot = CreateSprite(0)
    d.spr = sprShot
    SetSpriteSize(sprShot, 3, 3)
    SetSpritePositionByOffset(sprShot, GetSpriteXByOffset(char), GetSpriteYByOffset(char))
 
    create_Trail(sprShot)
    shots.insert(d)
endfunction
Posted: 4th Oct 2022 17:37
Yeh, I also tried something like this
and in fact increasing the time variable solves the problem
but for certain things I will need it to be lower

because this is really only a problem when it goes below +-0.05 seconds
Posted: 4th Oct 2022 17:45
If you need the time variable to be effectively lower, you will need to try a completely different approach because what you're asking for is to create new trail positions between frames.
Posted: 5th Oct 2022 15:46
I tried using particles, which works quite well, but unfortunately it is not possible to control the width and height of the particles individually, only the scale
Posted: 5th Oct 2022 20:14
I can think of two options you might try. The first is instead of placing a new trail sprite at the current position of the bullet sprite, interpolate the position between the bullet sprite and the most recent trail sprite based on the current value of time, or you could place the new trail sprite the desired distance from the previous one. You'd have to place the first trail sprite a bit differently, maybe place one at the bullet origin when the bullet is created.

The second way is instead of creating, fading, and then deleting trail sprites you could create them when the bullet is created and move them when the bullet moves.
Posted: 6th Oct 2022 6:35
I think it would look best with particles. Usually you don't need to distort the particle shape.
The only important thing is that you move the emitter with the bullet and not the particles themselves. (SetParticlesPosition)

Alternatively you can create the traileffect with a sprite with a gradient image. This would simplify the complexity of the code and is also resource friendly.


Here is my example:
this should be similar to what Prime describes in his second way.

+ Code Snippet
SetWindowSize(640,360,0)
SetVirtualResolution( 640,360)
SetSyncRate( 60, 1 ) 
SetScissor( 0,0,0,0 )
UseNewDefaultFonts( 1 ) 

#constant BULLET_SPEED		500

type Bullet
	bullet as integer
	trail as integer
	delay as float
	dirx as float
	diry as float
	speed as float
	active as integer
endtype
global bullets as bullet[]

CreateImageColor(1,255,0,0,255)		// bullet color
LoadImage(2,"gradient-white.png")	// bullet trail

do
	DrawEllipse(GetVirtualWidth()*0.5, GetVirtualHeight()*0.5, 20,20,0xff00ffff,0xff0000ff,1)
	if GetRawMouseLeftPressed() = 1
		// shot a bullet to the position of the mouse
		ShotTo(GetVirtualWidth()*0.5, GetVirtualHeight()*0.5, GetRawMouseX(), GetRawMouseY())
	endif
	
	// update all bullets
	UpdateBullets(GetFrameTime())
    
    print(bullets.length)
    Sync()
loop

// calculate the direction from posx,posy to tox,toy and create a bulet
function ShotTo(posx as float, posy as float, tox as float, toy as float)
	angle# = ATanFull(tox-posx,toy-posy)
	dirx# = sin(angle#)
	diry# = -cos(angle#)
	CreateBullet(posx, posy, dirx#, diry#)
endfunction

// create, set the angle and direction of the bullet
function CreateBullet(posx as float, posy as float, dirx as float, diry as float)
	b as Bullet
	id=-1
	
	// find and use a free bullet
	for i=0 to bullets.length
		if bullets[i].active = 0
			b = bullets[i]
			id = i
			exit
		endif
	next
	
	// if no bullet found create one
	if b.bullet = 0
		b.bullet = CreateSprite(1)
		SetSpriteSize(b.bullet, 5,5)
		b.trail = Createsprite(2)
		SetSpriteSize(b.trail,32,5)
		SetSpriteOffset(b.trail,35,2.5)
		SetSpriteColorAlpha(b.trail,128)
	endif
	
	// set start position and angle of the bullet with trail
	SetSpritePositionByOffset(b.bullet, posx, posy)
	SetSpritePositionByOffset(b.trail, posx, posy)
	SetSpriteAngle(b.trail,ATanFull(dirx,diry)-90)
	SetSpriteVisible(b.bullet, 1)
	SetSpriteVisible(b.trail, 1)
	b.dirx = dirx
	b.diry = diry
	b.speed = BULLET_SPEED
	b.active = 1
	
	// if a bullet was created, add it to the array
	if id = -1
		bullets.insert(b)
	else
		bullets[id] = b
	endif
endfunction

// update movement and position of the bullet and trail.
function UpdateBullets(ft as float)
	for i=0 to bullets.length
		if bullets[i].active
			mx# = GetSpriteX(bullets[i].bullet)+bullets[i].dirx*bullets[i].speed*ft
			my# = GetSpriteY(bullets[i].bullet)+bullets[i].diry*bullets[i].speed*ft			
			SetSpritePosition(bullets[i].bullet,mx#,my#)
			
			if GetSpriteInScreen(bullets[i].trail) = 0
				bullets[i].active = 0
				SetSpriteVisible(bullets[i].bullet, 0)
				SetSpriteVisible(bullets[i].trail, 0)
			endif
		endif		
		SetSpritePositionByOffset(bullets[i].trail, GetSpriteXByOffset(bullets[i].bullet),GetSpriteYByOffset(bullets[i].bullet))
	next
endfunction

Posted: 6th Oct 2022 11:26
it's a good approach making sprites follow another

the reason i can't do with particles, is because particles can only be square sprites, and I can't make the particles have width different from height
and this effect is to be used on various types of sprites of various shapes, including characters
Posted: 6th Oct 2022 12:18
and I can't make the particles have width different from height

with transparent particles this is achievable, not sure if it would be to resource
hungry.

I did try madbits last method and that worked well this end
Posted: 6th Oct 2022 13:41
I think I have an ideia, but
It's possible o layer sprites in front of particles?
Posted: 7th Oct 2022 17:54
brunuu you could make use of the render commands and use particles

an example of fireworks below

+ Code Snippet
 
#constant maxNumberFireworks=8
#constant speed=1.0
#constant frequency=3.14
#constant maxWidth=800
#constant maxHeight=600

SetWindowTitle( "fireworks" )
SetWindowSize( maxWidth, MaxHeight, 0 )
   
// set display properties
SetVirtualResolution( maxWidth, MaxHeight )
SetOrientationAllowed( 0, 0, 1, 1 )
SetDisplayAspect( -1 ) 
UseNewDefaultFonts( 1 ) // since version 2.0.20 we can use nicer default fonts
//SetVSync(1)
SetPrintSize(50)

do 
	if GetPointerPressed()  
		//congratulations("Congratulations tap screen to exit")
		congratulations("Happy New Year")
	endif
	Print("tap screen for fireworks")
	Sync()	

loop
end
  
function setupFirework(obj as integer)
    CreateParticles(obj,100,100) //id,x,y
    SetParticlesSize(obj,2)      //pixel size of particles  
    SetParticlesLife(obj,5)      //sets the length of 5 seconds the particles live
    SetParticlesMax(obj,75)      //max number of particles set to 75 f
    `SetParticlesActive(1,0)
    AddParticlesForce(obj,1,4,0,15) //The time a particle has to wait before forces apply 
    SetParticlesFrequency(obj,100)  //Sets the frequency of new particle generation
    SetParticlesVelocityRange(obj,2,4)  //velocity factor of particles ranging from min of 2 to 4
    SetParticlesColorInterpolation(obj,0) //1 smooth 0 none
    SetParticlesVisible( obj, 0 )       //hide particles when they first created so as timer can be used
endfunction
  
function doFirework (obj as integer)
    SetParticlesVisible( obj, 1 ) //make particle visible 
    SetParticlesPosition(obj,Random(100,GetVirtualWidth()-100),Random(100,GetVirtualHeight()-100))
    ResetParticleCount(obj)
  
    AddParticlesColorKeyFrame(obj,1,Random(100,250),Random(100,250),Random(100,250),255)
    AddParticlesColorKeyFrame(obj,2,Random(100,200),Random(100,200),Random(100,200),200)
    AddParticlesColorKeyFrame(obj,4,Random(50,100),Random(50,100),Random(50,100),25)   
endfunction

function congratulations (text as String)
 
dim fireFlag [maxNumberFireworks] as integer //variable to hold the present particle emmitter
dim fireSound [maxNumberFireworks] as integer
SetSoundSystemVolume(100)
for num = 1 to maxNumberFireworks
    setupFirework(num)
	fireSound[num]=LoadSound("firework.wav")
    SetSoundInstanceVolume(fireSound[num],75)
    fireFlag[num]=0
next num
  
fireBg = createsprite(0)
setspritesize(fireBg,maxWidth,maxHeight)
SetSpriteColor(fireBg,0,0,0,8)

Print(text)
render() //draw all 2d and 3d to screen


myFireworkBackImage=GetImage(0,0,maxWidth,maxHeight)
MyFireworkBackSprite=CreateSprite(MyFireworkBackImage)
SetSpriteSize(MyFireworkBackSprite,maxWidth,maxHeight)
SetSpriteDepth(MyFireworkBackSprite,10000)
SetSpriteColorAlpha(MyFireworkBackSprite,100)
SetSpritePositionByOffset(MyFireworkBackSprite,maxWidth/2,maxHeight/2)

renderImg=CreateRenderImage (maxWidth,maxHeight,0,0) 
ClearScreen()

repeat
SetRenderToImage (renderImg,0) 

DrawSprite(MyFireworkBackSprite) 
DrawSprite(fireBg)
 
for num=1 to maxNumberFireworks 
    if (fireFlag[num]=0) and random(1,100)<5 //a 5 percent chance of that particle being emitted
        doFirework(num)
        playSound(fireSound[num])
        fireFlag[num]=1
        UpdateParticles(num,0)
    endif
      
    if GetParticlesMaxReached(num)=1 //and GetParticlesMaxReached(num+100)=1
        fireFlag[num]=0
    endif
      
next num
  
Update(0)
Render()
setrendertoscreen () 
CreateSprite(350,renderImg) 
SetSpriteSize(350,maxWidth,maxHeight)

Update(0)
Render()
swap()
until GetPointerPressed ( ) = 1 

DeleteSprite(MyFireworkBackSprite)
DeleteImage(MyFireworkBackImage)	
for num = 1 to maxNumberFireworks
    DeleteParticles(num)
    DeleteSound (fireSound[num])
next 
SetRenderToImage (renderImg,0) 
drawSprite(fireBg) //pastes a black sprite to buffer and screen
Update(0)
Render()
swap()
DeleteSprite(350)
DeleteSprite(fireBg)
setrendertoscreen () 
DeleteImage(renderImg)
endfunction
 
Posted: 7th Oct 2022 18:23
I gathered several of the ideas suggested here and I had this idea
instead of using time to control, why not measure the distance between each position? because position is independent of framerate, 10 pixels is 10 pixels
so I did it like this, I just put a sprite for the trail at a certain distance from the main sprite

+ Code Snippet
SetErrorMode(2)
SetWindowTitle( "Trail" )
SetWindowSize( 1280, 720, 0 )
SetWindowAllowResize( 1 )
SetVirtualResolution( 320, 180 )
SetOrientationAllowed( 1, 1, 1, 1 )
SetSyncRate( 60, 1 )
SetScissor( 0,0,0,0 )
UseNewDefaultFonts( 1 )
SetClearColor(80, 80, 80)

spr as integer
spr = CreateSprite(0)
SetSpriteSize(spr, 25, 25)
SetSpritePosition(spr, 15, 15)

type _trailEffect
  sprMain as integer
  sprs as integer[]
  active as integer
  time as float
  lastX as float
  lastY as float
endtype

global List_TrailEffect as _trailEffect[]

type _shot
	spr as integer
	angle as float
endtype

global shots as _shot[]

do

	if GetPointerPressed() = 1
		Shot( ScreenToWorldX(getpointerX()), ScreenToWorldY(getpointerY()), spr)
	endif

	for i = 0 to shots.length
		SetSpritePosition(shots[i].spr, getspriteX(shots[i].spr) + (300 * sin(shots[i].angle) * GetFrameTime()), getspriteY(shots[i].spr) + (300 * cos(shots[i].angle) * GetFrameTime()))
	next i

	if GetRawKeyPressed(69) // E
		SetSyncRate( 999, 1 )
	endif

	if GetRawKeyPressed(87) // W	
		SetSyncRate( 60, 1 )
	endif

	print(ScreenFPS())
	update_Trail()
    Sync()
loop


function create_Trail(spr as integer)
  for i = 0 to List_TrailEffect.length // prevent repetition
    if List_TrailEffect[i].sprMain = spr then exitfunction
  next i

  t as _trailEffect
  t.time = 2.0
  t.sprMain = spr
  t.active = 1
  List_TrailEffect.insert(t)
endfunction

function update_Trail()
    for i = 0 to List_TrailEffect.length
	
		if Distance(List_TrailEffect[i].lastX, List_TrailEffect[i].lastY, GetSpriteXByOffset(List_TrailEffect[i].sprMain), GetSpriteYByOffset(List_TrailEffect[i].sprMain)) > 5 or List_TrailEffect[i].sprs.length = -1 
			if not GetSpriteExists(List_TrailEffect[i].sprMain) //If the sprite where the effect is applied does not exist, disables
			  List_TrailEffect[i].active = 0
			endif

			// create effect
			if List_TrailEffect[i].active = 1
			  n = CreateSprite(GetSpriteImageID(List_TrailEffect[i].sprMain))
			  List_TrailEffect[i].lastX = GetSpriteXByOffset(List_TrailEffect[i].sprMain)
			  List_TrailEffect[i].lastY = GetSpriteYByOffset(List_TrailEffect[i].sprMain)
			  SetSpriteSize(n, 3, 3)
			  SetSpritePosition(n, GetSpriteX(List_TrailEffect[i].sprMain),  GetSpriteY(List_TrailEffect[i].sprMain))
			  List_TrailEffect[i].sprs.insert(n)
			endif
		endif
		
        for k = List_TrailEffect[i].sprs.length to 0 step -1
          alfa = GetSpriteColorAlpha(List_TrailEffect[i].sprs[k])
          dec alfa, 3000 * GetFrameTime()
          if alfa < 0 then alfa = 0
          SetSpriteColorAlpha(List_TrailEffect[i].sprs[k], alfa)
          if alfa = 0
            DeleteSprite(List_TrailEffect[i].sprs[k])
            List_TrailEffect[i].sprs.remove(k)
          endif
        next k

        if List_TrailEffect[i].sprs.length = -1
          List_TrailEffect.remove(i)
          continue
        endif

    next i
endfunction



function Shot(AimX as integer, AimY as integer, char as integer)
	xi as float
	yi as float
	dirx as float
	diry as float
	d as _shot

	xi = getspriteX(char) + (GetSpriteWidth(char) / 2)
	yi = (getspriteY(char) + (GetSpriteHeight(char) / 2))
	dirx = (AimX) - xi
	diry = yi - (AimY)
	angle = atanfull(dirx, diry)
	d.angle = angle

	sprShot = CreateSprite(0)
	d.spr = sprShot
	SetSpriteSize(sprShot, 3, 3)
	SetSpritePositionByOffset(sprShot, GetSpriteXByOffset(char), GetSpriteYByOffset(char))

	create_Trail(sprShot)
	shots.insert(d)
endfunction


Function Distance(x1 as float, y1 as float, x2 as float, y2 as float) 
	dnst as float
	dnst = sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2))
EndFunction dnst


and it works!