.NET
Languages
Any
programming language that complies with the Common Language Infrastructure
(CLI) specification can be used to write .NET applications. At this point C#
and Visual Basic .NET are the most common and well documented CLI languages for
development.
At
Vicarious Visions, we have decided to use C# whenever possible for developing
.NET applications. We settled on C# because the language was developed
with the .NET Framework in mind, and looks to integrate some of the best
practices from C++ and Java to produce a straightforward application
development language.
One of the key features of C# is that it is a reference-based language, and pointers do not exist. This may seem like a drawback over
C++, but pointers are the source of many issues in applications because of the
unrestricted control they give to the engineer.
While C#
is the language we use to develop .NET tools, there are numerous languages to
choose from -- to date, there are over 40 CLI languages available for building
programs, including:
- Smalltalk
- A# (Ada)
- Active Oberon
- APLNext
- Boo
- C++/CLI
- Chrome
- Cobra
- Common Larceny (Scheme)
- Component Pascal
- Delphi.NET
- Delta Forth .NET
- DotLisp
- EiffelEnvision
- F# (Experimental ML language)
- Gardens Point Modula-2/CLR
- Haskell for .NET/Haskell.net/Hugs for .NET
- IKVM.NET (converts Java byte code to CIL)
- IronLisp
- IronPython
- IronRun
- J# (Java)
- JScript.NET
- L# (Lisp)
- Lexico
- LOLCode.NET
- Mercury on .NET
- Mondrian
- Nemerle (C#/PERL/LISP hybrid)
- Net Express (COBOL)
- NetCOBOL
- OxygenScheme
- P sharp (Prolog)
- Phalanger (PHP)
- Phrogram (CLI language for kids)
- PowerBuilder
- Ruby.NET
- S# (Smalltalk)
- sml.net (Stardard ML)
- VBx (dynamic VB.NET)
- Wildcat Cobol
Our Approach
One of the biggest requirements
for our level editor was that it had to be project-independent. It could not be
designed to work with any one engine specifically, so it would need to be built
upon a truly generic set of functionality. This is unique among editors, which
are almost always made for a specific engine, so it meant we immediately ruled
out any sort of in-game level editor.
Another
limitation was the number of developers who could give full-time support to the
level editor. This number turned out to be two; one engineer and one technical
artist. As exciting as writing a level editor from the ground up sounded, this
option was becoming very unlikely.
We chose
to avoid 3DS Max's C++ API for two reasons. The first is due to the scope of
the code we were writing in 3DS Max. Most of the code would be for scene
manipulation and event registration, which is something MaxScript is already
very good at. The other reason comes from maintaining the code across major 3DS
Max versions. MaxScript often "just works" in a new version with no
modifications, but C++ plug-ins need to be re-compiled against the new API.
Additionally,
the requirement of having Visual Studio configured with the 3DS Max API, and
having a strong knowledge of C++, makes plug-ins far less accessible to
otherwise capable people. Lastly, the need to utilize both 32-bit and 64-bit
workstations at Vicarious Visions meant maintaining two versions of our plug-ins.
We began to look for solutions that could run in either environment.
Writing
this system purely in MaxScript did not seem realistic because of performance
requirements and resource limitations. After doing much research into
MaxScript, along with performance benchmarks, we found that MaxScript could not
support the goals of our system. Benchmarks showed that complex computations
could be performed across the .NET boundary in C# much faster than natively
within MaxScript. The benchmark involved passing an array of integers to a
function, which accumulated the values into a single integer, and returned the
result. The benchmark was run using three array lengths; 16, 1024, and 131072
integers (size). Each benchmark was run
on the buffer in three phases; 10 times, 100 times, and 1000 times (length).
- Size 16 Length 10 MS: 0 DN: 0
- Size 1024 Length 10 MS: 16 DN: 0
- Size 131072 Length 10 MS: 1250 DN: 563
- Size 16 Length 100 MS: 16 DN: 0
- Size 1024 Length 100 MS: 93 DN: 47
- Size 131072 Length 100 MS: 12281 DN: 5750
- Size 16 Length 1000 MS: 16 DN: 15
- Size 1024 Length 1000 MS: 906 DN: 454
- Size 131072 Length 1000 MS: 119953 DN: 57406
MaxScript (MS) vs. .NET (DN),
results given in milliseconds
According
to these results, calling a .NET function is consistently about twice as fast
as calling a MaxScript function to do the same work. See the benchmark
appendix at the end of this article for full benchmark source code.
MaxScript
also has memory limitations, which become a problem when dealing with large data
sets. From an implementation standpoint, having an engineer and a technical
artist meant that we needed a solution which was both C# driven and MaxScript
driven, since engineers typically do not work with MaxScript, and most technical
artists do not work with traditional programming languages.
We decided 3DS Max's role would be
to serve as the user interface. In other words, 3DS Max was the upper layer for
interacting with users and displaying information. This design allowed us to
implement most of the level editor functionality outside of 3DS Max by calling
C# functions and listening for C# events within MaxScript.
The key design
philosophy became providing new functionality using C#, with MaxScript as glue,
interfacing 3DS Max to the C# code.
The tools group at Vicarious Visions had previously developed
a .NET application for viewing and manipulating large data sets. The .NET
integration functionality provided in 3DS Max 9 allowed us to plug into this
application, thus giving our developers a familiar user interface for creating
and manipulating game data. By integrating the two systems, we were able to
offload data processing from 3DS Max to our internal tools, which were already
optimized for the task.
An integrated solution also gave us the ability to attach
Visual Studio to the 3DS Max process, trigger a bug, and step through the .NET
code, setting breakpoints, watching values, and exploring data structures while
3DS Max quietly
sat in the background. User Interfaces written in .NET can also be used directly in
3DS Max, making for a much more seamless user experience.
One of the fortunate side-effects of this approach comes from
our toolchain's ability to communicate directly with the game during runtime.
Because of this, changes in 3DS Max can be immediately reflected while the game
is running. This has provided us with much shorter iteration time for designers
and artists when editing levels.
|
Much obliged.
This is actually not quite right :-)
http://msdn.microsoft.com/en-us/library/y31yhkeb(VS.80).aspx
The .NET integration-features that have been introduced into MAX is a step in the right direction, but there are some shortcomings that may benefit from the act of being brought to the surface, in case someone out there were dancing a mazurka of joy, thinking they could throw out the MAX sdk come blue morning.
The ability to interface with MAX datatypes from the .NET assembly is practically nonexistant. As a result, shuffling large portions of data to the .NET-side therefore requires iterating the data. This e.g. hardens the possibilities of quickly performing a very common plugin task: munching geometry.
Hopefully, integration will become a bit tighter in the future, although I would rather prefer they put their weight on making MAXscript run faster.
I had such a bad time with making the UI for the tool. I could never make it as good as I would have liked.
IIRC Maxscript cannot access everything that's visible in the UI. For example I had issues with using the "loft" tool in Maxscript. Not sure if anything's changed now. I used Maxscript essentially for raising events and for the UI. The majority of the work was done in the 3DS Max Plugins.
Could you tell us something about the formats in which you had exported the data from 3DS Max?
"For some developers, integration with 3DS Max may mean rewriting thousands of lines of code within .NET, and each developer will need to assess how reasonable that would be to achieve."
I've been in positions where I needed to get a lot of old C code into C#. Lucky for me, it was in a DLL, and C# makes running DLL functions relatively easy with platform invoke. Microsoft has a tutorial at http://msdn.microsoft.com/en-us/library/aa288468.aspx.
You'll have to marshal data between the DLL and your C# code, but there ends up being about 10 basic types of marshals (string, array, pointers, etc etc), and once you've got a handle on them, building a class that will run all your DLL functions for you becomes pretty easy.
Anyways, just my two cents. With platform invoke, you'll still have to write a good bit of C# code, but you don't have to rewrite (and re-debug) the code that does most of the work.