MAX脚本规范

MAXScript Coding Standards

17
votes

This document should be used as a source of input when developing your own, or your company’s, coding standards for MAXScript. There are many ways to format code, this style being just one of near infinite choices.

When contributing code to an existing codebase write code that matches the dominant style of the already written code. It is always better to be consistent with what has already been done than to have multiple coding styles interspersed throughout a project.

1. Names

1.1. Make Names Fit

Functions perform actions. A function’s name should reflect that fact. WriteSettingsData() is much more descriptive than SettingsData().

Structures contain data but (normally) do not perform any actions. Structure names should be in the form of nouns. If the structure contains an embedded function the structure’s name should not imply an action, as the structure is more importantly a data format.

Variable names should be long enough so that anyone reading the code can easily derive the type and use for the data the variable holds. While a simple for loop counter can be a single letter like, “i”, an array name of aVerts could mean an array of texture vertices, an array of geometry vertices, an array of free-form deformation modifier control points, or any of a number of different vertex types. aTextureVerts is a much more descriptive, therefore useful, name.

1.2. Include Type in Names

MAXScript has a large number of data types and trying to easily categorize all of them would be impossible. Yet, prefixes for variable names that hold the most often used types helps to make the code more readable.

iVar: integer
fVar: float
tVar: time
aVar: array
sVar: string
ssVar: stringStream
cVar: color
p2Var: point2
p3Var: point3

etc…

MAXScript is a type-free language. It is possible, for instance, to store a string in a variable that is currently holding a float. To maintain readability of the code a variable should always hold the type of data it is assigned at declaration. If there is need to recast a variable the keyword as should be used to change the type of the variable for only the statement in which it is being used:

messageBox (“The value is: “ + (fVar as string))

In addition to variable names prefixes should be used when naming UI controls to help identify what type of control the name refers to:

actxVar: activeXControl
bmpVar: bitmap
btnVar: button
cbxVar: comboBox
chkVar: checkbox
ckbtnVar: checkButton
cpVar: colorPicker
ddlVar: dropDownList
edtVar: editText
grpVar: groupBox
lblVar: label
lbxVar: listBox
mbtnVar: mapButton
mlbxVar: multiListBox
mtlbtnVar: materialButton
pbVar: progressBar
pbtnVar: pickButton
rdoVar: radioButtons
rltVar: rollout
sldVar: slider
spnVar: spinner
tmrVar: timer

1.3. Case Usage
1.3.1.Variables

The prefex of a variable name should be in all lowercase. The remaing words in the variable name should all be initial cap. iVar, not IVar, ivar, or iVAR.

1.3.2.Functions, Structures, and Arrays

Functions should be named with all words initial cap even if words or abbreviations within the name are normally written in all uppercase. GetBoolIniSetting() instead of GetBoolINISetting().

1.3.3.Keywords and Methods

MAXScript methods and keywords should be written with the first word in lowercase and any following words initial caps. This helps to differentiate MAXScript commands from user created functions or structures. messageBox, findString, matchPattern, getNodeByName, etc…

2. Comments

Documents should be written while the script is being written. There is rarely time to go back and comment a script after it is finished. Even if the chance arises to document a script later it won’t be as easy as documenting ideas while the script is being written.

The first line after a function declaration and its opening parenthesis should be the beginning of a blockquote, /*. The blockquote should contain enough information to describe the purpose of the function, what parameters it uses, and what, if any, results it returns. Even if the description is one line long it should be contained within blockquotes. This helps separate the function description from any inline quotes contained within the function.

fn CalculateResult iValue1 iValue2 &iResult =
(
/*
Takes the two integer parameters iValue1
and iValue2 and adds them together. The
answer is assigned to the reference
parameter iResult.
/*


iResult = iValue1 + iValue2
)


Quotes used elsewhere in the script should always be inline quotes, even if they require multiple lines. This helps differentiate them from function descriptions.

-- Extract the properties for an animation so we can get
-- the props. This will go through all notes in a range.
-- Sometimes the properties aren't in the begin/end
-- notes.

local iKey
local sPropName



Always add a comment after the closing parenthesis of a loop or conditional indicating the end of that code block. This helps to delineate where each loop or conditional ends. This is especially useful in scripts that have multiple layers of nested loops and/or conditionals.

-- For all of the gathered texturemaps scan them to
-- see if any of them has the extension we’re looking
-- for. This can’t be done mapped, unfortunately.
for MyMap in aTextureMaps do
(
if (matchPattern MyMap.name pattern:”.dds”) then
(
) -- if conditional valid
else
(
) -- condition not valid
) -- end of the “for MyMap in a Texturemaps” loop


3. Formatting

3.1. Indentation

Different text editors interpret tabs in vastly different ways. What might look like a nice two space indent in UltraEdit can come across as a 16+ space indent in Max’s internal MAXScript editor. For this reason tabs should be avoided whenever possible. If there is consensus within a company or group as to how many spaces indented lines should be indented then each individual user can make the personal determination as to whether they want to manually space in or set their editor’s tab stop to use spaces instead of a tab character. In this way the code will retain it’s formatting on any number of machines and across any number of different text editors.

Visually an indent of two (2) spaces is pleasing to the eye and indents the line enough to make the indentation noticeable.

3.2. Spacing

White space helps in readability. Spaces should be used wherever they will aid in readability. Spaces should always come after words/commands and around mathematical operators.

for i = 1 to 100 do
(
iResult = iInput * i

) -- end of for loop

is much more readable than

for i=1 to 100 do
(
iResult=iInput*i
) -- end of for loop

3.3. One Statement Per Line

To provide maximum readability individual statements should be on separate lines.

3.4. Maximum Line Length

Lines of code should be no longer than 78 characters. While large monitors with high resolutions are the norm there are times when code needs to be printed. If a line of code exceeds 78 characters it will print on multiple lines, throwing off the code formatting.

3.5. Parenthesis/Brace Policy

There are two generally accepted strategies for parenthesis/brace placement:

if (condition)
(

)

or

if (condition) (

)

The first form is the strategy that should be utilized. Without getting into yet another UNIX vs The World argument (UNIX code tends to use the second form) the first form provides two benefits of the second:

1. It is easier to understand at a glance.
2. If you use an editor that supports brace-matching, like UltraEdit, you can find the corresponding brace with a simple command. While this might not seem like an advantage to a simple conditional, as shown above, its increased efficiency is easily shown with a slightly more complex example:

if (condition)
(

)
else
(

)

In this example to find the corresponding else clause to the if clause, regardless of how many lines of code lie between the two, all the user has to do is position their cursor to the left of the if statement, press the down arrow to go to the opening parenthesis, execute the brace-matching command to find the corresponding closing parenthesis, and press the down arrow a second time to get to the else statement. Regardless of how many blocks of code exist the user can get between them with the down arrow key and the brace-matching command.

If the code was formatted using the UNIX style of having the opening parenthesis/brace on the statement line, the user would have to go to the end of the line to find an opening parenthesis in order to use it with the brace-matching command to find the end of the block.
3.6. Function, If/Then/Else, For/Do, While/Do, and Try/Catch Formatting

All conditionals, loops, exception handling blocks, and functions should always be formatted to use open/close parenthesis and multiple lines. For example:

Correct
for i = 1 to 10 do
(

)

Incorrect
for i = 1 to 10 do …

The reason for this is simple. If the code needs to be edited later to add more statements to the loop the first example is already formatted to include those statements as part of the block. The parenthesis and multiple lines also provide visual cues as to what statements are included within the block.

3.7. Never Use If/Do

The conditional form of if/do should never be used in a MAXScript. While if/do is syntactically correct in MAXScript its use can cause problems when the script is edited later. If/do does not allow for an else clause. If there is an if/do conditional it is possible for a person editing the code later to add an else clause and then be presented with the cryptic error,

-- Syntax error: at else, expected <factor>
-- In line: else

In extremely large scripts it could take quite some time to track down the cause of this error. Be safe, always use if/then. It doesn’t require an else clause, but will use one if it is available.

3.8. Case/Of Statement Formatting

Unlike other languages MAXScript’s case statements do not support fall-through. A determination is made at the start of the case/of statement to determine the of state. Then the correct provided case, or default, is jumped to immediately. For this reason the default case should always be provided. Without it the script will fail if an unsupported case is encountered. If the assumption is that the default case will never be triggered then it should be written to trap the error and alert the user.

As with other formatting only one case expression should exist per line. If the case expression is comprised of multiple statements then the entire block should be within parenthesis.

case iCounter of
(
1: print (“The counter is: “ + (iCounter as string))
2: (
for z = iCounter to aTextures.count do
(
print aTextures[z].name
) -- end of “for z” loop
)
default: messageBox “iCounter is out of bounds.”
)

4. Ways To Make MAXScript Faster

Information contained in this section is paraphrased from the “How To Make It Faster?” topic in the MAXScript help file. The information appears here for the sake of brevity.

4.1. Use Mapped Functions Instead Of For Loops

MAXScript supports performing a single operation on multiple objects in a selection simultaneously. The internal loop that accomplishes this can be faster than a MAXScript loop performing the operation on each item in the selection. The MAXScript help file notes which methods and functions can be used
as mapped.

4.2. Cache Frequently Used Data

If code frequently calls the same MAXScript method, object, property, etc.., say while in a loop, it is best to move that method statement outside of the loop, assign the results to a variable, and call that variable within the loop. Getting the resultant data from the variable is much faster than waiting for the method to generate a result during each loop iteration.

4.3. Don't Use Return(), Break(), Exit(), Continue(), or Throw()

These methods use C++ exceptions which are excruciatingly slow. If you need to return a result from a function code the function to accept a reference as a parameter and put the result directly into the reference. Instead of break(), exit(), or continue() use the for/while/do variant of a for loop. For/while/do loops behave exactly like for/do loops but will halt and the end of a loop iteration if the while condition has changed from true to false.

4.4. Use StringStreams to Build Large Strings

In MAXScript each string addition creates a new string to hold the result. If strA = “AAA” and you assign strB as strB = strA + strA + strA three strings will be created.

StringStreams can be added to and the length of the stringstream will be increased to hold the new data without causing new strings to be created. Once you’ve added all of the information to the stringstream convert it back to a string for use.

4.5. Don't use Execute()

To paraphrase a quote by Larry Minton, “If you’re using execute() you’re doing it wrong.” The MAXScript reference help file lists only two valid uses for execute(), Dynamic rollout creation and dynamic scripted custom attribute creation. For anything else chances are great that a MAXScript method, property, keyword, or function exists.

4.6. Don't Use Persistent Global Variables

While not tied to performance, persistent global variables should not be used. Storing useful information in the .max scene file, for use during a later editing session does have its benefits, but the implementation of persistent global variables can lead to corrupt data, globals being inadvertently stored on all scene files opened after the globals are declared, and various other issues. Additionally persistent global variables are only accessible via MAXScript, which limits their usefulness.

Instead of persistent global variables use scripted custom attribute blocks to store data that should remain between editing sessions. Scripted CAs don’t suffer the same limitations as persistent global variables and can be accessed via the Max SDK, making the data stored within them accessible to both scripts and plugins.

 

你可能感兴趣的:(max)