Setalpha by distance traveled

BlitzMax Forums/BlitzMax Programming/Setalpha by distance traveled

Hey, i'm trying to make some partical effects, one of them is a partical explosion.

Basicly, you create it, at [blank] point using [blank] image, using [blank] amount of particals, and a max travel distance is set (imagine a circle around the start point). Each partical is then assigned a direction randomly generated. Then, this is where I'm stumped, you set the partical alpha according to how much distance it traveled.

I have two problems:

1. How to calculate the alpha setting, where 1 (fully solid) being at the beginning point, and 0 (completely transparent) being at the max distance (imaginary circle around the start point)
2. How to calculate how much distance the partical has traveled (i'm having trouble becase it doesnt just travel on one axis. And because it can be going any direction)


Here is my code at the moment:

Global ParticalList:TList = CreateList()
Global ParticalExpList:TList = CreateList()


Type TPartical
	Field x:Int,y:Int,a:Float,r:Int
	Field d_x:Int,d_y:Int
	Field img:TImage
	Field dist_traveled:Int
	Field ID:String
	
	Method Create(_id:String,x:Int,y:Int,_img:TImage,_d_x:Int = 0,_d_y:Int = 0)
		ListAddLast(ParticalList,Self)
		x = _x
		y = _y
		img = _img
		ID = _id
		
	End Method
	
	Method Update()
		x:+d_x
		y:+d_y
		dist_traveled:+(d_x+d_y)
		SetRotation(r)
		SetAlpha(a)
		DrawImage img,x,y
		SetRotation(0)
		SetAlpha(1)
	End Method
	
	Method SetAlpha(alpha:Float)
		a = alpha
	End Method
	
	Method SetRotation(rot:Int)
		r = rot
	End Method
	
	Method Destroy()
		ParticalList.Remove(Self)
	End Method
	
End Type

Type TParticalExp
	Field part_n:Int,part_img:TImage
	Field dist:Int
	Field x:Int,y:Int
	Field d_x:Int,d_y:Int
	
	Method Create(_x:Int,_y:Int,_part_img:TImage,_part_n:Int,dist:Int)
		ListAddLast(ParticalExpList,Self)
		part_n = _part_n
		part_img = _part_img
		x = _x
		y = _y
		d_x = Rand(-3,3)
		d_y = Rand(-3,3)
		For Local i:Int = 1 To part_n
			Local part:TPartical = TPartical.Create("pexp",x,y,part_img,d_x,d_y)
		Next
	End Method
	
	Method Update()
		For Local i:TPartical = EachIn ParticalList
			If i.ID = "pexp"
				If i.dist_traveled >
					i.SetAlpha(i.
				i.Update()
			End If
				
	
End Type


Any help appriciated!

first you need to work out the distance:

Function distance#(x1, y1, x2, y2)
Local x=Abs(x1-x2)
Local y=Abs(y1-y2)
Return x+y
End Function

then, calcuate a number from zero to one based on distance

particle_alpha#=distance()/maxdistance#

make sure all numbers are floats involved in the calculation

I'm still not sure i get it, the function seems to calculate the distance between two points... while i'm trying to get the distance an object has traveled in pixels, and bare in mind these objects can be traveling in any direction. (see my code)

But that does answer one of my questions: getting the setting of alpha based on the distance between two points

x1 and y1 are the x and y coordinates of the particles start point
x2 and y2 are where the particle is at the current moment

it doesnt matter what direction it travels, the function still works
(because of the abs command)

alright thanks, i'll mess with it.

Right,

I've gotten rid of the partical explosion function, and created one type, and a function to create the explosion..

But it isnt working correctly... anyone know why?

note the DistanceTraveled() function.

In my testing, it doesnt always work, and when it does, it seems to fire a solid yellow block in one direction, and it doesnt fade...

Global ParticalList:TList = CreateList()


Type TPartical
	Field x:Int,y:Int,a:Float,r:Int
	Field ox:Int,oy:Int
	Field img:TImage
	Field dx:Int,dy:Int
	Field maxdistance:Float
	Field fade:Int
	
	Method Create:TPartical(_x:Int, _y:Int, _img:TImage, _dx:Int = 0, _dy:Int = 0, mxdist:Float = 0, _fade:Int = False)
		ListAddLast(ParticalList,Self)
		x = _x
		y = _y
		ox = _x
		oy = _y
		img = _img
		dx = _dx
		dy = _dy
		If mxdist
			maxdistance = mxdist
		End If
		If _fade
			fade = True
		End If
	End Method
	
	Method Update:TPartical()
		x:+dx
		y:+dy
		SetRotation(r)
		SetAlpha(a)
		DrawImage img,x,y
		SetRotation(0)
		SetAlpha(1)
		
		If maxdistance
			If DistanceTraveled() >= maxdistance
				Destroy()
			End If
		End If
		
		If fade
			a = DistanceTraveled()/maxdistance
		End If
	End Method
	
	Method SetPAlpha(alpha:Float)
		a = alpha
	End Method
	
	Method SetPRotation(rot:Int)
		r = rot
	End Method
	
	Method Destroy()
		ParticalList.Remove(Self)
	End Method
	
	Method SetX(_x:Int)
		x = _x
	End Method
	
	Method SetY(_y:Int)
		y = _y
	End Method
	
	Method DistanceTraveled:Float()
		Local returnx:Float=Abs(ox-y)
		Local returny:Float=Abs(oy-y)
		Return returnx+returny	
	End Method
	
	
End Type


'Explosion function
Function ParticalExplosion(_x:Int, _y:Int, image:TImage, n:Int, dist:Float)
	Local dx:Int = Rand(-3,3)
	Local dy:Int = Rand(-3,3)
	
	For Local i:Int = 1 To n
		Local part:TPartical = New TPartical.Create(_x, _y, image, dx, dy, dist, True)
	Next
	
End Function

'Update particals like so
		For Local UpdateParticals:TPartical = EachIn ParticalList
			UpdateParticals.Update()
		Next



I'll see if i can get a working example

Alright here's an example i knocked up, just paste 'n compile.

click to create a partical explosion with 50 particals and a max distance of 100.

But as you can see it doesnt work correctly, what am i doing wrong?


SeedRnd MilliSecs()

Global ParticalList:TList = CreateList()


Type TPartical
	Field x:Int,y:Int,a:Float,r:Int
	Field ox:Int,oy:Int
	Field img:TImage
	Field dx:Int,dy:Int
	Field maxdistance:Float
	Field fade:Int
	
	Method Create:TPartical(_x:Int, _y:Int, _img:TImage, _dx:Int = 0, _dy:Int = 0, mxdist:Float = 0, _fade:Int = False)
		ListAddLast(ParticalList,Self)
		x = _x
		y = _y
		ox = _x
		oy = _y
		img = _img
		dx = _dx
		dy = _dy
		If mxdist
			maxdistance = mxdist
		End If
		If _fade
			fade = True
		End If
	End Method
	
	Method Update:TPartical()
		x:+dx
		y:+dy
		SetRotation(r)
		SetAlpha(a)
		DrawImage img,x,y
		SetRotation(0)
		SetAlpha(1)
		
		If maxdistance
			If DistanceTraveled() >= maxdistance
				Destroy()
			End If
		End If
		
		If fade
			a = DistanceTraveled()/maxdistance
		End If
	End Method
	
	Method SetPAlpha(alpha:Float)
		a = alpha
	End Method
	
	Method SetPRotation(rot:Int)
		r = rot
	End Method
	
	Method Destroy()
		ParticalList.Remove(Self)
	End Method
	
	Method SetX(_x:Int)
		x = _x
	End Method
	
	Method SetY(_y:Int)
		y = _y
	End Method
	
	Method DistanceTraveled:Float()
		Local returnx:Float=Abs(ox-y)
		Local returny:Float=Abs(oy-y)
		Return returnx+returny	
	End Method
	
	
End Type


'Partical explosion function; creates a explosion of particals
Function ParticalExplosion(_x:Int, _y:Int, image:TImage, n:Int, dist:Float)
	Local dx:Int = Rand(-3,3)
	Local dy:Int = Rand(-3,3)
	
	For Local i:Int = 1 To n
		Local part:TPartical = New TPartical.Create(_x, _y, image, dx, dy, dist, True)
	Next
	
End Function

AppTitle = "Partical test - click to fire"
Graphics 800,600

'Create a dot image (our partical)
Global dot:TImage = CreateImage(5, 5) 
DrawOval 0, 0, 5, 5
GrabImage(dot,0,0)

While Not KeyHit(KEY_ESCAPE)
Cls
	For Local UpdateParticals:TPartical = EachIn ParticalList
		UpdateParticals.Update()
	Next
	If MouseHit(1) ParticalExplosion(MouseX(), MouseY(), dot, 50, 100)
Flip
Wend
End


Here's one based on your code above, but changed a bit in places.

Rather than set a dx,dy using Ints, here we pass in a random direction (0-359), as well as a random speed. The random speed thing gives you a bigger "spread" of particles.

It now runs on a max number of frames, rather than distance, which it simply counts down, removing the particle when the count is 0.
Fading is based on a range of 1 -> 1/num frames.

SeedRnd MilliSecs()

Global ParticalList:TList = CreateList()


Type TPartical
	Field x:Float,y:Float,a:Float = 1,r:Int
	Field ox:Int,oy:Int
	Field img:TImage
	Field dx:Float,dy:Float, da:Float
	Field maxdistance:Int
	Field fade:Int
	
	Method Create:TPartical(_x:Int, _y:Int, _img:TImage, _dir:Int = 0, _speed:Float = 0, frames:Int = 0, _fade:Int = False)
		ListAddLast(ParticalList,Self)
		x = _x
		y = _y
		ox = _x
		oy = _y
		img = _img
		dx = Sin(_dir) * _speed
		dy = Cos(_dir) * _speed
		da = 1.0 / frames
		maxdistance = frames
		
'		If mxdist
'			maxdistance = mxdist
'		End If
		If _fade
			fade = True
		End If
	End Method
	
	Method Update:TPartical()
		x:+dx
		y:+dy
		If fade a:-da
		SetRotation(r)
		SetAlpha(a)
		DrawImage img,x,y
		SetRotation(0)
		SetAlpha(1)
		
		maxdistance:- 1
		If Not maxdistance Then
			Destroy()
		End If
		
		'If fade
		'	a = DistanceTraveled()/maxdistance
		'End If
	End Method
	
	Method SetPAlpha(alpha:Float)
		a = alpha
	End Method
	
	Method SetPRotation(rot:Int)
		r = rot
	End Method
	
	Method Destroy()
		ParticalList.Remove(Self)
	End Method
	
	Method SetX(_x:Int)
		x = _x
	End Method
	
	Method SetY(_y:Int)
		y = _y
	End Method
	
	Method DistanceTraveled:Float()
		Local returnx:Float=Abs(ox-y)
		Local returny:Float=Abs(oy-y)
		Return returnx+returny	
	End Method
	
	
End Type


'Partical explosion function; creates a explosion of particals
Function ParticalExplosion(_x:Int, _y:Int, image:TImage, n:Int, frames:Int)
	
	For Local i:Int = 1 To n
		Local dir:Int = Rand(0, 359)
		Local speed:Float = Rnd(0.08, 1.5)
		Local part:TPartical = New TPartical.Create(_x, _y, image, dir, speed, frames - 10 + Rand(20), True)
	Next
	
End Function

AppTitle = "Partical test - click to fire"
Graphics 800,600,0
SetBlend alphablend

'Create a dot image (our partical)
Global dot:TImage = CreateImage(5, 5) 
DrawOval 0, 0, 5, 5
GrabImage(dot,0,0)

While Not KeyHit(KEY_ESCAPE)
Cls
	For Local UpdateParticals:TPartical = EachIn ParticalList
		UpdateParticals.Update()
	Next
	If MouseHit(1) ParticalExplosion(MouseX(), MouseY(), dot, 50, 100)
Flip
Wend
End


Thats awsome, much simpler.

You continue to amaze me brucey, thanks!

Here ist also some old code in wich i tried to generate a Starfield:

SuperStrict

Rem
Framework BRL.GLMax2D
Import BRL.LinkedList
Import BRL.Random
EndRem

Incbin "star.png"

Type TStarfield

	Global Starlist:TList = New TList
	
	Global center_X:Double
	Global center_Y:Double
	
	Global x_home:Int
	Global y_home:Int
	Global height:Int
	Global width:Int
	
	Global accel:Double = 1.05
	Global fade:Double = 1.05
	Global max_age:Int = 500
	
	Global image:TImage 
	
	Field x:Double
	Field y:Double
	Field vx:Double
	Field vy:Double
	
	Field red:Int
	Field green:Int
	Field blue:Int
	
	Field age:Int
	
	Field inuse:Byte
	
	Method New()
		Self.Emit()
	 	ListAddLast(Starlist, Self)
	EndMethod
	
	Method Draw() 
		If inuse > 0 Then
			Local oldBlend:Int = GetBlend()
			Local oldAlpha:Double = GetAlpha() 
			Local oldMaskRED:Int
			Local oldMaskGREEN:Int
			Local oldMaskBLUE:Int
			
			GetMaskColor(oldMaskRED , oldMaskGREEN , oldMaskBLUE) 
			
			SetMaskColor(0,0,0)			
			SetBlend(ALPHABLEND)
			
			If age < max_age
				SetAlpha(1.0-Float(age)/Float(max_age))
			Else
				SetAlpha(1.0)
			EndIf
			
			DrawImage(image , x , y) 
			
			SetBlend oldBlend
			SetAlpha oldAlpha
			SetMaskColor(oldMaskRED , oldMaskGREEN , oldMaskBLUE)
			
		EndIf
	EndMethod
	
	Method Update() 
		If inuse > 0
			x :+ vx
			y :+ vy
			
			vx :* accel
			vy :* accel
			
			age :- 1
			If age = 0 Then inuse = 0

			If x < x_home Or x > (x_home + width) Then inuse = 0
			If y < y_home Then inuse = 0
			If y > (y_home + height) Then inuse = 0
		Else
			Self.Emit()
		EndIf
	EndMethod
	
	Method Emit() 
		
		vx = - .5 + RndDouble() 
		vy = - .5 + RndDouble() 
				
		x = Center_X
		y = Center_Y
		
		age = max_age
		
		inuse = 1
	EndMethod
	
	Function UpdateAll() 
		For Local s:TStarfield=EachIn Starlist
			s.Update() 
		Next
	EndFunction
	
	Function DrawAll() 
		For Local s:TStarfield=EachIn Starlist
			s.Draw() 
		Next
	EndFunction
	
	Function SetAcceleration(acceleration:Double) 
		accel = acceleration
	EndFunction
	
	Function EmitStars(amount:Int) 
		For Local i:Int = 1 To amount
			Local temp:TStarfield = New TStarfield
		Next
	EndFunction
	
	Function SetCenter(in_x:Int , in_y:Int) 
		center_X = in_x
		center_Y = in_y
	EndFunction
	
	Function SetFade(fade:Double) 
		
	EndFunction
	
	Function SetImage(picture:TImage) 
		image = picture
	EndFunction
	
	Function SetDimensions(x:Int , y:Int , w:Int , h:Int) 
		x_home = x
		y_home = y
		width = w
		height = h
		SetCenter(x + w/2, y + h/2)
	EndFunction

EndType


Graphics 800 , 600, 0, 60, GRAPHICS_BACKBUFFER|GRAPHICS_ALPHABUFFER
'Graphics 800 , 600

SetColor 0 , 0 , 0
Cls

Local picture:Timage = LoadImage("incbin::star.png" , -1)

If  picture = Null
	RuntimeError "Could not load Picture!"
EndIf

TStarfield.SetDimensions(0,0,800,600)
TStarfield.SetAcceleration(1.01) 
TStarfield.SetFade(1.5) 
TStarfield.SetImage(picture) 

Local StarCount:Int = 1500

Repeat

	If StarCount > 0
		TStarfield.EmitStars(1) 
		Starcount :- 1
	Endif

	SetColor 255 , 255 , 255

	Cls
	
	TStarfield.UpdateAll() 
	TStarfield.DrawAll() 
	
	Flip
		
Until AppTerminate() Or KeyDown(KEY_ESCAPE) 


This ist the Image


Cool,

Speaking of particles, is there any way to optimise this?

I see people talking about making the particles "one surface" or "a single dynamic mesh", how would you do this? And would this increase speed?

The reason why iIask is because, on some of my lower end systems, it gets slow around 1500-2000 particals.

Alternatively, are there any free particle modules / source code out there? (using images)

Single surface, mesh, etc, would help slightly perhaps alot, but....99% of Blitzmax coders are not using it. They just use the built in handling, especially useful when you start porting to other platforms.

"The reason why iIask is because, on some of my lower end systems, it gets slow around 1500-2000 particals."

That's alot of image rendering, regardless the hardware running. Ask yourself if you need that many, would 200 or so get the job done "good enough"?

Also consider creating an image with several particles already on it and then manipulate the render in real time with scaling, rotation, etc. This is a valuable technique alot of pros use to fool the user into thinking there's alot more rendering going on then there actually is. ;)