Scolring - Forum

Entraides et échanges autour de la technologie Scol - Informations and exchanges on the Scol technology

Vous pouvez changer la langue de l'interface une fois inscrit - You can change the language once registered

You are not logged in.

#1 1-Dec-2015 16:55:51

hebdemnobad
Member
From: northamerica
Registered: 20-Apr-2011
Posts: 1,477
Website

a cylinder soap opera/ mon feuilleton de cylrindres

to re familiarize myself with scol for this winter's efforts (I hope to actually finish some project, however small, a plugit at least that others can use, maybe the throttle from last year, or using arkeon's avatar plugit to support user determined meshes for virtual meetings and, at my son's request, fps shooters), I made a script that creates objects, executes their functions, and destroys them by building and destroying lists.  it puts the objects in one list and their unique indexes in another list, and minimizes the time consuming recursive list functions (searching through a list to find, do, add, or remove something) to one.  maybe this could be used in a simple fps game, where the players are hiding behind structures that appear and disappear randomly.

i'm sure there are more elegant ways to do this.  i wanted to write a script that could store unused indecies for objects in one list, so that when there were available indices, the vm would not have to iterate through an entire list.  i was thinking of setting the maximum index each time a new object (cylinder) was created, but if the objects have different lifespans, I'm not sure if that would work.

struct   Cylinder =[

	Icylindernumber	:	I,

	Icylinderindex		:	I,

	Fradius				: 	F,

	Fheight				: 	F,

	funlifespan			:	Timer,
	Ilifespanlength 	:	I,

	Sbirthfphrase		:	S,

	Sdeathphrase		:	S,

	fundyingclyinder	:	fun [Cylinder] S,

	funreturnarea		:	fun [Cylinder] F,

	funreturnvolume	:	fun[Cylinder] F,

	fbirthfunction  	:  fun[Cylinder] S,

	fdeathfunction  	:  fun[Cylinder] S

	

	]mkCylinder;;


//global list of Cylinders to grow and shrink

typeof lCylinderlist=[Cylinder r1];;
//global list of intergers to grab for new cylinders if they are free and to hold indeces of dead cylinders

typeof lDeleted_cylinder_ids = [I r1];;

typeof ICylinder_counter=		I;;

var Ihighest_Cylinder_Index= 0;;



//fun [Cylinder] F

fun returnarea(cylinder)=

   PIf*. cylinder.Fradius;; 

//fun [Cylinder] F 

fun returnvolume(cylinder)=

   (returnarea cylinder)*. cylinder.Fheight;; 



//fun [Cylinder] S

fun birthphase(cylinder)=

   _fooS strcatn "My name is: ":: (itoa(cylinder.Icylinderindex))::" and now I am being born, hello!"::nil;
   _fooS strcatn "My lifespan is :":: (itoa (cylinder.Ilifespanlength)):: " milliseconds."::nil;;

//fun [Cylinder] S     

fun deathphase(cylinder)=

   _fooS strcatn "My name is: ":: (itoa(cylinder.Icylinderindex))::" and now I am dying, goodbye!"::nil;;

/* --------- */



// Miscelleanous

// Note : A function already exists for that in the standard library

fun removeItemInList (list, item)=

	if list == nil then

		nil

	else

		if item == hd list then

			tl list

		else

			(hd list) :: removeItemInList tl list item;;

			

/* --------- */



//fun [] I

//get an id from lDeleted_cylinder_ids....if there are no ids available, return 0, once an index is encountered, return the index

fun get_deleted_cylinder_ID ()=

_fooS "get_deleted_cylinder_ID called";

let hd lDeleted_cylinder_ids -> head in

(

	if (head == nil)  then 

		(

		// there are no available deleted cylinder indexes, they have been used up between the function calling this function and the execution

		//of the line below

		nil;

	) else

	(

	

		_fooS strcatn "head of lDeleted_cylinder_ids is ":: (itoa head)::nil;

		//remove head from lDeleted_cylinder_ids

		set lDeleted_cylinder_ids=  (tl  lDeleted_cylinder_ids);

		_fooS strcatn "the new head of the lDeleted_cylinder_ids list is: " ::(itoa(hd lDeleted_cylinder_ids))::nil;

		_fooS strcatn "the head of the list in this function after resetting list is: ":: (itoa head)::nil; 

		head; 

	



	);

);;

	

//fun [[Cylinder r1] I] I

fun get_highest_id_from_cylinderlist (cylinderlist, id)=

//we want to get the highest Cylinder.Icylinderindex that exists in this list

if ((hd cylinderlist)==nil)&& ((sizelist lCylinderlist)==0) then

(

_fooS "there are no items in the list at all";

1;

)

else if ((hd cylinderlist)==nil)&& ((sizelist lCylinderlist)>0) then

(

      _fooS "we are at the end of a list that has items in it";

      _fooS strcat  "the following value is the highest value found in the complete list is: " (itoa id);

      //since we are making a new cylinder with an index that does not exist, increment global

      //Ihighest_Cylinder_Index by 1 from the highest found id

      set Ihighest_Cylinder_Index = id+1;

      Ihighest_Cylinder_Index;

) 

else

   //we are not at the end of the list yet

(

   //_fooS "there are items in the list and we are not at the end yet";

   let hd cylinderlist -> cylinder in

   let cylinder.Icylinderindex -> this_index in

   if (this_index > id) then

      (

         set id = this_index;

         //_fooS strcat "the highest id found so far is: " (itoa id);

         //we havve the higghest id so far, recursively search through rest of list

         //to see if we can find a higher id

         get_highest_id_from_cylinderlist (tl cylinderlist) id;



      )

   else

      (

         //don't change the id, we have the highest found so far

         //so we recursively search through rest of list

        // _fooS strcat "the highest id found so far is: " (itoa id);

         get_highest_id_from_cylinderlist (tl cylinderlist) id;



      );

);;



//fun[] I

fun get_highest_id_from_all_lists()=

	let  get_deleted_cylinder_ID -> result in

	(

//if there are no items in lDeleted_cylinder_ids, go search for highest id in lCylinderlist

if (result == nil) then

   (

    

    get_highest_id_from_cylinderlist  lCylinderlist nil;

   )

   else

   (

     result;

      

   );

   );;





//fun [cylinder] lCylinderlist



fun delete_cylinder(timer, dcylinder)=

   //destroy the timer

   _deltimer timer;

   //execute death function

   exec dcylinder.fdeathfunction with [dcylinder];

   //add the deleted cylinder index to the lDeleted_cylinder_ids list, so we can grab it if it is free and we add a new cylinder

   let dcylinder.Icylinderindex -> lindex in

   ( 

   _fooS strcatn "cylinder with index number ":: (itoa lindex):: " has died"::nil;

   //add index to lDeleted_cylinder_ids

   set lDeleted_cylinder_ids =lindex:: lDeleted_cylinder_ids;

   //decrement amount of cylinders in existence

   set  ICylinder_counter= ICylinder_counter-1;

   //remove cylinder from lCylinderlist

   set lCylinderlist= removeItemInList lCylinderlist dcylinder;
   _fooS strcatn "there are now :" ::(itoa(sizelist   lDeleted_cylinder_ids))::" cylinders in the lDeleted_cylinder_ids list."::nil;

   _fooS "the list of deleted cylinder id's is below:";  

   _fooIdList  lDeleted_cylinder_ids;

 

    _fooS strcatn "there are now ":: (itoa (sizelist lCylinderlist))::" cylinders in the lCylinderlist. \n \n \n":: nil;

    lCylinderlist;

   );;



//fun [Cylinder Timer] Cylinder

fun makeCylinderTimer (sCylinder, lifespan)=
	 //assign the lifespan in milliseconds for our ephemeral cylinder
   set sCylinder.Ilifespanlength = lifespan;

	set sCylinder.funlifespan = _rfltimer _starttimer _channel lifespan @delete_cylinder sCylinder;

	sCylinder;; 

  

//fun [] Cylinder

fun add_cylinder()=

   //whenever we add a cylinder, no matter where its id comes from, we are incemeneting the ICylinder_counter by one

   set ICylinder_counter=ICylinder_counter+1;

   //get cylinder id

   let get_highest_id_from_all_lists -> id in

   let  mkCylinder [ICylinder_counter id 2.0 22.0 nil nil nil nil nil nil nil nil nil ]->lcylinder in
   let rand/20 -> llifespan in

   let makeCylinderTimer lcylinder llifespan-> lcylinder in


   (

     


       //assign birth and death functions

      set lcylinder.fbirthfunction = @birthphase;

      set lcylinder.fdeathfunction =  @deathphase;

      set lcylinder.funreturnarea = @returnvolume;

      set lcylinder.funreturnvolume = @returnarea;

       //execute birth function

      _fooS "\n \n \n \n";

       _fooS "new cylinder created!";

      exec lcylinder.fbirthfunction with [lcylinder];

      let exec lcylinder.funreturnarea with [lcylinder] ->result in

      _fooS strcat "my area is " (ftoa result);

      let exec lcylinder.funreturnvolume with [lcylinder] ->result in

      _fooS strcat "my volume is " (ftoa result);



      //add cylinder to global lCylinderlist

      set lCylinderlist= lcylinder::lCylinderlist;

      _fooS strcatn "there are now ":: (itoa (sizelist lCylinderlist))::" cylinders in the lCylinderlist.":: nil;

      _fooS strcatn "the new cylinder id is: ":: (itoa id)::nil;

      lcylinder;

  );;

  

fun add_another_cylinder_after_timer(timer, param)=

   

_deltimer timer;

let 100 -> counter in

while (counter >0) do

(

add_cylinder;

set counter= counter-1;

);;





fun main()=

_showconsole;

let 20 -> counter in

while (counter >0) do

(

add_cylinder;

set counter= counter-1;

);

_rfltimer _starttimer _channel 4000 @add_another_cylinder_after_timer  nil;;

[edited] as per most of Iri's suggestions.

Last edited by hebdemnobad (1-Dec-2015 20:47:37)

Offline

#2 1-Dec-2015 17:12:43

hebdemnobad
Member
From: northamerica
Registered: 20-Apr-2011
Posts: 1,477
Website

Re: a cylinder soap opera/ mon feuilleton de cylrindres

I still haven't figured how to configure bluefish so I can paste the code into posts correctly, here is the .pkg:

http://ifam.net/openspace3d/coding/cylinderlist6.pkg

Offline

#3 1-Dec-2015 19:31:26

iri
Admin. / Scol language & Scol apps developer
From: France
Registered: 22-Feb-2009
Posts: 2,024
Website

Re: a cylinder soap opera/ mon feuilleton de cylrindres

Well, few remarks in vrac.

//fun [] I
//get an id from lDeleted_cylinder_ids....if there are no ids available, return 0, once an index is encountered, return the index
fun get_deleted_cylinder_ID ()=

Are you sure that an index will never be equal at 0 ? If no, you should return nil instead if the deleted list is empty.


You should rename explicitely your variables. By example, in lDeleted_cylinder_ids, the first l for a list is ok. But lindex is an integer, not a list. Why this first l ? An i (iIndex by example) is more comprehensive for the reader. And if the second character is upper, keep this convention.


To optimize easily the memory, you should avoid to define uselessly some locale variables. By example :

fun add_cylinder()=
   (...)
   let @birthphase -> birth_function in
   let @deathphase -> death_function in
   let @returnvolume-> volume_function in
   let @returnarea -> area_function in 
   (
       //assign birth and death functions
      set lcylinder.fbirthfunction = birth_function;
      set lcylinder.fdeathfunction = death_function;
      set lcylinder.funreturnarea = area_function;
      set lcylinder.funreturnvolume = volume_function;
(...)

Write instead :

fun add_cylinder()=
   (...)
   (
       //assign birth and death functions
      set lcylinder.fbirthfunction = @birthphase ;
      set lcylinder.fdeathfunction = @deathphase;
      set lcylinder.funreturnarea = @returnvolume;
      set lcylinder.funreturnvolume = @returnarea;

If i understand correctly, you want keep the index value when a cylinder is deleted ? Right ?


If several life span exist, set several lists may be another method, instead of a big list. One list by life span. It is an idea ...
Or, if you know, you could use an hash table (otherwise, forget that !).


A timer object (not only in Scol) is always a greedy object. Some timers are ok. A large number can be problematic, especially as they are destroyed to the first "top". Perhaps, one timer by life span and all cylinders (for a given life span) are created and destroyed in the same "top" ? Depending on your project, of course.


This is a good start !

Offline

#4 1-Dec-2015 20:46:44

hebdemnobad
Member
From: northamerica
Registered: 20-Apr-2011
Posts: 1,477
Website

Re: a cylinder soap opera/ mon feuilleton de cylrindres

iri wrote:

Well, few remarks in vrac.

//fun [] I
//get an id from lDeleted_cylinder_ids....if there are no ids available, return 0, once an index is encountered, return the index
fun get_deleted_cylinder_ID ()=

Are you sure that an index will never be equal at 0 ? If no, you should return nil instead if the deleted list is empty.

Thx I changed that, I don't know how the index could ever be zero, but nil does seem safer.

You should rename explicitely your variables. By example, in lDeleted_cylinder_ids, the first l for a list is ok. But lindex is an integer, not a list. Why this first l ? An i (iIndex by example) is more comprehensive for the reader. And if the second character is upper, keep this convention.

I'll do that when I work on my throttle plugit that I never finished.

To optimize easily the memory, you should avoid to define uselessly some locale variables. By example :

fun add_cylinder()=
   (...)
   let @birthphase -> birth_function in
   let @deathphase -> death_function in
   let @returnvolume-> volume_function in
   let @returnarea -> area_function in
   (
       //assign birth and death functions
      set lcylinder.fbirthfunction = birth_function;
      set lcylinder.fdeathfunction = death_function;
      set lcylinder.funreturnarea = area_function;
      set lcylinder.funreturnvolume = volume_function;
(...)

Write instead :

fun add_cylinder()=
   (...)
   (
       //assign birth and death functions
      set lcylinder.fbirthfunction = @birthphase ;
      set lcylinder.fdeathfunction = @deathphase;
      set lcylinder.funreturnarea = @returnvolume;
      set lcylinder.funreturnvolume = @returnarea;

done

If i understand correctly, you want keep the index value when a cylinder is deleted ? Right ?

yes

If several life span exist, set several lists may be another method, instead of a big list. One list by life span. It is an idea ...
Or, if you know, you could use an hash table (otherwise, forget that !).

I set things up so the lifespan is anywhere from ms  to around 1638 ms.

A timer object (not only in Scol) is always a greedy object. Some timers are ok. A large number can be problematic, especially as they are destroyed to the first "top". Perhaps, one timer by life span and all cylinders (for a given life span) are created and destroyed in the same "top" ? Depending on your project, of course.

If the vm had something like a built in frame by frame callback, I could build some sort of 'kill' function depending on the lifespan left in each cylinder. But os3d does, and that's where I want to apply this knowledge.


This is a good start !

Thanks!  I think I'll end it here, these cylinders have had enough attention from me. Perhaps this example will help others who want to see how you can build simple objects and have them do things, albeit trival things. On to something some other people can actually use.

Offline

#5 1-Dec-2015 21:33:57

iri
Admin. / Scol language & Scol apps developer
From: France
Registered: 22-Feb-2009
Posts: 2,024
Website

Re: a cylinder soap opera/ mon feuilleton de cylrindres

hebdemnobad wrote:

If the vm had something like a built in frame by frame callback, I could build some sort of 'kill' function depending on the lifespan left in each cylinder. But os3d does, and that's where I want to apply this knowledge.

Other methods exist : advantages and disadvantages ...

Offline

Board footer

Powered by FluxBB