Hi all!
Thought I'd actually have a go at one of these challenges for once. I've been playing around with flocking code recently and had the bizarre idea to make a flocking clock!
There are some keys to control the visuals:-
1 - Switch between sphere and cube particles
2 - Rotate the particles (only useful for cubes really
3 - Toggle fading of the particles (which stops me being able to colour them oddly)
Page Up - Zoom in
Page Down - Zoom out
And that's about it. Hopefully the code will turn up ok, last time I looked I was still a moderated user.
autocam off
sync rate 60
sync on
hide mouse
global gParticleColour as DWORD
`------------------
` Config variables
` If set to 1 we'll use cubes, otherwise we'll use spheres
global gUseCubes = 0
` If set to 1 keep the particles rotating to add some movement to the static numbers
global gRotateParticles = 0
` Colour of the particles
gParticleColour = rgb(0, 196, 255)
` Fade the particles with distance from the numbers - NOTE: This will make all the particles white again for some reason
global gFadeParticles = 1
` Controls the scale of the numbers
global gCameraHeight# = 125.0
` End of config
`------------------
`set display mode 1600, 1200, 32
global gCurrentFreeObject = 0 ` Keep track of the object numbers we've used
global gTimePassed# ` Can't assign an expression on the same line as a global variable definition
gTimePassed# = 1.0/60.0
global gRotSpeed# = 2.8 ` How fast the particles can turn towards their destination
global gMoveSpeed# = 35.0 ` How fast the particles can move towards their destination
global gTimeZPos# = -20.0 ` Z Coordinate for the numbers
global gSpareZOffset# = 0.0 ` Z Coordinate for the spare particles
` Simple 3D vertex type
type Vertex
x as float
y as float
z as float
endtype
` Type for each number that will appear on the screen, there will be 8 of these, 6 numbers
` for hours, minutes and seconds and a couple of colons to separate them.
type Number
Position as Vertex
NumPositions as integer
StartObject as integer
endtype
` Type for the particles
type Particle
Position as Vertex ` Position in 3D space
Velocity as Vertex ` How much we move per frame
Rotation as Vertex ` Which direction we're currently pointing
RotationVelocity as Vertex ` How fast we're rotating if gRotateParticles is 1
Destination as Vertex ` Where we're heading in 3D space
ObjectIndex as integer ` The object index that we're controlling
endtype
` There can only be a maximum of 94 particles in use (00:00:00), add a couple of spares for effect
#constant NumParticles 96
` 8 characters on screen "00:00:00"
#constant NumNumbers 8
` Create and initialise the numbers
dim Numbers(NumNumbers) as Number
` Set the positions of each of the numbers in 3D space
NumberStartX# = -65.0
NumberSpacing# = 17.0
current# = NumberStartX#
for i=1 to NumNumbers
Number_Construct(i,current#,-10)
current# = current# + NumberSpacing#
next i
`Can't have arrays inside types so we'll have to keep it external
dim Number_Positions(NumNumbers, 25) as Vertex
` Read the data statements containing the number pixel offsets into an array
dim NumberTemplates(11, 25) as integer
restore NumberData
for i=1 to 11
for j = 1 to 25
read NumberTemplates(i, j)
next j
next i
` Create and initialise the particles
dim Particles(NumParticles) as Particle
for i=1 to NumParticles
Particle_Construct(i)
next i
` Make a top down camera
position camera 0, 0, gCameraHeight#, 0
point camera 0, 0, 0, 0
color backdrop RGB(0,0,0)
` Store the minute count last frame so we can toggle the clock between the top and the bottom of the screen
LastMinute = -1
Pressed1LastFrame = 0
Pressed2LastFrame = 0
Pressed3LastFrame = 0
` ----------------------------------
` ----------------------------------
` Main loop
` ----------------------------------
` ----------------------------------
do
` Get the time as a string
t$ = get time$()
` Run along the string character by character assigning the correct number template to each of our 8 numbers
for i=1 to NumNumbers
char$ = mid$(t$, i)
if char$ = ":"
Number_SetTemplate(i, 1)
else
index = val(char$)+2
Number_SetTemplate(i, index)
endif
next i
` Move the clock on the screen every minute
minute = val(mid$(t$,5))
if minute <> LastMinute
if gTimeZPos# = -20.0
gTimeZPos# = 30.0 ` Clock at the top of the screen
gSpareZOffset# = -50.0 ` Spare particles at the bottom
else
gTimeZPos# = -20.0 ` Clock at the bottom of the screen
gSpareZOffset# = 0.0 ` Spare particles at the top
endif
` Set the positions of the numbers
for i=1 to NumNumbers
Numbers(i).Position.z = gTimeZPos#
next i
endif
LastMinute = minute
` Assign particles to the valid positions in the numbers
current = 1;
for i=1 to NumNumbers
for j=1 to Numbers(i).NumPositions
Particle_SetDestination(current, Number_Positions(i, j).x, Number_Positions(i, j).z)
current = current + 1
next j
next i
` Send the rest of the particles off into the spare particles area
for i=current to NumParticles
Particle_SetDestination(i, Rnd(50)-25.0, Rnd(50)+gSpareZOffset#)
next i
` Update all of the particles
for i=1 to NumParticles
Particle_Tick(i)
next i
` Process the keys
` Key '1' toggle between cubes and spheres
if Keystate(2) = 1
if Pressed1LastFrame = 0
gUseCubes = 1-gUseCubes
Particle_ChangeObjects()
endif
Pressed1LastFrame = 1
else
Pressed1LastFrame = 0
endif
` Key '2' toggles between rotating and not rotating
if Keystate(3) = 1
if Pressed2LastFrame = 0
gRotateParticles = 1-gRotateParticles
endif
Pressed2LastFrame = 1
else
Pressed2LastFrame = 0
endif
` Key '3' toggles between fading and not fading the particles
if Keystate(4) = 1
if Pressed3LastFrame = 0
gFadeParticles = 1-gFadeParticles
Particle_ResetFade()
endif
Pressed3LastFrame = 1
else
Pressed3LastFrame = 0
endif
` PGUP to move the camera in
if Keystate(201) = 1
gCameraHeight# = gCameraHeight# - 50.0 * gTimePassed#
position camera 0, 0, gCameraHeight#, 0
endif
` PGDN to move the camera out
if Keystate(209) = 1
gCameraHeight# = gCameraHeight# + 50.0 * gTimePassed#
position camera 0, 0, gCameraHeight#, 0
endif
sync
loop
`----------------------
` End of Mainloop
`----------------------
` Initialisation function for the Number type
function Number_Construct(i as integer, x as float, z as float)
Numbers(i).Position.x = x
Numbers(i).Position.z = z
Numbers(i).StartObject = GetFreeObject()
gCurrentFreeObject = gCurrentFreeObject-1
endfunction
` Function to work out the 3D positions of the pixels of a number and store them in the Number_Positions array
function Number_SetTemplate(i as integer, number as integer)
numberDotSpacing# = 3.0
Numbers(i).NumPositions = 0
j=1
for z = 0 to 4
for x = 0 to 4
if NumberTemplates(number, j) = 1
Number_Positions(i, Numbers(i).NumPositions+1).x = Numbers(i).Position.x + x*numberDotSpacing#
Number_Positions(i, Numbers(i).NumPositions+1).z = Numbers(i).Position.z + -(z*numberDotSpacing#)
Numbers(i).NumPositions = Numbers(i).NumPositions + 1
endif
j = j + 1
next x
next y
endfunction
` Initialisation function for the particles
function Particle_Construct(i as integer)
Particles(i).ObjectIndex = GetFreeObject()
if gUseCubes = 1
Make Object Cube Particles(i).ObjectIndex, 2
else
Make Object Sphere Particles(i).ObjectIndex, 2.5
endif
Color Object Particles(i).ObjectIndex, gParticleColour
Ghost Object On Particles(i).ObjectIndex
Particles(i).Position.x = 0
Particles(i).Position.z = 0
Particles(i).Velocity.x = Particles(i).Velocity.z = 0
Particles(i).Destination.x = Particles(i).Destination.z = 0
Particles(i).Rotation.x = Particles(i).Rotation.y = Particles(i).Rotation.z = 0
Particles(i).RotationVelocity.x = Rnd(40)+10
Particles(i).RotationVelocity.z = Rnd(40)+10
endfunction
` Remake all of the objects
function Particle_ChangeObjects()
for i=1 to NumParticles
delete object Particles(i).ObjectIndex
if gUseCubes = 1
Make Object Cube Particles(i).ObjectIndex, 2
else
Make Object Sphere Particles(i).ObjectIndex, 2.5
endif
Color Object Particles(i).ObjectIndex, gParticleColour
Ghost Object On Particles(i).ObjectIndex
next i
endfunction
` Reset the fade values of all of the particles
function Particle_ResetFade()
for i=1 to NumParticles
Fade Object Particles(i).ObjectIndex, 100
` Need to reset the colour as the fade turns it off
Color Object Particles(i).ObjectIndex, gParticleColour
next i
endfunction
` Update function for the paritlces
function Particle_Tick(i as integer)
arrivedDist# = 0.1 ` How far away from the destination we need to be before we say we're there
diff as Vertex
angle as float
angleDiff as float
` Work out our distance from the destinayion
diff.x = Particles(i).Destination.x - Particles(i).Position.x
diff.z = Particles(i).Destination.z - Particles(i).Position.z
dist# = Sqrt(diff.x*diff.x + diff.z*diff.z)
if gFadeParticles = 1
` Alter the alpha based on distance from destination
diffZ# = abs(gTimeZPos# - Particles(i).Position.z)
if diffZ# > 60.0
Fade Object Particles(i).ObjectIndex, 40
else
if diffZ# < 10.0
Fade Object Particles(i).ObjectIndex, 100
else
alpha# = (60.0-diffZ#) + 40.0
Fade Object Particles(i).ObjectIndex, alpha#
endif
endif
endif
` Clamp the distance between 0.01 and 10 so we can vary the approach speed within a sensible range
if dist# < 0.01
dist# = 0.01
endif
if dist# > 10.0
dist# = 10.0
endif
if dist# > arrivedDist#
` Work out the angle we need to be facing to point at our destination
angle = AtanFull(diff.x, diff.z)
angle = WrapValue(angle)
angleDiff = angle-Particles(i).Rotation.y
angleDiff = WrapAngleForInterpolation(angleDiff)
` Rotate towards the destination
Particles(i).Rotation.y = Particles(i).Rotation.y + angleDiff * gTimePassed# * gRotSpeed#
Particles(i).Rotation.y = WrapValue(Particles(i).Rotation.y)
` Work out how fast we want to be moving. The closer we are the slower we go
tempMoveSpeed# = gMoveSpeed# * (dist# / 10.0)
` Get a vector pointing in the same dierection as us and apply velocity
` in that direction
pointing as Vertex
rot# = -Particles(i).Rotation.y
pointing.x = -sin(rot#)
pointing.z = cos(rot#)
Particles(i).Velocity.x = pointing.x * tempMoveSpeed#
Particles(i).Velocity.z = pointing.z * tempMoveSpeed#
Particles(i).Position.x = Particles(i).Position.x + (Particles(i).Velocity.x * gTimePassed#)
Particles(i).Position.z = Particles(i).Position.z + (Particles(i).Velocity.z * gTimePassed#)
endif
` Put the 3D object in the right place
Position Object Particles(i).ObjectIndex, Particles(i).Position.x, 0, Particles(i).Position.z
` Rotate the particles if we've got it set on
if gRotateParticles = 1
Particles(i).Rotation.x = Particles(i).Rotation.x + Particles(i).RotationVelocity.x * gTimePassed#
Particles(i).Rotation.x = WrapValue(Particles(i).Rotation.x)
Particles(i).Rotation.z = Particles(i).Rotation.z + Particles(i).RotationVelocity.z * gTimePassed#
Particles(i).Rotation.z = WrapValue(Particles(i).Rotation.z)
Rotate Object Particles(i).ObjectIndex, Particles(i).Rotation.x, Particles(i).Rotation.y, Particles(i).Rotation.z
endif
endfunction
` Function to set the destination coordinate of a particle
function Particle_SetDestination(i as integer, x as float, z as float)
Particles(i).Destination.x = x
Particles(i).Destination.z = z
endfunction
` Return the next free object number
function GetFreeObject()
gCurrentFreeObject = gCurrentFreeObject + 1
endfunction gCurrentFreeObject
` Wrap an angle to fall in the range -180 to 180 degrees. Avoids having to deal with wrap around from 359->0
function WrapAngleForInterpolation(angle as float)
sanityCheck = 0 ` This will stop us getting into an infinite loop if we get a bad angle from ATanFull
while angle < -180.0
angle = angle + 360.0
sanityCheck = sanityCheck + 1
if sanityCheck > 5
exitfunction 0.0
endif
endwhile
sanityCheck = 0
while angle > 180.0
angle = angle - 360.0
sanityCheck = sanityCheck + 1
if sanityCheck > 5
exitfunction 0.0
endif
endwhile
endfunction angle
` Data for a 5x5 number only font
NumberData:
Data 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0 ` :
Data 0,1,1,1,0, 1,0,0,1,1, 1,0,1,0,1, 1,1,0,0,1, 0,1,1,1,0 ` 0
Data 0,0,1,0,0, 0,1,1,0,0, 0,0,1,0,0, 0,0,1,0,0, 1,1,1,1,1 ` 1
Data 0,1,1,1,0, 1,0,0,0,1, 0,0,1,1,0, 0,1,0,0,0, 1,1,1,1,1 ` 2
Data 1,1,1,1,0, 0,0,0,0,1, 0,1,1,1,0, 0,0,0,0,1, 1,1,1,1,0 ` 3
Data 1,0,0,0,1, 1,0,0,0,1, 0,1,1,1,1, 0,0,0,0,1, 0,0,0,0,1 ` 4
Data 1,1,1,1,1, 1,0,0,0,0, 1,1,1,1,0, 0,0,0,0,1, 1,1,1,1,0 ` 5
Data 0,1,1,1,0, 1,0,0,0,0, 1,1,1,1,0, 1,0,0,0,1, 0,1,1,1,0 ` 6
Data 1,1,1,1,1, 0,0,0,0,1, 0,0,0,1,0, 0,0,0,1,0, 0,0,1,0,0 ` 7
Data 0,1,1,1,0, 1,0,0,0,1, 0,1,1,1,0, 1,0,0,0,1, 0,1,1,1,0 ` 8
Data 0,1,1,1,0, 1,0,0,0,1, 0,1,1,1,1, 0,0,0,0,1, 0,1,1,1,0 ` 9
Kaiyodo