| Content Creation Discuss building, scripting, and other forms of content creation for SL. |
| |
![]() |
| | LinkBack | Thread Tools | Display Modes |
| | #1 (permalink) | |
| Doing stuff ![]() ![]() ![]() ![]()
Happles!
Join Date: Sep 2007 Location: Glasgow, Scotland
Posts: 2,778
My Mood: SLShopper Ads: 1 SL Join Date: 14/10/2006
Business: MagoTek Industries | Let's talk about coding I feel in the mood to have a little chat about general syntax and layout in code. From looking at other people's now and then, I've noticed that many people seem to do things differently. it's geeky chat time! A notable thing I do, is classifying my global variables. I arrange them at the top of a script in four sections Protocols: Exclusively contains channel numbers/codes which are used for listens and linkmessages. This section is usually quite large. I write the name of protocols in all caps. Operations: Variables I put in this class, are things which change constantly. For example, a global index variable for incrmenting through notecard lines. In general, operation variables store data which is temporary and in constant flux. Registry: I use this category for more longterm storage. Any data read and stored from notecards goes in a registry variable. As do almost all settings, and just about anything that's going to be synchronised across multiple scripts Constants: A fairly self explanatory category. Stores values which are hardcoded and not changed. Following global variable declarations at the top, I then have all my functions inbetween. Since I'm kind of disorganised, my functions in most medium/small scripts are just a big monolithic block. But when I get into bigger scripts, like 600 lines+, I generally try to categorise the functions according to... their function, I guess. For example functions concerned with showing menus or messages to the user are seperated off into one section. Functions concerning handling and managing data go into another. And I also classify my logic functions seperately - ones that usually have a lot of if~else'ing. Math functions also go seperate, generally concerned with calculating things. I love lists. I love them so much. I do everything with lists. You can bet anything concerning settings in one of my projects is going to have everything stored in and drawn from a list. I'm always comparing things against llList2String(settings, listentry). I tend to keep a big text file with what all my values mean, otherwise I'd end up getting lost in my own code. I find lists can be used to speed up a lot of things. For example, in a recent project, I had a big menu for user settings which entirely contains boolean values. An efficient way of handling this is storing the list of buttons for the menu, and the settings they correspond to, in two lists which are ordered exactly the same way. When the user clicks a button and the listen response comes back, I run it though listFindList to get the list element of the button clicked, and then simply invert the value in that element of the settings list. This approach to things ended up saving me a lot of time and memory, as before that I used to just have a big chain of : if (message == button1) change setting 1 else if (message == button2) change setting 2 etc, etc. Do you guys ever use states? I';ve never found much use for them, finding the whole concept to be rather inflexible and annoying to work with. IIRC, it discards permissions and listens when you change states. Also a random tip from experience. Whenever working with rezzable scripted objects such as projectiles, Always have something like this in on_rez Code: on_rez(integer param)
{
if (param != 0)
{
}
}
Anyways, feel free to post stuff about how you code, any neat tips or advice you can give, etc. questions are neat too.
__________________ Wounds, both physical and mental, heal in time. bones reknit, therapy and drugs make you forget. Life goes on. But nothing cures death. Please remember this. Quote:
| |
| | |
| 1 User Said Yay!: |
| 2 Users Said Thanks : |
| | #2 (permalink) | ||
| The Purple ![]() ![]() ![]() ![]()
Kinda at work. Somewhat.
Join Date: Dec 2007 Location: Somewhere purple, Germany
Posts: 3,096
My Mood: | I did a similiar post ages ago, explaining how I code my stuff and/or giving tips, but I am honestly too lazy to find it, and things may have changed..so I'll just do it again! And answer some of your questions/comment on styles. Quote:
1. I seperate them in constants, which get the ALL_CAPS treatment, and normal variables. 2. Normal variables use polish notation syntax, I.e. sWhatever is a string, vWhatever is a vector, iWhatever is an integer, fWhatever is a float, rWhatever is a rotation. 3. Variables that get re-used through the code at several places for several purposes get named sTemp, vTemp, vTemp2 etc. A friend pointed out vShared would make more sense, but I'm just used to it. The above allow me to clearly identify global variables in the code as constant/non-constant, multi- or specific-purpose, along with their type. My coding mostly is based on a few priorities: High performance, low memory, resource-reuse. Easy readability by third parties or what one would call maintainability (by others) is the lowest priority, as I mostly code for myself exclusively and/or write code that I know I'll be the only maintainer of. Hence I make heavy use of in-line variable assignment, which lowers readability somewhat, but also decreases verbosity of the code, speeds it up and lowers memory usage, along with doing the usual deed of reducing unneeded repeated function calls: Quote:
Basically I try to never, ever repeat the same function call twice in an event or state if it will return the same value in the first place, as variable lookups are much faster than running the function again. Likewise, I try to re-use variables whenever possible. Global variables, along with being faster to access internally with LSL, make for wonderful reusable data containers between events an states. Need a vector variable at times, for vectors that don't have to do with each other and usually don't need to be kept for another part of code? make an aformentioned vTemp variable and re-use at will. I drive it to an extreme. I can't say I use states. Usually I don't have a reason to, as I mostly see them as a way to beautify code and making it more visually easy on the eyes and maintainable for others, which as I previously stated is a low priority for me. On the other hand, state changes have delays, require you to reinstate listens and the like. Speaking of listens... Either I do a single, constantly-on llListen in my state_entry, thus avoiding any problems with duplicate listeners, or I do the usual spiel of only running the listen when needed, and set it up via iListener=llListen... and just listenRemove iListener. never put a listener into on_rez unless it's really required by logic. Another awesome way to speed up things and save memory is using bitfields. What's a bitfield? Well, consider a gadget or script that has alot of on/off switches. Now, you could, of course, use a whole integer variable for each switch, and just make it iBlahEnabled, iWhateverEnabled etc. etc. Or you could use a single integer for 32 on/off options at once. The magic here is how binary works, and boolean operators. Boolean operators allow you to work on a binary level, and in binary, the powers of two always have different bits in the 3 bytes set that make up the normal integer. To illustrate in binary (and a normal 32bit integer): 0: 000000000000000000000000 1: 000000000000000000000001 2: 000000000000000000000010 4: 000000000000000000000100 8: 000000000000000000001000 16: 000000000000000000010000 32: 000000000000000000100000 64: 000000000000000001000000 128: 000000000000000010000000 And so forth. Given that an integer is 32 bits, that gives us 32 on/off switches to check in a single integer variable. Let's call the variable 'iFlags' Code: if(iFlags & 2) Which means, above will return TRUE if the integer is 2: 000000000000000000000010 or 6: 000000000000000000000110 or 11: 000000000000000000001011 It doesn't matter. The '2' bit is set in either case of the integer value. But! That gives us another handy thing! Checking several flags at once! Looking at the above.. Code: if(iFlags & 11) ![]() Likewise, you can set/unset several flags in that matter: Code: if(!(iFlags & 11)) iFlags+=11 I usually put the iFlags variable into the list of global variables at the top of the script like this: Code: integer iFlags; //1 - optionblah, 2 - optionwee, 4 - optionmoo I personally wrap the setting and unsetting of flags into global functions I declare, so I don't need to manually do the if() checks for the flag status manually every time. setFlag(...) and unsetFlag(...) take care of that for me. Which brings us to global functions! I only use them if a piece of code gets reused several times in the script. I never use them if said code only gets used once..it doesn't make sense, as global variable calls are slightly slower and more memory consuming than having the code directly in the script (however, repeating the same code manually over and over eats up more memory, hence global function use in that case). In regards to your use of lists, buttons and setting according values, Kirby, consider this: (see the notation of a constant) Code: list BUTTONS=["Button 1",value,"Button 2",value,"Button 3",value]; integer iTemp; Code: if((iTemp=llListFindList(BUTTONS,[pressedbuttonname])) != -1) whatever(llList2String(BUTTONS,iTemp+1)); When it comes to button lists and long series of ifs() to check what button got pressed and which functionality got called, I tend to use Code: list BUTTONS=["blah","bleh","foo","bar","baz"]; Code: if((iTemp=llListFindList(BUTTONS,[pressedbuttonname])) != -1)
{
if(!iTemp) //button "blah"
{
dostuff
}
else if(iTemp == 1) //button "bleh"
{
dostuff
}
....
}
Last but not least, I love flip-flop switches. consider calling Code: if(iToggle=!iToggle) ... else ... Speaking of fancyness: list != [] is faster than llGetListLength() value=~-value is faster than --value value=-~value is faster than ++value do{...} while(...); is the fastest way to loop. Also, speaking of loops, the do/while is handy for things like sensor or collision events: Code: collision_start(integer foo)
{
do
{
llOwnerSay("I collided with "+llGetDetectedName(foo=~-foo));
}
while(foo);
}
When it comes to braces, I tend to not use them in single-line ifs, loops etc. Above would become Code: collision_start(integer foo)
{
do
llOwnerSay("I collided with "+llGetDetectedName(foo=~-foo));
while(foo);
}
__________________ "Have you ever noticed that anybody driving slower than you is an idiot, and anyone going faster than you is a maniac?" - George Carlin Last edited by Chalice Yao; 11-06-2009 at 05:01 AM. | ||
| | |
| 4 Users Said Thanks : |
| | #3 (permalink) |
| The Purple ![]() ![]() ![]() ![]()
Kinda at work. Somewhat.
Join Date: Dec 2007 Location: Somewhere purple, Germany
Posts: 3,096
My Mood: | Another topic that popped up in my head: Scripts are single-threaded. That means -no- collision/listen/touch/timer/whatever events will be triggered if currently the script is going through code of an already running event. The code of a timer event won't get suspended and resumed due to a touch, for example. An event always finishes before other events trigger, which is also why sometimes you'll be wondering why a touch dialog won't pop up, look for bugs there..and in reality you have an infinite loop in some other event the script is caught in. Not only that, but events can queue up. If you run a 0.5 timer event, and have code in a touch event, the timer will -instantly- trigger afterwards if the touch code took longer than those 0.5 seconds. let's just imagine for example's sake that the touch event takes 2 second to do its code. Likewise, if the touch code took 2 second, the timer interval has passed during the execution of the code, and somebody also collided with the object during those two seconds..say, after 1 second (which means after the 0.5 seconds of the timer) it would instantly trigger the timer after the touch event, and then instantly trigger the collision event after the timer event. As I said, they queue up. Events of the -same- type will also queue up. Another reason -not- to do things like calling the same kind of event at the end of an event for speed, say, sensor. llSensorRepeat has its justification (tho I prefer putting it into a timer(). Habit.). Interesting script time tidbits: Short timers (0.1 for example) are actually very light on script time, compared to all alternative methods for rapid re-execution. No, really...they are, despite the repeated statements to avoid them. Compared to a while(1) (AVOID! IT'S EVIL ON SCRIPTTIME), or doing something fancy like Code: sensor(integer foo)
{
....
llSensor(..)
}
Another scripttime monster are PASSIVE type sensor scans of all kinds. Really. A repeated PASSIVE sensor scan, compared to a repeated ACTIVE scan easily takes up 10 times the script time. I am not kidding. Last edited by Chalice Yao; 11-06-2009 at 05:22 AM. |
| | |
| | #4 (permalink) |
| Senior Member ![]() ![]() ![]() ![]()
Behold, the power of flopsy
ears.
| I use states fairly frequently (and consider the discarding of listens a feature). I find that they can be helpful in reducing the number of if statements I have to use. I have a script to drop leaves, for instance, and the leaf drops can be set to high, medium, or low frequency (or none). If I shove all of these into one state, I'll have to run the timer at the high frequency, use a counter variable, and every time the timer dings: check what frequency I'm using, increment the counter variable, check if it's been enough time, and then drop a leaf or not. If I break the script up into high, medium, and low states, though, I can avoid all that. |
| | |
| | #5 (permalink) |
| Senior Member ![]() ![]() ![]() ![]()
Behold, the power of flopsy
ears.
| Having just finished a scripting project with time intervals of 0.3 (and shorter), I can confirm this. The script tends to use 0.002 ms of time, which is nuts. When I tried using a loop instead, the script time was much, much higher. |
| | |
| | #6 (permalink) | |
| The Purple ![]() ![]() ![]() ![]()
Kinda at work. Somewhat.
Join Date: Dec 2007 Location: Somewhere purple, Germany
Posts: 3,096
My Mood: | Quote:
Last edited by Chalice Yao; 11-06-2009 at 05:30 AM. | |
| | |
| | #7 (permalink) |
| The Purple ![]() ![]() ![]() ![]()
Kinda at work. Somewhat.
Join Date: Dec 2007 Location: Somewhere purple, Germany
Posts: 3,096
My Mood: | Actually, I think the simplest way here would be to simply have a global float variable that keeps time, a global float variable that holds the current interval value, set llGetTime() into it each time a leaf drops, and just do an Code: if((llGetTime() - fLastDropTime) >= fCurrentInterval)
{
drop leaf,
set fLastDropTime to llGetTime()
}
|
| | |
| 1 User Said Thanks: |
| | #8 (permalink) | |
| Senior Member ![]() ![]() ![]() ![]()
Behold, the power of flopsy
ears.
| Quote:
It was just an example, though. I recently did a script that I can't talk much about, but it had a whole bunch of options and was using timers on short intervals to do things as rapidly as possible. I made very heavy use of states in that script, which was a pain to write, but it cut down on unnecessary logic the script needed to process. I also use states in things like single user menu systems, giving each submenu its own state. | |
| | |
| 1 User Agreed: |
| | #9 (permalink) | |
| Senior Member ![]() ![]() ![]() ![]()
Behold, the power of flopsy
ears.
| Quote:
The leaf drop is actually semi random, taking place within a range of time. The best way to do it would be to have a MIN_TIMER and MAX_TIMER variable (I use ALL_CAPS for all my global variables, because I'm annoying like that), set these once the leaf frequency is decided by the user, and just plug them into the line with llFrand() when I reset the timer event after each drop. | |
| | |
| 1 User Agreed: |
| | #11 (permalink) |
| Doing stuff ![]() ![]() ![]() ![]()
Happles!
Join Date: Sep 2007 Location: Glasgow, Scotland
Posts: 2,778
My Mood: SLShopper Ads: 1 SL Join Date: 14/10/2006
Business: MagoTek Industries | The single threaded nature of scripts is something I've had to contend with many a time. My current sword project has, among it's design principles, keeping the core responsive at ALL times. Or at least, all times it's theoretically possible, which is 95% of the time anyways. I use a short timer to bridge the gaps between various events in a given attack, rather than sleeps. So it's never locked in a function just waiting. I've also made one seperate script my little bitch, and it handles all the "dirty work". Ie, executing functions which have delays, such as rezzing and changing prim params. it all works quite beautifully in practice. The entire system weighs in at 6 scripts, one of which is just idle sitting in the sheath to detect clicks. A random thing I'm running into ATM. I think this project is the first time I've ever had more than one script in a prim reading from notecards. I forgot that dataserver events trigger for everything in the prim. It's like crosstalk on steroids. Oh and as an aside. In testing I've done, I found that 0.04 was the smallest timer value you could reliably call repeatedly. It just seemed to not work any faster than that. Could be based on sim fps though. |
| | |
| | #12 (permalink) | |||
| The Purple ![]() ![]() ![]() ![]()
Kinda at work. Somewhat.
Join Date: Dec 2007 Location: Somewhere purple, Germany
Posts: 3,096
My Mood: | Quote:
1. Allow scripts to be compiled without delays (yay) 2. Give us more link-based functions. He seriously hates delays, as they do just that..make people use more scripts, which in turn increases lag, not prevent it as delays originally were intended to do. And he also thinks that with the upcoming script memory limits, people should also have more effective lsl functions to work with instead of bloated code. So yay! Good times a-comin' Quote:
What's handy tho, the object_rez event also triggers for every script :3 Quote:
| |||
| | |
| | #13 (permalink) | ||
| Doing stuff ![]() ![]() ![]() ![]()
Happles!
Join Date: Sep 2007 Location: Glasgow, Scotland
Posts: 2,778
My Mood: SLShopper Ads: 1 SL Join Date: 14/10/2006
Business: MagoTek Industries | Quote:
Allowing scripts to be compiled without delays seems like an excellent solution to avoid breaking existing stuff. Quote:
| ||
| | |
| | #14 (permalink) | |
| The Purple ![]() ![]() ![]() ![]()
Kinda at work. Somewhat.
Join Date: Dec 2007 Location: Somewhere purple, Germany
Posts: 3,096
My Mood: | Quote:
User:Babbage Linden - Second Life Wiki User:Babbage Linden/Office Hours/2009 11 04 - Second Life Wiki And pretty much all the previous office hours of the last months. They're great. | |
| | |
| | #15 (permalink) |
| Doing stuff ![]() ![]() ![]() ![]()
Happles!
Join Date: Sep 2007 Location: Glasgow, Scotland
Posts: 2,778
My Mood: SLShopper Ads: 1 SL Join Date: 14/10/2006
Business: MagoTek Industries | Now that is an interesting read. But I wonder when we'll see such things come to fruition. Babbage seems like a nice guy with big plans. A friend reading that noted that using signatures instead of hashes internally, seems odd and wasteful. |
| | |
| | #16 (permalink) | |
| Emergency Mustelid ![]() ![]() ![]() Join Date: Sep 2009
Posts: 1,073
| Quote:
Polish notation is Cambridge Normal Form, like Lisp. Reverse Polish is stack notation, like Forth. Edit: I want "llSetLinkNumber(integer link);" that would temporarily (until the next llSetLinkNumber() or the end of the event) change the link in the link set that calls apply to. Kind of like a "seteuid" or "chroot" for links. | |
| | |
| | #17 (permalink) | |
| Doing stuff ![]() ![]() ![]() ![]()
Happles!
Join Date: Sep 2007 Location: Glasgow, Scotland
Posts: 2,778
My Mood: SLShopper Ads: 1 SL Join Date: 14/10/2006
Business: MagoTek Industries | Quote:
| |
| | |
| | #18 (permalink) | |
| The Purple ![]() ![]() ![]() ![]()
Kinda at work. Somewhat.
Join Date: Dec 2007 Location: Somewhere purple, Germany
Posts: 3,096
My Mood: | Er, yes, that's what I meant. I always mix up those two for some reason. Quote:
| |
| | |
| | #20 (permalink) |
| The Purple ![]() ![]() ![]() ![]()
Kinda at work. Somewhat.
Join Date: Dec 2007 Location: Somewhere purple, Germany
Posts: 3,096
My Mood: | Quite. Given that he's currently the head honcho behind anyting LSL, and seems to have quite a personal interest in the whole thing, I have high hopes. |
| | |
| | #21 (permalink) |
| Indulging Inaccuracy ![]() ![]() ![]() ![]()
Snack time..
| (Is it no longer possible to post from the slu mobile skin? I had to switch my phone to the full display, blah) Anywho, I guess I'm also one of the apparently annoying types who uses ALLCAPS for constants, and loves states ![]() I pretty much follow the programming style outlined here, as it's the standard I am currently held to in meatspace as well (though obviously the java-specific bits call for a bit of improvising when adapted to LSL): Java Programming Style Guidelines Have I ever mentioned how much I absolutely hate that LL prefixes their functions with 'll'? |
| | |
| 1 User Said Yay!: |
| | #22 (permalink) | |
| Junior Member ![]() Join Date: Oct 2009
Posts: 19
Business: ab's Emporium
| Quote:
Here I have to disagree with you, I like how LL have portioned the name space so that it is easy to avoid stepping on their functions. And when I was new, browsing examples while learning LSL I found it extremely useful immediately being able to pick out the LL functions.
__________________ Vendors, clocks, timers, neon signs, donation boxes and other gadgets Xstreet: https://www.xstreetsl.com/modules.ph...rchantID=45738 In-world: http://slurl.com/secondlife/Monowai/237/111/68 Web: http://www.absemporium.com | |
| | |
| | #23 (permalink) |
| Senior Member ![]() ![]() ![]() ![]()
Behold, the power of flopsy
ears.
| I like it because I can immediately see whether I'm using an LL function or one I defined myself. It's a small help, but a help nonetheless. A function I defined may contain a bug, or may need to be altered after changing other parts of the script; llWhatever() isn't going to be cause either of those problems, and doesn't need to be checked for them. |
| | |
| | #24 (permalink) |
| dorkasaurus ![]() ![]() ![]() ![]() ![]() ![]() SLU Supporter ![]() ![]() ![]()
same as the old me
Join Date: Mar 2008 Location: N.E. United States
Posts: 1,586
My Mood: SLShopper Ads: 3 SL Join Date: Nov 2007
Business: Madhu's Cafe Blog Entries: 3 | I am totally a novice scripter with nothing to contribute to this thread whatsoever. But thanks very much to you experts for the discussion. I've learned some stuff and got some good ideas just reading it once, and I'm sure I will learn more as I re-read it.
__________________ |
| | |
| 1 User Said Yay!: |
| 1 User Said Thanks: |
| 3 Users Agreed: |
| | #25 (permalink) |
| Doing stuff ![]() ![]() ![]() ![]()
Happles!
Join Date: Sep 2007 Location: Glasgow, Scotland
Posts: 2,778
My Mood: SLShopper Ads: 1 SL Join Date: 14/10/2006
Business: MagoTek Industries | Does anyone have any tips on optimising control events? Specifically, I'm looking to return true when the user presses any WASD key, and also a seperate function to return true when the user is not holding any of them. I had the bright idea to combine the bitfields so as to only run one check against an integer named wasd. But I'm not sure how that would work in practice. for example, would it be true when the user presses any of those keys, or only all of them at once. and likewise for the second option, would it work as I want, or would it only be false if the user is holding all keys. ie, would still be rue if they're holding only one. currently the way I'm doing it is checking against each of the four keys individually. it seems rather inefficient though considering this is an operation that's getting done a lot |
| | |
![]() |
| Thread Tools | |
| Display Modes | |
| |